MediaResult H264Converter::CreateDecoderAndInit(MediaRawData* aSample) { RefPtr<MediaByteBuffer> extra_data = H264::ExtractExtraData(aSample); bool inbandExtradata = H264::HasSPS(extra_data); if (!inbandExtradata && !H264::HasSPS(mCurrentConfig.mExtraData)) { return NS_ERROR_NOT_INITIALIZED; } if (inbandExtradata) { UpdateConfigFromExtraData(extra_data); } MediaResult rv = CreateDecoder(mCurrentConfig, /* DecoderDoctorDiagnostics* */ nullptr); if (NS_SUCCEEDED(rv)) { RefPtr<H264Converter> self = this; RefPtr<MediaRawData> sample = aSample; mDecoder->Init() ->Then( AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, sample, this](const TrackType aTrackType) { mInitPromiseRequest.Complete(); 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); }, [self, this](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__); }) ->Track(mInitPromiseRequest); return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER; } return rv; }
nsresult H264Converter::CheckForSPSChange(MediaRawData* aSample) { RefPtr<MediaByteBuffer> extra_data = mp4_demuxer::AnnexB::ExtractExtraData(aSample); if (!mp4_demuxer::AnnexB::HasSPS(extra_data) || mp4_demuxer::AnnexB::CompareExtraData(extra_data, mCurrentConfig.mExtraData)) { return NS_OK; } RefPtr<MediaRawData> sample = aSample; if (CanRecycleDecoder()) { // Do not recreate the decoder, reuse it. UpdateConfigFromExtraData(extra_data); if (!sample->mTrackInfo) { sample->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. RefPtr<H264Converter> self = this; mDecoder->Flush() ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, sample, this]() { mFlushRequest.Complete(); mShutdownPromise = Shutdown(); mShutdownPromise ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, sample, this]() { mShutdownRequest.Complete(); mShutdownPromise = nullptr; mNeedAVCC.reset(); 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__); }) ->Track(mFlushRequest); return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER; }
void H264Converter::DecodeFirstSample(MediaRawData* aSample) { if (mNeedKeyframe && !aSample->mKeyframe) { mDecodePromise.Resolve(DecodedData(), __func__); return; } mNeedKeyframe = false; mNeedAVCC = Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC); if (!*mNeedAVCC && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) { mDecodePromise.Reject( MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAnnexB")), __func__); return; } if (CanRecycleDecoder()) { mDecoder->ConfigurationChanged(mCurrentConfig); } RefPtr<H264Converter> self = this; mDecoder->Decode(aSample) ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, this](const MediaDataDecoder::DecodedData& aResults) { mDecodePromiseRequest.Complete(); mDecodePromise.Resolve(aResults, __func__); }, [self, this](const MediaResult& aError) { mDecodePromiseRequest.Complete(); mDecodePromise.Reject(aError, __func__); }) ->Track(mDecodePromiseRequest); }
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 (!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( MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC")), __func__); } if (!AnnexB::IsAVCC(aSample)) { return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Invalid H264 content")), __func__); } MediaResult rv(NS_OK); if (!mDecoder) { // It is not possible to create an AVCC H264 decoder without SPS. // As such, creation will fail if the extra_data just extracted doesn't // contain a SPS. rv = CreateDecoderAndInit(aSample); if (rv == NS_ERROR_NOT_INITIALIZED) { // We are missing the required SPS to create the decoder. // Ignore for the time being, the MediaRawData will be dropped. return DecodePromise::CreateAndResolve(DecodedData(), __func__); } } else { // Initialize the members that we couldn't if the extradata was given during // H264Converter's construction. if (!mNeedAVCC) { mNeedAVCC = Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC); } if (!mCanRecycleDecoder) { mCanRecycleDecoder = Some(CanRecycleDecoder()); } rv = CheckForSPSChange(aSample); } if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) { // The decoder is pending initialization. RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__); return p; } if (NS_FAILED(rv)) { return DecodePromise::CreateAndReject(rv, __func__); } if (mNeedKeyframe && !aSample->mKeyframe) { return DecodePromise::CreateAndResolve(DecodedData(), __func__); } auto res = !*mNeedAVCC ? AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe) : Ok(); if (res.isErr()) { return DecodePromise::CreateAndReject( MediaResult(res.unwrapErr(), RESULT_DETAIL("ConvertSampleToAnnexB")), __func__); } mNeedKeyframe = false; aSample->mExtraData = mCurrentConfig.mExtraData; return mDecoder->Decode(aSample); }
nsresult H264Converter::CheckForSPSChange(MediaRawData* aSample) { RefPtr<MediaByteBuffer> extra_data = mp4_demuxer::AnnexB::ExtractExtraData(aSample); if (!mp4_demuxer::AnnexB::HasSPS(extra_data) || mp4_demuxer::AnnexB::CompareExtraData(extra_data, mCurrentConfig.mExtraData)) { return NS_OK; } mPendingSample = aSample; if (CanRecycleDecoder()) { // Do not recreate the decoder, reuse it. UpdateConfigFromExtraData(extra_data); // Ideally we would want to drain the decoder instead of flushing it. // However the draining operation requires calling Drain and looping several // times which isn't possible from within the H264Converter. So instead we // flush the decoder. In practice, this is a no-op as SPS change will only // be used with MSE. And with MSE, the MediaFormatReader would have drained // the decoder already. RefPtr<H264Converter> self = this; mDecoder->Flush() ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, this]() { mFlushRequest.Complete(); DecodeFirstSample(mPendingSample); mPendingSample = nullptr; }, [self, this](const MediaResult& aError) { mFlushRequest.Complete(); mDecodePromise.Reject(aError, __func__); }) ->Track(mFlushRequest); mNeedKeyframe = true; // This is not really initializing the decoder, but it will do as it // indicates an operation is pending. return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER; } // 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, this]() { mFlushRequest.Complete(); mShutdownPromise = Shutdown(); mShutdownPromise ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, this]() { mShutdownRequest.Complete(); mShutdownPromise = nullptr; mNeedAVCC.reset(); RefPtr<MediaRawData> sample = mPendingSample.forget(); 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__); }) ->Track(mFlushRequest); return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER; }