Bug 1336947: P1. Drain decoder until no more data is returned. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 07 Feb 2017 15:36:20 +0100
changeset 480637 74d30ad2d368fd828be2490c47d0fa8b58808538
parent 480238 f4f374622111022d41dd8d5eb9220624135c534a
child 480638 dd0be3835d1dd55063d2025adfe5dbde86c0cdd4
push id44612
push userbmo:jyavenard@mozilla.com
push dateWed, 08 Feb 2017 19:03:27 +0000
reviewersgerald
bugs1336947
milestone54.0a1
Bug 1336947: P1. Drain decoder until no more data is returned. r?gerald It may not be possible for a decoder to return all drained samples in one go. This is particularly a problem on android where there's a limited amount of memory buffers available. So we no longer expect the operation to complete in one go, we call Drain until there are no more samples returned. MozReview-Commit-ID: GbfntKNBDaW
dom/media/MediaFormatReader.cpp
dom/media/platforms/PlatformDecoderModule.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1886,32 +1886,38 @@ MediaFormatReader::DrainDecoder(TrackTyp
 {
   MOZ_ASSERT(OnTaskQueue());
 
   auto& decoder = GetDecoderData(aTrack);
   if (!decoder.mNeedDraining || decoder.mDraining) {
     return;
   }
   decoder.mNeedDraining = false;
+  decoder.mDraining = true;
   if (!decoder.mDecoder ||
       decoder.mNumSamplesInput == decoder.mNumSamplesOutput) {
     // No frames to drain.
     LOGV("Draining %s with nothing to drain", TrackTypeToStr(aTrack));
     NotifyDrainComplete(aTrack);
     return;
   }
-  decoder.mDraining = true;
   RefPtr<MediaFormatReader> self = this;
   decoder.mDecoder->Drain()
     ->Then(mTaskQueue, __func__,
            [self, this, aTrack, &decoder]
            (const MediaDataDecoder::DecodedData& aResults) {
              decoder.mDrainRequest.Complete();
-             NotifyNewOutput(aTrack, aResults);
-             NotifyDrainComplete(aTrack);
+             if (aResults.IsEmpty()) {
+               NotifyDrainComplete(aTrack);
+             } else {
+               NotifyNewOutput(aTrack, aResults);
+               // Let's see if we have any more data available to drain.
+               decoder.mNeedDraining = true;
+               decoder.mDraining = false;
+             }
            },
            [self, this, aTrack, &decoder](const MediaResult& aError) {
              decoder.mDrainRequest.Complete();
              NotifyError(aTrack, aError);
            })
     ->Track(decoder.mDrainRequest);
   LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack));
 }
@@ -2022,25 +2028,23 @@ MediaFormatReader::Update(TrackType aTra
         mVideo.mIsHardwareAccelerated =
           mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
       }
     } else if (decoder.HasFatalError()) {
       LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
       decoder.RejectPromise(decoder.mError.ref(), __func__);
       return;
     } else if (decoder.mDrainComplete) {
-      bool wasDraining = decoder.mDraining;
       decoder.mDrainComplete = false;
       decoder.mDraining = false;
       if (decoder.mDemuxEOS) {
         LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
         decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
       } else if (decoder.mWaitingForData) {
-        if (wasDraining && decoder.mLastSampleTime &&
-            !decoder.mNextStreamSourceID) {
+        if (decoder.mLastSampleTime && !decoder.mNextStreamSourceID) {
           // We have completed draining the decoder following WaitingForData.
           // Set up the internal seek machinery to be able to resume from the
           // last sample decoded.
           LOG("Seeking to last sample time: %lld",
               decoder.mLastSampleTime.ref().mStart.ToMicroseconds());
           InternalSeek(aTrack,
                        InternalSeekTarget(decoder.mLastSampleTime.ref(), true));
         }
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -246,18 +246,21 @@ public:
   // indicate that Decode should be called again before a MediaData is returned.
   virtual RefPtr<DecodePromise> Decode(MediaRawData* aSample) = 0;
 
   // Causes all complete samples in the pipeline that can be decoded to be
   // output. If the decoder can't produce samples from the current output,
   // it drops the input samples. The decoder may be holding onto samples
   // that are required to decode samples that it expects to get in future.
   // This is called when the demuxer reaches end of stream.
-  // This function is asynchronous. The MediaDataDecoder shall resolve the
-  // pending DecodePromise will all drained samples.
+  // This function is asynchronous.
+  // The MediaDataDecoder shall resolve the pending DecodePromise with drained
+  // samples. Drain will be called multiple times until the resolved
+  // DecodePromise is empty which indicates that there are no more samples to
+  // drain.
   virtual RefPtr<DecodePromise> Drain() = 0;
 
   // Causes all samples in the decoding pipeline to be discarded. When this
   // promise resolves, the decoder must be ready to accept new data for
   // decoding. This function is called when the demuxer seeks, before decoding
   // resumes after the seek. The current DecodePromise if any shall be rejected
   // with NS_ERROR_DOM_MEDIA_CANCELED
   virtual RefPtr<FlushPromise> Flush() = 0;