Bug 1122228 - Use document's status change to trigger MediaDecoderStateMachine's dormant status change. r=cpearce, a=sledru
authorSotaro Ikeda <sikeda@mozilla.com>
Wed, 21 Jan 2015 20:01:12 -0800
changeset 243070 c3a59f01db15
parent 243069 38b5ab4af771
child 243071 afc24a951c4e
push id4382
push userryanvm@gmail.com
push date2015-01-28 14:58 +0000
treeherdermozilla-beta@f35aa2298df8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, sledru
bugs1122228
milestone36.0
Bug 1122228 - Use document's status change to trigger MediaDecoderStateMachine's dormant status change. r=cpearce, a=sledru
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderOwner.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2477,41 +2477,41 @@ nsresult HTMLMediaElement::BindToTree(ns
   if (aDocument) {
     mAutoplayEnabled =
       IsAutoplayEnabled() && (!aDocument || !aDocument->IsStaticDocument()) &&
       !IsEditable();
     // The preload action depends on the value of the autoplay attribute.
     // It's value may have changed, so update it.
     UpdatePreloadAction();
   }
+  mElementInTreeState = ELEMENT_INTREE;
+
   if (mDecoder) {
     // When the MediaElement is binding to tree, the dormant status is
     // aligned to document's hidden status.
-    nsIDocument* ownerDoc = OwnerDoc();
-    if (ownerDoc) {
-      mDecoder->SetDormantIfNecessary(ownerDoc->Hidden());
-    }
-  }
-  mElementInTreeState = ELEMENT_INTREE;
+    mDecoder->NotifyOwnerActivityChanged();
+  }
 
   return rv;
 }
 
 void HTMLMediaElement::UnbindFromTree(bool aDeep,
                                       bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
     Pause();
 
-  if (mDecoder) {
-    mDecoder->SetDormantIfNecessary(true);
-  }
   mElementInTreeState = ELEMENT_NOT_INTREE_HAD_INTREE;
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+
+  if (mDecoder) {
+    MOZ_ASSERT(IsHidden());
+    mDecoder->NotifyOwnerActivityChanged();
+  }
 }
 
 /* static */
 CanPlayStatus
 HTMLMediaElement::GetCanPlay(const nsAString& aType)
 {
   nsContentTypeParser parser(aType);
   nsAutoString mimeType;
@@ -3376,16 +3376,31 @@ void HTMLMediaElement::CheckAutoplayData
     } else if (mSrcStream) {
       SetPlayedOrSeeked(true);
       GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
     }
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
   }
 }
 
+bool HTMLMediaElement::IsActive()
+{
+  nsIDocument* ownerDoc = OwnerDoc();
+  return ownerDoc && ownerDoc->IsActive() && ownerDoc->IsVisible();
+}
+
+bool HTMLMediaElement::IsHidden()
+{
+  if (mElementInTreeState == ELEMENT_NOT_INTREE_HAD_INTREE) {
+    return true;
+  }
+  nsIDocument* ownerDoc = OwnerDoc();
+  return !ownerDoc || ownerDoc->Hidden();
+}
+
 VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
 {
   // If we have loaded the metadata, and the size of the video is still
   // (-1, -1), the media has no video. Don't go a create a video frame
   // container.
   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
       mMediaSize == nsIntSize(-1, -1)) {
     return nullptr;
@@ -3562,38 +3577,29 @@ void HTMLMediaElement::SuspendOrResumeEl
 }
 
 void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
 {
   nsIDocument* ownerDoc = OwnerDoc();
 
   if (mDecoder) {
     mDecoder->SetElementVisibility(!ownerDoc->Hidden());
-
-    if (mElementInTreeState == ELEMENT_NOT_INTREE_HAD_INTREE) {
-      mDecoder->SetDormantIfNecessary(true);
-    } else if (mElementInTreeState == ELEMENT_NOT_INTREE ||
-               mElementInTreeState == ELEMENT_INTREE) {
-      // The MediaElement had never been binded to tree, or in the tree now,
-      // align to document.
-      mDecoder->SetDormantIfNecessary(ownerDoc->Hidden());
-    }
+    mDecoder->NotifyOwnerActivityChanged();
   }
 
   // SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
   // CanPlayChanged callback.
   if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&
       mAudioChannelAgent) {
     AutoNoJSAPI nojsapi;
     mAudioChannelAgent->SetVisibilityState(!ownerDoc->Hidden());
   }
