/// <summary> /// Continuously converts frmes and places them on the corresponding /// block buffer. This task is responsible for keeping track of the clock /// and calling the render methods appropriate for the current clock position. /// </summary> internal void RunBlockRenderingWorker() { try { #region 0. Initialize Running State // Holds the main media type var main = Container.Components.Main.MediaType; // Holds the auxiliary media types var auxs = Container.Components.MediaTypes.Where(t => t != main).ToArray(); // Holds all components var all = Container.Components.MediaTypes.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 = 0; // reset render times for all components foreach (var t in all) { LastRenderTime[t] = TimeSpan.MinValue; } // Ensure the other workers are running PacketReadingCycle?.WaitOne(); FrameDecodingCycle?.WaitOne(); // Set the initial clock position Clock.Position = Blocks[main].RangeStartTime; var wallClock = Clock.Position; // Wait for renderers to be ready foreach (var t in all) { Renderers[t]?.WaitForReadyState(); } #endregion while (IsTaskCancellationPending == false) { #region 1. Control and Capture // Reset the rendered count to 0 renderedBlockCount = 0; // Capture current clock position for the rest of this cycle BlockRenderingCycle?.Reset(); // capture the wall clock for this cycle wallClock = Clock.Position; #endregion #region 2. Handle Block Rendering // Capture the blocks to render foreach (var t in all) { currentBlock[t] = HasDecoderSeeked ? null : 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) if (LastRenderTime[t] == TimeSpan.MinValue) { renderedBlockCount += SendBlockToRenderer(currentBlock[t], wallClock); continue; } // Render because we simply have not rendered if (currentBlock[t].StartTime != LastRenderTime[t]) { renderedBlockCount += SendBlockToRenderer(currentBlock[t], wallClock); continue; } } #endregion #region 6. Finalize the Rendering Cycle // Signal the rendering cycle was set. BlockRenderingCycle?.Set(); // 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); } // Delay the thread for a bit if we have no more stuff to process if (IsSeeking == false && renderedBlockCount <= 0 && Commands.PendingCount <= 0) { Task.Delay(1).GetAwaiter().GetResult(); } #endregion } } catch (ThreadAbortException) { } finally { // Always exit notifying the cycle is done. BlockRenderingCycle?.Set(); } }
/// <summary> /// Starts the block rendering worker. /// </summary> private void StartBlockRenderingWorker() { if (HasBlockRenderingWorkerExited != null) { return; } HasBlockRenderingWorkerExited = new ManualResetEvent(false); // Synchronized access to parts of the run cycle var isRunningRenderingCycle = false; // Holds the main media type var main = Container.Components.Main.MediaType; // Holds the auxiliary media types var auxs = Container.Components.MediaTypes.ExcludeMediaType(main); // Holds all components var all = Container.Components.MediaTypes.DeepCopy(); // 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.WaitOne(); // wait for main component blocks or EOF or cancellation pending while (CanReadMoreFramesOf(main) && Blocks[main].Count <= 0) { FrameDecodingCycle.WaitOne(); } // Set the initial clock position // TODO: maybe update media start time offset to this Minimum, initial Start Time intead of relying on contained meta? Clock.Update(Blocks[main].RangeStartTime); // .GetMinStartTime() 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 || HasBlockRenderingWorkerExited.IsSet() || IsDisposed) { HasBlockRenderingWorkerExited.Set(); return; } #endregion #region Run the Rendering Cycle // Updatete Status Properties State.UpdateBufferingProperties(); // Don't run the cycle if it's already running if (isRunningRenderingCycle) { // TODO: Maybe Log a frame skip here? 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.Reset(); #endregion #region 2. Handle Block Rendering // Wait for the seek op to finish before we capture blocks if (HasDecoderSeeked) { SeekingDone.WaitOne(); } // capture the wall clock for this cycle wallClock = WallClock; // Capture the blocks to render foreach (var t in all) { 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) if (LastRenderTime[t] == TimeSpan.MinValue) { renderedBlockCount[t] += SendBlockToRenderer(currentBlock[t], wallClock); continue; } // Render because we simply have not rendered if (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.Set(); isRunningRenderingCycle = false; } #endregion }, this, // the state argument passed on to the ticker 0, (int)Constants.Interval.HighPriority.TotalMilliseconds); }
/// <summary> /// Starts the block rendering worker. /// </summary> private void StartBlockRenderingWorker() { if (HasBlockRenderingWorkerExited != null) { return; } HasBlockRenderingWorkerExited = new ManualResetEvent(false); // Synchronized access to parts of the run cycle var isRunningPropertyUpdates = false; var isRunningRenderingCycle = false; // Holds the main media type var main = Container.Components.Main.MediaType; // Holds the auxiliary media types var auxs = Container.Components.MediaTypes.ExcludeMediaType(main); // Holds all components var all = Container.Components.MediaTypes.DeepCopy(); // 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 the other workers are running PacketReadingCycle.WaitOne(); FrameDecodingCycle.WaitOne(); // Set the initial clock position Clock.Position = Blocks[main].RangeStartTime; var wallClock = Clock.Position; // Wait for renderers to be ready foreach (var t in all) { Renderers[t]?.WaitForReadyState(); } // The Property update timer is responsible for timely updates to properties outside of the worker threads BlockRenderingWorker = new Timer((s) => { #region Detect a Timer Stop if (IsTaskCancellationPending || HasBlockRenderingWorkerExited.IsSet() || m_IsDisposing.Value) { HasBlockRenderingWorkerExited.Set(); return; } #endregion #region Run the property Updates if (isRunningPropertyUpdates == false) { isRunningPropertyUpdates = true; try { UpdatePosition(IsOpen ? Clock?.Position ?? TimeSpan.Zero : TimeSpan.Zero); UpdateBufferingProperties(); } catch (Exception ex) { Log(MediaLogMessageType.Error, $"{nameof(BlockRenderingWorker)} callabck failed. {ex.GetType()}: {ex.Message}"); } finally { isRunningPropertyUpdates = false; } } #endregion #region Run the Rendering Cycle // Don't run the cycle if it's already running if (isRunningRenderingCycle) { // TODO: Log a frame skip 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.Reset(); // capture the wall clock for this cycle wallClock = Clock.Position; #endregion #region 2. Handle Block Rendering // Wait for the seek op to finish before we capture blocks if (HasDecoderSeeked) { SeekingDone.WaitOne(); } // Capture the blocks to render foreach (var t in all) { 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) if (LastRenderTime[t] == TimeSpan.MinValue) { renderedBlockCount[t] += SendBlockToRenderer(currentBlock[t], wallClock); continue; } // Render because we simply have not rendered if (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) { } catch { throw; } finally { // Always exit notifying the cycle is done. BlockRenderingCycle.Set(); isRunningRenderingCycle = false; } #endregion }, this, // the state argument passed on to the ticker 0, (int)Constants.Interval.HighPriority.TotalMilliseconds); }