--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -131,54 +131,60 @@ H264Converter::Decode(MediaRawData* aSam
}
RefPtr<MediaDataDecoder::FlushPromise>
H264Converter::Flush()
{
mDecodePromiseRequest.DisconnectIfExists();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mNeedKeyframe = true;
+ mPendingFrames.Clear();
MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Previous flush didn't complete");
/*
- When we detect a change of content in the H264 stream, we first flush the
- current decoder (1), shut it down (2) create a new decoder and initialize
- it (3).
- It is possible possible for H264Converter::Flush to be called during any of
- those time.
+ When we detect a change of content in the H264 stream, we first drain the
+ current decoder (1), flush (2), shut it down (3) create a new decoder and
+ initialize it (4). It is possible for H264Converter::Flush to be called
+ during any of those times.
If during (1):
- - mFlushRequest will not be empty.
+ - mDrainRequest will not be empty.
- The old decoder can still be used, with the current extradata as stored
in mCurrentConfig.mExtraData.
If during (2):
+ - mFlushRequest will not be empty.
+ - The old decoder can still be used, with the current extradata as stored
+ in mCurrentConfig.mExtraData.
+
+ If during (3):
- mShutdownRequest won't be empty.
- mDecoder is empty.
- The old decoder is no longer referenced by the H264Converter.
- If during (3):
+ If during (4):
- mInitPromiseRequest won't be empty.
- mDecoder is set but not usable yet.
*/
- if (mFlushRequest.Exists() || mShutdownRequest.Exists() ||
- mInitPromiseRequest.Exists()) {
+ if (mDrainRequest.Exists() || mFlushRequest.Exists() ||
+ mShutdownRequest.Exists() || mInitPromiseRequest.Exists()) {
// We let the current decoder complete and will resume after.
return mFlushPromise.Ensure(__func__);
}
if (mDecoder) {
return mDecoder->Flush();
}
return FlushPromise::CreateAndResolve(true, __func__);
}
RefPtr<MediaDataDecoder::DecodePromise>
H264Converter::Drain()
{
+ MOZ_RELEASE_ASSERT(!mDrainRequest.Exists());
mNeedKeyframe = true;
if (mDecoder) {
return mDecoder->Drain();
}
return DecodePromise::CreateAndResolve(DecodedData(), __func__);
}
RefPtr<ShutdownPromise>
@@ -348,17 +354,18 @@ H264Converter::CanRecycleDecoder() const
return MediaPrefs::MediaDecoderCheckRecycling()
&& mDecoder->SupportDecoderRecycling();
}
void
H264Converter::DecodeFirstSample(MediaRawData* aSample)
{
if (mNeedKeyframe && !aSample->mKeyframe) {
- mDecodePromise.Resolve(DecodedData(), __func__);
+ mDecodePromise.Resolve(mPendingFrames, __func__);
+ mPendingFrames.Clear();
return;
}
if (!*mNeedAVCC
&& !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) {
mDecodePromise.Reject(
MediaResult(NS_ERROR_OUT_OF_MEMORY,
RESULT_DETAIL("ConvertSampleToAnnexB")),
@@ -368,17 +375,19 @@ H264Converter::DecodeFirstSample(MediaRa
mNeedKeyframe = false;
RefPtr<H264Converter> self = this;
mDecoder->Decode(aSample)
->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
[self, this](const MediaDataDecoder::DecodedData& aResults) {
mDecodePromiseRequest.Complete();
- mDecodePromise.Resolve(aResults, __func__);
+ mPendingFrames.AppendElements(aResults);
+ mDecodePromise.Resolve(mPendingFrames, __func__);
+ mPendingFrames.Clear();
},
[self, this](const MediaResult& aError) {
mDecodePromiseRequest.Complete();
mDecodePromise.Reject(aError, __func__);
})
->Track(mDecodePromiseRequest);
}
@@ -406,30 +415,72 @@ H264Converter::CheckForSPSChange(MediaRa
}
extra_data = mOriginalExtraData = aSample->mExtraData;
}
if (mp4_demuxer::AnnexB::CompareExtraData(extra_data,
mCurrentConfig.mExtraData)) {
return NS_OK;
}
- RefPtr<MediaRawData> sample = aSample;
MOZ_ASSERT(mCanRecycleDecoder.isSome());
if (*mCanRecycleDecoder) {
// Do not recreate the decoder, reuse it.
UpdateConfigFromExtraData(extra_data);
- if (!sample->mTrackInfo) {
- sample->mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, 0);
+ if (!aSample->mTrackInfo) {
+ aSample->mTrackInfo = new TrackInfoSharedPtr(mCurrentConfig, 0);
}
mNeedKeyframe = true;
return NS_OK;
}
- // The SPS has changed, signal to flush the current decoder and create a
- // new one.
+ // The SPS has changed, signal to drain the current decoder and once done
+ // create a new one.
+ DrainThenFlushDecoder(aSample);
+ return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
+}
+
+void
+H264Converter::DrainThenFlushDecoder(MediaRawData* aPendingSample)
+{
+ RefPtr<MediaRawData> sample = aPendingSample;
+ RefPtr<H264Converter> self = this;
+ mDecoder->Drain()
+ ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
+ __func__,
+ [self, sample, this](const MediaDataDecoder::DecodedData& aResults) {
+ mDrainRequest.Complete();
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Resolve(true, __func__);
+ return;
+ }
+ if (aResults.Length() > 0) {
+ mPendingFrames.AppendElements(aResults);
+ DrainThenFlushDecoder(sample);
+ return;
+ }
+ // We've completed the draining operation, we can now flush the
+ // decoder.
+ FlushThenShutdownDecoder(sample);
+ },
+ [self, this](const MediaResult& aError) {
+ mDrainRequest.Complete();
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Reject(aError, __func__);
+ return;
+ }
+ mDecodePromise.Reject(aError, __func__);
+ })
+ ->Track(mDrainRequest);
+}
+
+void H264Converter::FlushThenShutdownDecoder(MediaRawData* aPendingSample)
+{
+ RefPtr<MediaRawData> sample = aPendingSample;
RefPtr<H264Converter> self = this;
mDecoder->Flush()
->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
__func__,
[self, sample, this]() {
mFlushRequest.Complete();
if (!mFlushPromise.IsEmpty()) {
@@ -461,25 +512,24 @@ H264Converter::CheckForSPSChange(MediaRa
mDecodePromise.Reject(rv, __func__);
return;
},
[] { MOZ_CRASH("Can't reach here'"); })
->Track(mShutdownRequest);
},
[self, this](const MediaResult& aError) {
mFlushRequest.Complete();
- if (!mFlushPromise.IsEmpty()) {
- // A Flush is pending, abort the current operation.
- mFlushPromise.Reject(aError, __func__);
- return;
- }
- mDecodePromise.Reject(aError, __func__);
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Reject(aError, __func__);
+ return;
+ }
+ mDecodePromise.Reject(aError, __func__);
})
->Track(mFlushRequest);
- return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
}
void
H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
{
mp4_demuxer::SPSData spsdata;
if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)
&& spsdata.pic_width > 0