Bug 1385699. P2 - rewrite the code of calculating duration in MDSM. draft
authorJW Wang <jwwang@mozilla.com>
Tue, 01 Aug 2017 13:54:14 +0800
changeset 620124 9339adae695f2794ad5dc0a17bd77db9db609127
parent 620123 287068135df99be828c3390cc9d624a5448cac5b
child 620125 b280260210f4900f621a35ce14b388a66d85fb36
push id71920
push userjwwang@mozilla.com
push dateThu, 03 Aug 2017 03:21:04 +0000
bugs1385699
milestone57.0a1
Bug 1385699. P2 - rewrite the code of calculating duration in MDSM. We pretty much rewrite the whole logic of duration calculation of MDSM. The new logic is much simpler for we have only one duration to manage which is mDuration. Below is the details of the code changes: 1. remove the mExplicitDuration mirror since it should be handled in MediaDecoder. 2. remove mObservedDuration and mDuration will take its place. 3. mDuration is updated from: a. metadata b. playback position as playback progresses. c. buffer ranges. 4. change mDuration to be finite when playback reaches the end. MozReview-Commit-ID: 1EmlWvmw1R2
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2003,16 +2003,20 @@ public:
 
     // StopPlayback in order to reset the IsPlaying() state so audio
     // is restarted correctly.
     mMaster->StopPlayback();
 
     if (!mSentPlaybackEndedEvent) {
       auto clockTime =
         std::max(mMaster->AudioEndTime(), mMaster->VideoEndTime());
+      if (mMaster->mDuration.Ref()->IsInfinite()) {
+        // We have a finite duration when playback reaches the end.
+        mMaster->mDuration = Some(clockTime);
+      }
       mMaster->UpdatePlaybackPosition(clockTime);
 
       // Ensure readyState is updated before firing the 'ended' event.
       mMaster->UpdateNextFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
 
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::PlaybackEnded);
 
       mSentPlaybackEndedEvent = true;
