Bug 1272964: P4. Only flush decoder if skip to next keyframe actually succeeds. r?cpearce
As the decoder was flushed and reset prior the skip to next keyframe started, and future error would be unrecoverable. So only reset the decoder once the skip completes and succeeded. Otherwise we default back to normal decoding.
MozReview-Commit-ID: GEj1i0EsaYO
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1095,16 +1095,21 @@ MediaFormatReader::Update(TrackType aTra
bool needOutput = false;
auto& decoder = GetDecoderData(aTrack);
decoder.mUpdateScheduled = false;
if (!mInitDone) {
return;
}
+ if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) {
+ LOGV("Skipping in progress, nothing more to do");
+ return;
+ }
+
if (UpdateReceivedNewData(aTrack)) {
LOGV("Nothing more to do");
return;
}
if (decoder.mSeekRequest.Exists()) {
LOGV("Seeking hasn't completed, nothing more to do");
return;
@@ -1404,86 +1409,77 @@ MediaFormatReader::Reset(TrackType aTrac
void
MediaFormatReader::SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mVideo.HasPromise());
LOG("Skipping up to %lld", aTimeThreshold.ToMicroseconds());
- // Cancel any pending demux request and pending demuxed samples.
- mVideo.mDemuxRequest.DisconnectIfExists();
-
- // I think it's still possible for an output to have been sent from the decoder
- // and is currently sitting in our event queue waiting to be processed. The following
- // flush won't clear it, and when we return to the event loop it'll be added to our
- // output queue and be used.
- // This code will count that as dropped, which was the intent, but not quite true.
- mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
-
- if (mVideo.mTimeThreshold) {
- LOGV("Internal Seek pending, cancelling it");
- }
- Reset(TrackInfo::kVideoTrack);
-
- if (mVideo.mError) {
- // We have flushed the decoder, and we are in error state, we can
- // immediately reject the promise as there is nothing more to do.
- mVideo.RejectPromise(DECODE_ERROR, __func__);
- return;
- }
-
mSkipRequest.Begin(mVideo.mTrackDemuxer->SkipToNextRandomAccessPoint(aTimeThreshold)
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnVideoSkipCompleted,
&MediaFormatReader::OnVideoSkipFailed));
return;
}
void
+MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
+{
+ MOZ_ASSERT(OnTaskQueue());
+ // I think it's still possible for an output to have been sent from the decoder
+ // and is currently sitting in our event queue waiting to be processed. The following
+ // flush won't clear it, and when we return to the event loop it'll be added to our
+ // output queue and be used.
+ // This code will count that as dropped, which was the intent, but not quite true.
+ if (mDecoder) {
+ mDecoder->NotifyDecodedFrames(0, 0, SizeOfVideoQueueInFrames());
+ }
+
+ // Cancel any pending demux request and pending demuxed samples.
+ mVideo.mDemuxRequest.DisconnectIfExists();
+ Reset(TrackType::kVideoTrack);
+
+ if (mDecoder) {
+ mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
+ }
+
+ mVideo.mNumSamplesSkippedTotal += aSkipped;
+ mVideo.mNumSamplesSkippedTotalSinceTelemetry += aSkipped;
+}
+
+void
MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping succeeded, skipped %u frames", aSkipped);
mSkipRequest.Complete();
- if (mDecoder) {
- mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
- }
- mVideo.mNumSamplesSkippedTotal += aSkipped;
- mVideo.mNumSamplesSkippedTotalSinceTelemetry += aSkipped;
- MOZ_ASSERT(!mVideo.mError); // We have flushed the decoder, no frame could
- // have been decoded (and as such errored)
+
+ VideoSkipReset(aSkipped);
+
NotifyDecodingRequested(TrackInfo::kVideoTrack);
}
void
MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure)
{
MOZ_ASSERT(OnTaskQueue());
LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
mSkipRequest.Complete();
- if (mDecoder) {
- mDecoder->NotifyDecodedFrames(aFailure.mSkipped, 0, aFailure.mSkipped);
- }
+
MOZ_ASSERT(mVideo.HasPromise());
switch (aFailure.mFailure) {
case DemuxerFailureReason::END_OF_STREAM:
+ VideoSkipReset(aFailure.mSkipped);
NotifyEndOfStream(TrackType::kVideoTrack);
break;
case DemuxerFailureReason::WAITING_FOR_DATA:
- // While there is nothing to drain considering the decoder has been
- // flushed in SkipVideoDemuxToNextKeyFrame, we need to set mNeedDraining
- // to true as the video MediaDataPromise will only be rejected once drain
- // has completed.
- MOZ_DIAGNOSTIC_ASSERT(!mVideo.mDecodingRequested,
- "Reset must have been called");
- if (!mVideo.mWaitingForData) {
- mVideo.mNeedDraining = true;
- }
- NotifyWaitingForData(TrackType::kVideoTrack);
+ // We can't complete the skip operation, will just service a video frame
+ // normally.
+ NotifyDecodingRequested(TrackInfo::kVideoTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::SHUTDOWN:
if (mVideo.HasPromise()) {
mVideo.RejectPromise(CANCELED, __func__);
}
break;
default:
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -480,16 +480,17 @@ private:
void OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
void OnAudioDemuxFailed(DemuxerFailureReason aFailure)
{
OnDemuxFailed(TrackType::kAudioTrack, aFailure);
}
void SkipVideoDemuxToNextKeyFrame(media::TimeUnit aTimeThreshold);
MozPromiseRequestHolder<MediaTrackDemuxer::SkipAccessPointPromise> mSkipRequest;
+ void VideoSkipReset(uint32_t aSkipped);
void OnVideoSkipCompleted(uint32_t aSkipped);
void OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailure);
// The last number of decoded output frames that we've reported to
// MediaDecoder::NotifyDecoded(). We diff the number of output video
// frames every time that DecodeVideoData() is called, and report the
// delta there.
uint64_t mLastReportedNumDecodedFrames;