I need your help regarding an animation I want to apply to an overlay image I add to a mp4 video. To add an overlay on a video, I followed Ray Wenderlich tutorials here: https://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos. At the end of that link, an animation is created on an overlay.
Ray Wenderlich is not coding with c# but I do since I use Xamarin to create my app. Using the animation, the overlay is supposed to fade away during the very first seconds of the video. But nothing happens. I mean nothing animated happens except for the video: the overlay is well put on top of the video but it does not fade away.
So I am asking myself: is the animation not working because I use c# or did I write something wrong?
Can somebody help me with this, please?
This is the function I wrote:
public static void addFadeOverlayAtTheBeginning (NSUrl source, String overlay, long secondsToFade, NSUrl destination)
{
AVUrlAsset videoAsset = new AVUrlAsset(source);
if (secondsToFade > videoAsset.Duration.Seconds)
secondsToFade = (long)videoAsset.Duration.Seconds;
CALayer overlayLayer = CALayer.Create();
UIImage image = new UIImage(overlay);
image.CreateResizableImage(new UIEdgeInsets(0, 0, videoAsset.NaturalSize.Width, videoAsset.NaturalSize.Height));
overlayLayer.Contents = image.CGImage;
overlayLayer.Frame = new CGRect(0, 0, videoAsset.NaturalSize.Width, videoAsset.NaturalSize.Height);
overlayLayer.MasksToBounds = true;
//overlayLayer.Opacity = 0.65F;
//On crée l'animation pour le CAlayer
CABasicAnimation animation = CABasicAnimation.FromKeyPath(@"opacity");
//CABasicAnimation.FromKeyPath (@"opacity");
//animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseIn);
animation.BeginTime = 0; // GetSeconds(image.CGImage.StartTime);
animation.Duration = secondsToFade;
animation.SetFrom (NSNumber.FromFloat (1));
animation.SetTo (NSNumber.FromFloat(0));
animation.FillMode = CAFillMode.Forwards;
animation.RepeatCount = 1;
animation.AutoReverses = false;
//animation.
//animation.RemovedOnCompletion = false;
//animation.FillMode = CAFillMode.Both;
//animation.Additive = true;
//animation.Values = new NSObject[] {
//NSNumber.FromFloat (1f),
//NSNumber.FromFloat (0.75f),
//NSNumber.FromFloat (0.5f ),
//NSNumber.FromFloat (0.25f ),
//NSNumber.FromFloat (0f)};
//on ajoute l'animation au layer
overlayLayer.AddAnimation(animation, @"animateOpacity");
AVMutableComposition videoComposition = AVMutableComposition.Create();
AVMutableCompositionTrack track = videoComposition.AddMutableTrack(AVMediaType.Video, 0);
AVAssetTrack sourceVideoTrack = videoAsset.TracksWithMediaType(AVMediaType.Video)[0];
CMTimeRange timeRangeInAsset = new CMTimeRange();
timeRangeInAsset.Start = CMTime.Zero;
timeRangeInAsset.Duration = videoAsset.Duration;
NSError videoInsertError = null;
track.InsertTimeRange(timeRangeInAsset, sourceVideoTrack, CMTime.Zero, out videoInsertError);
track.PreferredTransform = sourceVideoTrack.PreferredTransform;
CALayer parentLayer = CALayer.Create();
CALayer videoLayer = CALayer.Create();
parentLayer.Frame = new CGRect(0, 0, videoAsset.NaturalSize.Width, videoAsset.NaturalSize.Height);
videoLayer.Frame = new CGRect(0, 0, videoAsset.NaturalSize.Width, videoAsset.NaturalSize.Height);
parentLayer.AddSublayer(videoLayer);
parentLayer.AddSublayer(overlayLayer);
//parentLayer.AddAnimation(animation, "opacity");
AVMutableVideoComposition animationComposition = AVMutableVideoComposition.Create(); //videoComposition
animationComposition.AnimationTool = AVVideoCompositionCoreAnimationTool.FromLayer(videoLayer, parentLayer);
animationComposition.RenderSize = videoAsset.NaturalSize;
animationComposition.FrameDuration = new CMTime(1, 30);
AVMutableVideoCompositionInstruction instruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction;
CMTimeRange timeRangeInstruction = new CMTimeRange();
timeRangeInstruction.Start = CMTime.Zero;
timeRangeInstruction.Duration = videoComposition.Duration;
instruction.TimeRange = timeRangeInstruction;
AVMutableVideoCompositionLayerInstruction layerInstruction = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(track);
CMTimeRange timeRangeFading = new CMTimeRange();
timeRangeFading.Start = CMTime.Zero;
timeRangeFading.Duration = new CMTime(secondsToFade, 1);
//layerInstruction.SetOpacityRamp(1, 0, timeRangeFading);
instruction.LayerInstructions = new AVVideoCompositionLayerInstruction[] { layerInstruction };
List<AVVideoCompositionInstruction> instructions = new List<AVVideoCompositionInstruction>();
instructions.Add(instruction);
animationComposition.Instructions = instructions.ToArray();
if (File.Exists(destination.Path))
File.Delete(destination.Path);
AVAssetExportSession exporter = new AVAssetExportSession(videoComposition, AVAssetExportSession.PresetHighestQuality);
exporter.OutputUrl = destination;
exporter.VideoComposition = animationComposition;
exporter.OutputFileType = AVFileType.Mpeg4;
exporter.ExportAsynchronously(() => {
VideoManagement.state ++;
AVAssetExportSessionStatus status = exporter.Status;
Console.WriteLine("addFadeOverlayAtTheBeginning Done. Status: " + status.ToString());
switch (status)
{
case AVAssetExportSessionStatus.Completed:
Console.WriteLine("Sucessfully Completed");
if (File.Exists(destination.Path))
{
Console.WriteLine(String.Format("Saved to {0}", destination.Path));
}
else
Console.WriteLine("Failed");
break;
case AVAssetExportSessionStatus.Cancelled:
break;
case AVAssetExportSessionStatus.Exporting:
break;
case AVAssetExportSessionStatus.Failed:
Console.WriteLine("Task failed => {0}", exporter.Error);
Console.WriteLine(exporter.Error.Description);
break;
case AVAssetExportSessionStatus.Unknown:
break;
case AVAssetExportSessionStatus.Waiting:
break;
default:
break;
}
});
}