/// <summary> /// Provides the implementation for the Play Media Command. /// </summary> /// <returns>True if the command was successful</returns> private bool CommandPlayMedia() { if (!CanResumeMedia) { return(false); } foreach (var renderer in MediaCore.Renderers.Values) { renderer.Play(); } MediaCore.ResumePlayback(); return(true); }
/// <inheritdoc /> protected override void ExecuteCycleLogic(CancellationToken ct) { #region Setup the Decoding Cycle // Update state properties -- this must be done on every cycle // because a direct command might have changed the components var wallClock = MediaCore.WallClock; var main = Container.Components.MainMediaType; MediaBlockBuffer blocks; DecodedFrameCount = 0; var rangePercent = 0d; #endregion try { // The 2-part logic blocks detect a sync-buffering scenario // and then decodes the necessary frames. if (MediaCore.HasDecodingEnded || ct.IsCancellationRequested) { return; } #region Sync-Buffering Detection // Capture the blocks for easier readability blocks = MediaCore.Blocks[main]; // If we are not in range then we need to enter the sync-buffering state if (NeedsMorePackets && !MediaCore.IsSyncBuffering && blocks.IsInRange(wallClock) == false) { // Enter sync-buffering scenario MediaCore.Clock.Pause(); wallClock = MediaCore.WallClock; MediaCore.IsSyncBuffering = true; SyncBufferStartTime = DateTime.UtcNow; this.LogInfo(Aspects.DecodingWorker, $"SYNC-BUFFER: Started. Buffer: {State.BufferingProgress:p}. Clock: {wallClock.Format()}"); } #endregion #region Component Decoding // We need to add blocks if the wall clock is over 75% // for each of the components so that we have some buffer. foreach (var t in Container.Components.MediaTypes) { if (ct.IsCancellationRequested) { break; } // Capture a reference to the blocks and the current Range Percent const double rangePercentThreshold = 0.75d; blocks = MediaCore.Blocks[t]; rangePercent = blocks.GetRangePercent(wallClock); // Read as much as we can for this cycle but always within range. while (blocks.IsFull == false || rangePercent > rangePercentThreshold) { if (ct.IsCancellationRequested || AddNextBlock(t) == false) { break; } DecodedFrameCount += 1; rangePercent = blocks.GetRangePercent(wallClock); // Determine break conditions to save CPU time if (rangePercent > 0 && rangePercent <= rangePercentThreshold && blocks.IsFull == false && blocks.CapacityPercent >= 0.25d && blocks.IsInRange(wallClock)) { break; } } #endregion } } finally { // Provide updates to decoding stats State.UpdateDecodingBitRate( MediaCore.Blocks.Values.Sum(b => b.IsInRange(wallClock) ? b.RangeBitRate : 0)); // Detect End of Decoding Scenarios // The Rendering will check for end of media when this // condition is set. var hasDecodingEnded = DetectHasDecodingEnded(wallClock, DecodedFrameCount, main); MediaCore.HasDecodingEnded = hasDecodingEnded; // Detect if an exit from Sync Buffering is required var mustExitSyncBuffering = MediaCore.IsSyncBuffering && (ct.IsCancellationRequested || hasDecodingEnded || State.BufferingProgress >= 0.95); // Detect if we need an immediate exit from sync buffering if (mustExitSyncBuffering || (MediaCore.IsSyncBuffering && !NeedsMorePackets)) { blocks = MediaCore.Blocks[main]; if (blocks.Count > 0 && !blocks.IsInRange(wallClock)) { wallClock = blocks[wallClock].StartTime; } MediaCore.ChangePosition(wallClock); MediaCore.IsSyncBuffering = false; this.LogInfo(Aspects.DecodingWorker, $"SYNC-BUFFER: Completed in {DateTime.UtcNow.Subtract(SyncBufferStartTime).TotalMilliseconds:0.0} ms"); if (State.MediaState == PlaybackStatus.Play) { MediaCore.ResumePlayback(); } } } }