Bug 1758789 - part13 : fallback to our state machine when the external engine fails to work. r=jolin

This is important for testing because the media foundation doesn't support all the formats Firefox supports, we would need to fallback to the normal state machine in order to keep tests running without breaking them.

Differential Revision: https://phabricator.services.mozilla.com/D145402
This commit is contained in:
alwu 2022-06-30 22:34:20 +00:00
Родитель c925033c81
Коммит 2a20f58d80
14 изменённых файлов: 137 добавлений и 71 удалений

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

@ -201,7 +201,8 @@ already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Clone(
return decoder.forget();
}
MediaDecoderStateMachineBase* ChannelMediaDecoder::CreateStateMachine() {
MediaDecoderStateMachineBase* ChannelMediaDecoder::CreateStateMachine(
bool aDisableExternalEngine) {
MOZ_ASSERT(NS_IsMainThread());
MediaFormatReaderInit init;
init.mVideoFrameContainer = GetVideoFrameContainer();
@ -211,11 +212,13 @@ MediaDecoderStateMachineBase* ChannelMediaDecoder::CreateStateMachine() {
init.mResource = mResource;
init.mMediaDecoderOwnerID = mOwner;
mReader = DecoderTraits::CreateReader(ContainerType(), init);
#ifdef MOZ_WMF
// TODO : Only for testing development for now. In the future this should be
// used for encrypted content only.
if (StaticPrefs::media_wmf_media_engine_enabled() &&
StaticPrefs::media_wmf_media_engine_channel_decoder_enabled()) {
StaticPrefs::media_wmf_media_engine_channel_decoder_enabled() &&
!aDisableExternalEngine) {
return new ExternalEngineStateMachine(this, mReader);
}
#endif
@ -267,13 +270,7 @@ nsresult ChannelMediaDecoder::Load(nsIChannel* aChannel,
rv = mResource->Open(aStreamListener);
NS_ENSURE_SUCCESS(rv, rv);
SetStateMachine(CreateStateMachine());
NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
GetStateMachine()->DispatchIsLiveStream(mResource->IsLiveStream());
return InitializeStateMachine();
return CreateAndInitStateMachine(mResource->IsLiveStream());
}
nsresult ChannelMediaDecoder::Load(BaseMediaResource* aOriginal) {
@ -290,13 +287,7 @@ nsresult ChannelMediaDecoder::Load(BaseMediaResource* aOriginal) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SetStateMachine(CreateStateMachine());
NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
GetStateMachine()->DispatchIsLiveStream(mResource->IsLiveStream());
return InitializeStateMachine();
return CreateAndInitStateMachine(mResource->IsLiveStream());
}
void ChannelMediaDecoder::NotifyDownloadEnded(nsresult aStatus) {

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

@ -102,7 +102,8 @@ class ChannelMediaDecoder
void DownloadProgressed();
// Create a new state machine to run this decoder.
MediaDecoderStateMachineBase* CreateStateMachine();
MediaDecoderStateMachineBase* CreateStateMachine(
bool aDisableExternalEngine) override;
nsresult Load(BaseMediaResource* aOriginal);

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

@ -898,6 +898,20 @@ void ExternalEngineStateMachine::NotifyEventInternal(
}
}
void ExternalEngineStateMachine::NotifyErrorInternal(
const MediaResult& aError) {
AssertOnTaskQueue();
LOG("Engine error: %s", aError.Description().get());
if (aError == NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR) {
// The external engine doesn't support the type, try to notify the decoder
// to use our own state machine again.
DecodeError(
MediaResult(NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR));
} else {
DecodeError(aError);
}
}
#undef FMT
#undef LOG
#undef LOGV

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

@ -85,7 +85,7 @@ class ExternalEngineStateMachine final
// On the engine manager thread.
Unused << OwnerThread()->Dispatch(NS_NewRunnableFunction(
"ExternalEngineStateMachine::NotifyError",
[self = RefPtr{this}, aError] { self->DecodeError(aError); }));
[self = RefPtr{this}, aError] { self->NotifyErrorInternal(aError); }));
}
const char* GetStateStr() const;
@ -197,6 +197,7 @@ class ExternalEngineStateMachine final
using State = StateObject::State;
void NotifyEventInternal(ExternalEngineEvent aEvent);
void NotifyErrorInternal(const MediaResult& aError);
RefPtr<ShutdownPromise> Shutdown() override;

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

