Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add a text overlay with AVMutableVideoComposition to a specific timerange

I have found a few examples that show how to add a text overlay on a video.

Ray's Tutorial - http://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos

This SO answer - How can I add a watermark in a captured video on iOS

There were a few others that I referenced as well. My code looks almost identical to that answer and I've been trying to tweak it to get the text overlay to only show up for a second or two at the beginning of the video. Any help on how I could accomplish that?

Here is my code. This works as is to export the video with the overlay being shown for the entire duration.

if(vA) {
    videoCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [videoCompositionTrack insertTimeRange:videoTimerange ofTrack:[[vA tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:&error];
    [videoCompositionTrack setPreferredTransform:[[[vA tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] preferredTransform]];
    if(error)
        NSLog(@"%@", error);
}

if(aA) {
    audioCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [audioCompositionTrack insertTimeRange:audioTimerange ofTrack:[[aA tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:&error];
    if(error)
        NSLog(@"%@", error);
}

CGSize videoSize = [videoCompositionTrack naturalSize];

UIImage *myImage = [UIImage imageNamed:@"cover.png"];
CALayer *aLayer = [CALayer layer];
aLayer.contents = (id)myImage.CGImage;
aLayer.frame = CGRectMake(5, 25, 100, 56);
aLayer.opacity = 0.7;

CATextLayer *titleLayer = [CATextLayer layer];
titleLayer.string = titleText;
titleLayer.fontSize = 18;
titleLayer.foregroundColor = titleColor.CGColor;
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.frame = CGRectMake(20, 10, videoSize.width - 40, 20);
[titleLayer displayIfNeeded];

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:aLayer];
if(titleText && [titleText length] > 0) {
    [parentLayer addSublayer:titleLayer];
}

AVMutableVideoComposition *videoComp = [AVMutableVideoComposition videoComposition];
videoComp.renderSize = videoSize;
videoComp.frameDuration = CMTimeMake(1, 30);
videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

AVAssetTrack *videoTrack = [[mixComposition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];

AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
[instruction setTimeRange:CMTimeRangeMake(kCMTimeZero, [mixComposition duration])];
instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
videoComp.instructions = [NSArray arrayWithObject:instruction];

AVAssetExportSession *_assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];
_assetExport.videoComposition = videoComp;
_assetExport.outputFileType = AVFileTypeMPEG4;
_assetExport.outputURL = outputFileUrl;

[_assetExport exportAsynchronouslyWithCompletionHandler:^{
    AVAssetExportSessionStatus status = [_assetExport status];
    switch (status) {
        case AVAssetExportSessionStatusFailed:
            NSLog(@"Export Failed");
            NSLog(@"Export Error: %@", [_assetExport.error localizedDescription]);
            NSLog(@"Export Error Reason: %@", [_assetExport.error localizedFailureReason]);
            break;
        case AVAssetExportSessionStatusCompleted:
            NSLog(@"Export Completed");
            [self performSelectorOnMainThread:@selector(updateProgressIndicator:) withObject:[NSNumber numberWithFloat:2] waitUntilDone:YES];
            break;
        case AVAssetExportSessionStatusUnknown:
            NSLog(@"Export Unknown");
            break;
        case AVAssetExportSessionStatusExporting:
            NSLog(@"Export Exporting");
            break;
        case AVAssetExportSessionStatusWaiting:
            NSLog(@"Export Waiting");
            break;
    }
}];
like image 629
ScottF Avatar asked Feb 10 '14 17:02

ScottF


2 Answers

I figured out what I needed to do. It wasn't anything special really. It just required a better understanding of what was all possible.

Basically all I had to do was add a basic opacity animation to the layer with text in it.

// My original code for creating the text layer
CATextLayer *titleLayer = [CATextLayer layer];
.
.
.
[titleLayer displayIfNeeded];

// the code for the opacity animation which then removes the text
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setDuration:0];
[animation setFromValue:[NSNumber numberWithFloat:1.0]];
[animation setToValue:[NSNumber numberWithFloat:0.0]];
[animation setBeginTime:1];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[titleLayer addAnimation:animation forKey:@"animateOpacity"];
like image 122
ScottF Avatar answered Nov 14 '22 23:11

ScottF


We should add two opacity animations for Video overlay at particular time rang. First animation for show overlay at start duration and second animation for hide overlay at end duration. We should take care to set fill mode of animation.

Sample code of Video overlay for particular time Rang.

            let textLayer = CATextLayer()
            textLayer.opacity = 0.0
            .
            .
            textLayer.frame = CGRect(x: 0.0, y: 0.0, width: 150.0, height:100.0)
            textLayer.string = "Overlay Text"

            let startVisible = CABasicAnimation.init(keyPath:"opacity")
            startVisible.duration = 0.1    // for fade in duration
            startVisible.repeatCount = 1
            startVisible.fromValue = 0.0  
            startVisible.toValue = 1.0
            startVisible.beginTime = overlay.startTime.seconds // overlay time range start duration
            startVisible.isRemovedOnCompletion = false 
            startVisible.fillMode = kCAFillModeForwards 
            textLayer.add(startVisible, forKey: "startAnimation")

            let endVisible = CABasicAnimation.init(keyPath:"opacity")
            endVisible.duration = 0.1
            endVisible.repeatCount = 1
            endVisible.fromValue = 1.0
            endVisible.toValue = 0.0
            endVisible.beginTime = overlay.endTime.seconds
            endVisible.fillMode = kCAFillModeForwards
            endVisible.isRemovedOnCompletion = false
            textLayer.add(endVisible, forKey: "endAnimation")
like image 39
Jatin Kathrotiya Avatar answered Nov 14 '22 22:11

Jatin Kathrotiya