Bug 1096089: Only return end of stream if we're near the known duration. r=mattwoodrow
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sat, 24 Jan 2015 21:46:21 +1100
changeset 225514 2cff252b37e2332d79e5a40d9bce36c5613ba3aa
parent 225513 19608b0262aba7282ff3c18c7ee1bc7a26e8bff5
child 225515 42d4b2cb18b6567890c2113784761eb8f17337a1
push id28163
push userphilringnalda@gmail.com
push dateSat, 24 Jan 2015 16:27:39 +0000
treeherdermozilla-central@1cf171c1a177 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1096089
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1096089: Only return end of stream if we're near the known duration. r=mattwoodrow YouTube will call endOfStream once the video has been entirely buffered. When changing video quality, it will clear the entire source buffer, following which it will append data once again which will reopen the mediasource. We don't want to consider a mediasource in ended state as not ever going to receive more data in the future unless we're actually reached the end.
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/MediaSourceReader.h
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -235,16 +235,19 @@ MediaSourceDecoder::DoSetMediaSourceDura
 {
   if (aDuration >= 0) {
     mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
     mMediaSourceDuration = aDuration;
   } else {
     mDecoderStateMachine->SetDuration(INT64_MAX);
     mMediaSourceDuration = PositiveInfinity<double>();
   }
+  if (mReader) {
+    mReader->SetMediaSourceDuration(mMediaSourceDuration);
+  }
 }
 
 void
 MediaSourceDecoder::ScheduleDurationChange(double aOldDuration,
                                            double aNewDuration,
                                            MSRangeRemovalAction aAction)
 {
   if (aAction == MSRangeRemovalAction::SKIP) {
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -51,16 +51,17 @@ MediaSourceReader::MediaSourceReader(Med
   , mPendingSeekTime(-1)
   , mWaitingForSeekData(false)
   , mAudioIsSeeking(false)
   , mVideoIsSeeking(false)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
   , mEnded(false)
+  , mMediaSourceDuration(0)
   , mHasEssentialTrackBuffers(false)
 #ifdef MOZ_FMP4
   , mSharedDecoderManager(new SharedDecoderManager())
 #endif
 {
 }
 
 void
@@ -128,17 +129,17 @@ MediaSourceReader::RequestAudioData()
     case READER_NEW:
       mAudioReader->Seek(mLastAudioTime, 0)
                   ->Then(GetTaskQueue(), __func__, this,
                          &MediaSourceReader::RequestAudioDataComplete,
                          &MediaSourceReader::RequestAudioDataFailed);
       break;
     case READER_ERROR:
       if (mLastAudioTime) {
-        CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA);
+        CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
         break;
       }
       // Fallback to using current reader
     default:
       RequestAudioDataComplete(0);
       break;
   }
   return p;
@@ -233,17 +234,17 @@ MediaSourceReader::OnAudioNotDecoded(Not
   if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US) == READER_NEW) {
     mAudioReader->Seek(mLastAudioTime, 0)
                 ->Then(GetTaskQueue(), __func__, this,
                        &MediaSourceReader::RequestAudioDataComplete,
                        &MediaSourceReader::RequestAudioDataFailed);
     return;
   }
 
-  CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA);
+  CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
 }
 
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
@@ -268,17 +269,17 @@ MediaSourceReader::RequestVideoData(bool
     case READER_NEW:
       mVideoReader->Seek(mLastVideoTime, 0)
                   ->Then(GetTaskQueue(), __func__, this,
                          &MediaSourceReader::RequestVideoDataComplete,
                          &MediaSourceReader::RequestVideoDataFailed);
       break;
     case READER_ERROR:
       if (mLastVideoTime) {
-        CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA);
+        CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
         break;
       }
       // Fallback to using current reader.
     default:
       mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
                   ->Then(GetTaskQueue(), __func__, this,
                          &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
       break;
@@ -350,24 +351,24 @@ MediaSourceReader::OnVideoNotDecoded(Not
   if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US) == READER_NEW) {
     mVideoReader->Seek(mLastVideoTime, 0)
                 ->Then(GetTaskQueue(), __func__, this,
                        &MediaSourceReader::RequestVideoDataComplete,
                        &MediaSourceReader::RequestVideoDataFailed);
     return;
   }
 
-  CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA);
+  CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
 }
 
 void
-MediaSourceReader::CheckForWaitOrEndOfStream(MediaData::Type aType)
+MediaSourceReader::CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime)
 {
   // If the entire MediaSource is done, generate an EndOfStream.
-  if (IsEnded()) {
+  if (IsNearEnd(aTime)) {
     if (aType == MediaData::AUDIO_DATA) {
       mAudioPromise.Reject(END_OF_STREAM, __func__);
     } else {
       mVideoPromise.Reject(END_OF_STREAM, __func__);
     }
     return;
   }
 
@@ -941,16 +942,30 @@ MediaSourceReader::Ended()
 
 bool
 MediaSourceReader::IsEnded()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   return mEnded;
 }
 
+bool
+MediaSourceReader::IsNearEnd(int64_t aTime)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  return mEnded && aTime >= (mMediaSourceDuration * USECS_PER_S - EOS_FUZZ_US);
+}
+
+void
+MediaSourceReader::SetMediaSourceDuration(double aDuration)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  mMediaSourceDuration = aDuration;
+}
+
 #ifdef MOZ_EME
 nsresult
 MediaSourceReader::SetCDMProxy(CDMProxy* aProxy)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   mCDMProxy = aProxy;
   for (size_t i = 0; i < mTrackBuffers.Length(); i++) {
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -124,16 +124,20 @@ public:
   // Return true if all of the active tracks contain data for the specified time.
   bool TrackBuffersContainTime(int64_t aTime);
 
   // Mark the reader to indicate that EndOfStream has been called on our MediaSource
   void Ended();
 
   // Return true if the Ended method has been called
   bool IsEnded();
+  bool IsNearEnd(int64_t aTime /* microseconds */);
+
+  // Set the duration of the attached mediasource element.
+  void SetMediaSourceDuration(double aDuration /* seconds */);
 
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
   virtual bool IsAsync() const MOZ_OVERRIDE {
     return (!mAudioReader || mAudioReader->IsAsync()) &&
            (!mVideoReader || mVideoReader->IsAsync());
@@ -154,17 +158,17 @@ private:
   SwitchReaderResult SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
   SwitchReaderResult SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
   void RequestAudioDataComplete(int64_t aTime);
   void RequestAudioDataFailed(nsresult aResult);
   void RequestVideoDataComplete(int64_t aTime);
   void RequestVideoDataFailed(nsresult aResult);
   // Will reject the MediaPromise with END_OF_STREAM if mediasource has ended
   // or with WAIT_FOR_DATA otherwise.
-  void CheckForWaitOrEndOfStream(MediaData::Type aType);
+  void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
 
   // Return a reader from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
   already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
                                                     int64_t aError,
                                                     const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
 
@@ -205,16 +209,17 @@ private:
   bool mAudioIsSeeking;
   bool mVideoIsSeeking;
 
   int64_t mTimeThreshold;
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
   bool mEnded;
+  double mMediaSourceDuration;
 
   bool mHasEssentialTrackBuffers;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mMediaSourceShutdownPromise;
 #ifdef MOZ_FMP4
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 #endif