/// <summary>
        /// Performs a seek operation to the specified position.
        /// </summary>
        /// <param name="position">The position.</param>
        private void Seek(TimeSpan position)
        {
            SeekingDone.Wait();
            var startTime   = DateTime.UtcNow;
            var resumeClock = Clock.IsRunning;

            Clock.Pause();

            SeekingDone.Reset();
            PacketReadingCycle.Wait();
            FrameDecodingCycle.Wait();
            BlockRenderingCycle.Wait();

            // Clear Blocks and frames, reset the render times
            foreach (var t in Container.Components.MediaTypes)
            {
                Frames[t].Clear();
                Blocks[t].Clear();
                LastRenderTime[t] = TimeSpan.MinValue;
            }

            // Populate frame with after-seek operation
            var frames = Container.Seek(position);

            foreach (var frame in frames)
            {
                Frames[frame.MediaType].Push(frame);
            }

            // Resume the clock if it was running before the seek operation
            OnPropertyChanged(nameof(Position));
            if (resumeClock)
            {
                Clock.Play();
            }

            Container.Log(MediaLogMessageType.Debug,
                          $"SEEK D: Elapsed: {startTime.DebugElapsedUtc()}");

            RequestedSeekPosition = null;
            SeekingDone.Set();
        }
        /// <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>
        /// 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));
        }
        public async Task CloseAsync()
        {
            Container?.Log(MediaLogMessageType.Debug, $"{nameof(CloseAsync)}: Entered");
            Clock.Pause();

            IsTaskCancellationPending = true;

            // Wait for cycles to complete.
            await Task.Run(() =>
            {
                while (!BlockRenderingCycle.Wait(1))
                {
                }
                while (!FrameDecodingCycle.Wait(1))
                {
                }
                while (!PacketReadingCycle.Wait(1))
                {
                }
            });

            BlockRenderingTask?.Join();
            FrameDecodingTask?.Join();
            PacketReadingTask?.Join();

            BlockRenderingTask = null;
            FrameDecodingTask  = null;
            PacketReadingTask  = null;

            foreach (var renderer in Renderers.Values)
            {
                renderer.Close();
            }

            Renderers.Clear();

            // Reset the clock
            Clock.Reset();

            Container?.Log(MediaLogMessageType.Debug, $"{nameof(CloseAsync)}: Completed");

            // Dispose the container
            if (Container != null)
            {
                Container.Dispose();
                Container = null;
            }

            // Dispose the Blocks for all components
            foreach (var kvp in Blocks)
            {
                kvp.Value.Dispose();
            }
            Blocks.Clear();

            // Dispose the Frames for all components
            foreach (var kvp in Frames)
            {
                kvp.Value.Dispose();
            }
            Frames.Clear();

            // Clear the render times
            LastRenderTime.Clear();

            // Update notification properties
            UpdateMediaProperties();
            MediaState = MediaState.Close;
        }