summaryrefslogtreecommitdiffstats
path: root/dom/media/eme/mediafoundation
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMImpl.cpp128
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMImpl.h100
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMProxy.cpp306
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMProxy.h134
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMProxyCallback.cpp72
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMProxyCallback.h38
-rw-r--r--dom/media/eme/mediafoundation/moz.build21
7 files changed, 799 insertions, 0 deletions
diff --git a/dom/media/eme/mediafoundation/WMFCDMImpl.cpp b/dom/media/eme/mediafoundation/WMFCDMImpl.cpp
new file mode 100644
index 0000000000..c2ca31bf15
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMImpl.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "WMFCDMImpl.h"
+
+#include "mozilla/dom/MediaKeySession.h"
+
+namespace mozilla {
+
+/* static */
+bool WMFCDMImpl::Supports(const nsAString& aKeySystem) {
+ static std::map<nsString, bool> supports;
+
+ nsString key(aKeySystem);
+ if (const auto& s = supports.find(key); s != supports.end()) {
+ return s->second;
+ }
+
+ RefPtr<WMFCDMImpl> cdm = MakeRefPtr<WMFCDMImpl>(aKeySystem);
+ KeySystemConfig c;
+ bool s = cdm->GetCapabilities(c);
+ supports[key] = s;
+
+ return s;
+}
+
+static void MFCDMCapabilitiesIPDLToKeySystemConfig(
+ const MFCDMCapabilitiesIPDL& aCDMConfig,
+ KeySystemConfig& aKeySystemConfig) {
+ aKeySystemConfig.mKeySystem = aCDMConfig.keySystem();
+
+ for (const auto& type : aCDMConfig.initDataTypes()) {
+ aKeySystemConfig.mInitDataTypes.AppendElement(type);
+ }
+
+ for (const auto& type : aCDMConfig.sessionTypes()) {
+ aKeySystemConfig.mSessionTypes.AppendElement(type);
+ }
+
+ for (const auto& c : aCDMConfig.videoCapabilities()) {
+ if (!c.robustness().IsEmpty() &&
+ !aKeySystemConfig.mVideoRobustness.Contains(c.robustness())) {
+ aKeySystemConfig.mVideoRobustness.AppendElement(c.robustness());
+ }
+ aKeySystemConfig.mMP4.SetCanDecryptAndDecode(
+ NS_ConvertUTF16toUTF8(c.contentType()));
+ }
+ for (const auto& c : aCDMConfig.audioCapabilities()) {
+ if (!c.robustness().IsEmpty() &&
+ !aKeySystemConfig.mAudioRobustness.Contains(c.robustness())) {
+ aKeySystemConfig.mAudioRobustness.AppendElement(c.robustness());
+ }
+ aKeySystemConfig.mMP4.SetCanDecryptAndDecode(
+ NS_ConvertUTF16toUTF8(c.contentType()));
+ }
+ aKeySystemConfig.mPersistentState = aCDMConfig.persistentState();
+ aKeySystemConfig.mDistinctiveIdentifier = aCDMConfig.distinctiveID();
+}
+
+static const char* EncryptionSchemeStr(const CryptoScheme aScheme) {
+ switch (aScheme) {
+ case CryptoScheme::None:
+ return "none";
+ case CryptoScheme::Cenc:
+ return "cenc";
+ case CryptoScheme::Cbcs:
+ return "cbcs";
+ }
+}
+
+bool WMFCDMImpl::GetCapabilities(KeySystemConfig& aConfig) {
+ nsCOMPtr<nsISerialEventTarget> backgroundTaskQueue;
+ NS_CreateBackgroundTaskQueue(__func__, getter_AddRefs(backgroundTaskQueue));
+
+ bool ok = false;
+ media::Await(
+ backgroundTaskQueue.forget(), mCDM->GetCapabilities(),
+ [&ok, &aConfig](const MFCDMCapabilitiesIPDL& capabilities) {
+ EME_LOG("capabilities: keySystem=%s",
+ NS_ConvertUTF16toUTF8(capabilities.keySystem()).get());
+ for (const auto& v : capabilities.videoCapabilities()) {
+ EME_LOG("capabilities: video=%s",
+ NS_ConvertUTF16toUTF8(v.contentType()).get());
+ }
+ for (const auto& a : capabilities.audioCapabilities()) {
+ EME_LOG("capabilities: audio=%s",
+ NS_ConvertUTF16toUTF8(a.contentType()).get());
+ }
+ for (const auto& v : capabilities.encryptionSchemes()) {
+ EME_LOG("capabilities: encryptionScheme=%s", EncryptionSchemeStr(v));
+ }
+ MFCDMCapabilitiesIPDLToKeySystemConfig(capabilities, aConfig);
+ ok = true;
+ },
+ [](nsresult rv) {
+ EME_LOG("Fail to get key system capabilities. rv=%x", rv);
+ });
+ return ok;
+}
+
+RefPtr<WMFCDMImpl::InitPromise> WMFCDMImpl::Init(
+ const WMFCDMImpl::InitParams& aParams) {
+ MOZ_ASSERT(mCDM);
+
+ RefPtr<WMFCDMImpl> self = this;
+ mCDM->Init(aParams.mOrigin, aParams.mInitDataTypes,
+ aParams.mPersistentStateRequired
+ ? KeySystemConfig::Requirement::Required
+ : KeySystemConfig::Requirement::Optional,
+ aParams.mDistinctiveIdentifierRequired
+ ? KeySystemConfig::Requirement::Required
+ : KeySystemConfig::Requirement::Optional,
+ aParams.mHWSecure, aParams.mProxyCallback)
+ ->Then(
+ mCDM->ManagerThread(), __func__,
+ [self, this](const MFCDMInitIPDL& init) {
+ mInitPromiseHolder.ResolveIfExists(true, __func__);
+ },
+ [self, this](const nsresult rv) {
+ mInitPromiseHolder.RejectIfExists(rv, __func__);
+ });
+ return mInitPromiseHolder.Ensure(__func__);
+}
+
+} // namespace mozilla
diff --git a/dom/media/eme/mediafoundation/WMFCDMImpl.h b/dom/media/eme/mediafoundation/WMFCDMImpl.h
new file mode 100644
index 0000000000..fc4ef840d6
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMImpl.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMIMPL_H_
+#define DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMIMPL_H_
+
+#include "MediaData.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/EMEUtils.h"
+#include "mozilla/KeySystemConfig.h"
+#include "mozilla/media/MediaUtils.h"
+#include "mozilla/MFCDMChild.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+class WMFCDMProxyCallback;
+
+/**
+ * WMFCDMImpl is a helper class for MFCDM protocol clients. It creates, manages,
+ * and calls MFCDMChild object in the content process on behalf of the client,
+ * and performs conversion between EME and MFCDM types and constants.
+ */
+class WMFCDMImpl final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WMFCDMImpl);
+
+ explicit WMFCDMImpl(const nsAString& aKeySystem)
+ : mCDM(MakeRefPtr<MFCDMChild>(aKeySystem)) {}
+
+ static bool Supports(const nsAString& aKeySystem);
+ // TODO: make this async?
+ bool GetCapabilities(KeySystemConfig& aConfig);
+
+ using InitPromise = GenericPromise;
+ struct InitParams {
+ nsString mOrigin;
+ CopyableTArray<nsString> mInitDataTypes;
+ bool mPersistentStateRequired;
+ bool mDistinctiveIdentifierRequired;
+ bool mHWSecure;
+ WMFCDMProxyCallback* mProxyCallback;
+ };
+
+ RefPtr<InitPromise> Init(const InitParams& aParams);
+
+ RefPtr<MFCDMChild::SessionPromise> CreateSession(
+ uint32_t aPromiseId, const KeySystemConfig::SessionType aSessionType,
+ const nsAString& aInitDataType, const nsTArray<uint8_t>& aInitData) {
+ return mCDM->CreateSessionAndGenerateRequest(aPromiseId, aSessionType,
+ aInitDataType, aInitData);
+ }
+
+ RefPtr<GenericPromise> LoadSession(
+ uint32_t aPromiseId, const KeySystemConfig::SessionType aSessionType,
+ const nsAString& aSessionId) {
+ return mCDM->LoadSession(aPromiseId, aSessionType, aSessionId);
+ }
+
+ RefPtr<GenericPromise> UpdateSession(uint32_t aPromiseId,
+ const nsAString& aSessionId,
+ nsTArray<uint8_t>& aResponse) {
+ return mCDM->UpdateSession(aPromiseId, aSessionId, aResponse);
+ }
+
+ RefPtr<GenericPromise> CloseSession(uint32_t aPromiseId,
+ const nsAString& aSessionId) {
+ return mCDM->CloseSession(aPromiseId, aSessionId);
+ }
+
+ RefPtr<GenericPromise> RemoveSession(uint32_t aPromiseId,
+ const nsAString& aSessionId) {
+ return mCDM->RemoveSession(aPromiseId, aSessionId);
+ }
+
+ uint64_t Id() {
+ MOZ_ASSERT(mCDM->Id() != 0,
+ "Should be called only after Init() is resolved");
+ return mCDM->Id();
+ }
+
+ private:
+ ~WMFCDMImpl() {
+ if (mCDM) {
+ mCDM->Shutdown();
+ }
+ };
+
+ const RefPtr<MFCDMChild> mCDM;
+
+ MozPromiseHolder<InitPromise> mInitPromiseHolder;
+};
+
+} // namespace mozilla
+
+#endif // DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMIMPL_H_
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxy.cpp b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp
new file mode 100644
index 0000000000..1d691168d8
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "WMFCDMProxy.h"
+
+#include "mozilla/dom/MediaKeySession.h"
+#include "mozilla/WMFCDMProxyCallback.h"
+#include "WMFCDMImpl.h"
+#include "WMFCDMProxyCallback.h"
+
+namespace mozilla {
+
+#define LOG(msg, ...) \
+ EME_LOG("WMFCDMProxy[%p]@%s: " msg, this, __func__, ##__VA_ARGS__)
+
+#define RETURN_IF_SHUTDOWN() \
+ do { \
+ MOZ_ASSERT(NS_IsMainThread()); \
+ if (mIsShutdown) { \
+ return; \
+ } \
+ } while (false)
+
+#define PERFORM_ON_CDM(operation, promiseId, ...) \
+ do { \
+ mCDM->operation(promiseId, __VA_ARGS__) \
+ ->Then( \
+ mMainThread, __func__, \
+ [self = RefPtr{this}, this, promiseId]() { \
+ RETURN_IF_SHUTDOWN(); \
+ if (mKeys.IsNull()) { \
+ EME_LOG("WMFCDMProxy(this=%p, pid=%" PRIu32 \
+ ") : abort the " #operation " due to empty key", \
+ this, promiseId); \
+ return; \
+ } \
+ ResolvePromise(promiseId); \
+ }, \
+ [self = RefPtr{this}, this, promiseId]() { \
+ RETURN_IF_SHUTDOWN(); \
+ RejectPromiseWithStateError( \
+ promiseId, nsLiteralCString("WMFCDMProxy::" #operation ": " \
+ "failed to " #operation)); \
+ }); \
+ } while (false)
+
+WMFCDMProxy::WMFCDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem,
+ const dom::MediaKeySystemConfiguration& aConfig)
+ : CDMProxy(
+ aKeys, aKeySystem,
+ aConfig.mDistinctiveIdentifier == dom::MediaKeysRequirement::Required,
+ aConfig.mPersistentState == dom::MediaKeysRequirement::Required),
+ mConfig(aConfig) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+WMFCDMProxy::~WMFCDMProxy() {}
+
+void WMFCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ const nsAString& aName) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!aOrigin.IsEmpty());
+
+ MOZ_ASSERT(!mOwnerThread);
+ if (NS_FAILED(
+ NS_NewNamedThread("WMFCDMThread", getter_AddRefs(mOwnerThread)))) {
+ RejectPromiseWithStateError(
+ aPromiseId,
+ nsLiteralCString("WMFCDMProxy::Init: couldn't create CDM thread"));
+ return;
+ }
+
+ mCDM = MakeRefPtr<WMFCDMImpl>(mKeySystem);
+ mProxyCallback = new WMFCDMProxyCallback(this);
+ WMFCDMImpl::InitParams params{nsString(aOrigin),
+ mConfig.mInitDataTypes,
+ mPersistentStateRequired,
+ mDistinctiveIdentifierRequired,
+ false /* TODO : support HW secure */,
+ mProxyCallback};
+ mCDM->Init(params)->Then(
+ mMainThread, __func__,
+ [self = RefPtr{this}, this, aPromiseId](const bool) {
+ MOZ_ASSERT(mCDM->Id() > 0);
+ mKeys->OnCDMCreated(aPromiseId, mCDM->Id());
+ },
+ [self = RefPtr{this}, this, aPromiseId](const nsresult rv) {
+ RejectPromiseWithStateError(
+ aPromiseId,
+ nsLiteralCString("WMFCDMProxy::Init: WMFCDM init error"));
+ });
+}
+
+void WMFCDMProxy::ResolvePromise(PromiseId aId) {
+ auto resolve = [self = RefPtr{this}, this, aId]() {
+ RETURN_IF_SHUTDOWN();
+ EME_LOG("WMFCDMProxy::ResolvePromise(this=%p, pid=%" PRIu32 ")", this, aId);
+ if (!mKeys.IsNull()) {
+ mKeys->ResolvePromise(aId);
+ } else {
+ NS_WARNING("WMFCDMProxy unable to resolve promise!");
+ }
+ };
+
+ if (NS_IsMainThread()) {
+ resolve();
+ return;
+ }
+ mMainThread->Dispatch(
+ NS_NewRunnableFunction("WMFCDMProxy::ResolvePromise", resolve));
+}
+
+void WMFCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
+ const nsCString& aReason) {
+ if (!NS_IsMainThread()) {
+ // Use CopyableErrorResult to store our exception in the runnable,
+ // because ErrorResult is not OK to move across threads.
+ mMainThread->Dispatch(
+ NewRunnableMethod<PromiseId, StoreCopyPassByRRef<CopyableErrorResult>,
+ nsCString>("WMFCDMProxy::RejectPromise", this,
+ &WMFCDMProxy::RejectPromiseOnMainThread,
+ aId, std::move(aException), aReason),
+ NS_DISPATCH_NORMAL);
+ return;
+ }
+ EME_LOG("WMFCDMProxy::RejectPromise(this=%p, pid=%" PRIu32
+ ", code=0x%x, "
+ "reason='%s')",
+ this, aId, aException.ErrorCodeAsInt(), aReason.get());
+ if (!mKeys.IsNull()) {
+ mKeys->RejectPromise(aId, std::move(aException), aReason);
+ } else {
+ // We don't have a MediaKeys object to pass the exception to, so silence
+ // the exception to avoid it asserting due to being unused.
+ aException.SuppressException();
+ }
+}
+
+void WMFCDMProxy::RejectPromiseOnMainThread(PromiseId aId,
+ CopyableErrorResult&& aException,
+ const nsCString& aReason) {
+ RETURN_IF_SHUTDOWN();
+ // Moving into or out of a non-copyable ErrorResult will assert that both
+ // ErorResults are from our current thread. Avoid the assertion by moving
+ // into a current-thread CopyableErrorResult first. Note that this is safe,
+ // because CopyableErrorResult never holds state that can't move across
+ // threads.
+ CopyableErrorResult rv(std::move(aException));
+ RejectPromise(aId, std::move(rv), aReason);
+}
+
+void WMFCDMProxy::RejectPromiseWithStateError(PromiseId aId,
+ const nsCString& aReason) {
+ ErrorResult rv;
+ rv.ThrowInvalidStateError(aReason);
+ RejectPromise(aId, std::move(rv), aReason);
+}
+
+void WMFCDMProxy::CreateSession(uint32_t aCreateSessionToken,
+ MediaKeySessionType aSessionType,
+ PromiseId aPromiseId,
+ const nsAString& aInitDataType,
+ nsTArray<uint8_t>& aInitData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ const auto sessionType = ConvertToKeySystemConfigSessionType(aSessionType);
+ EME_LOG("WMFCDMProxy::CreateSession(this=%p, pid=%" PRIu32
+ "), sessionType=%s",
+ this, aPromiseId, SessionTypeToStr(sessionType));
+ mCDM->CreateSession(aPromiseId, sessionType, aInitDataType, aInitData)
+ ->Then(
+ mMainThread, __func__,
+ [self = RefPtr{this}, this, aCreateSessionToken,
+ aPromiseId](nsString sessionID) {
+ RETURN_IF_SHUTDOWN();
+ if (mKeys.IsNull()) {
+ EME_LOG("WMFCDMProxy(this=%p, pid=%" PRIu32
+ ") : abort the create session due to "
+ "empty key",
+ this, aPromiseId);
+ return;
+ }
+ if (RefPtr<dom::MediaKeySession> session =
+ mKeys->GetPendingSession(aCreateSessionToken)) {
+ session->SetSessionId(std::move(sessionID));
+ }
+ ResolvePromise(aPromiseId);
+ },
+ [self = RefPtr{this}, this, aPromiseId]() {
+ RETURN_IF_SHUTDOWN();
+ RejectPromiseWithStateError(
+ aPromiseId,
+ nsLiteralCString(
+ "WMFCDMProxy::CreateSession: cannot create session"));
+ });
+}
+
+void WMFCDMProxy::LoadSession(PromiseId aPromiseId,
+ dom::MediaKeySessionType aSessionType,
+ const nsAString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ const auto sessionType = ConvertToKeySystemConfigSessionType(aSessionType);
+ EME_LOG("WMFCDMProxy::LoadSession(this=%p, pid=%" PRIu32
+ "), sessionType=%s, sessionId=%s",
+ this, aPromiseId, SessionTypeToStr(sessionType),
+ NS_ConvertUTF16toUTF8(aSessionId).get());
+ PERFORM_ON_CDM(LoadSession, aPromiseId, sessionType, aSessionId);
+}
+
+void WMFCDMProxy::UpdateSession(const nsAString& aSessionId,
+ PromiseId aPromiseId,
+ nsTArray<uint8_t>& aResponse) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ EME_LOG("WMFCDMProxy::UpdateSession(this=%p, pid=%" PRIu32
+ "), sessionId=%s, responseLen=%zu",
+ this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get(),
+ aResponse.Length());
+ PERFORM_ON_CDM(UpdateSession, aPromiseId, aSessionId, aResponse);
+}
+
+void WMFCDMProxy::CloseSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ EME_LOG("WMFCDMProxy::CloseSession(this=%p, pid=%" PRIu32 "), sessionId=%s",
+ this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get());
+ PERFORM_ON_CDM(CloseSession, aPromiseId, aSessionId);
+}
+
+void WMFCDMProxy::RemoveSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ EME_LOG("WMFCDMProxy::RemoveSession(this=%p, pid=%" PRIu32 "), sessionId=%s",
+ this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get());
+ PERFORM_ON_CDM(RemoveSession, aPromiseId, aSessionId);
+}
+
+void WMFCDMProxy::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mIsShutdown);
+ if (mProxyCallback) {
+ mProxyCallback->Shutdown();
+ mProxyCallback = nullptr;
+ }
+ mIsShutdown = true;
+}
+
+void WMFCDMProxy::OnSessionMessage(const nsAString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ if (mKeys.IsNull()) {
+ return;
+ }
+ if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) {
+ LOG("Notify key message for session Id=%s",
+ NS_ConvertUTF16toUTF8(aSessionId).get());
+ session->DispatchKeyMessage(aMessageType, aMessage);
+ }
+}
+
+void WMFCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ if (mKeys.IsNull()) {
+ return;
+ }
+ if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) {
+ LOG("Notify key statuses for session Id=%s",
+ NS_ConvertUTF16toUTF8(aSessionId).get());
+ session->DispatchKeyStatusesChange();
+ }
+}
+
+void WMFCDMProxy::OnExpirationChange(const nsAString& aSessionId,
+ UnixTime aExpiryTime) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RETURN_IF_SHUTDOWN();
+ if (mKeys.IsNull()) {
+ return;
+ }
+ if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) {
+ LOG("Notify expiration for session Id=%s",
+ NS_ConvertUTF16toUTF8(aSessionId).get());
+ session->SetExpiration(static_cast<double>(aExpiryTime));
+ }
+}
+
+uint64_t WMFCDMProxy::GetCDMProxyId() const {
+ MOZ_DIAGNOSTIC_ASSERT(mCDM);
+ return mCDM->Id();
+}
+
+#undef LOG
+#undef RETURN_IF_SHUTDOWN
+#undef PERFORM_ON_CDM
+
+} // namespace mozilla
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxy.h b/dom/media/eme/mediafoundation/WMFCDMProxy.h
new file mode 100644
index 0000000000..7766b73f21
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMProxy.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXY_H_
+#define DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXY_H_
+
+#include "mozilla/CDMProxy.h"
+#include "mozilla/CDMCaps.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WMFCDMImpl.h"
+
+#include "nsString.h"
+
+namespace mozilla {
+
+class WMFCDMProxyCallback;
+
+class WMFCDMProxy : public CDMProxy {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WMFCDMProxy, override)
+
+ WMFCDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem,
+ const dom::MediaKeySystemConfiguration& aConfig);
+
+ // CDMProxy interface
+ void Init(PromiseId aPromiseId, const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin, const nsAString& aName) override;
+
+ void CreateSession(uint32_t aCreateSessionToken,
+ MediaKeySessionType aSessionType, PromiseId aPromiseId,
+ const nsAString& aInitDataType,
+ nsTArray<uint8_t>& aInitData) override;
+
+ void LoadSession(PromiseId aPromiseId, dom::MediaKeySessionType aSessionType,
+ const nsAString& aSessionId) override;
+
+ void SetServerCertificate(PromiseId aPromiseId,
+ nsTArray<uint8_t>& aCert) override {}
+
+ void UpdateSession(const nsAString& aSessionId, PromiseId aPromiseId,
+ nsTArray<uint8_t>& aResponse) override;
+
+ void CloseSession(const nsAString& aSessionId, PromiseId aPromiseId) override;
+
+ void RemoveSession(const nsAString& aSessionId,
+ PromiseId aPromiseId) override;
+
+ void QueryOutputProtectionStatus() override {}
+
+ void NotifyOutputProtectionStatus(
+ OutputProtectionCheckStatus aCheckStatus,
+ OutputProtectionCaptureStatus aCaptureStatus) override {}
+
+ void Shutdown() override;
+
+ void Terminated() override {}
+
+ void OnSetSessionId(uint32_t aCreateSessionToken,
+ const nsAString& aSessionId) override {}
+
+ void OnResolveLoadSessionPromise(uint32_t aPromiseId,
+ bool aSuccess) override {}
+
+ void OnSessionMessage(const nsAString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ const nsTArray<uint8_t>& aMessage) override;
+
+ void OnExpirationChange(const nsAString& aSessionId,
+ UnixTime aExpiryTime) override;
+
+ void OnSessionClosed(const nsAString& aSessionId) override {}
+
+ void OnSessionError(const nsAString& aSessionId, nsresult aException,
+ uint32_t aSystemCode, const nsAString& aMsg) override {}
+
+ void OnRejectPromise(uint32_t aPromiseId, ErrorResult&& aException,
+ const nsCString& aMsg) override {}
+
+ RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample) override {
+ return nullptr;
+ }
+ void OnDecrypted(uint32_t aId, DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData) override {}
+
+ void ResolvePromise(PromiseId aId) override;
+ void RejectPromise(PromiseId aId, ErrorResult&& aException,
+ const nsCString& aReason) override;
+
+ void OnKeyStatusesChange(const nsAString& aSessionId) override;
+
+ void GetStatusForPolicy(PromiseId aPromiseId,
+ const nsAString& aMinHdcpVersion) override {}
+
+#ifdef DEBUG
+ bool IsOnOwnerThread() override {
+ return NS_GetCurrentThread() == mOwnerThread;
+ }
+#endif
+
+ WMFCDMProxy* AsWMFCDMProxy() override { return this; }
+
+ // Can only be called after initialization succeeded.
+ uint64_t GetCDMProxyId() const;
+
+ private:
+ virtual ~WMFCDMProxy();
+
+ template <typename T>
+ void ResolvePromiseWithResult(PromiseId aId, const T& aResult) {}
+ void RejectPromiseOnMainThread(PromiseId aId,
+ CopyableErrorResult&& aException,
+ const nsCString& aReason);
+ // Reject promise with an InvalidStateError and the given message.
+ void RejectPromiseWithStateError(PromiseId aId, const nsCString& aReason);
+
+ RefPtr<WMFCDMImpl> mCDM;
+
+ const dom::MediaKeySystemConfiguration mConfig;
+
+ RefPtr<WMFCDMProxyCallback> mProxyCallback;
+
+ // It can only be used on the main thread.
+ bool mIsShutdown = false;
+};
+
+} // namespace mozilla
+
+#endif // DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXY_H_
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxyCallback.cpp b/dom/media/eme/mediafoundation/WMFCDMProxyCallback.cpp
new file mode 100644
index 0000000000..6464cec4c7
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMProxyCallback.cpp
@@ -0,0 +1,72 @@
+/* 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 "WMFCDMProxyCallback.h"
+
+#include "mozilla/WMFCDMProxy.h"
+
+#define RETURN_IF_NULL(proxy) \
+ if (!proxy) { \
+ return; \
+ }
+
+namespace mozilla {
+
+WMFCDMProxyCallback::WMFCDMProxyCallback(WMFCDMProxy* aProxy) : mProxy(aProxy) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mProxy);
+}
+
+WMFCDMProxyCallback::~WMFCDMProxyCallback() { MOZ_ASSERT(!mProxy); }
+
+void WMFCDMProxyCallback::OnSessionMessage(const MFCDMKeyMessage& aMessage) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WMFCDMProxyCallback::OnSessionMessage",
+ [self = RefPtr{this}, this, message = aMessage]() {
+ RETURN_IF_NULL(mProxy);
+ mProxy->OnSessionMessage(message.sessionId(), message.type(),
+ std::move(message.message()));
+ }));
+}
+
+void WMFCDMProxyCallback::OnSessionKeyStatusesChange(
+ const MFCDMKeyStatusChange& aKeyStatuses) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WMFCDMProxyCallback::OnSessionKeyStatusesChange",
+ [self = RefPtr{this}, this, keyStatuses = aKeyStatuses]() {
+ RETURN_IF_NULL(mProxy);
+ bool keyStatusesChange = false;
+ {
+ auto caps = mProxy->Capabilites().Lock();
+ for (const auto& keyInfo : keyStatuses.keyInfo()) {
+ keyStatusesChange |= caps->SetKeyStatus(
+ keyInfo.keyId(), keyStatuses.sessionId(),
+ dom::Optional<dom::MediaKeyStatus>(keyInfo.status()));
+ }
+ }
+ if (keyStatusesChange) {
+ mProxy->OnKeyStatusesChange(keyStatuses.sessionId());
+ }
+ }));
+}
+
+void WMFCDMProxyCallback::OnSessionKeyExpiration(
+ const MFCDMKeyExpiration& aExpiration) {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "WMFCDMProxyCallback::OnSessionKeyExpiration",
+ [self = RefPtr{this}, this, expiration = aExpiration]() {
+ RETURN_IF_NULL(mProxy);
+ mProxy->OnExpirationChange(
+ expiration.sessionId(),
+ expiration.expiredTimeMilliSecondsSinceEpoch());
+ }));
+}
+
+void WMFCDMProxyCallback::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxy = nullptr;
+}
+
+#undef RETURN_IF_NULL
+} // namespace mozilla
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxyCallback.h b/dom/media/eme/mediafoundation/WMFCDMProxyCallback.h
new file mode 100644
index 0000000000..6e76f63ccc
--- /dev/null
+++ b/dom/media/eme/mediafoundation/WMFCDMProxyCallback.h
@@ -0,0 +1,38 @@
+/* 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/. */
+
+#ifndef DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXYCALLBACK_H_
+#define DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXYCALLBACK_H_
+
+#include "mozilla/PMFCDM.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+class WMFCDMProxy;
+
+// This class is used to notify CDM related events called from MFCDMChild, and
+// it will forward the relative calls to WMFCDMProxy on the main thread.
+class WMFCDMProxyCallback final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WMFCDMProxyCallback);
+
+ explicit WMFCDMProxyCallback(WMFCDMProxy* aProxy);
+
+ void OnSessionMessage(const MFCDMKeyMessage& aMessage);
+
+ void OnSessionKeyStatusesChange(const MFCDMKeyStatusChange& aKeyStatuses);
+
+ void OnSessionKeyExpiration(const MFCDMKeyExpiration& aExpiration);
+
+ void Shutdown();
+
+ private:
+ ~WMFCDMProxyCallback();
+ RefPtr<WMFCDMProxy> mProxy;
+};
+
+} // namespace mozilla
+
+#endif // DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMPROXYCALLBACK_H_
diff --git a/dom/media/eme/mediafoundation/moz.build b/dom/media/eme/mediafoundation/moz.build
new file mode 100644
index 0000000000..8c54b381ff
--- /dev/null
+++ b/dom/media/eme/mediafoundation/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "WMFCDMImpl.h",
+ "WMFCDMProxy.h",
+ "WMFCDMProxyCallback.h",
+]
+
+UNIFIED_SOURCES += [
+ "WMFCDMImpl.cpp",
+ "WMFCDMProxy.cpp",
+ "WMFCDMProxyCallback.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"