Bug 1759033 - no longer start or shutdown MediaFoundation explictly, and ensure the media foundation's life cycle long enough. r=bryce

According to [1], `MFShutdown` will shutdown the media foundation for every other call of `MFStartup`, which means it's possible to shutdown the media foundation even if other components are still using that.

Therefore, we should consider make the media foundation alive when the first time any component wants to use it, and shutdown it when the whole process destroys.

We already did similar thing on the RDD process [2] so it makes sense to do it as well on other places. Especially considering we will move the MFT decoder into the RDD process in the future, we definitely don't want an encoder incorrectly shutdown the media foundation the decoder is using.

Also, it saves time to call `MFStartup` if the media foundation is already started.

[1] https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mfshutdown
[2] https://searchfox.org/mozilla-central/rev/eeeba8183d3268e0d563c2becf9f4adc21a37368/dom/media/ipc/RDDParent.cpp#111,303,315

Differential Revision: https://phabricator.services.mozilla.com/D140757
This commit is contained in:
alwu 2022-03-24 18:56:16 +00:00
parent 8b56e95458
commit 68dd7e07df
8 changed files with 87 additions and 50 deletions

View File

@ -108,7 +108,10 @@ bool RDDParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
gfxVars::Initialize();
#ifdef XP_WIN
DeviceManagerDx::Init();
wmf::MFStartup();
auto rv = wmf::MediaFoundationInitializer::HasInitialized();
if (!rv) {
NS_WARNING("Failed to init Media Foundation in the RDD process");
}
#endif
mozilla::ipc::SetThisProcessName("RDD Process");
@ -298,9 +301,6 @@ void RDDParent::ActorDestroy(ActorDestroyReason aWhy) {
[](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
#ifndef NS_FREE_PERMANENT_DATA
# ifdef XP_WIN
wmf::MFShutdown();
# endif
// No point in going through XPCOM shutdown because we don't keep persistent
// state.
ProcessChild::QuickExit();
@ -310,10 +310,6 @@ void RDDParent::ActorDestroy(ActorDestroyReason aWhy) {
mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/)
->Then(GetCurrentSerialEventTarget(), __func__, [&]() {
#ifdef XP_WIN
wmf::MFShutdown();
#endif
#if defined(XP_WIN)
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->DisableFull();

View File

@ -170,7 +170,7 @@ nsCString MFTEncoder::GetFriendlyName(const GUID& aSubtype) {
nsTArray<MFTEncoder::Info> MFTEncoder::Enumerate() {
nsTArray<Info> infos;
if (FAILED(wmf::MFStartup())) {
if (!wmf::MediaFoundationInitializer::HasInitialized()) {
MFT_ENC_SLOGE("cannot init Media Foundation");
return infos;
}
@ -179,7 +179,6 @@ nsTArray<MFTEncoder::Info> MFTEncoder::Enumerate() {
PopulateHWEncoderInfo(MFVideoFormat_VP90, infos);
PopulateHWEncoderInfo(MFVideoFormat_VP80, infos);
wmf::MFShutdown();
return infos;
}

View File

@ -22,6 +22,11 @@
#include <wmcodecdsp.h>
#include <codecapi.h>
#include "mozilla/Atomics.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticMutex.h"
#include "nsThreadUtils.h"
// The Windows headers helpfully declare min and max macros, which don't
// compile in the presence of std::min and std::max and unified builds.
// So undef them here.
@ -35,19 +40,76 @@
namespace mozilla {
namespace wmf {
// If successful, loads all required WMF DLLs and calls the WMF MFStartup()
// function. This delegates the WMF MFStartup() call to the MTA thread if
// the current thread is not MTA. This is to ensure we always interact with
// WMF from threads with the same COM compartment model.
HRESULT MFStartup();
// A helper class for automatically starting and shuting down the Media
// Foundation. Prior to using Media Foundation in a process, users should call
// MediaFoundationInitializer::HasInitialized() to ensure Media Foundation is
// initialized. Users should also check the result of this call, in case the
// internal call to MFStartup fails. The first check to HasInitialized will
// cause the helper to start up Media Foundation and set up a runnable to handle
// Media Foundation shutdown at XPCOM shutdown. Calls after the first will not
// cause any extra startups or shutdowns, so it's safe to check multiple times
// in the same process. Users do not need to do any manual shutdown, the helper
// will handle this internally.
class MediaFoundationInitializer final {
public:
~MediaFoundationInitializer() {
if (mHasInitialized) {
if (FAILED(MFShutdown())) {
NS_WARNING("MFShutdown failed");
}
}
}
static bool HasInitialized() {
if (sIsShutdown) {
return false;
}
return Get()->mHasInitialized;
}
private:
static MediaFoundationInitializer* Get() {
{
StaticMutexAutoLock lock(sCreateMutex);
if (!sInitializer) {
sInitializer.reset(new MediaFoundationInitializer());
GetMainThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction("MediaFoundationInitializer::Get", [&] {
// Need to run this before MTA thread gets destroyed.
RunOnShutdown([&] {
sInitializer.reset();
sIsShutdown = true;
}, ShutdownPhase::XPCOMShutdown);
}));
}
}
return sInitializer.get();
}
// Calls the WMF MFShutdown() function. Call this once for every time
// wmf::MFStartup() succeeds. Note: does not unload the WMF DLLs loaded by
// MFStartup(); leaves them in memory to save I/O at next MFStartup() call.
// This delegates the WMF MFShutdown() call to the MTA thread if the current
// thread is not MTA. This is to ensure we always interact with
// WMF from threads with the same COM compartment model.
HRESULT MFShutdown();
MediaFoundationInitializer()
: mHasInitialized(SUCCEEDED(MFStartup())) {
if (!mHasInitialized) {
NS_WARNING("MFStartup failed");
}
}
// If successful, loads all required WMF DLLs and calls the WMF MFStartup()
// function. This delegates the WMF MFStartup() call to the MTA thread if
// the current thread is not MTA. This is to ensure we always interact with
// WMF from threads with the same COM compartment model.
HRESULT MFStartup();
// Calls the WMF MFShutdown() function. Call this once for every time
// wmf::MFStartup() succeeds. Note: does not unload the WMF DLLs loaded by
// MFStartup(); leaves them in memory to save I/O at next MFStartup() call.
// This delegates the WMF MFShutdown() call to the MTA thread if the current
// thread is not MTA. This is to ensure we always interact with
// WMF from threads with the same COM compartment model.
HRESULT MFShutdown();
static inline UniquePtr<MediaFoundationInitializer> sInitializer;
static inline StaticMutex sCreateMutex;
static inline Atomic<bool> sIsShutdown{false};
const bool mHasInitialized;
};
// All functions below are wrappers around the corresponding WMF function,
// and automatically locate and call the corresponding function in the WMF DLLs.

View File

@ -61,13 +61,6 @@ already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() {
return wmf.forget();
}
WMFDecoderModule::~WMFDecoderModule() {
if (mWMFInitialized) {
DebugOnly<HRESULT> hr = wmf::MFShutdown();
NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
}
}
static bool IsRemoteAcceleratedCompositor(
layers::KnowsCompositor* aKnowsCompositor) {
if (!aKnowsCompositor) {
@ -105,13 +98,12 @@ static bool CanCreateMFTDecoder(const GUID& aCategory, const GUID& aInSubtype) {
// is not.
bool canCreateDecoder = false;
mozilla::mscom::EnsureMTA([&]() -> void {
if (FAILED(wmf::MFStartup())) {
if (!wmf::MediaFoundationInitializer::HasInitialized()) {
return;
}
RefPtr<MFTDecoder> decoder(new MFTDecoder());
canCreateDecoder =
SUCCEEDED(decoder->Create(aCategory, aInSubtype, outSubtype));
wmf::MFShutdown();
});
return canCreateDecoder;
}
@ -185,8 +177,8 @@ int WMFDecoderModule::GetNumDecoderThreads() {
}
nsresult WMFDecoderModule::Startup() {
mWMFInitialized = SUCCEEDED(wmf::MFStartup());
return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE;
return wmf::MediaFoundationInitializer::HasInitialized() ? NS_OK
: NS_ERROR_FAILURE;
}
already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateVideoDecoder(

View File

@ -49,9 +49,7 @@ class WMFDecoderModule : public PlatformDecoderModule {
private:
WMFDecoderModule() = default;
virtual ~WMFDecoderModule();
bool mWMFInitialized = false;
virtual ~WMFDecoderModule() = default;
};
} // namespace mozilla

View File

@ -40,12 +40,11 @@ static const GUID CodecToSubtype(MediaDataEncoder::CodecType aCodec) {
bool CanCreateWMFEncoder(MediaDataEncoder::CodecType aCodec) {
bool canCreate = false;
mscom::EnsureMTA([&]() {
if (FAILED(wmf::MFStartup())) {
if (!wmf::MediaFoundationInitializer::HasInitialized()) {
return;
}
RefPtr<MFTEncoder> enc(new MFTEncoder());
canCreate = SUCCEEDED(enc->Create(CodecToSubtype(aCodec)));
wmf::MFShutdown();
});
return canCreate;
}
@ -70,7 +69,7 @@ RefPtr<MediaDataEncoder::InitPromise> WMFMediaDataEncoder<T>::ProcessInit() {
MOZ_ASSERT(!mEncoder,
"Should not initialize encoder again without shutting down");
if (FAILED(wmf::MFStartup())) {
if (!wmf::MediaFoundationInitializer::HasInitialized()) {
return InitPromise::CreateAndReject(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("Can't create the MFT encoder.")),
@ -82,7 +81,6 @@ RefPtr<MediaDataEncoder::InitPromise> WMFMediaDataEncoder<T>::ProcessInit() {
mscom::EnsureMTA([&]() { hr = InitMFTEncoder(encoder); });
if (FAILED(hr)) {
wmf::MFShutdown();
WMF_ENC_LOGE("init MFTEncoder: error = 0x%X", hr);
return InitPromise::CreateAndReject(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
@ -437,7 +435,6 @@ RefPtr<ShutdownPromise> WMFMediaDataEncoder<T>::Shutdown() {
if (self->mEncoder) {
self->mEncoder->Destroy();
self->mEncoder = nullptr;
wmf::MFShutdown();
}
return ShutdownPromise::CreateAndResolve(true, __func__);
});

View File

@ -263,7 +263,7 @@ LoadDLLs() {
typedef HRESULT(STDMETHODCALLTYPE* FunctionName##Ptr_t)(__VA_ARGS__)
HRESULT
MFStartup() {
MediaFoundationInitializer::MFStartup() {
if (IsWin7AndPre2000Compatible()) {
/*
* Specific exclude the usage of WMF on Win 7 with compatibility mode
@ -292,7 +292,7 @@ MFStartup() {
}
HRESULT
MFShutdown() {
MediaFoundationInitializer::MFShutdown() {
ENSURE_FUNCTION_PTR(MFShutdown, Mfplat.dll)
HRESULT hr = E_FAIL;
mozilla::mscom::EnsureMTA([&]() -> void { hr = (MFShutdownPtr)(); });

View File

@ -651,9 +651,6 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
[](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
#ifndef NS_FREE_PERMANENT_DATA
# ifdef XP_WIN
wmf::MFShutdown();
# endif
// No point in going through XPCOM shutdown because we don't keep persistent
// state.
ProcessChild::QuickExit();
@ -662,10 +659,6 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
// Wait until all RemoteDecoderManagerParent have closed.
mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/)
->Then(GetCurrentSerialEventTarget(), __func__, [this]() {
#ifdef XP_WIN
wmf::MFShutdown();
#endif
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;