Использование CABasicAnimation animationWithKeyPath:@contents в CATextLayer для отображения динамического наложенного текста на видео

Я работаю над приложением, которое добавляет к видео динамический текст (например, субтитры). Есть ряд подобных вопросов, но ни один из них не имеет рабочего ответа. Я возлагал большие надежды на использование «ContentAnimate» вместо «contents» в качестве анимацииKeyPath на основе Синхронизация изображения, текста и позиционирования с CoreAnimation, но у меня это не сработало.

Я могу заставить текст отображаться на видео в виде наложения, но анимация не работает — я всегда вижу исходный базовый текст из CATextLayer. Вот как я настроил субтитры и наложение:

        // 1 - Set up the text layer
        CATextLayer *subtitle1Text = [[CATextLayer alloc] init];
        [subtitle1Text setFont:@"Helvetica-Bold"];
        //[subtitle1Text setString:[NSString stringWithFormat:@"%@: %@",  bookmark.creatorMonkeyName, bookmark.info]];
        [subtitle1Text setString:@"Place holder"];

        [subtitle1Text setFontSize:36];
        [subtitle1Text setFrame:CGRectMake(0, 0, videoSize.width, 100)];
        [subtitle1Text setAlignmentMode:kCAAlignmentCenter];
        [subtitle1Text setForegroundColor:[[UIColor whiteColor] CGColor]];


        // 2 - The  overlay
        CALayer *overlayLayer = [CALayer layer];
        [overlayLayer addSublayer:subtitle1Text];
        overlayLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
        [overlayLayer setMasksToBounds:YES];
        overlayLayer.hidden = YES;

        CALayer *parentLayer = [CALayer layer];
        CALayer *videoLayer = [CALayer layer];
        parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
        videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);

        [parentLayer addSublayer:videoLayer];
        [parentLayer addSublayer:overlayLayer];
        videoComp.renderSize = videoSize;
        videoComp.frameDuration = CMTimeMake(1, (int32_t) fps);

Вот как я добавляю анимацию:

            CABasicAnimation *showCommentAnimation2 = [CABasicAnimation animationWithKeyPath:@"contents"];

            [CATransaction begin];
            [CATransaction setDisableActions:YES];

            overlayLayer.hidden = NO;
            showCommentAnimation2.fromValue = @"Hello";
            showCommentAnimation2.toValue = @"Goodbye";
            showCommentAnimation2.beginTime = bookmark.relativeTimeStamp;
            showCommentAnimation2.duration = commentTimeDisplayed;
            [subtitle1Text addAnimation:showCommentAnimation2 forKey:[@(i) stringValue]]; //"i" is an iterator, This should allow multiple animations of the same type
            [CATransaction setDisableActions:NO];
            [CATransaction commit];

Подзаголовок появляется и исчезает, но я всегда получаю текст «заполнитель», а не «Привет» или «До свидания».

Я экспортирую видео в фотопленку, используя:

        videoComp.animationTool = [AVVideoCompositionCoreAnimationTool
                                   videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
    _assetExport.videoComposition = videoComp;
    _assetExport.outputFileType = @"public.mpeg-4";
    _assetExport.outputURL = outputFileUrl;
    [_assetExport exportAsynchronouslyWithCompletionHandler:
     ^(void ) {
         if (AVAssetExportSessionStatusCompleted == _assetExport.status) {
             DLog(@"AVAssetExportSessionStatusCompleted");
             ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
             DLog(@"About to copy video to camera roll");
             //move video to camera roll

             [library writeVideoAtPathToSavedPhotosAlbum:outputFileUrl completionBlock:^(NSURL *assetURL, NSError *error) {
                 [self removeWaitingScreen];
                 dispatch_async(dispatch_get_main_queue(), ^(void){
                     MPMoviePlayerViewController* theMovie = [[MPMoviePlayerViewController alloc] initWithContentURL: assetURL];
                     [_viewController presentMoviePlayerViewControllerAnimated:theMovie];

                 });
             }];
         } else{
             // a failure may happen because of an event out of your control
             // for example, an interruption like a phone call comming in
             // make sure and handle this case appropriately
             DLog(@"Error - Export Session Status: %ld", (long)_assetExport.status);
             dispatch_async(dispatch_get_main_queue(), ^(void){
                 [self removeWaitingScreen];
                 [[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", NSStringFromClass([self class]))
                                              message:NSLocalizedString(@"Video Creation Failed", nil)
                                             delegate:nil
                                    cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                    otherButtonTitles: nil] autorelease] show];
             });
         }

   [videoComp release];
   }];

}];

Кто-нибудь может помочь?

Если есть лучший способ записать субтитры в видео, я открыт для предложений.

Спасибо.


person JacobU    schedule 06.09.2015    source источник
comment
Что такое showCommentAnimation?   -  person jtbandes    schedule 06.09.2015
comment
Извините, что пропало из фрагмента. Это: CABasicAnimation *showCommentAnimation2 = [CABasicAnimation animationWithKeyPath:@contents];   -  person JacobU    schedule 06.09.2015


Ответы (2)


Анимацию с ключевым путем contents нельзя использовать со строковыми значениями (@"Hello" и @"Goodbye"). Свойство contents слоя содержит изображение, установленное вручную или нарисованное самим слоем.

Простой установки свойства string текстового слоя должно быть достаточно, чтобы вызвать неявную анимацию. Однако похоже, что вам нужна явная анимация, чтобы управлять beginTime. Это можно сделать с помощью CATransition. См. здесь для примера.

person jtbandes    schedule 06.09.2015
comment
В ПОРЯДКЕ. Я предполагаю, что то же самое верно (нет строковых значений для свойства содержимого) и для CATextLayer. Я не понимаю, как помогает CATransition - он для перехода между двумя слоями, и я не могу использовать его для установки свойства в слое. - person JacobU; 07.09.2015
comment
CATransition заставляет систему делать снимок слоя и анимировать между снимками. Таким образом, вы просто добавляете переход, а затем изменяете слой по своему вкусу. Хотя рад, что у вас все заработало :) - person jtbandes; 07.09.2015

Благодаря jtbandes я понял это.

Шаг 1: Создайте анимацию, которая изменяет скрытое свойство для этого слоя в нужное время:

CAKeyframeAnimation *showCommentAnimation = [CAKeyframeAnimation animationWithKeyPath:@"hidden"];
        [showCommentAnimation setValue:@"hidestring" forKey:@"MyAnimationType"];
        showCommentAnimation.values = @[@(NO),@(YES)];
        showCommentAnimation.keyTimes = @[@0.0, @1.0];
        showCommentAnimation.calculationMode = kCAAnimationDiscrete;
        showCommentAnimation.removedOnCompletion = NO;
        showCommentAnimation.delegate = self;

Шаг 2: Создайте отдельный CATextLayer для каждого комментария

CATextLayer *subtitle1Text = [[CATextLayer alloc] init];

измените текст для этого слоя на соответствующий подзаголовок:

[subtitle1Text setString:@"Actual subtitle for that layer"];

Шаг 3: добавьте анимацию с соответствующим временем:

showCommentAnimation.beginTime = subtitle.relativeTimeStamp;
[subtitle1Text addAnimation:showCommentAnimation forKey:[@"setstring" stringByAppendingString:[@(i) stringValue]]]; //use showCommentAnimation to set hidden propert
person JacobU    schedule 07.09.2015