summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/MFCDMParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc/MFCDMParent.cpp')
-rw-r--r--dom/media/ipc/MFCDMParent.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/dom/media/ipc/MFCDMParent.cpp b/dom/media/ipc/MFCDMParent.cpp
new file mode 100644
index 0000000000..26dce82bc5
--- /dev/null
+++ b/dom/media/ipc/MFCDMParent.cpp
@@ -0,0 +1,632 @@
+/* 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 "MFCDMParent.h"
+
+#include <mfmediaengine.h>
+#define INITGUID // Enable DEFINE_PROPERTYKEY()
+#include <propkeydef.h> // For DEFINE_PROPERTYKEY() definition
+#include <propvarutil.h> // For InitPropVariantFrom*()
+
+#include "mozilla/dom/KeySystemNames.h"
+#include "mozilla/EMEUtils.h"
+#include "mozilla/KeySystemConfig.h"
+#include "MFCDMProxy.h"
+#include "RemoteDecodeUtils.h" // For GetCurrentSandboxingKind()
+#include "SpecialSystemDirectory.h" // For temp dir
+
+using Microsoft::WRL::ComPtr;
+using Microsoft::WRL::MakeAndInitialize;
+
+namespace mozilla {
+
+// See
+// https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_util.cc;l=26-40;drc=503535015a7b373cc6185c69c991e01fda5da571
+#ifndef EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID
+DEFINE_PROPERTYKEY(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, 0x1218a3e2, 0xcfb0,
+ 0x4c98, 0x90, 0xe5, 0x5f, 0x58, 0x18, 0xd4, 0xb6, 0x7e,
+ PID_FIRST_USABLE);
+#endif
+
+#define MFCDM_PARENT_LOG(msg, ...) \
+ EME_LOG("MFCDMParent[%p, Id=%" PRIu64 "]@%s: " msg, this, this->mId, \
+ __func__, ##__VA_ARGS__)
+#define MFCDM_PARENT_SLOG(msg, ...) \
+ EME_LOG("MFCDMParent@%s: " msg, __func__, ##__VA_ARGS__)
+
+#define MFCDM_RETURN_IF_FAILED(x) \
+ do { \
+ HRESULT rv = x; \
+ if (MOZ_UNLIKELY(FAILED(rv))) { \
+ MFCDM_PARENT_SLOG("(" #x ") failed, rv=%lx", rv); \
+ return rv; \
+ } \
+ } while (false)
+
+#define MFCDM_REJECT_IF(pred, rv) \
+ do { \
+ if (MOZ_UNLIKELY(pred)) { \
+ MFCDM_PARENT_LOG("reject for [" #pred "], rv=%x", rv); \
+ aResolver(rv); \
+ return IPC_OK(); \
+ } \
+ } while (false)
+
+#define MFCDM_REJECT_IF_FAILED(op, rv) \
+ do { \
+ HRESULT hr = op; \
+ if (MOZ_UNLIKELY(FAILED(hr))) { \
+ MFCDM_PARENT_LOG("(" #op ") failed(hr=%lx), rv=%x", hr, rv); \
+ aResolver(rv); \
+ return IPC_OK(); \
+ } \
+ } while (false)
+
+void MFCDMParent::Register() {
+ MOZ_ASSERT(!sRegisteredCDMs.Contains(this->mId));
+ sRegisteredCDMs.InsertOrUpdate(this->mId, this);
+ MFCDM_PARENT_LOG("Registered!");
+}
+
+void MFCDMParent::Unregister() {
+ MOZ_ASSERT(sRegisteredCDMs.Contains(this->mId));
+ sRegisteredCDMs.Remove(this->mId);
+ MFCDM_PARENT_LOG("Unregistered!");
+}
+
+MFCDMParent::MFCDMParent(const nsAString& aKeySystem,
+ RemoteDecoderManagerParent* aManager,
+ nsISerialEventTarget* aManagerThread)
+ : mKeySystem(aKeySystem),
+ mManager(aManager),
+ mManagerThread(aManagerThread),
+ mId(sNextId++),
+ mKeyMessageEvents(aManagerThread),
+ mKeyChangeEvents(aManagerThread),
+ mExpirationEvents(aManagerThread) {
+ // TODO: check Widevine too when it's ready.
+ MOZ_ASSERT(IsPlayReadyKeySystemAndSupported(aKeySystem));
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aManagerThread);
+ MOZ_ASSERT(XRE_IsUtilityProcess());
+ MOZ_ASSERT(GetCurrentSandboxingKind() ==
+ ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM);
+
+ mIPDLSelfRef = this;
+ LoadFactory();
+ Register();
+
+ mKeyMessageListener = mKeyMessageEvents.Connect(
+ mManagerThread, this, &MFCDMParent::SendOnSessionKeyMessage);
+ mKeyChangeListener = mKeyChangeEvents.Connect(
+ mManagerThread, this, &MFCDMParent::SendOnSessionKeyStatusesChanged);
+ mExpirationListener = mExpirationEvents.Connect(
+ mManagerThread, this, &MFCDMParent::SendOnSessionKeyExpiration);
+}
+
+void MFCDMParent::Destroy() {
+ AssertOnManagerThread();
+ mKeyMessageEvents.DisconnectAll();
+ mKeyChangeEvents.DisconnectAll();
+ mExpirationEvents.DisconnectAll();
+ mKeyMessageListener.DisconnectIfExists();
+ mKeyChangeListener.DisconnectIfExists();
+ mExpirationListener.DisconnectIfExists();
+ mIPDLSelfRef = nullptr;
+}
+
+HRESULT MFCDMParent::LoadFactory() {
+ ComPtr<IMFMediaEngineClassFactory4> clsFactory;
+ MFCDM_RETURN_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory,
+ nullptr, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&clsFactory)));
+
+ ComPtr<IMFContentDecryptionModuleFactory> cdmFactory;
+ MFCDM_RETURN_IF_FAILED(clsFactory->CreateContentDecryptionModuleFactory(
+ mKeySystem.get(), IID_PPV_ARGS(&cdmFactory)));
+
+ mFactory.Swap(cdmFactory);
+ return S_OK;
+}
+
+// Use IMFContentDecryptionModuleFactory::IsTypeSupported() to get DRM
+// capabilities. It appears to be the same as
+// Windows.Media.Protection.ProtectionCapabilities.IsTypeSupported(). See
+// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-19041
+static bool FactorySupports(
+ ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
+ const nsString& aKeySystem,
+ const KeySystemConfig::EMECodecString& aVideoCodec,
+ const KeySystemConfig::EMECodecString& aAudioCodec =
+ KeySystemConfig::EMECodecString(""),
+ const nsString& aAdditionalFeature = nsString(u"")) {
+ // MP4 is the only container supported.
+ nsString mimeType(u"video/mp4;codecs=\"");
+ MOZ_ASSERT(!aVideoCodec.IsEmpty());
+ mimeType.AppendASCII(aVideoCodec);
+ if (!aAudioCodec.IsEmpty()) {
+ mimeType.AppendLiteral(u",");
+ mimeType.AppendASCII(aAudioCodec);
+ }
+ // These features are required to call IsTypeSupported(). We only care about
+ // codec and encryption scheme so hardcode the rest.
+ mimeType.AppendLiteral(
+ u"\";features=\"decode-bpp=8,"
+ "decode-res-x=1920,decode-res-y=1080,"
+ "decode-bitrate=10000000,decode-fps=30,");
+ if (!aAdditionalFeature.IsEmpty()) {
+ MOZ_ASSERT(aAdditionalFeature.Last() == u',');
+ mimeType.Append(aAdditionalFeature);
+ }
+ // TODO: add HW robustness setting too.
+ mimeType.AppendLiteral(u"encryption-robustness=SW_SECURE_DECODE\"");
+ return aFactory->IsTypeSupported(aKeySystem.get(), mimeType.get());
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvGetCapabilities(
+ GetCapabilitiesResolver&& aResolver) {
+ MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+
+ MFCDMCapabilitiesIPDL capabilities;
+
+ // TODO : check HW CDM creation
+ // TODO : add HEVC support?
+ static nsTArray<KeySystemConfig::EMECodecString> kVideoCodecs({
+ KeySystemConfig::EME_CODEC_H264,
+ KeySystemConfig::EME_CODEC_VP8,
+ KeySystemConfig::EME_CODEC_VP9,
+ });
+ // Remember supported video codecs.
+ // It will be used when collecting audio codec and encryption scheme
+ // support.
+ nsTArray<KeySystemConfig::EMECodecString> supportedVideoCodecs;
+ for (auto& codec : kVideoCodecs) {
+ if (FactorySupports(mFactory, mKeySystem, codec)) {
+ MFCDMMediaCapability* c =
+ capabilities.videoCapabilities().AppendElement();
+ c->contentType() = NS_ConvertUTF8toUTF16(codec);
+ MFCDM_PARENT_LOG("%s: +video:%s", __func__, codec.get());
+ supportedVideoCodecs.AppendElement(codec);
+ }
+ }
+ if (supportedVideoCodecs.IsEmpty()) {
+ // Return empty capabilities when no video codec is supported.
+ aResolver(std::move(capabilities));
+ return IPC_OK();
+ }
+
+ static nsTArray<KeySystemConfig::EMECodecString> kAudioCodecs({
+ KeySystemConfig::EME_CODEC_AAC,
+ KeySystemConfig::EME_CODEC_FLAC,
+ KeySystemConfig::EME_CODEC_OPUS,
+ KeySystemConfig::EME_CODEC_VORBIS,
+ });
+ for (auto& codec : kAudioCodecs) {
+ if (FactorySupports(mFactory, mKeySystem, supportedVideoCodecs[0], codec)) {
+ MFCDMMediaCapability* c =
+ capabilities.audioCapabilities().AppendElement();
+ c->contentType() = NS_ConvertUTF8toUTF16(codec);
+ MFCDM_PARENT_LOG("%s: +audio:%s", __func__, codec.get());
+ }
+ }
+
+ // Collect schemes supported by all video codecs.
+ static nsTArray<std::pair<CryptoScheme, nsDependentString>> kSchemes = {
+ std::pair<CryptoScheme, nsDependentString>(
+ CryptoScheme::Cenc, u"encryption-type=cenc,encryption-iv-size=8,"),
+ std::pair<CryptoScheme, nsDependentString>(
+ CryptoScheme::Cbcs, u"encryption-type=cbcs,encryption-iv-size=16,")};
+ for (auto& scheme : kSchemes) {
+ bool ok = true;
+ for (auto& codec : supportedVideoCodecs) {
+ ok &= FactorySupports(mFactory, mKeySystem, codec,
+ KeySystemConfig::EMECodecString(""),
+ scheme.second /* additional feature */);
+ if (!ok) {
+ break;
+ }
+ }
+ if (ok) {
+ capabilities.encryptionSchemes().AppendElement(scheme.first);
+ MFCDM_PARENT_LOG("%s: +scheme:%s", __func__,
+ scheme.first == CryptoScheme::Cenc ? "cenc" : "cbcs");
+ }
+ }
+
+ // TODO: don't hardcode
+ capabilities.initDataTypes().AppendElement(u"keyids");
+ capabilities.initDataTypes().AppendElement(u"cenc");
+ capabilities.sessionTypes().AppendElement(
+ KeySystemConfig::SessionType::Temporary);
+ capabilities.sessionTypes().AppendElement(
+ KeySystemConfig::SessionType::PersistentLicense);
+ // WMF CDMs usually require these. See
+ // https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/media_foundation_cdm_factory.cc;l=69-73;drc=b3ca5c09fa0aa07b7f9921501f75e43d80f3ba48
+ capabilities.persistentState() = KeySystemConfig::Requirement::Required;
+ capabilities.distinctiveID() = KeySystemConfig::Requirement::Required;
+
+ capabilities.keySystem() = mKeySystem;
+
+ aResolver(std::move(capabilities));
+ return IPC_OK();
+}
+
+// RAIIized PROPVARIANT. See
+// third_party/libwebrtc/modules/audio_device/win/core_audio_utility_win.h
+class AutoPropVar {
+ public:
+ AutoPropVar() { PropVariantInit(&mVar); }
+
+ ~AutoPropVar() { Reset(); }
+
+ AutoPropVar(const AutoPropVar&) = delete;
+ AutoPropVar& operator=(const AutoPropVar&) = delete;
+ bool operator==(const AutoPropVar&) const = delete;
+ bool operator!=(const AutoPropVar&) const = delete;
+
+ // Returns a pointer to the underlying PROPVARIANT for use as an out param
+ // in a function call.
+ PROPVARIANT* Receive() {
+ MOZ_ASSERT(mVar.vt == VT_EMPTY);
+ return &mVar;
+ }
+
+ // Clears the instance to prepare it for re-use (e.g., via Receive).
+ void Reset() {
+ if (mVar.vt != VT_EMPTY) {
+ HRESULT hr = PropVariantClear(&mVar);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ Unused << hr;
+ }
+ }
+
+ const PROPVARIANT& get() const { return mVar; }
+ const PROPVARIANT* ptr() const { return &mVar; }
+
+ private:
+ PROPVARIANT mVar;
+};
+
+static MF_MEDIAKEYS_REQUIREMENT ToMFRequirement(
+ const KeySystemConfig::Requirement aRequirement) {
+ switch (aRequirement) {
+ case KeySystemConfig::Requirement::NotAllowed:
+ return MF_MEDIAKEYS_REQUIREMENT_NOT_ALLOWED;
+ case KeySystemConfig::Requirement::Optional:
+ return MF_MEDIAKEYS_REQUIREMENT_OPTIONAL;
+ case KeySystemConfig::Requirement::Required:
+ return MF_MEDIAKEYS_REQUIREMENT_REQUIRED;
+ }
+};
+
+static inline LPCWSTR InitDataTypeToString(const nsAString& aInitDataType) {
+ // The strings are defined in https://www.w3.org/TR/eme-initdata-registry/
+ if (aInitDataType.EqualsLiteral("webm")) {
+ return L"webm";
+ } else if (aInitDataType.EqualsLiteral("cenc")) {
+ return L"cenc";
+ } else if (aInitDataType.EqualsLiteral("keyids")) {
+ return L"keyids";
+ } else {
+ return L"unknown";
+ }
+}
+
+static HRESULT BuildCDMAccessConfig(const MFCDMInitParamsIPDL& aParams,
+ ComPtr<IPropertyStore>& aConfig) {
+ ComPtr<IPropertyStore> mksc; // EME MediaKeySystemConfiguration
+ MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&mksc)));
+
+ // If we don't set `MF_EME_INITDATATYPES` then we won't be able to create
+ // CDM module on Windows 10, which is not documented officially.
+ BSTR* initDataTypeArray =
+ (BSTR*)CoTaskMemAlloc(sizeof(BSTR) * aParams.initDataTypes().Length());
+ for (size_t i = 0; i < aParams.initDataTypes().Length(); i++) {
+ initDataTypeArray[i] =
+ SysAllocString(InitDataTypeToString(aParams.initDataTypes()[i]));
+ }
+ AutoPropVar initDataTypes;
+ PROPVARIANT* var = initDataTypes.Receive();
+ var->vt = VT_VECTOR | VT_BSTR;
+ var->cabstr.cElems = static_cast<ULONG>(aParams.initDataTypes().Length());
+ var->cabstr.pElems = initDataTypeArray;
+ MFCDM_RETURN_IF_FAILED(
+ mksc->SetValue(MF_EME_INITDATATYPES, initDataTypes.get()));
+
+ // Empty 'audioCapabilities'.
+ AutoPropVar audioCapabilities;
+ var = audioCapabilities.Receive();
+ var->vt = VT_VARIANT | VT_VECTOR;
+ var->capropvar.cElems = 0;
+ MFCDM_RETURN_IF_FAILED(
+ mksc->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities.get()));
+
+ // 'videoCapabilites'.
+ ComPtr<IPropertyStore> mksmc; // EME MediaKeySystemMediaCapability
+ MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&mksmc)));
+ if (aParams.hwSecure()) {
+ AutoPropVar robustness;
+ var = robustness.Receive();
+ var->vt = VT_BSTR;
+ var->bstrVal = SysAllocString(L"HW_SECURE_ALL");
+ MFCDM_RETURN_IF_FAILED(
+ mksmc->SetValue(MF_EME_ROBUSTNESS, robustness.get()));
+ }
+ // Store mksmc in a PROPVARIANT.
+ AutoPropVar videoCapability;
+ var = videoCapability.Receive();
+ var->vt = VT_UNKNOWN;
+ var->punkVal = mksmc.Detach();
+ // Insert element.
+ AutoPropVar videoCapabilities;
+ var = videoCapabilities.Receive();
+ var->vt = VT_VARIANT | VT_VECTOR;
+ var->capropvar.cElems = 1;
+ var->capropvar.pElems =
+ reinterpret_cast<PROPVARIANT*>(CoTaskMemAlloc(sizeof(PROPVARIANT)));
+ PropVariantCopy(var->capropvar.pElems, videoCapability.ptr());
+ MFCDM_RETURN_IF_FAILED(
+ mksc->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities.get()));
+
+ AutoPropVar persistState;
+ InitPropVariantFromUInt32(ToMFRequirement(aParams.persistentState()),
+ persistState.Receive());
+ MFCDM_RETURN_IF_FAILED(
+ mksc->SetValue(MF_EME_PERSISTEDSTATE, persistState.get()));
+
+ AutoPropVar distinctiveID;
+ InitPropVariantFromUInt32(ToMFRequirement(aParams.distinctiveID()),
+ distinctiveID.Receive());
+ MFCDM_RETURN_IF_FAILED(
+ mksc->SetValue(MF_EME_DISTINCTIVEID, distinctiveID.get()));
+
+ aConfig.Swap(mksc);
+ return S_OK;
+}
+
+static HRESULT BuildCDMProperties(const nsString& aOrigin,
+ ComPtr<IPropertyStore>& aProps) {
+ MOZ_ASSERT(!aOrigin.IsEmpty());
+
+ ComPtr<IPropertyStore> props;
+ MFCDM_RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&props)));
+
+ AutoPropVar origin;
+ MFCDM_RETURN_IF_FAILED(
+ InitPropVariantFromString(aOrigin.get(), origin.Receive()));
+ MFCDM_RETURN_IF_FAILED(
+ props->SetValue(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, origin.get()));
+
+ // TODO: support client token?
+
+ // TODO: CDM store path per profile?
+ nsCOMPtr<nsIFile> dir;
+ if (NS_FAILED(GetSpecialSystemDirectory(OS_TemporaryDirectory,
+ getter_AddRefs(dir)))) {
+ return E_ACCESSDENIED;
+ }
+ if (NS_FAILED(dir->AppendNative(nsDependentCString("mfcdm")))) {
+ return E_ACCESSDENIED;
+ }
+ nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_FAILED(rv)) {
+ return E_ACCESSDENIED;
+ }
+ nsAutoString cdmStorePath;
+ if (NS_FAILED(dir->GetPath(cdmStorePath))) {
+ return E_ACCESSDENIED;
+ }
+
+ AutoPropVar path;
+ MFCDM_RETURN_IF_FAILED(
+ InitPropVariantFromString(cdmStorePath.get(), path.Receive()));
+ MFCDM_RETURN_IF_FAILED(
+ props->SetValue(MF_CONTENTDECRYPTIONMODULE_STOREPATH, path.get()));
+
+ aProps.Swap(props);
+ return S_OK;
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvInit(
+ const MFCDMInitParamsIPDL& aParams, InitResolver&& aResolver) {
+ static auto RequirementToStr = [](KeySystemConfig::Requirement aRequirement) {
+ switch (aRequirement) {
+ case KeySystemConfig::Requirement::Required:
+ return "Required";
+ case KeySystemConfig::Requirement::Optional:
+ return "Optional";
+ default:
+ return "NotAllowed";
+ }
+ };
+
+ MFCDM_PARENT_LOG(
+ "Creating a CDM (key-system=%s, origin=%s, distinctiveID=%s, "
+ "persistentState=%s, "
+ "hwSecure=%d)",
+ NS_ConvertUTF16toUTF8(mKeySystem).get(),
+ NS_ConvertUTF16toUTF8(aParams.origin()).get(),
+ RequirementToStr(aParams.distinctiveID()),
+ RequirementToStr(aParams.persistentState()), aParams.hwSecure());
+ MOZ_ASSERT(mFactory->IsTypeSupported(mKeySystem.get(), nullptr));
+
+ // Get access object to CDM.
+ ComPtr<IPropertyStore> accessConfig;
+ MFCDM_REJECT_IF_FAILED(BuildCDMAccessConfig(aParams, accessConfig),
+ NS_ERROR_FAILURE);
+
+ AutoTArray<IPropertyStore*, 1> configs = {accessConfig.Get()};
+ ComPtr<IMFContentDecryptionModuleAccess> cdmAccess;
+ MFCDM_REJECT_IF_FAILED(
+ mFactory->CreateContentDecryptionModuleAccess(
+ mKeySystem.get(), configs.Elements(), configs.Length(), &cdmAccess),
+ NS_ERROR_FAILURE);
+ // Get CDM.
+ ComPtr<IPropertyStore> cdmProps;
+ MFCDM_REJECT_IF_FAILED(BuildCDMProperties(aParams.origin(), cdmProps),
+ NS_ERROR_FAILURE);
+ ComPtr<IMFContentDecryptionModule> cdm;
+ MFCDM_REJECT_IF_FAILED(
+ cdmAccess->CreateContentDecryptionModule(cdmProps.Get(), &cdm),
+ NS_ERROR_FAILURE);
+
+ mCDM.Swap(cdm);
+ MFCDM_PARENT_LOG("Created a CDM!");
+
+ // TODO : for Widevine CDM, would we still need to do following steps?
+ ComPtr<IMFPMPHost> pmpHost;
+ ComPtr<IMFGetService> cdmService;
+ MFCDM_REJECT_IF_FAILED(mCDM.As(&cdmService), NS_ERROR_FAILURE);
+ MFCDM_REJECT_IF_FAILED(
+ cdmService->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE,
+ IID_PPV_ARGS(&pmpHost)),
+ NS_ERROR_FAILURE);
+ MFCDM_REJECT_IF_FAILED(
+ SUCCEEDED(MakeAndInitialize<MFPMPHostWrapper>(&mPMPHostWrapper, pmpHost)),
+ NS_ERROR_FAILURE);
+ MFCDM_REJECT_IF_FAILED(mCDM->SetPMPHostApp(mPMPHostWrapper.Get()),
+ NS_ERROR_FAILURE);
+ MFCDM_PARENT_LOG("Set PMPHostWrapper on CDM!");
+ aResolver(MFCDMInitIPDL{mId});
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvCreateSessionAndGenerateRequest(
+ const MFCDMCreateSessionParamsIPDL& aParams,
+ CreateSessionAndGenerateRequestResolver&& aResolver) {
+ MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
+
+ static auto SessionTypeToStr = [](KeySystemConfig::SessionType aSessionType) {
+ switch (aSessionType) {
+ case KeySystemConfig::SessionType::Temporary:
+ return "temporary";
+ case KeySystemConfig::SessionType::PersistentLicense:
+ return "persistent-license";
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Unsupported license type!");
+ return "invalid";
+ }
+ }
+ };
+ MFCDM_PARENT_LOG("Creating session for type '%s'",
+ SessionTypeToStr(aParams.sessionType()));
+ UniquePtr<MFCDMSession> session{
+ MFCDMSession::Create(aParams.sessionType(), mCDM.Get(), mManagerThread)};
+ if (!session) {
+ MFCDM_PARENT_LOG("Failed to create CDM session");
+ aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
+ return IPC_OK();
+ }
+
+ MFCDM_REJECT_IF_FAILED(session->GenerateRequest(aParams.initDataType(),
+ aParams.initData().Elements(),
+ aParams.initData().Length()),
+ NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
+ ConnectSessionEvents(session.get());
+
+ // TODO : now we assume all session ID is available after session is
+ // created, but this is not always true. Need to remove this assertion and
+ // handle cases where session Id is not available yet.
+ const auto& sessionId = session->SessionID();
+ MOZ_ASSERT(sessionId);
+ mSessions.emplace(*sessionId, std::move(session));
+ MFCDM_PARENT_LOG("Created a CDM session!");
+ aResolver(*sessionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvLoadSession(
+ const KeySystemConfig::SessionType& aSessionType,
+ const nsString& aSessionId, LoadSessionResolver&& aResolver) {
+ MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
+
+ nsresult rv = NS_OK;
+ auto* session = GetSession(aSessionId);
+ if (!session) {
+ aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
+ return IPC_OK();
+ }
+ MFCDM_REJECT_IF_FAILED(session->Load(aSessionId),
+ NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvUpdateSession(
+ const nsString& aSessionId, const CopyableTArray<uint8_t>& aResponse,
+ UpdateSessionResolver&& aResolver) {
+ MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
+ nsresult rv = NS_OK;
+ auto* session = GetSession(aSessionId);
+ if (!session) {
+ aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
+ return IPC_OK();
+ }
+ MFCDM_REJECT_IF_FAILED(session->Update(aResponse),
+ NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvCloseSession(
+ const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
+ MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
+ nsresult rv = NS_OK;
+ auto* session = GetSession(aSessionId);
+ if (!session) {
+ aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
+ return IPC_OK();
+ }
+ MFCDM_REJECT_IF_FAILED(session->Close(),
+ NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMParent::RecvRemoveSession(
+ const nsString& aSessionId, UpdateSessionResolver&& aResolver) {
+ MOZ_ASSERT(mCDM, "RecvInit() must be called and waited on before this call");
+ nsresult rv = NS_OK;
+ auto* session = GetSession(aSessionId);
+ if (!session) {
+ aResolver(NS_ERROR_DOM_MEDIA_CDM_NO_SESSION_ERR);
+ return IPC_OK();
+ }
+ MFCDM_REJECT_IF_FAILED(session->Remove(),
+ NS_ERROR_DOM_MEDIA_CDM_SESSION_OPERATION_ERR);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+void MFCDMParent::ConnectSessionEvents(MFCDMSession* aSession) {
+ // TODO : clear session's event source when the session gets removed.
+ mKeyMessageEvents.Forward(aSession->KeyMessageEvent());
+ mKeyChangeEvents.Forward(aSession->KeyChangeEvent());
+ mExpirationEvents.Forward(aSession->ExpirationEvent());
+}
+
+MFCDMSession* MFCDMParent::GetSession(const nsString& aSessionId) {
+ AssertOnManagerThread();
+ auto iter = mSessions.find(aSessionId);
+ if (iter == mSessions.end()) {
+ return nullptr;
+ }
+ return iter->second.get();
+}
+
+already_AddRefed<MFCDMProxy> MFCDMParent::GetMFCDMProxy() {
+ if (!mCDM) {
+ return nullptr;
+ }
+ RefPtr<MFCDMProxy> proxy = new MFCDMProxy(mCDM.Get());
+ return proxy.forget();
+}
+
+#undef MFCDM_REJECT_IF_FAILED
+#undef MFCDM_REJECT_IF
+#undef MFCDM_RETURN_IF_FAILED
+#undef MFCDM_PARENT_SLOG
+#undef MFCDM_PARENT_LOG
+
+} // namespace mozilla