objective developer apple app iphone avfoundation avassetwriter avmutablecomposition

iphone - developer - AVFoundation+AssetWriter: Generar película con imágenes y audio



xcode developer (2)

¿Puedes reemplazar el ciclo "for" por un solo diccionario "audioInfo" que tenga todos los valores que se deben establecer para que sea más compatible con copiar y pegar? :)

Si solo desea agregar un solo archivo de audio, el siguiente código debería reemplazar el ciclo for:

NSString * pathString = [self getAudioFilePath]; AVURLAsset * urlAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:pathString] options:nil]; AVAssetTrack * audioAssetTrack = [[urlAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID: kCMPersistentTrackID_Invalid]; [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,urlAsset.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:&error];

Tengo que exportar una película desde mi aplicación de iPhone que contiene UIImage de un NSArray y agregar algunos archivos de audio en formato .caf que tienen que comenzar en momentos preespecificados. Ahora he podido usar el AVAssetWriter (después de revisar muchas preguntas y respuestas en este y otros sitios) para exportar la parte del video que contiene las imágenes, pero no puedo encontrar la manera de agregar los archivos de audio para completar la película.

Esto es lo que he obtenido hasta ahora

-(void) writeImagesToMovieAtPath:(NSString *) path withSize:(CGSize) size { NSLog(@"Write Started"); NSError *error = nil; AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL: [NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie error:&error]; NSParameterAssert(videoWriter); NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey, [NSNumber numberWithInt:size.width], AVVideoWidthKey, [NSNumber numberWithInt:size.height], AVVideoHeightKey, nil]; AVAssetWriterInput* videoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain]; AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput sourcePixelBufferAttributes:nil]; NSParameterAssert(videoWriterInput); NSParameterAssert([videoWriter canAddInput:videoWriterInput]); videoWriterInput.expectsMediaDataInRealTime = YES; [videoWriter addInput:videoWriterInput]; //Start a session: [videoWriter startWriting]; [videoWriter startSessionAtSourceTime:kCMTimeZero]; CVPixelBufferRef buffer = NULL; //convert uiimage to CGImage. int frameCount = 0; for(UIImage * img in imageArray) { buffer = [self pixelBufferFromCGImage:[img CGImage] andSize:size]; BOOL append_ok = NO; int j = 0; while (!append_ok && j < 30) { if (adaptor.assetWriterInput.readyForMoreMediaData) { printf("appending %d attemp %d/n", frameCount, j); CMTime frameTime = CMTimeMake(frameCount,(int32_t) kRecordingFPS); append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime]; if(buffer) CVBufferRelease(buffer); [NSThread sleepForTimeInterval:0.05]; } else { printf("adaptor not ready %d, %d/n", frameCount, j); [NSThread sleepForTimeInterval:0.1]; } j++; } if (!append_ok) { printf("error appending image %d times %d/n", frameCount, j); } frameCount++; } } //Finish the session: [videoWriterInput markAsFinished]; [videoWriter finishWriting]; NSLog(@"Write Ended"); }

Y ahora el código para pixelBufferFromCGImage

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image andSize:(CGSize) size { NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; CVPixelBufferRef pxbuffer = NULL; CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, &pxbuffer); NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); CVPixelBufferLockBaseAddress(pxbuffer, 0); void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); NSParameterAssert(pxdata != NULL); CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaNoneSkipFirst); NSParameterAssert(context); CGContextConcatCTM(context, CGAffineTransformMakeRotation(0)); CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); CGColorSpaceRelease(rgbColorSpace); CGContextRelease(context); CVPixelBufferUnlockBaseAddress(pxbuffer, 0); return pxbuffer; }

Entonces, ¿me pueden ayudar con respecto a cómo agregar los archivos de audio y cómo hacer los búferes para ellos y el adaptador y la configuración de entrada, etc.

Si este enfoque pudiera causar un problema, guíame sobre cómo usar una composición de AVMutable para usar la matriz de imágenes para la exportación de video


Terminé exportando el video por separado usando el código anterior y agregué los archivos de audio por separado usando AVComposition y AVExportSession. Aquí está el código

-(void) addAudioToFileAtPath:(NSString *) filePath toPath:(NSString *)outFilePath { NSError * error = nil; AVMutableComposition * composition = [AVMutableComposition composition]; AVURLAsset * videoAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:filePath] options:nil]; AVAssetTrack * videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID: kCMPersistentTrackID_Invalid]; [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:&error]; CMTime audioStartTime = kCMTimeZero; for (NSDictionary * audioInfo in audioInfoArray) { NSString * pathString = [audioInfo objectForKey:audioFilePath]; AVURLAsset * urlAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:pathString] options:nil]; AVAssetTrack * audioAssetTrack = [[urlAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID: kCMPersistentTrackID_Invalid]; [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,urlAsset.duration) ofTrack:audioAssetTrack atTime:audioStartTime error:&error]; audioStartTime = CMTimeAdd(audioStartTime, CMTimeMake((int) (([[audioInfo objectForKey:audioDuration] floatValue] * kRecordingFPS) + 0.5), kRecordingFPS)); } AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality]; assetExport.videoComposition = mutableVideoComposition; assetExport.outputFileType =AVFileTypeQuickTimeMovie;// @"com.apple.quicktime-movie"; assetExport.outputURL = [NSURL fileURLWithPath:outFilePath]; [assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) { switch (assetExport.status) { case AVAssetExportSessionStatusCompleted: // export complete NSLog(@"Export Complete"); break; case AVAssetExportSessionStatusFailed: NSLog(@"Export Failed"); NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); // export error (see exportSession.error) break; case AVAssetExportSessionStatusCancelled: NSLog(@"Export Failed"); NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); // export cancelled break; } }]; }