nsresult H264Converter::Input(MediaRawData* aSample) { if (!mNeedAVCC) { if (!mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) { return NS_ERROR_FAILURE; } } else { if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) { return NS_ERROR_FAILURE; } } nsresult rv; 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 NS_OK; } } else { rv = CheckForSPSChange(aSample); } NS_ENSURE_SUCCESS(rv, rv); aSample->mExtraData = mCurrentConfig.mExtraData; return mDecoder->Input(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; } 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::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()) { // A Flush is pending, abort the current operation. mFlushPromise.Resolve(true, __func__); return; } mShutdownPromise = ShutdownDecoder(); mShutdownPromise ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__, [self, sample, this]() { mShutdownRequest.Complete(); mShutdownPromise = nullptr; if (!mFlushPromise.IsEmpty()) { // A Flush is pending, abort the current operation. mFlushPromise.Resolve(true, __func__); return; } MediaResult 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(); if (!mFlushPromise.IsEmpty()) { // A Flush is pending, abort the current operation. mFlushPromise.Reject(aError, __func__); return; } mDecodePromise.Reject(aError, __func__); }) ->Track(mFlushRequest); }
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; } // The SPS has changed, signal to flush the current decoder and create a // new one. mDecoder->Flush(); Shutdown(); return CreateDecoderAndInit(aSample); }
nsresult H264Converter::Input(MediaRawData* aSample) { 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 NS_ERROR_FAILURE; } if (mInitPromiseRequest.Exists()) { mMediaRawSamples.AppendElement(aSample); return NS_OK; } nsresult rv; 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 NS_OK; } } else { rv = CheckForSPSChange(aSample); } NS_ENSURE_SUCCESS(rv, rv); if (!mNeedAVCC && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) { return NS_ERROR_FAILURE; } aSample->mExtraData = mCurrentConfig.mExtraData; return mDecoder->Input(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; } if (MediaPrefs::MediaDecoderCheckRecycling() && mDecoder->SupportDecoderRecycling()) { // Do not recreate the decoder, reuse it. UpdateConfigFromExtraData(extra_data); mNeedKeyframe = true; return NS_OK; } // The SPS has changed, signal to flush the current decoder and create a // new one. mDecoder->Flush(); Shutdown(); return CreateDecoderAndInit(aSample); }
void H264Converter::Input(MediaRawData* aSample) { 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. mCallback->Error(MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC"))); return; } if (mInitPromiseRequest.Exists()) { if (mNeedKeyframe) { if (!aSample->mKeyframe) { // Frames dropped, we need a new one. mCallback->InputExhausted(); return; } mNeedKeyframe = false; } mMediaRawSamples.AppendElement(aSample); return; } nsresult rv; 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. mCallback->InputExhausted(); return; } } else { rv = CheckForSPSChange(aSample); if (rv == NS_ERROR_NOT_INITIALIZED) { // The decoder is pending initialization. mCallback->InputExhausted(); return; } } if (NS_FAILED(rv)) { mCallback->Error( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Unable to create H264 decoder"))); return; } if (mNeedKeyframe && !aSample->mKeyframe) { mCallback->InputExhausted(); return; } if (!mNeedAVCC && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) { mCallback->Error(MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAnnexB"))); return; } mNeedKeyframe = false; aSample->mExtraData = mCurrentConfig.mExtraData; mDecoder->Input(aSample); }
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); }
RefPtr<MediaDataDecoder::DecodePromise> H264Converter::Decode(MediaRawData* aSample) { 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( MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC")), __func__); } nsresult rv; 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 { 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( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Unable to create H264 decoder")), __func__); } if (mNeedKeyframe && !aSample->mKeyframe) { return DecodePromise::CreateAndResolve(DecodedData(), __func__); } if (!mNeedAVCC) { mNeedAVCC = Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC); } if (!*mNeedAVCC && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)) { return DecodePromise::CreateAndReject( MediaResult(NS_ERROR_OUT_OF_MEMORY, 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; }