Reputation: 161
Im trying to load a video, add an animation on top of it and then export it, but the animation never starts playing in the exported video. It just shows the image "dogge_icon.png" as it is.
I've tried different types of animations, not sure what i'm doing wrong. Any help would be much appreciated.
The code:
-(void) createCompositionWithPicture {
AVMutableComposition* composition = [AVMutableComposition composition];
NSString *videoPath = [[NSBundle mainBundle] pathForResource:@"Movie" ofType:@"m4v"];
NSLog(@"Path: %@", videoPath);
NSURL *videoURL = [[NSURL alloc] initFileURLWithPath:videoPath];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:nil];
// Create an AVMutableVideoCompositionLayerInstruction for the video track.
AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
// Setup video composition
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
videoComposition.frameDuration = CMTimeMake(1, 30);
mainInstruction.layerInstructions = [NSArray arrayWithObject:videolayerInstruction];
videoComposition.instructions = [NSArray arrayWithObject:mainInstruction];
NSLog(@"Width: %f Height: %f", videoTrack.naturalSize.width, videoTrack.naturalSize.height);
// Setup animation layer
UIImage* image = [UIImage imageNamed:@"dogge_icon.png"];
CALayer *animationLayer = [CALayer layer];
animationLayer.frame = CGRectMake(0, 0, image.size.width, image.size.height);
[animationLayer setMasksToBounds:YES];
[animationLayer setContents: (id)image.CGImage];
// Add animation
CABasicAnimation *animation =
[CABasicAnimation animationWithKeyPath:@"transform.scale"];
animation.duration=5.0;
animation.autoreverses=YES;
animation.fromValue = [NSNumber numberWithFloat:1.0f];
animation.toValue = [NSNumber numberWithFloat:2.0f];
animation.repeatCount=10;
animation.beginTime = AVCoreAnimationBeginTimeAtZero;
[animationLayer addAnimation:animation forKey:@"scale"];
NSLog(@"animationLayer animations: %@", [animationLayer animationKeys]);
// Build layer hierarchy
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
videoLayer.frame = CGRectMake(0, 0, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:animationLayer];
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool
videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
// Export
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSString *exportVideoPath = [STPFileUtilities getPathToFileIn: NSDocumentDirectory WithName: @"composition.mov"];
[STPFileUtilities deleteFileIfExists:exportVideoPath];
NSURL *exportURL = [NSURL fileURLWithPath:exportVideoPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.videoComposition = videoComposition;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:{
NSLog(@"FAIL: %@", exportSession.error);
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (@"SUCCESS");
}
};
}];
}
Upvotes: 4
Views: 1233
Reputation: 2495
The documentation states:
You use an AVVideoCompositionCoreAnimationTool object to incorporate Core Animation in a video composition.
Any animations will be interpreted on the video's timeline, not real-time, so you should:
- Set animations’ beginTime property to AVCoreAnimationBeginTimeAtZero rather than 0 (which CoreAnimation replaces with CACurrentMediaTime);
- Set removedOnCompletion to NO on animations so they are not automatically removed;
- Avoid using layers that are associated with UIView objects.
Hence why adding
animation.removedOnCompletion = NO;
works as indicated in your answer to your own question. While I'm not 100% sure what's going on here, if I had to guess, I would say that the core animation is pre-processed then composited with the video layer. If you remove it, it's remove before the video compositing occurs.
When using CALayers and Animations with compositions, I've had to animate them to 0 opacity to hide them if I want them switched "off".
How to start an animation at a specific time.
The following example (untested) is essentially setting the whole layer to transparent (Opacity 0.0). At 2 seconds (startTime) it begins an animation and fades the Opacity from 0.5 to 1.0 (half transparent to fully opaque) over 3 seconds. It should "turn off" at the end - it will default back to it's initialized (fully transparent) value.
float startTime = 2.0f;
float duration = 3.0f;
CALayer layerToAnimate = [CALayer layer];
layerToAnimate.opacity = 0.0;
// Just turn it on then off.
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[myAnimation setBeginTime: startTime];
[myAnimation setDuration: duration];
[myAnimation setFromValue:[NSNumber numberWithFloat:0.5]];
[myAnimation setToValue:[NSNumber numberWithFloat:1.0]];
[myAnimation setRemovedOnCompletion:NO];
[layerToAnimate addAnimation:myAnimation forKey:@"myUniqueAnimationKey"];
// NB You may not need to be specific on track ID. I like to do so to use to sort layers in to the proper order.
AVVideoCompositionCoreAnimationTool *aniTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithAdditionalLayer:_layerToAnimate asTrackID:intTrackID];
That's basically it, just set the start and durations in seconds. It gets "fun" tracking between frames and times, but you can do it. I use a large number of animation layers all hooked into one layer, all with multiple timings. The biggest hassle is just keeping track of what is on top of what and when it's on.
Upvotes: 2
Reputation: 161
I'm not sure what i did. I rewrote the entire code from scratch and added
animation.removedOnCompletion = NO;
to all animations and now it works.
Upvotes: 4