private async Task InvokeAsyncInternal(DispatcherPriority priority, Delegate callback, params object[] arguments) { if (Thread == Thread.CurrentThread) { callback.DynamicInvoke(arguments); return; } try { // We try here because we'd like to catch cancellations and ignore then switch (Type) { case GuiContextType.WPF: { await GuiDispatcher.InvokeAsync(() => { callback.DynamicInvoke(arguments); }, priority); return; } case GuiContextType.WinForms: { var doneEvent = WaitEventFactory.Create(isCompleted: false, useSlim: true); ThreadContext.Post(a => { try { callback.DynamicInvoke(arguments); } finally { doneEvent.Complete(); } }, null); var waitingTask = new Task(() => { doneEvent.Wait(); doneEvent.Dispose(); }); var awaiter = waitingTask.ConfigureAwait(false); waitingTask.Start(); await awaiter; return; } default: { var runnerTask = new Task(() => { callback.DynamicInvoke(arguments); }); var awaiter = runnerTask.ConfigureAwait(false); runnerTask.Start(); await awaiter; return; } } } catch (OperationCanceledException) { // Ignore cancellation Debug.WriteLine($"FFME {nameof(GuiContext)}.{nameof(InvokeAsyncInternal)}: Operation was cancelled"); } }
private async Task InvokeAsyncInternal(DispatcherPriority priority, Delegate callback, params object[] arguments) { if (Thread == Thread.CurrentThread) { callback.DynamicInvoke(arguments); return; } switch (Type) { case GuiContextType.WPF: { await GuiDispatcher.InvokeAsync(() => { callback.DynamicInvoke(arguments); }, priority); return; } case GuiContextType.WinForms: { var doneEvent = WaitEventFactory.Create(isCompleted: false, useSlim: true); ThreadContext.Post((args) => { try { callback.DynamicInvoke(args as object[]); } catch { throw; } finally { doneEvent.Complete(); } }, arguments); var waitingTask = new Task(() => { doneEvent.Wait(); doneEvent.Dispose(); }); var awaiter = waitingTask.ConfigureAwait(false); waitingTask.Start(); await awaiter; return; } case GuiContextType.None: default: { var runnerTask = new Task(() => { callback.DynamicInvoke(arguments); }); var awaiter = runnerTask.ConfigureAwait(false); runnerTask.Start(); await awaiter; return; } } }
public void IfDisposed_IsInProgressEqualsFalse() { var wait = WaitEventFactory.Create(true); wait.Begin(); wait.Dispose(); wait.Begin(); Assert.IsFalse(wait.IsInProgress); Assert.IsFalse(wait.IsValid); }
public void NormalCycle(bool initialValue, bool useSlim) { var wait = WaitEventFactory.Create(initialValue, useSlim); Assert.IsTrue(wait.IsCompleted); wait.Begin(); Assert.IsFalse(wait.IsCompleted); Assert.IsTrue(wait.IsInProgress); wait.Complete(); Assert.IsTrue(wait.IsCompleted); Assert.IsFalse(wait.IsInProgress); }
public async Task InvokeAsync(DispatcherPriority priority, Delegate callback, params object[] arguments) { if (ContextThread == Thread.CurrentThread) { callback.DynamicInvoke(arguments); return; } switch (ContextType) { case GuiContextType.None: { await Task.Run(() => { callback.DynamicInvoke(arguments); }); return; } case GuiContextType.WPF: { await GuiDispatcher.InvokeAsync(() => { callback.DynamicInvoke(arguments); }, priority); return; } case GuiContextType.WinForms: { var doneEvent = WaitEventFactory.Create(isCompleted: false, useSlim: true); Context.Post((args) => { try { callback.DynamicInvoke(args as object[]); } catch { throw; } finally { doneEvent.Complete(); } }, arguments); await Task.Run(() => { doneEvent.Wait(); doneEvent.Dispose(); }); return; } } }
/// <summary> /// Starts the block rendering worker. /// </summary> private void StartBlockRenderingWorker() { if (BlockRenderingWorkerExit != null) { return; } BlockRenderingWorkerExit = WaitEventFactory.Create(isCompleted: false, useSlim: true); // Holds the main media type var main = Container.Components.MainMediaType; // Holds all components var all = Renderers.Keys.ToArray(); // Holds a snapshot of the current block to render var currentBlock = new MediaTypeDictionary <MediaBlock>(); // Keeps track of how many blocks were rendered in the cycle. var renderedBlockCount = new MediaTypeDictionary <int>(); // wait for main component blocks or EOF or cancellation pending while (CanReadMoreFramesOf(main) && Blocks[main].Count <= 0) { FrameDecodingCycle.Wait(Constants.Interval.LowPriority); } // Set the initial clock position var wallClock = ChangePosition(Blocks[main].RangeStartTime); // Wait for renderers to be ready foreach (var t in all) { Renderers[t]?.WaitForReadyState(); } // The Render timer is responsible for sending frames to renders BlockRenderingWorker = new Timer((s) => { #region Detect Exit/Skip Conditions if (Commands.IsStopWorkersPending || BlockRenderingWorkerExit.IsCompleted || IsDisposed) { BlockRenderingWorkerExit?.Complete(); return; } // Skip the cycle if it's already running if (BlockRenderingCycle.IsInProgress) { Log(MediaLogMessageType.Trace, $"SKIP: {nameof(BlockRenderingWorker)} already in a cycle. {WallClock}"); return; } #endregion #region Run the Rendering Cycle try { #region 1. Control and Capture // Wait for the seek op to finish before we capture blocks Commands.WaitForActiveSeekCommand(); // Signal the start of a block rendering cycle BlockRenderingCycle.Begin(); // Skip the cycle if we are running a priority command if (Commands.IsExecutingDirectCommand) { return; } // Updatete Status Properties main = Container.Components.MainMediaType; all = Renderers.Keys.ToArray(); // Reset the rendered count to 0 foreach (var t in all) { renderedBlockCount[t] = 0; } #endregion #region 2. Handle Block Rendering // capture the wall clock for this cycle wallClock = WallClock; // Capture the blocks to render foreach (var t in all) { // Get the audio, video, or subtitle block to render currentBlock[t] = (t == MediaType.Subtitle && PreloadedSubtitles != null) ? PreloadedSubtitles[wallClock] : currentBlock[t] = Blocks[t][wallClock]; } // Render each of the Media Types if it is time to do so. foreach (var t in all) { // Skip rendering for nulls if (currentBlock[t] == null || currentBlock[t].IsDisposed) { continue; } // Render by forced signal (TimeSpan.MinValue) or because simply it is time to do so if (LastRenderTime[t] == TimeSpan.MinValue || currentBlock[t].StartTime != LastRenderTime[t]) { renderedBlockCount[t] += SendBlockToRenderer(currentBlock[t], wallClock, main); } } #endregion #region 3. Finalize the Rendering Cycle // Call the update method on all renderers so they receive what the new wall clock is. foreach (var t in all) { Renderers[t]?.Update(wallClock); } #endregion } catch { throw; } finally { // Update the Position if (IsWorkerInterruptRequested == false && IsSyncBuffering == false) { State.UpdatePosition(Clock.IsRunning ? wallClock : Clock.Position); } // Always exit notifying the cycle is done. BlockRenderingCycle.Complete(); } #endregion }, this, // the state argument passed on to the ticker 0, Convert.ToInt32(Constants.Interval.HighPriority.TotalMilliseconds)); }
/// <summary> /// Initializes a new instance of the <see cref="CommandManager" /> class. /// </summary> /// <param name="mediaCore">The media core.</param> public CommandManager(MediaEngine mediaCore) { DirectCommandEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true); SeekingDone = WaitEventFactory.Create(isCompleted: true, useSlim: true); MediaCore = mediaCore; }
/// <summary> /// Starts the block rendering worker. /// </summary> private void StartBlockRenderingWorker() { if (BlockRenderingWorkerExit != null) { return; } BlockRenderingWorkerExit = WaitEventFactory.Create(isCompleted: false, useSlim: true); // Synchronized access to parts of the run cycle var isRunningRenderingCycle = false; // Holds the main media type var main = Container.Components.Main.MediaType; // Holds all components var all = Renderers.Keys.ToArray(); // Holds a snapshot of the current block to render var currentBlock = new MediaTypeDictionary <MediaBlock>(); // Keeps track of how many blocks were rendered in the cycle. var renderedBlockCount = new MediaTypeDictionary <int>(); // reset render times for all components foreach (var t in all) { LastRenderTime[t] = TimeSpan.MinValue; } // Ensure packet reading is running PacketReadingCycle.Wait(); // wait for main component blocks or EOF or cancellation pending while (CanReadMoreFramesOf(main) && Blocks[main].Count <= 0) { FrameDecodingCycle.Wait(); } // Set the initial clock position Clock.Update(Blocks[main].RangeStartTime); var wallClock = WallClock; // Wait for renderers to be ready foreach (var t in all) { Renderers[t]?.WaitForReadyState(); } // The Render timer is responsible for sending frames to renders BlockRenderingWorker = new Timer((s) => { #region Detect a Timer Stop if (IsTaskCancellationPending || BlockRenderingWorkerExit.IsCompleted || IsDisposed) { BlockRenderingWorkerExit.Complete(); return; } #endregion #region Run the Rendering Cycle // Updatete Status Properties State.UpdateBufferingProperties(); // Don't run the cycle if it's already running if (isRunningRenderingCycle) { Log(MediaLogMessageType.Trace, $"SKIP: {nameof(BlockRenderingWorker)} alredy in a cycle. {WallClock}"); return; } try { #region 1. Control and Capture // Flag the current rendering cycle isRunningRenderingCycle = true; // Reset the rendered count to 0 foreach (var t in all) { renderedBlockCount[t] = 0; } // Capture current clock position for the rest of this cycle BlockRenderingCycle.Begin(); #endregion #region 2. Handle Block Rendering // Wait for the seek op to finish before we capture blocks if (HasDecoderSeeked) { SeekingDone.Wait(); } // capture the wall clock for this cycle wallClock = WallClock; // Capture the blocks to render foreach (var t in all) { if (t == MediaType.Subtitle && PreloadedSubtitles != null) { // Get the preloaded, cached subtitle block currentBlock[t] = PreloadedSubtitles[wallClock]; } else { // Get the regular audio, video, or sub block currentBlock[t] = Blocks[t][wallClock]; } } // Render each of the Media Types if it is time to do so. foreach (var t in all) { // Skip rendering for nulls if (currentBlock[t] == null) { continue; } // Render by forced signal (TimeSpan.MinValue) or because simply it is time to do so if (LastRenderTime[t] == TimeSpan.MinValue || currentBlock[t].StartTime != LastRenderTime[t]) { renderedBlockCount[t] += SendBlockToRenderer(currentBlock[t], wallClock); continue; } } #endregion #region 6. Finalize the Rendering Cycle // Call the update method on all renderers so they receive what the new wall clock is. foreach (var t in all) { Renderers[t]?.Update(wallClock); } #endregion } catch (ThreadAbortException) { /* swallow */ } catch { if (!IsDisposed) { throw; } } finally { // Always exit notifying the cycle is done. BlockRenderingCycle.Complete(); isRunningRenderingCycle = false; } #endregion }, this, // the state argument passed on to the ticker 0, Convert.ToInt32(Constants.Interval.HighPriority.TotalMilliseconds)); }