コード例 #1
0
        private void DetectPlaybackEnded(MediaType main)
        {
            var playbackEndClock = MediaCore.Blocks[main].Count > 0
                ? MediaCore.Blocks[main].RangeEndTime
                : MediaCore.Timing.GetEndTime(main) ?? TimeSpan.MaxValue;

            // Check End of Media Scenarios
            if (!Commands.HasPendingCommands &&
                MediaCore.HasDecodingEnded &&
                !CanResumeClock(main))
            {
                // Rendered all and nothing else to render
                if (State.HasMediaEnded == false)
                {
                    if (Container.IsStreamSeekable)
                    {
                        var componentStartTime      = Container.Components[main].StartTime;
                        var actualComponentDuration = TimeSpan.FromTicks(playbackEndClock.Ticks - componentStartTime.Ticks);
                        Container.Components[main].Duration = actualComponentDuration;
                    }

                    MediaCore.PausePlayback();
                    MediaCore.ChangePlaybackPosition(playbackEndClock);
                }

                State.MediaState    = MediaPlaybackState.Stop;
                State.HasMediaEnded = true;
            }
            else
            {
                State.HasMediaEnded = false;
            }
        }
コード例 #2
0
 /// <summary>
 /// Performs actions when the command has been executed.
 /// This is useful to notify exceptions or update the state of the media.
 /// </summary>
 public override void PostProcess()
 {
     MediaCore.SendOnMediaClosed();
     MediaCore.State.UpdateFixedContainerProperties();
     LogReferenceCounter(MediaCore);
     MediaCore.Log(MediaLogMessageType.Debug, $"Command {CommandType}: Completed");
 }
コード例 #3
0
        private void DetectPlaybackEnded(MediaType main)
        {
            var playbackEndClock = MediaCore.Blocks[main].Count > 0
                ? MediaCore.Blocks[main].RangeEndTime
                : Container.Components.PlaybackEndTime ?? TimeSpan.MaxValue;

            var isAtEndOfPlayback = MediaCore.PlaybackPosition.Ticks >= playbackEndClock.Ticks ||
                                    MediaCore.Timing.HasDisconnectedClocks;

            // Check End of Media Scenarios
            if (!Commands.HasPendingCommands &&
                MediaCore.HasDecodingEnded &&
                isAtEndOfPlayback)
            {
                // Rendered all and nothing else to render
                if (State.HasMediaEnded == false)
                {
                    MediaCore.PausePlayback();
                    MediaCore.ChangePlaybackPosition(playbackEndClock);
                }

                State.MediaState    = MediaPlaybackState.Stop;
                State.HasMediaEnded = true;
            }
            else
            {
                State.HasMediaEnded = false;
            }
        }
コード例 #4
0
 private void LogEventDone(RoutedEvent e)
 {
     if (WindowsPlatform.Instance.IsInDebugMode)
     {
         MediaCore?.Log(MediaLogMessageType.Trace, $"EVENT DONE : {e.Name}");
     }
 }
コード例 #5
0
 internal void RaiseMediaFailedEvent(Exception ex)
 {
     LogEventStart(MediaFailedEvent);
     MediaCore?.Log(MediaLogMessageType.Error, $"Media Failure - {ex?.GetType()}: {ex?.Message}");
     WindowsPlatform.Instance.Gui?.Invoke(DispatcherPriority.DataBind, () =>
     {
         RaiseEvent(CreateExceptionRoutedEventArgs(MediaFailedEvent, this, ex));
     });
     LogEventDone(MediaFailedEvent);
 }
コード例 #6
0
 internal void RaiseMediaFailedEvent(Exception ex)
 {
     LogEventStart(MediaFailedEvent);
     MediaCore?.Log(MediaLogMessageType.Error, $"Media Failure - {ex?.GetType()}: {ex?.Message}");
     GuiContext.Current.EnqueueInvoke(() =>
     {
         RaiseEvent(CreateExceptionRoutedEventArgs(MediaFailedEvent, this, ex));
         LogEventDone(MediaFailedEvent);
     });
 }
コード例 #7
0
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        private void Dispose(bool alsoManaged)
        {
            if (!m_IsDisposed)
            {
                if (alsoManaged)
                {
                    MediaCore.Dispose();
                }

                m_IsDisposed = true;
            }
        }
