зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c925033c81
Коммит
2a20f58d80
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче