Beispiel #1
0
        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");
            }
        }
Beispiel #2
0
        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;
            }
            }
        }
Beispiel #3
0
        public void IfDisposed_IsInProgressEqualsFalse()
        {
            var wait = WaitEventFactory.Create(true);

            wait.Begin();
            wait.Dispose();
            wait.Begin();

            Assert.IsFalse(wait.IsInProgress);
            Assert.IsFalse(wait.IsValid);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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));
        }