Bug 1536766 - End a track only after the graph has reported reaching its end time in DecodedStream. r=jya,padenot

This gives us a guarantee that the first frame of a media file can be rendered
with a second media element and mozCaptureStream(), even if the file is very
very short.

With longer video-only files there were also cases where nothing ended up being
rendered. Probably because the MediaStreamGraph ended up switching from an
AudioCallbackDriver to a SystemClockDriver and this took enough time to put the
SourceMediaStream::EndTrack and the SourceMediaStream::AddTrackListener calls
for this video track to be processed in the same iteration. The listener would
then always lose to the ending track and update main thread state too late,
leading to its media element not rendering any frames and nasty intermittent
failures.

Differential Revision: https://phabricator.services.mozilla.com/D27270

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andreas Pehrson 2019-04-24 10:56:16 +00:00
Родитель 8734620d4d
Коммит 2cab1f445f
1 изменённых файлов: 45 добавлений и 2 удалений

Просмотреть файл

@ -92,6 +92,17 @@ class DecodedStreamGraphListener {
}
void NotifyOutput(TrackID aTrackID, StreamTime aCurrentTrackTime) {
if (aTrackID == mAudioTrackID) {
if (aCurrentTrackTime >= mAudioEnd) {
mStream->EndTrack(mAudioTrackID);
}
} else if (aTrackID == mVideoTrackID) {
if (aCurrentTrackTime >= mVideoEnd) {
mStream->EndTrack(mVideoTrackID);
}
} else {
MOZ_CRASH("Unexpected TrackID");
}
if (aTrackID != mAudioTrackID && mAudioTrackID != TRACK_NONE &&
!mAudioEnded) {
// Only audio playout drives the clock forward, if present and live.
@ -120,6 +131,36 @@ class DecodedStreamGraphListener {
TrackID VideoTrackID() const { return mVideoTrackID; }
/**
* Tell the graph listener to end the given track after it has seen at least
* aEnd worth of output reported as processed by the graph.
*
* A StreamTime of STREAM_TIME_MAX indicates that the track has no end and is
* the default.
*
* This method of ending tracks is needed because the MediaStreamGraph
* processes ended tracks (through SourceMediaStream::EndTrack) at the
* beginning of an iteration, but waits until the end of the iteration to
* process any ControlMessages. When such a ControlMessage is a listener that
* is to be added to a track that has ended in its very first iteration, the
* track ends before the listener tracking this ending is added. This can lead
* to a MediaStreamTrack ending on main thread (it uses another listener)
* before the listeners to render the track get added, potentially meaning a
* media element doesn't progress before reaching the end although data was
* available.
*
* Callable from any thread.
*/
void EndTrackAt(TrackID aTrackID, StreamTime aEnd) {
if (aTrackID == mAudioTrackID) {
mAudioEnd = aEnd;
} else if (aTrackID == mVideoTrackID) {
mVideoEnd = aEnd;
} else {
MOZ_CRASH("Unexpected TrackID");
}
}
void DoNotifyTrackEnded(TrackID aTrackID) {
MOZ_ASSERT(NS_IsMainThread());
if (aTrackID == mAudioTrackID) {
@ -172,7 +213,9 @@ class DecodedStreamGraphListener {
// Any thread.
const RefPtr<SourceMediaStream> mStream;
const TrackID mAudioTrackID;
Atomic<StreamTime> mAudioEnd{STREAM_TIME_MAX};
const TrackID mVideoTrackID;
Atomic<StreamTime> mVideoEnd{STREAM_TIME_MAX};
const RefPtr<AbstractThread> mAbstractMainThread;
};
@ -617,7 +660,7 @@ void DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin,
}
if (mAudioQueue.IsFinished() && !mData->mHaveSentFinishAudio) {
sourceStream->EndTrack(audioTrackId);
mData->mListener->EndTrackAt(audioTrackId, mData->mStreamAudioWritten);
mData->mHaveSentFinishAudio = true;
}
}
@ -810,7 +853,7 @@ void DecodedStream::SendVideo(bool aIsSameOrigin,
mData->mStreamVideoWritten +=
sourceStream->AppendToTrack(videoTrackId, &endSegment);
}
sourceStream->EndTrack(videoTrackId);
mData->mListener->EndTrackAt(videoTrackId, mData->mStreamVideoWritten);
mData->mHaveSentFinishVideo = true;
}
}