-  bool suspendEvents = !ownerDoc->IsActive() || !ownerDoc->IsVisible();
-  bool pauseElement = suspendEvents || (mMuted & MUTED_BY_AUDIO_CHANNEL);
-
-  SuspendOrResumeElement(pauseElement, suspendEvents);
+  bool pauseElement = !IsActive() || (mMuted & MUTED_BY_AUDIO_CHANNEL);
+
+  SuspendOrResumeElement(pauseElement, !IsActive());
 
   AddRemoveSelfReference();
 }
 
 void HTMLMediaElement::AddRemoveSelfReference()
 {
   // XXX we could release earlier here in many situations if we examined
   // which event listeners are attached. Right now we assume there is a
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -206,16 +206,20 @@ public:
 
   // Called to indicate the download is progressing.
   virtual void DownloadProgressed() MOZ_FINAL MOZ_OVERRIDE;
 
   // Called by the media decoder to indicate whether the media cache has
   // suspended the channel.
   virtual void NotifySuspendedByCache(bool aIsSuspended) MOZ_FINAL MOZ_OVERRIDE;
 
+  virtual bool IsActive() MOZ_FINAL MOZ_OVERRIDE;
+
+  virtual bool IsHidden() MOZ_FINAL MOZ_OVERRIDE;
+
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   layers::ImageContainer* GetImageContainer();
 
   // Dispatch events
   virtual nsresult DispatchAsyncEvent(const nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
 
@@ -364,17 +368,17 @@ public:
 
   uint16_t NetworkState() const
   {
     return mNetworkState;
   }
 
   // Called by the media decoder object, on the main thread,
   // when the connection between Rtsp server and client gets lost.
-  void ResetConnectionState() MOZ_FINAL MOZ_OVERRIDE;
+  virtual void ResetConnectionState() MOZ_FINAL MOZ_OVERRIDE;
 
   // XPCOM GetPreload() is OK
   void SetPreload(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::preload, aValue, aRv);
   }
 
   already_AddRefed<TimeRanges> Buffered() const;
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -113,28 +113,49 @@ public:
 };
 
 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
 
 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
 
 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
 
-void MediaDecoder::SetDormantIfNecessary(bool aDormant)
+void MediaDecoder::NotifyOwnerActivityChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   if (!mDecoderStateMachine ||
       !mDecoderStateMachine->IsDormantNeeded() ||
       mPlayState == PLAY_STATE_SHUTDOWN) {
     return;
   }
 
-  if(aDormant) {
+  if (!mOwner) {
+    NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
+    return;
+  }
+
+  bool prevDormant = mIsDormant;
+  mIsDormant = false;
+  if (!mOwner->IsActive() && mOwner->GetVideoFrameContainer()) {
+    mIsDormant = true;
+  }
+#ifdef MOZ_WIDGET_GONK
+  if (mOwner->IsHidden() && mOwner->GetVideoFrameContainer()) {
+    mIsDormant = true;
+  }
+#endif
+
+  if (prevDormant == mIsDormant) {
+    // No update to dormant state
+    return;
+  }
+
+  if (mIsDormant) {
     // enter dormant state
     mDecoderStateMachine->SetDormant(true);
 
     int64_t timeUsecs = 0;
     SecondsToUsecs(mCurrentTime, timeUsecs);
     mRequestedSeekTarget = SeekTarget(timeUsecs, SeekTarget::Accurate);
 
     mNextState = mPlayState;
@@ -432,17 +453,18 @@ MediaDecoder::MediaDecoder() :
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
   mMinimizePreroll(false),
-  mMediaTracksConstructed(false)
+  mMediaTracksConstructed(false),
+  mIsDormant(false)
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -355,21 +355,22 @@ public:
 
   // Initialize state machine and schedule it.
   nsresult InitializeStateMachine(MediaDecoder* aCloneDonor);
 
   // Start playback of a video. 'Load' must have previously been
   // called.
   virtual nsresult Play();
 
-  // Set/Unset dormant state if necessary.
+  // Notify activity of the decoder owner is changed.
+  // Based on the activity, dormant state is updated.
   // Dormant state is a state to free all scarce media resources
   //  (like hw video codec), did not decoding and stay dormant.
   // It is used to share scarece media resources in system.
-  virtual void SetDormantIfNecessary(bool aDormant);
+  virtual void NotifyOwnerActivityChanged();
 
   // Pause video playback.
   virtual void Pause();
   // Adjust the speed of the playback, optionally with pitch correction,
   virtual void SetVolume(double aVolume);
   // Sets whether audio is being captured. If it is, we won't play any
   // of our audio.
   virtual void SetAudioCaptured(bool aCaptured);
@@ -1207,13 +1208,16 @@ protected:
 
   // True if audio tracks and video tracks are constructed and added into the
   // track list, false if all tracks are removed from the track list.
   bool mMediaTracksConstructed;
 
   // Stores media info, including info of audio tracks and video tracks, should
   // only be accessed from main thread.
   nsAutoPtr<MediaInfo> mInfo;
+
+  // True if MediaDecoder is in dormant state.
+  bool mIsDormant;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -115,16 +115,22 @@ public:
 
   // Called by the decoder when some data has been downloaded or
   // buffering/seeking has ended. aNextFrameAvailable is true when
   // the data for the next frame is available. This method will
   // decide whether to set the ready state to HAVE_CURRENT_DATA,
   // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
   virtual void UpdateReadyStateForData(NextFrameStatus aNextFrame) = 0;
 
+  // Check if the decoder owner is active.
+  virtual bool IsActive() = 0;
+
+  // Check if the decoder owner is hidden.
+  virtual bool IsHidden() = 0;
+
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
 
   // Called by the media decoder object, on the main thread,
   // when the connection between Rtsp server and client gets lost.
   virtual void ResetConnectionState() = 0;