mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 1121692 - Make seeks cancelable. r=cpearce,r=mattwoodrow
This commit is contained in:
parent
f5e1dd8c14
commit
986f783e03
@ -632,10 +632,8 @@ nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
||||
mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
|
||||
mCurrentTime = aTime;
|
||||
|
||||
// If we are already in the seeking state, then setting mRequestedSeekTarget
|
||||
// above will result in the new seek occurring when the current seek
|
||||
// completes.
|
||||
if (mPlayState != PLAY_STATE_LOADING && mPlayState != PLAY_STATE_SEEKING) {
|
||||
// If we are already in the seeking state, the new seek overrides the old one.
|
||||
if (mPlayState != PLAY_STATE_LOADING) {
|
||||
bool paused = false;
|
||||
if (mOwner) {
|
||||
paused = mOwner->GetPaused();
|
||||
|
@ -156,6 +156,15 @@ public:
|
||||
virtual nsRefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) = 0;
|
||||
|
||||
// Cancels an ongoing seek, if any. Any previously-requested seek is
|
||||
// guaranteeed to be resolved or rejected in finite time, though no
|
||||
// guarantees are made about precise nature of the resolve/reject, since the
|
||||
// promise might have already dispatched a resolution or an error code before
|
||||
// the cancel arrived.
|
||||
//
|
||||
// Must be called on the decode task queue.
|
||||
virtual void CancelSeek() { };
|
||||
|
||||
// Called to move the reader into idle state. When the reader is
|
||||
// created it is assumed to be active (i.e. not idle). When the media
|
||||
// element is paused and we don't need to decode any more data, the state
|
||||
|
@ -220,6 +220,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mDropVideoUntilNextDiscontinuity(false),
|
||||
mDecodeToSeekTarget(false),
|
||||
mWaitingForDecoderSeek(false),
|
||||
mCancelingSeek(false),
|
||||
mCurrentTimeBeforeSeek(0),
|
||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
||||
mDecodingFrozenAtStateDecoding(false),
|
||||
@ -1703,10 +1704,6 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
||||
return;
|
||||
}
|
||||
|
||||
// MediaDecoder::mPlayState should be SEEKING while we seek, and
|
||||
// in that case MediaDecoder shouldn't be calling us.
|
||||
NS_ASSERTION(mState != DECODER_STATE_SEEKING,
|
||||
"We shouldn't already be seeking");
|
||||
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
|
||||
"We should have got duration already");
|
||||
|
||||
@ -2381,12 +2378,28 @@ void MediaDecoderStateMachine::DecodeSeek()
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
if (mState != DECODER_STATE_SEEKING ||
|
||||
!mSeekTarget.IsValid() ||
|
||||
mCurrentSeekTarget.IsValid()) {
|
||||
!mSeekTarget.IsValid()) {
|
||||
DECODER_LOG("Early returning from DecodeSeek");
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's already an existing seek in progress, we need to handle that.
|
||||
if (mCurrentSeekTarget.IsValid()) {
|
||||
// There are 3 states we might be in, listed in the order that they occur:
|
||||
// (1) Waiting for the seek to be resolved.
|
||||
// (2) Waiting for the seek to be resolved, having already issued a cancel.
|
||||
// (3) After seek resolution, waiting for SeekComplete to run.
|
||||
//
|
||||
// If we're in the first state, we move to the second. Otherwise, we just wait
|
||||
// for things to sort themselves out.
|
||||
if (mWaitingForDecoderSeek && !mCancelingSeek) {
|
||||
mReader->CancelSeek();
|
||||
mCancelingSeek = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentSeekTarget = mSeekTarget;
|
||||
mSeekTarget.Reset();
|
||||
mDropAudioUntilNextDiscontinuity = HasAudio();
|
||||
@ -2470,6 +2483,7 @@ MediaDecoderStateMachine::OnSeekCompleted(int64_t aTime)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mWaitingForDecoderSeek = false;
|
||||
mCancelingSeek = false;
|
||||
|
||||
// We must decode the first samples of active streams, so we can determine
|
||||
// the new stream time. So dispatch tasks to do that.
|
||||
@ -2481,12 +2495,21 @@ void
|
||||
MediaDecoderStateMachine::OnSeekFailed(nsresult aResult)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
bool wasCanceled = mCancelingSeek;
|
||||
mWaitingForDecoderSeek = false;
|
||||
// Sometimes we reject the promise for non-failure reasons, like
|
||||
// when we request a second seek before the previous one has
|
||||
// completed.
|
||||
mCancelingSeek = false;
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
DecodeError();
|
||||
} else if (wasCanceled && mSeekTarget.IsValid() && mState == DECODER_STATE_SEEKING) {
|
||||
// Try again.
|
||||
mCurrentSeekTarget = mSeekTarget;
|
||||
mSeekTarget.Reset();
|
||||
mReader->Seek(mCurrentSeekTarget.mTime, mEndTime)
|
||||
->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnSeekCompleted,
|
||||
&MediaDecoderStateMachine::OnSeekFailed);
|
||||
mWaitingForDecoderSeek = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2549,7 +2572,12 @@ MediaDecoderStateMachine::SeekCompleted()
|
||||
|
||||
nsCOMPtr<nsIRunnable> stopEvent;
|
||||
bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
|
||||
if (GetMediaTime() == mEndTime && !isLiveStream) {
|
||||
if (mSeekTarget.IsValid()) {
|
||||
// A new seek target came in while we were processing the old one. No rest
|
||||
// for the seeking.
|
||||
DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING");
|
||||
SetState(DECODER_STATE_SEEKING);
|
||||
} else if (GetMediaTime() == mEndTime && !isLiveStream) {
|
||||
// Seeked to end of media, move to COMPLETED state. Note we don't do
|
||||
// this if we're playing a live stream, since the end of media will advance
|
||||
// once we download more data!
|
||||
|
@ -1112,6 +1112,10 @@ protected:
|
||||
// until this completes.
|
||||
bool mWaitingForDecoderSeek;
|
||||
|
||||
// True if we're in the process of canceling a seek. This allows us to avoid
|
||||
// invoking CancelSeek() multiple times.
|
||||
bool mCancelingSeek;
|
||||
|
||||
// We record the playback position before we seek in order to
|
||||
// determine where the seek terminated relative to the playback position
|
||||
// we were at before the seek.
|
||||
|
@ -627,7 +627,7 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
|
||||
MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
|
||||
this, aTime);
|
||||
|
||||
mSeekPromise.RejectIfExists(NS_OK, __func__);
|
||||
MOZ_ASSERT(mSeekPromise.IsEmpty());
|
||||
nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
|
||||
|
||||
if (IsShutdown()) {
|
||||
@ -639,9 +639,6 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
|
||||
// the desired time and we delay doing the seek.
|
||||
mPendingSeekTime = aTime;
|
||||
|
||||
// Only increment the number of expected OnSeekCompleted
|
||||
// notifications if we weren't already waiting for AttemptSeek
|
||||
// to complete (and they would have been accounted for already).
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mWaitingForSeekData = true;
|
||||
@ -651,6 +648,26 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::CancelSeek()
|
||||
{
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (mWaitingForSeekData) {
|
||||
mSeekPromise.Reject(NS_OK, __func__);
|
||||
mWaitingForSeekData = false;
|
||||
mPendingSeekTime = -1;
|
||||
} else if (mVideoIsSeeking) {
|
||||
// NB: Currently all readers have sync Seeks(), so this is a no-op.
|
||||
mVideoReader->CancelSeek();
|
||||
} else if (mAudioIsSeeking) {
|
||||
// NB: Currently all readers have sync Seeks(), so this is a no-op.
|
||||
mAudioReader->CancelSeek();
|
||||
} else {
|
||||
MOZ_ASSERT(mSeekPromise.IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::OnVideoSeekCompleted(int64_t aTime)
|
||||
{
|
||||
|
@ -99,6 +99,8 @@ public:
|
||||
nsRefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
|
||||
|
||||
void CancelSeek() MOZ_OVERRIDE;
|
||||
|
||||
// Acquires the decoder monitor, and is thus callable on any thread.
|
||||
nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user