Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mix video with static image in CALayer using AVVideoCompositionCoreAnimationTool

I am trying to mix video, coming from the camera with a static image (watermarking).

I have checked around questions/answers here and some examples, including WWDC AVEditDemo from Apple and ended with the following code. Unfortunately, the exported video does not contain the layer with the image. Any ideas?

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

/// incoming video
NSURL *videoURL = [info valueForKey:UIImagePickerControllerMediaURL];

/// UIImage into CALayer
UIImage *myImage = [UIImage imageNamed:@"m1h.png"];
CALayer *aLayer = [CALayer layer];
aLayer.contents = (id)myImage.CGImage;

AVURLAsset* url = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVMutableComposition *videoComposition = [AVMutableComposition composition];
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];

AVMutableCompositionTrack *compositionVideoTrack = [videoComposition  addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *clipVideoTrack = [[url tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [url duration])  ofTrack:clipVideoTrack atTime:kCMTimeZero error:&error];

AVMutableVideoComposition* videoComp = [[AVMutableVideoComposition videoComposition] retain];
videoComp.renderSize = CGSizeMake(640, 480);
videoComp.frameDuration = CMTimeMake(1, 30);
videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithAdditionalLayer:aLayer asTrackID:2];


/// instruction
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30) );
AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:clipVideoTrack];
instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
videoComp.instructions = [NSArray arrayWithObject: instruction];

/// outputs
NSString *filePath = nil;
filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
filePath = [filePath stringByAppendingPathComponent:@"temp.mov"]; 
NSLog(@"exporting to: %@", filePath);
if ([fileManager fileExistsAtPath:filePath]) 
{
        BOOL success = [fileManager removeItemAtPath:filePath error:&error];
        if (!success) NSLog(@"FM error: %@", [error localizedDescription]);
}

/// exporting
AVAssetExportSession *exporter;
exporter = [[AVAssetExportSession alloc] initWithAsset:videoComposition presetName:AVAssetExportPresetHighestQuality] ;
exporter.videoComposition = videoComp;
exporter.outputURL=[NSURL fileURLWithPath:filePath];
exporter.outputFileType=AVFileTypeQuickTimeMovie;

[statusLabel setText:@"processing..."];

[exporter exportAsynchronouslyWithCompletionHandler:^(void){
    switch (exporter.status) {
        case AVAssetExportSessionStatusFailed:
            NSLog(@"exporting failed");
            break;
        case AVAssetExportSessionStatusCompleted:
            NSLog(@"exporting completed");
            UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, @selector(video:didFinishSavingWithError:contextInfo:), NULL);
            break;
        case AVAssetExportSessionStatusCancelled:
            NSLog(@"export cancelled");
            break;
    }
}];

}

like image 788
izne Avatar asked May 13 '11 20:05

izne


2 Answers

After playing around I ended up with something like this in a addition to the above code, also changing the used method to videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:

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];
videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];

Hope it helps somebody.

like image 79
izne Avatar answered Oct 18 '22 15:10

izne


i got this to work! heres the code! i didn't write most of it, i just tweaked some, but the only issue is the video itself is rotated for landscape in portrait mode? and then in landscape its portrait video, but the image is right side up!

CALayer *aLayer = [CALayer layer];
    aLayer.frame = CGRectMake(5, 0, 320, 480);
    aLayer.bounds = CGRectMake(5, 0, 320, 480);
    aLayer.contents = (id) [UIImage imageNamed:@"image.png"].CGImage;
    aLayer.opacity = 0.5;
    aLayer.backgroundColor = [UIColor clearColor].CGColor;
    NSURL *url = [NSURL fileURLWithPath:[urlsOfVideos objectAtIndex:self.pageControl.currentPage]];
    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
    cmp = [AVMutableComposition composition]; 

    AVMutableCompositionTrack *trackA = [cmp addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    AVAssetTrack *sourceVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    [trackA insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:sourceVideoTrack atTime:kCMTimeZero error:nil] ;

    animComp = [AVMutableVideoComposition videoComposition];
    animComp.renderSize = CGSizeMake(320, 480);
    animComp.frameDuration = CMTimeMake(1,30);
    CALayer *parentLayer = [CALayer layer];
    CALayer *videoLayer = [CALayer layer];
    parentLayer.frame = CGRectMake(0, 0, 320, 480);
    videoLayer.frame = CGRectMake(0, 0, 320, 480);
    [parentLayer addSublayer:videoLayer];
    [parentLayer addSublayer:aLayer];
    animComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
    AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [asset duration]);
    AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:trackA];
    //[layerInstruction setTrackID:2];
    [layerInstruction setOpacity:1.0 atTime:kCMTimeZero];
    instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction] ;
    animComp.instructions = [NSArray arrayWithObject:instruction];
    [self exportMovie:self];

and here is the exporting code

-(IBAction) exportMovie:(id)sender{
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *tempPath = [docPaths objectAtIndex:0];
NSLog(@"Temp Path: %@",tempPath);

NSString *fileName = [NSString stringWithFormat:@"%@/output-anot.MOV",tempPath];
NSFileManager *fileManager = [NSFileManager defaultManager] ;
if([fileManager fileExistsAtPath:fileName ]){
    //NSError *ferror = nil ;
    //BOOL success = [fileManager removeItemAtPath:fileName error:&ferror];
}

NSURL *exportURL = [NSURL fileURLWithPath:fileName];

AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:cmp presetName:AVAssetExportPresetHighestQuality]  ;
exporter.outputURL = exportURL;
exporter.videoComposition = animComp;
exporter.outputFileType= AVFileTypeQuickTimeMovie;
[exporter exportAsynchronouslyWithCompletionHandler:^(void){
    switch (exporter.status) {
        case AVAssetExportSessionStatusFailed:{
            NSLog(@"Fail");
            break;
        }
        case AVAssetExportSessionStatusCompleted:{
            NSLog(@"Success");
            break;
        }

        default:
            break;
    } 
}];

}

like image 26
Maximilian Litteral Avatar answered Oct 18 '22 14:10

Maximilian Litteral