I've been trying to produce a proof of concept that connects a video with an animation object (essentially, I am "rotoscoping" a user-selected-bitmap onto a canned video at specific locations). After watching all the available Apple material regarding Core Animation (WWDC10, etc.) and the AVSync..Layer (Apple WWDC10 sample code, doc sample code), there simply isn't a clear enough isolated code example to derive how to implemented this playback mode correctly.
Using CA, I have a video asset which is loaded into a CALayer, and an animation which is attached to a bitmap, and then connected to another CALayer. I have created an AVSynchronizedLayer and added both objects as sublayers.
When this Tree is added to the UIView's layer property I was expecting the playback of the animation to run in sync with the video. Instead, I only see the video playing, and the animation never executes - here are the relevant pieces of my code which all resides within a single View Controller (called within ViewDidLoad).
Video101.m4v = simple m4v video, gerald.png = bitmap
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Video101" withExtension:@"m4v"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer *player = [[AVPlayer playerWithPlayerItem:playerItem]retain];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = CGRectMake(0, 0, 1024, 768);
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
CALayer *gerald = [CALayer layer];
gerald.frame = CGRectMake(0, 0, 120, 120);
gerald.contents = (id) [UIImage imageNamed:@"gerald.png"].CGImage;
[self.view.layer addSublayer:playerLayer];
[self.view.layer insertSublayer:gerald above:playerLayer];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
anim.duration = 18.0;
anim.values = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(480.0, 206.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(10.0, 760.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)],
[NSValue valueWithCGPoint:CGPointMake(320.0, 406.0)],
[NSValue valueWithCGPoint:CGPointMake(310.0, 400.0)], nil];
anim.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.02],
[NSNumber numberWithFloat:0.06],
[NSNumber numberWithFloat:0.09],
[NSNumber numberWithFloat:0.1],
[NSNumber numberWithFloat:0.12],
[NSNumber numberWithFloat:0.2],
[NSNumber numberWithFloat:0.25],
[NSNumber numberWithFloat:0.32],
[NSNumber numberWithFloat:0.38],
[NSNumber numberWithFloat:0.49],
[NSNumber numberWithFloat:0.53],
[NSNumber numberWithFloat:0.59],
[NSNumber numberWithFloat:0.63],
[NSNumber numberWithFloat:0.69],
[NSNumber numberWithFloat:0.78],
[NSNumber numberWithFloat:0.81],
[NSNumber numberWithFloat:0.91],
[NSNumber numberWithFloat:1.0], nil];
AVSynchronizedLayer *syncLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:playerItem];
[syncLayer addSublayer:gerald];
[gerald addAnimation:anim forKey:@"transform.position"];
[self.view.layer addSublayer:syncLayer];
[player play];
What is the correct format or manner to implement AVSynchronizedLayer, so that both animation and video playback together?
You have the CAKeyframeAnimation
set up correctly except for one bit:
According to the WWDC "Editing Media with AV Foundation" lecture, you need to set the beginTime
property of your animation to a non-zero value.
Zero beginTime is automatically translated to CACurrentMediaTime(). Use a small nonzero number: e.g., 1e-100 or -1e-100 (AVCoreAnimationBeginTimeAtZero)
I think if you add anim.beginTime = AVCoreAnimationBeginTimeAtZero;
you'll find the animation begins right when the video starts playing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With