Bug 1257107: discard decoded data if its pts is smaller than seek time. r=jya draft
authorAlfredo Yang <ayang@mozilla.com>
Wed, 08 Jun 2016 10:59:57 +0800
changeset 376489 55406582ea4bc895ce0b4229d7fc877e8c63fc26
parent 376275 ec20b463c04f57a4bfca1edb987fcb9e9707c364
child 376490 75e0aa09f59236cf6f219c7f3a144db11cb86c85
push id20593
push userayang@mozilla.com
push dateWed, 08 Jun 2016 04:11:43 +0000
reviewersjya
bugs1257107
milestone50.0a1
Bug 1257107: discard decoded data if its pts is smaller than seek time. r=jya MozReview-Commit-ID: 70rbBsor8lp
dom/media/MediaData.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/platforms/PlatformDecoderModule.h
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -268,17 +268,18 @@ typedef AlignedBuffer<AudioDataValue> Al
 class MediaData {
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaData)
 
   enum Type {
     AUDIO_DATA = 0,
     VIDEO_DATA,
-    RAW_DATA
+    RAW_DATA,
+    NULL_DATA
   };
 
   MediaData(Type aType,
             int64_t aOffset,
             int64_t aTimestamp,
             int64_t aDuration,
             uint32_t aFrames)
     : mType(aType)
@@ -349,16 +350,27 @@ protected:
     , mKeyframe(false)
     , mDiscontinuity(false)
   {}
 
   virtual ~MediaData() {}
 
 };
 