@@ -2233,22 +2237,22 @@ DecodeMetadataState::OnMetadataRead(Meta
   Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
 
   mMaster->mInfo.emplace(*aMetadata.mInfo);
   mMaster->mMediaSeekable = Info().mMediaSeekable;
   mMaster->mMediaSeekableOnlyInBufferedRanges =
     Info().mMediaSeekableOnlyInBufferedRanges;
 
   if (Info().mMetadataDuration.isSome()) {
-    mMaster->RecomputeDuration();
+    mMaster->mDuration = Info().mMetadataDuration;
   } else if (Info().mUnadjustedMetadataEndTime.isSome()) {
     const TimeUnit unadjusted = Info().mUnadjustedMetadataEndTime.ref();
     const TimeUnit adjustment = Info().mStartTime;
     mMaster->mInfo->mMetadataDuration.emplace(unadjusted - adjustment);
-    mMaster->RecomputeDuration();
+    mMaster->mDuration = Info().mMetadataDuration;
   }
 
   // If we don't know the duration by this point, we assume infinity, per spec.
   if (mMaster->mDuration.Ref().isNothing()) {
     mMaster->mDuration = Some(TimeUnit::FromInfinity());
   }
 
   if (mMaster->HasVideo()) {
@@ -2665,17 +2669,16 @@ ShutdownState::Enter()
   // Prevent dangling pointers by disconnecting the listeners.
   master->mAudioQueueListener.Disconnect();
   master->mVideoQueueListener.Disconnect();
   master->mMetadataManager.Disconnect();
   master->mOnMediaNotSeekable.Disconnect();
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   master->mBuffered.DisconnectIfConnected();
-  master->mExplicitDuration.DisconnectIfConnected();
   master->mPlayState.DisconnectIfConnected();
   master->mVolume.DisconnectIfConnected();
   master->mPreservesPitch.DisconnectIfConnected();
   master->mLooping.DisconnectIfConnected();
   master->mSameOriginMedia.DisconnectIfConnected();
   master->mMediaPrincipalHandle.DisconnectIfConnected();
   master->mPlaybackBytesPerSecond.DisconnectIfConnected();
   master->mPlaybackRateReliable.DisconnectIfConnected();
@@ -2712,31 +2715,29 @@ MediaDecoderStateMachine::MediaDecoderSt
   mAudioChannel(aDecoder->GetAudioChannel()),
   mTaskQueue(new TaskQueue(
     GetMediaThreadPool(MediaThreadType::PLAYBACK),
     "MDSM::mTaskQueue", /* aSupportsTailDispatch = */ true)),
   mWatchManager(this, mTaskQueue),
   mDispatchedStateMachine(false),
   mDelayedScheduler(mTaskQueue),
   mCurrentFrameID(0),
-  INIT_WATCHABLE(mObservedDuration, TimeUnit()),
   mReader(new ReaderProxy(mTaskQueue, aReader)),
   mPlaybackRate(1.0),
   mAmpleAudioThreshold(detail::AMPLE_AUDIO_THRESHOLD),
   mAudioCaptured(false),
   mMinimizePreroll(aDecoder->GetMinimizePreroll()),
   mSentFirstFrameLoadedEvent(false),
   mVideoDecodeSuspended(false),
   mVideoDecodeSuspendTimer(mTaskQueue),
   mOutputStreamManager(new OutputStreamManager()),
   mResource(aDecoder->GetResource()),
   mVideoDecodeMode(VideoDecodeMode::Normal),
   mIsMSE(aDecoder->IsMSE()),
   INIT_MIRROR(mBuffered, TimeIntervals()),
-  INIT_MIRROR(mExplicitDuration, Maybe<double>()),
   INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
   INIT_MIRROR(mLooping, false),
   INIT_MIRROR(mSameOriginMedia, false),
   INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
   INIT_MIRROR(mPlaybackBytesPerSecond, 0.0),
   INIT_MIRROR(mPlaybackRateReliable, true),
@@ -2772,37 +2773,32 @@ MediaDecoderStateMachine::~MediaDecoderS
 
 void
 MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
   mBuffered.Connect(mReader->CanonicalBuffered());
-  mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(aDecoder->CanonicalPlayState());
   mVolume.Connect(aDecoder->CanonicalVolume());
   mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
   mLooping.Connect(aDecoder->CanonicalLooping());
   mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
   mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle());
   mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
   mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable());
   mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
 
   // Initialize watchers.
   mWatchManager.Watch(mBuffered,
                       &MediaDecoderStateMachine::BufferedRangeUpdated);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mPreservesPitch,
                       &MediaDecoderStateMachine::PreservesPitchChanged);
-  mWatchManager.Watch(mExplicitDuration,
-                      &MediaDecoderStateMachine::RecomputeDuration);
-  mWatchManager.Watch(mObservedDuration,
-                      &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
 
   MOZ_ASSERT(!mStateObj);
   auto* s = new DecodeMetadataState(this);
   mStateObj.reset(s);
   s->Enter();
 }
 
@@ -3035,17 +3031,17 @@ void
 MediaDecoderStateMachine::UpdatePlaybackPositionInternal(const TimeUnit& aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("UpdatePlaybackPositionInternal(%" PRId64 ")", aTime.ToMicroseconds());
 
   mCurrentPosition = aTime;
   NS_ASSERTION(mCurrentPosition.Ref() >= TimeUnit::Zero(),
                "CurrentTime should be positive!");
-  mObservedDuration = std::max(mObservedDuration.Ref(), mCurrentPosition.Ref());
+  mDuration = Some(std::max(mDuration.Ref().ref(), mCurrentPosition.Ref()));
 }
 
 void
 MediaDecoderStateMachine::UpdatePlaybackPosition(const TimeUnit& aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   UpdatePlaybackPositionInternal(aTime);
 
@@ -3084,53 +3080,16 @@ MediaDecoderStateMachine::ToStateStr()
 }
 
 void MediaDecoderStateMachine::VolumeChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   mMediaSink->SetVolume(mVolume);
 }
 
