private string GetDescription(string lastPathComponent) { string description = string.Empty; if (!Empty) { CMTime startTime = TimeRange.Start; CMTime endTime = CMTime.Add(TimeRange.Start, TimeRange.Duration); string url = lastPathComponent; description = String.Format("{0:0.0} - {1:0.0}: {2}", startTime.Seconds, endTime.Seconds, url); if (MediaType == AVMediaType.Video) { description += "(v)"; } else if (MediaType == AVMediaType.Audio) { description += "(a)"; } else { description += String.Format("({0})", MediaType); } } return(description); }
public void MethodsTest() { CMTime v, w, x, y; v = new CMTime(1, 2); w = new CMTime(1, 2); x = new CMTime(2, 1); y = new CMTime(2, 2); // equality operators Assert.That(v == w, "Equality #1"); Assert.That(!(v == x), "Equality #2"); Assert.That(v != x, "Inequality #1"); Assert.That(!(v != w), "Inequality #2"); Assert.That(CMTime.Compare(v, w), Is.EqualTo(0), "Compare #1"); Assert.That(CMTime.Compare(v, x) != 0, "Compare #2"); Assert.That(v.Equals(w), "Equals #1"); Assert.That(!x.Equals(v), "Equals #2"); // addition operator Assert.That(v + w == new CMTime(2, 2), "Addition #1"); Assert.That(CMTime.Add(v, w) == new CMTime(2, 2), "Addition #2"); // subtraction operator Assert.That(v - w == new CMTime(0, 2), "Subtraction #1"); Assert.That(CMTime.Subtract(v, w) == new CMTime(0, 2), "Subtraction #2"); // multiplication operators Assert.That(v * 2 == new CMTime(2, 2), "Multiplication * int, #1"); Assert.That(CMTime.Multiply(v, 3) == new CMTime(3, 2), "Multiplication * int, #2"); Assert.That(v * 4.0 == new CMTime(4, 2), "Multiplication * double, #1"); Assert.That(CMTime.Multiply(v, 5.0) == new CMTime(5, 2), "Multiplication * double, #2"); // ConvertScale Assert.That(new CMTime(10, 2).ConvertScale(1, CMTimeRoundingMethod.Default) == new CMTime(5, 1), "ConvertScale #1"); // FromSeconds Assert.That(CMTime.FromSeconds(20, 1) == new CMTime(20, 1), "FromSeconds #1"); // GetMaximum Assert.That(CMTime.GetMaximum(v, y) == y, "GetMaximum #1"); // GetMinimum Assert.That(CMTime.GetMinimum(v, y) == v, "GetMinimum #1"); #if XAMCORE_2_0 using (var d = x.ToDictionary()) { Assert.That(d.RetainCount, Is.EqualTo((nint)1), "RetainCount"); Assert.That(d.Count, Is.EqualTo((nuint)4), "Count"); var time = CMTime.FromDictionary(d); Assert.That(time, Is.EqualTo(x), "FromDictionary"); } #endif }
private void ProcessVideoComposition(AVMutableVideoComposition videoComposition) { var stages = new List <APLVideoCompositionStageInfo> (); foreach (AVVideoCompositionInstruction instruction in videoComposition.Instructions) { var stage = new APLVideoCompositionStageInfo(); stage.TimeRange = instruction.TimeRange; var rampsDictionary = new Dictionary <string, List <CGPoint> > (); var layerNames = new List <string> (); foreach (AVVideoCompositionLayerInstruction layerInstruction in instruction.LayerInstructions) { var ramp = new List <CGPoint> (); CMTime startTime = CMTime.Zero; float startOpacity = 1f; float endOpacity = 1f; CMTimeRange timeRange = new CMTimeRange(); while (layerInstruction.GetOpacityRamp(startTime, ref startOpacity, ref endOpacity, ref timeRange)) { if (CMTime.Compare(startTime, CMTime.Zero) == 0 && CMTime.Compare(timeRange.Start, CMTime.Zero) == 1) { ramp.Add(new CGPoint((float)timeRange.Start.Seconds, startOpacity)); } CMTime endTime = CMTime.Add(timeRange.Start, timeRange.Duration); ramp.Add(new CGPoint((float)endTime.Seconds, endOpacity)); startTime = CMTime.Add(timeRange.Start, timeRange.Duration); } NSString name = new NSString(layerInstruction.TrackID.ToString()); layerNames.Add(name); rampsDictionary [name] = ramp; } if (layerNames.Count > 1) { stage.OpacityRamps = rampsDictionary; } stage.LayerNames = layerNames; stages.Add(stage); } videoCompositionStages = stages; }
private void ProcessAudioMix(AVMutableAudioMix audioMix) { var mixTracks = new List <List <CGPoint> > (); foreach (AVAudioMixInputParameters input in audioMix.InputParameters) { List <CGPoint> ramp = new List <CGPoint> (); CMTime startTime = CMTime.Zero; float startVolume = 1f; float endVolume = 1f; CMTimeRange timeRange = new CMTimeRange(); while (input.GetVolumeRamp(startTime, ref startVolume, ref endVolume, ref timeRange)) { if (CMTime.Compare(startTime, CMTime.Zero) == 0 && CMTime.Compare(timeRange.Start, CMTime.Zero) == 1) { ramp.Add(new CGPoint(0f, 1f)); ramp.Add(new CGPoint((float)timeRange.Start.Seconds, startVolume)); } ramp.Add(new CGPoint((float)timeRange.Start.Seconds, startVolume)); CMTime endTime = CMTime.Add(timeRange.Start, timeRange.Duration); ramp.Add(new CGPoint((float)endTime.Seconds, endVolume)); startTime = CMTime.Add(timeRange.Start, timeRange.Duration); } if (CMTime.Compare(startTime, duration) == -1) { ramp.Add(new CGPoint((float)duration.Seconds, endVolume)); } mixTracks.Add(ramp); } audioMixTracks = mixTracks; }
private void BuildTransitionComposition(AVMutableComposition composition, AVMutableVideoComposition videoComposition, AVMutableAudioMix audioMix) { CMTime nextClipStartTime = CMTime.Zero; int clipsCount = Clips.Count; // Make transitionDuration no greater than half the shortest clip duration. CMTime transitionDuration = TransitionDuration; Console.WriteLine("Clips Count:" + clipsCount); Console.WriteLine("Clips Range Count:" + ClipTimeRanges.Count); for (int i = 0; i < clipsCount; i++) { NSValue clipTimeRange = ClipTimeRanges [i]; if (clipTimeRange != null) { CMTime halfClipDuration = clipTimeRange.CMTimeRangeValue.Duration; halfClipDuration.TimeScale *= 2; transitionDuration = CMTime.GetMinimum(transitionDuration, halfClipDuration); } } // Add two video tracks and two audio tracks. var compositionVideoTracks = new AVMutableCompositionTrack [] { composition.AddMutableTrack(AVMediaType.Video, 0), composition.AddMutableTrack(AVMediaType.Video, 0) }; var compositionAudioTracks = new AVMutableCompositionTrack [] { composition.AddMutableTrack(AVMediaType.Audio, 0), composition.AddMutableTrack(AVMediaType.Audio, 0) }; var passThroughTimeRanges = new CMTimeRange[clipsCount]; var transitionTimeRanges = new CMTimeRange[clipsCount]; // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration. for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; AVAsset asset = Clips [i]; NSValue clipTimeRange = ClipTimeRanges [i]; CMTimeRange timeRangeInAsset; if (clipTimeRange != null) { timeRangeInAsset = clipTimeRange.CMTimeRangeValue; } else { timeRangeInAsset = new CMTimeRange(); timeRangeInAsset.Start = CMTime.Zero; timeRangeInAsset.Duration = asset.Duration; } NSError error; AVAssetTrack clipVideoTrack = asset.TracksWithMediaType(AVMediaType.Video) [0]; compositionVideoTracks [alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out error); AVAssetTrack clipAudioTrack = asset.TracksWithMediaType(AVMediaType.Audio) [0]; compositionAudioTracks [alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out error); // Remember the time range in which this clip should pass through. // First clip ends with a transition. // Second clip begins with a transition. // Exclude that transition from the pass through time ranges CMTimeRange timeRange = new CMTimeRange(); timeRange.Start = nextClipStartTime; timeRange.Duration = timeRangeInAsset.Duration; passThroughTimeRanges [i] = timeRange; if (i > 0) { passThroughTimeRanges[i].Start = CMTime.Add(passThroughTimeRanges[i].Start, transitionDuration); passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } if (i + 1 < clipsCount) { passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } // The end of this clip will overlap the start of the next by transitionDuration. // (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.) nextClipStartTime = CMTime.Add(nextClipStartTime, timeRangeInAsset.Duration); nextClipStartTime = CMTime.Subtract(nextClipStartTime, transitionDuration); // Remember the time range for the transition to the next item if (i + 1 < clipsCount) { transitionTimeRanges [i] = new CMTimeRange() { Start = nextClipStartTime, Duration = transitionDuration }; } } List <AVVideoCompositionInstruction> instructions = new List <AVVideoCompositionInstruction> (); List <AVMutableAudioMixInputParameters> trackMixArray = new List <AVMutableAudioMixInputParameters> (); // Set up the video composition if we are to perform crossfade transitions between clips. for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; AVMutableVideoCompositionInstruction passThroughInstructions = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; passThroughInstructions.TimeRange = passThroughTimeRanges [i]; AVMutableVideoCompositionLayerInstruction passThroughLayerInstructions = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [alternatingIndex]); passThroughInstructions.LayerInstructions = new AVVideoCompositionLayerInstruction[] { passThroughLayerInstructions }; instructions.Add(passThroughInstructions); if (i + 1 < clipsCount) { var transitionInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; transitionInstruction.TimeRange = transitionTimeRanges [i]; var fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [alternatingIndex]); var toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [1 - alternatingIndex]); // Fade in the toLayer by setting a ramp from 0.0 to 1.0. toLayer.SetOpacityRamp(0.0f, 1.0f, transitionTimeRanges [i]); transitionInstruction.LayerInstructions = new AVVideoCompositionLayerInstruction[] { toLayer, fromLayer, }; instructions.Add(transitionInstruction); // Add AudioMix to fade in the volume ramps var trackMix = AVMutableAudioMixInputParameters.FromTrack(compositionAudioTracks[0]); trackMix.SetVolumeRamp(1f, 0f, transitionTimeRanges[0]); trackMixArray.Add(trackMix); trackMix = AVMutableAudioMixInputParameters.FromTrack(compositionAudioTracks[1]); trackMix.SetVolumeRamp(0f, 1f, transitionTimeRanges[0]); trackMix.SetVolumeRamp(1f, 1f, passThroughTimeRanges[1]); trackMixArray.Add(trackMix); } } videoComposition.Instructions = instructions.ToArray(); audioMix.InputParameters = trackMixArray.ToArray(); }
void buildTransitionComposition(AVMutableComposition composition, AVMutableVideoComposition videoComposition) { CMTime nextClipStartTime = CMTime.Zero; int clipsCount = Clips.Count; // Make transitionDuration no greater than half the shortest clip duration. CMTime transitionDuration = TransitionDuration; foreach (var clipTimeRange in ClipTimeRanges) { if (clipTimeRange == null) { continue; } CMTime halfClipDuration = clipTimeRange.CMTimeRangeValue.Duration; halfClipDuration.TimeScale *= 2; transitionDuration = CMTime.GetMinimum(transitionDuration, halfClipDuration); } // Add two video tracks and two audio tracks. var compositionVideoTracks = new AVMutableCompositionTrack [2]; var compositionAudioTracks = new AVMutableCompositionTrack [2]; compositionVideoTracks [0] = composition.AddMutableTrack(AVMediaType.Video, 0); compositionVideoTracks [1] = composition.AddMutableTrack(AVMediaType.Video, 0); compositionAudioTracks [0] = composition.AddMutableTrack(AVMediaType.Audio, 0); compositionAudioTracks [1] = composition.AddMutableTrack(AVMediaType.Audio, 0); var passThroughTimeRanges = new CMTimeRange[clipsCount]; var transitionTimeRanges = new CMTimeRange[clipsCount]; // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration. for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; AVAsset asset = Clips [i]; NSValue clipTimeRange = ClipTimeRanges [i]; CMTimeRange timeRangeInAsset; if (clipTimeRange != null) { timeRangeInAsset = clipTimeRange.CMTimeRangeValue; } else { timeRangeInAsset = new CMTimeRange { Start = CMTime.Zero, Duration = asset.Duration }; } NSError error = new NSError(); AVAssetTrack clipVideoTrack = asset.TracksWithMediaType(AVMediaType.Video) [0]; compositionVideoTracks [alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out error); AVAssetTrack clipAudioTrack = asset.TracksWithMediaType(AVMediaType.Audio) [0]; compositionAudioTracks [alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out error); // Remember the time range in which this clip should pass through. // First clip ends with a transition. // Second clip begins with a transition. // Exclude that transition from the pass through time ranges passThroughTimeRanges [i] = new CMTimeRange { Start = nextClipStartTime, Duration = timeRangeInAsset.Duration }; if (i > 0) { passThroughTimeRanges[i].Start = CMTime.Add(passThroughTimeRanges[i].Start, transitionDuration); passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } if (i + 1 < clipsCount) { passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } // The end of this clip will overlap the start of the next by transitionDuration. // (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.) nextClipStartTime = CMTime.Add(nextClipStartTime, timeRangeInAsset.Duration); nextClipStartTime = CMTime.Subtract(nextClipStartTime, transitionDuration); // Remember the time range for the transition to the next item. if (i + 1 < clipsCount) { transitionTimeRanges [i] = new CMTimeRange() { Start = nextClipStartTime, Duration = transitionDuration }; } } // Set up the video composition to perform cross dissolve or diagonal wipe transitions between clips. var instructions = new List <AVVideoCompositionInstruction> (); // Cycle between "pass through A", "transition from A to B", "pass through B" for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; // if (videoComposition.CustomVideoCompositorClass != null) { // var videoInstruction = new CustomVideoCompositionInstruction (compositionVideoTracks [alternatingIndex].TrackID, passThroughTimeRanges [i]); // instructions.Add (videoInstruction); // } else { // // Pass through clip i. // var passThroughInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction; // passThroughInstruction.TimeRange = passThroughTimeRanges [i]; // var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]); // passThroughInstruction.LayerInstructions = new [] { passThroughLayer }; // instructions.Add (passThroughInstruction); // // } //TODO: Remove following call if previous works if (videoComposition.CustomVideoCompositorClass.Name != "nil") { var videoInstruction = new CustomVideoCompositionInstruction(compositionVideoTracks [alternatingIndex].TrackID, passThroughTimeRanges [i]); instructions.Add(videoInstruction); } else { // Pass through clip i. var passThroughInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; passThroughInstruction.TimeRange = passThroughTimeRanges [i]; var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [alternatingIndex]); passThroughInstruction.LayerInstructions = new [] { passThroughLayer }; instructions.Add(passThroughInstruction); } if (i + 1 < clipsCount) { // Add transition from clip i to clip i+1. // if (videoComposition.CustomVideoCompositorClass != null) { // var videoInstruction = new CustomVideoCompositionInstruction (new NSNumber [] { // compositionVideoTracks [0].TrackID, // compositionVideoTracks [1].TrackID // }, transitionTimeRanges [1]); // // if (alternatingIndex == 0) { // videoInstruction.ForegroundTrackID = compositionVideoTracks [alternatingIndex].TrackID; // videoInstruction.BackgroundTrackID = compositionVideoTracks [1 - alternatingIndex].TrackID; // } // // instructions.Add (videoInstruction); // } else { // var transitionInstruction = AVMutableVideoCompositionInstruction.Create () as AVMutableVideoCompositionInstruction; // transitionInstruction.TimeRange = transitionTimeRanges [i]; // var fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [alternatingIndex]); // var toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack (compositionVideoTracks [1 - alternatingIndex]); // transitionInstruction.LayerInstructions = new [] { toLayer, fromLayer }; // instructions.Add (transitionInstruction); // } // TODO: remove following call if previous works if (videoComposition.CustomVideoCompositorClass.Name != "nil") { NSNumber[] sources = new NSNumber[] { new NSNumber(compositionVideoTracks [0].TrackID), new NSNumber(compositionVideoTracks [1].TrackID) }; var videoInstructions = new CustomVideoCompositionInstruction(sources, transitionTimeRanges [i]); if (alternatingIndex == 0) { videoInstructions.ForegroundTrackID = compositionVideoTracks [alternatingIndex].TrackID; videoInstructions.BackgroundTrackID = compositionVideoTracks [1 - alternatingIndex].TrackID; } instructions.Add(videoInstructions); Console.WriteLine("Add transition from clip i to clip i+1"); } else { AVMutableVideoCompositionInstruction transitionInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; transitionInstruction.TimeRange = transitionTimeRanges [i]; AVMutableVideoCompositionLayerInstruction fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [alternatingIndex]); AVMutableVideoCompositionLayerInstruction toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks [1 - alternatingIndex]); transitionInstruction.LayerInstructions = new AVVideoCompositionLayerInstruction[] { fromLayer, toLayer, }; instructions.Add(transitionInstruction); } } } videoComposition.Instructions = instructions.ToArray(); }
private void BuildTransitionComposition(AVMutableComposition mutableComposition, AVMutableVideoComposition mutableVideoComposition) { var nextClipStartTime = CMTime.Zero; var clipsCount = this.Clips.Count; // Make transitionDuration no greater than half the shortest clip duration. var transitionDuration = this.TransitionDuration; foreach (var clipTimeRange in this.ClipTimeRanges) { var halfClipDuration = clipTimeRange.Duration; halfClipDuration.TimeScale *= 2; // You can halve a rational by doubling its denominator. transitionDuration = CMTime.GetMinimum(transitionDuration, halfClipDuration); } // Add two video tracks and two audio tracks. var compositionVideoTracks = new AVMutableCompositionTrack[2]; var compositionAudioTracks = new AVMutableCompositionTrack[2]; compositionVideoTracks[0] = mutableComposition.AddMutableTrack(AVMediaType.Video, 0); compositionVideoTracks[1] = mutableComposition.AddMutableTrack(AVMediaType.Video, 0); compositionAudioTracks[0] = mutableComposition.AddMutableTrack(AVMediaType.Audio, 0); compositionAudioTracks[1] = mutableComposition.AddMutableTrack(AVMediaType.Audio, 0); var passThroughTimeRanges = new CMTimeRange[clipsCount]; var transitionTimeRanges = new CMTimeRange[clipsCount]; // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration. for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; // alternating targets: 0, 1, 0, 1, ... var asset = this.Clips[i]; var timeRangeInAsset = this.ClipTimeRanges[i]; var clipVideoTrack = asset.TracksWithMediaType(AVMediaType.Video)[0]; compositionVideoTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out _); var clipAudioTrack = asset.TracksWithMediaType(AVMediaType.Audio)[0]; compositionAudioTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out _); // Remember the time range in which this clip should pass through. // First clip ends with a transition. // Second clip begins with a transition. // Exclude that transition from the pass through time ranges passThroughTimeRanges[i] = new CMTimeRange { Start = nextClipStartTime, Duration = timeRangeInAsset.Duration }; if (i > 0) { passThroughTimeRanges[i].Start = CMTime.Add(passThroughTimeRanges[i].Start, transitionDuration); passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } if (i + 1 < clipsCount) { passThroughTimeRanges[i].Duration = CMTime.Subtract(passThroughTimeRanges[i].Duration, transitionDuration); } // The end of this clip will overlap the start of the next by transitionDuration. // (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.) nextClipStartTime = CMTime.Add(nextClipStartTime, timeRangeInAsset.Duration); nextClipStartTime = CMTime.Subtract(nextClipStartTime, transitionDuration); // Remember the time range for the transition to the next item. if (i + 1 < clipsCount) { transitionTimeRanges[i] = new CMTimeRange { Start = nextClipStartTime, Duration = transitionDuration }; } } // Set up the video composition to perform cross dissolve or diagonal wipe transitions between clips. var instructions = new List <AVVideoCompositionInstruction>(); // Cycle between "pass through A", "transition from A to B", "pass through B" for (int i = 0; i < clipsCount; i++) { int alternatingIndex = i % 2; // alternating targets if (mutableVideoComposition.CustomVideoCompositorClass != null) { var videoInstruction = new CustomVideoCompositionInstruction(compositionVideoTracks[alternatingIndex].TrackID, passThroughTimeRanges[i]); instructions.Add(videoInstruction); } else { // Pass through clip i. var passThroughInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; passThroughInstruction.TimeRange = passThroughTimeRanges[i]; var passThroughLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[alternatingIndex]); passThroughInstruction.LayerInstructions = new[] { passThroughLayer }; instructions.Add(passThroughInstruction); } if (i + 1 < clipsCount) { // Add transition from clip i to clip i+1. if (mutableVideoComposition.CustomVideoCompositorClass != null) { var videoInstruction = new CustomVideoCompositionInstruction(new NSNumber[] { compositionVideoTracks[0].TrackID, compositionVideoTracks[1].TrackID }, transitionTimeRanges[i]); if (alternatingIndex == 0) { // First track -> Foreground track while compositing videoInstruction.ForegroundTrackId = compositionVideoTracks[alternatingIndex].TrackID; // Second track -> Background track while compositing videoInstruction.BackgroundTrackId = compositionVideoTracks[1 - alternatingIndex].TrackID; } instructions.Add(videoInstruction); } else { var transitionInstruction = AVMutableVideoCompositionInstruction.Create() as AVMutableVideoCompositionInstruction; transitionInstruction.TimeRange = transitionTimeRanges[i]; var fromLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[alternatingIndex]); var toLayer = AVMutableVideoCompositionLayerInstruction.FromAssetTrack(compositionVideoTracks[1 - alternatingIndex]); transitionInstruction.LayerInstructions = new[] { toLayer, fromLayer }; instructions.Add(transitionInstruction); } } } mutableVideoComposition.Instructions = instructions.ToArray(); }