private void SuspendPlayback()
        {
            // Get most current player time.
            _suspendClock = PlayerClockProvider.LastClock;

            // Stop data clock to halt data providers. Not required for UI based operation
            // but needed for multitasking with player preemption. Prevents data clock difference
            // between player & data provider.
            _dataClock.Stop();
            StopClockGenerator();
            player.Pause();

            logger.Info($"Playback time {_suspendClock}");
        }
        /// <summary>
        /// Pauses playback on all initialized streams. Playback had to be played.
        /// </summary>
        public void Pause()
        {
            logger.Info("");

            try
            {
                DisableTransfer();
                StopClockGenerator();
                player.Pause();
                stateChangedSubject.OnNext(PlayerState.Paused);
            }
            catch (InvalidOperationException ioe)
            {
                logger.Error(ioe);
            }
        }
Beispiel #3
0
        public async Task Resume()
        {
            if (!activeTaskCts.IsCancellationRequested)
            {
                return;
            }

            activeTaskCts = new CancellationTokenSource();
            var token = activeTaskCts.Token;

            logger.Info($"Resuming State/Clock {_suspendState}/{_suspendClock}");

            // If suspend happened before or during PrepareAsync, suspend state will be Idle
            // There is no state "have configuration" based on which prepare operation would be invoked,
            // as such we need to get to Ready state when suspend occured in Idle
            var targetState = _suspendState == ESPlayer.ESPlayerState.Idle
                ? ESPlayer.ESPlayerState.Ready : _suspendState;

            var currentState = ESPlayer.ESPlayerState.None;

            // Loop through startup states till target state reached, performing start activities
            // corresponding to each start step.
            do
            {
                switch (currentState)
                {
                // Open player
                case ESPlayer.ESPlayerState.None:
                    OpenPlayer();
                    currentState = ESPlayer.ESPlayerState.Idle;
                    break;

                // Prepare playback
                case ESPlayer.ESPlayerState.Idle:

                    // Set'n'start clocks to suspend time.
                    _dataClock.SetClock(_suspendClock, token);
                    StartClockGenerator();

                    // Push current configuration (if available)
                    if (!AllStreamsHaveConfiguration)
                    {
                        logger.Info(
                            $"Have Configuration. Audio: {esStreams[(int)StreamType.Audio].HaveConfiguration}  Video: {esStreams[(int)StreamType.Video].HaveConfiguration}");
                        return;
                    }

                    SetPlayerConfiguration();
                    try
                    {
                        await PreparePlayback(token);
                    }
                    catch (OperationCanceledException)
                    {
                        logger.Info("Resume cancelled");
                        return;
                    }

                    currentState = ESPlayer.ESPlayerState.Ready;
                    break;

                // Suspended in Pause. Start then pause
                case ESPlayer.ESPlayerState.Ready:
                    player.Start();
                    currentState = ESPlayer.ESPlayerState.Playing;
                    break;

                case ESPlayer.ESPlayerState.Playing:
                    player.Pause();
                    currentState = ESPlayer.ESPlayerState.Paused;
                    break;
                }
            } while (currentState != targetState && !token.IsCancellationRequested);

            // Push out target state.
            switch (currentState)
            {
            case ESPlayer.ESPlayerState.Ready:
                SetState(PlayerState.Prepared);
                break;

            case ESPlayer.ESPlayerState.Playing:
                SetState(PlayerState.Playing);
                break;
            }
        }
        private async Task ChangeRepresentationInternal(object representation, bool isPending, CancellationToken token)
        {
            logger.Info("");

            using (await asyncOpSerializer.LockAsync(token))
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    if (_pauseReason == PauseReason.Requested)
                    {
                        _pendingRepresentation = representation;
                        logger.Info("RepresentationChange pending");
                        return;
                    }

                    // Pending representation change. Check if someone beat us to it.
                    // Depending how pending representation change gets scheduled, it's feasible for pending change to become
                    // outdated by another representation change.
                    if (isPending &&
                        (_pendingRepresentation == null || !ReferenceEquals(_pendingRepresentation, representation)))
                    {
                        logger.Info($"Stale pending representation change. Ignored.");
                        return;
                    }

                    player.Pause();
                    await FlushStreams();

                    player.GetPlayingTime(out var currentPlayerPosition);
                    var playerPosition = _pendingPosition ?? currentPlayerPosition;
                    _pendingPosition       = null;
                    _pendingRepresentation = null;

                    var(representationPosition, isCompatible) = await StartRepresentation(representation, playerPosition, token);

                    if (!isCompatible)
                    {
                        // Destructive player change results in packet loss.
                        // Reposition data provider.
                        DisableInput();
                        await FlushStreams();

                        var streamClock = await Client.Seek(playerPosition, CancellationToken.None);

                        EnableInput();

                        logger.Info($"Incompatible. Restarting player @{streamClock} Player clock was {playerPosition}");
                        await ChangeConfiguration(streamClock, token);

                        // In async op. Play() issued in ChangeConfiguration->RestoreState won't start clock and buffering events.
                        StartClockGenerator();
                        SubscribeBufferingEvent();
                        logger.Info("End");
                        return;
                    }

                    logger.Info($"Compatible. Repositionting to {playerPosition}");
                    if (await ExecuteSeek(playerPosition, token))
                    {
                        StartClockGenerator();
                        SubscribeBufferingEvent();
                        logger.Info("End");
                        return;
                    }
                }
                catch (SeekException e)
                {
                    var msg = $"ChangeRepresentation seek failed, reason \"{e.Message}\"";
                    logger.Error(msg);
                    playbackErrorSubject.OnNext(msg);

                    // Propagate subject content.
                    await Task.Yield();
                }
                catch (Exception ce)
                    when(ce is OperationCanceledException || ce is TaskCanceledException)
                    {
                        logger.Info($"ChangeRepresentation cancelled. Pending {isPending}");

                        _configurationsCollected = null;
                        // Don't terminate playback for cancelled pending change.
                        if (isPending)
                        {
                            return;
                        }
                    }
                catch (Exception e)
                {
                    logger.Error(e);
                    playbackErrorSubject.OnNext($"ChangeRepresentation failed");
                    throw;
                }

                player.SubmitEosPacket(ESPlayer.StreamType.Audio);
                player.SubmitEosPacket(ESPlayer.StreamType.Video);
                logger.Info("EOS Sent");
            }
        }