Bug 1119757 - Allow seeking on media with infinite duration. r=cpearce, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 16 Jan 2015 23:49:01 +1100
changeset 242930 b0c42a7f0dc7
parent 242929 18ade4ad787e
child 242931 3e5d8c21f3a2
push id4342
push userryanvm@gmail.com
push date2015-01-20 16:03 +0000
treeherdermozilla-beta@fae52bd681e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, sledru
bugs1119757
milestone36.0
Bug 1119757 - Allow seeking on media with infinite duration. r=cpearce, a=sledru MSE defines content to have infinite duration when no duration is defined. And MSE is always seekable within the buffered range, regardless of the duration
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -183,16 +183,17 @@ MediaDecoderStateMachine::MediaDecoderSt
       &MediaDecoderStateMachine::TimeoutExpired,
       MOZ_THIS_IN_INITIALIZER_LIST(), aRealTime)),
   mState(DECODER_STATE_DECODING_NONE),
   mSyncPointInMediaStream(-1),
   mSyncPointInDecodedStream(-1),
   mPlayDuration(0),
   mStartTime(-1),
   mEndTime(-1),
+  mDurationSet(false),
   mFragmentEndTime(-1),
   mReader(aReader),
   mCurrentFrameTime(0),
   mAudioStartTime(-1),
   mAudioEndTime(-1),
   mDecodedAudioEndTime(-1),
   mVideoFrameEndTime(-1),
   mDecodedVideoEndTime(-1),
@@ -1384,30 +1385,46 @@ int64_t MediaDecoderStateMachine::GetDur
 {
   AssertCurrentThreadInMonitor();
 
   if (mEndTime == -1 || mStartTime == -1)
     return -1;
   return mEndTime - mStartTime;
 }
 
+int64_t MediaDecoderStateMachine::GetEndTime()
+{
+  if (mEndTime == -1 && mDurationSet) {
+    return INT64_MAX;
+  }
+  return mEndTime;
+}
+
 void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
 {
   NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
                "Should be on main or decode thread.");
   AssertCurrentThreadInMonitor();
 
   if (aDuration == -1) {
+    mDurationSet = false;
     return;
   }
 
+  mDurationSet = true;
+
   if (mStartTime == -1) {
     SetStartTime(0);
   }
 
