Bug 1096089 - Only return end of stream if we're near the known duration. r=mattwoodrow, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sat, 24 Jan 2015 21:46:21 +1100
changeset 243060 c7db1d42b4b6
parent 243059 570a09a6eb68
child 243061 6fe6b2e779ef
push id4378
push userryanvm@gmail.com
push date2015-01-27 15:46 +0000
treeherdermozilla-beta@f91cc6838063 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, sledru
bugs1096089
milestone36.0
Bug 1096089 - Only return end of stream if we're near the known duration. r=mattwoodrow, a=sledru 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;
   }
 
@@ -921,16 +922,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
@@ -121,16 +121,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());
@@ -151,17 +155,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);
 
@@ -202,16 +206,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