@ -339,23 +339,9 @@ void MediaDecoder::Shutdown() {
// necessary to unblock the state machine thread if it's blocked, so
// the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
if (mDecoderStateMachine) {
mTimedMetadataListener.Disconnect();
mMetadataLoadedListener.Disconnect();
mFirstFrameLoadedListener.Disconnect();
mOnPlaybackEvent.Disconnect();
mOnPlaybackErrorEvent.Disconnect();
mOnDecoderDoctorEvent.Disconnect();
mOnMediaNotSeekable.Disconnect();
mOnEncrypted.Disconnect();
mOnWaitingForKey.Disconnect();
mOnDecodeWarning.Disconnect();
mOnNextFrameStatus.Disconnect();
mOnSecondaryVideoContainerInstalled.Disconnect();
mOnStoreDecoderBenchmark.Disconnect();
mDecoderStateMachine->BeginShutdown()->Then(
mAbstractMainThread, __func__, this, &MediaDecoder::FinishShutdown,
&MediaDecoder::FinishShutdown);
ShutdownStateMachine()->Then(mAbstractMainThread, __func__, this,
&MediaDecoder::FinishShutdown,
&MediaDecoder::FinishShutdown);
} else {
// Ensure we always unregister asynchronously in order not to disrupt
// the hashtable iterating in MediaShutdownManager::Shutdown().
@ -433,7 +419,44 @@ bool MediaDecoder::IsVideoDecodingSuspended() const {
}
void MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
#ifndef MOZ_WMF
DecodeError(aError);
#else
if (aError != NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR) {
DecodeError(aError);
return;
}
// Already in shutting down decoder, no need to create another state machine.
if (mPlayState == PLAY_STATE_SHUTDOWN) {
return;
}
// External engine can't play the resource, try to use our own state machine
// again. Here we will create a new state machine immediately and asynchrously
// shutdown the old one because we don't want to dispatch any task to the old
// state machine. Therefore, we will disconnect anything related with the old
// state machine, create a new state machine and setup events/mirror/etc, then
// shutdown the old one and release its reference once it finishes shutdown.
MOZ_ASSERT(aError == NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR);
RefPtr<MediaDecoderStateMachineBase> discardStateMachine =
mDecoderStateMachine;
// Disconnect mirror and events first.
SetStateMachine(nullptr);
DisconnectEvents();
// Recreate a state machine and shutdown the old one.
LOG("Need to create a new state machine");
nsresult rv =
CreateAndInitStateMachine(false, true /* disable external engine*/);
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG("Failed to create a new state machine!");
}
discardStateMachine->BeginShutdown()->Then(
AbstractThread::MainThread(), __func__, [discardStateMachine] {});
#endif
}
void MediaDecoder::OnDecoderDoctorEvent(DecoderDoctorEvent aEvent) {
@ -518,9 +541,13 @@ void MediaDecoder::FinishShutdown() {
ShutdownInternal();
}
nsresult MediaDecoder::InitializeStateMachine() {
nsresult MediaDecoder::CreateAndInitStateMachine(bool aIsLiveStream,
bool aDisableExternalEngine) {
MOZ_ASSERT(NS_IsMainThread());
NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
SetStateMachine(CreateStateMachine(aDisableExternalEngine));
NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
GetStateMachine()->DispatchIsLiveStream(aIsLiveStream);
nsresult rv = mDecoderStateMachine->Init(this);
NS_ENSURE_SUCCESS(rv, rv);
@ -570,6 +597,30 @@ void MediaDecoder::SetStateMachineParameters() {
mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DecodeWarning);
}
void MediaDecoder::DisconnectEvents() {
MOZ_ASSERT(NS_IsMainThread());
mTimedMetadataListener.Disconnect();
mMetadataLoadedListener.Disconnect();
mFirstFrameLoadedListener.Disconnect();
mOnPlaybackEvent.Disconnect();
mOnPlaybackErrorEvent.Disconnect();
mOnDecoderDoctorEvent.Disconnect();
mOnMediaNotSeekable.Disconnect();
mOnEncrypted.Disconnect();
mOnWaitingForKey.Disconnect();
mOnDecodeWarning.Disconnect();
mOnNextFrameStatus.Disconnect();
mOnSecondaryVideoContainerInstalled.Disconnect();
mOnStoreDecoderBenchmark.Disconnect();
}
RefPtr<ShutdownPromise> MediaDecoder::ShutdownStateMachine() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(GetStateMachine());
DisconnectEvents();
return mDecoderStateMachine->BeginShutdown();
}
void MediaDecoder::Play() {
MOZ_ASSERT(NS_IsMainThread());

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

@ -135,9 +135,6 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
// the seek target.
void Seek(double aTime, SeekTarget::Type aSeekType);
// Initialize state machine and schedule it.
nsresult InitializeStateMachine();
// Start playback of a video. 'Load' must have previously been
// called.
virtual void Play();
@ -405,8 +402,21 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
virtual void FirstFrameLoaded(UniquePtr<MediaInfo> aInfo,
MediaDecoderEventVisibility aEventVisibility);
// Return error if fail to init the state machine.
nsresult CreateAndInitStateMachine(bool aIsLiveStream,
bool aDisableExternalEngine = false);
// Always return a state machine. If the decoder supports using external
// engine, `aDisableExternalEngine` can disable the external engine if needed.
virtual MediaDecoderStateMachineBase* CreateStateMachine(
bool aDisableExternalEngine) MOZ_NONNULL_RETURN = 0;
void SetStateMachineParameters();
// Disconnect any events before shutting down the state machine.
void DisconnectEvents();
RefPtr<ShutdownPromise> ShutdownStateMachine();
// Called when MediaDecoder shutdown is finished. Subclasses use this to clean
// up internal structures, and unregister potential shutdown blockers when
// they're done.

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

@ -131,7 +131,8 @@ HLSDecoder::~HLSDecoder() {
HLS_DEBUG("HLSDecoder", "~HLSDecoder(): allocated=%zu", sAllocatedInstances);
}
MediaDecoderStateMachineBase* HLSDecoder::CreateStateMachine() {
MediaDecoderStateMachineBase* HLSDecoder::CreateStateMachine(
bool aDisableExternalEngine) {
MOZ_ASSERT(NS_IsMainThread());
MediaFormatReaderInit init;
@ -178,13 +179,7 @@ nsresult HLSDecoder::Load(nsIChannel* aChannel) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SetStateMachine(CreateStateMachine());
NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
GetStateMachine()->DispatchIsLiveStream(false);
return InitializeStateMachine();
return CreateAndInitStateMachine(false);
}
void HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes) {

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

@ -52,7 +52,8 @@ class HLSDecoder final : public MediaDecoder {
explicit HLSDecoder(MediaDecoderInit& aInit);
~HLSDecoder();
MediaDecoderStateMachineBase* CreateStateMachine();
MediaDecoderStateMachineBase* CreateStateMachine(
bool aDisableExternalEngine) override;
bool CanPlayThroughImpl() final {
// TODO: We don't know how to estimate 'canplaythrough' for this decoder.

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

@ -202,7 +202,8 @@ void MFMediaEngineParent::NotifyError(MF_MEDIA_ENGINE_ERR aError,
return;
}
case MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED: {
MediaResult error(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Source not supported");
MediaResult error(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR,
"Source not supported");
Unused << SendNotifyError(error);
return;
}

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

@ -35,9 +35,15 @@ MediaSourceDecoder::MediaSourceDecoder(MediaDecoderInit& aInit)
mExplicitDuration.emplace(UnspecifiedNaN<double>());
}
MediaDecoderStateMachineBase* MediaSourceDecoder::CreateStateMachine() {
MediaDecoderStateMachineBase* MediaSourceDecoder::CreateStateMachine(
bool aDisableExternalEngine) {
MOZ_ASSERT(NS_IsMainThread());
mDemuxer = new MediaSourceDemuxer(AbstractMainThread());
// if `mDemuxer` already exists, that means we're in the process of recreating
// the state machine. The track buffers are tied to the demuxer so we would
// need to reuse it.
if (!mDemuxer) {
mDemuxer = new MediaSourceDemuxer(AbstractMainThread());
}
MediaFormatReaderInit init;
init.mVideoFrameContainer = GetVideoFrameContainer();
init.mKnowsCompositor = GetCompositor();
@ -48,7 +54,8 @@ MediaDecoderStateMachineBase* MediaSourceDecoder::CreateStateMachine() {
#ifdef MOZ_WMF
// TODO : Only for testing development for now. In the future this should be
// used for encrypted content only.
if (StaticPrefs::media_wmf_media_engine_enabled()) {
if (StaticPrefs::media_wmf_media_engine_enabled() &&
!aDisableExternalEngine) {
return new ExternalEngineStateMachine(this, mReader);
}
#endif
@ -65,15 +72,7 @@ nsresult MediaSourceDecoder::Load(nsIPrincipal* aPrincipal) {
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
SetStateMachine(CreateStateMachine());
if (!GetStateMachine()) {
NS_WARNING("Failed to create state machine!");
return NS_ERROR_FAILURE;
}
GetStateMachine()->DispatchIsLiveStream(!mEnded);
return InitializeStateMachine();
return CreateAndInitStateMachine(!mEnded);
}
media::TimeIntervals MediaSourceDecoder::GetSeekable() {

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

@ -73,7 +73,8 @@ class MediaSourceDecoder : public MediaDecoder,
void NotifyDataArrived();
private:
MediaDecoderStateMachineBase* CreateStateMachine();
MediaDecoderStateMachineBase* CreateStateMachine(
bool aDisableExternalEngine) override;
void DoSetMediaSourceDuration(double aDuration);
media::TimeInterval ClampIntervalToEnd(const media::TimeInterval& aInterval);
bool CanPlayThroughImpl() override;

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

@ -254,6 +254,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
TrackInfo::TrackType aType,
TrackBuffersManager* aManager)
: mParent(aParent),
mTaskQueue(mParent->GetTaskQueue()),
mType(aType),
mMonitor("MediaSourceTrackDemuxer"),
mManager(aManager),
@ -271,7 +272,10 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
// So we always seek 2112 frames
? (2112 * 1000000ULL /
mParent->GetTrackInfo(mType)->GetAsAudioInfo()->mRate)
: 0)) {}
: 0)) {
MOZ_ASSERT(mParent);
MOZ_ASSERT(mTaskQueue);
}
UniquePtr<TrackInfo> MediaSourceTrackDemuxer::GetInfo() const {
return mParent->GetTrackInfo(mType)->Clone();

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

@ -123,12 +123,7 @@ class MediaSourceTrackDemuxer
void DetachManager();
private:
bool OnTaskQueue() const {
MOZ_ASSERT(mParent);
auto taskQueue = mParent->GetTaskQueue();
MOZ_ASSERT(taskQueue);
return taskQueue->IsCurrentThreadIn();
}
bool OnTaskQueue() const { return mTaskQueue->IsCurrentThreadIn(); }
RefPtr<SeekPromise> DoSeek(const media::TimeUnit& aTime);
RefPtr<SamplesPromise> DoGetSamples(int32_t aNumSamples);
@ -139,6 +134,8 @@ class MediaSourceTrackDemuxer
media::TimeUnit GetNextRandomAccessPoint();
RefPtr<MediaSourceDemuxer> mParent;
const RefPtr<TaskQueue> mTaskQueue;
TrackInfo::TrackType mType;
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor MOZ_UNANNOTATED;

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

@ -1170,7 +1170,7 @@ with modules["DOM_MEDIA"]:
# Internal platform-related errors
errors["NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR"] = FAILURE(101)
errors["NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR"] = FAILURE(102)
# =======================================================================
# 42: NS_ERROR_MODULE_URL_CLASSIFIER