+  if (aDuration == INT64_MAX) {
+    mEndTime = -1;
+    return;
+  }
+
   mEndTime = mStartTime + aDuration;
 }
 
 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
   AssertCurrentThreadInMonitor();
   int64_t duration = GetDuration();
   if (aDuration != duration &&
@@ -1691,22 +1708,23 @@ MediaDecoderStateMachine::StartSeek(cons
 
   MOZ_ASSERT(mState >= DECODER_STATE_DECODING);
 
   if (mState == DECODER_STATE_SHUTDOWN) {
     return;
   }
 
   // Bound the seek time to be inside the media range.
+  int64_t end = GetEndTime();
   NS_ASSERTION(mStartTime != -1, "Should know start time by now");
-  NS_ASSERTION(mEndTime != -1, "Should know end time by now");
+  NS_ASSERTION(end != -1, "Should know end time by now");
   int64_t seekTime = aTarget.mTime + mStartTime;
-  seekTime = std::min(seekTime, mEndTime);
+  seekTime = std::min(seekTime, end);
   seekTime = std::max(mStartTime, seekTime);
-  NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime,
+  NS_ASSERTION(seekTime >= mStartTime && seekTime <= end,
                "Can only seek in range [0,duration]");
   mSeekTarget = SeekTarget(seekTime, aTarget.mType);
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
   SetState(DECODER_STATE_SEEKING);
   if (mDecoder->GetDecodedStream()) {
     mDecoder->RecreateDecodedStream(seekTime - mStartTime);
   }
@@ -2147,17 +2165,17 @@ nsresult MediaDecoderStateMachine::Decod
 
   mInfo = info;
 
   if (NS_FAILED(res) || (!info.HasValidMedia())) {
     DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia());
     return NS_ERROR_FAILURE;
   }
   mDecoder->StartProgressUpdates();
-  mGotDurationFromMetaData = (GetDuration() != -1);
+  mGotDurationFromMetaData = (GetDuration() != -1) || mDurationSet;
 
   if (mGotDurationFromMetaData) {
     // We have all the information required: duration and size
     // Inform the element that we've loaded the metadata.
     EnqueueLoadedMetadataEvent();
   }
 
   if (mState == DECODER_STATE_DECODING_METADATA) {
@@ -2259,22 +2277,18 @@ MediaDecoderStateMachine::FinishDecodeFi
     SetStartTime(mReader->ComputeStartTime(v, a));
     if (VideoQueue().GetSize()) {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       RenderVideoFrame(VideoQueue().PeekFront(), TimeStamp::Now());
     }
   }
 
   NS_ASSERTION(mStartTime != -1, "Must have start time");
-  MOZ_ASSERT((!HasVideo() && !HasAudio()) ||
-               !(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
-               mEndTime != -1,
-             "Active seekable media should have end time");
   MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
-               GetDuration() != -1,
+               (GetDuration() != -1) || mDurationSet,
              "Seekable media should have duration");
   DECODER_LOG("Media goes from %lld to %lld (duration %lld) "
               "transportSeekable=%d, mediaSeekable=%d",
               mStartTime, mEndTime, GetDuration(),
               mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
 
   mDecodingFrozenAtStateMetadata = false;
 
@@ -2394,17 +2408,17 @@ void MediaDecoderStateMachine::DecodeSee
 
     nsresult res;
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       // We must not hold the state machine monitor while we call into
       // the reader, since it could do I/O or deadlock some other way.
       res = mReader->ResetDecode();
       if (NS_SUCCEEDED(res)) {
-        mReader->Seek(seekTime, mStartTime, mEndTime, mCurrentTimeBeforeSeek)
+        mReader->Seek(seekTime, mStartTime, GetEndTime(), mCurrentTimeBeforeSeek)
                ->Then(DecodeTaskQueue(), __func__, this,
                       &MediaDecoderStateMachine::OnSeekCompleted,
                       &MediaDecoderStateMachine::OnSeekFailed);
       }
     }
     if (NS_FAILED(res)) {
       DecodeError();
       return;
@@ -3217,17 +3231,17 @@ MediaDecoderStateMachine::DropAudioUpToS
 
 void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs)
 {
   AssertCurrentThreadInMonitor();
   DECODER_LOG("SetStartTime(%lld)", aStartTimeUsecs);
   mStartTime = 0;
   if (aStartTimeUsecs != 0) {
     mStartTime = aStartTimeUsecs;
-    if (mGotDurationFromMetaData) {
+    if (mGotDurationFromMetaData && GetEndTime() != INT64_MAX) {
       NS_ASSERTION(mEndTime != -1,
                    "We should have mEndTime as supplied duration here");
       // We were specified a duration from a Content-Duration HTTP header.
       // Adjust mEndTime so that mEndTime-mStartTime matches the specified
       // duration.
       mEndTime = mStartTime + mEndTime;
     }
   }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -165,21 +165,28 @@ public:
   void FinishShutdown();
 
   bool IsRealTime() const;
 
   // Called from the main thread to get the duration. The decoder monitor
   // must be obtained before calling this. It is in units of microseconds.
   int64_t GetDuration();
 
+  // Time of the last frame in the media, in microseconds or INT64_MAX if
+  // media has an infinite duration.
+  // Accessed on state machine, decode, and main threads.
+  // Access controlled by decoder monitor.
+  int64_t GetEndTime();
+
   // Called from the main thread to set the duration of the media resource
   // if it is able to be obtained via HTTP headers. Called from the
   // state machine thread to set the duration if it is obtained from the
   // media metadata. The decoder monitor must be obtained before calling this.
   // aDuration is in microseconds.
+  // A value of INT64_MAX will be treated as infinity.
   void SetDuration(int64_t aDuration);
 
   // Called while decoding metadata to set the end time of the media
   // resource. The decoder monitor must be obtained before calling this.
   // aEndTime is in microseconds.
   void SetMediaEndTime(int64_t aEndTime);
 
   // Called from main thread to update the duration with an estimated value.
@@ -808,18 +815,24 @@ protected:
   // time of the first frame decoded from the media, and is used to calculate
   // duration and as a bounds for seeking. Accessed on state machine, decode,
   // and main threads. Access controlled by decoder monitor.
   int64_t mStartTime;
 
   // Time of the last frame in the media, in microseconds. This is the
   // end time of the last frame in the media. Accessed on state
   // machine, decode, and main threads. Access controlled by decoder monitor.
+  // It will be set to -1 if the duration is infinite
   int64_t mEndTime;
 
+  // Will be set when SetDuration has been called with a value != -1
+  // mDurationSet false doesn't indicate that we do not have a valid duration
+  // as mStartTime and mEndTime could have been set separately.
+  bool mDurationSet;
+
   // Position to seek to in microseconds when the seek state transition occurs.
   // The decoder monitor lock must be obtained before reading or writing
   // this value. Accessed on main and decode thread.
   SeekTarget mSeekTarget;
 
   // Position to seek to in microseconds when DecodeFirstFrame completes.
   // The decoder monitor lock must be obtained before reading or writing
   // this value. Accessed on main and decode thread.