Bug 1269408: P3. Ensure a new seek request will cancel the previous internal seek. r?gerald
MozReview-Commit-ID: 3dR8JWt4KSN
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -65,16 +65,17 @@ MediaFormatReader::MediaFormatReader(Abs
, mDemuxer(aDemuxer)
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mDemuxOnly(false)
+ , mSeekScheduled(false)
, mVideoFrameContainer(aVideoFrameContainer)
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
}
MediaFormatReader::~MediaFormatReader()
{
@@ -824,38 +825,42 @@ MediaFormatReader::UpdateReceivedNewData
decoder.mTimeThreshold.ref().mWaiting = false;
}
decoder.mWaitingForData = false;
if (decoder.mError) {
return false;
}
+ if (!mSeekPromise.IsEmpty()) {
+ MOZ_ASSERT(!decoder.HasPromise());
+ MOZ_DIAGNOSTIC_ASSERT(!mAudio.mTimeThreshold && !mVideo.mTimeThreshold,
+ "InternalSeek must have been aborted when Seek was first called");
+ MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasWaitingPromise() && !mVideo.HasWaitingPromise(),
+ "Waiting promises must have been rejected when Seek was first called");
+ if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) {
+ // Already waiting for a seek to complete. Nothing more to do.
+ return true;
+ }
+ LOG("Attempting Seek");
+ ScheduleSeek();
+ return true;
+ }
if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) {
if (decoder.HasInternalSeekPending()) {
LOG("Attempting Internal Seek");
InternalSeek(aTrack, decoder.mTimeThreshold.ref());
}
if (decoder.HasWaitingPromise()) {
MOZ_ASSERT(!decoder.HasPromise());
LOG("We have new data. Resolving WaitingPromise");
decoder.mWaitingPromise.Resolve(decoder.mType, __func__);
}
return true;
}
- if (!mSeekPromise.IsEmpty()) {
- MOZ_ASSERT(!decoder.HasPromise());
- if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) {
- // Already waiting for a seek to complete. Nothing more to do.
- return true;
- }
- LOG("Attempting Seek");
- AttemptSeek();
- return true;
- }
return false;
}
void
MediaFormatReader::RequestDemuxSamples(TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
@@ -1296,34 +1301,47 @@ MediaFormatReader::ResetDecode()
// Do the same for any data wait promises.
mAudio.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
mVideo.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__);
// Reset miscellaneous seeking state.
mPendingSeekTime.reset();
+ ResetDemuxers();
+
if (HasVideo()) {
- mVideo.ResetDemuxer();
Flush(TrackInfo::kVideoTrack);
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
}
}
if (HasAudio()) {
- mAudio.ResetDemuxer();
Flush(TrackInfo::kAudioTrack);
if (mAudio.HasPromise()) {
mAudio.RejectPromise(CANCELED, __func__);
}
}
return MediaDecoderReader::ResetDecode();
}
void
+MediaFormatReader::ResetDemuxers()
+{
+ if (HasVideo()) {
+ mVideo.ResetDemuxer();
+ mVideo.ResetState();
+ }
+ if (HasAudio()) {
+ mAudio.ResetDemuxer();
+ mAudio.ResetState();
+ }
+}
+
+void
MediaFormatReader::Output(TrackType aTrack, MediaData* aSample)
{
LOGV("Decoded %s sample time=%lld timecode=%lld kf=%d dur=%lld",
TrackTypeToStr(aTrack), aSample->mTime, aSample->mTimecode,
aSample->mKeyframe, aSample->mDuration);
if (!aSample) {
NS_WARNING("MediaFormatReader::Output() passed a null sample");
@@ -1474,32 +1492,42 @@ MediaFormatReader::Seek(SeekTarget aTarg
return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
mOriginalSeekTarget = Some(aTarget);
mPendingSeekTime = Some(aTarget.GetTime());
RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
- OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaFormatReader::AttemptSeek));
+ ScheduleSeek();
return p;
}
void
+MediaFormatReader::ScheduleSeek()
+{
+ if (mSeekScheduled) {
+ return;
+ }
+ mSeekScheduled = true;
+ OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaFormatReader::AttemptSeek));
+}
+
+void
MediaFormatReader::AttemptSeek()
{
MOZ_ASSERT(OnTaskQueue());
+
+ mSeekScheduled = false;
+
if (mPendingSeekTime.isNothing()) {
return;
}
- // An internal seek may be pending due to Seek queueing multiple tasks calling
- // AttemptSeek ; we can ignore those by resetting any pending demuxer's seek.
- mAudio.mSeekRequest.DisconnectIfExists();
- mVideo.mSeekRequest.DisconnectIfExists();
+ ResetDemuxers();
if (HasVideo()) {
DoVideoSeek();
} else if (HasAudio()) {
DoAudioSeek();
} else {
MOZ_CRASH();
}
}
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -169,16 +169,17 @@ private:
// functions.
void Output(TrackType aType, MediaData* aSample);
void InputExhausted(TrackType aTrack);
void Error(TrackType aTrack);
void Flush(TrackType aTrack);
void DrainComplete(TrackType aTrack);
bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
+ void ResetDemuxers();
size_t SizeOfQueue(TrackType aTrack);
RefPtr<PDMFactory> mPlatform;
class DecoderCallback : public MediaDataDecoderCallback {
public:
DecoderCallback(MediaFormatReader* aReader, TrackType aType)
@@ -457,24 +458,26 @@ private:
// Set to true if any of our track buffers may be blocking.
bool mTrackDemuxersMayBlock;
// Set the demuxed-only flag.
Atomic<bool> mDemuxOnly;
// Seeking objects.
bool IsSeeking() const { return mPendingSeekTime.isSome(); }
+ void ScheduleSeek();
void AttemptSeek();
void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
void DoVideoSeek();
void OnVideoSeekCompleted(media::TimeUnit aTime);
void OnVideoSeekFailed(DemuxerFailureReason aFailure)
{
OnSeekFailed(TrackType::kVideoTrack, aFailure);
}
+ bool mSeekScheduled;
void DoAudioSeek();
void OnAudioSeekCompleted(media::TimeUnit aTime);
void OnAudioSeekFailed(DemuxerFailureReason aFailure)
{
OnSeekFailed(TrackType::kAudioTrack, aFailure);
}