249 lines
8.6 KiB
C++
249 lines
8.6 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "UtilityMediaServiceChild.h"
|
|
|
|
#include "base/basictypes.h"
|
|
#include "mozilla/AppShutdown.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
|
|
#ifdef MOZ_WMF_MEDIA_ENGINE
|
|
# include "mozilla/StaticPrefs_media.h"
|
|
# include "mozilla/gfx/GPUProcessManager.h"
|
|
# include "mozilla/ipc/UtilityProcessManager.h"
|
|
# include "mozilla/layers/PVideoBridge.h"
|
|
# include "mozilla/layers/VideoBridgeParent.h"
|
|
# include "mozilla/layers/VideoBridgeUtils.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_WMF_CDM
|
|
# include "mozilla/dom/Promise.h"
|
|
# include "mozilla/EMEUtils.h"
|
|
# include "mozilla/PMFCDM.h"
|
|
#endif
|
|
|
|
namespace mozilla::ipc {
|
|
|
|
NS_IMETHODIMP UtilityMediaServiceChildShutdownObserver::Observe(
|
|
nsISupports* aSubject, const char* aTopic, const char16_t* aData) {
|
|
MOZ_ASSERT(strcmp(aTopic, "ipc:utility-shutdown") == 0);
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->RemoveObserver(this, "ipc:utility-shutdown");
|
|
}
|
|
|
|
UtilityMediaServiceChild::Shutdown(mSandbox);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(UtilityMediaServiceChildShutdownObserver, nsIObserver);
|
|
|
|
static EnumeratedArray<SandboxingKind, StaticRefPtr<UtilityMediaServiceChild>,
|
|
size_t(SandboxingKind::COUNT)>
|
|
sAudioDecoderChilds;
|
|
|
|
UtilityMediaServiceChild::UtilityMediaServiceChild(SandboxingKind aKind)
|
|
: mSandbox(aKind), mAudioDecoderChildStart(TimeStamp::Now()) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
|
|
if (observerService) {
|
|
auto* obs = new UtilityMediaServiceChildShutdownObserver(aKind);
|
|
observerService->AddObserver(obs, "ipc:utility-shutdown", false);
|
|
}
|
|
}
|
|
|
|
nsresult UtilityMediaServiceChild::BindToUtilityProcess(
|
|
RefPtr<UtilityProcessParent> aUtilityParent) {
|
|
Endpoint<PUtilityMediaServiceChild> utilityMediaServiceChildEnd;
|
|
Endpoint<PUtilityMediaServiceParent> utilityMediaServiceParentEnd;
|
|
nsresult rv = PUtilityMediaService::CreateEndpoints(
|
|
aUtilityParent->OtherEndpointProcInfo(), EndpointProcInfo::Current(),
|
|
&utilityMediaServiceParentEnd, &utilityMediaServiceChildEnd);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
MOZ_ASSERT(false, "Protocol endpoints failure");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsTArray<gfx::GfxVarUpdate> updates = gfx::gfxVars::FetchNonDefaultVars();
|
|
if (!aUtilityParent->SendStartUtilityMediaService(
|
|
std::move(utilityMediaServiceParentEnd), std::move(updates))) {
|
|
MOZ_ASSERT(false, "StartUtilityMediaService service failure");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
Bind(std::move(utilityMediaServiceChildEnd));
|
|
|
|
PROFILER_MARKER_UNTYPED("UtilityMediaServiceChild::BindToUtilityProcess", IPC,
|
|
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(
|
|
mAudioDecoderChildStart)));
|
|
return NS_OK;
|
|
}
|
|
|
|
void UtilityMediaServiceChild::ActorDestroy(ActorDestroyReason aReason) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
gfx::gfxVars::RemoveReceiver(this);
|
|
#ifdef MOZ_WMF_MEDIA_ENGINE
|
|
mHasCreatedVideoBridge = State::None;
|
|
if (auto* gpm = gfx::GPUProcessManager::Get()) {
|
|
// Note: the manager could have shutdown already.
|
|
gpm->RemoveListener(this);
|
|
}
|
|
#endif
|
|
Shutdown(mSandbox);
|
|
}
|
|
|
|
void UtilityMediaServiceChild::Bind(
|
|
Endpoint<PUtilityMediaServiceChild>&& aEndpoint) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (NS_WARN_IF(!aEndpoint.Bind(this))) {
|
|
MOZ_ASSERT_UNREACHABLE("Failed to bind UtilityMediaServiceChild!");
|
|
return;
|
|
}
|
|
gfx::gfxVars::AddReceiver(this);
|
|
}
|
|
|
|
/* static */
|
|
void UtilityMediaServiceChild::Shutdown(SandboxingKind aKind) {
|
|
sAudioDecoderChilds[aKind] = nullptr;
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<UtilityMediaServiceChild> UtilityMediaServiceChild::GetSingleton(
|
|
SandboxingKind aKind) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
bool shutdown = AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown);
|
|
if (!sAudioDecoderChilds[aKind] && !shutdown) {
|
|
sAudioDecoderChilds[aKind] = new UtilityMediaServiceChild(aKind);
|
|
}
|
|
return sAudioDecoderChilds[aKind];
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
UtilityMediaServiceChild::RecvUpdateMediaCodecsSupported(
|
|
const RemoteMediaIn& aLocation,
|
|
const media::MediaCodecsSupported& aSupported) {
|
|
dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(aLocation,
|
|
aSupported);
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UtilityMediaServiceChild::OnVarChanged(
|
|
const nsTArray<gfx::GfxVarUpdate>& aVar) {
|
|
SendUpdateVar(aVar);
|
|
}
|
|
|
|
#ifdef MOZ_WMF_MEDIA_ENGINE
|
|
mozilla::ipc::IPCResult
|
|
UtilityMediaServiceChild::RecvCompleteCreatedVideoBridge() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM);
|
|
if (mHasCreatedVideoBridge == State::Creating) {
|
|
mHasCreatedVideoBridge = State::Created;
|
|
} else {
|
|
MOZ_ASSERT_UNREACHABLE("Video bridge created but was not creating?");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UtilityMediaServiceChild::OnCompositorUnexpectedShutdown() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM);
|
|
mHasCreatedVideoBridge = State::None;
|
|
|
|
if (auto* gpm = gfx::GPUProcessManager::Get()) {
|
|
if (auto utilpm = UtilityProcessManager::GetSingleton())
|
|
if (auto parent = utilpm->GetProcessParent(mSandbox)) {
|
|
if (NS_SUCCEEDED(gpm->CreateUtilityMFCDMVideoBridge(
|
|
this, parent->OtherEndpointProcInfo()))) {
|
|
mHasCreatedVideoBridge = State::Creating;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UtilityMediaServiceChild::CreateVideoBridge(
|
|
mozilla::ipc::EndpointProcInfo aOtherProcess) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM);
|
|
|
|
// Creating or already created, avoiding reinit a bridge.
|
|
if (mHasCreatedVideoBridge != State::None) {
|
|
return true;
|
|
}
|
|
|
|
auto* gpm = gfx::GPUProcessManager::Get();
|
|
if (NS_WARN_IF(!gpm) || NS_WARN_IF(NS_FAILED(gpm->EnsureGPUReady())) ||
|
|
NS_WARN_IF(
|
|
NS_FAILED(gpm->CreateUtilityMFCDMVideoBridge(this, aOtherProcess)))) {
|
|
return false;
|
|
}
|
|
|
|
gpm->AddListener(this);
|
|
mHasCreatedVideoBridge = State::Creating;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_WMF_CDM
|
|
void UtilityMediaServiceChild::GetKeySystemCapabilities(
|
|
dom::Promise* aPromise) {
|
|
EME_LOG("Ask capabilities for all supported CDMs");
|
|
SendGetKeySystemCapabilities()->Then(
|
|
NS_GetCurrentThread(), __func__,
|
|
[promise = RefPtr<dom::Promise>(aPromise)](
|
|
CopyableTArray<MFCDMCapabilitiesIPDL>&& result) {
|
|
FallibleTArray<dom::CDMInformation> cdmInfo;
|
|
for (const auto& capabilities : result) {
|
|
EME_LOG("Received capabilities for %s",
|
|
NS_ConvertUTF16toUTF8(capabilities.keySystem()).get());
|
|
for (const auto& v : capabilities.videoCapabilities()) {
|
|
for (const auto& scheme : v.encryptionSchemes()) {
|
|
EME_LOG(" capabilities: video=%s, scheme=%s",
|
|
NS_ConvertUTF16toUTF8(v.contentType()).get(),
|
|
EnumValueToString(scheme));
|
|
}
|
|
}
|
|
for (const auto& a : capabilities.audioCapabilities()) {
|
|
for (const auto& scheme : a.encryptionSchemes()) {
|
|
EME_LOG(" capabilities: audio=%s, scheme=%s",
|
|
NS_ConvertUTF16toUTF8(a.contentType()).get(),
|
|
EnumValueToString(scheme));
|
|
}
|
|
}
|
|
auto* info = cdmInfo.AppendElement(fallible);
|
|
if (!info) {
|
|
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
info->mKeySystemName = capabilities.keySystem();
|
|
|
|
KeySystemConfig config;
|
|
MFCDMCapabilitiesIPDLToKeySystemConfig(capabilities, config);
|
|
info->mCapabilities = config.GetDebugInfo();
|
|
info->mClearlead =
|
|
DoesKeySystemSupportClearLead(info->mKeySystemName);
|
|
info->mIsHardwareDecryption = capabilities.isHardwareDecryption();
|
|
}
|
|
promise->MaybeResolve(cdmInfo);
|
|
},
|
|
[promise = RefPtr<dom::Promise>(aPromise)](
|
|
const mozilla::ipc::ResponseRejectReason& aReason) {
|
|
EME_LOG("IPC failure for GetKeySystemCapabilities!");
|
|
promise->MaybeReject(NS_ERROR_DOM_MEDIA_CDM_ERR);
|
|
});
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UtilityMediaServiceChild::RecvDisableHardwareDRM() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
static constexpr const char* kHardDRMPref = "media.eme.hwdrm.failed";
|
|
Preferences::SetBool(kHardDRMPref, true);
|
|
return IPC_OK();
|
|
}
|
|
#endif
|
|
|
|
} // namespace mozilla::ipc
|