3

When I create a video using this tutorial and I try to move the AVAssetTrack* firstTrack (without animation, so I cannot use CAAnimations) using AVMutableVideoCompositionInstruction with a AVMutableVideoCompositionLayerInstruction that contains some CGAffineTransforms, the transformations just get ignored.

Does anyone know this problem and knows how to solve it?

Thanks in advance!

This info might help: I use AVAssetExportPreset1280x720 as preset when exporting. So I think (might be wrong) this is not the problem.

EDIT: The code was split in various functions, but I tried to get everything together to one code. I hopefully did not forget anything or messed something up.

// The layer I want to move
CALayer* layerToMove = nil;
// A layer containing just the black background
CALayer* backgroundLayer = nil;
// The videos duration
double duration = 12.0;

AVMutableComposition *exportAsset = [[AVMutableComposition alloc] init];
[exportAsset setNaturalSize:layerToMove.frame.size];

AVMutableCompositionTrack* dstTrack = [self addBlackVideoTrackOfDuration:duration toComposition:exportAsset];

AVMutableVideoCompositionInstruction * MainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, exportAsset.duration);

CGAffineTransform Scale = CGAffineTransformMakeScale(0.1f,0.1f);
CGAffineTransform Move = CGAffineTransformMakeTranslation(230,230);
AVMutableVideoCompositionLayerInstruction *FirstlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:dstTrack];

[FirstlayerInstruction setTransform:CGAffineTransformConcat(Scale,Move) atTime:kCMTimeZero];
[MainInstruction setLayerInstructions:@[FirstlayerInstruction]];

CALayer* compositionLayer = [CALayer layer];

compositionLayer.bounds = layerToMove.frame;
compositionLayer.anchorPoint = CGPointMake(0, 0);
compositionLayer.position = CGPointMake(0, 0);

[compositionLayer setFrame:layerToMove.frame];
[compositionLayer setBounds:layerToMove.frame];

[compositionLayer addSublayer:backgroundLayer];
[compositionLayer addSublayer:layerToMove];

AVVideoCompositionCoreAnimationTool *animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:layerToMove inLayer:compositionLayer];

CMTime frameDuration = CMTimeMakeWithSeconds(1.0 / 25,
                                             1000);

AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
[videoComposition setFrameDuration:frameDuration];
[videoComposition setInstructions:@[MainInstruction]];
[videoComposition setAnimationTool:animationTool];
[videoComposition setRenderSize:layerToMove.frame.size];
[videoComposition setRenderScale:1.0];

AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:exportAsset presetName:AVAssetExportPreset1280x720];

[exportSession setOutputFileType:AVFileTypeQuickTimeMovie];
[exportSession setOutputURL:nil/*Any url*/];
[exportSession setVideoComposition:videoComposition];
// The export is complete
[exportSession exportAsynchronouslyWithCompletionHandler:^
 {
     /*Whatever*/
 }];
Community
  • 1
  • 1
0x6368
  • 337
  • 2
  • 17

1 Answers1

0

I found that the only way I could get the transform to work every time was to use the preferredTransform from the AVAssetTrack object as the starting point, then concat your desired transform(s) with that:

AVAsset *asset = [AVAsset assetWithURL:videoURL];
AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,asset.duration) ofTrack:track atTime:kCMTimeZero error:nil];
AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:track];
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

CGAffineTransform transform = track.preferredTransform;

CGAffineTransform scale = CGAffineTransformMakeScale(0.1f,0.1f);
transform = CGAffineTransformConcat(transform, scale);
[layerInstruction setTransform:transform atTime:kCMTimeZero];

I have some code that repositions an image in the middle of a video. This seems to work for me. I set the duration to 0.01 and it animates instantly. There is a bug in the simulator that makes it appear as if the animation doesn't work (sometimes). If you run it on a device it will work:

//animate from the start to end bounds
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
animation.fromValue = [NSValue valueWithCGRect:layerToMove.bounds];
animation.toValue = [NSValue valueWithCGRect:CGRectOffset(layerToMove.bounds, 100, 100)]; //change to the desired new position
animation.duration = 0.01;
animation.beginTime = 1e-100;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

//animate from the start to end center points.
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"position"];
animation2.fromValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(layerToMove.bounds), CGRectGetMidY(layerToMove.bounds))];
animation2.toValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(layerToMove.bounds) + 50, CGRectGetMidY(layerToMove.bounds) + 50)];
animation2.duration = 0.01;
animation2.beginTime = 1e-100;
animation2.removedOnCompletion = NO;
animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

//Create an animation group
CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.duration = 0.01;
group.beginTime = 2.0;
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards; //causes the values that were animated to remain at their end values
group.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
group.animations = @[animation, animation2];

//Add the animation group to the layer
[layerToMove addAnimation:group forKey:@"reposition"];
Brian Shamblen
  • 4,653
  • 1
  • 23
  • 37
  • First, thanks for your answer! But this doesn't seem to work for me. `track.preferredTransform` seems to be equal to `CGAffineTransformIdentity` in my case. – 0x6368 Dec 09 '13 at 21:07
  • 1
    I took a closer look at your code and it looks like you're trying to create a video from a background layer and some other content layer (no video) and move the layer. Is that correct? I'm assuming this because of the method `addBlackVideoTrackOfDuration`. Are you attempting to animate the layer over the course of the video or just position it for rendering, in a static position? – Brian Shamblen Dec 09 '13 at 21:41
  • Yes, that is correct. I attempt to move a layer during the video, but without animation. I tried this with CAAnimations, but the minimum duration of such an animation is 0.25s => it's animated. – 0x6368 Dec 09 '13 at 22:29