Bug 1097375 - Implement MediaSource::setDuration. r=kinetik
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 12 Nov 2014 17:11:33 +1300
changeset 215191 1ef10ba3d73c87f0771d5345134d63a13dadea1e
parent 215190 534f1eb6d7081e6108edc7a8154ce2c3a0baa6a7
child 215192 f96fe425680e30cefeed2919cc6adb750d75ff47
push id27810
push usercbook@mozilla.com
push dateWed, 12 Nov 2014 15:03:31 +0000
treeherdermozilla-central@cf9eafef4ffa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1097375
milestone36.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 1097375 - Implement MediaSource::setDuration. r=kinetik
dom/media/mediasource/MediaSource.cpp
dom/media/mediasource/MediaSource.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceDecoder.h
dom/media/mediasource/MediaSourceReader.cpp
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -161,34 +161,35 @@ MediaSource::ReadyState()
 
 double
 MediaSource::Duration()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mReadyState == MediaSourceReadyState::Closed) {
     return UnspecifiedNaN<double>();
   }
-  return mDuration;
+  MOZ_ASSERT(mDecoder);
+  return mDecoder->GetMediaSourceDuration();
 }
 
 void
 MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("MediaSource(%p)::SetDuration(aDuration=%f)", this, aDuration);
   if (aDuration < 0 || IsNaN(aDuration)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  DurationChange(aDuration, aRv);
+  mDecoder->SetMediaSourceDuration(aDuration);
 }
 
 already_AddRefed<SourceBuffer>
 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsresult rv = mozilla::IsTypeSupported(aType);
   MSE_API("MediaSource(%p)::AddSourceBuffer(aType=%s)%s",
@@ -266,17 +267,17 @@ MediaSource::EndOfStream(const Optional<
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   SetReadyState(MediaSourceReadyState::Ended);
   mSourceBuffers->Ended();
   mDecoder->Ended();
   if (!aError.WasPassed()) {
-    DurationChange(mSourceBuffers->GetHighestBufferedEndTime(), aRv);
+    mDecoder->SetMediaSourceDuration(mSourceBuffers->GetHighestBufferedEndTime());
     if (aRv.Failed()) {
       return;
     }
     // TODO:
     //   Notify media element that all data is now available.
     return;
   }
   switch (aError.Value()) {
@@ -331,28 +332,26 @@ MediaSource::Detach()
     MOZ_ASSERT(mReadyState == MediaSourceReadyState::Closed);
     MOZ_ASSERT(mActiveSourceBuffers->IsEmpty() && mSourceBuffers->IsEmpty());
     return;
   }
   mDecoder->DetachMediaSource();
   mDecoder = nullptr;
   mFirstSourceBufferInitialized = false;
   SetReadyState(MediaSourceReadyState::Closed);
-  mDuration = UnspecifiedNaN<double>();
   if (mActiveSourceBuffers) {
     mActiveSourceBuffers->Clear();
   }
   if (mSourceBuffers) {
     mSourceBuffers->Clear();
   }
 }
 
 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
-  , mDuration(UnspecifiedNaN<double>())
   , mDecoder(nullptr)
   , mPrincipal(nullptr)
   , mReadyState(MediaSourceReadyState::Closed)
   , mFirstSourceBufferInitialized(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
@@ -411,33 +410,29 @@ void
 MediaSource::QueueAsyncSimpleEvent(const char* aName)
 {
   MSE_DEBUG("MediaSource(%p) Queuing event '%s'", this, aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 void
-MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
+MediaSource::DurationChange(double aOldDuration, double aNewDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("MediaSource(%p)::DurationChange(aNewDuration=%f)", this, aNewDuration);
-  if (mDuration == aNewDuration) {
-    return;
-  }
-  double oldDuration = mDuration;
-  mDuration = aNewDuration;
-  if (aNewDuration < oldDuration) {
-    mSourceBuffers->Remove(aNewDuration, oldDuration, aRv);
-    if (aRv.Failed()) {
+
+  if (aNewDuration < aOldDuration) {
+    ErrorResult rv;
+    mSourceBuffers->Remove(aNewDuration, aOldDuration, rv);
+    if (rv.Failed()) {
       return;
     }
   }
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
-  // TODO: Update media element's duration and run element's duration change algorithm.
 }
 
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("MediaSource(%p)::NotifyEvicted(aStart=%f, aEnd=%f)", this, aStart, aEnd);
   // Cycle through all SourceBuffers and tell them to evict data in
--- a/dom/media/mediasource/MediaSource.h
+++ b/dom/media/mediasource/MediaSource.h
@@ -115,22 +115,20 @@ private:
   ~MediaSource();
 
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
-  void DurationChange(double aNewDuration, ErrorResult& aRv);
+  void DurationChange(double aOldDuration, double aNewDuration);
 
   void InitializationEvent();
 
-  double mDuration;
-
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
   nsRefPtr<MediaSourceDecoder> mDecoder;
 
   nsRefPtr<nsIPrincipal> mPrincipal;
 
   MediaSourceReadyState mReadyState;
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -28,16 +28,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #endif
 
 namespace mozilla {
 
 class SourceBufferDecoder;
 
 MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
   : mMediaSource(nullptr)
+  , mMediaSourceDuration(UnspecifiedNaN<double>())
 {
   Init(aElement);
 }
 
 MediaDecoder*
 MediaSourceDecoder::Clone()
 {
   // TODO: Sort out cloning.
@@ -166,25 +167,84 @@ MediaSourceDecoder::Ended()
 
 bool
 MediaSourceDecoder::IsExpectingMoreData()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return !mReader->IsEnded();
 }
 
+class DurationChangedRunnable : public nsRunnable {
+public:
+  explicit DurationChangedRunnable(MediaSourceDecoder* aDecoder,
+                                   double aOldDuration,
+                                   double aNewDuration)
+    : mDecoder(aDecoder)
+    , mOldDuration(aOldDuration)
+    , mNewDuration(aNewDuration)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+    mDecoder->DurationChanged(mOldDuration, mNewDuration);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<MediaSourceDecoder> mDecoder;
+  double mOldDuration;
+  double mNewDuration;
+};
+
+void
+MediaSourceDecoder::DurationChanged(double aOldDuration, double aNewDuration)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Run the MediaSource duration changed algorithm
+  if (mMediaSource) {
+    mMediaSource->DurationChange(aOldDuration, aNewDuration);
+  }
+  // Run the MediaElement duration changed algorithm
+  MediaDecoder::DurationChanged();
+}
+
+void
+MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
+{
+  // Only use the decoded duration if one wasn't already
+  // set.
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  if (!mMediaSource || !IsNaN(mMediaSourceDuration)) {
+    return;
+  }
+  double duration = aDuration;
+  duration /= USECS_PER_S;
+  SetMediaSourceDuration(duration);
+}
+
 void
 MediaSourceDecoder::SetMediaSourceDuration(double aDuration)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mMediaSource) {
-    return;
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  double oldDuration = mMediaSourceDuration;
+  mMediaSourceDuration = aDuration;
+  mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
+  if (NS_IsMainThread()) {
+    DurationChanged(oldDuration, aDuration);
+  } else {
+    nsRefPtr<nsIRunnable> task =
+      new DurationChangedRunnable(this, oldDuration, aDuration);
+    NS_DispatchToMainThread(task);
   }
-  ErrorResult dummy;
-  mMediaSource->DurationChange(aDuration, dummy);
+}
+
+double
+MediaSourceDecoder::GetMediaSourceDuration()
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  return mMediaSourceDuration;
 }
 
 void
 MediaSourceDecoder::NotifyTimeRangesChanged()
 {
   MOZ_ASSERT(mReader);
   mReader->NotifyTimeRangesChanged();
 }
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -49,17 +49,20 @@ public:
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   void Ended();
   bool IsExpectingMoreData() MOZ_OVERRIDE;
 
+  void SetDecodedDuration(int64_t aDuration);
   void SetMediaSourceDuration(double aDuration);
+  double GetMediaSourceDuration();
+  void DurationChanged(double aOldDuration, double aNewDuration);
 
   // Called whenever a TrackBuffer has new data appended or a new decoder
   // initializes.  Safe to call from any thread.
   void NotifyTimeRangesChanged();
 
   // Indicates the point in time at which the reader should consider
   // registered TrackBuffers essential for initialization.
   void PrepareReaderInitialization();
@@ -69,13 +72,16 @@ public:
 #endif
 
 private:
   // The owning MediaSource holds a strong reference to this decoder, and
   // calls Attach/DetachMediaSource on this decoder to set and clear
   // mMediaSource.
   dom::MediaSource* mMediaSource;
   nsRefPtr<MediaSourceReader> mReader;
+
+  // Protected by GetReentrantMonitor()
+  double mMediaSourceDuration;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEDECODER_H_ */
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -625,23 +625,17 @@ MediaSourceReader::ReadMetadata(MediaInf
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
     maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
               this, mVideoReader.get(), maxDuration);
   }
 
   if (maxDuration != -1) {
-    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    mDecoder->SetMediaDuration(maxDuration);
-    nsRefPtr<nsIRunnable> task (
-      NS_NewRunnableMethodWithArg<double>(static_cast<MediaSourceDecoder*>(mDecoder),
-                                          &MediaSourceDecoder::SetMediaSourceDuration,
-                                          static_cast<double>(maxDuration) / USECS_PER_S));
-    NS_DispatchToMainThread(task);
+    static_cast<MediaSourceDecoder*>(mDecoder)->SetDecodedDuration(maxDuration);
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }