diff options
Diffstat (limited to 'media/wmf-clearkey')
25 files changed, 2541 insertions, 0 deletions
diff --git a/media/wmf-clearkey/WMFClearKey.def b/media/wmf-clearkey/WMFClearKey.def new file mode 100644 index 0000000000..c473b44d79 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKey.def @@ -0,0 +1,5 @@ +LIBRARY WMFClearKey.dll +EXPORTS + DllCanUnloadNow PRIVATE + DllGetActivationFactory PRIVATE + DllGetClassObject PRIVATE diff --git a/media/wmf-clearkey/WMFClearKeyActivate.cpp b/media/wmf-clearkey/WMFClearKeyActivate.cpp new file mode 100644 index 0000000000..494e58d922 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyActivate.cpp @@ -0,0 +1,213 @@ +/* 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 "WMFClearKeyActivate.h" + +#include <mfapi.h> +#include <mferror.h> + +#include "WMFClearKeyContentEnabler.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +HRESULT WMFClearKeyActivate::RuntimeClassInitialize() { return S_OK; } + +STDMETHODIMP WMFClearKeyActivate::ActivateObject(REFIID aRiid, void** aPpv) { + ENTRY_LOG(); + ComPtr<IMFContentEnabler> contentEnabler; + RETURN_IF_FAILED( + MakeAndInitialize<WMFClearKeyContentEnabler>(&contentEnabler)); + RETURN_IF_FAILED(contentEnabler.CopyTo(aRiid, aPpv)); + return S_OK; +} + +STDMETHODIMP WMFClearKeyActivate::ShutdownObject() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::DetachObject() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +// IMFAttributes inherited by IMFActivate +STDMETHODIMP WMFClearKeyActivate::GetItem(REFGUID aGuidKey, + PROPVARIANT* aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetItemType(REFGUID aGuidKey, + MF_ATTRIBUTE_TYPE* aType) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::CompareItem(REFGUID aGuidKey, + REFPROPVARIANT aValue, + BOOL* aResult) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::Compare(IMFAttributes* aAttributes, + MF_ATTRIBUTES_MATCH_TYPE aType, + BOOL* aResult) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetUINT32(REFGUID aGuidKey, UINT32* aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetUINT64(REFGUID aGuidKey, UINT64* aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetDouble(REFGUID aGuidKey, double* aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetGUID(REFGUID aGuidKey, GUID* aGuidValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetStringLength(REFGUID aGuidKey, + UINT32* aPcchLength) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetString(REFGUID aGuidKey, LPWSTR aPwszValue, + UINT32 aCchBufSize, + UINT32* aPcchLength) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetAllocatedString(REFGUID aGuidKey, + LPWSTR* aPpwszValue, + UINT32* aPcchLength) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetBlobSize(REFGUID aGuidKey, + UINT32* aPcbBlobSize) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetBlob(REFGUID aGuidKey, UINT8* pBuf, + UINT32 aCbBufSize, + UINT32* aPcbBlobSize) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetAllocatedBlob(REFGUID aGuidKey, + UINT8** aBuf, + UINT32* aPcbSize) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetUnknown(REFGUID aGuidKey, REFIID aRiid, + LPVOID* aPpv) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetItem(REFGUID aGuidKey, + REFPROPVARIANT aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::DeleteItem(REFGUID aGuidKey) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::DeleteAllItems() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetUINT32(REFGUID aGuidKey, UINT32 aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetUINT64(REFGUID aGuidKey, UINT64 aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetDouble(REFGUID aGuidKey, double aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetGUID(REFGUID aGuidKey, + REFGUID aGuidValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetString(REFGUID aGuidKey, + LPCWSTR aWszValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetBlob(REFGUID aGuidKey, const UINT8* aBuf, + UINT32 aCbBufSize) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::SetUnknown(REFGUID aGuidKey, + IUnknown* aUnknown) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::LockStore() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::UnlockStore() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetCount(UINT32* aPcItems) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::GetItemByIndex(UINT32 aIndex, GUID* aGuidKey, + PROPVARIANT* aValue) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyActivate::CopyAllItems(IMFAttributes* aDest) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyActivate.h b/media/wmf-clearkey/WMFClearKeyActivate.h new file mode 100644 index 0000000000..cd992221e7 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyActivate.h @@ -0,0 +1,77 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYACTIVATE_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYACTIVATE_H + +#include <wrl.h> +#include <wrl/client.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// This class is used to provide WMFClearKeyContentEnabler. +class WMFClearKeyActivate + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFActivate, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyActivate() = default; + ~WMFClearKeyActivate() = default; + WMFClearKeyActivate(const WMFClearKeyActivate&) = delete; + WMFClearKeyActivate& operator=(const WMFClearKeyActivate&) = delete; + + HRESULT RuntimeClassInitialize(); + + // IMFActivate + STDMETHODIMP ActivateObject(REFIID aRiid, void** aPpv) override; + STDMETHODIMP ShutdownObject() override; + STDMETHODIMP DetachObject() override; + + // IMFAttributes inherited by IMFActivate + STDMETHODIMP GetItem(REFGUID aGuidKey, PROPVARIANT* aValue) override; + STDMETHODIMP GetItemType(REFGUID aGuidKey, MF_ATTRIBUTE_TYPE* aType) override; + STDMETHODIMP CompareItem(REFGUID aGuidKey, REFPROPVARIANT aValue, + BOOL* aResult) override; + STDMETHODIMP Compare(IMFAttributes* aAttributes, + MF_ATTRIBUTES_MATCH_TYPE aType, BOOL* aResult) override; + STDMETHODIMP GetUINT32(REFGUID aGuidKey, UINT32* aValue) override; + STDMETHODIMP GetUINT64(REFGUID aGuidKey, UINT64* aValue) override; + STDMETHODIMP GetDouble(REFGUID aGuidKey, double* aValue) override; + STDMETHODIMP GetGUID(REFGUID aGuidKey, GUID* aGuidValue) override; + STDMETHODIMP GetStringLength(REFGUID aGuidKey, UINT32* aPcchLength) override; + STDMETHODIMP GetString(REFGUID aGuidKey, LPWSTR aPwszValue, + UINT32 aCchBufSize, UINT32* aPcchLength) override; + STDMETHODIMP GetAllocatedString(REFGUID aGuidKey, LPWSTR* aPpwszValue, + UINT32* aPcchLength) override; + STDMETHODIMP GetBlobSize(REFGUID aGuidKey, UINT32* aPcbBlobSize) override; + STDMETHODIMP GetBlob(REFGUID aGuidKey, UINT8* aBuf, UINT32 aCbBufSize, + UINT32* aPcbBlobSize) override; + STDMETHODIMP GetAllocatedBlob(REFGUID aGuidKey, UINT8** aBuf, + UINT32* aPcbSize) override; + STDMETHODIMP GetUnknown(REFGUID aGuidKey, REFIID aRiid, + LPVOID* aPpv) override; + STDMETHODIMP SetItem(REFGUID aGuidKey, REFPROPVARIANT aValue) override; + STDMETHODIMP DeleteItem(REFGUID aGuidKey) override; + STDMETHODIMP DeleteAllItems() override; + STDMETHODIMP SetUINT32(REFGUID aGuidKey, UINT32 aValue) override; + STDMETHODIMP SetUINT64(REFGUID aGuidKey, UINT64 aValue) override; + STDMETHODIMP SetDouble(REFGUID aGuidKey, double aValue) override; + STDMETHODIMP SetGUID(REFGUID aGuidKey, REFGUID aGuidValue) override; + STDMETHODIMP SetString(REFGUID aGuidKey, LPCWSTR aWszValue) override; + STDMETHODIMP SetBlob(REFGUID aGuidKey, const UINT8* aBuf, + UINT32 aCbBufSize) override; + STDMETHODIMP SetUnknown(REFGUID aGuidKey, IUnknown* aUnknown) override; + STDMETHODIMP LockStore() override; + STDMETHODIMP UnlockStore() override; + STDMETHODIMP GetCount(UINT32* aPcItems) override; + STDMETHODIMP GetItemByIndex(UINT32 aIndex, GUID* aGuidKey, + PROPVARIANT* aValue) override; + STDMETHODIMP CopyAllItems(IMFAttributes* aDest) override; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYACTIVATE_H diff --git a/media/wmf-clearkey/WMFClearKeyCDM.cpp b/media/wmf-clearkey/WMFClearKeyCDM.cpp new file mode 100644 index 0000000000..b782aec5da --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDM.cpp @@ -0,0 +1,371 @@ +/* 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 "WMFClearKeyCDM.h" + +#include <Mferror.h> +#include <mfapi.h> +#include <oleauto.h> +#include <optional> +#include <windows.h> +#include <windows.media.h> + +#include "WMFClearKeyTrustedInput.h" +#include "WMFClearKeySession.h" +#include "WMFDecryptedBlock.h" +#include "WMFPMPServer.h" + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +static HRESULT AddPropertyToSet( + ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, + LPCWSTR aName, IInspectable* aInspectable) { + boolean replaced = false; + ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> + map; + RETURN_IF_FAILED(aPropertySet->QueryInterface(IID_PPV_ARGS(&map))); + RETURN_IF_FAILED( + map->Insert(Microsoft::WRL::Wrappers::HStringReference(aName).Get(), + aInspectable, &replaced)); + return S_OK; +} + +static HRESULT AddStringToPropertySet( + ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, + LPCWSTR aName, LPCWSTR aString) { + ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue; + ComPtr<ABI::Windows::Foundation::IPropertyValueStatics> propertyValueStatics; + RETURN_IF_FAILED(ABI::Windows::Foundation::GetActivationFactory( + Microsoft::WRL::Wrappers::HStringReference( + RuntimeClass_Windows_Foundation_PropertyValue) + .Get(), + &propertyValueStatics)); + RETURN_IF_FAILED(propertyValueStatics->CreateString( + Microsoft::WRL::Wrappers::HStringReference(aString).Get(), + &propertyValue)); + RETURN_IF_FAILED(AddPropertyToSet(aPropertySet, aName, propertyValue.Get())); + return S_OK; +} + +static HRESULT AddBoolToPropertySet( + ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, + LPCWSTR aName, BOOL aValue) { + ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue; + ComPtr<ABI::Windows::Foundation::IPropertyValueStatics> propertyValueStatics; + RETURN_IF_FAILED(ABI::Windows::Foundation::GetActivationFactory( + Microsoft::WRL::Wrappers::HStringReference( + RuntimeClass_Windows_Foundation_PropertyValue) + .Get(), + &propertyValueStatics)); + RETURN_IF_FAILED( + propertyValueStatics->CreateBoolean(!!aValue, &propertyValue)); + RETURN_IF_FAILED(AddPropertyToSet(aPropertySet, aName, propertyValue.Get())); + return S_OK; +} + +MF_MEDIAKEYSESSION_MESSAGETYPE ToMFMessageType(cdm::MessageType aMessageType) { + switch (aMessageType) { + case cdm::MessageType::kLicenseRequest: + return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST; + case cdm::MessageType::kLicenseRenewal: + return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RENEWAL; + case cdm::MessageType::kLicenseRelease: + return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RELEASE; + case cdm::MessageType::kIndividualizationRequest: + return MF_MEDIAKEYSESSION_MESSAGETYPE_INDIVIDUALIZATION_REQUEST; + } +} + +namespace mozilla { + +HRESULT WMFClearKeyCDM::RuntimeClassInitialize(IPropertyStore* aProperties) { + ENTRY_LOG(); + // A workaround in order to create an in-process PMP server regardless of the + // system's HWDRM capability. As accoriding to Microsoft, only PlayReady is + // supported for the in-process PMP so we pretend ourselves as a PlayReady + // CDM for the PMP server. + ComPtr<ABI::Windows::Foundation::Collections::IPropertySet> propertyPmp; + RETURN_IF_FAILED(Windows::Foundation::ActivateInstance( + Microsoft::WRL::Wrappers::HStringReference( + RuntimeClass_Windows_Foundation_Collections_PropertySet) + .Get(), + &propertyPmp)); + RETURN_IF_FAILED(AddStringToPropertySet( + propertyPmp.Get(), L"Windows.Media.Protection.MediaProtectionSystemId", + PLAYREADY_GUID_MEDIA_PROTECTION_SYSTEM_ID_STRING)); + RETURN_IF_FAILED(AddBoolToPropertySet( + propertyPmp.Get(), L"Windows.Media.Protection.UseHardwareProtectionLayer", + TRUE)); + RETURN_IF_FAILED((MakeAndInitialize< + WMFPMPServer, + ABI::Windows::Media::Protection::IMediaProtectionPMPServer>( + &mPMPServer, propertyPmp.Get()))); + + mSessionManager = new SessionManagerWrapper(this); + return S_OK; +} + +WMFClearKeyCDM::~WMFClearKeyCDM() { ENTRY_LOG(); } + +STDMETHODIMP WMFClearKeyCDM::SetContentEnabler( + IMFContentEnabler* aContentEnabler, IMFAsyncResult* aResult) { + ENTRY_LOG(); + if (!aContentEnabler || !aResult) { + return E_INVALIDARG; + } + // Invoke the callback immediately but will determine whether the keyid exists + // or not in the decryptor's ProcessOutput(). + RETURN_IF_FAILED(MFInvokeCallback(aResult)); + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::SetPMPHostApp(IMFPMPHostApp* aPmpHostApp) { + ENTRY_LOG(); + // Simply return S_OK and ignore IMFPMPHostApp, which is only used for the + // out-of-process PMP. + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::CreateSession( + MF_MEDIAKEYSESSION_TYPE aSessionType, + IMFContentDecryptionModuleSessionCallbacks* aCallbacks, + IMFContentDecryptionModuleSession** aSession) { + ENTRY_LOG(); + RETURN_IF_FAILED( + (MakeAndInitialize<WMFClearKeySession, IMFContentDecryptionModuleSession>( + aSession, aSessionType, aCallbacks, mSessionManager))); + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::CreateTrustedInput( + const BYTE* aContentInitData, DWORD aContentInitDataSize, + IMFTrustedInput** aTrustedInput) { + ENTRY_LOG(); + ComPtr<IMFTrustedInput> trustedInput; + RETURN_IF_FAILED((MakeAndInitialize<WMFClearKeyTrustedInput, IMFTrustedInput>( + &trustedInput, mSessionManager))); + *aTrustedInput = trustedInput.Detach(); + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::GetProtectionSystemIds(GUID** aSystemIds, + DWORD* aCount) { + ENTRY_LOG(); + GUID* systemId = static_cast<GUID*>(CoTaskMemAlloc(sizeof(GUID))); + if (!systemId) { + return E_OUTOFMEMORY; + } + *systemId = CLEARKEY_GUID_CLEARKEY_PROTECTION_SYSTEM_ID; + *aSystemIds = systemId; + *aCount = 1; + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::GetService(REFGUID aGuidService, REFIID aRiid, + LPVOID* aPpvObject) { + ENTRY_LOG(); + if (MF_CONTENTDECRYPTIONMODULE_SERVICE != aGuidService) { + ENTRY_LOG_ARGS("unsupported guid!"); + return MF_E_UNSUPPORTED_SERVICE; + } + if (!mPMPServer) { + ENTRY_LOG_ARGS("no PMP server!"); + return MF_INVALID_STATE_ERR; + } + if (aRiid == ABI::Windows::Media::Protection::IID_IMediaProtectionPMPServer) { + RETURN_IF_FAILED(mPMPServer.CopyTo(aRiid, aPpvObject)); + } else { + ComPtr<IMFGetService> getService; + RETURN_IF_FAILED(mPMPServer.As(&getService)); + RETURN_IF_FAILED(getService->GetService(MF_PMP_SERVICE, aRiid, aPpvObject)); + } + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::GetSuspendNotify(IMFCdmSuspendNotify** aNotify) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyCDM::SetServerCertificate(const BYTE* aCertificate, + DWORD aCertificateSize) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyCDM::Shutdown() { + ENTRY_LOG(); + mSessionManager->Shutdown(); + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDM::GetShutdownStatus(MFSHUTDOWN_STATUS* aStatus) { + ENTRY_LOG(); + // https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfshutdown-getshutdownstatus#return-value + if (mSessionManager->IsShutdown()) { + return MF_E_INVALIDREQUEST; + } + return S_OK; +} + +// SessionManagerWrapper + +SessionManagerWrapper::SessionManagerWrapper(WMFClearKeyCDM* aCDM) + : mOwnerCDM(aCDM), mSessionManager(new ClearKeySessionManager(this)) { + // For testing, we don't care about these. + mSessionManager->Init(false /* aDistinctiveIdentifierAllowed */, + false /* aPersistentStateAllowed*/); +} + +SessionManagerWrapper::~SessionManagerWrapper() { ENTRY_LOG(); } + +// Callback methods +void SessionManagerWrapper::OnResolveNewSessionPromise( + uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdSize) { + if (auto rv = mActiveSyncResultChecker.find(aPromiseId); + rv != mActiveSyncResultChecker.end()) { + LOG("Generated request (promise-id=%u, sessionId=%s)", aPromiseId, + aSessionId); + rv->second->SetResultConstChar(aSessionId); + std::string sessionId{aSessionId}; + MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end()); + mSessions[sessionId] = rv->second->GetKeySession(); + } +} + +void SessionManagerWrapper::OnResolvePromise(uint32_t aPromiseId) { + if (auto rv = mActiveSyncResultChecker.find(aPromiseId); + rv != mActiveSyncResultChecker.end()) { + LOG("Resolved promise (promise-id=%u)", aPromiseId); + rv->second->SetResultBool(true); + } +} + +void SessionManagerWrapper::OnRejectPromise(uint32_t aPromiseId, + cdm::Exception aException, + uint32_t aSystemCode, + const char* aErrorMessage, + uint32_t aErrorMessageSize) { + if (auto rv = mActiveSyncResultChecker.find(aPromiseId); + rv != mActiveSyncResultChecker.end()) { + LOG("Rejected promise (promise-id=%u)", aPromiseId); + rv->second->SetResultBool(false); + } +} + +HRESULT SessionManagerWrapper::GenerateRequest(cdm::InitDataType aInitDataType, + const BYTE* aInitData, + DWORD aInitDataSize, + cdm::SessionType aSessionType, + WMFClearKeySession* aSession, + std::string& aSessionIdOut) { + ENTRY_LOG(); + MOZ_DIAGNOSTIC_ASSERT(aSessionType == cdm::SessionType::kTemporary); + SyncResultChecker checker(*this, aSession); + mSessionManager->CreateSession(checker.GetPromiseId(), aInitDataType, + aInitData, aInitDataSize, aSessionType); + auto* rv = std::get_if<const char*>(&checker.GetResult()); + if (!rv) { + LOG("Failed to generate request, no session Id!"); + return E_FAIL; + } + aSessionIdOut = std::string(*rv); + return S_OK; +} + +HRESULT SessionManagerWrapper::UpdateSession(const std::string& aSessionId, + const BYTE* aResponse, + DWORD aResponseSize) { + ENTRY_LOG(); + SyncResultChecker checker(*this); + mSessionManager->UpdateSession(checker.GetPromiseId(), aSessionId.c_str(), + aSessionId.size(), aResponse, aResponseSize); + auto* rv = std::get_if<bool>(&checker.GetResult()); + return rv && *rv ? S_OK : E_FAIL; +} + +HRESULT SessionManagerWrapper::CloseSession(const std::string& aSessionId) { + ENTRY_LOG(); + SyncResultChecker checker(*this); + mSessionManager->CloseSession(checker.GetPromiseId(), aSessionId.c_str(), + aSessionId.size()); + auto* rv = std::get_if<bool>(&checker.GetResult()); + return rv && *rv ? S_OK : E_FAIL; +} + +HRESULT SessionManagerWrapper::RemoveSession(const std::string& aSessionId) { + ENTRY_LOG(); + SyncResultChecker checker(*this); + MOZ_ASSERT(mSessions.find(aSessionId) != mSessions.end()); + mSessions.erase(aSessionId); + mSessionManager->RemoveSession(checker.GetPromiseId(), aSessionId.c_str(), + aSessionId.size()); + auto* rv = std::get_if<bool>(&checker.GetResult()); + return rv && *rv ? S_OK : E_FAIL; +} + +HRESULT SessionManagerWrapper::Decrypt(const cdm::InputBuffer_2& aBuffer, + cdm::DecryptedBlock* aDecryptedBlock) { + ENTRY_LOG(); + // From MF thread pool. + std::lock_guard<std::mutex> lock(mMutex); + if (mIsShutdown) { + return MF_E_SHUTDOWN; + } + auto rv = mSessionManager->Decrypt(aBuffer, aDecryptedBlock); + return rv == cdm::kSuccess ? S_OK : E_FAIL; +} + +void SessionManagerWrapper::OnSessionMessage(const char* aSessionId, + uint32_t aSessionIdSize, + cdm::MessageType aMessageType, + const char* aMessage, + uint32_t aMessageSize) { + ENTRY_LOG_ARGS("sessionId=%s, sz=%u", aSessionId, aSessionIdSize); + std::string sessionId(aSessionId); + MOZ_ASSERT(mSessions.find(sessionId) != mSessions.end()); + mSessions[sessionId]->OnKeyMessage(ToMFMessageType(aMessageType), + reinterpret_cast<const BYTE*>(aMessage), + aMessageSize); +} + +void SessionManagerWrapper::OnSessionKeysChange( + const char* aSessionId, uint32_t aSessionIdSize, + bool aHasAdditionalUsableKey, const cdm::KeyInformation* aKeysInfo, + uint32_t aKeysInfoCount) { + ENTRY_LOG_ARGS("sessionId=%s, sz=%u", aSessionId, aSessionIdSize); + std::string sessionId(aSessionId); + MOZ_ASSERT(mSessions.find(sessionId) != mSessions.end()); + mSessions[sessionId]->OnKeyStatusChanged(aKeysInfo, aKeysInfoCount); +} + +// `ClearKeySessionManager::Decrypt` will call this method to allocate buffer +// for decryted data. +cdm::Buffer* SessionManagerWrapper::Allocate(uint32_t aCapacity) { + ENTRY_LOG_ARGS("capacity=%u", aCapacity); + return new WMFDecryptedBuffer(aCapacity); +} + +void SessionManagerWrapper::Shutdown() { + ENTRY_LOG(); + std::lock_guard<std::mutex> lock(mMutex); + if (mIsShutdown) { + return; + } + mOwnerCDM = nullptr; + for (const auto& session : mSessions) { + session.second->Shutdown(); + } + mSessions.clear(); + mSessionManager = nullptr; + mIsShutdown = true; +} + +bool SessionManagerWrapper::IsShutdown() { + std::lock_guard<std::mutex> lock(mMutex); + return mIsShutdown; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyCDM.h b/media/wmf-clearkey/WMFClearKeyCDM.h new file mode 100644 index 0000000000..765cac36f0 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDM.h @@ -0,0 +1,207 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDM_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDM_H + +#include <mfidl.h> +#include <unordered_map> +#include <variant> +#include <windows.h> +#include <windows.media.protection.h> +#include <wrl.h> +#include <wrl/client.h> + +#include "ClearKeySessionManager.h" +#include "MFCDMExtra.h" +#include "WMFClearKeyUtils.h" +#include "content_decryption_module.h" + +namespace mozilla { + +class SessionManagerWrapper; +class WMFClearKeySession; + +// This our customized MFCDM for supporting clearkey in our testing. It would +// use ClearKeySessionManager via SessionManagerWrapper to perform decryption. +class WMFClearKeyCDM final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFContentDecryptionModule, IMFGetService, IMFShutdown, + Microsoft::WRL::FtmBase> { + public: + WMFClearKeyCDM() = default; + ~WMFClearKeyCDM(); + WMFClearKeyCDM(const WMFClearKeyCDM&) = delete; + WMFClearKeyCDM& operator=(const WMFClearKeyCDM&) = delete; + + HRESULT RuntimeClassInitialize(IPropertyStore* aProperties); + + // IMFContentDecryptionModule + STDMETHODIMP SetContentEnabler(IMFContentEnabler* aContentEnabler, + IMFAsyncResult* aResult) override; + STDMETHODIMP GetSuspendNotify(IMFCdmSuspendNotify** aNotify) override; + STDMETHODIMP SetPMPHostApp(IMFPMPHostApp* aPmpHostApp) override; + STDMETHODIMP CreateSession( + MF_MEDIAKEYSESSION_TYPE aSessionType, + IMFContentDecryptionModuleSessionCallbacks* aCallbacks, + IMFContentDecryptionModuleSession** aSession) override; + STDMETHODIMP SetServerCertificate(const BYTE* aCertificate, + DWORD aCertificateSize) override; + STDMETHODIMP CreateTrustedInput(const BYTE* aContentInitData, + DWORD aContentInitDataSize, + IMFTrustedInput** aTrustedInput) override; + STDMETHODIMP GetProtectionSystemIds(GUID** aSystemIds, + DWORD* aCount) override; + // IMFGetService + STDMETHODIMP GetService(REFGUID aGuidService, REFIID aRiid, + LPVOID* aPpvObject) override; + + // IMFShutdown + STDMETHODIMP Shutdown() override; + STDMETHODIMP GetShutdownStatus(MFSHUTDOWN_STATUS* aStatus) override; + + private: + RefPtr<SessionManagerWrapper> mSessionManager; + Microsoft::WRL::ComPtr< + ABI::Windows::Media::Protection::IMediaProtectionPMPServer> + mPMPServer; +}; + +// In order to reuse existing Gecko clearkey implementation, we need to +// inherit the class `cdm::Host_10`. +// TODO : add a way to assert thread usage. It would be used on MF thread pool +// and the media supervisor thread pool. +class SessionManagerWrapper final : public RefCounted, private cdm::Host_10 { + public: + explicit SessionManagerWrapper(WMFClearKeyCDM* aCDM); + + HRESULT GenerateRequest(cdm::InitDataType aInitDataType, + const BYTE* aInitData, DWORD aInitDataSize, + cdm::SessionType aSessionType, + WMFClearKeySession* aSession, + std::string& aSessionIdOut); + HRESULT UpdateSession(const std::string& aSessionId, const BYTE* aResponse, + DWORD aResponseSize); + HRESULT CloseSession(const std::string& aSessionId); + HRESULT RemoveSession(const std::string& aSessionId); + HRESULT Decrypt(const cdm::InputBuffer_2& aBuffer, + cdm::DecryptedBlock* aDecryptedBlock); + + void Shutdown(); + bool IsShutdown(); + + private: + ~SessionManagerWrapper(); + // cdm::Host_10 + void OnInitialized(bool aSuccess) override {} + void OnResolveKeyStatusPromise(uint32_t aPromiseId, + cdm::KeyStatus aKeyStatus) override {} + void OnResolveNewSessionPromise(uint32_t aPromiseId, const char* aSessionId, + uint32_t aSessionIdSize) override; + void OnResolvePromise(uint32_t aPromiseId) override; + void OnRejectPromise(uint32_t aPromiseId, cdm::Exception aException, + uint32_t aSystemCode, const char* aErrorMessage, + uint32_t aErrorMessageSize) override; + void OnSessionMessage(const char* aSessionId, uint32_t aSessionIdSize, + cdm::MessageType aMessageType, const char* aMessage, + uint32_t aMessageSize) override; + void OnSessionKeysChange(const char* aSessionId, uint32_t aSessionIdSize, + bool aHasAdditionalUsableKey, + const cdm::KeyInformation* aKeysInfo, + uint32_t aKeysInfoCount) override; + void OnExpirationChange(const char* aSessionId, uint32_t aSessionIdSize, + cdm::Time aNewExpiryTime) override{ + // No need to implement this because the session would never expire in + // testing. + }; + void OnSessionClosed(const char* aSessionId, + uint32_t aSessionIdSize) override{ + // No need to implement this because session doesn't have close callback + // or events. + }; + cdm::FileIO* CreateFileIO(cdm::FileIOClient* aClient) override { + // We don't support this because we only support temporary session. + return nullptr; + } + void SendPlatformChallenge(const char* aServiceId, uint32_t aServiceIdSize, + const char* aChallenge, + uint32_t aChallengeSize) override {} + void EnableOutputProtection(uint32_t aDesiredProtectionMask) override {} + void QueryOutputProtectionStatus() override{}; + void OnDeferredInitializationDone(cdm::StreamType aStreamType, + cdm::Status aDecoderStatus) override {} + void RequestStorageId(uint32_t aVersion) override {} + cdm::Buffer* Allocate(uint32_t aCapacity) override; + void SetTimer(int64_t aDelayMs, void* aContext) override {} + cdm::Time GetCurrentWallTime() override { return 0.0; } + friend class SessionManager; + + Microsoft::WRL::ComPtr<WMFClearKeyCDM> mOwnerCDM; + RefPtr<ClearKeySessionManager> mSessionManager; + std::unordered_map<std::string, Microsoft::WRL::ComPtr<WMFClearKeySession>> + mSessions; + + // This is a RAII helper class to use ClearKeySessionManager::XXXSession + // methods in a sync style, which is what MFCDM is required. + // ClearKeySessionManager uses cdm::Host_10's OnResolve/RejectXXX as callback + // to report whether those function calls relatd with specific promise id + // succeed or not. As we only do temporary session for ClearKey testing, we + // don't need to wait to setup the storage so calling those XXXsession + // functions are actully a sync process. We guarantee that + // ClearKeySessionManager will use OnResolve/Reject methods to notify us + // result, right after we calling the session related method. + // [How to to use this class, not thread-safe] + // 1. create it on the stack + // 2. use GetPromiseId() to generate a fake promise id for tracking + // 3. in cdm::Host_10's callback function, check promise id to know what + // result needs to be set + // 4. check result to see if the session method succeed or not + class SyncResultChecker final { + public: + using ResultType = std::variant<const char*, bool>; + explicit SyncResultChecker(SessionManagerWrapper& aOwner) + : mOwner(aOwner), mIdx(sIdx++), mKeySession(nullptr) { + mOwner.mActiveSyncResultChecker.insert({mIdx, this}); + } + SyncResultChecker(SessionManagerWrapper& aOwner, + WMFClearKeySession* aKeySession) + : mOwner(aOwner), mIdx(sIdx++), mKeySession(aKeySession) { + mOwner.mActiveSyncResultChecker.insert({mIdx, this}); + } + ~SyncResultChecker() { mOwner.mActiveSyncResultChecker.erase(mIdx); } + uint32_t GetPromiseId() const { return mIdx; } + const ResultType& GetResult() const { return mResult; } + WMFClearKeySession* GetKeySession() const { return mKeySession; } + + private: + // Only allow setting result from these callbacks. + friend void SessionManagerWrapper::OnResolveNewSessionPromise(uint32_t, + const char*, + uint32_t); + friend void SessionManagerWrapper::OnResolvePromise(uint32_t); + friend void SessionManagerWrapper::OnRejectPromise(uint32_t, cdm::Exception, + uint32_t, const char*, + uint32_t); + void SetResultConstChar(const char* aResult) { + mResult.emplace<const char*>(aResult); + } + void SetResultBool(bool aResult) { mResult.emplace<bool>(aResult); } + + static inline uint32_t sIdx = 0; + SessionManagerWrapper& mOwner; + const uint32_t mIdx; + ResultType mResult; + WMFClearKeySession* const mKeySession; + }; + std::unordered_map<uint32_t, SyncResultChecker*> mActiveSyncResultChecker; + + // Protect following members. + std::mutex mMutex; + bool mIsShutdown = false; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDM_H diff --git a/media/wmf-clearkey/WMFClearKeyCDMAccess.cpp b/media/wmf-clearkey/WMFClearKeyCDMAccess.cpp new file mode 100644 index 0000000000..709690e48f --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDMAccess.cpp @@ -0,0 +1,53 @@ +/* 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 "WMFClearKeyCDMAccess.h" + +#include <Mferror.h> +#include <oleauto.h> + +#include "WMFClearKeyCDM.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +STDMETHODIMP WMFClearKeyCDMAccess::CreateContentDecryptionModule( + IPropertyStore* aProperties, IMFContentDecryptionModule** aCdm) { + ENTRY_LOG(); + if (!aProperties) { + ENTRY_LOG_ARGS("Null properties!"); + return MF_E_UNEXPECTED; + } + + *aCdm = nullptr; + ComPtr<IMFContentDecryptionModule> cdm; + RETURN_IF_FAILED( + (MakeAndInitialize<WMFClearKeyCDM, IMFContentDecryptionModule>( + &cdm, aProperties))); + *aCdm = cdm.Detach(); + ENTRY_LOG_ARGS("Created clearkey CDM!"); + return S_OK; +} + +STDMETHODIMP WMFClearKeyCDMAccess::GetConfiguration(IPropertyStore** aConfig) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyCDMAccess::GetKeySystem(LPWSTR* aKeySystem) { + ENTRY_LOG(); + *aKeySystem = (LPWSTR)CoTaskMemAlloc((wcslen(kCLEARKEY_SYSTEM_NAME) + 1) * + sizeof(wchar_t)); + if (*aKeySystem == NULL) { + return E_OUTOFMEMORY; + } + wcscpy_s(*aKeySystem, wcslen(kCLEARKEY_SYSTEM_NAME) + 1, + kCLEARKEY_SYSTEM_NAME); + return S_OK; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyCDMAccess.h b/media/wmf-clearkey/WMFClearKeyCDMAccess.h new file mode 100644 index 0000000000..49bd80b660 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDMAccess.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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMACCESS_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMACCESS_H + +#include <mfidl.h> +#include <windows.h> +#include <wrl.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// This class is used to create WMFClearKeyCDM. +class WMFClearKeyCDMAccess final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFContentDecryptionModuleAccess, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyCDMAccess() = default; + ~WMFClearKeyCDMAccess() = default; + WMFClearKeyCDMAccess(const WMFClearKeyCDMAccess&) = delete; + WMFClearKeyCDMAccess& operator=(const WMFClearKeyCDMAccess&) = delete; + + HRESULT RuntimeClassInitialize() { return S_OK; }; + + // IMFContentDecryptionModuleAccess + STDMETHODIMP CreateContentDecryptionModule( + IPropertyStore* aProperties, IMFContentDecryptionModule** aCdm) override; + STDMETHODIMP GetConfiguration(IPropertyStore** aConfig) override; + STDMETHODIMP GetKeySystem(LPWSTR* aKeySystem) override; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMACCESS_H diff --git a/media/wmf-clearkey/WMFClearKeyCDMFactory.cpp b/media/wmf-clearkey/WMFClearKeyCDMFactory.cpp new file mode 100644 index 0000000000..6ec253850d --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDMFactory.cpp @@ -0,0 +1,82 @@ +/* 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 "WMFClearKeyCDMFactory.h" + +#include <string> + +#include <Mferror.h> + +#include "WMFClearKeyCDMAccess.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::MakeAndInitialize; + +ActivatableClass(WMFClearKeyCDMFactory); + +bool isRequiringHDCP22OrAbove(LPCWSTR aType) { + if (aType == nullptr || *aType == L'\0') { + return false; + } + + // The HDCP value follows the feature value in + // https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilities.istypesupported?view=winrt-19041 + // - 1 (on without HDCP 2.2 Type 1 restriction) + // - 2 (on with HDCP 2.2 Type 1 restriction) + std::wstring wstr(aType); + std::string hdcpStr(wstr.begin(), wstr.end()); + return wstr.find(L"hdcp=2") != std::string::npos; +} + +STDMETHODIMP_(BOOL) +WMFClearKeyCDMFactory::IsTypeSupported(_In_ LPCWSTR aKeySystem, + _In_opt_ LPCWSTR aContentType) { + // For testing, return support for most of cases. Only returns false for some + // special cases. + + bool needHDCP22OrAbove = isRequiringHDCP22OrAbove(aContentType); + ENTRY_LOG_ARGS("Need-HDCP2.2+=%d", needHDCP22OrAbove); + + // As the API design of the Media Foundation, we can only know whether the + // requester is asking for HDCP 2.2+ or not, we can't know the exact HDCP + // version which is used in getStatusPolicy web api. Therefore, we pretend + // ourselves only having HDCP 2.1 compliant. + if (needHDCP22OrAbove) { + return false; + } + return true; +} + +STDMETHODIMP +WMFClearKeyCDMFactory::CreateContentDecryptionModuleAccess( + LPCWSTR aKeySystem, IPropertyStore** aConfigurations, + DWORD aNumConfigurations, IMFContentDecryptionModuleAccess** aCdmAccess) { + ENTRY_LOG(); + if (aKeySystem == nullptr || aKeySystem[0] == L'\0') { + ENTRY_LOG_ARGS("Key system is null or empty"); + return MF_TYPE_ERR; + } + + if (aNumConfigurations == 0) { + ENTRY_LOG_ARGS("No available configration"); + return MF_TYPE_ERR; + } + + if (!IsTypeSupported(aKeySystem, nullptr)) { + ENTRY_LOG_ARGS("Not supported type"); + return MF_NOT_SUPPORTED_ERR; + } + + RETURN_IF_FAILED(( + MakeAndInitialize<WMFClearKeyCDMAccess, IMFContentDecryptionModuleAccess>( + aCdmAccess))); + ENTRY_LOG_ARGS("Created CDM access!"); + return S_OK; +} + +#undef LOG + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyCDMFactory.h b/media/wmf-clearkey/WMFClearKeyCDMFactory.h new file mode 100644 index 0000000000..af1fbd5db7 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyCDMFactory.h @@ -0,0 +1,45 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMFACTORY_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMFACTORY_H + +#include <mfidl.h> +#include <windows.h> +#include <wrl.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// This class is used to return WMFClearKeyCDMAccess and answer what kind of key +// system is supported. +class WMFClearKeyCDMFactory final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, + Microsoft::WRL::CloakedIid<IMFContentDecryptionModuleFactory>, + Microsoft::WRL::FtmBase> { + InspectableClass(L"org.w3.clearkey", BaseTrust); + + public: + WMFClearKeyCDMFactory() = default; + ~WMFClearKeyCDMFactory() = default; + WMFClearKeyCDMFactory(const WMFClearKeyCDMFactory&) = delete; + WMFClearKeyCDMFactory& operator=(const WMFClearKeyCDMFactory&) = delete; + + HRESULT RuntimeClassInitialize() { return S_OK; } + + // IMFContentDecryptionModuleFactory + STDMETHODIMP_(BOOL) + IsTypeSupported(LPCWSTR aKeySystem, LPCWSTR aContentType) override; + + STDMETHODIMP CreateContentDecryptionModuleAccess( + LPCWSTR aKeySystem, IPropertyStore** aConfigurations, + DWORD aNumConfigurations, + IMFContentDecryptionModuleAccess** aCdmAccess) override; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMFACTORY_H diff --git a/media/wmf-clearkey/WMFClearKeyContentEnabler.cpp b/media/wmf-clearkey/WMFClearKeyContentEnabler.cpp new file mode 100644 index 0000000000..a9aa230e11 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyContentEnabler.cpp @@ -0,0 +1,65 @@ +/* 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 "WMFClearKeyContentEnabler.h" + +#include <mfapi.h> +#include <mferror.h> + +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; + +HRESULT WMFClearKeyContentEnabler::RuntimeClassInitialize() { return S_OK; } + +STDMETHODIMP WMFClearKeyContentEnabler::AutomaticEnable() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyContentEnabler::Cancel() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyContentEnabler::GetEnableData(BYTE** aData, + DWORD* aDataSize) { + ENTRY_LOG(); + // Does not support this method of content enabling with EME. + return MF_E_NOT_AVAILABLE; +} + +STDMETHODIMP WMFClearKeyContentEnabler::GetEnableURL( + LPWSTR* aUrl, DWORD* aUrlSize, MF_URL_TRUST_STATUS* aTrustStatus) { + ENTRY_LOG(); + // Does not support this method of content enabling with EME. + return MF_E_NOT_AVAILABLE; +} + +STDMETHODIMP WMFClearKeyContentEnabler::IsAutomaticSupported(BOOL* aAutomatic) { + ENTRY_LOG(); + if (!aAutomatic) { + return E_INVALIDARG; + } + *aAutomatic = FALSE; + return S_OK; +} + +STDMETHODIMP WMFClearKeyContentEnabler::MonitorEnable() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyContentEnabler::GetEnableType(GUID* aType) { + LOG("WMFClearKeyContentEnabler::GetEnableType"); + if (!aType) { + return E_INVALIDARG; + } + *aType = MEDIA_FOUNDATION_CLEARKEY_GUID_CONTENT_ENABLER_TYPE; + return S_OK; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyContentEnabler.h b/media/wmf-clearkey/WMFClearKeyContentEnabler.h new file mode 100644 index 0000000000..ebaf5be7ad --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyContentEnabler.h @@ -0,0 +1,42 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCONTENTENABLER_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCONTENTENABLER_H + +#include <wrl.h> +#include <wrl/client.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// This class is used to return correct enable type. +class WMFClearKeyContentEnabler + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFContentEnabler, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyContentEnabler() = default; + ~WMFClearKeyContentEnabler() = default; + WMFClearKeyContentEnabler(const WMFClearKeyContentEnabler&) = delete; + WMFClearKeyContentEnabler& operator=(const WMFClearKeyContentEnabler&) = + delete; + + HRESULT RuntimeClassInitialize(); + + // IMFContentEnabler + STDMETHODIMP AutomaticEnable() override; + STDMETHODIMP Cancel() override; + STDMETHODIMP GetEnableData(BYTE** aData, DWORD* aDataSize) override; + STDMETHODIMP GetEnableType(GUID* aType) override; + STDMETHODIMP GetEnableURL(LPWSTR* aUrl, DWORD* aUrlSize, + MF_URL_TRUST_STATUS* aTrustStatus) override; + STDMETHODIMP IsAutomaticSupported(BOOL* automatic) override; + STDMETHODIMP MonitorEnable() override; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCONTENTENABLER_H diff --git a/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.cpp b/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.cpp new file mode 100644 index 0000000000..3943c03e4d --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.cpp @@ -0,0 +1,115 @@ +/* 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 "WMFClearKeyInputTrustAuthority.h" + +#include <mfapi.h> +#include <mferror.h> + +#include "WMFClearKeyActivate.h" +#include "WMFClearKeyCDM.h" +// #include "WMFClearKeyDecryptor.h" +#include "WMFClearKeyUtils.h" +#include "WMFClearKeyOutputPolicy.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +HRESULT WMFClearKeyInputTrustAuthority::RuntimeClassInitialize( + UINT32 aStreamId, SessionManagerWrapper* aSessionManager) { + ENTRY_LOG(); + mSessionManager = aSessionManager; + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::GetDecrypter(REFIID aRiid, + void** aPpv) { + ENTRY_LOG(); + ComPtr<IMFTransform> decryptor; + // TODO : As currently we are still not able to make decryption working in the + // media foundation pipeline, we will finish the implementation for + // WMFClearKeyDecryptor later. + // RETURN_IF_FAILED((MakeAndInitialize<WMFClearKeyDecryptor, IMFTransform>( + // &decryptor, mSessionManager))); + RETURN_IF_FAILED(decryptor.CopyTo(aRiid, aPpv)); + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::RequestAccess( + MFPOLICYMANAGER_ACTION aAction, IMFActivate** aContentEnablerActivate) { + ENTRY_LOG_ARGS("aAction=%d", aAction); + // The ITA only allows the PLAY, EXTRACT and NO actions + // NOTE: Topology created only on the basis of EXTRACT or NO action will NOT + // decrypt content. + if (PEACTION_EXTRACT == aAction || PEACTION_NO == aAction) { + return S_OK; + } + if (PEACTION_PLAY != aAction) { + ENTRY_LOG_ARGS("Unsupported action"); + return MF_E_ITA_UNSUPPORTED_ACTION; + } + ComPtr<IMFActivate> activate; + RETURN_IF_FAILED(MakeAndInitialize<WMFClearKeyActivate>(&activate)); + *aContentEnablerActivate = activate.Detach(); + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::GetPolicy( + MFPOLICYMANAGER_ACTION aAction, IMFOutputPolicy** aPolicy) { + ENTRY_LOG(); + // For testing purpose, we don't need to set the output policy/ + *aPolicy = nullptr; + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::BindAccess( + MFINPUTTRUSTAUTHORITY_ACCESS_PARAMS* aParams) { + ENTRY_LOG(); + if (aParams == nullptr || aParams->dwVer != 0) { + return E_INVALIDARG; + } + for (DWORD i = 0; i < aParams->cActions; ++i) { + MFPOLICYMANAGER_ACTION action = aParams->rgOutputActions[i].Action; + if (action != PEACTION_PLAY && action != PEACTION_EXTRACT && + action != PEACTION_NO) { + ENTRY_LOG_ARGS("Unexpected action!"); + return MF_E_UNEXPECTED; + } + } + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::UpdateAccess( + MFINPUTTRUSTAUTHORITY_ACCESS_PARAMS* aParams) { + ENTRY_LOG(); + return BindAccess(aParams); +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::Reset() { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::GetShutdownStatus( + MFSHUTDOWN_STATUS* aStatus) { + ENTRY_LOG(); + // https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfshutdown-getshutdownstatus#return-value + if (mSessionManager->IsShutdown()) { + return MF_E_INVALIDREQUEST; + } + return S_OK; +} + +STDMETHODIMP WMFClearKeyInputTrustAuthority::Shutdown() { + ENTRY_LOG(); + if (mSessionManager->IsShutdown()) { + return MF_E_SHUTDOWN; + } + mSessionManager->Shutdown(); + return S_OK; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.h b/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.h new file mode 100644 index 0000000000..9eb2beea87 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyInputTrustAuthority.h @@ -0,0 +1,56 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYINPUTTRUSTAUTHORITY_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYINPUTTRUSTAUTHORITY_H + +#include <wrl.h> +#include <wrl/client.h> + +#include "MFCDMExtra.h" +#include "RefCounted.h" + +namespace mozilla { + +class SessionManagerWrapper; + +// This class is used to provide WMFClearKeyActivate and WMFClearKeyDecryptor. +class WMFClearKeyInputTrustAuthority final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFInputTrustAuthority, IMFShutdown, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyInputTrustAuthority() = default; + ~WMFClearKeyInputTrustAuthority() = default; + WMFClearKeyInputTrustAuthority(const WMFClearKeyInputTrustAuthority&) = + delete; + WMFClearKeyInputTrustAuthority& operator=( + const WMFClearKeyInputTrustAuthority&) = delete; + + HRESULT RuntimeClassInitialize(UINT32 aStreamId, + SessionManagerWrapper* aSessionManager); + + // IMFInputTrustAuthority + STDMETHODIMP GetDecrypter(REFIID aRiid, void** aPpv) override; + STDMETHODIMP RequestAccess(MFPOLICYMANAGER_ACTION aAction, + IMFActivate** aContentEnablerActivate) override; + STDMETHODIMP GetPolicy(MFPOLICYMANAGER_ACTION aAction, + IMFOutputPolicy** aPolicy) override; + STDMETHODIMP BindAccess( + MFINPUTTRUSTAUTHORITY_ACCESS_PARAMS* aParams) override; + STDMETHODIMP UpdateAccess( + MFINPUTTRUSTAUTHORITY_ACCESS_PARAMS* aParams) override; + STDMETHODIMP Reset() override; + + // IMFShutdown + STDMETHODIMP GetShutdownStatus(MFSHUTDOWN_STATUS* aStatus) override; + STDMETHODIMP Shutdown() override; + + private: + RefPtr<SessionManagerWrapper> mSessionManager; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYINPUTTRUSTAUTHORITY_H diff --git a/media/wmf-clearkey/WMFClearKeyOutputPolicy.cpp b/media/wmf-clearkey/WMFClearKeyOutputPolicy.cpp new file mode 100644 index 0000000000..b992d931dd --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyOutputPolicy.cpp @@ -0,0 +1,203 @@ +/* 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 "WMFClearKeyOutputPolicy.h" + +#include <mfapi.h> +#include <mferror.h> + +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +HRESULT WMFClearKeyOutputPolicy::RuntimeClassInitialize( + MFPOLICYMANAGER_ACTION aAction) { + ENTRY_LOG_ARGS("aAction=%d", aAction); + if (aAction != PEACTION_PLAY && aAction != PEACTION_EXTRACT && + aAction != PEACTION_NO) { + return MF_E_UNEXPECTED; + } + return S_OK; +} + +// IMFOutputPolicy +STDMETHODIMP WMFClearKeyOutputPolicy::GenerateRequiredSchemas( + DWORD aAttributes, GUID aGuidOutputSubtype, + GUID* aGuidProtectionSchemasSupported, + DWORD aProtectionSchemasSupportedCount, + IMFCollection** aRequiredProtectionSchemas) { + ENTRY_LOG(); + // We don't require an OTA (output trust authority) to enforce for testing. + // However, we still need to return an empty collection on success. + ComPtr<IMFCollection> collection; + RETURN_IF_FAILED(MFCreateCollection(&collection)); + *aRequiredProtectionSchemas = collection.Detach(); + return S_OK; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetOriginatorID(GUID* aGuidOriginatorId) { + ENTRY_LOG(); + *aGuidOriginatorId = GUID_NULL; + return S_OK; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetMinimumGRLVersion( + DWORD* aMinimumGrlVersion) { + ENTRY_LOG(); + *aMinimumGrlVersion = 0; + return S_OK; +} + +// IMFAttributes inherited by IMFOutputPolicy +STDMETHODIMP WMFClearKeyOutputPolicy::GetItem(REFGUID aGuidKey, + PROPVARIANT* aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetItemType(REFGUID aGuidKey, + MF_ATTRIBUTE_TYPE* aType) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::CompareItem(REFGUID aGuidKey, + REFPROPVARIANT aValue, + BOOL* aResult) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::Compare(IMFAttributes* aAttributes, + MF_ATTRIBUTES_MATCH_TYPE aType, + BOOL* aResult) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetUINT32(REFGUID aGuidKey, + UINT32* aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetUINT64(REFGUID aGuidKey, + UINT64* aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetDouble(REFGUID aGuidKey, + double* aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetGUID(REFGUID aGuidKey, + GUID* aGuidValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetStringLength(REFGUID aGuidKey, + UINT32* aPcchLength) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetString(REFGUID aGuidKey, + LPWSTR aPwszValue, + UINT32 aCchBufSize, + UINT32* aPcchLength) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetAllocatedString(REFGUID aGuidKey, + LPWSTR* aPpwszValue, + UINT32* aPcchLength) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetBlobSize(REFGUID aGuidKey, + UINT32* aPcbBlobSize) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetBlob(REFGUID aGuidKey, UINT8* pBuf, + UINT32 aCbBufSize, + UINT32* aPcbBlobSize) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetAllocatedBlob(REFGUID aGuidKey, + UINT8** aBuf, + UINT32* aPcbSize) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetUnknown(REFGUID aGuidKey, REFIID aRiid, + LPVOID* aPpv) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetItem(REFGUID aGuidKey, + REFPROPVARIANT aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::DeleteItem(REFGUID aGuidKey) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::DeleteAllItems() { return E_NOTIMPL; } + +STDMETHODIMP WMFClearKeyOutputPolicy::SetUINT32(REFGUID aGuidKey, + UINT32 aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetUINT64(REFGUID aGuidKey, + UINT64 aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetDouble(REFGUID aGuidKey, + double aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetGUID(REFGUID aGuidKey, + REFGUID aGuidValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetString(REFGUID aGuidKey, + LPCWSTR aWszValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetBlob(REFGUID aGuidKey, + const UINT8* aBuf, + UINT32 aCbBufSize) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::SetUnknown(REFGUID aGuidKey, + IUnknown* aUnknown) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::LockStore() { return E_NOTIMPL; } + +STDMETHODIMP WMFClearKeyOutputPolicy::UnlockStore() { return E_NOTIMPL; } + +STDMETHODIMP WMFClearKeyOutputPolicy::GetCount(UINT32* aPcItems) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::GetItemByIndex(UINT32 aIndex, + GUID* aGuidKey, + PROPVARIANT* aValue) { + return E_NOTIMPL; +} + +STDMETHODIMP WMFClearKeyOutputPolicy::CopyAllItems(IMFAttributes* aDest) { + return E_NOTIMPL; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyOutputPolicy.h b/media/wmf-clearkey/WMFClearKeyOutputPolicy.h new file mode 100644 index 0000000000..b53f77a88d --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyOutputPolicy.h @@ -0,0 +1,81 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYOUTPUTPOLICY_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYOUTPUTPOLICY_H + +#include <wrl.h> +#include <wrl/client.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// This class is used to return output protection scheme. +class WMFClearKeyOutputPolicy final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFOutputPolicy, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyOutputPolicy() = default; + ~WMFClearKeyOutputPolicy() = default; + WMFClearKeyOutputPolicy(const WMFClearKeyOutputPolicy&) = delete; + WMFClearKeyOutputPolicy& operator=(const WMFClearKeyOutputPolicy&) = delete; + + HRESULT RuntimeClassInitialize(MFPOLICYMANAGER_ACTION aAction); + + // IMFOutputPolicy + STDMETHODIMP GenerateRequiredSchemas( + DWORD aAttributes, GUID aGuidOutputSubtype, + GUID* aGuidProtectionSchemasSupported, + DWORD aProtectionSchemasSupportedCount, + IMFCollection** aRequiredProtectionSchemas) override; + STDMETHODIMP GetOriginatorID(_Out_ GUID* aGuidOriginatorId) override; + STDMETHODIMP GetMinimumGRLVersion(_Out_ DWORD* aMinimumGrlVersion) override; + + // IMFAttributes inherited by IMFOutputPolicy + STDMETHODIMP GetItem(REFGUID aGuidKey, PROPVARIANT* aValue) override; + STDMETHODIMP GetItemType(REFGUID aGuidKey, MF_ATTRIBUTE_TYPE* aType) override; + STDMETHODIMP CompareItem(REFGUID aGuidKey, REFPROPVARIANT aValue, + BOOL* aResult) override; + STDMETHODIMP Compare(IMFAttributes* aAttributes, + MF_ATTRIBUTES_MATCH_TYPE aType, BOOL* aResult) override; + STDMETHODIMP GetUINT32(REFGUID aGuidKey, UINT32* aValue) override; + STDMETHODIMP GetUINT64(REFGUID aGuidKey, UINT64* aValue) override; + STDMETHODIMP GetDouble(REFGUID aGuidKey, double* aValue) override; + STDMETHODIMP GetGUID(REFGUID aGuidKey, GUID* aGuidValue) override; + STDMETHODIMP GetStringLength(REFGUID aGuidKey, UINT32* aPcchLength) override; + STDMETHODIMP GetString(REFGUID aGuidKey, LPWSTR aPwszValue, + UINT32 aCchBufSize, UINT32* aPcchLength) override; + STDMETHODIMP GetAllocatedString(REFGUID aGuidKey, LPWSTR* aPpwszValue, + UINT32* aPcchLength) override; + STDMETHODIMP GetBlobSize(REFGUID aGuidKey, UINT32* aPcbBlobSize) override; + STDMETHODIMP GetBlob(REFGUID aGuidKey, UINT8* aBuf, UINT32 aCbBufSize, + UINT32* aPcbBlobSize) override; + STDMETHODIMP GetAllocatedBlob(REFGUID aGuidKey, UINT8** aBuf, + UINT32* aPcbSize) override; + STDMETHODIMP GetUnknown(REFGUID aGuidKey, REFIID aRiid, + LPVOID* aPpv) override; + STDMETHODIMP SetItem(REFGUID aGuidKey, REFPROPVARIANT aValue) override; + STDMETHODIMP DeleteItem(REFGUID aGuidKey) override; + STDMETHODIMP DeleteAllItems() override; + STDMETHODIMP SetUINT32(REFGUID aGuidKey, UINT32 aValue) override; + STDMETHODIMP SetUINT64(REFGUID aGuidKey, UINT64 aValue) override; + STDMETHODIMP SetDouble(REFGUID aGuidKey, double aValue) override; + STDMETHODIMP SetGUID(REFGUID aGuidKey, REFGUID aGuidValue) override; + STDMETHODIMP SetString(REFGUID aGuidKey, LPCWSTR aWszValue) override; + STDMETHODIMP SetBlob(REFGUID aGuidKey, const UINT8* aBuf, + UINT32 aCbBufSize) override; + STDMETHODIMP SetUnknown(REFGUID aGuidKey, IUnknown* aUnknown) override; + STDMETHODIMP LockStore() override; + STDMETHODIMP UnlockStore() override; + STDMETHODIMP GetCount(UINT32* aPcItems) override; + STDMETHODIMP GetItemByIndex(UINT32 aIndex, GUID* aGuidKey, + PROPVARIANT* aValue) override; + STDMETHODIMP CopyAllItems(IMFAttributes* aDest) override; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYOUTPUTPOLICY_H diff --git a/media/wmf-clearkey/WMFClearKeySession.cpp b/media/wmf-clearkey/WMFClearKeySession.cpp new file mode 100644 index 0000000000..077665a8ae --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeySession.cpp @@ -0,0 +1,233 @@ +/* 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 "WMFClearKeySession.h" + +#include <codecvt> +#include <cmath> +#include <Mferror.h> +#include <winerror.h> + +#include "WMFClearKeyCDM.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; + +static cdm::SessionType ToCdmSessionType(MF_MEDIAKEYSESSION_TYPE aSessionType) { + switch (aSessionType) { + case MF_MEDIAKEYSESSION_TYPE_TEMPORARY: + return cdm::SessionType::kTemporary; + default: + MOZ_ASSERT_UNREACHABLE("Only support temporary type for testing"); + return cdm::SessionType::kTemporary; + } +} + +static MF_MEDIAKEY_STATUS ToMFKeyStatus(cdm::KeyStatus aStatus) { + switch (aStatus) { + case cdm::KeyStatus::kUsable: + return MF_MEDIAKEY_STATUS_USABLE; + case cdm::KeyStatus::kExpired: + return MF_MEDIAKEY_STATUS_EXPIRED; + case cdm::KeyStatus::kOutputDownscaled: + return MF_MEDIAKEY_STATUS_OUTPUT_DOWNSCALED; + case cdm::KeyStatus::kStatusPending: + return MF_MEDIAKEY_STATUS_STATUS_PENDING; + case cdm::KeyStatus::kInternalError: + return MF_MEDIAKEY_STATUS_INTERNAL_ERROR; + case cdm::KeyStatus::kReleased: + return MF_MEDIAKEY_STATUS_RELEASED; + case cdm::KeyStatus::kOutputRestricted: + return MF_MEDIAKEY_STATUS_OUTPUT_RESTRICTED; + } +} + +HRESULT WMFClearKeySession::RuntimeClassInitialize( + MF_MEDIAKEYSESSION_TYPE aSessionType, + IMFContentDecryptionModuleSessionCallbacks* aCallbacks, + SessionManagerWrapper* aManager) { + ENTRY_LOG(); + MOZ_ASSERT(aCallbacks); + MOZ_ASSERT(aManager); + mSessionType = ToCdmSessionType(aSessionType); + mCallbacks = aCallbacks; + mSessionManager = aManager; + return S_OK; +} + +WMFClearKeySession::~WMFClearKeySession() { ENTRY_LOG(); } + +STDMETHODIMP WMFClearKeySession::GenerateRequest(LPCWSTR aInitDataType, + const BYTE* aInitData, + DWORD aInitDataSize) { + if (!mSessionManager) { + return MF_E_SHUTDOWN; + } + ENTRY_LOG_ARGS("init-type=%ls", aInitDataType); + cdm::InitDataType initType; + if (wcscmp(aInitDataType, L"cenc") == 0) { + initType = cdm::InitDataType::kCenc; + } else if (wcscmp(aInitDataType, L"keyids") == 0) { + initType = cdm::InitDataType::kKeyIds; + } else if (wcscmp(aInitDataType, L"webm") == 0) { + initType = cdm::InitDataType::kWebM; + } else { + return MF_NOT_SUPPORTED_ERR; + } + RETURN_IF_FAILED(mSessionManager->GenerateRequest( + initType, aInitData, aInitDataSize, mSessionType, this, mSessionId)); + MOZ_ASSERT(!mSessionId.empty()); + ENTRY_LOG_ARGS("created session, sid=%s", mSessionId.c_str()); + return S_OK; +} + +STDMETHODIMP WMFClearKeySession::Load(LPCWSTR session_id, BOOL* loaded) { + ENTRY_LOG(); + // Per step 5, Load can only be called on persistent session, but we only + // support temporary session for MF clearkey due to testing reason. + // https://rawgit.com/w3c/encrypted-media/V1/index.html#dom-mediakeysession-load + return MF_E_NOT_AVAILABLE; +} + +STDMETHODIMP WMFClearKeySession::Update(const BYTE* aResponse, + DWORD aResponseSize) { + ENTRY_LOG(); + if (!mSessionManager) { + return MF_E_SHUTDOWN; + } + RETURN_IF_FAILED( + mSessionManager->UpdateSession(mSessionId, aResponse, aResponseSize)); + return S_OK; +} + +STDMETHODIMP WMFClearKeySession::Close() { + ENTRY_LOG(); + // It has been shutdowned and sesssion has been closed. + if (!mSessionManager) { + return S_OK; + } + RETURN_IF_FAILED(mSessionManager->CloseSession(mSessionId)); + return S_OK; +} + +STDMETHODIMP WMFClearKeySession::Remove() { + ENTRY_LOG(); + // It has been shutdowned and sesssion has been removed. + if (!mSessionManager) { + return S_OK; + } + RETURN_IF_FAILED(mSessionManager->RemoveSession(mSessionId)); + return S_OK; +} + +STDMETHODIMP WMFClearKeySession::GetSessionId(LPWSTR* aSessionId) { + ENTRY_LOG(); + if (!mSessionManager) { + return S_OK; + } + if (mSessionId.empty()) { + *aSessionId = (LPWSTR)CoTaskMemAlloc(sizeof(wchar_t)); + if (*aSessionId != NULL) { + **aSessionId = L'\0'; + return S_OK; + } else { + return E_OUTOFMEMORY; + } + } + + std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; + std::wstring wideStr = converter.from_bytes(mSessionId); + *aSessionId = + (LPWSTR)CoTaskMemAlloc((wideStr.length() + 1) * sizeof(wchar_t)); + if (*aSessionId != NULL) { + wcscpy_s(*aSessionId, wideStr.length() + 1, wideStr.c_str()); + return S_OK; + } else { + return E_OUTOFMEMORY; + } +} + +STDMETHODIMP WMFClearKeySession::GetExpiration(double* expiration) { + ENTRY_LOG(); + // This is used for testing, never expires. + *expiration = std::nan("1"); + return S_OK; +} + +STDMETHODIMP WMFClearKeySession::GetKeyStatuses(MFMediaKeyStatus** aKeyStatuses, + UINT* aKeyStatusesCount) { + ENTRY_LOG(); + *aKeyStatuses = nullptr; + *aKeyStatusesCount = 0; + + const auto keyStatusCount = mKeyInfo.size(); + if (mSessionId.empty() || keyStatusCount == 0) { + ENTRY_LOG_ARGS("No session ID or no key info!"); + return S_OK; + } + + MFMediaKeyStatus* keyStatusArray = nullptr; + keyStatusArray = static_cast<MFMediaKeyStatus*>( + CoTaskMemAlloc(keyStatusCount * sizeof(MFMediaKeyStatus))); + if (!keyStatusArray) { + ENTRY_LOG_ARGS("OOM when alloacting keyStatusArray!"); + return E_OUTOFMEMORY; + } + ZeroMemory(keyStatusArray, keyStatusCount * sizeof(MFMediaKeyStatus)); + for (UINT idx = 0; idx < keyStatusCount; idx++) { + keyStatusArray[idx].cbKeyId = mKeyInfo[idx].mKeyId.size(); + keyStatusArray[idx].pbKeyId = + static_cast<BYTE*>(CoTaskMemAlloc(sizeof(mKeyInfo[idx].mKeyId.size()))); + if (keyStatusArray[idx].pbKeyId == nullptr) { + ENTRY_LOG_ARGS("OOM when alloacting keyStatusArray's pbKeyId!"); + return E_OUTOFMEMORY; + } + + keyStatusArray[idx].eMediaKeyStatus = + ToMFKeyStatus(mKeyInfo[idx].mKeyStatus); + memcpy(keyStatusArray[idx].pbKeyId, mKeyInfo[idx].mKeyId.data(), + mKeyInfo[idx].mKeyId.size()); + } + + *aKeyStatuses = keyStatusArray; + *aKeyStatusesCount = keyStatusCount; + ENTRY_LOG_ARGS("return key status!"); + return S_OK; +} + +void WMFClearKeySession::OnKeyMessage( + MF_MEDIAKEYSESSION_MESSAGETYPE aMessageType, const BYTE* aMessage, + DWORD aMessageSize) { + ENTRY_LOG_ARGS("aMessageSize=%lu", aMessageSize); + if (!mSessionManager) { + return; + } + mCallbacks->KeyMessage(aMessageType, aMessage, aMessageSize, nullptr); +} + +void WMFClearKeySession::OnKeyStatusChanged( + const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount) { + ENTRY_LOG_ARGS("aKeysInfoCount=%u", aKeysInfoCount); + if (!mSessionManager) { + return; + } + mKeyInfo.clear(); + for (uint32_t idx = 0; idx < aKeysInfoCount; idx++) { + const cdm::KeyInformation& key = aKeysInfo[idx]; + mKeyInfo.push_back(KeyInformation{key.key_id, key.key_id_size, key.status}); + ENTRY_LOG_ARGS("idx=%u, keySize=%u, status=%u", idx, key.key_id_size, + key.status); + } + mCallbacks->KeyStatusChanged(); +} + +void WMFClearKeySession::Shutdown() { + ENTRY_LOG(); + mCallbacks = nullptr; + mSessionManager = nullptr; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeySession.h b/media/wmf-clearkey/WMFClearKeySession.h new file mode 100644 index 0000000000..d2032cccef --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeySession.h @@ -0,0 +1,78 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYSESSION_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYSESSION_H + +#include <mfidl.h> +#include <windows.h> +#include <wrl.h> + +#include "content_decryption_module.h" +#include "MFCDMExtra.h" +#include "RefCounted.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +class SessionManagerWrapper; + +// This is the implementation for EME API's MediaKeySession. +// TODO : add a way to assert thread usage. It would only be used on the media +// supervisor thread pool. +class WMFClearKeySession final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IMFContentDecryptionModuleSession, Microsoft::WRL::FtmBase> { + public: + WMFClearKeySession() = default; + ~WMFClearKeySession(); + WMFClearKeySession(const WMFClearKeySession&) = delete; + WMFClearKeySession& operator=(const WMFClearKeySession&) = delete; + + HRESULT RuntimeClassInitialize( + MF_MEDIAKEYSESSION_TYPE aSessionType, + IMFContentDecryptionModuleSessionCallbacks* aCallbacks, + SessionManagerWrapper* aManager); + + void Shutdown(); + + // IMFContentDecryptionModuleSession + STDMETHODIMP GenerateRequest(LPCWSTR init_data_type, + + const BYTE* init_data, + DWORD init_data_size) override; + STDMETHODIMP Load(LPCWSTR session_id, BOOL* loaded) override; + STDMETHODIMP Update(const BYTE* aResponse, DWORD aResponseSize) override; + STDMETHODIMP Close() override; + STDMETHODIMP Remove() override; + STDMETHODIMP GetSessionId(LPWSTR* aSessionId) override; + STDMETHODIMP GetExpiration(double* aExpiration) override; + STDMETHODIMP GetKeyStatuses(MFMediaKeyStatus** aKeyStatuses, + UINT* aKeyStatusesCount) override; + + void OnKeyMessage(MF_MEDIAKEYSESSION_MESSAGETYPE aMessageType, + const BYTE* aMessage, DWORD aMessageSize); + void OnKeyStatusChanged(const cdm::KeyInformation* aKeysInfo, + uint32_t aKeysInfoCount); + + private: + cdm::SessionType mSessionType; + std::string mSessionId; + Microsoft::WRL::ComPtr<IMFContentDecryptionModuleSessionCallbacks> mCallbacks; + RefPtr<SessionManagerWrapper> mSessionManager; + + struct KeyInformation { + KeyInformation(const uint8_t* aKeyId, uint32_t aKeyIdSize, + cdm::KeyStatus aKeyStatus) + : mKeyId(aKeyId, aKeyId + aKeyIdSize), mKeyStatus(aKeyStatus) {} + std::vector<uint8_t> mKeyId; + cdm::KeyStatus mKeyStatus; + }; + std::vector<KeyInformation> mKeyInfo; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYSESSION_H diff --git a/media/wmf-clearkey/WMFClearKeyTrustedInput.cpp b/media/wmf-clearkey/WMFClearKeyTrustedInput.cpp new file mode 100644 index 0000000000..37ce108886 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyTrustedInput.cpp @@ -0,0 +1,35 @@ +/* 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 "WMFClearKeyTrustedInput.h" + +#include "WMFClearKeyCDM.h" +#include "WMFClearKeyInputTrustAuthority.h" +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +HRESULT WMFClearKeyTrustedInput::RuntimeClassInitialize( + SessionManagerWrapper* aSessionManager) { + ENTRY_LOG(); + mSessionManager = aSessionManager; + return S_OK; +} + +// IMFTrustedInput +STDMETHODIMP WMFClearKeyTrustedInput::GetInputTrustAuthority( + DWORD aStreamId, REFIID aRiid, IUnknown** aAuthority) { + ENTRY_LOG(); + ComPtr<IMFInputTrustAuthority> ita; + RETURN_IF_FAILED(( + MakeAndInitialize<WMFClearKeyInputTrustAuthority, IMFInputTrustAuthority>( + &ita, aStreamId, mSessionManager))); + *aAuthority = ita.Detach(); + return S_OK; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFClearKeyTrustedInput.h b/media/wmf-clearkey/WMFClearKeyTrustedInput.h new file mode 100644 index 0000000000..b89c64f5c9 --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyTrustedInput.h @@ -0,0 +1,41 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYTRUSTEDINPUT_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYTRUSTEDINPUT_H + +#include <wrl.h> +#include <wrl/client.h> + +#include "MFCDMExtra.h" +#include "RefCounted.h" + +namespace mozilla { + +class SessionManagerWrapper; + +// This class is used to provide WMFClearKeyInputTrustAuthority. +class WMFClearKeyTrustedInput final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, + IMFTrustedInput, Microsoft::WRL::FtmBase> { + public: + WMFClearKeyTrustedInput() = default; + ~WMFClearKeyTrustedInput() = default; + WMFClearKeyTrustedInput(const WMFClearKeyTrustedInput&) = delete; + WMFClearKeyTrustedInput& operator=(const WMFClearKeyTrustedInput&) = delete; + + HRESULT RuntimeClassInitialize(SessionManagerWrapper* aSessionManager); + + // IMFTrustedInput + STDMETHODIMP GetInputTrustAuthority(DWORD aStreamId, REFIID aRiid, + IUnknown** aAuthority) override; + + private: + RefPtr<SessionManagerWrapper> mSessionManager; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYTRUSTEDINPUT_H diff --git a/media/wmf-clearkey/WMFClearKeyUtils.h b/media/wmf-clearkey/WMFClearKeyUtils.h new file mode 100644 index 0000000000..e834e6cb9a --- /dev/null +++ b/media/wmf-clearkey/WMFClearKeyUtils.h @@ -0,0 +1,229 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMUTILS_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMUTILS_H + +#include <initguid.h> +#include <memory> +#include <mfidl.h> +#include <mutex> +#include <thread> +#include <windows.h> +#include <wrl.h> +#include <sstream> +#include <stdio.h> + +#include "MFCDMExtra.h" +#include "mozilla/Assertions.h" +#include "mozilla/Unused.h" + +namespace mozilla { + +inline constexpr WCHAR kCLEARKEY_SYSTEM_NAME[] = L"org.w3.clearkey"; + +#define WMF_CLEARKEY_DEBUG 0 + +#ifdef WMF_CLEARKEY_DEBUG +# define LOG(msg, ...) \ + printf(("[Thread %lu]: D/WMFClearKey " msg "\n"), \ + static_cast<unsigned long>( \ + std::hash<std::thread::id>{}(std::this_thread::get_id())), \ + ##__VA_ARGS__) +#else +# define LOG(msg, ...) +#endif + +#define PRETTY_FUNC \ + ([&]() -> std::string { \ + std::string prettyFunction(__PRETTY_FUNCTION__); \ + std::size_t pos1 = prettyFunction.find("::"); \ + std::size_t pos2 = prettyFunction.find("(", pos1); \ + return prettyFunction.substr(0, pos2); \ + })() \ + .c_str() + +// We can't reuse the definition in the `MediaEngineUtils.h` due to the +// restriction of not being able to use XPCOM string in the external library. +#ifndef RETURN_IF_FAILED +# define RETURN_IF_FAILED(x) \ + do { \ + HRESULT rv = x; \ + if (FAILED(rv)) { \ + LOG("(" #x ") failed, rv=%lx", rv); \ + return rv; \ + } \ + } while (false) +#endif + +#ifndef NOT_IMPLEMENTED +# define NOT_IMPLEMENTED() \ + do { \ + LOG("WARNING : '%s' NOT IMPLEMENTED!", PRETTY_FUNC); \ + } while (0) +#endif + +#ifndef ENTRY_LOG +# define ENTRY_LOG(...) \ + do { \ + LOG("%s [%p]", PRETTY_FUNC, this); \ + } while (0) +# define ENTRY_LOG_ARGS(fmt, ...) \ + do { \ + LOG("%s [%p]: " fmt, PRETTY_FUNC, this, ##__VA_ARGS__); \ + (void)(0, ##__VA_ARGS__); \ + } while (0) +#endif + +// TODO : should we use Microsoft's definition or Chromium's defintion? + +// This is defined in Microsoft's sample code +// https://github.com/microsoft/media-foundation/blob/dc81175a3e893c7c58fcbf1a943ac342e39f172c/samples/storecdm/clearkeyStoreCDM/clearkeydll/guids.h#L17-L18C14 +// DEFINE_GUID(CLEARKEY_GUID_CLEARKEY_PROTECTION_SYSTEM_ID, 0x9b0ff3ca, 0x1378, +// 0x4f28, 0x96, 0x65, 0x18, 0x9e, 0x15, 0x30, 0x2a, 0x71); + +// Media Foundation Clear Key protection system ID from Chromium +// {E4E94971-696A-447E-96E4-93FDF3A57A7A} +// https://source.chromium.org/chromium/chromium/src/+/main:media/cdm/win/test/media_foundation_clear_key_guids.h;l=16-29?q=CLEARKEY_GUID_CLEARKEY_PROTECTION_SYSTEM_ID&ss=chromium%2Fchromium%2Fsrc +DEFINE_GUID(CLEARKEY_GUID_CLEARKEY_PROTECTION_SYSTEM_ID, 0xe4e94971, 0x696a, + 0x447e, 0x96, 0xe4, 0x93, 0xfd, 0xf3, 0xa5, 0x7a, 0x7a); + +// Media Foundation Clear Key content enabler type +// {C262FD73-2F13-41C2-94E7-4CAF087AE1D1} +DEFINE_GUID(MEDIA_FOUNDATION_CLEARKEY_GUID_CONTENT_ENABLER_TYPE, 0xc262fd73, + 0x2f13, 0x41c2, 0x94, 0xe7, 0x4c, 0xaf, 0x8, 0x7a, 0xe1, 0xd1); + +// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:media/cdm/win/test/media_foundation_clear_key_guids.h;l=46;drc=fcefb4563e8ea5b2036ecde882f4f228dd1e4338 +#define PLAYREADY_GUID_MEDIA_PROTECTION_SYSTEM_ID_STRING \ + L"{F4637010-03C3-42CD-B932-B48ADF3A6A54}" + +// A PROPVARIANT that is automatically initialized and cleared upon respective +// construction and destruction of this class. +class AutoPropVar final { + 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; +}; + +template <typename T> +class ScopedCoMem final { + public: + ScopedCoMem() : mPtr(nullptr) {} + + ~ScopedCoMem() { Reset(nullptr); } + + ScopedCoMem(const ScopedCoMem&) = delete; + ScopedCoMem& operator=(const ScopedCoMem&) = delete; + + T** operator&() { // NOLINT + MOZ_ASSERT(mPtr == nullptr); // To catch memory leaks. + return &mPtr; + } + + operator T*() { return mPtr; } + + T* operator->() { + MOZ_ASSERT(mPtr != nullptr); + return mPtr; + } + + const T* operator->() const { + MOZ_ASSERT(mPtr != nullptr); + return mPtr; + } + + explicit operator bool() const { return mPtr; } + + friend bool operator==(const ScopedCoMem& lhs, std::nullptr_t) { + return lhs.Get() == nullptr; + } + + friend bool operator==(std::nullptr_t, const ScopedCoMem& rhs) { + return rhs.Get() == nullptr; + } + + friend bool operator!=(const ScopedCoMem& lhs, std::nullptr_t) { + return lhs.Get() != nullptr; + } + + friend bool operator!=(std::nullptr_t, const ScopedCoMem& rhs) { + return rhs.Get() != nullptr; + } + + void Reset(T* ptr) { + if (mPtr) { + CoTaskMemFree(mPtr); + } + mPtr = ptr; + } + + T* Get() const { return mPtr; } + + private: + T* mPtr; +}; + +template <typename T> +class DataMutex final { + public: + DataMutex() = default; + + template <typename... Args> + explicit DataMutex(Args&&... args) : mData(std::forward<Args>(args)...) {} + + class AutoLock { + public: + AutoLock(std::unique_lock<std::mutex>&& lock, DataMutex<T>* mutex) + : mLock(std::move(lock)), mMutex(mutex) {} + T& operator*() { return ref(); } + T* operator->() { return &ref(); } + T& ref() const& { return mMutex->mData; } + operator T*() const& { return &ref(); } + ~AutoLock() { mMutex = nullptr; } + + private: + std::unique_lock<std::mutex> mLock; + DataMutex<T>* mMutex; + }; + + AutoLock Lock() { + return AutoLock(std::unique_lock<std::mutex>(mMutex), this); + } + + private: + std::mutex mMutex; + T mData; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFCLEARKEYCDMUTILS_H diff --git a/media/wmf-clearkey/WMFDecryptedBlock.h b/media/wmf-clearkey/WMFDecryptedBlock.h new file mode 100644 index 0000000000..1ffa41e7c9 --- /dev/null +++ b/media/wmf-clearkey/WMFDecryptedBlock.h @@ -0,0 +1,67 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFDECRYPTEDBLOCK_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFDECRYPTEDBLOCK_H + +#include <vector> + +#include "WMFClearKeyUtils.h" +#include "content_decryption_module.h" + +namespace mozilla { + +// This class is used to store the decrypted data. It will be allocated by +// `SessionManagerWrapper::Allocate`. +class WMFDecryptedBuffer : public cdm::Buffer { + public: + explicit WMFDecryptedBuffer(size_t aSize) { + SetSize(aSize); + LOG("WMFDecryptedBuffer ctor %p, sz=%u", this, Size()); + } + ~WMFDecryptedBuffer() override { + LOG("WMFDecryptedBuffer dtor %p, sz=%u", this, Size()); + } + WMFDecryptedBuffer(const WMFDecryptedBuffer&) = delete; + WMFDecryptedBuffer& operator=(const WMFDecryptedBuffer&) = delete; + + // This is not good, but that is how cdm::buffer works. + void Destroy() override { delete this; } + uint32_t Capacity() const override { return mBuffer.size(); } + uint8_t* Data() override { return mBuffer.data(); } + void SetSize(uint32_t aSize) override { return mBuffer.resize(aSize); } + uint32_t Size() const override { return mBuffer.size(); } + + private: + std::vector<uint8_t> mBuffer; +}; + +class WMFDecryptedBlock : public cdm::DecryptedBlock { + public: + WMFDecryptedBlock() : mBuffer(nullptr), mTimestamp(0) { + LOG("WMFDecryptedBlock ctor %p", this); + } + ~WMFDecryptedBlock() override { + LOG("WMFDecryptedBlock dtor %p", this); + if (mBuffer) { + mBuffer->Destroy(); + mBuffer = nullptr; + } + } + void SetDecryptedBuffer(cdm::Buffer* aBuffer) override { + LOG("WMFDecryptedBlock(%p)::SetDecryptedBuffer, buffer=%p", this, aBuffer); + mBuffer = aBuffer; + } + cdm::Buffer* DecryptedBuffer() override { return mBuffer; } + void SetTimestamp(int64_t aTimestamp) override { mTimestamp = aTimestamp; } + int64_t Timestamp() const override { return mTimestamp; } + + private: + cdm::Buffer* mBuffer; + int64_t mTimestamp; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFDECRYPTEDBLOCK_H diff --git a/media/wmf-clearkey/WMFPMPServer.cpp b/media/wmf-clearkey/WMFPMPServer.cpp new file mode 100644 index 0000000000..86036cc0c0 --- /dev/null +++ b/media/wmf-clearkey/WMFPMPServer.cpp @@ -0,0 +1,68 @@ +/* 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 "WMFPMPServer.h" + +#include <mfapi.h> +#include <mferror.h> + +#include "WMFClearKeyUtils.h" + +namespace mozilla { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::MakeAndInitialize; + +HRESULT WMFPMPServer::RuntimeClassInitialize( + ABI::Windows::Foundation::Collections::IPropertySet* aPropertyPmp) { + ENTRY_LOG(); + mPropertyPmp = aPropertyPmp; + RETURN_IF_FAILED(MFCreatePMPMediaSession(MFPMPSESSION_IN_PROCESS, nullptr, + &mMediaSession, nullptr)); + RETURN_IF_FAILED(MFGetService(mMediaSession.Get(), MF_PMP_SERVER_CONTEXT, + IID_PPV_ARGS(&mPmpServer))); + RETURN_IF_FAILED(MFGetService(mMediaSession.Get(), MF_PMP_SERVICE, + IID_PPV_ARGS(&mPmpHost))); + return S_OK; +} + +STDMETHODIMP WMFPMPServer::GetIids(ULONG* aIidCount, IID** aIids) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFPMPServer::GetRuntimeClassName( + _COM_Outptr_ HSTRING* aClassName) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP WMFPMPServer::GetTrustLevel(TrustLevel* aTrustLevel) { + NOT_IMPLEMENTED(); + return E_NOTIMPL; +} +STDMETHODIMP WMFPMPServer::get_Properties( + ABI::Windows::Foundation::Collections::IPropertySet** aPpProperties) { + ENTRY_LOG(); + RETURN_IF_FAILED(mPropertyPmp.CopyTo(aPpProperties)); + return S_OK; +} + +STDMETHODIMP WMFPMPServer::GetService(REFGUID aGuidService, REFIID aRiid, + LPVOID* aObject) { + ENTRY_LOG(); + if (!aObject) { + return E_POINTER; + } + if (aGuidService == MF_PMP_SERVER_CONTEXT) { + RETURN_IF_FAILED(mPmpServer.CopyTo(aRiid, aObject)); + } else if (aGuidService == MF_PMP_SERVICE && aRiid == IID_IMFPMPHost) { + RETURN_IF_FAILED(mPmpHost.CopyTo(aRiid, aObject)); + } else { + RETURN_IF_FAILED(MF_E_UNSUPPORTED_SERVICE); + } + return S_OK; +} + +} // namespace mozilla diff --git a/media/wmf-clearkey/WMFPMPServer.h b/media/wmf-clearkey/WMFPMPServer.h new file mode 100644 index 0000000000..15d9116bac --- /dev/null +++ b/media/wmf-clearkey/WMFPMPServer.h @@ -0,0 +1,57 @@ +/* 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_PLATFORM_WMF_CLEARKEY_WMFPMPSERVER_H +#define DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFPMPSERVER_H + +#include <mfidl.h> +#include <windows.h> +#include <windows.media.protection.h> +#include <wrl.h> + +#include "MFCDMExtra.h" + +namespace mozilla { + +// A mock PMP server in order to allow media engine to create in-process PMP +// server. +class WMFPMPServer final + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + ABI::Windows::Media::Protection::IMediaProtectionPMPServer, + IMFGetService, Microsoft::WRL::FtmBase> { + public: + WMFPMPServer() = default; + ~WMFPMPServer() = default; + WMFPMPServer(const WMFPMPServer&) = delete; + WMFPMPServer& operator=(const WMFPMPServer&) = delete; + + HRESULT RuntimeClassInitialize( + ABI::Windows::Foundation::Collections::IPropertySet* aPropertyPmp); + + // IInspectable + STDMETHODIMP GetIids(ULONG* aIidCount, IID** aIids) override; + STDMETHODIMP GetRuntimeClassName(HSTRING* aClassName) override; + STDMETHODIMP GetTrustLevel(TrustLevel* aTrustLevel) override; + + // IMediaProtectionPMPServer + STDMETHODIMP get_Properties( + ABI::Windows::Foundation::Collections::IPropertySet** aPpProperties) + override; + + // IMFGetService + STDMETHODIMP GetService(REFGUID aGuidService, REFIID aRiid, + LPVOID* aObject) override; + + private: + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IPropertySet> + mPropertyPmp; + Microsoft::WRL::ComPtr<IMFPMPServer> mPmpServer; + Microsoft::WRL::ComPtr<IMFPMPHost> mPmpHost; + Microsoft::WRL::ComPtr<IMFMediaSession> mMediaSession; +}; + +} // namespace mozilla + +#endif // DOM_MEDIA_PLATFORM_WMF_CLEARKEY_WMFPMPSERVER_H diff --git a/media/wmf-clearkey/dllmain.cpp b/media/wmf-clearkey/dllmain.cpp new file mode 100644 index 0000000000..6b5da38ae5 --- /dev/null +++ b/media/wmf-clearkey/dllmain.cpp @@ -0,0 +1,37 @@ +/* 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 <windows.h> +#include <wrl.h> + +using namespace Microsoft::WRL; + +BOOL WINAPI DllMain(_In_opt_ HINSTANCE aInstance, _In_ DWORD aReason, + _In_opt_ LPVOID aReserved) { + if (DLL_PROCESS_ATTACH == aReason) { + DisableThreadLibraryCalls(aInstance); + Module<InProc>::GetModule().Create(); + } else if (DLL_PROCESS_DETACH == aReason) { + Module<InProc>::GetModule().Terminate(); + } + return TRUE; +} + +HRESULT WINAPI +DllGetActivationFactory(_In_ HSTRING aActivatibleClassId, + _COM_Outptr_ IActivationFactory** aFactory) { + auto& module = Module<InProc>::GetModule(); + return module.GetActivationFactory(aActivatibleClassId, aFactory); +} + +HRESULT WINAPI DllCanUnloadNow() { + auto& module = Module<InProc>::GetModule(); + return (module.Terminate()) ? S_OK : S_FALSE; +} + +STDAPI DllGetClassObject(_In_ REFCLSID aRclsid, _In_ REFIID aRiid, + _COM_Outptr_ LPVOID FAR* aPpv) { + auto& module = Module<InProc>::GetModule(); + return module.GetClassObject(aRclsid, aRiid, aPpv); +} diff --git a/media/wmf-clearkey/moz.build b/media/wmf-clearkey/moz.build new file mode 100644 index 0000000000..74680a50e0 --- /dev/null +++ b/media/wmf-clearkey/moz.build @@ -0,0 +1,43 @@ +# 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/. + +DEFINES["CDM_IMPLEMENTATION"] = True + +SOURCES += [ + "dllmain.cpp", + "WMFClearKeyActivate.cpp", + "WMFClearKeyCDM.cpp", + "WMFClearKeyCDMAccess.cpp", + "WMFClearKeyCDMFactory.cpp", + "WMFClearKeyContentEnabler.cpp", + # TODO : enable decryptor in bug 1870722. + # "WMFClearKeyDecryptor.cpp", + "WMFClearKeyInputTrustAuthority.cpp", + "WMFClearKeyOutputPolicy.cpp", + "WMFClearKeySession.cpp", + "WMFClearKeyTrustedInput.cpp", + "WMFPMPServer.cpp", +] + +OS_LIBS += [ + "mf", + "mfplat", + "mfuuid", + "oleaut32", + "propsys", + "runtimeobject", +] + +USE_LIBS += ["gecko-clearkey"] + +DEFFILE = "WMFClearKey.def" + +# Suppress warnings in platform headers, eg. wrl/module.h +if CONFIG["CC_TYPE"] == "clang-cl": + CXXFLAGS += [ + "-Wno-missing-braces", + "-Wunused-result", + ] + +GeckoSharedLibrary("wmfclearkey", linkage=None) |