Bug 1370805: P2. Let current operation completes before continuing. r?jwwang
This is a simpler approach required as both InitPromise and FlushPromise are exclusives.
It's in practice simpler too.
MozReview-Commit-ID: ItaAhC0Bk8T
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -55,16 +55,18 @@ H264Converter::Init()
// We haven't been able to initialize a decoder due to a missing SPS/PPS.
return MediaDataDecoder::InitPromise::CreateAndResolve(
TrackType::kVideoTrack, __func__);
}
RefPtr<MediaDataDecoder::DecodePromise>
H264Converter::Decode(MediaRawData* aSample)
{
+ MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Flush operatin didn't complete");
+
MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists()
&& !mInitPromiseRequest.Exists(),
"Can't request a new decode until previous one completed");
if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
// We need AVCC content to be able to later parse the SPS.
// This is a no-op if the data is already AVCC.
return DecodePromise::CreateAndReject(
@@ -127,20 +129,47 @@ H264Converter::Decode(MediaRawData* aSam
return mDecoder->Decode(aSample);
}
RefPtr<MediaDataDecoder::FlushPromise>
H264Converter::Flush()
{
mDecodePromiseRequest.DisconnectIfExists();
- mFlushRequest.DisconnectIfExists();
- mShutdownRequest.DisconnectIfExists();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mNeedKeyframe = true;
+
+ 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.
+ If during (1):
+ - mFlushRequest will not be empty.
+ - The old decoder can still be used, with the current extradata as stored
+ in mCurrentConfig.mExtraData.
+
+ If during (2):
+ - mShutdownRequest won't be empty.
+ - mDecoder is empty.
+ - The old decoder is no longer referenced by the H264Converter.
+
+ If during (3):
+ - mInitPromiseRequest won't be empty.
+ - mDecoder is set but not usable yet.
+ */
+
+ if (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()
@@ -154,17 +183,20 @@ H264Converter::Drain()
RefPtr<ShutdownPromise>
H264Converter::Shutdown()
{
mInitPromiseRequest.DisconnectIfExists();
mDecodePromiseRequest.DisconnectIfExists();
mFlushRequest.DisconnectIfExists();
mShutdownRequest.DisconnectIfExists();
+ mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mPendingSample = nullptr;
+ mNeedAVCC.reset();
+
if (mShutdownPromise) {
// We have a shutdown in progress, return that promise instead as we can't
// shutdown a decoder twice.
return mShutdownPromise.forget();
}
if (mDecoder) {
RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
return decoder->Shutdown();
@@ -273,23 +305,36 @@ H264Converter::OnDecoderInitDone(const T
{
mInitPromiseRequest.Complete();
RefPtr<MediaRawData> sample = mPendingSample.forget();
mNeedAVCC =
Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
mCanRecycleDecoder = Some(CanRecycleDecoder());
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Resolve(true, __func__);
+ return;
+ }
+
DecodeFirstSample(sample);
}
void
H264Converter::OnDecoderInitFailed(const MediaResult& aError)
{
mInitPromiseRequest.Complete();
+
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Reject(aError, __func__);
+ return;
+ }
+
mDecodePromise.Reject(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("Unable to initialize H264 decoder")),
__func__);
}
bool
H264Converter::CanRecycleDecoder() const
@@ -376,39 +421,57 @@ H264Converter::CheckForSPSChange(MediaRa
// The SPS has changed, signal to flush the current decoder and create a
// new one.
RefPtr<H264Converter> self = this;
mDecoder->Flush()
->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
__func__,
[self, sample, this]() {
mFlushRequest.Complete();
+
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Resolve(true, __func__);
+ return;
+ }
+
mShutdownPromise = Shutdown();
mShutdownPromise
->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
__func__,
[self, sample, this]() {
mShutdownRequest.Complete();
mShutdownPromise = nullptr;
- mNeedAVCC.reset();
+
+ if (!mFlushPromise.IsEmpty()) {
+ // A Flush is pending, abort the current operation.
+ mFlushPromise.Resolve(true, __func__);
+ return;
+ }
+
nsresult rv = CreateDecoderAndInit(sample);
if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
// All good so far, will continue later.
return;
}
MOZ_ASSERT(NS_FAILED(rv));
mDecodePromise.Reject(rv, __func__);
return;
},
[] { MOZ_CRASH("Can't reach here'"); })
->Track(mShutdownRequest);
},
[self, this](const MediaResult& aError) {
mFlushRequest.Complete();
- 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)
{
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -89,16 +89,17 @@ private:
RefPtr<MediaRawData> mPendingSample;
RefPtr<MediaDataDecoder> mDecoder;
MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
MozPromiseHolder<DecodePromise> mDecodePromise;
MozPromiseRequestHolder<FlushPromise> mFlushRequest;
MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
RefPtr<ShutdownPromise> mShutdownPromise;
+ MozPromiseHolder<FlushPromise> mFlushPromise;
RefPtr<GMPCrashHelper> mGMPCrashHelper;
Maybe<bool> mNeedAVCC;
nsresult mLastError;
bool mNeedKeyframe = true;
const TrackInfo::TrackType mType;
MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
const CreateDecoderParams::OptionSet mDecoderOptions;