-void MediaDecoderStateMachine::RecomputeDuration()
-{
-  MOZ_ASSERT(OnTaskQueue());
-
-  TimeUnit duration;
-  if (mExplicitDuration.Ref().isSome()) {
-    double d = mExplicitDuration.Ref().ref();
-    if (IsNaN(d)) {
-      // We have an explicit duration (which means that we shouldn't look at
-      // any other duration sources), but the duration isn't ready yet.
-      return;
-    }
-    // We don't fire duration changed for this case because it should have
-    // already been fired on the main thread when the explicit duration was set.
-    duration = TimeUnit::FromSeconds(d);
-  } else if (mInfo.isSome() && Info().mMetadataDuration.isSome()) {
-    // We need to check mInfo.isSome() because that this method might be invoked
-    // while mObservedDuration is changed which might before the metadata been
-    // read.
-    duration = Info().mMetadataDuration.ref();
-  } else {
-    return;
-  }
-
-  // Only adjust the duration when an explicit duration isn't set (MSE).
-  // The duration is always exactly known with MSE and there's no need to adjust
-  // it based on what may have been seen in the past; in particular as this data
-  // may no longer exist such as when the mediasource duration was reduced.
-  if (mExplicitDuration.Ref().isNothing()
-      && duration < mObservedDuration.Ref()) {
-    duration = mObservedDuration;
-  }
-
-  MOZ_ASSERT(duration >= TimeUnit::Zero());
-  mDuration = Some(duration);
-}
-
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
   return mStateObj->HandleShutdown();
 }
 
 void MediaDecoderStateMachine::PlayStateChanged()
@@ -3205,27 +3164,36 @@ void MediaDecoderStateMachine::SetVideoD
     mStateObj->HandleResumeVideoDecoding(target + detail::RESUME_VIDEO_PREMIUM);
   }
 }
 
 void MediaDecoderStateMachine::BufferedRangeUpdated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  // While playing an unseekable stream of unknown duration, mObservedDuration
-  // is updated (in AdvanceFrame()) as we play. But if data is being downloaded
-  // faster than played, mObserved won't reflect the end of playable data
+  // While playing an unseekable stream of unknown duration, mDuration
+  // is updated as we play. But if data is being downloaded
+  // faster than played, mDuration won't reflect the end of playable data
   // since we haven't played the frame at the end of buffered data. So update
-  // mObservedDuration here as new data is downloaded to prevent such a lag.
-  if (!mBuffered.Ref().IsInvalid()) {
-    bool exists;
-    media::TimeUnit end{mBuffered.Ref().GetEnd(&exists)};
-    if (exists) {
-      mObservedDuration = std::max(mObservedDuration.Ref(), end);
-    }
+  // mDuration here as new data is downloaded to prevent such a lag.
+  if (mBuffered.Ref().IsInvalid()) {
+    return;
+  }
+
+  bool exists;
+  media::TimeUnit end{ mBuffered.Ref().GetEnd(&exists) };
+  if (!exists) {
+    return;
+  }
+
+  // Use estimated duration from buffer ranges when mDuration is unknown or
+  // the estimated duration is larger.
+  if (mDuration.Ref().isNothing() || mDuration.Ref()->IsInfinite() ||
+      end > mDuration.Ref().ref()) {
+    mDuration = Some(end);
   }
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -514,27 +514,19 @@ private:
   UniquePtr<StateObject> mStateObj;
 
   media::TimeUnit Duration() const
   {
     MOZ_ASSERT(OnTaskQueue());
     return mDuration.Ref().ref();
   }
 
-  // Recomputes the canonical duration from various sources.
-  void RecomputeDuration();
-
-
   // FrameID which increments every time a frame is pushed to our queue.
   FrameID mCurrentFrameID;
 
-  // The highest timestamp that our position has reached. Monotonically
-  // increasing.
-  Watchable<media::TimeUnit> mObservedDuration;
-
   // Media Fragment end time.
   media::TimeUnit mFragmentEndTime = media::TimeUnit::Invalid();
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
 
   const RefPtr<ReaderProxy> mReader;
 
@@ -665,19 +657,16 @@ private:
   MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
 
   const bool mIsMSE;
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
-  // The duration explicitly set by JS, mirrored from the main thread.
-  Mirror<Maybe<double>> mExplicitDuration;
-
   // The current play state, mirrored from the main thread.
   Mirror<MediaDecoder::PlayState> mPlayState;
 
   // Volume of playback. 0.0 = muted. 1.0 = full volume.
   Mirror<double> mVolume;
 
   // Pitch preservation for the playback rate.
   Mirror<bool> mPreservesPitch;