From 1524dbeb74010de322eac8b3d9e61e5f8072688a Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 8 Apr 2015 17:51:21 +1200 Subject: [PATCH] Bug 1149494 - Part 1. Add a listener directly to the unblocked input stream that reports the size of the first non-empty frame seen. r=pehrsons --HG-- extra : rebase_source : 79b92a9726670125cd028355e298e43a200785ab --- dom/html/HTMLMediaElement.cpp | 92 +++++++++++++++++++++++++++++++---- dom/html/HTMLMediaElement.h | 14 ++++-- dom/media/MediaSegment.h | 12 +++++ 3 files changed, 105 insertions(+), 13 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index f5d2d3f6112a..2583d75faa8a 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2045,7 +2045,6 @@ HTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI) HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNodeInfo) : nsGenericHTMLElement(aNodeInfo), - mSrcStreamListener(nullptr), mCurrentLoadID(0), mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY), mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING), @@ -2955,6 +2954,61 @@ private: 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 { +public: + explicit StreamSizeListener(HTMLMediaElement* aElement) : + mElement(aElement), + mMutex("HTMLMediaElement::StreamSizeListener") + {} + void Forget() { mElement = nullptr; } + + void ReceivedSize() + { + if (!mElement) { + return; + } + gfxIntSize size; + { + MutexAutoLock lock(mMutex); + size = mInitialSize; + } + nsRefPtr deathGrip = mElement; + mElement->UpdateInitialMediaSize(size); + } + virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, + StreamTime aTrackOffset, + uint32_t aTrackEvents, + const MediaSegment& aQueuedMedia) override + { + MutexAutoLock lock(mMutex); + if (mInitialSize != gfxIntSize(0,0) || + aQueuedMedia.GetType() != MediaSegment::VIDEO) { + return; + } + const VideoSegment& video = static_cast(aQueuedMedia); + for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) { + if (c->mFrame.GetIntrinsicSize() != gfxIntSize(0,0)) { + mInitialSize = c->mFrame.GetIntrinsicSize(); + nsCOMPtr 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; + gfxIntSize mInitialSize; +}; + class HTMLMediaElement::MediaStreamTracksAvailableCallback: public DOMMediaStream::OnTracksAvailableCallback { @@ -2975,7 +3029,8 @@ private: void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) { - NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already"); + NS_ASSERTION(!mSrcStream && !mMediaStreamListener && !mMediaStreamSizeListener, + "Should have been ended already"); mSrcStream = aStream; @@ -3008,8 +3063,13 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) // XXX if we ever support capturing the output of a media element which is // playing a stream, we'll need to add a CombineWithPrincipal call here. - mSrcStreamListener = new StreamListener(this); - GetSrcMediaStream()->AddListener(mSrcStreamListener); + mMediaStreamListener = new StreamListener(this); + mMediaStreamSizeListener = new StreamSizeListener(this); + + GetSrcMediaStream()->AddListener(mMediaStreamListener); + // Listen for an initial image size on mSrcStream so we can get results even + // if we block the mPlaybackStream. + stream->AddListener(mMediaStreamSizeListener); if (mPaused) { GetSrcMediaStream()->ChangeExplicitBlockerCount(1); } @@ -3042,7 +3102,10 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback() { MediaStream* stream = GetSrcMediaStream(); if (stream) { - stream->RemoveListener(mSrcStreamListener); + stream->RemoveListener(mMediaStreamListener); + } + if (mSrcStream->GetStream()) { + mSrcStream->GetStream()->RemoveListener(mMediaStreamSizeListener); } mSrcStream->DisconnectTrackListListeners(AudioTracks(), VideoTracks()); @@ -3051,8 +3114,10 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback() } // Kill its reference to this element - mSrcStreamListener->Forget(); - mSrcStreamListener = nullptr; + mMediaStreamListener->Forget(); + mMediaStreamListener = nullptr; + mMediaStreamSizeListener->Forget(); + mMediaStreamSizeListener = nullptr; if (stream) { stream->RemoveAudioOutput(this); } @@ -3801,16 +3866,23 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged() } } -void HTMLMediaElement::UpdateMediaSize(nsIntSize size) +void HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize) { - if (IsVideo() && mReadyState != HAVE_NOTHING && mMediaSize != size) { + if (IsVideo() && mReadyState != HAVE_NOTHING && mMediaSize != aSize) { DispatchAsyncEvent(NS_LITERAL_STRING("resize")); } - mMediaSize = size; + mMediaSize = aSize; UpdateReadyStateForData(mLastNextFrameStatus); } +void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize) +{ + if (mMediaSize == nsIntSize(-1, -1)) { + UpdateMediaSize(aSize); + } +} + void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents) { if (aPauseElement != mPausedForInactiveDocumentOrChannel) { diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 8278a640b626..eb29e8dca9b9 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -260,7 +260,10 @@ public: // Update the visual size of the media. Called from the decoder on the // main thread when/if the size changes. - void UpdateMediaSize(nsIntSize size); + void UpdateMediaSize(const nsIntSize& aSize); + // Like UpdateMediaSize, but only updates the size if no size has yet + // been set. + void UpdateInitialMediaSize(const nsIntSize& aSize); // Returns the CanPlayStatus indicating if we can handle the // full MIME type including the optional codecs parameter. @@ -642,6 +645,7 @@ protected: class MediaLoadListener; class MediaStreamTracksAvailableCallback; class StreamListener; + class StreamSizeListener; virtual void GetItemValueText(DOMString& text) override; virtual void SetItemValueText(const nsAString& text) override; @@ -1047,8 +1051,12 @@ protected: }; nsTArray mOutputStreams; - // Holds a reference to the MediaStreamListener attached to mSrcStream. - nsRefPtr mSrcStreamListener; + // Holds a reference to the MediaStreamListener attached to mPlaybackStream + // (or mSrcStream if mPlaybackStream is null). + nsRefPtr mMediaStreamListener; + // Holds a reference to the size-getting MediaStreamListener attached to + // mSrcStream. + nsRefPtr mMediaStreamSizeListener; // Holds a reference to the MediaSource supplying data for playback. nsRefPtr mMediaSource; diff --git a/dom/media/MediaSegment.h b/dom/media/MediaSegment.h index f22791a52718..9c1f5353cec2 100644 --- a/dom/media/MediaSegment.h +++ b/dom/media/MediaSegment.h @@ -265,6 +265,18 @@ public: MediaSegmentBase& mSegment; uint32_t mIndex; }; + class ConstChunkIterator { + public: + explicit ConstChunkIterator(const MediaSegmentBase& aSegment) + : mSegment(aSegment), mIndex(0) {} + bool IsEnded() { return mIndex >= mSegment.mChunks.Length(); } + void Next() { ++mIndex; } + const Chunk& operator*() { return mSegment.mChunks[mIndex]; } + const Chunk* operator->() { return &mSegment.mChunks[mIndex]; } + private: + const MediaSegmentBase& mSegment; + uint32_t mIndex; + }; void RemoveLeading(StreamTime aDuration) {