12

编辑:最奇怪的事情:似乎从一个完整的应用程序运行这段代码时一切正常,但我总是从我的单元测试中运行电影的创建,只有在那里它不起作用。试图弄清楚这是为什么...

我正在尝试使用 AVMutableComposition 组合视频 + 音频 + 文本并将其导出到新视频。

我的代码基于 WWDC '10 的 AVEditDemo

我在 CATextLayer 中添加了紫色背景,因此我可以知道它已导出到电影中,但没有显示文本……我尝试使用各种字体、位置、颜色定义,但没有任何帮助,所以我决定在这里发布代码,看看是否有人偶然发现了类似的东西,可以告诉我我错过了什么。

这是代码(self.audio 和 self.video 是 AVURLAssets):

CMTime exportDuration = self.audio.duration;

AVMutableComposition *composition = [[AVMutableComposition alloc] init];

AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *videoTrack = [[self.video tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

// add the video in loop until the audio ends
CMTime currStartTime = kCMTimeZero;
while (CMTimeCompare(currStartTime, exportDuration) < 0) {
    CMTime timeRemaining = CMTimeSubtract(exportDuration, currStartTime);
    CMTime currLoopDuration = self.video.duration;

    if (CMTimeCompare(currLoopDuration, timeRemaining) > 0) {
        currLoopDuration = timeRemaining;
    }
    CMTimeRange currLoopTimeRange = CMTimeRangeMake(kCMTimeZero, currLoopDuration);

    [compositionVideoTrack insertTimeRange:currLoopTimeRange ofTrack:videoTrack
                                    atTime:currStartTime error:nil];

    currStartTime = CMTimeAdd(currStartTime, currLoopDuration);
}

AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

AVAssetTrack *audioTrack = [self.audio.tracks objectAtIndex:0];
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.audio.duration) ofTrack:audioTrack atTime:kCMTimeZero error:nil];

AVMutableVideoComposition *videoComposition;

// the text layer part - THIS IS THE PART THAT DOESN'T WORK WELL
CALayer *animatedTitleLayer = [CALayer layer];
CATextLayer *titleLayer = [[CATextLayer alloc] init];
titleLayer.string = @"asdfasdf";
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.bounds = CGRectMake(0, 0, self.video.naturalSize.width / 2, self.video.naturalSize.height / 2);
titleLayer.opacity = 1.0;
titleLayer.backgroundColor = [UIColor purpleColor].CGColor;

[animatedTitleLayer addSublayer:titleLayer];
animatedTitleLayer.position = CGPointMake(self.video.naturalSize.width / 2.0, self.video.naturalSize.height / 2.0);

// build a Core Animation tree that contains both the animated title and the video.
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, self.video.naturalSize.width, self.video.naturalSize.height);
videoLayer.frame = CGRectMake(0, 0, self.video.naturalSize.width, self.video.naturalSize.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:animatedTitleLayer];

videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
passThroughInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, exportDuration);
AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack];

passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
videoComposition.instructions = [NSArray arrayWithObject:passThroughInstruction];

videoComposition.frameDuration = CMTimeMake(1, 30);
videoComposition.renderSize = self.video.naturalSize;

AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];

exportSession.videoComposition = videoComposition;
exportSession.outputURL = [NSURL fileURLWithPath:self.outputFilePath];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;

[exportSession exportAsynchronouslyWithCompletionHandler:^() {
    // save the video ...
}];
4

4 回答 4

2

我在不同的上下文中遇到了同样的问题。就我而言,我已将 AVMutableComposition 的准备工作移至后台线程。将这部分准备工作移回主队列/线程使 CATextLayer 覆盖再次正常工作。

这可能并不完全适用于您的单元测试上下文,但我的猜测是 CATextLayer/AVFoundation 依赖于 UIKit/AppKit 的某些部分在该线程上下文中运行/可用(绘图上下文?当前屏幕?),这可能解释我们都看到的失败。

于 2016-01-11T23:26:11.870 回答
1

我遇到的问题是几乎所有东西都渲染得很好,还有带有 CALayer 和内容设置为 CGImage 的图像。除了 CGTextLayer 文本,如果我将背景颜色设置为 CGTextLayer,则 beginTime 和 duration 也完美呈现 - 只是不想出现实际文本。这一切都在模拟器上,然后我在手机上运行它:它是完美的。

结论:模拟器渲染出不错的视频......直到你使用 CATextLayer。

于 2016-11-15T19:18:48.597 回答
0

需要进一步调查,但 AFAICT 现在 AVMutableVideoComposition 内的 CATextLayer 根本无法在逻辑单元测试目标内工作,并且必须从常规目标测试此功能。

于 2012-05-01T21:13:26.977 回答
0

我的问题是我需要设置 contentsGravity = kCAGravityBottomLeft - 否则我的文字不在屏幕上。

于 2017-04-17T21:14:19.613 回答