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:
@@ -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
|
||||||
aVideoContainer, aMainThread)
|
? MakeAndAddRef<FirstFrameVideoOutput>(
|
||||||
: nullptr) {
|
aVideoContainer, aMainThread)
|
||||||
if (mFirstFrameListener) {
|
: nullptr) {
|
||||||
mWatchManager.Watch(mFirstFrameListener->mFirstFrameRendered,
|
if (mFirstFrameVideoOutput) {
|
||||||
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user