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:
Jean-Yves Avenard 2017-02-13 11:15:54 +01:00
Родитель 36471a6bc1
Коммит 7a7dc2c2ed
4 изменённых файлов: 115 добавлений и 45 удалений

Просмотреть файл

@ -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;
};