AVFoundation + AssetWriter:通过图像和audio生成电影

我必须从我的iPhone应用程序包含UIImage从NSArray中导出电影,并添加一些.caf格式的audio文件,必须在预先指定的时间开始。 现在我已经能够使用AVAssetWriter(经过这个和其他网站上的许多问题和答案)导出包含图像的video部分,但似乎找不到一种方法来添加audio文件来完成电影。

这是我迄今得到的

-(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"); } 

现在代码为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; } 

那么你能帮我解决一下如何添加audio文件以及如何为它们制作缓冲区以及适配器和input设置等

如果这种方法可能会导致一个问题,指导我如何使用AVMutableComposition来使用图像数组进行video导出

我最终分别使用上面的代码导出video,并使用AVComposition&AVExportSession单独添加audio文件。 这是代码

 -(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; } }]; } 

你可以用一个“audioInfo”字典replace“for”循环,它具有所有需要设置的值,以便它变得更加复制粘贴友好? 🙂

如果你只是想添加一个audio文件,下面的代码应该replacefor循环:

 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];