зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1336431: P5. Re-add ConfigurationChanged API. r=JamesCheng
We need to inform the MediaDataDecoder that a new sample configuration has been detected. To do so, and to ensure that samples are easily matched to a given configuration, we first drain the decoder prior feeding a new frame. MozReview-Commit-ID: Hye251CF21i --HG-- extra : rebase_source : d6e5be74a6678cef0ff4c798fac91d95911be70a
This commit is contained in:
Родитель
36471a6bc1
Коммит
7a7dc2c2ed
|
@ -289,6 +289,10 @@ public:
|
|||
{
|
||||
return mDecoder->SupportDecoderRecycling();
|
||||
}
|
||||
void ConfigurationChanged(const TrackInfo& aConfig) override
|
||||
{
|
||||
mDecoder->ConfigurationChanged(aConfig);
|
||||
}
|
||||
RefPtr<ShutdownPromise> Shutdown() override
|
||||
{
|
||||
RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
|
||||
|
@ -1760,19 +1764,15 @@ MediaFormatReader::HandleDemuxedSamples(
|
|||
RefPtr<TrackInfoSharedPtr> info = sample->mTrackInfo;
|
||||
|
||||
if (info && decoder.mLastStreamSourceID != info->GetID()) {
|
||||
bool supportRecycling = MediaPrefs::MediaDecoderCheckRecycling()
|
||||
&& decoder.mDecoder->SupportDecoderRecycling();
|
||||
if (decoder.mNextStreamSourceID.isNothing()
|
||||
|| decoder.mNextStreamSourceID.ref() != info->GetID()) {
|
||||
if (!supportRecycling) {
|
||||
LOG("%s stream id has changed from:%d to:%d, draining decoder.",
|
||||
TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
|
||||
info->GetID());
|
||||
decoder.RequestDrain();
|
||||
decoder.mNextStreamSourceID = Some(info->GetID());
|
||||
ScheduleUpdate(aTrack);
|
||||
return;
|
||||
}
|
||||
LOG("%s stream id has changed from:%d to:%d, draining decoder.",
|
||||
TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
|
||||
info->GetID());
|
||||
decoder.RequestDrain();
|
||||
decoder.mNextStreamSourceID = Some(info->GetID());
|
||||
ScheduleUpdate(aTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("%s stream id has changed from:%d to:%d.",
|
||||
|
@ -1780,9 +1780,9 @@ MediaFormatReader::HandleDemuxedSamples(
|
|||
info->GetID());
|
||||
decoder.mLastStreamSourceID = info->GetID();
|
||||
decoder.mNextStreamSourceID.reset();
|
||||
decoder.mInfo = info;
|
||||
|
||||
if (!supportRecycling) {
|
||||
if (!MediaPrefs::MediaDecoderCheckRecycling()
|
||||
|| !decoder.mDecoder->SupportDecoderRecycling()) {
|
||||
LOG("Decoder does not support recycling, recreate decoder.");
|
||||
// If flushing is required, it will clear our array of queued samples.
|
||||
// So make a copy now.
|
||||
|
@ -1791,8 +1791,13 @@ MediaFormatReader::HandleDemuxedSamples(
|
|||
if (sample->mKeyframe) {
|
||||
decoder.mQueuedSamples.AppendElements(Move(samples));
|
||||
}
|
||||
} else if (decoder.mInfo && *decoder.mInfo != *info) {
|
||||
const TrackInfo* trackInfo = *info;
|
||||
decoder.mDecoder->ConfigurationChanged(*trackInfo);
|
||||
}
|
||||
|
||||
decoder.mInfo = info;
|
||||
|
||||
if (sample->mKeyframe) {
|
||||
ScheduleUpdate(aTrack);
|
||||
} else {
|
||||
|
|
|
@ -283,7 +283,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Return the name of the MediaDataDecoder, only used for decoding.
|
||||
// Only return a static const string, as the information may be accessed
|
||||
// in a non thread-safe fashion.
|
||||
|
@ -302,6 +301,21 @@ public:
|
|||
// Reuse the decoder if the decoder support recycling.
|
||||
// Currently, only Android video decoder will return true.
|
||||
virtual bool SupportDecoderRecycling() const { return false; }
|
||||
|
||||
// ConfigurationChanged will be called to inform the video or audio decoder
|
||||
// that the format of the next input sample is about to change.
|
||||
// If video decoder, aConfig will be a VideoInfo object.
|
||||
// If audio decoder, aConfig will be a AudioInfo object.
|
||||
// It is not safe to store a reference to this object and the decoder must
|
||||
// make a copy.
|
||||
// Care should be taken as ConfigurationChanged is called on the reader's
|
||||
// taskqueue.
|
||||
virtual void ConfigurationChanged(const TrackInfo& aConfig)
|
||||
{
|
||||
MOZ_ASSERT(SupportDecoderRecycling(),
|
||||
"Can only work with a decoder supporting recycling.");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace mozilla
|
|||
H264Converter::H264Converter(PlatformDecoderModule* aPDM,
|
||||
const CreateDecoderParams& aParams)
|
||||
: mPDM(aPDM)
|
||||
, mOriginalConfig(aParams.VideoConfig())
|
||||
, mCurrentConfig(aParams.VideoConfig())
|
||||
, mKnowsCompositor(aParams.mKnowsCompositor)
|
||||
, mImageContainer(aParams.mImageContainer)
|
||||
|
@ -199,7 +200,7 @@ H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
|
|||
}
|
||||
|
||||
mDecoder = mPDM->CreateVideoDecoder({
|
||||
mCurrentConfig,
|
||||
mUseOriginalConfig ? mOriginalConfig : mCurrentConfig,
|
||||
mTaskQueue,
|
||||
aDiagnostics,
|
||||
mImageContainer,
|
||||
|
@ -214,6 +215,7 @@ H264Converter::CreateDecoder(DecoderDoctorDiagnostics* aDiagnostics)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mUseOriginalConfig = false;
|
||||
mNeedKeyframe = true;
|
||||
|
||||
return NS_OK;
|
||||
|
@ -250,31 +252,7 @@ H264Converter::OnDecoderInitDone(const TrackType aTrackType)
|
|||
{
|
||||
mInitPromiseRequest.Complete();
|
||||
RefPtr<MediaRawData> sample = mPendingSample.forget();
|
||||
if (mNeedKeyframe && !sample->mKeyframe) {
|
||||
mDecodePromise.Resolve(DecodedData(), __func__);
|
||||
return;
|
||||
}
|
||||
mNeedKeyframe = false;
|
||||
if (!mNeedAVCC
|
||||
&& !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(sample, mNeedKeyframe)) {
|
||||
mDecodePromise.Reject(
|
||||
MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("ConvertSampleToAnnexB")),
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
RefPtr<H264Converter> self = this;
|
||||
mDecoder->Decode(sample)
|
||||
->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);
|
||||
DecodeFirstSample(sample);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -287,6 +265,39 @@ H264Converter::OnDecoderInitFailed(const MediaResult& aError)
|
|||
__func__);
|
||||
}
|
||||
|
||||
void
|
||||
H264Converter::DecodeFirstSample(MediaRawData* aSample)
|
||||
{
|
||||
if (mNeedKeyframe && !aSample->mKeyframe) {
|
||||
mDecodePromise.Resolve(DecodedData(), __func__);
|
||||
return;
|
||||
}
|
||||
mNeedKeyframe = false;
|
||||
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);
|
||||
}
|
||||
|
||||
nsresult
|
||||
H264Converter::CheckForSPSChange(MediaRawData* aSample)
|
||||
{
|
||||
|
@ -298,16 +309,39 @@ H264Converter::CheckForSPSChange(MediaRawData* aSample)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (MediaPrefs::MediaDecoderCheckRecycling()
|
||||
&& mDecoder->SupportDecoderRecycling()) {
|
||||
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;
|
||||
return NS_OK;
|
||||
// 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.
|
||||
mPendingSample = aSample;
|
||||
RefPtr<H264Converter> self = this;
|
||||
mDecoder->Flush()
|
||||
->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
|
||||
|
|
|
@ -47,7 +47,12 @@ public:
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigurationChanged(const TrackInfo& aConfig) override
|
||||
{
|
||||
if (mDecoder && mDecoder->SupportDecoderRecycling()) {
|
||||
mDecoder->ConfigurationChanged(aConfig);
|
||||
}
|
||||
}
|
||||
nsresult GetLastError() const { return mLastError; }
|
||||
|
||||
private:
|
||||
|
@ -62,7 +67,17 @@ private:
|
|||
void OnDecoderInitDone(const TrackType aTrackType);
|
||||
void OnDecoderInitFailed(const MediaResult& aError);
|
||||
|
||||
bool CanRecycleDecoder() const
|
||||
{
|
||||
MOZ_ASSERT(mDecoder);
|
||||
return MediaPrefs::MediaDecoderCheckRecycling()
|
||||
&& mDecoder->SupportDecoderRecycling();
|
||||
}
|
||||
|
||||
void DecodeFirstSample(MediaRawData* aSample);
|
||||
|
||||
RefPtr<PlatformDecoderModule> mPDM;
|
||||
const VideoInfo mOriginalConfig;
|
||||
VideoInfo mCurrentConfig;
|
||||
RefPtr<layers::KnowsCompositor> mKnowsCompositor;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
|
@ -80,6 +95,8 @@ private:
|
|||
bool mNeedAVCC;
|
||||
nsresult mLastError;
|
||||
bool mNeedKeyframe = true;
|
||||
// Set to true once a decoder has been created.
|
||||
bool mUseOriginalConfig = true;
|
||||
const TrackInfo::TrackType mType;
|
||||
MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче