[avcustomedit] Some timing and simplification fixes (#349)

Running this sample on device with the interpreter has spotted a few
issues in the sample.

* Timing issue: Use `DispatchGroup.Notify` (like sample) instead of
  `Waiting` (it's not the same) and remove the `InvokeOnMainThread`
  since we're already running on that thread. Also uncomment some
  code that, likely, did not work because `Notify` was not used;

* Timing issue: `composition` can be `null` but it triggers an
  `ArgumentNullException` (because the API does not _officially_ accept
   `nil`).

* Enhancement: Set initial capacity of `List<T>` - just like it's
  done in the original sample;

* Simplification: There's no point in checking for `null` values inside
  `List<T>` since that would have triggered an `ArgumentNullException`;

* Simplification" There's no point in converting `CMTimeRange` into
  `NSValue` (and back into `CMTimeRange`). In contrast to `NSArray`
  we can add non-`NSObject` (including value type like struct) inside
  `List<T>`;

* Cleanup: remove ugly `goto` and use `_` for unused `out NSError`

This will fix https://github.com/xamarin/xamarin-macios/issues/5364
This commit is contained in:
Sebastien Pouliot 2019-01-15 14:25:52 -05:00 коммит произвёл GitHub
Родитель beb972af1f
Коммит df1b3e3a62
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 35 добавлений и 58 удалений

Просмотреть файл

@ -13,13 +13,19 @@ namespace AVCustomEdit
public List<AVAsset> Clips { get; set; } // array of AVURLAssets
public List<NSValue> ClipTimeRanges { get; set; } // array of CMTimeRanges stored in NSValues.
public List<CMTimeRange> ClipTimeRanges { get; set; }
public TransitionType TransitionType { get; set; }
public CMTime TransitionDuration { get; set; }
public AVPlayerItem PlayerItem => new AVPlayerItem(this.composition) { VideoComposition = this.videoComposition };
public AVPlayerItem PlayerItem {
get {
if (composition == null)
return null;
return new AVPlayerItem(this.composition) { VideoComposition = this.videoComposition };
}
}
private void BuildTransitionComposition(AVMutableComposition mutableComposition, AVMutableVideoComposition mutableVideoComposition)
{
@ -30,12 +36,9 @@ namespace AVCustomEdit
var transitionDuration = this.TransitionDuration;
foreach (var clipTimeRange in this.ClipTimeRanges)
{
if (clipTimeRange != null)
{
var halfClipDuration = clipTimeRange.CMTimeRangeValue.Duration;
halfClipDuration.TimeScale *= 2; // You can halve a rational by doubling its denominator.
transitionDuration = CMTime.GetMinimum(transitionDuration, halfClipDuration);
}
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.
@ -55,16 +58,13 @@ namespace AVCustomEdit
{
int alternatingIndex = i % 2; // alternating targets: 0, 1, 0, 1, ...
var asset = this.Clips[i];
var clipTimeRange = this.ClipTimeRanges[i];
var timeRangeInAsset = clipTimeRange != null ? clipTimeRange.CMTimeRangeValue
: new CMTimeRange { Start = CMTime.Zero, Duration = asset.Duration };
var timeRangeInAsset = this.ClipTimeRanges[i];
var clipVideoTrack = asset.TracksWithMediaType(AVMediaType.Video)[0];
compositionVideoTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out NSError error);
compositionVideoTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipVideoTrack, nextClipStartTime, out _);
var clipAudioTrack = asset.TracksWithMediaType(AVMediaType.Audio)[0];
compositionAudioTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out error);
compositionAudioTracks[alternatingIndex].InsertTimeRange(timeRangeInAsset, clipAudioTrack, nextClipStartTime, out _);
// Remember the time range in which this clip should pass through.
// First clip ends with a transition.

Просмотреть файл

@ -19,7 +19,7 @@ namespace AVCustomEdit
private SimpleEditor editor;
private List<AVAsset> clips;
private List<NSValue> clipTimeRanges;
private List<CMTimeRange> clipTimeRanges;
private AVPlayer player;
private AVPlayerItem playerItem;
@ -44,8 +44,8 @@ namespace AVCustomEdit
base.ViewDidLoad();
this.editor = new SimpleEditor();
this.clips = new List<AVAsset>();
this.clipTimeRanges = new List<NSValue>();
this.clips = new List<AVAsset>(2);
this.clipTimeRanges = new List<CMTimeRange>(2);
// Defaults for the transition settings.
this.transitionType = TransitionType.DiagonalWipeTransition;
@ -115,8 +115,9 @@ namespace AVCustomEdit
this.LoadAsset(asset2, assetKeysToLoadAndTest, dispatchGroup);
// Wait until both assets are loaded
dispatchGroup.Wait(DispatchTime.Forever);
base.InvokeOnMainThread(() => this.SynchronizeWithEditor());
dispatchGroup.Notify(DispatchQueue.MainQueue, () => {
SynchronizeWithEditor();
});
}
private void LoadAsset(AVAsset asset, string[] assetKeysToLoad, DispatchGroup dispatchGroup)
@ -124,28 +125,32 @@ namespace AVCustomEdit
dispatchGroup.Enter();
asset.LoadValuesAsynchronously(assetKeysToLoad, () =>
{
bool add_asset = true;
// First test whether the values of each of the keys we need have been successfully loaded.
foreach (var key in assetKeysToLoad)
{
if (asset.StatusOfValue(key, out NSError error) == AVKeyValueStatus.Failed)
{
Console.WriteLine($"Key value loading failed for key:{key} with error: {error?.LocalizedDescription ?? ""}");
goto bail;
add_asset = false;
break;
}
}
if (!asset.Composable)
{
Console.WriteLine("Asset is not composable");
goto bail;
add_asset = false;
}
this.clips.Add(asset);
// This code assumes that both assets are atleast 5 seconds long.
var value = NSValue.FromCMTimeRange(new CMTimeRange { Start = CMTime.FromSeconds(0, 1), Duration = CMTime.FromSeconds(5, 1) });
this.clipTimeRanges.Add(value);
if (add_asset)
{
this.clips.Add(asset);
// This code assumes that both assets are atleast 5 seconds long.
var value = new CMTimeRange { Start = CMTime.FromSeconds(0, 1), Duration = CMTime.FromSeconds(5, 1) };
this.clipTimeRanges.Add(value);
}
bail:
dispatchGroup.Leave();
});
}
@ -185,8 +190,8 @@ namespace AVCustomEdit
private void SynchronizeWithEditor()
{
// Clips
this.SynchronizeEditorClipsWithOurClips();
this.SynchronizeEditorClipTimeRangesWithOurClipTimeRanges();
this.editor.Clips = new List<AVAsset> (clips);
this.editor.ClipTimeRanges = new List<CMTimeRange> (clipTimeRanges);
// Transitions
if (this.isTransitionsEnabled)
@ -200,36 +205,8 @@ namespace AVCustomEdit
}
// Build AVComposition and AVVideoComposition objects for playback
//this.editor.BuildCompositionObjectsForPlayback(true);
//this.SynchronizePlayerWithEditor();
}
private void SynchronizeEditorClipsWithOurClips()
{
var validClips = new List<AVAsset>();
foreach (var asset in this.clips)
{
if (asset != null)
{
validClips.Add(asset);
}
}
this.editor.Clips = validClips;
}
private void SynchronizeEditorClipTimeRangesWithOurClipTimeRanges()
{
var validClipTimeRanges = new List<NSValue>();
foreach (var timeRange in this.clipTimeRanges)
{
if (timeRange != null)
{
validClipTimeRanges.Add(timeRange);
}
}
this.editor.ClipTimeRanges = validClipTimeRanges;
this.editor.BuildCompositionObjectsForPlayback(true);
this.SynchronizePlayerWithEditor();
}
#endregion