核心动画,AVFoundation以及视频导出功能

11
我正在寻找将图片序列正确导出为QuickTime视频的方法。我知道AV Foundation可以合并或重新组合视频,并添加音频轨道来构建单个视频资源。现在,我的目标有点不同。我想从头开始创建一个视频。我有一系列UIImage,需要将它们全部渲染到单个视频中。我阅读了关于AV Foundation的所有Apple文档,并找到了AVVideoCompositionCoreAnimationTool类,该类能够接收CoreAnimation并将其重新编码为视频。我还检查了由Apple提供的AVEditDemo项目,但似乎我的项目上有些东西无法正常工作。
以下是我的步骤:
1)我创建了CoreAnimation层
CALayer *animationLayer = [CALayer layer];
[animationLayer setFrame:CGRectMake(0, 0, 1024, 768)];

CALayer *backgroundLayer = [CALayer layer];
[backgroundLayer setFrame:animationLayer.frame];
[backgroundLayer setBackgroundColor:[UIColor blackColor].CGColor];

CALayer *anImageLayer = [CALayer layer];
[anImageLayer setFrame:animationLayer.frame];

CAKeyframeAnimation *changeImageAnimation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
[changeImageAnimation setDelegate:self];
changeImageAnimation.duration = [[albumSettings transitionTime] floatValue] * [uiImagesArray count];
changeImageAnimation.repeatCount = 1;
changeImageAnimation.values = [NSArray arrayWithArray:uiImagesArray];
changeImageAnimation.removedOnCompletion = YES;
[anImageLayer addAnimation:changeImageAnimation forKey:nil];

[animationLayer addSublayer:anImageLayer];

2) 然后我实例化AVComposition

AVMutableComposition *composition = [AVMutableComposition composition];
composition.naturalSize = CGSizeMake(1024, 768);

CALayer *wrapLayer = [CALayer layer];
wrapLayer.frame = CGRectMake(0, 0, 1024, 768);
CALayer *videoLayer = [CALayer layer];
videoLayer.frame = CGRectMake(0, 0, 1024, 768);
[wrapLayer addSublayer:animationLayer];
[wrapLayer addSublayer:videoLayer];

AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];


AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMake([imagesFilePath count] * [[albumSettings transitionTime] intValue] * 25, 25));

AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstruction];
videoCompositionInstruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];

videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:wrapLayer];
videoComposition.frameDuration = CMTimeMake(1, 25); // 25 fps
videoComposition.renderSize = CGSizeMake(1024, 768);
videoComposition.instructions = [NSArray arrayWithObject:videoCompositionInstruction];

3) 我将视频导出到文档路径

AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetLowQuality];
session.videoComposition = videoComposition;

NSString *filePath = nil;
filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
filePath = [filePath stringByAppendingPathComponent:@"Output.mov"];    

session.outputURL = [NSURL fileURLWithPath:filePath];
session.outputFileType = AVFileTypeQuickTimeMovie;

[session exportAsynchronouslyWithCompletionHandler:^
 {
     dispatch_async(dispatch_get_main_queue(), ^{
         NSLog(@"Export Finished: %@", session.error);
         if (session.error) {
             [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL];
         }
     });
 }];

导出结束时出现以下错误:

导出完成:错误域=AVFoundationErrorDomain,代码=-11822,“无法打开” UserInfo=0x49a97c0 {NSLocalizedFailureReason=不支持此媒体格式,NSLocalizedDescription=无法打开}

我在文档中找到了这个错误代码:AVErrorInvalidSourceMedia = -11822,

AVErrorInvalidSourceMedia 由于某些源媒体无法读取,无法完成操作。

我确定我的CoreAnimation构建是正确的,因为我将其呈现到测试层中,我可以正确地看到动画进度。

有人能帮我理解我的错误在哪里吗?


尝试在苹果开发者论坛上发布帖子,或向苹果提交错误报告。他们可能能够告诉您您做错了什么。 - Andrew Pouliot
2个回答

5
我找到了 AVVideoCompositionCoreAnimationTool 类,它可以将 CoreAnimation 转换成视频。
我之前的理解是它只能将 CoreAnimation 添加到现有的视频中。但是我刚刚查看了文档,发现所有可用的方法都需要一个视频层。
编辑:是的,在查看文档和 WWDC 视频后,我认为你应该使用 AVAssetWriter,然后将图像附加到写入器中。类似这样:
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:somePath] fileType:AVFileTypeQuickTimeMovie error:&error];

NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey, [NSNumber numberWithInt:320], AVVideoWidthKey, [NSNumber numberWithInt:480], AVVideoHeightKey, nil];
AVAssetWriterInput* writerInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain];

[videoWriter addInput:writerInput];

[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:CMTimeMakeWithSeconds(0, 30)]
[writerInput appendSampleBuffer:sampleBuffer];
[writerInput markAsFinished];
[videoWriter endSessionAtSourceTime:CMTimeMakeWithSeconds(60, 30)];
[videoWriter finishWriting];

我认为你是对的。我会更深入地研究这个问题。我正在尝试保存一系列图像。 - Franky
在编写 UIImageCGImageRef 的上下文中,sampleBuffer 是什么?只是原始图像像素吗?是用 CMSampleBufferCreateForImageBuffer 创建的吗? - bcattle
@bcattle,请将您的问题作为一个新问题提出,而不是作为评论。 - Adam
你可以在Core Animation中使用AVAssetWriter吗? - Carolina

5

也许您需要一个包含完全黑帧的虚假电影来填充视频层,然后添加一个CALayer来操作图像。


这就是我最终不得不做的事情。 - spring
同样的情况。好处是它不需要填满你想要完成的总长度。我正在使用1秒的黑色视频(无论如何都被CALayers隐藏),但输出更长的长度。 - jpswain
有关将图像导出为带有过渡效果的电影的任何想法吗?如果您知道,请告诉我。谢谢。 - Divyesh Dobariya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接