Bug 1592539 - Move the FirstFrameListener to VideoOutput.h. r=jib

This allows the FirstFrameListener to be instantiated outside of
HTMLMediaElement. A future patch brings in a subclass in HTMLVideoElement.

This also renames FirstFrameListener to FirstFrameVideoOutput since that better
denotes what it is.

Differential Revision: https://phabricator.services.mozilla.com/D87135
This commit is contained in:
Andreas Pehrson
2020-08-26 14:25:05 +00:00
parent fe4286e0d6
commit 580236ee61
3 changed files with 80 additions and 78 deletions

View File

@@ -759,63 +759,6 @@ class HTMLMediaElement::MediaStreamTrackListener
const WeakPtr<HTMLMediaElement> mElement; const WeakPtr<HTMLMediaElement> mElement;
}; };
/**
* This listener observes the first video frame to arrive with a non-empty size,
* and renders it to its VideoFrameContainer.
*/
class HTMLMediaElement::FirstFrameListener : public VideoOutput {
public:
FirstFrameListener(VideoFrameContainer* aContainer,
AbstractThread* aMainThread)
: VideoOutput(aContainer, aMainThread) {
MOZ_ASSERT(NS_IsMainThread());
}
// NB that this overrides VideoOutput::NotifyRealtimeTrackData, so we can
// filter out all frames but the first one with a real size. This allows us to
// later re-use the logic in VideoOutput for rendering that frame.
void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aTrackOffset,
const MediaSegment& aMedia) override {
MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
if (mInitialSizeFound) {
return;
}
const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0, 0)) {
mInitialSizeFound = true;
mMainThread->Dispatch(NS_NewRunnableFunction(
"HTMLMediaElement::FirstFrameListener::FirstFrameRenderedSetter",
[self = RefPtr<FirstFrameListener>(this), this] {
mFirstFrameRendered = true;
}));
// Pick the first frame and run it through the rendering code.
VideoSegment segment;
segment.AppendFrame(do_AddRef(c->mFrame.GetImage()),
c->mFrame.GetIntrinsicSize(),
c->mFrame.GetPrincipalHandle(),
c->mFrame.GetForceBlack(), c->mTimeStamp);
VideoOutput::NotifyRealtimeTrackData(aGraph, aTrackOffset, segment);
return;
}
}
}
// Main thread only.
Watchable<bool> mFirstFrameRendered = {
false, "HTMLMediaElement::FirstFrameListener::mFirstFrameRendered"};
private:
// Whether a frame with a concrete size has been received. May only be
// accessed on the MTG's appending thread. (this is a direct listener so we
// get called by whoever is producing this track's data)
bool mInitialSizeFound = false;
};
/** /**
* Helper class that manages audio and video outputs for all enabled tracks in a * Helper class that manages audio and video outputs for all enabled tracks in a
* media element. It also manages calculating the current time when playing a * media element. It also manages calculating the current time when playing a
@@ -832,11 +775,12 @@ class HTMLMediaElement::MediaStreamRenderer
: mVideoContainer(aVideoContainer), : mVideoContainer(aVideoContainer),
mAudioOutputKey(aAudioOutputKey), mAudioOutputKey(aAudioOutputKey),
mWatchManager(this, aMainThread), mWatchManager(this, aMainThread),
mFirstFrameListener(aVideoContainer ? MakeAndAddRef<FirstFrameListener>( mFirstFrameVideoOutput(aVideoContainer
? MakeAndAddRef<FirstFrameVideoOutput>(
aVideoContainer, aMainThread) aVideoContainer, aMainThread)
: nullptr) { : nullptr) {
if (mFirstFrameListener) { if (mFirstFrameVideoOutput) {
mWatchManager.Watch(mFirstFrameListener->mFirstFrameRendered, mWatchManager.Watch(mFirstFrameVideoOutput->mFirstFrameRendered,
&MediaStreamRenderer::SetFirstFrameRendered); &MediaStreamRenderer::SetFirstFrameRendered);
} }
} }
@@ -851,7 +795,7 @@ class HTMLMediaElement::MediaStreamRenderer
RemoveTrack(mVideoTrack->AsVideoStreamTrack()); RemoveTrack(mVideoTrack->AsVideoStreamTrack());
} }
mWatchManager.Shutdown(); mWatchManager.Shutdown();
mFirstFrameListener = nullptr; mFirstFrameVideoOutput = nullptr;
} }
void UpdateGraphTime() { void UpdateGraphTime() {
@@ -860,15 +804,16 @@ class HTMLMediaElement::MediaStreamRenderer
} }
void SetFirstFrameRendered() { void SetFirstFrameRendered() {
if (!mFirstFrameListener) { if (!mFirstFrameVideoOutput) {
return; return;
} }
if (mVideoTrack) { if (mVideoTrack) {
mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput(mFirstFrameListener); mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput(
mFirstFrameVideoOutput);
} }
mWatchManager.Unwatch(mFirstFrameListener->mFirstFrameRendered, mWatchManager.Unwatch(mFirstFrameVideoOutput->mFirstFrameRendered,
&MediaStreamRenderer::SetFirstFrameRendered); &MediaStreamRenderer::SetFirstFrameRendered);
mFirstFrameListener = nullptr; mFirstFrameVideoOutput = nullptr;
} }
void SetProgressingCurrentTime(bool aProgress) { void SetProgressingCurrentTime(bool aProgress) {
@@ -913,9 +858,9 @@ class HTMLMediaElement::MediaStreamRenderer
} }
if (mVideoTrack) { if (mVideoTrack) {
if (mFirstFrameListener) { if (mFirstFrameVideoOutput) {
mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput( mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput(
mFirstFrameListener); mFirstFrameVideoOutput);
} }
mVideoTrack->AsVideoStreamTrack()->AddVideoOutput(mVideoContainer); mVideoTrack->AsVideoStreamTrack()->AddVideoOutput(mVideoContainer);
} }
@@ -940,8 +885,9 @@ class HTMLMediaElement::MediaStreamRenderer
if (mVideoTrack) { if (mVideoTrack) {
mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput(mVideoContainer); mVideoTrack->AsVideoStreamTrack()->RemoveVideoOutput(mVideoContainer);
if (mFirstFrameListener) { if (mFirstFrameVideoOutput) {
mVideoTrack->AsVideoStreamTrack()->AddVideoOutput(mFirstFrameListener); mVideoTrack->AsVideoStreamTrack()->AddVideoOutput(
mFirstFrameVideoOutput);
} }
} }
} }
@@ -1016,8 +962,8 @@ class HTMLMediaElement::MediaStreamRenderer
EnsureGraphTimeDummy(); EnsureGraphTimeDummy();
if (mRendering) { if (mRendering) {
aTrack->AddVideoOutput(mVideoContainer); aTrack->AddVideoOutput(mVideoContainer);
} else if (mFirstFrameListener) { } else if (mFirstFrameVideoOutput) {
aTrack->AddVideoOutput(mFirstFrameListener); aTrack->AddVideoOutput(mFirstFrameVideoOutput);
} }
} }
@@ -1035,8 +981,8 @@ class HTMLMediaElement::MediaStreamRenderer
} }
if (mRendering) { if (mRendering) {
aTrack->RemoveVideoOutput(mVideoContainer); aTrack->RemoveVideoOutput(mVideoContainer);
} else if (mFirstFrameListener) { } else if (mFirstFrameVideoOutput) {
aTrack->RemoveVideoOutput(mFirstFrameListener); aTrack->RemoveVideoOutput(mFirstFrameVideoOutput);
} }
mVideoTrack = nullptr; mVideoTrack = nullptr;
} }
@@ -1122,10 +1068,10 @@ class HTMLMediaElement::MediaStreamRenderer
// Currently selected (and rendered) video track. // Currently selected (and rendered) video track.
WeakPtr<MediaStreamTrack> mVideoTrack; WeakPtr<MediaStreamTrack> mVideoTrack;
// Holds a reference to the first-frame-getting track listener attached to // Holds a reference to the first-frame-getting video output attached to
// mVideoTrack. Set by the constructor, unset when the media element tells us // mVideoTrack. Set by the constructor, unset when the media element tells us
// it has rendered the first frame. // it has rendered the first frame.
RefPtr<FirstFrameListener> mFirstFrameListener; RefPtr<FirstFrameVideoOutput> mFirstFrameVideoOutput;
}; };
class HTMLMediaElement::MediaElementTrackSource class HTMLMediaElement::MediaElementTrackSource

View File

@@ -766,7 +766,6 @@ class HTMLMediaElement : public nsGenericHTMLElement,
class MediaLoadListener; class MediaLoadListener;
class MediaStreamRenderer; class MediaStreamRenderer;
class MediaStreamTrackListener; class MediaStreamTrackListener;
class FirstFrameListener;
class ShutdownObserver; class ShutdownObserver;
class MediaControlKeyListener; class MediaControlKeyListener;

View File

@@ -225,6 +225,63 @@ class VideoOutput : public DirectMediaTrackListener {
layers::ImageContainer::AllocateProducerID(); layers::ImageContainer::AllocateProducerID();
}; };
/**
* This listener observes the first video frame to arrive with a non-empty size,
* and renders it to its VideoFrameContainer.
*/
class FirstFrameVideoOutput : public VideoOutput {
public:
FirstFrameVideoOutput(VideoFrameContainer* aContainer,
AbstractThread* aMainThread)
: VideoOutput(aContainer, aMainThread) {
MOZ_ASSERT(NS_IsMainThread());
}
// NB that this overrides VideoOutput::NotifyRealtimeTrackData, so we can
// filter out all frames but the first one with a real size. This allows us to
// later re-use the logic in VideoOutput for rendering that frame.
void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aTrackOffset,
const MediaSegment& aMedia) override {
MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
if (mInitialSizeFound) {
return;
}
const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0, 0)) {
mInitialSizeFound = true;
mMainThread->Dispatch(NS_NewRunnableFunction(
"FirstFrameVideoOutput::FirstFrameRenderedSetter",
[self = RefPtr<FirstFrameVideoOutput>(this)] {
self->mFirstFrameRendered = true;
}));
// Pick the first frame and run it through the rendering code.
VideoSegment segment;
segment.AppendFrame(do_AddRef(c->mFrame.GetImage()),
c->mFrame.GetIntrinsicSize(),
c->mFrame.GetPrincipalHandle(),
c->mFrame.GetForceBlack(), c->mTimeStamp);
VideoOutput::NotifyRealtimeTrackData(aGraph, aTrackOffset, segment);
return;
}
}
}
// Main thread only.
Watchable<bool> mFirstFrameRendered = {
false, "FirstFrameVideoOutput::mFirstFrameRendered"};
private:
// Whether a frame with a concrete size has been received. May only be
// accessed on the MTG's appending thread. (this is a direct listener so we
// get called by whoever is producing this track's data)
bool mInitialSizeFound = false;
};
} // namespace mozilla } // namespace mozilla
#endif // VideoOutput_h #endif // VideoOutput_h