コード例 #8
0
        /// <summary>
        /// Opens the specified URI.
        /// This command gets processed in a threadpool thread asynchronously.
        /// </summary>
        /// <param name="uri">The URI.</param>
        /// <returns>The asynchronous task</returns>
        public async Task OpenAsync(Uri uri)
        {
            // Check Uri Argument
            if (uri == null)
            {
                MediaCore?.Log(
                    MediaLogMessageType.Warning,
                    $"{nameof(MediaCommandManager)}.{nameof(OpenAsync)}: '{nameof(uri)}' cannot be null");

                return; // Task.CompletedTask;
            }

            if (CanExecuteCommands == false)
            {
                return; // Task.CompletedTask;
            }
            else
            {
                IsOpening.Value = true;
            }

            var command = new OpenCommand(this, uri);

            ExecutingCommand = command;
            ClearCommandQueue();

            var action = new Action(() =>
            {
                try
                {
                    if (command.HasCompleted)
                    {
                        return;
                    }
                    command.RunSynchronously();
                }
                catch (Exception ex)
                {
                    MediaCore?.Log(
                        MediaLogMessageType.Error,
                        $"{nameof(MediaCommandManager)}.{nameof(OpenAsync)}: {ex.GetType()} - {ex.Message}");
                }
                finally
                {
                    ExecutingCommand?.Complete();
                    ExecutingCommand = null;
                    IsOpening.Value  = false;
                }
            });

            await TaskEx.Run(action);
        }
コード例 #9
0
        private void LogEventDone(RoutedEvent e)
        {
            if (e.Name.Equals(nameof(BufferingEnded)))
            {
                MediaCore?.Log(MediaLogMessageType.Debug, $"EVENT DONE: {e.Name}");
                return;
            }

            if (WindowsPlatform.Instance.IsInDebugMode)
            {
                MediaCore?.Log(MediaLogMessageType.Trace, $"EVENT DONE : {e.Name}");
            }
        }
コード例 #10
0
        /// <inheritdoc />
        protected override void ExecuteCycleLogic(CancellationToken ct)
        {
            // Update Status Properties
            var main = Container.Components.MainMediaType;
            var all  = MediaCore.Renderers.Keys.ToArray();

            // Ensure we have renderers ready and main blocks available
            if (!Initialize(all))
            {
                return;
            }

            try
            {
                // If we are in the middle of a seek, wait for seek blocks
                WaitForSeekBlocks(main, ct);

                // Ensure the RTC clocks match the playback position
                AlignClocksToPlayback(main, all);

                // Check for and enter a sync-buffering scenario
                EnterSyncBuffering(main, all);

                // Render each of the Media Types if it is time to do so.
                if (MediaOptions.UseParallelRendering)
                {
                    ParallelRenderBlocks.Invoke(all);
                }
                else
                {
                    SerialRenderBlocks.Invoke(all);
                }
            }
            catch (Exception ex)
            {
                MediaCore.LogError(
                    Aspects.RenderingWorker, "Error while in rendering worker cycle", ex);

                throw;
            }
            finally
            {
                DetectPlaybackEnded(main);

                // CatchUpWithLiveStream(); // TODO: We are on to something good here
                ExitSyncBuffering(main, all, ct);
                ReportAndResumePlayback(main);
                AdjustWorkerPeriod(main);
            }
        }
コード例 #11
0
        /// <summary>
        /// Performs actions when the command has been executed.
        /// This is useful to notify exceptions or update the state of the media.
        /// </summary>
        public override void PostProcess()
        {
            MediaCore.State.UpdateFixedContainerProperties();

            if (ErrorException == null)
            {
                MediaCore.SendOnMediaChanged();
            }
            else
            {
                MediaCore.SendOnMediaFailed(ErrorException);
            }

            MediaCore.Log(MediaLogMessageType.Debug, $"Command {CommandType}: Completed");
        }
