CATextLayer не отображается в AVMutableComposition при запуске из модульного теста
РЕДАКТИРОВАТЬ: Самое странное: кажется, что при запуске этого кода из полного приложения все работает, но я всегда запускал создание фильма из моих модульных тестов, и только там он не работал. Пытаясь выяснить, почему это...
Я пытаюсь объединить видео + аудио + текст с помощью AVMutableComposition и экспортировать его в новое видео.
Мой код основан на AVEditDemo из WWDC '10
Я добавил фиолетовый фон в 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 ответа
Необходимы дальнейшие исследования, но AFAICT прямо сейчас CATextLayer внутри AVMutableVideoComposition просто не работает изнутри цели тестов логических модулей, и эту функцию необходимо тестировать из обычной цели.
Я столкнулся с той же проблемой в другом контексте. В моем случае я перенес подготовку AVMutableComposition в фоновый поток. Перемещение этой части подготовки обратно в основную очередь / поток заставило наложения CATextLayer снова работать должным образом.
Это, вероятно, не совсем применимо к вашему контексту модульного тестирования, но я предполагаю, что CATextLayer/AVFoundation зависит от того, какая часть UIKit/AppKit выполняется / доступна (контекст рисования? Текущий экран?) В этом контексте потока, что может объяснить неудачу, которую мы оба видим.
У меня была проблема, что почти все отрисовывалось великолепно, а также изображения с CALayer и контентом, установленным на CGImage. За исключением текста CGTextLayer, если я установил цвет фона в CGTextLayer, beginTime и длительность, которые были также отлично отрисованы - только фактический текст не хотел появляться. Это было все на симуляторе, затем я запустил его на телефоне: и это было прекрасно.
Вывод: симулятор рендерит хорошие видео... Пока вы не используете CATextLayer.
Моя проблема заключалась в том, что мне нужно было установить contentsGravity = kCAGravityBottomLeft - иначе мой текст был бы вне экрана.