Bug 1201363 - Add ImageSizeChangedListener to notify image size changed. r?jesup,r?pehrsons draft
authorctai <ctai@mozilla.com>
Fri, 25 Dec 2015 11:01:37 +0800
changeset 344706 6d6c11003fa5d5ddfc0daf2d411491863d1a6f81
parent 344705 15bb8a82689ab61b32496bcec00194ef09d7b3a8
child 344707 75d9356911b33b14375b3db4681858bded247abe
push id13910
push userbmo:ctai@mozilla.com
push dateFri, 25 Mar 2016 13:21:09 +0000
reviewersjesup, pehrsons
bugs1201363
milestone46.0a1
Bug 1201363 - Add ImageSizeChangedListener to notify image size changed. r?jesup,r?pehrsons HTMLMediaElement uses ImageSizeChangedListener to receive the image size changed event from VideoFrameContainer. MozReview-Commit-ID: G9IQKuQw6z5
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/VideoFrameContainer.cpp
dom/media/VideoFrameContainer.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3046,19 +3046,20 @@ private:
   Mutex mMutex;
   bool mPendingNotifyOutput;
 };
 
 /**
  * This listener observes the first video frame to arrive with a non-empty size,
  * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
  */
-class HTMLMediaElement::StreamSizeListener : public MediaStreamListener {
+class HTMLMediaElement::StreamSizeListener : public ImageSizeChangedListener {
 public:
-  explicit StreamSizeListener(HTMLMediaElement* aElement) :
+  explicit StreamSizeListener(HTMLMediaElement* aElement, MediaStreamGraph* aGraph) :
+    ImageSizeChangedListener(aGraph),
     mElement(aElement),
     mMutex("HTMLMediaElement::StreamSizeListener")
   {}
   void Forget() { mElement = nullptr; }
 
   void ReceivedSize()
   {
     if (!mElement) {
@@ -3067,37 +3068,30 @@ public:
     gfx::IntSize size;
     {
       MutexAutoLock lock(mMutex);
       size = mInitialSize;
     }
     RefPtr<HTMLMediaElement> deathGrip = mElement;
     mElement->UpdateInitialMediaSize(size);
   }
-  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                        StreamTime aTrackOffset,
-                                        uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia,
-                                        MediaStream* aInputStream,
-                                        TrackID aInputTrackID) override
+  virtual void NotifyImageSizeChanged(MediaStreamGraph* aGraph,
+                                      const gfx::IntSize& aIntrinsicSize) override
   {
+    MOZ_ASSERT(aGraph);
     MutexAutoLock lock(mMutex);
-    if (mInitialSize != gfx::IntSize(0,0) ||
-        aQueuedMedia.GetType() != MediaSegment::VIDEO) {
+    if (mInitialSize != gfx::IntSize(0,0))
+    {
       return;
     }
-    const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
-    for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
-      if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
-        mInitialSize = c->mFrame.GetIntrinsicSize();
-        nsCOMPtr<nsIRunnable> event =
-          NS_NewRunnableMethod(this, &StreamSizeListener::ReceivedSize);
-        aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
-      }
-    }
+
+    mInitialSize = aIntrinsicSize;
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &StreamSizeListener::ReceivedSize);
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
 
 private:
   // These fields may only be accessed on the main thread
   HTMLMediaElement* mElement;
 
   // mMutex protects the fields below; they can be accessed on any thread
   Mutex mMutex;
@@ -3166,40 +3160,40 @@ void HTMLMediaElement::UpdateSrcMediaStr
                         this, shouldPlay ? "Setting up" : "Removing",
                         mSrcStream.get()));
 
   if (shouldPlay) {
     mSrcStreamPausedCurrentTime = -1;
 
     mMediaStreamListener = new StreamListener(this,
         "HTMLMediaElement::mMediaStreamListener");
-    mMediaStreamSizeListener = new StreamSizeListener(this);
+    mMediaStreamSizeListener = new StreamSizeListener(this, stream->Graph());
     stream->AddListener(mMediaStreamListener);
-    stream->AddListener(mMediaStreamSizeListener);
 
     mWatchManager.Watch(*mMediaStreamListener,
         &HTMLMediaElement::UpdateReadyStateInternal);
 
     stream->AddAudioOutput(this);
     SetVolumeInternal();
 
     VideoFrameContainer* container = GetVideoFrameContainer();
     if (container) {
+      container->AddImageSizeChangedListener(mMediaStreamSizeListener);
       stream->AddVideoOutput(container);
     }
   } else {
     if (stream) {
       mSrcStreamPausedCurrentTime = CurrentTime();
 
       stream->RemoveListener(mMediaStreamListener);
-      stream->RemoveListener(mMediaStreamSizeListener);
 
       stream->RemoveAudioOutput(this);
       VideoFrameContainer* container = GetVideoFrameContainer();
       if (container) {
+        container->RemoveImageSizeChangedListener(mMediaStreamSizeListener);
         stream->RemoveVideoOutput(container);
       }
     }
     // If stream is null, then DOMMediaStream::Destroy must have been
     // called and that will remove all listeners/outputs.
 
     mWatchManager.Unwatch(*mMediaStreamListener,
         &HTMLMediaElement::UpdateReadyStateInternal);
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1134,18 +1134,18 @@ protected:
     RefPtr<DOMMediaStream> mStream;
     bool mFinishWhenEnded;
   };
   nsTArray<OutputMediaStream> mOutputStreams;
 
   // Holds a reference to the MediaStreamListener attached to mSrcStream's
   // playback stream.
   RefPtr<StreamListener> mMediaStreamListener;
-  // Holds a reference to the size-getting MediaStreamListener attached to
-  // mSrcStream.
+  // Holds a reference to the size-getting ImageSizeChangedListener attached to
+  // VideoFrameContainer.
   RefPtr<StreamSizeListener> mMediaStreamSizeListener;
 
   // Holds a reference to the MediaSource, if any, referenced by the src
   // attribute on the media element.
   RefPtr<MediaSource> mSrcMediaSource;
 
   // Holds a reference to the MediaSource supplying data for playback.  This
   // may either match mSrcMediaSource or come from Source element children.
--- a/dom/media/VideoFrameContainer.cpp
+++ b/dom/media/VideoFrameContainer.cpp
@@ -75,16 +75,19 @@ void VideoFrameContainer::SetCurrentFram
   if (aImages.IsEmpty()) {
     mImageContainer->ClearAllImages();
   } else {
     mImageContainer->SetCurrentImages(aImages);
   }
   gfx::IntSize newFrameSize = mImageContainer->GetCurrentSize();
   if (oldFrameSize != newFrameSize) {
     mImageSizeChanged = true;
+    for (ImageSizeChangedListener* l : mListeners) {
+      l->NotifyImageSizeChanged(l->mGraph, newFrameSize);
+    }
   }
 }
 
 void VideoFrameContainer::ClearCurrentFrame()
 {
   MutexAutoLock lock(mMutex);
 
   // See comment in SetCurrentFrame for the reasoning behind
--- a/dom/media/VideoFrameContainer.h
+++ b/dom/media/VideoFrameContainer.h
@@ -15,16 +15,31 @@
 #include "ImageContainer.h"
 
 namespace mozilla {
 
 namespace dom {
 class HTMLMediaElement;
 } // namespace dom
 
+class MediaStreamGraph;
+
+class ImageSizeChangedListener {
+protected:
+  // Protected destructor, to discourage deletion outside of Release():
+  virtual ~ImageSizeChangedListener() {}
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageSizeChangedListener)
+  ImageSizeChangedListener(MediaStreamGraph* aGraph) : mGraph(aGraph) {}
+  virtual void NotifyImageSizeChanged(MediaStreamGraph* aGraph,
+                                      const gfx::IntSize& aIntrinsicSize) = 0;
+  MediaStreamGraph* mGraph;
+};
+
 /**
  * This object is used in the decoder backend threads and the main thread
  * to manage the "current video frame" state. This state includes timing data
  * and an intrinsic size (see below).
  * This has to be a thread-safe object since it's accessed by resource decoders
  * and other off-main-thread components. So we can't put this state in the media
  * element itself ... well, maybe we could, but it could be risky and/or
  * confusing.
@@ -37,18 +52,28 @@ public:
   typedef layers::Image Image;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoFrameContainer)
 
   VideoFrameContainer(dom::HTMLMediaElement* aElement,
                       already_AddRefed<ImageContainer> aContainer);
 
   // Call on any thread
+  void AddImageSizeChangedListener(ImageSizeChangedListener* aListener)
+  {
+    MutexAutoLock lock(mMutex);
+    *mListeners.AppendElement() = aListener;
+  }
+  void RemoveImageSizeChangedListener(ImageSizeChangedListener* aListener)
+  {
+    MutexAutoLock lock(mMutex);
+    mListeners.RemoveElement(aListener);
+  }
   B2G_ACL_EXPORT void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage,
-                       const TimeStamp& aTargetTime);
+                                      const TimeStamp& aTargetTime);
   void SetCurrentFrames(const gfx::IntSize& aIntrinsicSize,
                         const nsTArray<ImageContainer::NonOwningImage>& aImages);
   void ClearCurrentFrame(const gfx::IntSize& aIntrinsicSize)
   {
     SetCurrentFrames(aIntrinsicSize, nsTArray<ImageContainer::NonOwningImage>());
   }
 
   void ClearCurrentFrame();
@@ -86,16 +111,17 @@ protected:
 
   // Non-addreffed pointer to the element. The element calls ForgetElement
   // to clear this reference when the element is destroyed.
   dom::HTMLMediaElement* mElement;
   RefPtr<ImageContainer> mImageContainer;
 
   // mMutex protects all the fields below.
   Mutex mMutex;
+  nsTArray<RefPtr<ImageSizeChangedListener>> mListeners;
   // The intrinsic size is the ideal size which we should render the
   // ImageContainer's current Image at.
   // This can differ from the Image's actual size when the media resource
   // specifies that the Image should be stretched to have the correct aspect
   // ratio.
   gfx::IntSize mIntrinsicSize;
   // We maintain our own mFrameID which is auto-incremented at every
   // SetCurrentFrame() or NewFrameID() call.