Bug 1312337. Part 1 - move creation/initialization of decoders into DecoderFactory.
So m{Audio,Video}.mDecoder will never reference a half-baked decoder.
MozReview-Commit-ID: FwuzaRR9ugf
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -32,16 +32,180 @@ using mozilla::layers::LayersBackend;
static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader");
mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer");
#define LOG(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define LOGV(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
namespace mozilla {
+class MediaFormatReader::DecoderFactory
+{
+ using InitPromise = MediaDataDecoder::InitPromise;
+
+public:
+ explicit DecoderFactory(MediaFormatReader* aOwner) : mOwner(aOwner) {}
+ void CreateDecoder(TrackType aTrack);
+
+private:
+ enum class Stage : int8_t
+ {
+ None,
+ WaitForInit
+ };
+
+ struct Data
+ {
+ Stage mStage = Stage::None;
+ RefPtr<MediaDataDecoder> mDecoder;
+ MozPromiseRequestHolder<InitPromise> mInitPromise;
+ ~Data()
+ {
+ mInitPromise.DisconnectIfExists();
+ if (mDecoder) {
+ mDecoder->Shutdown();
+ }
+ }
+ } mAudio, mVideo;
+
+ void RunStage(TrackType aTrack);
+ MediaResult DoCreateDecoder(TrackType aTrack);
+ void DoInitDecoder(TrackType aTrack);
+
+ MediaFormatReader* const mOwner; // guaranteed to be valid by the owner.
+};
+
+void
+MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack)
+{
+ MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
+ aTrack == TrackInfo::kVideoTrack);
+ RunStage(aTrack);
+}
+
+void
+MediaFormatReader::DecoderFactory::RunStage(TrackType aTrack)
+{
+ auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+ switch (data.mStage) {
+ case Stage::None: {
+ MOZ_ASSERT(!data.mDecoder);
+ MOZ_ASSERT(!data.mInitPromise.Exists());
+
+ MediaResult rv = DoCreateDecoder(aTrack);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Error constructing decoders");
+ data.mStage = Stage::None;
+ mOwner->NotifyError(aTrack, rv);
+ return;
+ }
+
+ DoInitDecoder(aTrack);
+ data.mStage = Stage::WaitForInit;
+ break;
+ }
+
+ case Stage::WaitForInit: {
+ MOZ_ASSERT(data.mDecoder);
+ MOZ_ASSERT(data.mInitPromise.Exists());
+ break;
+ }
+ }
+}
+
+MediaResult
+MediaFormatReader::DecoderFactory::DoCreateDecoder(TrackType aTrack)
+{
+ auto& ownerData = mOwner->GetDecoderData(aTrack);
+ auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+ auto decoderCreatingError = "error creating audio decoder";
+ MediaResult result = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, decoderCreatingError);
+
+ if (!mOwner->mPlatform) {
+ mOwner->mPlatform = new PDMFactory();
+ if (mOwner->IsEncrypted()) {
+ MOZ_ASSERT(mOwner->mCDMProxy);
+ mOwner->mPlatform->SetCDMProxy(mOwner->mCDMProxy);
+ }
+ }
+
+ switch (aTrack) {
+ case TrackInfo::kAudioTrack: {
+ data.mDecoder = mOwner->mPlatform->CreateDecoder({
+ ownerData.mInfo
+ ? *ownerData.mInfo->GetAsAudioInfo()
+ : *ownerData.mOriginalInfo->GetAsAudioInfo(),
+ ownerData.mTaskQueue,
+ ownerData.mCallback.get(),
+ mOwner->mCrashHelper,
+ ownerData.mIsBlankDecode,
+ &result
+ });
+ break;
+ }
+
+ case TrackType::kVideoTrack: {
+ // Decoders use the layers backend to decide if they can use hardware decoding,
+ // so specify LAYERS_NONE if we want to forcibly disable it.
+ data.mDecoder = mOwner->mPlatform->CreateDecoder({
+ ownerData.mInfo
+ ? *ownerData.mInfo->GetAsVideoInfo()
+ : *ownerData.mOriginalInfo->GetAsVideoInfo(),
+ ownerData.mTaskQueue,
+ ownerData.mCallback.get(),
+ mOwner->mKnowsCompositor,
+ mOwner->GetImageContainer(),
+ mOwner->mCrashHelper,
+ ownerData.mIsBlankDecode,
+ &result
+ });
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (data.mDecoder) {
+ result = MediaResult(NS_OK);
+ return result;
+ }
+
+ ownerData.mDescription = decoderCreatingError;
+ return result;
+}
+
+void
+MediaFormatReader::DecoderFactory::DoInitDecoder(TrackType aTrack)
+{
+ auto& ownerData = mOwner->GetDecoderData(aTrack);
+ auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+ data.mInitPromise.Begin(data.mDecoder->Init()->Then(
+ mOwner->OwnerThread(), __func__,
+ [this, &data, &ownerData] (TrackType aTrack) {
+ data.mInitPromise.Complete();
+ data.mStage = Stage::None;
+ MonitorAutoLock mon(ownerData.mMonitor);
+ ownerData.mDecoder = data.mDecoder.forget();
+ ownerData.mDescription = ownerData.mDecoder->GetDescriptionName();
+ mOwner->SetVideoDecodeThreshold();
+ mOwner->ScheduleUpdate(aTrack);
+ },
+ [this, &data, aTrack] (MediaResult aError) {
+ data.mInitPromise.Complete();
+ data.mStage = Stage::None;
+ data.mDecoder->Shutdown();
+ data.mDecoder = nullptr;
+ mOwner->NotifyError(aTrack, aError);
+ }));
+}
+
static const char*
TrackTypeToStr(TrackInfo::TrackType aTrack)
{
MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
aTrack == TrackInfo::kVideoTrack ||
aTrack == TrackInfo::kTextTrack);
switch (aTrack) {
case TrackInfo::kAudioTrack:
@@ -67,31 +231,33 @@ MediaFormatReader::MediaFormatReader(Abs
, mDemuxerInitDone(false)
, mLastReportedNumDecodedFrames(0)
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
, mInitDone(false)
, mTrackDemuxersMayBlock(false)
, mDemuxOnly(false)
, mSeekScheduled(false)
, mVideoFrameContainer(aVideoFrameContainer)
+ , mDecoderFactory(new DecoderFactory(this))
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
}
MediaFormatReader::~MediaFormatReader()
{
MOZ_COUNT_DTOR(MediaFormatReader);
}
RefPtr<ShutdownPromise>
MediaFormatReader::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
+ mDecoderFactory = nullptr;
mDemuxerInitRequest.DisconnectIfExists();
mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mSkipRequest.DisconnectIfExists();
if (mAudio.mDecoder) {
Reset(TrackInfo::kAudioTrack);
if (mAudio.HasPromise()) {
@@ -370,127 +536,16 @@ MediaFormatReader::IsEncrypted() const
void
MediaFormatReader::OnDemuxerInitFailed(const MediaResult& aError)
{
mDemuxerInitRequest.Complete();
mMetadataPromise.Reject(aError, __func__);
}
-MediaResult
-MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
-{
- MOZ_ASSERT(OnTaskQueue());
- MOZ_DIAGNOSTIC_ASSERT(!IsSuspended());
-
- auto decoderCreatingError = "error creating decoder";
- MediaResult result = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, decoderCreatingError);
- auto& decoder = GetDecoderData(aTrack);
-
- if (decoder.mDecoder) {
- result = NS_OK;
- return result;
- }
-
- if (!mPlatform) {
- mPlatform = new PDMFactory();
- if (IsEncrypted()) {
- MOZ_ASSERT(mCDMProxy);
- mPlatform->SetCDMProxy(mCDMProxy);
- }
- }
-
- decoder.mDecoderInitialized = false;
-
- MonitorAutoLock mon(decoder.mMonitor);
-
- switch (aTrack) {
- case TrackType::kAudioTrack: {
- decoder.mDecoder = mPlatform->CreateDecoder({
- decoder.mInfo
- ? *decoder.mInfo->GetAsAudioInfo()
- : *decoder.mOriginalInfo->GetAsAudioInfo(),
- decoder.mTaskQueue,
- decoder.mCallback.get(),
- mCrashHelper,
- decoder.mIsBlankDecode,
- &result
- });
- break;
- }
-
- case TrackType::kVideoTrack: {
- // Decoders use the layers backend to decide if they can use hardware decoding,
- // so specify LAYERS_NONE if we want to forcibly disable it.
- decoder.mDecoder = mPlatform->CreateDecoder({
- decoder.mInfo
- ? *decoder.mInfo->GetAsVideoInfo()
- : *decoder.mOriginalInfo->GetAsVideoInfo(),
- decoder.mTaskQueue,
- decoder.mCallback.get(),
- mKnowsCompositor,
- GetImageContainer(),
- mCrashHelper,
- decoder.mIsBlankDecode,
- &result
- });
- break;
- }
- default:
- break;
- }
- if (decoder.mDecoder) {
- decoder.mDescription = decoder.mDecoder->GetDescriptionName();
- result = MediaResult(NS_OK);
- return result;
- }
-
- decoder.mDescription = decoderCreatingError;
- return result;
-}
-
-bool
-MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack)
-{
- MOZ_ASSERT(OnTaskQueue());
- MOZ_DIAGNOSTIC_ASSERT(!IsSuspended());
-
- auto& decoder = GetDecoderData(aTrack);
-
- if (!decoder.mDecoder || decoder.mInitPromise.Exists()) {
- MOZ_ASSERT(decoder.mDecoder);
- return false;
- }
- if (decoder.mDecoderInitialized) {
- return true;
- }
-
- RefPtr<MediaFormatReader> self = this;
- decoder.mInitPromise.Begin(decoder.mDecoder->Init()
- ->Then(OwnerThread(), __func__,
- [self] (TrackType aTrack) {
- MOZ_DIAGNOSTIC_ASSERT(!self->IsSuspended());
- auto& decoder = self->GetDecoderData(aTrack);
- MOZ_DIAGNOSTIC_ASSERT(decoder.mDecoder);
- decoder.mInitPromise.Complete();
- decoder.mDecoderInitialized = true;
- MonitorAutoLock mon(decoder.mMonitor);
- decoder.mDescription = decoder.mDecoder->GetDescriptionName();
- self->SetVideoDecodeThreshold();
- self->ScheduleUpdate(aTrack);
- },
- [self, aTrack] (MediaResult aError) {
- auto& decoder = self->GetDecoderData(aTrack);
- decoder.mInitPromise.Complete();
- decoder.ShutdownDecoder();
- self->NotifyError(aTrack, aError);
- }));
- return false;
-}
-
void
MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo)
{
*aInfo = mInfo;
}
MediaFormatReader::DecoderData&
MediaFormatReader::GetDecoderData(TrackType aTrack)
@@ -943,24 +998,18 @@ MediaFormatReader::HandleDemuxedSamples(
}
auto& decoder = GetDecoderData(aTrack);
if (decoder.mQueuedSamples.IsEmpty()) {
return;
}
- MediaResult rv = EnsureDecoderCreated(aTrack);
- if (NS_FAILED(rv)) {
- NS_WARNING("Error constructing decoders");
- NotifyError(aTrack, rv);
- return;
- }
-
- if (!EnsureDecoderInitialized(aTrack)) {
+ if (!decoder.mDecoder) {
+ mDecoderFactory->CreateDecoder(aTrack);
return;
}
if (!ForceZeroStartTime() && decoder.mFirstDemuxedSampleTime.isNothing()) {
decoder.mFirstDemuxedSampleTime.emplace(
media::TimeUnit::FromMicroseconds(decoder.mQueuedSamples[0]->mTime));
}
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -109,19 +109,16 @@ private:
bool InitDemuxer();
// Notify the demuxer that new data has been received.
// The next queued task calling GetBuffered() is guaranteed to have up to date
// buffered ranges.
void NotifyDemuxer();
void ReturnOutput(MediaData* aData, TrackType aTrack);
- MediaResult EnsureDecoderCreated(TrackType aTrack);
- bool EnsureDecoderInitialized(TrackType aTrack);
-
// Enqueues a task to call Update(aTrack) on the decoder task queue.
// Lock for corresponding track must be held.
void ScheduleUpdate(TrackType aTrack);
void Update(TrackType aTrack);
// Handle actions should more data be received.
// Returns true if no more action is required.
bool UpdateReceivedNewData(TrackType aTrack);
// Called when new samples need to be demuxed.
@@ -233,17 +230,16 @@ private:
, mType(aType)
, mMonitor("DecoderData")
, mDescription("shutdown")
, mUpdateScheduled(false)
, mDemuxEOS(false)
, mWaitingForData(false)
, mWaitingForKey(false)
, mReceivedNewData(false)
- , mDecoderInitialized(false)
, mOutputRequested(false)
, mDecodePending(false)
, mNeedDraining(false)
, mDraining(false)
, mDrainComplete(false)
, mNumOfConsecutiveError(0)
, mMaxConsecutiveError(aNumOfMaxError)
, mNumSamplesInput(0)
@@ -268,17 +264,16 @@ private:
// Monitor protecting mDescription and mDecoder.
Monitor mMonitor;
// The platform decoder.
RefPtr<MediaDataDecoder> mDecoder;
const char* mDescription;
void ShutdownDecoder()
{
- mInitPromise.DisconnectIfExists();
MonitorAutoLock mon(mMonitor);
if (mDecoder) {
mDecoder->Shutdown();
}
mDescription = "shutdown";
mDecoder = nullptr;
}
@@ -305,20 +300,16 @@ private:
}
bool IsWaiting() const
{
MOZ_ASSERT(mOwner->OnTaskQueue());
return mWaitingForData || mWaitingForKey;
}
// MediaDataDecoder handler's variables.
- // Decoder initialization promise holder.
- MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mInitPromise;
- // False when decoder is created. True when decoder Init() promise is resolved.
- bool mDecoderInitialized;
bool mOutputRequested;
// Set to true once the MediaDataDecoder has been fed a compressed sample.
// No more samples will be passed to the decoder while true.
// mDecodePending is reset when:
// 1- The decoder calls InputExhausted
// 2- The decoder is Flushed or Reset.
bool mDecodePending;
bool mNeedDraining;
@@ -576,13 +567,16 @@ private:
RefPtr<VideoFrameContainer> mVideoFrameContainer;
layers::ImageContainer* GetImageContainer();
RefPtr<CDMProxy> mCDMProxy;
RefPtr<GMPCrashHelper> mCrashHelper;
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
+
+ class DecoderFactory;
+ UniquePtr<DecoderFactory> mDecoderFactory;
};
} // namespace mozilla
#endif