Exemple #1
0
        private bool DetectEndOfMedia(int decodedFrameCount, MediaType main)
        {
            // Detect end of block rendering
            // TODO: Maybe this detection should be performed on the BlockRendering worker?
            if (decodedFrameCount <= 0 &&
                IsWorkerInterruptRequested == false &&
                CanReadMoreFramesOf(main) == false &&
                Blocks[main].IndexOf(WallClock) >= Blocks[main].Count - 1)
            {
                if (State.HasMediaEnded == false)
                {
                    // Rendered all and nothing else to read
                    Clock.Pause();
                    ChangePosition(Blocks[main].RangeEndTime);

                    if (State.NaturalDuration != null &&
                        State.NaturalDuration != TimeSpan.MinValue &&
                        State.NaturalDuration < WallClock)
                    {
                        Log(MediaLogMessageType.Warning,
                            $"{nameof(State.HasMediaEnded)} conditions met at {WallClock.Format()} but " +
                            $"{nameof(State.NaturalDuration)} reports {State.NaturalDuration.Value.Format()}");
                    }

                    State.UpdateMediaEnded(true);
                    State.UpdateMediaState(PlaybackStatus.Stop);
                    foreach (var mt in Container.Components.MediaTypes)
                    {
                        InvalidateRenderer(mt);
                    }

                    SendOnMediaEnded();
                }
            }
            else
            {
                State.UpdateMediaEnded(false);
            }

            return(State.HasMediaEnded);
        }
Exemple #2
0
        /// <summary>
        /// Continually decodes the available packet buffer to have as
        /// many frames as possible in each frame queue and
        /// up to the MaxFrames on each component
        /// </summary>
        internal void RunFrameDecodingWorker()
        {
            // TODO: Don't use State properties in workers as they are only for
            // TODO: Check the use of wall clock. Maybe it's be more consistent
            // to use a single atomic wallclock value per cycle. Check other workers as well.
            // state notification purposes.
            // State variables
            var wasSyncBuffering  = false;
            var delay             = new DelayProvider(); // The delay provider prevents 100% core usage
            var decodedFrameCount = 0;
            var rangePercent      = 0d;
            var main = Container.Components.MainMediaType; // Holds the main media type
            var resumeSyncBufferingClock = false;
            MediaBlockBuffer blocks      = null;

            try
            {
                while (Commands.IsStopWorkersPending == false)
                {
                    #region Setup the Decoding Cycle

                    // Determine what to do on a priority command
                    if (Commands.IsExecutingDirectCommand)
                    {
                        if (Commands.IsClosing)
                        {
                            break;
                        }
                        if (Commands.IsChanging)
                        {
                            Commands.WaitForDirectCommand();
                        }
                    }

                    // Execute the following command at the beginning of the cycle
                    if (IsSyncBuffering == false)
                    {
                        Commands.ExecuteNextQueuedCommand();
                    }

                    // Signal a Seek starting operation and set the initial state
                    FrameDecodingCycle.Begin();

                    // Update state properties -- this must be after processing commanmds as
                    // a direct command might have changed the components
                    main = Container.Components.MainMediaType;
                    decodedFrameCount = 0;

                    #endregion

                    // The 2-part logic blocks detect a sync-buffering scenario
                    // and then decodes the necessary frames.
                    if (State.HasMediaEnded == false && IsWorkerInterruptRequested == false)
                    {
                        #region Sync-Buffering

                        // Capture the blocks for easier readability
                        blocks = Blocks[main];

                        // If we are not then we need to begin sync-buffering
                        if (wasSyncBuffering == false && blocks.IsInRange(WallClock) == false)
                        {
                            // Signal the start of a sync-buffering scenario
                            IsSyncBuffering          = true;
                            wasSyncBuffering         = true;
                            resumeSyncBufferingClock = Clock.IsRunning;
                            Clock.Pause();
                            State.UpdateMediaState(PlaybackStatus.Manual);
                            Log(MediaLogMessageType.Debug, $"SYNC-BUFFER: Started.");
                        }

                        #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 (IsWorkerInterruptRequested)
                            {
                                break;
                            }

                            // Capture a reference to the blocks and the current Range Percent
                            const double rangePercentThreshold = 0.75d;
                            blocks       = 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)
                            {
                                // Stop decoding under sync-buffering conditions
                                if (IsSyncBuffering && blocks.IsFull)
                                {
                                    break;
                                }

                                if (IsWorkerInterruptRequested || AddNextBlock(t) == false)
                                {
                                    break;
                                }

                                decodedFrameCount += 1;
                                rangePercent       = blocks.GetRangePercent(WallClock);

                                // Determine break conditions to save CPU time
                                if (IsSyncBuffering == false &&
                                    rangePercent > 0 &&
                                    rangePercent <= rangePercentThreshold &&
                                    blocks.IsFull == false &&
                                    blocks.CapacityPercent >= 0.25d &&
                                    blocks.IsInRange(WallClock))
                                {
                                    break;
                                }
                            }
                        }

                        // Give it a break if we are still buffering packets
                        if (IsSyncBuffering)
                        {
                            delay.WaitOne();
                            FrameDecodingCycle.Complete();
                            continue;
                        }

                        #endregion
                    }

                    #region Finish the Cycle

                    // Detect End of Media Scenarios
                    DetectEndOfMedia(decodedFrameCount, main);

                    // Resume sync-buffering clock
                    if (wasSyncBuffering && IsSyncBuffering == false)
                    {
                        // Sync-buffering blocks
                        blocks = Blocks[main];

                        // Unfortunately at this point we will need to adjust the clock after creating the frames.
                        // to ensure tha mian component is within the clock range if the decoded
                        // frames are not with range. This is normal while buffering though.
                        if (blocks.IsInRange(WallClock) == false)
                        {
                            // Update the wall clock to the most appropriate available block.
                            if (blocks.Count > 0)
                            {
                                ChangePosition(blocks[WallClock].StartTime);
                            }
                            else
                            {
                                resumeSyncBufferingClock = false; // Hard stop the clock.
                            }
                        }

                        // log some message and resume the clock if it was playing
                        Log(MediaLogMessageType.Debug, $"SYNC-BUFFER: Finished. Clock set to {WallClock.Format()}");

                        if (resumeSyncBufferingClock && State.HasMediaEnded == false)
                        {
                            ResumePlayback();
                        }

                        wasSyncBuffering         = false;
                        resumeSyncBufferingClock = false;
                    }

                    // Provide updates to decoding stats
                    State.UpdateDecodingBitrate(
                        Blocks.Values.Sum(b => b.IsInRange(WallClock) ? b.RangeBitrate : 0));

                    // Complete the frame decoding cycle
                    FrameDecodingCycle.Complete();

                    // Give it a break if there was nothing to decode.
                    DelayDecoder(delay, decodedFrameCount);

                    #endregion
                }
            }
            catch { throw; }
            finally
            {
                // Reset decoding stats
                State.UpdateDecodingBitrate(0);

                // Always exit notifying the cycle is done.
                FrameDecodingCycle.Complete();
                delay.Dispose();
            }
        }