コード例 #12
0
        /// <summary>
        /// Handles the asynchronous dispose of the underlying Media Engine.
        /// </summary>
        private void HandledAsynchronousDispose()
        {
            // Dispose outside of the current thread to avoid deadlocks
            ThreadPool.QueueUserWorkItem(s =>
            {
                MediaCore.Dispose();

                // Notify the one last state
                GuiContext.Current.EnqueueInvoke(() =>
                {
                    UpdateNotificationProperties();
                    UpdateDependencyProperties();
                });
            });
        }
コード例 #13
0
        /// <summary>
        /// Opens the specified custom input stream.
        /// This command gets processed in a threadpool thread asynchronously.
        /// </summary>
        /// <param name="stream">The custom input stream.</param>
        /// <returns>
        /// The asynchronous task
        /// </returns>
        public async Task OpenAsync(IMediaInputStream stream)
        {
            // Check Uri Argument
            if (stream == null)
            {
                MediaCore?.Log(
                    MediaLogMessageType.Warning,
                    $"{nameof(MediaCommandManager)}.{nameof(OpenAsync)}: '{nameof(stream)}' cannot be null");

                return;
            }

            if (CanExecuteCommands == false)
            {
                return;
            }
            else
            {
                IsOpening.Value = true;
            }

            var command = new OpenCommand(this, stream);

            ExecutingCommand = command;
            ClearCommandQueue();

            try
            {
                if (command.HasCompleted)
                {
                    return;
                }

                await command.StartAsync();
            }
            catch (Exception ex)
            {
                MediaCore?.Log(
                    MediaLogMessageType.Error,
                    $"{nameof(MediaCommandManager)}.{nameof(OpenAsync)}: {ex.GetType()} - {ex.Message}");
            }
            finally
            {
                ExecutingCommand?.Complete();
                ExecutingCommand = null;
                IsOpening.Value  = false;
            }
        }
コード例 #14
0
        /// <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);
        }
コード例 #15
0
        /// <summary>
        /// Performs actions when the command has been executed.
        /// This is useful to notify exceptions or update the state of the media.
        /// </summary>
        public override void PostProcess()
        {
            MediaCore.State.UpdateFixedContainerProperties();

            if (ExceptionResult == null)
            {
                MediaCore.State.UpdateMediaState(PlaybackStatus.Stop);
                MediaCore.SendOnMediaOpened();
            }
            else
            {
                MediaCore.State.UpdateMediaState(PlaybackStatus.Close);
                MediaCore.SendOnMediaFailed(ExceptionResult);
            }

            MediaCore.Log(MediaLogMessageType.Debug, $"Command {CommandType}: Completed");
        }
コード例 #16
0
        /// <summary>
        /// Provides the implementation for the Pause Media Command.
        /// </summary>
        /// <returns>True if the command was successful.</returns>
        private bool CommandPauseMedia()
        {
            if (State.CanPause == false)
            {
                return(false);
            }

            MediaCore.PausePlayback();

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

            MediaCore.ChangePlaybackPosition(SnapPositionToBlockPosition(MediaCore.PlaybackPosition));
            State.MediaState = MediaPlaybackState.Pause;
            return(true);
        }
コード例 #17
0
        /// <inheritdoc />
        public override void PostProcess()
        {
            MediaCore.State.UpdateFixedContainerProperties();

            if (ExceptionResult == null)
            {
                MediaCore.State.UpdateMediaState(PlaybackStatus.Stop);
                MediaCore.SendOnMediaOpened();
            }
            else
            {
                MediaCore.ResetPosition();
                MediaCore.State.UpdateMediaState(PlaybackStatus.Close);
                MediaCore.SendOnMediaFailed(ExceptionResult);
            }

            this.LogDebug(Aspects.EngineCommand, $"{CommandType} Completed");
        }
コード例 #18
0
        /// <summary>
        /// Provides the implementation for the Pause Media Command.
        /// </summary>
        /// <returns>True if the command was successful</returns>
        private bool CommandPauseMedia()
        {
            if (State.CanPause == false)
            {
                return(false);
            }

            MediaCore.Clock.Pause();

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

            MediaCore.ChangePosition(SnapPositionToBlockPosition(MediaCore.WallClock));
            State.UpdateMediaState(PlaybackStatus.Pause);
            return(true);
        }
