Trying a CABasicAnimation on a CALayer with AVAssetExportSession but not succeeding

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;
            }
        });

    }

@stlshbx Thanks very much for your question, and my apologies for the delayed response.

While I personally don’t have an answer for you, I do recommend that you post your question on StackOverflow, and I can almost guarantee you that you will get an answer to your question within 24 hours :slight_smile: Experts in almost every programming language are always waiting to answer the latest questions in their realm, so give it a try!

I hope this helps!

All the best!