+// NullData is for decoder generating a sample which doesn't need to be
+// rendered.
+class NullData : public MediaData {
+public:
+  NullData(int64_t aOffset, int64_t aTime, int64_t aDuration)
+    : MediaData(NULL_DATA, aOffset, aTime, aDuration, 0)
+  {}
+
+  static const Type sType = NULL_DATA;
+};
+
 // Holds chunk a decoded audio frames.
 class AudioData : public MediaData {
 public:
 
   AudioData(int64_t aOffset,
             int64_t aTime,
             int64_t aDuration,
             uint32_t aFrames,
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -457,16 +457,17 @@ MediaFormatReader::EnsureDecoderInitiali
   decoder.mInitPromise.Begin(decoder.mDecoder->Init()
        ->Then(OwnerThread(), __func__,
               [self] (TrackType aTrack) {
                 auto& decoder = self->GetDecoderData(aTrack);
                 decoder.mInitPromise.Complete();
                 decoder.mDecoderInitialized = true;
                 MonitorAutoLock mon(decoder.mMonitor);
                 decoder.mDescription = decoder.mDecoder->GetDescriptionName();
+                self->SetVideoDecodeThreshold();
                 self->ScheduleUpdate(aTrack);
               },
               [self, aTrack] (MediaDataDecoder::DecoderFailureReason aResult) {
                 auto& decoder = self->GetDecoderData(aTrack);
                 decoder.mInitPromise.Complete();
                 decoder.ShutdownDecoder();
                 self->NotifyError(aTrack);
               }));
@@ -1033,16 +1034,17 @@ MediaFormatReader::InternalSeek(TrackTyp
   decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().Time())
              ->Then(OwnerThread(), __func__,
                     [self, aTrack] (media::TimeUnit aTime) {
                       auto& decoder = self->GetDecoderData(aTrack);
                       decoder.mSeekRequest.Complete();
                       MOZ_ASSERT(decoder.mTimeThreshold,
                                  "Seek promise must be disconnected when timethreshold is reset");
                       decoder.mTimeThreshold.ref().mHasSeeked = true;
+                      self->SetVideoDecodeThreshold();
                       self->NotifyDecodingRequested(aTrack);
                     },
                     [self, aTrack] (DemuxerFailureReason aResult) {
                       auto& decoder = self->GetDecoderData(aTrack);
                       decoder.mSeekRequest.Complete();
                       switch (aResult) {
                         case DemuxerFailureReason::WAITING_FOR_DATA:
                           self->NotifyWaitingForData(aTrack);
@@ -1145,16 +1147,22 @@ MediaFormatReader::Update(TrackType aTra
            media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
            target.Time().ToSeconds(),
            output->mKeyframe);
       decoder.mOutput.RemoveElementAt(0);
       decoder.mSizeOfQueue -= 1;
     }
   }
 
+  while (decoder.mOutput.Length() && decoder.mOutput[0]->mType == MediaData::NULL_DATA) {
+    LOGV("Dropping null data. Time: %lld", decoder.mOutput[0]->mTime);
+    decoder.mOutput.RemoveElementAt(0);
+    decoder.mSizeOfQueue -= 1;
+  }
+
   if (decoder.HasPromise()) {
     needOutput = true;
     if (decoder.mOutput.Length()) {
       RefPtr<MediaData> output = decoder.mOutput[0];
       decoder.mOutput.RemoveElementAt(0);
       decoder.mSizeOfQueue -= 1;
       decoder.mLastSampleTime =
         Some(TimeInterval(TimeUnit::FromMicroseconds(output->mTime),
@@ -1264,16 +1272,17 @@ MediaFormatReader::Update(TrackType aTra
   HandleDemuxedSamples(aTrack, a);
 }
 
 void
 MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack)
 {
   auto& decoder = GetDecoderData(aTrack);
   MOZ_ASSERT(decoder.HasPromise());
+  MOZ_DIAGNOSTIC_ASSERT(aData->mType != MediaData::NULL_DATA);
   if (decoder.mDiscontinuity) {
     LOGV("Setting discontinuity flag");
     decoder.mDiscontinuity = false;
     aData->mDiscontinuity = true;
   }
 
   LOG("Resolved data promise for %s [%lld, %lld]", TrackTypeToStr(aTrack),
       aData->mTime, aData->GetEndTime());
@@ -1716,31 +1725,71 @@ MediaFormatReader::DoVideoSeek()
 
 void
 MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("Video seeked to %lld", aTime.ToMicroseconds());
   mVideo.mSeekRequest.Complete();
 
+  SetVideoDecodeThreshold();
+
   if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) {
     MOZ_ASSERT(mPendingSeekTime.isSome());
     if (mOriginalSeekTarget.IsFast()) {
       // We are performing a fast seek. We need to seek audio to where the
       // video seeked to, to ensure proper A/V sync once playback resume.
       mPendingSeekTime = Some(aTime);
     }
     DoAudioSeek();
   } else {
     mPendingSeekTime.reset();
     mSeekPromise.Resolve(aTime, __func__);
   }
 }
 
 void
+MediaFormatReader::SetVideoDecodeThreshold()
+{
+  MOZ_ASSERT(OnTaskQueue());
+
+  if (!HasVideo() || !mVideo.mDecoder) {
+    return;
+  }
+
+  if (!mVideo.mTimeThreshold && !IsSeeking()) {
+    return;
+  }
+
+  TimeUnit threshold;
+  if (mVideo.mTimeThreshold) {
+    // For internalSeek.
+    threshold = mVideo.mTimeThreshold.ref().Time();
+  } else if (IsSeeking()) {
+    // If IsSeeking() is true, then video seek must have completed already.
+    TimeUnit keyframe;
+    if (NS_FAILED(mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&keyframe))) {
+      return;
+    }
+
+    // If the key frame is invalid/infinite, it means the target position is
+    // closing to end of stream. We don't want to skip any frame at this point.
+    if (!keyframe.IsValid() || keyframe.IsInfinite()) {
+      return;
+    }
+    threshold = mOriginalSeekTarget.GetTime();
+  } else {
+    return;
+  }
+
+  LOG("Set seek threshold to %lld", threshold.ToMicroseconds());
+  mVideo.mDecoder->SetSeekThreshold(threshold);
+}
+
+void
 MediaFormatReader::DoAudioSeek()
 {
   MOZ_ASSERT(mPendingSeekTime.isSome());
   LOGV("Seeking audio to %lld", mPendingSeekTime.ref().ToMicroseconds());
   media::TimeUnit seekTime = mPendingSeekTime.ref();
   mAudio.mSeekRequest.Begin(mAudio.mTrackDemuxer->Seek(seekTime)
                          ->Then(OwnerThread(), __func__, this,
                                 &MediaFormatReader::OnAudioSeekCompleted,
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -178,16 +178,18 @@ private:
   void InputExhausted(TrackType aTrack);
   void Error(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR);
   void Reset(TrackType aTrack);
   void DrainComplete(TrackType aTrack);
   void DropDecodedSamples(TrackType aTrack);
 
   bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
 
+  void SetVideoDecodeThreshold();
+
   size_t SizeOfQueue(TrackType aTrack);
 
   RefPtr<PDMFactory> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
   public:
     DecoderCallback(MediaFormatReader* aReader, TrackType aType)
       : mReader(aReader)
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -230,13 +230,21 @@ public:
   {
     return NS_OK;
   }
 
   // Return the name of the MediaDataDecoder, only used for decoding.
   // Only return a static const string, as the information may be accessed
   // in a non thread-safe fashion.
   virtual const char* GetDescriptionName() const = 0;
+
+  // Set a hint of seek target time to decoder. Decoder will drop any decoded
+  // data which pts is smaller than this value. This threshold needs to be clear
+  // after reset decoder.
+  // Decoder may not honor this value. However, it'd be better that
+  // video decoder implements this API to improve seek performance.
+  // Note: it should be called before Input() or after Flush().
+  virtual void SetSeekThreshold(const media::TimeUnit& aTime) {}
 };
 
 } // namespace mozilla
 
 #endif