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);
|
mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
|
||||||
mCurrentTime = aTime;
|
mCurrentTime = aTime;
|
||||||
|
|
||||||
// If we are already in the seeking state, then setting mRequestedSeekTarget
|
// If we are already in the seeking state, the new seek overrides the old one.
|
||||||
// above will result in the new seek occurring when the current seek
|
if (mPlayState != PLAY_STATE_LOADING) {
|
||||||
// completes.
|
|
||||||
if (mPlayState != PLAY_STATE_LOADING && mPlayState != PLAY_STATE_SEEKING) {
|
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
if (mOwner) {
|
if (mOwner) {
|
||||||
paused = mOwner->GetPaused();
|
paused = mOwner->GetPaused();
|
||||||
|
@ -156,6 +156,15 @@ public:
|
|||||||
virtual nsRefPtr<SeekPromise>
|
virtual nsRefPtr<SeekPromise>
|
||||||
Seek(int64_t aTime, int64_t aEndTime) = 0;
|
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
|
// 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
|
// 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
|
// 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),
|
mDropVideoUntilNextDiscontinuity(false),
|
||||||
mDecodeToSeekTarget(false),
|
mDecodeToSeekTarget(false),
|
||||||
mWaitingForDecoderSeek(false),
|
mWaitingForDecoderSeek(false),
|
||||||
|
mCancelingSeek(false),
|
||||||
mCurrentTimeBeforeSeek(0),
|
mCurrentTimeBeforeSeek(0),
|
||||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
||||||
mDecodingFrozenAtStateDecoding(false),
|
mDecodingFrozenAtStateDecoding(false),
|
||||||
@ -1703,10 +1704,6 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
|||||||
return;
|
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,
|
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
|
||||||
"We should have got duration already");
|
"We should have got duration already");
|
||||||
|
|
||||||
@ -2381,12 +2378,28 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||||
|
|
||||||
if (mState != DECODER_STATE_SEEKING ||
|
if (mState != DECODER_STATE_SEEKING ||
|
||||||
!mSeekTarget.IsValid() ||
|
!mSeekTarget.IsValid()) {
|
||||||
mCurrentSeekTarget.IsValid()) {
|
|
||||||
DECODER_LOG("Early returning from DecodeSeek");
|
DECODER_LOG("Early returning from DecodeSeek");
|
||||||
return;
|
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;
|
mCurrentSeekTarget = mSeekTarget;
|
||||||
mSeekTarget.Reset();
|
mSeekTarget.Reset();
|
||||||
mDropAudioUntilNextDiscontinuity = HasAudio();
|
mDropAudioUntilNextDiscontinuity = HasAudio();
|
||||||
@ -2470,6 +2483,7 @@ MediaDecoderStateMachine::OnSeekCompleted(int64_t aTime)
|
|||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
mWaitingForDecoderSeek = false;
|
mWaitingForDecoderSeek = false;
|
||||||
|
mCancelingSeek = false;
|
||||||
|
|
||||||
// We must decode the first samples of active streams, so we can determine
|
// We must decode the first samples of active streams, so we can determine
|
||||||
// the new stream time. So dispatch tasks to do that.
|
// the new stream time. So dispatch tasks to do that.
|
||||||
@ -2481,12 +2495,21 @@ void
|
|||||||
MediaDecoderStateMachine::OnSeekFailed(nsresult aResult)
|
MediaDecoderStateMachine::OnSeekFailed(nsresult aResult)
|
||||||
{
|
{
|
||||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
|
bool wasCanceled = mCancelingSeek;
|
||||||
mWaitingForDecoderSeek = false;
|
mWaitingForDecoderSeek = false;
|
||||||
// Sometimes we reject the promise for non-failure reasons, like
|
mCancelingSeek = false;
|
||||||
// when we request a second seek before the previous one has
|
|
||||||
// completed.
|
|
||||||
if (NS_FAILED(aResult)) {
|
if (NS_FAILED(aResult)) {
|
||||||
DecodeError();
|
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;
|
nsCOMPtr<nsIRunnable> stopEvent;
|
||||||
bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
|
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
|
// 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
|
// this if we're playing a live stream, since the end of media will advance
|
||||||
// once we download more data!
|
// once we download more data!
|
||||||
|
@ -1112,6 +1112,10 @@ protected:
|
|||||||
// until this completes.
|
// until this completes.
|
||||||
bool mWaitingForDecoderSeek;
|
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
|
// We record the playback position before we seek in order to
|
||||||
// determine where the seek terminated relative to the playback position
|
// determine where the seek terminated relative to the playback position
|
||||||
// we were at before the seek.
|
// 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)",
|
MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
|
||||||
this, aTime);
|
this, aTime);
|
||||||
|
|
||||||
mSeekPromise.RejectIfExists(NS_OK, __func__);
|
MOZ_ASSERT(mSeekPromise.IsEmpty());
|
||||||
nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
|
nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
|
||||||
|
|
||||||
if (IsShutdown()) {
|
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.
|
// the desired time and we delay doing the seek.
|
||||||
mPendingSeekTime = aTime;
|
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());
|
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||||
mWaitingForSeekData = true;
|
mWaitingForSeekData = true;
|
||||||
@ -651,6 +648,26 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg whi
|
|||||||
return p;
|
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
|
void
|
||||||
MediaSourceReader::OnVideoSeekCompleted(int64_t aTime)
|
MediaSourceReader::OnVideoSeekCompleted(int64_t aTime)
|
||||||
{
|
{
|
||||||
|
@ -99,6 +99,8 @@ public:
|
|||||||
nsRefPtr<SeekPromise>
|
nsRefPtr<SeekPromise>
|
||||||
Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
|
Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
void CancelSeek() MOZ_OVERRIDE;
|
||||||
|
|
||||||
// Acquires the decoder monitor, and is thus callable on any thread.
|
// Acquires the decoder monitor, and is thus callable on any thread.
|
||||||
nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
|
nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user