Bug 1160695 - Track "network duration" separately and mirror it to the MDSM. r=jww
authorBobby Holley <bobbyholley@gmail.com>
Thu, 28 May 2015 12:13:56 -0700
changeset 247615 149efc9c88cc2af250789bfb9711f6c4c9597323
parent 247614 89dd8f457becd1b987b749b4d6bbfd9f87f5e772
child 247616 cd83b74ed1e69dfd31080274fa3846d92e8d430e
push id60756
push userbobbyholley@gmail.com
push dateMon, 08 Jun 2015 16:23:28 +0000
treeherdermozilla-inbound@636412252449 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjww
bugs1160695
milestone41.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 1160695 - Track "network duration" separately and mirror it to the MDSM. r=jww
dom/html/HTMLMediaElement.cpp
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaResource.cpp
dom/media/RtspMediaResource.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2715,19 +2715,19 @@ nsresult HTMLMediaElement::InitializeDec
 
   LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
 
   if (!decoder->Init(this)) {
     LOG(LogLevel::Debug, ("%p Failed to init cloned decoder %p", this, decoder.get()));
     return NS_ERROR_FAILURE;
   }
 
-  double duration = aOriginal->GetDuration();
-  if (duration >= 0) {
-    decoder->SetDuration(duration);
+  media::NullableTimeUnit duration = aOriginal->NetworkDuration();
+  if (duration.isSome()) {
+    decoder->SetNetworkDuration(duration.ref());
     decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
   }
 
   nsRefPtr<MediaResource> resource = originalResource->CloneData(decoder);
   if (!resource) {
     LOG(LogLevel::Debug, ("%p Failed to cloned stream for decoder %p", this, decoder.get()));
     return NS_ERROR_FAILURE;
   }
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -27,18 +27,19 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
+using namespace mozilla::dom;
 using namespace mozilla::layers;
-using namespace mozilla::dom;
+using namespace mozilla::media;
 
 // Default timeout msecs until try to enter dormant state by heuristic.
 static const int DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS = 60000;
 
 namespace mozilla {
 
 // Number of estimated seconds worth of data we need to have buffered
 // ahead of the current playback position before we allow the media decoder
@@ -338,16 +339,18 @@ MediaDecoder::MediaDecoder() :
   mCurrentPosition(AbstractThread::MainThread(), 0, "MediaDecoder::mCurrentPosition (Mirror)"),
   mVolume(AbstractThread::MainThread(), 0.0, "MediaDecoder::mVolume (Canonical)"),
   mPlaybackRate(AbstractThread::MainThread(), 1.0, "MediaDecoder::mPlaybackRate (Canonical)"),
   mPreservesPitch(AbstractThread::MainThread(), true, "MediaDecoder::mPreservesPitch (Canonical)"),
   mDuration(-1),
   mMediaSeekable(true),
   mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
+  mNetworkDuration(AbstractThread::MainThread(), NullableTimeUnit(),
+                   "MediaDecoder::mNetworkDuration (Canonical)"),
   mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
              "MediaDecoder::mPlayState (Canonical)"),
   mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
              "MediaDecoder::mNextState (Canonical)"),
   mLogicallySeeking(AbstractThread::MainThread(), false,
              "MediaDecoder::mLogicallySeeking (Canonical)"),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
@@ -483,17 +486,16 @@ nsresult MediaDecoder::InitializeStateMa
   SetStateMachineParameters();
 
   return ScheduleStateMachine();
 }
 
 void MediaDecoder::SetStateMachineParameters()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  mDecoderStateMachine->SetDuration(mDuration);
   if (mMinimizePreroll) {
     mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
   }
 }
 
 void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
 {
   DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
@@ -1077,34 +1079,35 @@ void MediaDecoder::DurationChanged()
   SetInfinite(mDuration == -1);
 
   if (mOwner && oldDuration != mDuration && !IsInfinite()) {
     DECODER_LOG("Duration changed to %lld", mDuration);
     mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   }
 }
 
-void MediaDecoder::SetDuration(double aDuration)
+void MediaDecoder::SetNetworkDuration(TimeUnit aNetworkDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  mNetworkDuration = Some(aNetworkDuration);
+
+  // XXXbholley - it doesn't really make sense to change mDuration here, but
+  // we leave it for this patch. This will change in subsequent patches.
+  double aDuration = aNetworkDuration.ToSeconds();
   if (mozilla::IsInfinite(aDuration)) {
     SetInfinite(true);
   } else if (IsNaN(aDuration)) {
     mDuration = -1;
     SetInfinite(true);
   } else {
     mDuration = static_cast<int64_t>(NS_round(aDuration * static_cast<double>(USECS_PER_S)));
   }
 
+  // Duration has changed so we should recompute playback rate
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  if (mDecoderStateMachine) {
-    mDecoderStateMachine->SetDuration(mDuration);
-  }
-
-  // Duration has changed so we should recompute playback rate
   UpdatePlaybackRate();
 }
 
 void MediaDecoder::SetMediaDuration(int64_t aDuration)
 {
   NS_ENSURE_TRUE_VOID(GetStateMachine());
   GetStateMachine()->SetDuration(aDuration);
 }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -454,17 +454,18 @@ public:
   // Return true if the decoder has reached the end of playback or the decoder
   // has shutdown.
   // Call on the main thread only.
   virtual bool IsEndedOrShutdown() const;
 
   // Set the duration of the media resource in units of seconds.
   // This is called via a channel listener if it can pick up the duration
   // from a content header. Must be called from the main thread only.
-  virtual void SetDuration(double aDuration);
+  void SetNetworkDuration(media::TimeUnit aDuration);
+  media::NullableTimeUnit NetworkDuration() { return mNetworkDuration; }
 
   // Sets the initial duration of the media. Called while the media metadata
   // is being read and the decode is being setup.
   void SetMediaDuration(int64_t aDuration) override;
   // Updates the media duration. This is called while the media is being
   // played, calls before the media has reached loaded metadata are ignored.
   // The duration is assumed to be an estimate, and so a degree of
   // instability is expected; if the incoming duration is not significantly
@@ -989,16 +990,22 @@ private:
   // change.  Explicitly private for force access via GetReentrantMonitor.
   ReentrantMonitor mReentrantMonitor;
 
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mProxy;
 #endif
 
 protected:
+  // Media duration according to HTTP headers.
+  Canonical<media::NullableTimeUnit> mNetworkDuration;
+public:
+  AbstractCanonical<media::NullableTimeUnit>* CanonicalNetworkDuration() { return &mNetworkDuration; }
+protected:
+
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
   // Any change to the state on the main thread must call NotifyAll on the
   // monitor so the decode thread can wake up.
   Canonical<PlayState> mPlayState;
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -39,19 +39,20 @@
 #include "nsPrintfCString.h"
 #include "DOMMediaStream.h"
 #include "DecodedStream.h"
 
 #include <algorithm>
 
 namespace mozilla {
 
-using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::media;
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractThreadDispatchInstead
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 
@@ -192,16 +193,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mRealTime(aRealTime),
   mDispatchedStateMachine(false),
   mDelayedScheduler(this),
   mState(DECODER_STATE_DECODING_NONE, "MediaDecoderStateMachine::mState"),
   mPlayDuration(0),
   mStartTime(-1),
   mEndTime(-1),
   mDurationSet(false),
+  mNetworkDuration(mTaskQueue, NullableTimeUnit(),
+                   "MediaDecoderStateMachine::mNetworkDuration (Mirror)"),
   mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
              "MediaDecoderStateMachine::mPlayState (Mirror)"),
   mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED,
                  "MediaDecoderStateMachine::mNextPlayState (Mirror)"),
   mLogicallySeeking(mTaskQueue, false,
              "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"),
   mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"),
@@ -289,29 +292,31 @@ MediaDecoderStateMachine::~MediaDecoderS
 }
 
 void
 MediaDecoderStateMachine::InitializationTask()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
+  mNetworkDuration.Connect(mDecoder->CanonicalNetworkDuration());
   mPlayState.Connect(mDecoder->CanonicalPlayState());
   mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
   mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(mDecoder->CanonicalVolume());
   mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
   mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
 
   // Initialize watchers.
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
+  mWatchManager.Watch(mNetworkDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
@@ -1445,20 +1450,31 @@ public:
     mDecoder->Seek(mSeekTarget, SeekTarget::Accurate);
     return NS_OK;
   }
 private:
   nsRefPtr<MediaDecoder> mDecoder;
   double mSeekTarget;
 };
 
+void MediaDecoderStateMachine::RecomputeDuration()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+  // XXXbholley - This will do something more sensible in upcoming patches. This
+  // would be incorrect if we ever sent spurious state mirroring updates.
+  if (mNetworkDuration.Ref().isSome()) {
+    SetDuration(mNetworkDuration.Ref().ref().ToMicroseconds());
+  }
+}
+
 void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
 {
-  MOZ_ASSERT(NS_IsMainThread() || OnDecodeTaskQueue());
-  AssertCurrentThreadInMonitor();
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   if (aDuration < 0) {
     mDurationSet = false;
     return;
   }
 
   mDurationSet = true;
 
@@ -2550,16 +2566,17 @@ MediaDecoderStateMachine::FinishShutdown
   AudioQueue().ClearListeners();
   VideoQueue().ClearListeners();
 
   // Now that those threads are stopped, there's no possibility of
   // mPendingWakeDecoder being needed again. Revoke it.
   mPendingWakeDecoder = nullptr;
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
+  mNetworkDuration.DisconnectIfConnected();
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
   mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
   mNextFrameStatus.DisconnectAll();
   mCurrentPosition.DisconnectAll();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -895,21 +895,27 @@ public:
   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;
 
+  // Recomputes the canonical duration from various sources.
+  void RecomputeDuration();
+
   // 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;
 
+  // The duration according to HTTP headers etc, mirrored from the main thread.
+  Mirror<media::NullableTimeUnit> mNetworkDuration;
+
   // The current play state and next play state, mirrored from the main thread.
   Mirror<MediaDecoder::PlayState> mPlayState;
   Mirror<MediaDecoder::PlayState> mNextPlayState;
   Mirror<bool> mLogicallySeeking;
 
   // Returns true if we're logically playing, that is, if the Play() has
   // been called and Pause() has not or we have not yet reached the end
   // of media. This is irrespective of the seeking state; if the owner
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -28,16 +28,18 @@
 #include "nsICachingChannel.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsContentUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 #include <algorithm>
 #include "nsProxyRelease.h"
 #include "nsIContentPolicy.h"
 
+using mozilla::media::TimeUnit;
+
 PRLogModuleInfo* gMediaResourceLog;
 #define RESOURCE_LOG(msg, ...) MOZ_LOG(gMediaResourceLog, mozilla::LogLevel::Debug, \
                                       (msg, ##__VA_ARGS__))
 // Debug logging macro with object pointer and class name.
 #define CMLOG(msg, ...) \
         RESOURCE_LOG("%p [ChannelMediaResource]: " msg, this, ##__VA_ARGS__)
 
 static const uint32_t HTTP_OK_CODE = 200;
@@ -255,17 +257,17 @@ ChannelMediaResource::OnStartRequest(nsI
         rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-Content-Duration"), durationText);
       }
 
       // If there is a Content-Duration header with a valid value, record
       // the duration.
       if (NS_SUCCEEDED(rv)) {
         double duration = durationText.ToDouble(&ec);
         if (ec == NS_OK && duration >= 0) {
-          mDecoder->SetDuration(duration);
+          mDecoder->SetNetworkDuration(TimeUnit::FromSeconds(duration));
           // We know the resource must be bounded.
           dataIsBounded = true;
         }
       }
     }
 
     // Assume Range requests have a bounded upper limit unless the
     // Content-Range header tells us otherwise.
--- a/dom/media/RtspMediaResource.cpp
+++ b/dom/media/RtspMediaResource.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/Preferences.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamingProtocolService.h"
 #include "nsServiceManagerUtils.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspChannelChild.h"
 #endif
 using namespace mozilla::net;
+using namespace mozilla::media;
 
 PRLogModuleInfo* gRtspMediaResourceLog;
 #define RTSP_LOG(msg, ...) MOZ_LOG(gRtspMediaResourceLog, mozilla::LogLevel::Debug, \
                                   (msg, ##__VA_ARGS__))
 // Debug logging macro with object pointer and class name.
 #define RTSPMLOG(msg, ...) \
         RTSP_LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__)
 
@@ -720,17 +721,17 @@ RtspMediaResource::OnConnected(uint8_t a
     return NS_ERROR_FAILURE;
   }
 
   // If the durationUs is 0, imply the stream is live stream.
   if (durationUs) {
     // Not live stream.
     mIsLiveStream = false;
     mDecoder->SetInfinite(false);
-    mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
+    mDecoder->SetNetworkDuration(TimeUnit::FromMicroseconds(durationUs));
   } else {
     // Live stream.
     // Check the preference "media.realtime_decoder.enabled".
     if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) {
       // Give up, report error to media element.
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
       NS_DispatchToMainThread(event);