コード例 #19
0
        /// <summary>
        /// Provides the implementation for the Stop Media Command.
        /// </summary>
        /// <returns>True if the command was successful.</returns>
        private bool CommandStopMedia()
        {
            if (State.IsSeekable == false)
            {
                return(false);
            }

            MediaCore.ResetPlaybackPosition();

            SeekMedia(new SeekOperation(TimeSpan.MinValue, SeekMode.Stop), CancellationToken.None);

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

            State.MediaState = MediaPlaybackState.Stop;
            return(true);
        }
コード例 #20
0
        /// <summary>
        /// Closes the specified media.
        /// This command gets processed in a threadpool thread asynchronously.
        /// </summary>
        /// <returns>Returns the background task.</returns>
        public async Task CloseAsync()
        {
            if (CanExecuteCommands == false)
            {
                return;
            }
            else
            {
                IsClosing.Value = true;
            }

            var command = new CloseCommand(this);

            ExecutingCommand = command;
            ClearCommandQueue();

            var action = new Action(() =>
            {
                try
                {
                    if (command.HasCompleted)
                    {
                        return;
                    }
                    command.RunSynchronously();
                }
                catch (Exception ex)
                {
                    MediaCore?.Log(
                        MediaLogMessageType.Error,
                        $"{nameof(MediaCommandManager)}.{nameof(CloseAsync)}: {ex.GetType()} - {ex.Message}");
                }
                finally
                {
                    ExecutingCommand?.Complete();
                    ExecutingCommand = null;
                    IsClosing.Value  = false;
                }
            });

            await TaskEx.Run(action);
        }
コード例 #21
0
        /// <inheritdoc />
        public override void PostProcess()
        {
            var m = MediaCore;

            if (m == null)
            {
                return;
            }

            // Update notification properties
            m.State.ResetAll();
            m.ResetPosition();
            m.State.UpdateMediaState(PlaybackStatus.Close);
            m.State.UpdateSource(null);

            // Notify media has closed
            MediaCore.SendOnMediaClosed();
            LogReferenceCounter();
            this.LogDebug(Aspects.EngineCommand, $"{CommandType} Completed");
        }
コード例 #22
0
        /// <summary>
        /// Performs actions when the command has been executed.
        /// This is useful to notify exceptions or update the state of the media.
        /// </summary>
        public override void PostProcess()
        {
            var m = MediaCore;

            if (m == null)
            {
                return;
            }

            // Update notification properties
            m.State.ResetAll();
            m.ResetPosition();
            m.State.UpdateMediaState(PlaybackStatus.Close);
            m.State.UpdateSource(null);

            // Notify media has closed
            MediaCore.SendOnMediaClosed();
            LogReferenceCounter(MediaCore);
            MediaCore.Log(MediaLogMessageType.Debug, $"Command {CommandType}: Completed");
        }
コード例 #23
0
        /// <summary>
        /// Outputs the state of the queue
        /// </summary>
        /// <param name="operation">The operation.</param>
        /// <param name="outputEmpty">if set to <c>true</c> [output empty].</param>
        private void DumpQueue(string operation, bool outputEmpty)
        {
            if (MediaEngine.Platform.IsInDebugMode == false)
            {
                return;
            }

            lock (SyncLock)
            {
                if (outputEmpty == false && Commands.Count <= 0)
                {
                    return;                                              // Prevent output for empty commands
                }
                MediaCore.Log(MediaLogMessageType.Trace, $"Command Queue ({Commands.Count} commands): {operation}");
                foreach (var c in Commands)
                {
                    MediaCore.Log(MediaLogMessageType.Trace, $"   {c.ToString()}");
                }
            }
        }
コード例 #24
0
        /// <summary>
        /// Initializes the audio renderer.
        /// Call the Play Method to start reading samples
        /// </summary>
        private void Initialize()
        {
            Destroy();

            // Enumerate devices. The default device is the first one so we check
            // that we have more than 1 device (other than the default stub)
            var hasAudioDevices = MediaElement.RendererOptions.UseLegacyAudioOut ?
                                  LegacyAudioPlayer.EnumerateDevices().Count > 1 :
                                  DirectSoundPlayer.EnumerateDevices().Count > 1;

            // Check if we have an audio output device.
            if (hasAudioDevices == false)
            {
                WaitForReadyEvent = null;
                MediaCore.Log(MediaLogMessageType.Warning,
                              $"AUDIO OUT: No audio device found for output.");

                return;
            }

            // Initialize the SoundTouch Audio Processor (if available)
            AudioProcessor = (SoundTouch.IsAvailable == false) ? null : new SoundTouch
            {
                Channels   = Convert.ToUInt32(WaveFormat.Channels),
                SampleRate = Convert.ToUInt32(WaveFormat.SampleRate)
            };

            // Initialize the Audio Device
            AudioDevice = MediaElement.RendererOptions.UseLegacyAudioOut ?
                          new LegacyAudioPlayer(this, MediaElement.RendererOptions.LegacyAudioDevice?.DeviceId ?? -1) as IWavePlayer :
                          new DirectSoundPlayer(this, MediaElement.RendererOptions.DirectSoundDevice?.DeviceId ?? DirectSoundPlayer.DefaultPlaybackDeviceId);

            // Create the Audio Buffer
            SampleBlockSize = Constants.Audio.BytesPerSample * Constants.Audio.ChannelCount;
            var bufferLength = WaveFormat.ConvertMillisToByteSize(2000); // 2-second buffer

            AudioBuffer = new CircularBuffer(bufferLength);
            AudioDevice.Start();
        }
コード例 #25
0
        private void EnterSyncBuffering(MediaType main, MediaType[] all)
        {
            // Determine if Sync-buffering can be potentially entered.
            // Entering the sync-buffering state pauses the RTC and forces the decoder make
            // components catch up with the main component.
            if (MediaCore.IsSyncBuffering || HasDisconnectedClocks || Commands.HasPendingCommands ||
                State.MediaState != MediaPlaybackState.Play || State.HasMediaEnded || Container.IsAtEndOfStream)
            {
                return;
            }

            foreach (var t in all)
            {
                if (t == MediaType.Subtitle || t == main)
                {
                    continue;
                }

                // We don't want to sync-buffer on attached pictures
                if (Container.Components[t].IsStillPictures)
                {
                    continue;
                }

                // If we have data on the t component beyond the start time of the main
                // we don't need to enter sync-buffering.
                if (MediaCore.Blocks[t].RangeEndTime >= MediaCore.Blocks[main].RangeStartTime)
                {
                    continue;
                }

                // If we are not in range of the non-main component we need to
                // enter sync-buffering
                MediaCore.SignalSyncBufferingEntered();
                return;
            }
        }
コード例 #26
0
        /// <inheritdoc />
        public override void PostProcess()
        {
            MediaCore.State.UpdateFixedContainerProperties();

            if (ErrorException == null)
            {
                MediaCore.SendOnMediaChanged();

                if (PlayWhenCompleted)
                {
                    MediaCore.Clock.Play();
                }

                MediaCore.State.UpdateMediaState(
                    MediaCore.Clock.IsRunning ? PlaybackStatus.Play : PlaybackStatus.Pause);
            }
            else
            {
                MediaCore.SendOnMediaFailed(ErrorException);
                MediaCore.State.UpdateMediaState(PlaybackStatus.Pause);
            }

            this.LogDebug(Aspects.EngineCommand, $"{CommandType} Completed");
        }
コード例 #27
0
        /// <summary>
        /// Processes the next command in the command queue.
        /// This method is called in every block rendering cycle.
        /// </summary>
        public void ProcessNext()
        {
            DumpQueue($"Before {nameof(ProcessNext)}", false);
            if (MediaCore.IsTaskCancellationPending)
            {
                return;
            }

            MediaCommand command = null;

            lock (SyncLock)
            {
                if (Commands.Count == 0)
                {
                    return;
                }
                command = Commands[0];
                Commands.RemoveAt(0);
            }

            try
            {
                ExecutingCommand = command;
                command.RunSynchronously();
                DumpQueue($"After {nameof(ProcessNext)}", false);
            }
            catch (Exception ex)
            {
                MediaCore?.Log(MediaLogMessageType.Error, $"{ex.GetType()}: {ex.Message}");
                throw;
            }
            finally
            {
                ExecutingCommand = null;
            }
        }
コード例 #28
0
        /// <summary>
        /// Renders the specified media block.
        /// This needs to return immediately so the calling thread is not disturbed.
        /// </summary>
        /// <param name="mediaBlock">The media block.</param>
        /// <param name="clockPosition">The clock position.</param>
        public void Render(MediaBlock mediaBlock, TimeSpan clockPosition)
        {
            var block = mediaBlock as VideoBlock;

            if (block == null)
            {
                return;
            }
            if (IsRenderingInProgress.Value == true)
            {
                if (MediaCore?.State.IsPlaying ?? false)
                {
                    MediaCore?.Log(MediaLogMessageType.Debug,
                                   $"{nameof(VideoRenderer)}: Frame skipped at {mediaBlock.StartTime}");
                }

                return;
            }

            // Flag the start of a rendering cycle
            IsRenderingInProgress.Value = true;

            // Send the packets to the CC renderer
            MediaElement?.CaptionsView?.SendPackets(block, MediaCore);

            // Create an action that holds GUI thread actions
            var foregroundAction = new Action(() =>
            {
                MediaElement?.CaptionsView?.Render(MediaElement.ClosedCaptionsChannel, clockPosition);
                ApplyLayoutTransforms(block);
            });

            var canStartForegroundTask = MediaElement.VideoView.ElementDispatcher != MediaElement.Dispatcher;
            var foregroundTask         = canStartForegroundTask ?
                                         MediaElement.Dispatcher.InvokeAsync(foregroundAction) : null;

            // Ensure the target bitmap can be loaded
            MediaElement?.VideoView?.InvokeAsync(DispatcherPriority.Render, () =>
            {
                if (block.IsDisposed)
                {
                    IsRenderingInProgress.Value = false;
                    return;
                }

                // Run the foreground action if we could not start it in parallel.
                if (foregroundTask == null)
                {
                    try
                    {
                        foregroundAction();
                    }
                    catch (Exception ex)
                    {
                        MediaElement?.MediaCore?.Log(
                            MediaLogMessageType.Error,
                            $"{nameof(VideoRenderer)} {ex.GetType()}: {nameof(Render)} layout/CC failed. {ex.Message}.");
                    }
                }

                try
                {
                    // Render the bitmap data
                    var bitmapData = LockTargetBitmap(block);
                    if (bitmapData != null)
                    {
                        LoadTargetBitmapBuffer(bitmapData, block);
                        MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition);
                        RenderTargetBitmap(bitmapData, clockPosition);
                    }
                }
                catch (Exception ex)
                {
                    MediaElement?.MediaCore?.Log(
                        MediaLogMessageType.Error,
                        $"{nameof(VideoRenderer)} {ex.GetType()}: {nameof(Render)} bitmap failed. {ex.Message}.");
                }
                finally
                {
                    if (foregroundTask != null)
                    {
                        try
                        {
                            foregroundTask?.Wait();
                        }
                        catch (Exception ex)
                        {
                            MediaElement?.MediaCore?.Log(
                                MediaLogMessageType.Error,
                                $"{nameof(VideoRenderer)} {ex.GetType()}: {nameof(Render)} layout/CC failed. {ex.Message}.");
                        }
                    }

                    // Always reset the rendering state
                    IsRenderingInProgress.Value = false;
                }
            });
        }
コード例 #29
0
        private void AlignClocksToPlayback(MediaType main, MediaType[] all)
        {
            // we don't want to disturb the clock or align it if we are not ready
            if (Commands.HasPendingCommands)
            {
                return;
            }

            if (HasDisconnectedClocks)
            {
                foreach (var t in all)
                {
                    if (t == MediaType.Subtitle)
                    {
                        continue;
                    }

                    var compBlocks   = MediaCore.Blocks[t];
                    var compPosition = MediaCore.Timing.GetPosition(t);

                    if (compBlocks.Count <= 0)
                    {
                        MediaCore.PausePlayback(t, false);

                        if (MediaCore.Timing.GetIsRunning(t))
                        {
                            this.LogDebug(Aspects.Timing,
                                          $"CLOCK PAUSED: {t} clock was paused at {compPosition.Format()} because no decoded {t} content was found");
                        }

                        continue;
                    }

                    // Don't let the RTC lag behind the blocks or move beyond them
                    if (compPosition.Ticks < compBlocks.RangeStartTime.Ticks)
                    {
                        MediaCore.ChangePlaybackPosition(compBlocks.RangeStartTime, t, false);
                        this.LogDebug(Aspects.Timing,
                                      $"CLOCK BEHIND: {t} clock was {compPosition.Format()}. It was updated to {compBlocks.RangeStartTime.Format()}");
                    }
                    else if (compPosition.Ticks > compBlocks.RangeEndTime.Ticks)
                    {
                        if (t != MediaType.Audio)
                        {
                            MediaCore.PausePlayback(t, false);
                        }

                        MediaCore.ChangePlaybackPosition(compBlocks.RangeEndTime, t, false);

                        this.LogDebug(Aspects.Timing,
                                      $"CLOCK AHEAD : {t} clock was {compPosition.Format()}. It was updated to {compBlocks.RangeEndTime.Format()}");
                    }
                }

                return;
            }

            // Get a reference to the main blocks.
            // The range will be 0 if there are no blocks.
            var blocks   = MediaCore.Blocks[main];
            var position = MediaCore.PlaybackPosition;

            if (blocks.Count == 0)
            {
                // We have no main blocks in range. All we can do is pause the clock
                if (MediaCore.Timing.IsRunning)
                {
                    this.LogDebug(Aspects.Timing,
                                  $"CLOCK PAUSED: playback clock was paused at {position.Format()} because no decoded {main} content was found");
                }

                MediaCore.PausePlayback();
                return;
            }

            if (position.Ticks < blocks.RangeStartTime.Ticks)
            {
                // Don't let the RTC lag behind what is available on the main component
                MediaCore.ChangePlaybackPosition(blocks.RangeStartTime);
                this.LogTrace(Aspects.Timing,
                              $"CLOCK BEHIND: playback clock was {position.Format()}. It was updated to {blocks.RangeStartTime.Format()}");
            }
            else if (position.Ticks > blocks.RangeEndTime.Ticks)
            {
                // Don't let the RTC move beyond what is available on the main component
                MediaCore.PausePlayback();
                MediaCore.ChangePlaybackPosition(blocks.RangeEndTime);
                this.LogTrace(Aspects.Timing,
                              $"CLOCK AHEAD : playback clock was {position.Format()}. It was updated to {blocks.RangeEndTime.Format()}");
            }
        }
コード例 #30
0
        private void ExitSyncBuffering(MediaType main, MediaType[] all, CancellationToken ct)
        {
            // Don't exit syc-buffering if we are not in syncbuffering
            if (!MediaCore.IsSyncBuffering)
            {
                return;
            }

            // Detect if an exit from Sync Buffering is required
            var canExitSyncBuffering  = MediaCore.Blocks[main].Count > 0;
            var mustExitSyncBuffering =
                ct.IsCancellationRequested ||
                MediaCore.HasDecodingEnded ||
                Container.IsAtEndOfStream ||
                State.HasMediaEnded ||
                Commands.HasPendingCommands ||
                HasDisconnectedClocks;

            try
            {
                if (mustExitSyncBuffering)
                {
                    this.LogDebug(Aspects.ReadingWorker, $"SYNC-BUFFER: 'must exit' condition met.");
                    return;
                }

                if (!canExitSyncBuffering)
                {
                    return;
                }

                foreach (var t in all)
                {
                    if (t == MediaType.Subtitle || t == main)
                    {
                        continue;
                    }

                    // We don't want to consider sync-buffer on attached pictures
                    if (Container.Components[t].IsStillPictures)
                    {
                        continue;
                    }

                    // If we don't have data on the t component beyond the mid time of the main
                    // we can't exit sync-buffering.
                    if (MediaCore.Blocks[t].RangeEndTime < MediaCore.Blocks[main].RangeMidTime)
                    {
                        canExitSyncBuffering = false;
                        break;
                    }
                }
            }
            finally
            {
                // Exit sync-buffering state if we can or we must
                if (mustExitSyncBuffering || canExitSyncBuffering)
                {
                    AlignClocksToPlayback(main, all);
                    MediaCore.SignalSyncBufferingExited();
                }
            }
        }