From 205885a670e17a8686f0e3fe462b5ecc1f644439 Mon Sep 17 00:00:00 2001 From: Roman Ryltsov Date: Sat, 28 Oct 2023 12:53:04 +0200 Subject: [PATCH 1/3] Some code rearrangement, w/o addressing functional changes --- samples/MediaEngineEMEUWPSample/src/App.cpp | 341 +++++---- .../MediaEngineEMEUWPSample/src/EmeFactory.h | 671 ++++++++++++++++++ .../src/MediaEngineEMEUWPSample.vcxproj | 32 +- .../MediaEngineEMEUWPSample.vcxproj.filters | 70 +- .../src/MediaEnginePlayer.h | 497 +++++++++++++ .../src/{media => }/MediaFoundationHelpers.h | 0 samples/MediaEngineEMEUWPSample/src/README.md | 103 +++ .../MediaEngineEMEUWPSample/src/TestContent.h | 19 - .../src/media/CdmMediaSource.cpp | 88 --- .../src/media/CdmMediaSource.h | 48 -- .../src/media/MediaEngineExtension.cpp | 93 --- .../src/media/MediaEngineExtension.h | 44 -- .../MediaEngineProtectedContentInterfaces.h | 93 --- .../media/MediaEngineProtectionManager.cpp | 112 --- .../src/media/MediaEngineProtectionManager.h | 44 -- .../src/media/MediaEngineWrapper.cpp | 336 --------- .../src/media/MediaEngineWrapper.h | 92 --- .../media/eme/CdmConfigurationProperties.cpp | 46 -- .../media/eme/CdmConfigurationProperties.h | 26 - .../src/media/eme/EmeFactory.cpp | 53 -- .../src/media/eme/EmeFactory.h | 34 - .../src/media/eme/MediaKeySession.cpp | 154 ---- .../src/media/eme/MediaKeySession.h | 86 --- .../src/media/eme/MediaKeySystemAccess.cpp | 43 -- .../src/media/eme/MediaKeySystemAccess.h | 31 - .../media/eme/MediaKeySystemConfiguration.cpp | 191 ----- .../media/eme/MediaKeySystemConfiguration.h | 67 -- .../src/media/eme/MediaKeys.cpp | 30 - .../src/media/eme/MediaKeys.h | 31 - samples/MediaEngineEMEUWPSample/src/pch.h | 110 ++- 30 files changed, 1531 insertions(+), 2054 deletions(-) create mode 100644 samples/MediaEngineEMEUWPSample/src/EmeFactory.h create mode 100644 samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h rename samples/MediaEngineEMEUWPSample/src/{media => }/MediaFoundationHelpers.h (100%) create mode 100644 samples/MediaEngineEMEUWPSample/src/README.md delete mode 100644 samples/MediaEngineEMEUWPSample/src/TestContent.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectedContentInterfaces.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.h delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.cpp delete mode 100644 samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.h diff --git a/samples/MediaEngineEMEUWPSample/src/App.cpp b/samples/MediaEngineEMEUWPSample/src/App.cpp index 99117fd..a37ef7e 100644 --- a/samples/MediaEngineEMEUWPSample/src/App.cpp +++ b/samples/MediaEngineEMEUWPSample/src/App.cpp @@ -2,10 +2,9 @@ // Licensed under the MIT License. #include "pch.h" -#include "media/eme/EmeFactory.h" -#include "media/MediaEngineWrapper.h" -#include "media/MediaFoundationHelpers.h" -#include "TestContent.h" +#include "EmeFactory.h" +#include "MediaEnginePlayer.h" +#include "MediaFoundationHelpers.h" using namespace winrt; @@ -25,43 +24,73 @@ namespace abi using namespace ABI::Windows::UI::Composition; } -namespace winrt -{ +namespace winrt +{ using namespace Windows::UI::Composition; } +// Sample fragmented MP4 CBCS Protected Content +wchar_t constexpr const g_Location[] = + L"https://test.playready.microsoft.com/media/test/cbcs/bbb.main.30fps.1920x1080.h264.aac.cbcs.sameKeys.mp4"; + +// Sample init data with PlayReady PSSH box +// Contains the following PlayReady Header: +// https://test.playready.microsoft.com/core/rightsmanager.asmx?cfg=(playenablers:(786627d8-c2a6-44be-8f88-08ae255b01a7),sl:150,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:AES128BitCBC) +constexpr uint8_t g_InitializationData[] +{ + 0x00, 0x00, 0x03, 0x54, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95, 0x00, 0x00, 0x03, 0x34, 0x34, 0x03, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x2a, 0x03, 0x3c, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x20, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x52, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x22, 0x00, 0x20, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x3f, 0x00, 0x63, 0x00, 0x66, 0x00, 0x67, 0x00, 0x3d, 0x00, 0x28, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x28, 0x00, 0x37, 0x00, 0x38, 0x00, 0x36, 0x00, 0x36, 0x00, 0x32, 0x00, 0x37, 0x00, 0x64, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x63, 0x00, 0x32, 0x00, 0x61, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x34, 0x00, 0x62, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x66, 0x00, 0x38, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x38, 0x00, 0x61, 0x00, 0x65, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x62, 0x00, 0x30, 0x00, 0x31, 0x00, 0x61, 0x00, 0x37, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x73, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x3a, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x31, 0x00, 0x32, 0x00, 0x38, 0x00, 0x42, 0x00, 0x69, 0x00, 0x74, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x29, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x20, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x22, 0x00, 0x20, 0x00, 0x56, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x55, 0x00, 0x45, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x45, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x3e, 0x00, +}; + struct App : implements { // Media members - media::MFPlatformRef m_mfPlatform; - winrt::com_ptr m_mediaEngineWrapper; + media::MFPlatformRef m_Platform; + winrt::com_ptr m_Player; // EME media members - std::shared_ptr m_mediaKeySession; + std::shared_ptr m_MediaKeySession; // Composition members - wil::critical_section m_compositionLock; - winrt::Windows::Foundation::Size m_windowSize{}; - CompositionTarget m_target{ nullptr }; - Visual m_videoVisual{ nullptr }; + mutable wil::critical_section m_compositionLock; + winrt::Windows::Foundation::Size m_windowSize { }; + CompositionTarget m_target { nullptr }; + Visual m_videoVisual { nullptr }; - IFrameworkView CreateView() + void ReportError(HRESULT Result, const char* Message) { - return *this; + LOG_HR_MSG(Result, Message); } - void ReportError(HRESULT result, const char* message) + class Site : public winrt::implements { - // An application would typically display an error to the user. - // This sample just logs the error. - LOG_HR_MSG(result, message); - } + public: + Site(winrt::com_ptr const& Owner) : + m_Owner(Owner) + { + } - // EME callbacks - void OnEMEKeyMessage(MF_MEDIAKEYSESSION_MESSAGETYPE /*messageType*/, gsl::span message, LPCWSTR destinationUrl) + // IMediaEnginePlayerSite + void HandleLoaded() override + { + m_Owner->SetupVideoVisual(); + m_Owner->m_Player->StartPlayingFrom(0); + } + void HandleError([[maybe_unused]] MF_MEDIA_ENGINE_ERR MediaEngineError, HRESULT Result) override + { + LOG_HR_MSG(Result, "IMediaEnginePlayerSite::HandleError, MediaEngineError %u", MediaEngineError); + m_Owner->ReportError(Result, "Media playback error"); + } + + private: + winrt::com_ptr m_Owner; + }; + + void HandleKeySessionMessage(MF_MEDIAKEYSESSION_MESSAGETYPE Type, gsl::span Data, LPCWSTR Location) { + TRACE(L"Type %hs, Data.size() %zu, Location \"%ls\"\n", ToString(Type).c_str(), Data.size(), Location); + // Parse challenge value and request headers from key message - winrt::hstring messageString{ reinterpret_cast(const_cast(message.data())), static_cast(message.size() / sizeof(wchar_t)) }; + winrt::hstring messageString{ reinterpret_cast(const_cast(Data.data())), static_cast(Data.size() / sizeof(wchar_t)) }; XmlDocument messageXml; messageXml.LoadXml(messageString); XmlNodeList challengeNodeList = messageXml.GetElementsByTagName(L"Challenge"); @@ -97,227 +126,175 @@ struct App : implements winrt::hstring headerValue = headerValueNodes.GetAt(i).InnerText(); // Special handling required for the 'Content-Type' header if (std::wstring(headerName.c_str()) == std::wstring(L"Content-Type")) - { contentHeaders.ContentType(Headers::HttpMediaTypeHeaderValue::Parse(headerValue)); - } else - { headers.Insert(headerName, headerValue); - } } - IBuffer responseBuffer{}; - + IBuffer Response{}; try { - winrt::Windows::Foundation::Uri requestUri{ destinationUrl }; + winrt::Windows::Foundation::Uri requestUri{ Location }; HttpResponseMessage responseMessage = httpClient.PostAsync(requestUri, postContent).get(); responseMessage.EnsureSuccessStatusCode(); - responseBuffer = responseMessage.Content().ReadAsBufferAsync().get(); + Response = responseMessage.Content().ReadAsBufferAsync().get(); } catch (winrt::hresult_error const& ex) { ReportError(ex.code(), "License acquisition failure (invalid response)"); throw ex; } + TRACE(L"Type %hs, Response.Length() %u\n", ToString(Type).c_str(), Response.Length()); // Pass response data to update() method on the MediaKeySession - m_mediaKeySession->update(gsl::span(responseBuffer.data(), responseBuffer.Length())); - } - - void Initialize(CoreApplicationView const &) - { - m_mfPlatform.Startup(); - - // Initialize playback on MTA MF work queue thread - // Ensure that the callback lambda holds a reference to this app instance to prevent the instance from being destroyed prior to the lambda executing - winrt::com_ptr selfRef; - selfRef.copy_from(this); - media::MFPutWorkItem([&, selfRef]() { - try - { - InitializePlayback(); - } - catch(...) - { - ReportError(wil::ResultFromCaughtException(), "Playback initialization error"); - } - }); + m_MediaKeySession->update(gsl::span(Response.data(), Response.Length())); } - void InitializePlayback() { + TRACE(L"...\n"); + // Setup EME - media::eme::CdmConfigurationProperties configProperties = {}; + media::eme::EmeFactoryConfiguration Configuration { }; // Use the local cache folder which is not backed up / synced to the cloud for CDM storage - configProperties.storagePath = Windows::Storage::ApplicationData::Current().LocalCacheFolder().Path().c_str(); - media::eme::EmeFactory emeFactory(configProperties); + Configuration.storagePath = Windows::Storage::ApplicationData::Current().LocalCacheFolder().Path().c_str(); + media::eme::EmeFactory EmeFactory(Configuration); // Request access to the Playready key system. // This is equivalent to the following Javascript EME call: // Navigator.requestMediaKeySystemAccess("com.microsoft.playready.recommendation", [{ initDataTypes: ['cenc'], persistentState: 'optional', distinctiveIdentifier: 'required']) - std::vector keySystemConfigs(1); - keySystemConfigs[0].initDataTypes().push_back(L"cenc"); - keySystemConfigs[0].persistentState(MF_MEDIAKEYS_REQUIREMENT_OPTIONAL); - keySystemConfigs[0].distinctiveId(MF_MEDIAKEYS_REQUIREMENT_REQUIRED); - - std::shared_ptr keySystemAccess = emeFactory.requestMediaKeySystemAccess(L"com.microsoft.playready.recommendation", keySystemConfigs); - if(!keySystemAccess) + std::vector MediaKeySystemConfigurationVector(1); { - ReportError(MF_E_UNSUPPORTED_SERVICE, "Key system configuration not supported."); - return; + auto& MediaKeySystemConfiguration = MediaKeySystemConfigurationVector[0]; + MediaKeySystemConfiguration.m_initDataTypes.emplace_back(L"cenc"); + MediaKeySystemConfiguration.m_persistentStateRequirement = MF_MEDIAKEYS_REQUIREMENT_OPTIONAL; + MediaKeySystemConfiguration.m_distinctiveIdRequirement = MF_MEDIAKEYS_REQUIREMENT_REQUIRED; } + auto const KeySystemAccess = EmeFactory.requestMediaKeySystemAccess(L"com.microsoft.playready.recommendation", MediaKeySystemConfigurationVector); + THROW_HR_IF_MSG(MF_E_UNSUPPORTED_SERVICE, !KeySystemAccess, "Key system configuration not supported."); // Create MediaKeys, MediaKeySession and setup the onmessage callback on the session (to handle license acquisition requests) - std::shared_ptr mediaKeys = keySystemAccess->createMediaKeys(); - m_mediaKeySession = mediaKeys->createSession(); - m_mediaKeySession->onmessage(std::bind(&App::OnEMEKeyMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - - // Asynchronously acquire license using hardcoded init data - usually an application would find this init data within the media container (e.g in the PSSH box of a MP4 file) or acquire it out-of-band (e.g from an adaptive streaming manifest) - std::vector initData(sizeof(TestContent::c_initData)); - memcpy(&initData[0], &TestContent::c_initData[0], initData.size()); - m_mediaKeySession->generateRequest(L"cenc", initData); + auto const MediaKeys = KeySystemAccess->createMediaKeys(); + m_MediaKeySession = MediaKeys->createSession(); + m_MediaKeySession->onmessage([&] (MF_MEDIAKEYSESSION_MESSAGETYPE Type, gsl::span Data, LPCWSTR Location) { HandleKeySessionMessage(Type, Data, Location); }); + m_MediaKeySession->generateRequest(L"cenc", std::vector { g_InitializationData, g_InitializationData + std::size(g_InitializationData) }); // Create a source resolver to create an IMFMediaSource for the content URL. // This will create an instance of an inbuilt OS media source for playback. // An application can skip this step and instantiate a custom IMFMediaSource implementation instead. - winrt::com_ptr sourceResolver; - THROW_IF_FAILED(MFCreateSourceResolver(sourceResolver.put())); - constexpr uint32_t sourceResolutionFlags = MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_READ; - MF_OBJECT_TYPE objectType = {}; - winrt::com_ptr mediaSource; - std::wstring contentUri(TestContent::c_contentUrl); - THROW_IF_FAILED(sourceResolver->CreateObjectFromURL(contentUri.c_str(), sourceResolutionFlags, nullptr, &objectType, reinterpret_cast(mediaSource.put_void()))); - - // Callbacks invoked by the media engine wrapper - auto onInitialized = std::bind(&App::OnMediaInitialized, this); - auto onError = std::bind(&App::OnMediaError, this, std::placeholders::_1, std::placeholders::_2); - - // Create and initialize the MediaEngineWrapper which manages media playback - m_mediaEngineWrapper = winrt::make_self(onInitialized, onError, nullptr, nullptr, nullptr); - m_mediaEngineWrapper->Initialize(mediaSource.get(), mediaKeys); + wil::com_ptr SourceResolver; + THROW_IF_FAILED(MFCreateSourceResolver(SourceResolver.put())); + MF_OBJECT_TYPE ObjectType; + wil::com_ptr MediaSource; + THROW_IF_FAILED(SourceResolver->CreateObjectFromURL(g_Location, MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_READ, nullptr, &ObjectType, MediaSource.put_unknown())); + m_Player = winrt::make_self(winrt::make_self(get_strong()).get(), MediaSource.get(), MediaKeys); } - - // MediaEngineWrapper initialization callback which is invoked once the media has been loaded and a DCOMP surface handle is available - void OnMediaInitialized() - { - // Create video visual and add it to the DCOMP tree - SetupVideoVisual(); - - // Start playback - m_mediaEngineWrapper->StartPlayingFrom(0); - } - void SetupVideoVisual() { auto lock = m_compositionLock.lock(); + if (m_videoVisual) + return; // Complete setting up video visual if we have a surface from the media engine and the visual tree has been initialized - HANDLE videoSurfaceHandle = m_mediaEngineWrapper ? m_mediaEngineWrapper->GetSurfaceHandle() : NULL; + if(!m_Player) + return; + auto const videoSurfaceHandle = m_Player->GetSurfaceHandle(); + if(!videoSurfaceHandle) + return; + if (!m_target) + return; - if (!m_videoVisual && videoSurfaceHandle != NULL && m_target) - { - Compositor compositor = m_target.Compositor(); - - // Create sprite visual for video - SpriteVisual visual = compositor.CreateSpriteVisual(); - - visual.Offset( - { - 0.0f, - 0.0f, - 0.0f, - }); - - // Use the ABI ICompositorInterop interface to create an ABI composition surface using the video surface handle from the media engine - winrt::com_ptr compositorInterop{ compositor.as() }; - winrt::com_ptr abiCompositionSurface; - THROW_IF_FAILED(compositorInterop->CreateCompositionSurfaceForHandle(videoSurfaceHandle, abiCompositionSurface.put())); - - // Convert from ABI ICompositionSurface type to winrt CompositionSurface - CompositionVisualSurface compositionSurface{ nullptr }; - winrt::copy_from_abi(compositionSurface, abiCompositionSurface.get()); - - // Setup surface brush with surface from MediaEngineWrapper - CompositionSurfaceBrush surfaceBrush{ compositor.CreateSurfaceBrush() }; - surfaceBrush.Surface(compositionSurface); - visual.Brush(surfaceBrush); - - // Insert video visual intro tree - m_videoVisual = visual; - UpdateVideoSize(); - m_target.Root(m_videoVisual); - } - } + TRACE(L"...\n"); + Compositor compositor = m_target.Compositor(); + + // Create sprite visual for video + SpriteVisual visual = compositor.CreateSpriteVisual(); + visual.Offset({ 0.0f, 0.0f, 0.0f, }); + + // Use the ABI ICompositorInterop interface to create an ABI composition surface using the video surface handle from the media engine + winrt::com_ptr compositorInterop { compositor.as() }; + winrt::com_ptr abiCompositionSurface; + THROW_IF_FAILED(compositorInterop->CreateCompositionSurfaceForHandle(videoSurfaceHandle, abiCompositionSurface.put())); + + CompositionVisualSurface compositionSurface { nullptr }; + winrt::copy_from_abi(compositionSurface, abiCompositionSurface.get()); + CompositionSurfaceBrush surfaceBrush { compositor.CreateSurfaceBrush() }; + surfaceBrush.Surface(compositionSurface); + visual.Brush(surfaceBrush); + m_videoVisual = visual; + UpdateVideoSize(); + m_target.Root(m_videoVisual); + } void UpdateVideoSize() { auto lock = m_compositionLock.lock(); - - // If the window has not been initialized yet, use a default size of 640x480 - const bool windowInitialized = m_windowSize.Width != 0 && m_windowSize.Height != 0; - float width = windowInitialized ? m_windowSize.Width : 640; - float height = windowInitialized ? m_windowSize.Height : 480; - + auto WindowSize = m_windowSize; if (m_videoVisual) + m_videoVisual.Size({ WindowSize.Width, WindowSize.Height }); + if(!m_Player) + return; + media::RunSyncInMTA([&]() { - m_videoVisual.Size({ - width, - height - }); - } - - if (m_mediaEngineWrapper) - { - // Call into media engine wrapper on MTA thread to resize the video surface - media::RunSyncInMTA([&]() { - m_mediaEngineWrapper->OnWindowUpdate(static_cast(width), static_cast(height)); - }); - } + m_Player->OnWindowUpdate(std::make_pair(static_cast(WindowSize.Width), static_cast(WindowSize.Height))); + }); } - void OnMediaError(MF_MEDIA_ENGINE_ERR /*error*/, HRESULT hr) +// IFrameworkViewSource + IFrameworkView CreateView() { - ReportError(hr, "Media playback error."); + TRACE(L"this %p\n", this); + return *this; } - void Load(hstring const&) +// IFrameworkView + void Initialize(CoreApplicationView const&) { + TRACE(L"this %p\n", this); + m_Platform.Startup(); + media::MFPutWorkItem([&, StrongThis = get_strong()] + { + try + { + InitializePlayback(); + } + catch(...) + { + ReportError(wil::ResultFromCaughtException(), "Playback initialization error"); + } + }); } - - void Uninitialize() + void SetWindow(CoreWindow const& Window) { + TRACE(L"this %p\n", this); + auto lock = m_compositionLock.lock(); + Compositor compositor; + m_target = compositor.CreateTargetForCurrentView(); + m_windowSize = { Window.Bounds().Width, Window.Bounds().Height }; + UpdateVideoSize(); + Window.SizeChanged([&] (IInspectable const&, WindowSizeChangedEventArgs const& Args) + { + auto lock = m_compositionLock.lock(); + m_windowSize = Args.Size(); + UpdateVideoSize(); + }); + SetupVideoVisual(); } - - void Run() + void Load(hstring const& EntryPoint) { - CoreWindow window = CoreWindow::GetForCurrentThread(); - window.Activate(); - - CoreDispatcher dispatcher = window.Dispatcher(); - dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit); + TRACE(L"this %p, EntryPoint %ls\n", this, EntryPoint.c_str()); } - - void OnWindowSizeChanged(IInspectable const&, WindowSizeChangedEventArgs const& args) + void Run() { - auto lock = m_compositionLock.lock(); - m_windowSize = args.Size(); - UpdateVideoSize(); + TRACE(L"this %p\n", this); + CoreWindow Window = CoreWindow::GetForCurrentThread(); + Window.Activate(); + CoreDispatcher dispatcher = Window.Dispatcher(); + dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit); } - - void SetWindow(CoreWindow const & window) + void Uninitialize() { - auto lock = m_compositionLock.lock(); - Compositor compositor; - m_target = compositor.CreateTargetForCurrentView(); - m_windowSize = { window.Bounds().Width, window.Bounds().Height }; - UpdateVideoSize(); - - window.SizeChanged({ this, &App::OnWindowSizeChanged }); - - // If the mediaengine has been initialized, setup the video visual - SetupVideoVisual(); + TRACE(L"this %p\n", this); } }; diff --git a/samples/MediaEngineEMEUWPSample/src/EmeFactory.h b/samples/MediaEngineEMEUWPSample/src/EmeFactory.h new file mode 100644 index 0000000..759de66 --- /dev/null +++ b/samples/MediaEngineEMEUWPSample/src/EmeFactory.h @@ -0,0 +1,671 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +inline std::string ToString(MF_MEDIAKEYSESSION_TYPE Value) +{ + static std::pair constexpr const g_MessageTypes[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_TEMPORARY) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_LICENSE) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_RELEASE_MESSAGE) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_USAGE_RECORD) + #undef IDENFITIER + }; + return ToString(g_MessageTypes, Value); +} + +inline std::string ToString(MF_MEDIAKEY_STATUS Value) +{ + static std::pair constexpr const g_MessageTypes[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEY_STATUS_USABLE) + IDENFITIER(MF_MEDIAKEY_STATUS_EXPIRED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_DOWNSCALED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_NOT_ALLOWED) + IDENFITIER(MF_MEDIAKEY_STATUS_STATUS_PENDING) + IDENFITIER(MF_MEDIAKEY_STATUS_INTERNAL_ERROR) + IDENFITIER(MF_MEDIAKEY_STATUS_RELEASED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_RESTRICTED) + #undef IDENFITIER + }; + return ToString(g_MessageTypes, Value); +} + +// MFMediaKeyStatus + +inline std::string ToString(MF_MEDIAKEYSESSION_MESSAGETYPE Value) +{ + static std::pair constexpr const g_MessageTypes[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RENEWAL) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RELEASE) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_INDIVIDUALIZATION_REQUEST) + #undef IDENFITIER + }; + return ToString(g_MessageTypes, Value); +} + +// MF_CROSS_ORIGIN_POLICY + +namespace media { +namespace eme { + +// Based on the MediaKeySession interface in the EME specification: +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession +class MediaKeySession +{ +public: + MediaKeySession(wil::com_ptr const& ContentDecryptionModule, MF_MEDIAKEYSESSION_TYPE SessionType) + { + WI_ASSERT(ContentDecryptionModule); + auto const OnKeyMessage = [&] (MF_MEDIAKEYSESSION_MESSAGETYPE messageType, gsl::span message, LPCWSTR destinationUrl) { m_keyMessageCB(messageType, message, destinationUrl); }; + auto const OnKeyStatusChanged = [&] () { m_keyStatusChangedCB(); }; + m_sessionCallbacks = winrt::make_self(OnKeyMessage, OnKeyStatusChanged); + TRACE(L"Before IMFContentDecryptionModule::CreateSession, this %p, SessionType %hs\n", this, ToString(SessionType).c_str()); + THROW_IF_FAILED(ContentDecryptionModule->CreateSession(SessionType, m_sessionCallbacks.get(), m_cdmSession.put())); + TRACE(L"this %p, IMFContentDecryptionModuleSession m_cdmSession %p\n", this, m_cdmSession.get()); + } + virtual ~MediaKeySession() + { + static_cast(m_sessionCallbacks.get())->DetachParent(); + } + + // A unique string identifier generated by the CDM that can be used by the application + // to identify session objects. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-sessionid + wil::unique_cotaskmem_string sessionId() const + { + wil::unique_cotaskmem_string Result; + THROW_IF_FAILED(m_cdmSession->GetSessionId(Result.put())); + TRACE(L"After IMFContentDecryptionModule::GetSessionId, this %p, Result %ls\n", this, Result.get()); + return Result; + } + + // The expiration time for all key(s) in the session, or NaN if no such time exists or + // if the license explicitly never expires, as determined by the CDM. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-expiration + // expiration is based on https://tc39.es/ecma262/#sec-time-values-and-time-range + double expiration() const + { + double Result = 0.0; + THROW_IF_FAILED(m_cdmSession->GetExpiration(&Result)); + TRACE(L"After IMFContentDecryptionModule::GetExpiration, this %p, Result %.3f\n", this, Result); + return Result; + } + + using MediaKeyStatusPair = std::tuple, MF_MEDIAKEY_STATUS>; + // A reference to an array of key IDs known to the session to the current status + // of the associated key. Each entry MUST have a unique key ID. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-keystatuses + std::vector keyStatuses() const + { + wil::unique_cotaskmem_array_ptr abiKeyStatuses; + THROW_IF_FAILED(m_cdmSession->GetKeyStatuses(abiKeyStatuses.put(), abiKeyStatuses.size_address())); + TRACE(L"After IMFContentDecryptionModule::GetKeyStatuses, this %p, abiKeyStatuses.size() %zu\n", this, abiKeyStatuses.size()); + std::vector statusPairs(abiKeyStatuses.size()); + for(size_t i = 0; i < abiKeyStatuses.size(); i++) + { + std::vector keyId(abiKeyStatuses[i].cbKeyId); + memcpy(&keyId[0], abiKeyStatuses[i].pbKeyId, keyId.size()); + statusPairs[i] = std::make_tuple(std::move(keyId), abiKeyStatuses[i].eMediaKeyStatus); + } + return statusPairs; + } + + // Loads the data stored for the specified session into this object. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-load + bool load(const std::wstring& SessionIdentifier) const + { + BOOL Result = FALSE; + THROW_IF_FAILED(m_cdmSession->Load(SessionIdentifier.c_str(), &Result)); + TRACE(L"After IMFContentDecryptionModule::Load, this %p, SessionIdentifier %ls, Result %d\n", this, SessionIdentifier.c_str(), Result); + return !!Result; + } + + // Callback registration methods + using KeyMessageCB = std::function, LPCWSTR)>; + void onmessage(KeyMessageCB&& keyMessageCB) + { + m_keyMessageCB = std::move(keyMessageCB); + } + + using KeyStatusChangedCB = std::function; + void onkeystatuseschange(KeyStatusChangedCB&& keyStatusChangedCB) + { + m_keyStatusChangedCB = std::move(keyStatusChangedCB); + } + + // Generates a license request based on the InitializationData. A message of type "license-request" or + // "individualization-request" will always be queued if the algorithm succeeds + // and the promise is resolved. + // Structure for init data is based on InitializationDataType + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-generaterequest + // https://www.w3.org/TR/eme-initdata-registry/ + void generateRequest(const std::wstring& InitializationDataType, std::vector const& InitializationData) + { + TRACE(L"Before IMFContentDecryptionModule::GenerateRequest, this %p, InitializationDataType %ls, InitializationData %zu\n", this, InitializationDataType.c_str(), InitializationData.size()); + THROW_IF_FAILED(m_cdmSession->GenerateRequest(InitializationDataType.c_str(), InitializationData.data(), static_cast(InitializationData.size()))); + } + + // Provides messages, including licenses, to the CDM. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-update + void update(gsl::span ResponseData) + { + TRACE(L"Before IMFContentDecryptionModule::Update, this %p, ResponseData %zu\n", this, ResponseData.size()); + THROW_IF_FAILED(m_cdmSession->Update(ResponseData.data(), static_cast(ResponseData.size()))); + } + + // Indicates that the application no longer needs the session and the CDM should release any resources + // associated with the session and close it. Persisted data should not be released or cleared. + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-close + void close() + { + TRACE(L"Before IMFContentDecryptionModule::Close, this %p\n", this); + THROW_IF_FAILED(m_cdmSession->Close()); + } + + // Removes all license(s) and key(s) associated with the session. For persistent session types, + // other session data will be cleared as defined for each session type once a + // release message acknowledgment is processed by Update(). + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-remove + void remove() + { + TRACE(L"Before IMFContentDecryptionModule::Remove, this %p\n", this); + THROW_IF_FAILED(m_cdmSession->Remove()); + } + +private: + + class SessionCallbackHelper : public winrt::implements + { + public: + SessionCallbackHelper(MediaKeySession::KeyMessageCB&& keyMessageCB, MediaKeySession::KeyStatusChangedCB&& keyStatusChangedCB) : + m_keyMessageCB(std::move(keyMessageCB)), + m_keyStatusChangedCB(std::move(keyStatusChangedCB)) + { + THROW_HR_IF(E_INVALIDARG, !m_keyMessageCB); + THROW_HR_IF(E_INVALIDARG, !m_keyStatusChangedCB); + } + ~SessionCallbackHelper() + { + DetachParent(); + } + + void DetachParent() + { + auto lock = m_lock.lock(); + m_keyMessageCB = nullptr; + m_keyStatusChangedCB = nullptr; + } + + // IMFContentDecryptionModuleSessionCallbacks + IFACEMETHOD(KeyMessage)(MF_MEDIAKEYSESSION_MESSAGETYPE Type, const BYTE* Data, DWORD DataSize, LPCWSTR Location) noexcept override + { + TRACE(L"this %p, Type %hs, DataSize %u, Location \"%ls\"\n", this, ToString(Type).c_str(), DataSize, Location); + try + { + auto lock = m_lock.lock(); + m_keyMessageCB(Type, gsl::span(const_cast(Data), static_cast(DataSize)), Location); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(KeyStatusChanged)() noexcept override + { + TRACE(L"this %p\n", this); + try + { + auto lock = m_lock.lock(); + m_keyStatusChangedCB(); + } + CATCH_RETURN(); + return S_OK; + } + + private: + wil::critical_section m_lock; + MediaKeySession::KeyMessageCB m_keyMessageCB; + MediaKeySession::KeyStatusChangedCB m_keyStatusChangedCB; + }; + + wil::com_ptr m_cdmSession; + winrt::com_ptr m_sessionCallbacks; + KeyMessageCB m_keyMessageCB; + KeyStatusChangedCB m_keyStatusChangedCB; +}; + +// Based on the MediaKeys interface in the EME specification: +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeys +class MediaKeys +{ +public: + MediaKeys(wil::com_ptr const& ContentDecryptionModule) : + m_ContentDecryptionModule(ContentDecryptionModule) + { + } + + std::shared_ptr createSession(MF_MEDIAKEYSESSION_TYPE sessionType = MF_MEDIAKEYSESSION_TYPE_TEMPORARY) + { + return std::make_shared(m_ContentDecryptionModule, sessionType); + } + //bool SetServerCertificate(std::vector& ServerCertificate) + //{ + // return SUCCEEDED(m_ContentDecryptionModule->SetServerCertificate(ServerCertificate.data(), static_cast(ServerCertificate.size()))); + //} + + wil::com_ptr const m_ContentDecryptionModule; +}; + +// This struct represents a MediaKeySystemMediaCapability dictionary, as defined in the EME specification: +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysystemmediacapability +struct MediaKeySystemMediaCapability +{ + std::wstring contentType; + std::wstring robustness; +}; + +struct EmeFactoryConfiguration +{ + static void SetPropertyValue(wil::com_ptr const& PropertyStore, PROPERTYKEY const& Key, std::wstring const& Value) + { + wil::unique_prop_variant VariantValue; + VariantValue.vt = VT_BSTR; + VariantValue.bstrVal = wil::make_bstr(Value.c_str()).release(); + THROW_IF_FAILED(PropertyStore->SetValue(Key, VariantValue)); + } + + wil::com_ptr ConvertToPropertyStore() const + { + wil::com_ptr PropertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); + SetPropertyValue(PropertyStore, MF_EME_CDM_STOREPATH, storagePath); + if(!inPrivateStoragePath.empty()) + SetPropertyValue(PropertyStore, MF_EME_CDM_INPRIVATESTOREPATH, inPrivateStoragePath); + return PropertyStore; + } + + // Use this property to pass a storage location the CDM can use for initialization. + // This storage location will also be used for content specific data if INPRIVATESTOREPATH is not set. + // The app should not delete the store location to optimize performance of the CDM. + std::wstring storagePath; + // Use this property to pass a storage location the CDM can use for content specific data (e.g licenses) + // The app should delete the store location after the CDM object has been released. + std::wstring inPrivateStoragePath; +}; + +// This class represents a MediaKeySystemConfiguration dictionary, as defined in the EME specification: +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemconfiguration-dictionary +class MediaKeySystemConfiguration +{ +public: + static MediaKeySystemConfiguration FromPropertyStore(wil::com_ptr const& propertyStore) + { + MediaKeySystemConfiguration configuration; + + // Init data types + wil::unique_prop_variant initDataTypes; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_INITDATATYPES, &initDataTypes)) && + initDataTypes.vt == (VT_VECTOR | VT_BSTR)) + { + configuration.m_initDataTypes.resize(initDataTypes.cabstr.cElems); + for(size_t i = 0; i < initDataTypes.cabstr.cElems; i++) + { + configuration.m_initDataTypes[i] = std::wstring(initDataTypes.cabstr.pElems[i]); + } + } + + // Audio capabilities + wil::unique_prop_variant audioCapabilities; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_AUDIOCAPABILITIES, &audioCapabilities)) && + audioCapabilities.vt == (VT_VECTOR | VT_VARIANT)) + { + GetCapabilitiesFromPropVar(audioCapabilities, configuration.m_audioCapabilities); + } + + // Video capabilities + wil::unique_prop_variant videoCapabilities; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_VIDEOCAPABILITIES, &videoCapabilities)) && + videoCapabilities.vt == (VT_VECTOR | VT_VARIANT)) + { + GetCapabilitiesFromPropVar(videoCapabilities, configuration.m_videoCapabilities); + } + + // Distinctive ID + wil::unique_prop_variant distinctiveId; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_DISTINCTIVEID, &distinctiveId)) && + distinctiveId.vt == VT_UI4) + { + configuration.m_distinctiveIdRequirement = static_cast(distinctiveId.ulVal); + } + + // Persistent state + wil::unique_prop_variant persistentState; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_PERSISTEDSTATE, &persistentState)) && + persistentState.vt == VT_UI4) + { + configuration.m_persistentStateRequirement = static_cast(persistentState.ulVal); + } + + // Session types + wil::unique_prop_variant sessionTypes; + if(SUCCEEDED(propertyStore->GetValue(MF_EME_SESSIONTYPES, &sessionTypes)) && + sessionTypes.vt == (VT_VECTOR | VT_UI4)) + { + configuration.m_sessionTypes = std::vector(sessionTypes.caul.cElems); + for(size_t i = 0; i < sessionTypes.caul.cElems; i++) + { + configuration.m_sessionTypes[i] = static_cast(sessionTypes.caul.pElems[i]); + } + } + + return configuration; + } + wil::com_ptr ConvertToPropertyStore() + { + wil::com_ptr propertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(propertyStore.put()))); + + // Init data types + auto initDataTypeArray = wil::make_unique_cotaskmem(m_initDataTypes.size()); + for(size_t i = 0; i < m_initDataTypes.size(); i++) + { + initDataTypeArray[i] = wil::make_bstr(m_initDataTypes[i].c_str()).release(); + } + wil::unique_prop_variant initDataTypes; + initDataTypes.vt = VT_VECTOR | VT_BSTR; + initDataTypes.cabstr.cElems = static_cast(m_initDataTypes.size()); + initDataTypes.cabstr.pElems = initDataTypeArray.release(); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_INITDATATYPES, initDataTypes)); + + // Audio capabilities + wil::unique_prop_variant audioCapabilities = GenerateCapabilityProperty(m_audioCapabilities); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities)); + + // Video capabilities + wil::unique_prop_variant videoCapabilities = GenerateCapabilityProperty(m_videoCapabilities); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities)); + + // Distinctive ID + wil::unique_prop_variant distinctiveId; + distinctiveId.vt = VT_UI4; + distinctiveId.ulVal = static_cast(m_distinctiveIdRequirement); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_DISTINCTIVEID, distinctiveId)); + + // Persistent state + wil::unique_prop_variant persistentState; + persistentState.vt = VT_UI4; + persistentState.ulVal = static_cast(m_persistentStateRequirement); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_PERSISTEDSTATE, persistentState)); + + // Session types + auto sessionTypeArray = wil::make_unique_cotaskmem(m_sessionTypes.size()); + for(size_t i = 0; i < m_sessionTypes.size(); i++) + { + sessionTypeArray[i] = m_sessionTypes[i]; + } + wil::unique_prop_variant sessionTypes; + sessionTypes.vt = VT_VECTOR | VT_UI4; + sessionTypes.caul.cElems = static_cast(m_sessionTypes.size()); + sessionTypes.caul.pElems = sessionTypeArray.release(); + THROW_IF_FAILED(propertyStore->SetValue(MF_EME_SESSIONTYPES, sessionTypes)); + + return propertyStore; + } + + std::wstring m_label; + std::vector m_initDataTypes; + std::vector m_audioCapabilities; + std::vector m_videoCapabilities; + MF_MEDIAKEYS_REQUIREMENT m_distinctiveIdRequirement; + MF_MEDIAKEYS_REQUIREMENT m_persistentStateRequirement; + std::vector m_sessionTypes; + +private: + static wil::unique_prop_variant GenerateCapabilityProperty(const std::vector& capabilities) + { + auto capabilitiesArray = wil::make_unique_cotaskmem(capabilities.size()); + for(size_t i = 0; i < capabilities.size(); i++) + { + auto& Capability = capabilitiesArray[i]; + wil::com_ptr PropertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); + auto const SetValue = [&](PROPERTYKEY const& Key, std::wstring const& Value) + { + wil::unique_prop_variant VariantValue; + VariantValue.vt = VT_BSTR; + VariantValue.bstrVal = wil::make_bstr(Value.c_str()).release(); + THROW_IF_FAILED(PropertyStore->SetValue(Key, VariantValue)); + }; + SetValue(MF_EME_ROBUSTNESS, capabilities[i].robustness); + SetValue(MF_EME_CONTENTTYPE, capabilities[i].contentType); + Capability.vt = VT_UNKNOWN; + Capability.punkVal = PropertyStore.query().detach(); + } + wil::unique_prop_variant capabilitiesProperty; + capabilitiesProperty.vt = VT_VECTOR | VT_VARIANT; + capabilitiesProperty.capropvar.cElems = static_cast(capabilities.size()); + capabilitiesProperty.capropvar.pElems = capabilitiesArray.release(); + return capabilitiesProperty; + } + static void GetCapabilitiesFromPropVar(const PROPVARIANT& capabilitiesProperty, std::vector& capabilities) + { + capabilities = std::vector(capabilitiesProperty.capropvar.cElems); + for(size_t i = 0; i < capabilities.size(); i++) + { + PROPVARIANT& capabilityProperty = capabilitiesProperty.capropvar.pElems[i]; + wil::com_ptr capabilityPropertyStore; + if(capabilityProperty.vt != VT_UNKNOWN || FAILED(capabilityProperty.punkVal->QueryInterface(IID_PPV_ARGS(capabilityPropertyStore.put())))) + continue; + wil::unique_prop_variant robustness; + if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_ROBUSTNESS, &robustness)) && robustness.vt == VT_BSTR) + capabilities[i].robustness = std::wstring(robustness.bstrVal); + wil::unique_prop_variant contentType; + if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_CONTENTTYPE, &contentType)) && contentType.vt == VT_BSTR) + capabilities[i].contentType = std::wstring(contentType.bstrVal); + } + } +}; + +// Based on the MediaKeySystemAccess interface in the EME specification: +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemaccess-interface +class MediaKeySystemAccess +{ +public: + MediaKeySystemAccess(EmeFactoryConfiguration& cdmConfigProperties, wil::com_ptr const& cdmAccess) : + m_cdmAccess(cdmAccess) + { + m_cdmProperties = cdmConfigProperties.ConvertToPropertyStore(); + wil::com_ptr PropertyStore; + THROW_IF_FAILED(cdmAccess->GetConfiguration(PropertyStore.put())); + m_keySystemConfiguration = MediaKeySystemConfiguration::FromPropertyStore(PropertyStore); + } + + wil::unique_cotaskmem_string keySystem() const + { + wil::unique_cotaskmem_string keySystem; + THROW_IF_FAILED(m_cdmAccess->GetKeySystem(keySystem.put())); + return keySystem; + } + const MediaKeySystemConfiguration& getConfiguration() const + { + return m_keySystemConfiguration; + } + std::shared_ptr createMediaKeys() const + { + wil::com_ptr ContentDecryptionModule; + TRACE(L"Before IMFContentDecryptionModuleAccess::CreateContentDecryptionModule, this %p\n", this); + THROW_IF_FAILED(m_cdmAccess->CreateContentDecryptionModule(m_cdmProperties.get(), ContentDecryptionModule.put())); + TRACE(L"this %p, IMFContentDecryptionModule cdm %p\n", this, ContentDecryptionModule.get()); + return std::make_shared(ContentDecryptionModule.get()); + } + +private: + wil::com_ptr m_cdmProperties; + wil::com_ptr m_cdmAccess; + MediaKeySystemConfiguration m_keySystemConfiguration; +}; + +// Class used to query for EME keysystem support and attempt creation of a MediaKeySystemAccess object for a specified keysystem +class EmeFactory +{ +public: + EmeFactory(EmeFactoryConfiguration cdmConfigProperties) + { + TRACE(L"this %p\n", this); + THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(m_classFactory.put()))); + m_cdmConfigProperties = cdmConfigProperties; + } + virtual ~EmeFactory() = default; + + // Used to request creation of a MediaKeySystemAccess. Based on: + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-navigator-requestmediakeysystemaccess + // Returns: MediaKeySystemAccess on success, or nullptr if the keysystem or none of the specified configurations are supported + std::shared_ptr requestMediaKeySystemAccess(const std::wstring& keySystem, std::vector& configurations) + { + TRACE(L"this %p, keySystem %ls, configurations.size() %zu\n", this, keySystem.c_str(), configurations.size()); + + // Use a cached CDM factory if client is querying for the same keysystem, otherwise create a new CDM factory + if(!m_cdmFactory || m_cachedFactoryKeySystem != keySystem) + { + TRACE(L"Before IMFMediaEngineClassFactory4::CreateContentDecryptionModuleFactory, this %p\n", this); + if(FAILED(LOG_IF_FAILED(m_classFactory->CreateContentDecryptionModuleFactory(keySystem.c_str(), IID_PPV_ARGS(m_cdmFactory.put()))))) + return nullptr; + TRACE(L"this %p, IMFContentDecryptionModuleFactory m_cdmFactory %p\n", this, m_cdmFactory.get()); + m_cachedFactoryKeySystem = keySystem; + } + + if(!m_cdmFactory->IsTypeSupported(keySystem.c_str(), nullptr)) + return nullptr; + wil::com_ptr ContentDecryptionModuleAccess; + std::vector> configurationPropertyStores; + for(auto& config : configurations) + { + wil::com_ptr_t propStore; + propStore.attach(config.ConvertToPropertyStore().detach()); + configurationPropertyStores.push_back(propStore); + } + if(FAILED(LOG_IF_FAILED(m_cdmFactory->CreateContentDecryptionModuleAccess(keySystem.c_str(), configurationPropertyStores[0].addressof(), static_cast(configurationPropertyStores.size()), ContentDecryptionModuleAccess.put())))) + return nullptr; + + return std::make_shared(m_cdmConfigProperties, ContentDecryptionModuleAccess.get()); + } + +private: + EmeFactoryConfiguration m_cdmConfigProperties; + wil::com_ptr m_classFactory; + wil::com_ptr m_cdmFactory; + std::wstring m_cachedFactoryKeySystem; +}; + +} // namespace eme + +// A custom MF media source wrapper which supports protected playback using a provided ContentDecryptionModule +// Demuxing is performed by the inner source provided by the application +class InputTrustAuthorityMediaSource : public winrt::implements +{ +public: + InputTrustAuthorityMediaSource(std::shared_ptr mediaKeys, wil::com_ptr const& MediaSource) : + m_Inner(MediaSource), + m_ContentDecryptionModule(mediaKeys->m_ContentDecryptionModule) + { + WI_ASSERT(m_Inner && m_ContentDecryptionModule); + } + +// IMFMediaSource + IFACEMETHOD(GetCharacteristics)(DWORD* characteristics) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->GetCharacteristics(characteristics); + } + IFACEMETHOD(CreatePresentationDescriptor)(IMFPresentationDescriptor** presentationDescriptorOut) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->CreatePresentationDescriptor(presentationDescriptorOut); + } + IFACEMETHOD(Start)(IMFPresentationDescriptor* PresentationDescriptor, GUID const* TimeFormat, PROPVARIANT const* StartPosition) noexcept override + { + TRACE(L"this %p\n", this); + return m_Inner->Start(PresentationDescriptor, TimeFormat, StartPosition); + } + IFACEMETHOD(Stop)() noexcept override + { + TRACE(L"this %p\n", this); + return m_Inner->Stop(); + } + IFACEMETHOD(Pause)() noexcept override + { + TRACE(L"this %p\n", this); + return m_Inner->Pause(); + } + IFACEMETHOD(Shutdown)() noexcept override + { + TRACE(L"this %p\n", this); + { + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + m_StreamTrustedInputMap.clear(); + } + return m_Inner->Shutdown(); + } + +// IMFMediaEventGenerator + IFACEMETHOD(GetEvent)(DWORD flags, IMFMediaEvent** eventOut) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->GetEvent(flags, eventOut); + } + IFACEMETHOD(BeginGetEvent)(IMFAsyncCallback* callback, IUnknown* state) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->BeginGetEvent(callback, state); + } + IFACEMETHOD(EndGetEvent)(IMFAsyncResult* result, IMFMediaEvent** eventOut) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->EndGetEvent(result, eventOut); + } + IFACEMETHOD(QueueEvent)(MediaEventType type, REFGUID extendedType, HRESULT status, const PROPVARIANT* value) noexcept override + { + //TRACE(L"this %p\n", this); + return m_Inner->QueueEvent(type, extendedType, status, value); + } + +// IMFTrustedInput + IFACEMETHOD(GetInputTrustAuthority)(DWORD StreamIdentifier, REFIID InterfaceIdentifier, IUnknown** Object) noexcept override + { + TRACE(L"this %p, StreamIdentifier %u, InterfaceIdentifier %ls\n", this, StreamIdentifier, ToString(InterfaceIdentifier).c_str()); + try + { + LOG_HR_IF(S_FALSE, InterfaceIdentifier != __uuidof(IMFInputTrustAuthority)); + WI_ASSERT(Object); + wil::com_ptr Unknown; + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + auto const Iterator = m_StreamTrustedInputMap.find(StreamIdentifier); + if(Iterator == m_StreamTrustedInputMap.end()) + { + if(!m_TrustedInput) + THROW_IF_FAILED(m_ContentDecryptionModule->CreateTrustedInput(nullptr, 0, m_TrustedInput.put())); + THROW_IF_FAILED(m_TrustedInput->GetInputTrustAuthority(StreamIdentifier, InterfaceIdentifier, Unknown.put())); + m_StreamTrustedInputMap[StreamIdentifier] = Unknown; + } else + Unknown = Iterator->second; + *Object = Unknown.detach(); + } + CATCH_RETURN(); + return S_OK; + } + +private: + wil::com_ptr const m_ContentDecryptionModule; + wil::com_ptr const m_Inner; + mutable wil::srwlock m_DataMutex; + wil::com_ptr m_TrustedInput; + std::map> m_StreamTrustedInputMap; +}; + +} // namespace media diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj index a7e6959..7b50316 100644 --- a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj +++ b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj @@ -13,7 +13,7 @@ true Windows Store 10.0 - 10.0.19041.0 + 10.0 10.0.19041.0 @@ -45,7 +45,7 @@ Application - v140 + v143 v141 v142 Unicode @@ -68,7 +68,7 @@ - + Use @@ -99,20 +99,10 @@ - - - - - - - - - - - - + + + - @@ -130,16 +120,6 @@ - - - - - - - - - - Create diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters index c9fe702..3159572 100644 --- a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters +++ b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters @@ -3,76 +3,12 @@ - - media - - - media - - - media - - - media - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - - media - - - media - - - media - - - media - - - media - - - media - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - - media\eme - - + + + diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h b/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h new file mode 100644 index 0000000..e207675 --- /dev/null +++ b/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h @@ -0,0 +1,497 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "EmeFactory.h" +#include "MediaFoundationHelpers.h" + +inline std::string ToString(MF_MEDIA_ENGINE_EVENT Value) +{ + static std::pair constexpr const g_MessageTypes[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIA_ENGINE_EVENT_LOADSTART) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_PROGRESS) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_SUSPEND) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_ABORT) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_ERROR) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_EMPTIED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_STALLED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_PLAY) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_PAUSE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_LOADEDDATA) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_WAITING) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_PLAYING) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_CANPLAY) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_SEEKING) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_SEEKED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_TIMEUPDATE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_ENDED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_RATECHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_FORMATCHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_BALANCECHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_OPMINFO) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_RESOURCELOST) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_SUPPORTEDRATES_CHANGED) + IDENFITIER(MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE) + #undef IDENFITIER + }; + return ToString(g_MessageTypes, Value); +} + +namespace media +{ + +// This implementation of IMFMediaEngineExtension is used to integrate a custom +// IMFMediaSource with the MediaEngine pipeline +class MediaEngineExtension : public winrt::implements +{ +public: + MediaEngineExtension(wil::com_ptr const& MediaSource) : + m_MediaSource(MediaSource) + { + } + + void Shutdown() + { + TRACE(L"this %p\n", this); + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + if(std::exchange(m_Shutdown, true)) + return; + m_MediaSource.reset(); + } + +// IMFMediaEngineExtension + IFACEMETHOD(CanPlayType)([[maybe_unused]] BOOL AudioOnly, [[maybe_unused]] BSTR MimeType, MF_MEDIA_ENGINE_CANPLAY* Result) noexcept override + { + TRACE(L"this %p, AudioOnly %d, MimeType %ls\n", this, AudioOnly, MimeType ? MimeType : L"nullptr"); + WI_ASSERT(Result); + *Result = MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED; + return S_OK; + } + IFACEMETHOD(BeginCreateObject)([[maybe_unused]] BSTR Location, [[maybe_unused]] IMFByteStream* ByteStream, MF_OBJECT_TYPE Type, IUnknown** CancelCookie, IMFAsyncCallback* AsyncCallback, IUnknown* StateUnknown) noexcept override + { + TRACE(L"this %p, Location \"%ls\", ByteStream %p, Type %u\n", this, Location, ByteStream, Type); + try + { + WI_ASSERT(AsyncCallback); + wil::assign_null_to_opt_param(CancelCookie); + RETURN_HR_IF(MF_E_UNEXPECTED, Type != MF_OBJECT_MEDIASOURCE); + wil::com_ptr AsyncResult; + { + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + THROW_HR_IF(MF_E_SHUTDOWN, m_Shutdown); + THROW_HR_IF(MF_E_UNEXPECTED, !m_MediaSource); + THROW_IF_FAILED(MFCreateAsyncResult(m_MediaSource.get(), AsyncCallback, StateUnknown, AsyncResult.put())); + WI_ASSERT(!m_CreateMediaSourceActive); + m_CreateMediaSourceActive = true; + } + THROW_IF_FAILED(AsyncResult->SetStatus(S_OK)); + THROW_IF_FAILED(AsyncCallback->Invoke(AsyncResult.get())); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(CancelObjectCreation)([[maybe_unused]] IUnknown* CancelCookie) noexcept override + { + TRACE(L"this %p\n", this); + return E_NOTIMPL; + } + IFACEMETHOD(EndCreateObject)(IMFAsyncResult* AsyncResult, IUnknown** Object) noexcept override + { + TRACE(L"this %p\n", this); + try + { + *Object = nullptr; + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + auto CreateMediaSourceActive = std::exchange(m_CreateMediaSourceActive, false); + THROW_HR_IF(MF_E_UNEXPECTED, !CreateMediaSourceActive); + THROW_IF_FAILED(AsyncResult->GetStatus()); + THROW_IF_FAILED(AsyncResult->GetObject(Object)); + } + CATCH_RETURN(); + return S_OK; + } + +private: + mutable wil::srwlock m_DataMutex; + wil::com_ptr m_MediaSource; + bool m_Shutdown = false; + bool m_CreateMediaSourceActive = false; +}; + +// Implements IMFContentProtectionManager +// (https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfcontentprotectionmanager) +// and winrt::Windows::Media::Protection::IMediaProtectionManager +// (https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.mediaprotectionmanager) +// required by IMFMediaEngineProtectedContent::SetContentProtectionManager in +// https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfmediaengineprotectedcontent-setcontentprotectionmanager. +class MediaEngineProtectionManager : public winrt::implements +{ +public: + MediaEngineProtectionManager(std::shared_ptr const& mediaKeys) : + m_ContentDecryptionModule(mediaKeys->m_ContentDecryptionModule) + { + // Use the PMP server from the CDM - this ensures that the media engine uses the same protected process as the CDM + auto const cdmServices = m_ContentDecryptionModule.query(); + winrt::com_ptr abiPmpServer; + THROW_IF_FAILED(cdmServices->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE, IID_PPV_ARGS(abiPmpServer.put()))); + winrt::Windows::Media::Protection::MediaProtectionPMPServer pmpServer{ nullptr }; + winrt::copy_from_abi(pmpServer, abiPmpServer.get()); + { + // MFMediaEngine uses |pmpServerKey| to get the Protected Media Path (PMP) server used for playing protected content. + // This is not currently documented in MSDN. + winrt::hstring const pmpServerKey { L"Windows.Media.Protection.MediaProtectionPMPServer" }; + TRACE(L"this %p, pmpServerKey %ls\n", this, pmpServerKey.c_str()); + //using MapType = winrt::Windows::Foundation::Collections::IMap; + //m_PropertySet.as().Insert(pmpServerKey, pmpServer); + m_PropertySet.Insert(pmpServerKey, pmpServer); + } + } + +// IMFContentProtectionManager + IFACEMETHOD(BeginEnableContent)(IMFActivate* enablerActivate, [[maybe_unused]] IMFTopology* topology, IMFAsyncCallback* callback, IUnknown* state) noexcept override + { + TRACE(L"this %p\n", this); + try + { + wil::com_ptr unknownObject; + wil::com_ptr asyncResult; + THROW_IF_FAILED(MFCreateAsyncResult(nullptr, callback, state, asyncResult.put())); + THROW_IF_FAILED(enablerActivate->ActivateObject(IID_PPV_ARGS(unknownObject.put()))); + + // |enablerType| can be obtained from IMFContentEnabler + // (https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfcontentenabler). + // If not, try IMediaProtectionServiceRequest + // (https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.imediaprotectionservicerequest). + GUID enablerType = GUID_NULL; + auto const contentEnabler = unknownObject.try_query(); + if (contentEnabler) + { + THROW_IF_FAILED(contentEnabler->GetEnableType(&enablerType)); + } + else + { + auto const serviceRequest = unknownObject.query(); + THROW_IF_FAILED(serviceRequest->get_Type(&enablerType)); + } + + // Unhandled enabler types + THROW_HR_IF(MF_E_REBOOT_REQUIRED, enablerType == MFENABLETYPE_MF_RebootRequired); + THROW_HR_IF(MF_E_GRL_VERSION_TOO_LOW, enablerType == MFENABLETYPE_MF_UpdateRevocationInformation); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_IMAGE_HASH), enablerType == MFENABLETYPE_MF_UpdateUntrustedComponent); + // Other enabler types are handled by the CDM + THROW_IF_FAILED(m_ContentDecryptionModule->SetContentEnabler(contentEnabler.get(), asyncResult.get())); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(EndEnableContent)([[maybe_unused]] IMFAsyncResult* asyncResult) noexcept override + { + TRACE(L"this %p\n", this); + return S_OK; + } + +// IMediaProtectionManager + winrt::event_token ServiceRequested(winrt::Windows::Media::Protection::ServiceRequestedEventHandler const handler) + { + THROW_HR(E_NOTIMPL); + } + void ServiceRequested([[maybe_unused]] winrt::event_token const cookie) + { + THROW_HR(E_NOTIMPL); + } + winrt::event_token RebootNeeded([[maybe_unused]] winrt::Windows::Media::Protection::RebootNeededEventHandler const handler) + { + THROW_HR(E_NOTIMPL); + } + void RebootNeeded([[maybe_unused]] winrt::event_token const cookie) + { + THROW_HR(E_NOTIMPL); + } + winrt::event_token ComponentLoadFailed([[maybe_unused]] winrt::Windows::Media::Protection::ComponentLoadFailedEventHandler const handler) + { + THROW_HR(E_NOTIMPL); + } + void ComponentLoadFailed([[maybe_unused]] winrt::event_token const cookie) + { + THROW_HR(E_NOTIMPL); + } + winrt::Windows::Foundation::Collections::PropertySet Properties() + { + TRACE(L"this %p, m_PropertySet.Size() %u\n", this, m_PropertySet.Size()); + return m_PropertySet; + } + +private: + wil::com_ptr const m_ContentDecryptionModule; + winrt::Windows::Foundation::Collections::PropertySet m_PropertySet; +}; + +class __declspec(uuid("{4B134833-D245-4EEF-89F5-370DEE5D37DE}")) IMediaEnginePlayerSite : public IUnknown +{ +public: + virtual void HandleLoaded() = 0; + virtual void HandleError(MF_MEDIA_ENGINE_ERR Error, HRESULT Result) = 0; +}; + +// This class handles creation and management of the MediaFoundation MediaEngine. +// - It uses the provided IMFMediaSource to feed media samples into the MediaEngine pipeline. +// - A DirectComposition surface is exposed to the application layer to incorporate video from the MediaEngine in its visual tree. +// - The application must provide a callback object (std::function) which is invoked after the media engine has loaded and the DirectComposition surface is available. +class MediaEnginePlayer : public winrt::implements +{ +public: + MediaEnginePlayer(IMediaEnginePlayerSite* Site, IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) : + m_Site(Site) + { + TRACE(L"...\n"); + WI_ASSERT(m_Site); + Initialize(mediaSource, mediaKeys); + } + + void Initialize(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) + { + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + InitializeVideo(); + CreateMediaEngine(mediaSource, mediaKeys); + }); + } + void Shutdown() + { + TRACE(L"...\n"); + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + THROW_IF_FAILED(m_MediaEngine->Shutdown()); + }); + } + + void Pause() + { + TRACE(L"...\n"); + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + THROW_IF_FAILED(m_MediaEngine->Pause()); + }); + } + void StartPlayingFrom(uint64_t Time) + { + TRACE(L"...\n"); + RunSyncInMTA([&, Time]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + THROW_IF_FAILED(m_MediaEngine->SetCurrentTime(ConvertHnsToSeconds(Time))); + THROW_IF_FAILED(m_MediaEngine->Play()); + }); + } + void SetPlaybackRate(double playbackRate) + { + TRACE(L"...\n"); + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + THROW_IF_FAILED(m_MediaEngine->SetPlaybackRate(playbackRate)); + }); + } + void SetVolume(float volume) + { + TRACE(L"...\n"); + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + THROW_IF_FAILED(m_MediaEngine->SetVolume(volume)); + }); + } + uint64_t GetMediaTime() const + { + TRACE(L"...\n"); + uint64_t Result = 0; + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + auto lock = m_lock.lock(); + Result = ConvertSecondsToHns(m_MediaEngine->GetCurrentTime()); + }); + return Result; + } + + HANDLE GetSurfaceHandle() + { + TRACE(L"...\n"); + HANDLE surfaceHandle = INVALID_HANDLE_VALUE; + RunSyncInMTA([&]() + { + TRACE(L"...\n"); + surfaceHandle = m_dcompSurfaceHandle.get(); + }); + return surfaceHandle; + } + + // Inform media engine of output window position & size changes + void OnWindowUpdate(std::pair WindowSize) + { + RunSyncInMTA([&]() + { + auto lock = m_lock.lock(); + m_WindowSize = WindowSize; + if(m_MediaEngine) + { + RECT Position { 0, 0, static_cast(WindowSize.first), static_cast(WindowSize.second) }; + THROW_IF_FAILED(m_MediaEngine.query()->UpdateVideoStream(nullptr, &Position, nullptr)); + } + }); + } + + private: + + class MediaEngineNotify : public winrt::implements + { + public: + MediaEngineNotify(winrt::com_ptr const& Owner) : + m_Owner(Owner) + { + } + + void Shutdown() + { + TRACE(L"this %p\n", this); + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + m_Shutdown = true; + m_Owner = nullptr; + } + + // IMFMediaEngineNotify + IFACEMETHOD(EventNotify)(DWORD Code, DWORD_PTR Parameter1, DWORD Parameter2) noexcept override + { + TRACE(L"this %p, Code %hs, %p, %08x\n", this, ToString(static_cast(Code)).c_str(), Parameter1, Parameter2); + try + { + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + RETURN_HR_IF(MF_E_SHUTDOWN, m_Shutdown); + WI_ASSERT(m_Owner); + switch(static_cast(Code)) + { + case MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA: + { + media::MFPutWorkItem([&, StrongThis = get_strong()]() + { + m_Owner->HandleLoaded(); + m_Owner->m_Site->HandleLoaded(); + }); + } + break; + case MF_MEDIA_ENGINE_EVENT_ERROR: + m_Owner->m_Site->HandleError(static_cast(Parameter1), static_cast(Parameter2)); + break; + } + } + CATCH_RETURN(); + return S_OK; + } + + private: + mutable wil::srwlock m_DataMutex; + winrt::com_ptr m_Owner; + bool m_Shutdown = false; + }; + + void InitializeVideo() + { + m_dxgiDeviceManager = nullptr; + THROW_IF_FAILED(MFLockDXGIDeviceManager(&m_deviceResetToken, m_dxgiDeviceManager.put())); + UINT creationFlags = (D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS); + D3D_FEATURE_LEVEL constexpr featureLevels[] { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1 }; + wil::com_ptr d3d11Device; + wil::com_ptr d3d11DeviceContext; + THROW_IF_FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, d3d11Device.put(), nullptr, d3d11DeviceContext.put())); + d3d11DeviceContext.query()->SetMultithreadProtected(TRUE); + THROW_IF_FAILED(m_dxgiDeviceManager->ResetDevice(d3d11Device.get(), m_deviceResetToken)); + } + void CreateMediaEngine(wil::com_ptr const& MediaSource, std::shared_ptr mediaKeys) + { + WI_ASSERT(MediaSource); + + m_platformRef.Startup(); + + InitializeVideo(); + + wil::com_ptr ClassFactory; + THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(ClassFactory.put()))); + + m_Notify = winrt::make_self(get_strong()); + + wil::com_ptr Attributes; + THROW_IF_FAILED(MFCreateAttributes(Attributes.put(), 7)); + THROW_IF_FAILED(Attributes->SetUnknown(MF_MEDIA_ENGINE_CALLBACK, m_Notify.get())); + THROW_IF_FAILED(Attributes->SetUINT32(MF_MEDIA_ENGINE_CONTENT_PROTECTION_FLAGS, MF_MEDIA_ENGINE_ENABLE_PROTECTED_CONTENT)); + THROW_IF_FAILED(Attributes->SetGUID(MF_MEDIA_ENGINE_BROWSER_COMPATIBILITY_MODE, MF_MEDIA_ENGINE_BROWSER_COMPATIBILITY_MODE_IE_EDGE)); + THROW_IF_FAILED(Attributes->SetUINT32(MF_MEDIA_ENGINE_AUDIO_CATEGORY, AudioCategory_Media)); + if(m_dxgiDeviceManager != nullptr) + THROW_IF_FAILED(Attributes->SetUnknown(MF_MEDIA_ENGINE_DXGI_MANAGER, m_dxgiDeviceManager.get())); + + wil::com_ptr ExtensionMediaSource; + if(mediaKeys) + ExtensionMediaSource = winrt::make_self(mediaKeys, MediaSource).get(); // Also exposes an IMFTrustedInput to the pipeline + else + ExtensionMediaSource = MediaSource; // Set the app-provided media source directly on the media engine extension + m_Extension = winrt::make_self(ExtensionMediaSource); + THROW_IF_FAILED(Attributes->SetUnknown(MF_MEDIA_ENGINE_EXTENSION, m_Extension.get())); + THROW_IF_FAILED(ClassFactory->CreateInstance(0, Attributes.get(), m_MediaEngine.put())); + if(mediaKeys) + { + m_ProtectionManager = winrt::make_self(mediaKeys); + THROW_IF_FAILED(m_MediaEngine.query()->SetContentProtectionManager(m_ProtectionManager.get())); + // NOTE: The call adds to the properties of the manager + } + + THROW_IF_FAILED(m_MediaEngine.query()->SetSource(wil::make_bstr(L"MediaEnginePlayer").get())); + } + + void HandleLoaded() + { + auto lock = m_lock.lock(); + auto const MediaEngineEx = m_MediaEngine.query(); + THROW_IF_FAILED(MediaEngineEx->EnableWindowlessSwapchainMode(true)); + OnWindowUpdate(m_WindowSize); + THROW_IF_FAILED(MediaEngineEx->GetVideoSwapchainHandle(&m_dcompSurfaceHandle)); + } + + wil::com_ptr m_Site; + mutable wil::critical_section m_lock; + MFPlatformRef m_platformRef; + wil::com_ptr m_MediaEngine; + UINT m_deviceResetToken = 0; + wil::com_ptr m_dxgiDeviceManager; + winrt::com_ptr m_Extension; + winrt::com_ptr m_Notify; + winrt::com_ptr m_ProtectionManager; + wil::unique_handle m_dcompSurfaceHandle; + std::pair m_WindowSize = std::make_pair(640u, 480u); +}; + +} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaFoundationHelpers.h b/samples/MediaEngineEMEUWPSample/src/MediaFoundationHelpers.h similarity index 100% rename from samples/MediaEngineEMEUWPSample/src/media/MediaFoundationHelpers.h rename to samples/MediaEngineEMEUWPSample/src/MediaFoundationHelpers.h diff --git a/samples/MediaEngineEMEUWPSample/src/README.md b/samples/MediaEngineEMEUWPSample/src/README.md new file mode 100644 index 0000000..630c083 --- /dev/null +++ b/samples/MediaEngineEMEUWPSample/src/README.md @@ -0,0 +1,103 @@ +# + +## Sample Output + +``` +App.cpp(246): App::CreateView: this 00000211376276F0 +App.cpp(253): App::Initialize: this 00000211376276F0 +App.cpp(154): App::InitializePlayback: ... +onecoreuap\base\appmodel\statemanager\winrt\lib\windows.storage.applicationdatafactory.server.cpp(235)\Windows.Storage.ApplicationData.dll!00007FFCFC138483: (caller: 00007FFCFC148CCB) ReturnHr(1) tid(977c) 8000000B The operation attempted to access data outside the valid range + Msg:[User S-1-5-21-2072995867-3123792656-4078183186-1001] +EmeFactory.h(520): media::eme::EmeFactory::EmeFactory: this 0000008999CFEE30 +EmeFactory.h(531): media::eme::EmeFactory::requestMediaKeySystemAccess: this 0000008999CFEE30, keySystem com.microsoft.playready.recommendation, configurations.size() 1 +EmeFactory.h(536): media::eme::EmeFactory::requestMediaKeySystemAccess: Before IMFMediaEngineClassFactory4::CreateContentDecryptionModuleFactory, this 0000008999CFEE30 +App.cpp(269): App::SetWindow: this 00000211376276F0 +App.cpp(285): App::Load: this 00000211376276F0, EntryPoint MediaEngineEMEUWPSample.App +App.cpp(289): App::Run: this 00000211376276F0 +EmeFactory.h(539): media::eme::EmeFactory::requestMediaKeySystemAccess: this 0000008999CFEE30, IMFContentDecryptionModuleFactory m_cdmFactory 000002113A2020B0 +EmeFactory.h(502): media::eme::MediaKeySystemAccess::createMediaKeys: Before IMFContentDecryptionModuleAccess::CreateContentDecryptionModule, this 00000211376775D0 +EmeFactory.h(504): media::eme::MediaKeySystemAccess::createMediaKeys: this 00000211376775D0, IMFContentDecryptionModule cdm 000002113A211E80 +EmeFactory.h(70): media::eme::MediaKeySession::MediaKeySession: Before IMFContentDecryptionModule::CreateSession, this 00000211376DBE80, SessionType MF_MEDIAKEYSESSION_TYPE_TEMPORARY +EmeFactory.h(72): media::eme::MediaKeySession::MediaKeySession: this 00000211376DBE80, IMFContentDecryptionModuleSession m_cdmSession 000002113A20B910 +EmeFactory.h(152): media::eme::MediaKeySession::generateRequest: Before IMFContentDecryptionModule::GenerateRequest, this 00000211376DBE80, InitializationDataType cenc, InitializationData 852 +EmeFactory.h(210): media::eme::MediaKeySession::SessionCallbackHelper::KeyMessage: this 00000211376E6470, Type MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST, DataSize 26334, Location "https://test.playready.microsoft.com/core/rightsmanager.asmx?cfg=(playenablers:(786627d8-c2a6-44be-8f88-08ae255b01a7),sl:150,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:AES128BitCBC)" +App.cpp(90): App::HandleKeySessionMessage: Type MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST, Data.size() 26334, Location "https://test.playready.microsoft.com/core/rightsmanager.asmx?cfg=(playenablers:(786627d8-c2a6-44be-8f88-08ae255b01a7),sl:150,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:AES128BitCBC)" +App.cpp(147): App::HandleKeySessionMessage: Type MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST, Response.Length() 1372 +EmeFactory.h(160): media::eme::MediaKeySession::update: Before IMFContentDecryptionModule::Update, this 00000211376DBE80, ResponseData 1372 +EmeFactory.h(221): media::eme::MediaKeySession::SessionCallbackHelper::KeyStatusChanged: this 00000211376E6470 +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: std::bad_function_call at memory location 0x000000899A4FED60. +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000. +C:\Project\github.com\media-foundation\samples\MediaEngineEMEUWPSample\src\EmeFactory.h(227)\(null)!00007FF7B68579FF: (caller: 00007FFCE22F506D) ReturnHr(1) tid(91b4) 8007023E {Application Error} +The exception %s (0x Msg:[std::exception: bad function call] [media::eme::MediaKeySession::SessionCallbackHelper::KeyStatusChanged] +MediaEnginePlayer.h(264): media::MediaEnginePlayer::MediaEnginePlayer: ... +MediaEnginePlayer.h(273): media::MediaEnginePlayer::Initialize::::operator (): ... +onecore\windows\directx\database\helperlibrary\lib\perappusersettingsqueryimpl.cpp(121)\dxgi.dll!00007FFD1F5F1B34: (caller: 00007FFD1F5CE332) ReturnHr(1) tid(977c) 8000FFFF Catastrophic failure +onecore\windows\directx\database\helperlibrary\lib\perappusersettingsqueryimpl.cpp(98)\dxgi.dll!00007FFD1F5F1805: (caller: 00007FFD1F5BDF01) ReturnHr(2) tid(977c) 8000FFFF Catastrophic failure +onecore\windows\directx\database\helperlibrary\lib\directxdatabasehelper.cpp(1410)\dxgi.dll!00007FFD1F5F1925: (caller: 00007FFD1F5BDF01) ReturnHr(3) tid(977c) 8000FFFF Catastrophic failure +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: std::runtime_error at memory location 0x0000008999CF18E0. +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: std::runtime_error at memory location 0x0000008999CF18E0. +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: std::runtime_error at memory location 0x0000008999CF18E0. +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: Poco::NotFoundException at memory location 0x0000008999CF12D0. +Exception thrown at 0x00007FFD211CCF19 in MediaEngineEMEUWPSample.exe: Microsoft C++ exception: Poco::NotFoundException at memory location 0x0000008999CF1310. +MediaEnginePlayer.h(161): media::MediaEngineProtectionManager::MediaEngineProtectionManager: this 000002113A5133B0, pmpServerKey Windows.Media.Protection.MediaProtectionPMPServer +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 1 +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 3 +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 3 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED, 0000000000000001, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_LOADSTART, 0000000000000000, 00000000 +MediaEnginePlayer.h(90): media::MediaEngineExtension::BeginCreateObject: this 000002113A512070, Location "MediaEnginePlayer", ByteStream 0000000000000000, Type 1 +C:\Project\github.com\media-foundation\samples\MediaEngineEMEUWPSample\src\MediaEnginePlayer.h(95)\(null)!00007FF7B6813211: (caller: 00007FFCC2363957) ReturnHr(2) tid(91b4) C00D36BB An unexpected error has occurred in the operation requested. [media::MediaEngineExtension::BeginCreateObject(Type != MF_OBJECT_MEDIASOURCE)] +MediaEnginePlayer.h(90): media::MediaEngineExtension::BeginCreateObject: this 000002113A512070, Location "MediaEnginePlayer", ByteStream 0000000000000000, Type 0 +MediaEnginePlayer.h(118): media::MediaEngineExtension::EndCreateObject: this 000002113A512070 +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 3 +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 3 +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA, 0000000000000000, 00000000 +MediaEnginePlayer.h(238): media::MediaEngineProtectionManager::Properties: this 000002113A5133B0, m_PropertySet.Size() 3 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_LOADEDDATA, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_CANPLAY, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_SUSPEND, 0000000000000000, 00000000 +MediaEnginePlayer.h(345): media::MediaEnginePlayer::GetSurfaceHandle: ... +MediaEnginePlayer.h(349): media::MediaEnginePlayer::GetSurfaceHandle::::operator (): ... +App.cpp(207): App::SetupVideoVisual: ... +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +MediaEnginePlayer.h(301): media::MediaEnginePlayer::StartPlayingFrom: ... +MediaEnginePlayer.h(304): media::MediaEnginePlayer::StartPlayingFrom::::operator (): ... +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_SEEKING, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_SEEKED, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_PLAY, 0000000000000000, 00000000 +EmeFactory.h(593): media::InputTrustAuthorityMediaSource::Start: this 000002113AC7B500 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_WAITING, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED, 0000000000000000, 00000001 +EmeFactory.h(641): media::InputTrustAuthorityMediaSource::GetInputTrustAuthority: this 000002113AC7B500, StreamIdentifier 1, InterfaceIdentifier {D19F8E98-B126-4446-890C-5DCB7AD71453} +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_WAITING, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED, 0000000000000000, 00000001 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_FORMATCHANGE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY, 0000000000000000, 00000000 +EmeFactory.h(603): media::InputTrustAuthorityMediaSource::Pause: this 000002113AC7B500 +EmeFactory.h(593): media::InputTrustAuthorityMediaSource::Start: this 000002113AC7B500 +Exception thrown at 0x00007FFD211CCF19 (KernelBase.dll) in MediaEngineEMEUWPSample.exe: WinRT originate error - 0x80040155 : 'Failed to find proxy registration for IID: {3041C46C-9A45-47C7-9E23-9C9E1E4D4D50}.'. +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_WAITING, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED, 0000000000000000, 00000001 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_PLAYING, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +MediaEnginePlayer.h(391): media::MediaEnginePlayer::MediaEngineNotify::EventNotify: this 000002113A512C40, Code MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0000000000000000, 00000000 +EmeFactory.h(603): media::InputTrustAuthorityMediaSource::Pause: this 000002113AC7B500 +The program '[21156] MediaEngineEMEUWPSample.exe' has exited with code 1 (0x1). +``` \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/TestContent.h b/samples/MediaEngineEMEUWPSample/src/TestContent.h deleted file mode 100644 index a52f21a..0000000 --- a/samples/MediaEngineEMEUWPSample/src/TestContent.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -namespace TestContent -{ - -// Sample fragmented MP4 CBCS Protected Content -constexpr wchar_t c_contentUrl[] = L"https://test.playready.microsoft.com/media/test/cbcs/bbb.main.30fps.1920x1080.h264.aac.cbcs.sameKeys.mp4"; - -// Sample init data with PlayReady PSSH box -// Contains the following PlayReady Header: -// https://test.playready.microsoft.com/core/rightsmanager.asmx?cfg=(playenablers:(786627d8-c2a6-44be-8f88-08ae255b01a7),sl:150,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:AES128BitCBC) -constexpr uint8_t c_initData[] = { - 0x00, 0x00, 0x03, 0x54, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95, 0x00, 0x00, 0x03, 0x34, 0x34, 0x03, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x2a, 0x03, 0x3c, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x20, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x52, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x22, 0x00, 0x20, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x3f, 0x00, 0x63, 0x00, 0x66, 0x00, 0x67, 0x00, 0x3d, 0x00, 0x28, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x28, 0x00, 0x37, 0x00, 0x38, 0x00, 0x36, 0x00, 0x36, 0x00, 0x32, 0x00, 0x37, 0x00, 0x64, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x63, 0x00, 0x32, 0x00, 0x61, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x34, 0x00, 0x62, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x66, 0x00, 0x38, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x38, 0x00, 0x61, 0x00, 0x65, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x62, 0x00, 0x30, 0x00, 0x31, 0x00, 0x61, 0x00, 0x37, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x73, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x3a, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x31, 0x00, 0x32, 0x00, 0x38, 0x00, 0x42, 0x00, 0x69, 0x00, 0x74, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x29, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x20, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x22, 0x00, 0x20, 0x00, 0x56, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x55, 0x00, 0x45, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x45, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x3e, 0x00 -}; - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.cpp b/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.cpp deleted file mode 100644 index 2cad9dd..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "CdmMediaSource.h" - -namespace media -{ - -IFACEMETHODIMP CdmMediaSource::GetCharacteristics(DWORD *characteristics) noexcept -{ - return m_innerMFSource->GetCharacteristics(characteristics); -} - -IFACEMETHODIMP CdmMediaSource::CreatePresentationDescriptor(IMFPresentationDescriptor **presentationDescriptorOut) noexcept -{ - return m_innerMFSource->CreatePresentationDescriptor(presentationDescriptorOut); -} - -IFACEMETHODIMP CdmMediaSource::Start(IMFPresentationDescriptor *presentationDesc, const GUID *timeFormat, const PROPVARIANT *startPosition) noexcept -{ - return m_innerMFSource->Start(presentationDesc, timeFormat, startPosition); -} - -IFACEMETHODIMP CdmMediaSource::Stop() noexcept -{ - return m_innerMFSource->Stop(); -} - -IFACEMETHODIMP CdmMediaSource::Pause() noexcept -{ - return m_innerMFSource->Pause(); -} - -IFACEMETHODIMP CdmMediaSource::Shutdown() noexcept -{ - { - auto lock = m_itaMapLock.lock(); - m_itaMap.clear(); - } - return m_innerMFSource->Shutdown(); -} - -IFACEMETHODIMP CdmMediaSource::GetEvent(DWORD flags, IMFMediaEvent** eventOut) noexcept -{ - return m_innerMFSource->GetEvent(flags, eventOut); -} - -IFACEMETHODIMP CdmMediaSource::BeginGetEvent(IMFAsyncCallback* callback, IUnknown* state) noexcept -{ - return m_innerMFSource->BeginGetEvent(callback, state); -} - -IFACEMETHODIMP CdmMediaSource::EndGetEvent(IMFAsyncResult* result, IMFMediaEvent** eventOut) noexcept -{ - return m_innerMFSource->EndGetEvent(result, eventOut); -} - -IFACEMETHODIMP CdmMediaSource::QueueEvent(MediaEventType type, REFGUID extendedType, HRESULT status, const PROPVARIANT* value) noexcept -{ - return m_innerMFSource->QueueEvent(type, extendedType, status, value); -} - -IFACEMETHODIMP CdmMediaSource::GetInputTrustAuthority(DWORD streamId, REFIID riid, IUnknown** objectOut) noexcept -try -{ - auto lock = m_itaMapLock.lock(); - winrt::com_ptr itaUnknown; - auto itaIter = m_itaMap.find(streamId); - if (itaIter != m_itaMap.end()) - { - itaUnknown = itaIter->second; - } - else - { - if (!m_cdmTrustedInput) - { - THROW_IF_FAILED(m_contentDecryptionModule->CreateTrustedInput(nullptr, 0, m_cdmTrustedInput.put())); - } - THROW_IF_FAILED(m_cdmTrustedInput->GetInputTrustAuthority(streamId, riid, itaUnknown.put())); - m_itaMap[streamId] = itaUnknown; - } - itaUnknown.copy_to(objectOut); - return S_OK; -} -CATCH_RETURN(); - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.h b/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.h deleted file mode 100644 index b3dbd11..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/CdmMediaSource.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "eme/MediaKeys.h" - -namespace media -{ - -// A custom MF media source wrapper which supports protected playback using a provided ContentDecryptionModule -// Demuxing is performed by the inner source provided by the application -class CdmMediaSource - : public winrt::implements -{ - public: - CdmMediaSource(std::shared_ptr mediaKeys, IMFMediaSource* innerSource) - { - m_contentDecryptionModule = mediaKeys->GetCDM(); - m_innerMFSource.copy_from(innerSource); - } - ~CdmMediaSource() override = default; - - // IMFMediaSource - IFACEMETHODIMP GetCharacteristics(DWORD* characteristics) noexcept override; - IFACEMETHODIMP CreatePresentationDescriptor(IMFPresentationDescriptor** presentationDescriptorOut) noexcept override; - IFACEMETHODIMP Start(IMFPresentationDescriptor* presentationDescriptor, const GUID* guidTimeFormat, - const PROPVARIANT* startPosition) noexcept override; - IFACEMETHODIMP Stop() noexcept override; - IFACEMETHODIMP Pause() noexcept override; - IFACEMETHODIMP Shutdown() noexcept override; - - // IMFMediaEventGenerator - // Note: IMFMediaSource inherits IMFMediaEventGenerator. - IFACEMETHODIMP GetEvent(DWORD flags, IMFMediaEvent** eventOut) noexcept override; - IFACEMETHODIMP BeginGetEvent(IMFAsyncCallback* callback, IUnknown* state) noexcept override; - IFACEMETHODIMP EndGetEvent(IMFAsyncResult* result, IMFMediaEvent** eventOut) noexcept override; - IFACEMETHODIMP QueueEvent(MediaEventType type, REFGUID extendedType, HRESULT status, const PROPVARIANT* value) noexcept override; - - // IMFTrustedInput - IFACEMETHODIMP GetInputTrustAuthority(DWORD streamId, REFIID riid, IUnknown** objectOut) noexcept override; - - private: - wil::critical_section m_itaMapLock; - winrt::com_ptr m_contentDecryptionModule; - winrt::com_ptr m_cdmTrustedInput; - winrt::com_ptr m_innerMFSource; - std::map> m_itaMap; -}; - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.cpp b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.cpp deleted file mode 100644 index 49aa206..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "MediaEngineExtension.h" - -using namespace Microsoft::WRL; - -namespace media -{ - -IFACEMETHODIMP MediaEngineExtension::CanPlayType(BOOL /*isAudioOnly*/, BSTR /*mimeType*/, MF_MEDIA_ENGINE_CANPLAY* result) noexcept -{ - *result = MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED; - return S_OK; -} - -IFACEMETHODIMP MediaEngineExtension::BeginCreateObject(BSTR /*url*/, IMFByteStream* /*byteStream*/, MF_OBJECT_TYPE type, IUnknown** cancelCookie, - IMFAsyncCallback* callback, IUnknown* state) noexcept -try -{ - if(cancelCookie) - { - *cancelCookie = nullptr; - } - winrt::com_ptr localSource; - { - auto lock = m_lock.lock(); - THROW_HR_IF(MF_E_SHUTDOWN, m_hasShutdown); - localSource = m_mfMediaSource; - } - - if(type == MF_OBJECT_MEDIASOURCE && localSource != nullptr) - { - winrt::com_ptr asyncResult; - THROW_IF_FAILED(MFCreateAsyncResult(localSource.get(), callback, state, asyncResult.put())); - THROW_IF_FAILED(asyncResult->SetStatus(S_OK)); - m_uriType = ExtensionUriType::CustomSource; - // Invoke the callback synchronously since no outstanding work is required. - THROW_IF_FAILED(callback->Invoke(asyncResult.get())); - } - else - { - THROW_HR(MF_E_UNEXPECTED); - } - - return S_OK; -} -CATCH_RETURN(); - -STDMETHODIMP MediaEngineExtension::CancelObjectCreation(_In_ IUnknown* /*cancelCookie*/) noexcept -{ - // Cancellation not supported - return E_NOTIMPL; -} - -STDMETHODIMP MediaEngineExtension::EndCreateObject(IMFAsyncResult* result, IUnknown** object) noexcept -try -{ - *object = nullptr; - if(m_uriType == ExtensionUriType::CustomSource) - { - THROW_IF_FAILED(result->GetStatus()); - THROW_IF_FAILED(result->GetObject(object)); - m_uriType = ExtensionUriType::Unknown; - } - else - { - THROW_HR(MF_E_UNEXPECTED); - } - return S_OK; -} -CATCH_RETURN(); - -void MediaEngineExtension::SetMediaSource(IUnknown* mfMediaSource) -{ - auto lock = m_lock.lock(); - THROW_HR_IF(MF_E_SHUTDOWN, m_hasShutdown); - m_mfMediaSource.copy_from(mfMediaSource); -} - -// Break circular references. -void MediaEngineExtension::Shutdown() -{ - auto lock = m_lock.lock(); - if(!m_hasShutdown) - { - m_mfMediaSource = nullptr; - m_hasShutdown = true; - } -} - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.h b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.h deleted file mode 100644 index 70f9549..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineExtension.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -namespace media -{ - -// This implementation of IMFMediaEngineExtension is used to integrate a custom -// IMFMediaSource with the MediaEngine pipeline -class MediaEngineExtension - : public winrt::implements -{ - public: - MediaEngineExtension() = default; - ~MediaEngineExtension() override = default; - - // IMFMediaEngineExtension - IFACEMETHOD(CanPlayType) - (BOOL isAudioOnly, BSTR mimeType, MF_MEDIA_ENGINE_CANPLAY* result) noexcept override; - IFACEMETHOD(BeginCreateObject) - (BSTR url, IMFByteStream* byteStream, MF_OBJECT_TYPE type, IUnknown** cancelCookie, IMFAsyncCallback* callback, - IUnknown* state) noexcept override; - IFACEMETHOD(CancelObjectCreation)(IUnknown* cancelCookie) noexcept override; - IFACEMETHOD(EndCreateObject) - (IMFAsyncResult* result, IUnknown** object) noexcept override; - - // Public methods - void SetMediaSource(IUnknown* mfMediaSource); - void Shutdown(); - - private: - wil::critical_section m_lock; - enum class ExtensionUriType - { - Unknown = 0, - CustomSource - }; - ExtensionUriType m_uriType = ExtensionUriType::Unknown; - bool m_hasShutdown = false; - winrt::com_ptr m_mfMediaSource; -}; - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectedContentInterfaces.h b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectedContentInterfaces.h deleted file mode 100644 index 8af5e8b..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectedContentInterfaces.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -// These interfaces / constants are required for supporting protected playback using a CDM with the IMFMediaEngine -// They are defined in mfidl.h, however they are currently not available in the app partition (this will be addressed in a newer version of the SDK) - -MIDL_INTERFACE("ACF92459-6A61-42bd-B57C-B43E51203CB0") -IMFContentProtectionManager : public IUnknown -{ -public: - virtual /* [local] */ HRESULT STDMETHODCALLTYPE BeginEnableContent( - /* [in] */ IMFActivate * pEnablerActivate, - /* [in] */ IMFTopology * pTopo, - /* [in] */ IMFAsyncCallback * pCallback, - /* [in] */ IUnknown * punkState) = 0; - - virtual /* [local] */ HRESULT STDMETHODCALLTYPE EndEnableContent( - /* [in] */ IMFAsyncResult* pResult) = 0; - -}; - -enum MF_URL_TRUST_STATUS -{ - MF_LICENSE_URL_UNTRUSTED = 0, - MF_LICENSE_URL_TRUSTED = (MF_LICENSE_URL_UNTRUSTED + 1), - MF_LICENSE_URL_TAMPERED = (MF_LICENSE_URL_TRUSTED + 1) -}; - -MIDL_INTERFACE("D3C4EF59-49CE-4381-9071-D5BCD044C770") -IMFContentEnabler : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE GetEnableType( - /* [out] */ __RPC__out GUID * pType) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetEnableURL( - /* [size_is][size_is][out] */ __RPC__deref_out_ecount_full_opt(*pcchURL) LPWSTR* ppwszURL, - /* [out] */ __RPC__out DWORD* pcchURL, - /* [unique][out][in] */ __RPC__inout_opt MF_URL_TRUST_STATUS* pTrustStatus) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetEnableData( - /* [size_is][size_is][out] */ __RPC__deref_out_ecount_full_opt(*pcbData) BYTE** ppbData, - /* [out] */ __RPC__out DWORD* pcbData) = 0; - - virtual HRESULT STDMETHODCALLTYPE IsAutomaticSupported( - /* [out] */ __RPC__out BOOL* pfAutomatic) = 0; - - virtual HRESULT STDMETHODCALLTYPE AutomaticEnable(void) = 0; - - virtual HRESULT STDMETHODCALLTYPE MonitorEnable(void) = 0; - - virtual HRESULT STDMETHODCALLTYPE Cancel(void) = 0; - -}; - -EXTERN_GUID(MFENABLETYPE_MF_UpdateRevocationInformation, 0xe558b0b5, 0xb3c4, 0x44a0, 0x92, 0x4c, 0x50, 0xd1, 0x78, 0x93, 0x23, 0x85); -EXTERN_GUID(MFENABLETYPE_MF_UpdateUntrustedComponent, 0x9879f3d6, 0xcee2, 0x48e6, 0xb5, 0x73, 0x97, 0x67, 0xab, 0x17, 0x2f, 0x16); -EXTERN_GUID(MFENABLETYPE_MF_RebootRequired, 0x6d4d3d4b, 0x0ece, 0x4652, 0x8b, 0x3a, 0xf2, 0xd2, 0x42, 0x60, 0xd8, 0x87); - -MIDL_INTERFACE("fbe256c1-43cf-4a9b-8cb8-ce8632a34186") -IMFMediaEngineClassFactory4 : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE CreateContentDecryptionModuleFactory( - /* [annotation][in] */ - _In_ LPCWSTR keySystem, - /* [annotation][in] */ - _In_ REFIID riid, - /* [annotation][iid_is][out] */ - _Outptr_ LPVOID * ppvObject) = 0; - -}; - -enum MF_MEDIAKEYS_REQUIREMENT -{ - MF_MEDIAKEYS_REQUIREMENT_REQUIRED = 1, - MF_MEDIAKEYS_REQUIREMENT_OPTIONAL = 2, - MF_MEDIAKEYS_REQUIREMENT_NOT_ALLOWED = 3 -}; - -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_INITDATATYPES = { { 0x497d231b, 0x4eb9, 0x4df0, { 0xb4, 0x74, 0xb9, 0xaf, 0xeb, 0x0a, 0xdf, 0x38 } }, PID_FIRST_USABLE+0x00000001 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_DISTINCTIVEID = { { 0x7dc9c4a5, 0x12be, 0x497e, { 0x8b, 0xff, 0x9b, 0x60, 0xb2, 0xdc, 0x58, 0x45 } }, PID_FIRST_USABLE+0x00000002 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_PERSISTEDSTATE = { { 0x5d4df6ae, 0x9af1, 0x4e3d, { 0x95, 0x5b, 0x0e, 0x4b, 0xd2, 0x2f, 0xed, 0xf0 } }, PID_FIRST_USABLE+0x00000003 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_AUDIOCAPABILITIES = { { 0x980fbb84, 0x297d, 0x4ea7, { 0x89, 0x5f, 0xbc, 0xf2, 0x8a, 0x46, 0x28, 0x81 } }, PID_FIRST_USABLE+0x00000004 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_VIDEOCAPABILITIES = { { 0xb172f83d, 0x30dd, 0x4c10, { 0x80, 0x06, 0xed, 0x53, 0xda, 0x4d, 0x3b, 0xdb } }, PID_FIRST_USABLE+0x00000005 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_LABEL = { { 0x9eae270e, 0xb2d7, 0x4817, { 0xb8, 0x8f, 0x54, 0x00, 0x99, 0xf2, 0xef, 0x4e } }, PID_FIRST_USABLE+0x00000006 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_SESSIONTYPES = { { 0x7623384f, 0x00f5, 0x4376, { 0x86, 0x98, 0x34, 0x58, 0xdb, 0x03, 0x0e, 0xd5 } }, PID_FIRST_USABLE+0x00000007 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_ROBUSTNESS = { { 0x9d3d2b9e, 0x7023, 0x4944, { 0xa8, 0xf5, 0xec, 0xca, 0x52, 0xa4, 0x69, 0x90 } }, PID_FIRST_USABLE+0x00000001 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_CONTENTTYPE = { { 0x289fb1fc, 0xd9c4, 0x4cc7, { 0xb2, 0xbe, 0x97, 0x2b, 0x0e, 0x9b, 0x28, 0x3a } }, PID_FIRST_USABLE+0x00000002 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_CDM_INPRIVATESTOREPATH = { { 0xec305fd9, 0x039f, 0x4ac8, { 0x98, 0xda, 0xe7, 0x92, 0x1e, 0x00, 0x6a, 0x90 } }, PID_FIRST_USABLE+0x00000001 }; -EXTERN_C const DECLSPEC_SELECTANY PROPERTYKEY MF_EME_CDM_STOREPATH = { { 0xf795841e, 0x99f9, 0x44d7, { 0xaf, 0xc0, 0xd3, 0x09, 0xc0, 0x4c, 0x94, 0xab } }, PID_FIRST_USABLE+0x00000002 }; \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.cpp b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.cpp deleted file mode 100644 index f40142d..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "MediaEngineProtectionManager.h" - -namespace media -{ - -MediaEngineProtectionManager::MediaEngineProtectionManager(std::shared_ptr mediaKeys) -{ - m_contentDecryptionModule = mediaKeys->GetCDM(); - // Use the PMP server from the CDM - this ensures that the media engine uses the same protected process as the CDM - winrt::com_ptr cdmServices = m_contentDecryptionModule.as(); - winrt::com_ptr abiPmpServer; - THROW_IF_FAILED(cdmServices->GetService(MF_CONTENTDECRYPTIONMODULE_SERVICE, IID_PPV_ARGS(abiPmpServer.put()))); - winrt::Windows::Media::Protection::MediaProtectionPMPServer pmpServer{ nullptr }; - winrt::copy_from_abi(pmpServer, abiPmpServer.get()); - SetPMPServer(pmpServer); -} - -MediaEngineProtectionManager::~MediaEngineProtectionManager() = default; - -void MediaEngineProtectionManager::SetPMPServer(winrt::Windows::Media::Protection::MediaProtectionPMPServer pmpServer) -{ - using MapType = winrt::Windows::Foundation::Collections::IMap; - auto propertyMap = m_propertySet.as(); - - // MFMediaEngine uses |pmpServerKey| to get the Protected Media Path (PMP) server used for playing protected content. - // This is not currently documented in MSDN. - winrt::hstring pmpServerKey{ L"Windows.Media.Protection.MediaProtectionPMPServer" }; - propertyMap.Insert(pmpServerKey, pmpServer); -} - -HRESULT MediaEngineProtectionManager::BeginEnableContent(IMFActivate* enablerActivate, IMFTopology* /*topology*/, - IMFAsyncCallback* callback, IUnknown* state) noexcept -try -{ - winrt::com_ptr unknownObject; - winrt::com_ptr asyncResult; - THROW_IF_FAILED(MFCreateAsyncResult(nullptr, callback, state, asyncResult.put())); - THROW_IF_FAILED(enablerActivate->ActivateObject(IID_PPV_ARGS(unknownObject.put()))); - - // |enablerType| can be obtained from IMFContentEnabler - // (https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfcontentenabler). - // If not, try IMediaProtectionServiceRequest - // (https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.imediaprotectionservicerequest). - GUID enablerType = GUID_NULL; - winrt::com_ptr contentEnabler = unknownObject.try_as(); - if (contentEnabler) - { - THROW_IF_FAILED(contentEnabler->GetEnableType(&enablerType)); - } - else - { - winrt::com_ptr - serviceRequest = unknownObject.as(); - THROW_IF_FAILED(serviceRequest->get_Type(&enablerType)); - } - - // Unhandled enabler types - THROW_HR_IF(MF_E_REBOOT_REQUIRED, enablerType == MFENABLETYPE_MF_RebootRequired); - THROW_HR_IF(MF_E_GRL_VERSION_TOO_LOW, enablerType == MFENABLETYPE_MF_UpdateRevocationInformation); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_IMAGE_HASH), enablerType == MFENABLETYPE_MF_UpdateUntrustedComponent); - // Other enabler types are handled by the CDM - THROW_IF_FAILED(m_contentDecryptionModule->SetContentEnabler(contentEnabler.get(), asyncResult.get())); - return S_OK; -} CATCH_RETURN(); - -HRESULT MediaEngineProtectionManager::EndEnableContent(IMFAsyncResult* /*asyncResult*/) noexcept -{ - return S_OK; -} - - -// MediaEngineProtectionManager implementation -winrt::event_token MediaEngineProtectionManager::ServiceRequested(winrt::Windows::Media::Protection::ServiceRequestedEventHandler const handler) -{ - THROW_HR(E_NOTIMPL); -} - -void MediaEngineProtectionManager::ServiceRequested(winrt::event_token const /*cookie*/) -{ - THROW_HR(E_NOTIMPL); -} - -winrt::event_token MediaEngineProtectionManager::RebootNeeded(winrt::Windows::Media::Protection::RebootNeededEventHandler const handler) -{ - THROW_HR(E_NOTIMPL); -} - -void MediaEngineProtectionManager::RebootNeeded(winrt::event_token const /*cookie*/) -{ - THROW_HR(E_NOTIMPL); -} - -winrt::event_token MediaEngineProtectionManager::ComponentLoadFailed(winrt::Windows::Media::Protection::ComponentLoadFailedEventHandler const handler) -{ - THROW_HR(E_NOTIMPL); -} - -void MediaEngineProtectionManager::ComponentLoadFailed(winrt::event_token const /*cookie*/) -{ - THROW_HR(E_NOTIMPL); -} - -winrt::Windows::Foundation::Collections::PropertySet MediaEngineProtectionManager::Properties() -{ - return m_propertySet; -} - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.h b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.h deleted file mode 100644 index b886b79..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineProtectionManager.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "eme/MediaKeys.h" -#include "media/MediaEngineProtectedContentInterfaces.h" - -namespace media { - - // Implements IMFContentProtectionManager - // (https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfcontentprotectionmanager) - // and winrt::Windows::Media::Protection::IMediaProtectionManager - // (https://docs.microsoft.com/en-us/uwp/api/windows.media.protection.mediaprotectionmanager) - // required by IMFMediaEngineProtectedContent::SetContentProtectionManager in - // https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfmediaengineprotectedcontent-setcontentprotectionmanager. - class MediaEngineProtectionManager - : public winrt::implements { - public: - MediaEngineProtectionManager(std::shared_ptr mediaKeys); - ~MediaEngineProtectionManager() override; - - // IMFContentProtectionManager. - IFACEMETHODIMP BeginEnableContent(IMFActivate* enablerActivate, IMFTopology* topology, IMFAsyncCallback* callback, - IUnknown* state) noexcept override; - IFACEMETHODIMP EndEnableContent(IMFAsyncResult* asyncResult) noexcept override; - - // IMediaProtectionManager. - // MFMediaEngine can query this interface to invoke Properties(). - winrt::event_token ServiceRequested(winrt::Windows::Media::Protection::ServiceRequestedEventHandler const handler); - void ServiceRequested(winrt::event_token const cookie); - winrt::event_token RebootNeeded(winrt::Windows::Media::Protection::RebootNeededEventHandler const handler); - void RebootNeeded(winrt::event_token const cookie); - winrt::event_token ComponentLoadFailed(winrt::Windows::Media::Protection::ComponentLoadFailedEventHandler const handler); - void ComponentLoadFailed(winrt::event_token const cookie); - winrt::Windows::Foundation::Collections::PropertySet Properties(); - - protected: - winrt::com_ptr m_contentDecryptionModule; - winrt::Windows::Foundation::Collections::PropertySet m_propertySet; - void SetPMPServer(winrt::Windows::Media::Protection::MediaProtectionPMPServer pmpServer); - }; - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.cpp b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.cpp deleted file mode 100644 index c3dda97..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "CdmMediaSource.h" -#include "MediaEngineWrapper.h" -#include "MediaFoundationHelpers.h" -#include -#include - -using namespace Microsoft::WRL; - -namespace media -{ - -namespace -{ - class MediaEngineCallbackHelper - : public winrt::implements - { - public: - MediaEngineCallbackHelper(std::function onLoadedCB, MediaEngineWrapper::ErrorCB errorCB, - MediaEngineWrapper::BufferingStateChangeCB bufferingStateChangeCB, std::function playbackEndedCB, - std::function timeUpdateCB) - : m_onLoadedCB(onLoadedCB), m_errorCB(errorCB), m_bufferingStateChangeCB(bufferingStateChangeCB), - m_playbackEndedCB(playbackEndedCB), m_timeUpdateCB(timeUpdateCB) - { - // Ensure that callbacks are valid - THROW_HR_IF(E_INVALIDARG, !m_onLoadedCB); - THROW_HR_IF(E_INVALIDARG, !m_errorCB); - THROW_HR_IF(E_INVALIDARG, !m_bufferingStateChangeCB); - THROW_HR_IF(E_INVALIDARG, !m_playbackEndedCB); - THROW_HR_IF(E_INVALIDARG, !m_timeUpdateCB); - } - virtual ~MediaEngineCallbackHelper() = default; - - void DetachParent() - { - auto lock = m_lock.lock(); - m_detached = true; - m_onLoadedCB = nullptr; - m_errorCB = nullptr; - m_bufferingStateChangeCB = nullptr; - m_playbackEndedCB = nullptr; - m_timeUpdateCB = nullptr; - } - - // IMFMediaEngineNotify - IFACEMETHODIMP EventNotify(DWORD eventCode, DWORD_PTR param1, DWORD param2) noexcept override - try - { - auto lock = m_lock.lock(); - THROW_HR_IF(MF_E_SHUTDOWN, m_detached); - - switch((MF_MEDIA_ENGINE_EVENT)eventCode) - { - case MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA: - m_onLoadedCB(); - break; - case MF_MEDIA_ENGINE_EVENT_ERROR: - m_errorCB((MF_MEDIA_ENGINE_ERR)param1, (HRESULT)param2); - break; - case MF_MEDIA_ENGINE_EVENT_PLAYING: - m_bufferingStateChangeCB(MediaEngineWrapper::BufferingState::HAVE_ENOUGH); - break; - case MF_MEDIA_ENGINE_EVENT_WAITING: - m_bufferingStateChangeCB(MediaEngineWrapper::BufferingState::HAVE_NOTHING); - break; - case MF_MEDIA_ENGINE_EVENT_ENDED: - m_playbackEndedCB(); - break; - case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: - m_timeUpdateCB(); - break; - default: - break; - } - - return S_OK; - } - CATCH_RETURN(); - - private: - wil::critical_section m_lock; - std::function m_onLoadedCB; - MediaEngineWrapper::ErrorCB m_errorCB; - MediaEngineWrapper::BufferingStateChangeCB m_bufferingStateChangeCB; - std::function m_playbackEndedCB; - std::function m_timeUpdateCB; - bool m_detached = false; - }; -} // namespace - -// Public methods - -void MediaEngineWrapper::Initialize(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) -{ - RunSyncInMTA([&]() - { - InitializeVideo(); - CreateMediaEngine(mediaSource, mediaKeys); - }); -} - -void MediaEngineWrapper::Pause() -{ - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - THROW_IF_FAILED(m_mediaEngine->Pause()); - }); -} - -void MediaEngineWrapper::Shutdown() -{ - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - THROW_IF_FAILED(m_mediaEngine->Shutdown()); - }); -} - -void MediaEngineWrapper::StartPlayingFrom(uint64_t timeStamp) -{ - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - const double timestampInSeconds = ConvertHnsToSeconds(timeStamp); - THROW_IF_FAILED(m_mediaEngine->SetCurrentTime(timestampInSeconds)); - THROW_IF_FAILED(m_mediaEngine->Play()); - }); -} - -void MediaEngineWrapper::SetPlaybackRate(double playbackRate) -{ - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - THROW_IF_FAILED(m_mediaEngine->SetPlaybackRate(playbackRate)); - }); -} - -void MediaEngineWrapper::SetVolume(float volume) -{ - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - THROW_IF_FAILED(m_mediaEngine->SetVolume(volume)); - }); -} - -uint64_t MediaEngineWrapper::GetMediaTime() -{ - uint64_t currentTimeInHns = 0; - RunSyncInMTA([&]() - { - auto lock = m_lock.lock(); - double currentTimeInSeconds = m_mediaEngine->GetCurrentTime(); - currentTimeInHns = ConvertSecondsToHns(currentTimeInSeconds); - }); - return currentTimeInHns; -} - -HANDLE MediaEngineWrapper::GetSurfaceHandle() -{ - HANDLE surfaceHandle = INVALID_HANDLE_VALUE; - RunSyncInMTA([&](){ - surfaceHandle = m_dcompSurfaceHandle.get(); - }); - return surfaceHandle; -} - -void MediaEngineWrapper::OnWindowUpdate(uint32_t width, uint32_t height) -{ - RunSyncInMTA([&](){ - auto lock = m_lock.lock(); - - if(width != m_width || height != m_height) - { - m_width = width; - m_height = height; - } - - if(m_mediaEngine) - { - RECT destRect{ 0, 0, static_cast(m_width), static_cast(m_height) }; - winrt::com_ptr mediaEngineEx = m_mediaEngine.as(); - THROW_IF_FAILED(mediaEngineEx->UpdateVideoStream(nullptr, &destRect, nullptr)); - } - }); -} - -// Internal methods - -void MediaEngineWrapper::CreateMediaEngine(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) -{ - winrt::com_ptr classFactory; - winrt::com_ptr creationAttributes; - - m_platformRef.Startup(); - - InitializeVideo(); - - THROW_IF_FAILED(MFCreateAttributes(creationAttributes.put(), 7)); - m_callbackHelper = winrt::make([&]() { this->OnLoaded(); }, - [&](MF_MEDIA_ENGINE_ERR error, HRESULT hr) { this->OnError(error, hr); }, - [&](BufferingState state) { this->OnBufferingStateChange(state); }, - [&]() { this->OnPlaybackEnded(); }, [&]() { this->OnTimeUpdate(); }); - THROW_IF_FAILED(creationAttributes->SetUnknown(MF_MEDIA_ENGINE_CALLBACK, m_callbackHelper.get())); - THROW_IF_FAILED(creationAttributes->SetUINT32(MF_MEDIA_ENGINE_CONTENT_PROTECTION_FLAGS, MF_MEDIA_ENGINE_ENABLE_PROTECTED_CONTENT)); - THROW_IF_FAILED(creationAttributes->SetGUID(MF_MEDIA_ENGINE_BROWSER_COMPATIBILITY_MODE, MF_MEDIA_ENGINE_BROWSER_COMPATIBILITY_MODE_IE_EDGE)); - THROW_IF_FAILED(creationAttributes->SetUINT32(MF_MEDIA_ENGINE_AUDIO_CATEGORY, AudioCategory_Media)); - - if(m_dxgiDeviceManager != nullptr) - { - THROW_IF_FAILED(creationAttributes->SetUnknown(MF_MEDIA_ENGINE_DXGI_MANAGER, m_dxgiDeviceManager.get())); - } - - m_mediaEngineExtension = winrt::make_self(); - THROW_IF_FAILED(creationAttributes->SetUnknown(MF_MEDIA_ENGINE_EXTENSION, m_mediaEngineExtension.get())); - - THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(classFactory.put()))); - THROW_IF_FAILED(classFactory->CreateInstance(0, creationAttributes.get(), m_mediaEngine.put())); - - if (mediaKeys) - { - // Set a CdmMediaSource which wraps the app-provided media source and exposes an IMFTrustedInput to the pipeline - winrt::com_ptr cdmMediaSource = winrt::make_self(mediaKeys, mediaSource); - winrt::com_ptr sourceUnknown = cdmMediaSource.as(); - m_mediaEngineExtension->SetMediaSource(sourceUnknown.get()); - - // Setup the content protection manager for handling content enabling requests during playback - m_protectionManager = winrt::make_self(mediaKeys); - winrt::com_ptr protectedMediaEngine = m_mediaEngine.as(); - THROW_IF_FAILED(protectedMediaEngine->SetContentProtectionManager(m_protectionManager.get())); - } - else - { - // Set the app-provided media source directly on the media engine extension - winrt::com_ptr sourceUnknown; - THROW_IF_FAILED(mediaSource->QueryInterface(IID_PPV_ARGS(sourceUnknown.put()))); - m_mediaEngineExtension->SetMediaSource(sourceUnknown.get()); - } - - winrt::com_ptr mediaEngineEx = m_mediaEngine.as(); - if(!m_hasSetSource) - { - wil::unique_bstr source = wil::make_bstr(L"customSrc"); - THROW_IF_FAILED(mediaEngineEx->SetSource(source.get())); - m_hasSetSource = true; - } - else - { - THROW_IF_FAILED(mediaEngineEx->Load()); - } -} - -void MediaEngineWrapper::InitializeVideo() -{ - m_dxgiDeviceManager = nullptr; - THROW_IF_FAILED(MFLockDXGIDeviceManager(&m_deviceResetToken, m_dxgiDeviceManager.put())); - - winrt::com_ptr d3d11Device; - UINT creationFlags = (D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT | - D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS); - constexpr D3D_FEATURE_LEVEL featureLevels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1}; - THROW_IF_FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, - d3d11Device.put(), nullptr, nullptr)); - - winrt::com_ptr multithreadedDevice = d3d11Device.as(); - multithreadedDevice->SetMultithreadProtected(TRUE); - - THROW_IF_FAILED(m_dxgiDeviceManager->ResetDevice(d3d11Device.get(), m_deviceResetToken)); -} - -// Callback methods - -void MediaEngineWrapper::OnLoaded() -{ - // Call asynchronously to prevent potential deadlock due to lock inversion - // Ensure that the callback lambda holds a reference to this object to ensure that it isn't destroyed while there is a pending callback - winrt::com_ptr ref; - ref.copy_from(this); - media::MFPutWorkItem([&, ref]() { - { - auto lock = m_lock.lock(); - // Put media engine into DCOMP mode (as opposed to frame server mode) and - // obtain a handle to the DCOMP surface handle - winrt::com_ptr mediaEngineEx = m_mediaEngine.as(); - THROW_IF_FAILED(mediaEngineEx->EnableWindowlessSwapchainMode(true)); - - // If the wrapper has been notified of the actual window width / height, use the correct values, otherwise, use a default size of 640x480 - uint32_t width = m_width != 0 ? m_width : 640; - uint32_t height = m_height != 0 ? m_height : 480; - OnWindowUpdate(width, height); - THROW_IF_FAILED(mediaEngineEx->GetVideoSwapchainHandle(&m_dcompSurfaceHandle)); - } - m_initializedCB(); - }); -} - -void MediaEngineWrapper::OnError(MF_MEDIA_ENGINE_ERR error, HRESULT hr) -{ - if(m_errorCB) - { - m_errorCB(error, hr); - } -} - -void MediaEngineWrapper::OnBufferingStateChange(BufferingState state) -{ - if(m_bufferingStateChangeCB) - { - m_bufferingStateChangeCB(state); - } -} - -void MediaEngineWrapper::OnPlaybackEnded() -{ - if(m_playbackEndedCB) - { - m_playbackEndedCB(); - } -} - -void MediaEngineWrapper::OnTimeUpdate() -{ - if(m_timeUpdateCB) - { - m_timeUpdateCB(); - } -} - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.h b/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.h deleted file mode 100644 index b41818d..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/MediaEngineWrapper.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "eme/MediaKeys.h" -#include "MediaFoundationHelpers.h" -#include "MediaEngineExtension.h" -#include "MediaEngineProtectionManager.h" - -namespace media -{ - -// This class handles creation and management of the MediaFoundation -// MediaEngine. -// - It uses the provided IMFMediaSource to feed media samples into the -// MediaEngine pipeline. -// - A DirectComposition surface is exposed to the application layer to -// incorporate video from the MediaEngine in its visual tree. -// - The application must provide a callback object (std::function) which -// is invoked after the media engine has loaded and the DirectComposition surface is available. -class MediaEngineWrapper : public winrt::implements -{ - public: - using ErrorCB = std::function; - - enum class BufferingState - { - HAVE_NOTHING = 0, - HAVE_ENOUGH = 1 - }; - using BufferingStateChangeCB = std::function; - - MediaEngineWrapper(std::function initializedCB, ErrorCB errorCB, BufferingStateChangeCB bufferingStateChangeCB, - std::function playbackEndedCB, std::function timeUpdateCB) - : m_initializedCB(initializedCB), m_errorCB(errorCB), m_bufferingStateChangeCB(bufferingStateChangeCB), - m_playbackEndedCB(playbackEndedCB), m_timeUpdateCB(timeUpdateCB) - { - // The initialize callback is required - THROW_HR_IF(E_INVALIDARG, !m_initializedCB); - } - ~MediaEngineWrapper() {} - - // Create the media engine with the provided media source and CDM - void Initialize(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys); - - // Stop playback and cleanup resources - void Pause(); - void Shutdown(); - - // Control various aspects of playback - void StartPlayingFrom(uint64_t timeStamp); - void SetPlaybackRate(double playbackRate); - void SetVolume(float volume); - - // Query the current playback position - uint64_t GetMediaTime(); - - // Get a handle to a DCOMP surface for integrating into a visual tree - HANDLE GetSurfaceHandle(); - - // Inform media engine of output window position & size changes - void OnWindowUpdate(uint32_t width, uint32_t height); - - private: - wil::critical_section m_lock; - std::function m_initializedCB; - ErrorCB m_errorCB; - BufferingStateChangeCB m_bufferingStateChangeCB; - std::function m_playbackEndedCB; - std::function m_timeUpdateCB; - MFPlatformRef m_platformRef; - winrt::com_ptr m_mediaEngine; - UINT m_deviceResetToken = 0; - winrt::com_ptr m_dxgiDeviceManager; - winrt::com_ptr m_mediaEngineExtension; - winrt::com_ptr m_callbackHelper; - winrt::com_ptr m_protectionManager; - wil::unique_handle m_dcompSurfaceHandle; - uint32_t m_width = 0; - uint32_t m_height = 0; - bool m_hasSetSource = false; - void InitializeVideo(); - void CreateMediaEngine(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys); - void OnLoaded(); - void OnError(MF_MEDIA_ENGINE_ERR error, HRESULT hr); - void OnBufferingStateChange(BufferingState state); - void OnPlaybackEnded(); - void OnTimeUpdate(); -}; - -} // namespace media \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.cpp deleted file mode 100644 index 0a2baaa..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "CdmConfigurationProperties.h" - -#include "media/MediaEngineProtectedContentInterfaces.h" - -namespace media -{ - -namespace eme -{ - -namespace -{ - void SetBstrProperty(IPropertyStore* propertyStore, const PROPERTYKEY& propertyKey, std::wstring& value) - { - wil::unique_bstr bstr = wil::make_bstr(value.c_str()); - wil::unique_prop_variant property; - property.vt = VT_BSTR; - property.bstrVal = bstr.release(); - propertyStore->SetValue(propertyKey, property); - } -} - -winrt::com_ptr CdmConfigurationProperties::ConvertToPropertyStore() -{ - winrt::com_ptr propertyStore; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(propertyStore.put()))); - - // Storage path - SetBstrProperty(propertyStore.get(), MF_EME_CDM_STOREPATH, storagePath); - - // Only set private storage path when specified - if(inPrivateStoragePath.length() > 0) - { - SetBstrProperty(propertyStore.get(), MF_EME_CDM_INPRIVATESTOREPATH, inPrivateStoragePath); - } - - return propertyStore; -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.h b/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.h deleted file mode 100644 index 60e79eb..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/CdmConfigurationProperties.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -namespace media -{ - -namespace eme -{ - // Struct which encapsulates CDM configuration properties - struct CdmConfigurationProperties - { - // Use this property to pass a storage location the CDM can use for initialization. - // This storage location will also be used for content specific data if INPRIVATESTOREPATH is not set. - // The app should not delete the store location to optimize performance of the CDM. - std::wstring storagePath; - // Use this property to pass a storage location the CDM can use for content specific data (e.g licenses) - // The app should delete the store location after the CDM object has been released. - std::wstring inPrivateStoragePath; - - winrt::com_ptr ConvertToPropertyStore(); - }; -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.cpp deleted file mode 100644 index 7cd6d9f..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "EmeFactory.h" - -namespace media -{ - -namespace eme -{ - -EmeFactory::EmeFactory(CdmConfigurationProperties cdmConfigProperties) -{ - THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(m_classFactory.put()))); - m_cdmConfigProperties = cdmConfigProperties; -} - -std::shared_ptr EmeFactory::requestMediaKeySystemAccess(const std::wstring& keySystem, std::vector& configurations) -{ - // Use a cached CDM factory if client is querying for the same keysystem, otherwise create a new CDM factory - if(!m_cdmFactory || m_cachedFactoryKeySystem != keySystem) - { - if(FAILED(LOG_IF_FAILED(m_classFactory->CreateContentDecryptionModuleFactory(keySystem.c_str(), IID_PPV_ARGS(m_cdmFactory.put()))))) - { - return nullptr; - } - m_cachedFactoryKeySystem = keySystem; - } - - if(!m_cdmFactory->IsTypeSupported(keySystem.c_str(), nullptr)) - { - return nullptr; - } - winrt::com_ptr cdmAccess; - std::vector> configurationPropertyStores; - for(auto& config : configurations) - { - wil::com_ptr_t propStore; - propStore.attach(config.ConvertToPropertyStore().detach()); - configurationPropertyStores.push_back(propStore); - } - if(FAILED(LOG_IF_FAILED(m_cdmFactory->CreateContentDecryptionModuleAccess(keySystem.c_str(), configurationPropertyStores[0].addressof(), static_cast(configurationPropertyStores.size()), cdmAccess.put())))) - { - return nullptr; - } - - return std::make_shared(m_cdmConfigProperties, cdmAccess.get()); -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.h b/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.h deleted file mode 100644 index 9135c38..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/EmeFactory.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "media/eme/CdmConfigurationProperties.h" -#include "media/eme/MediaKeySystemAccess.h" -#include "media/eme/MediaKeySystemConfiguration.h" -#include "media/MediaEngineProtectedContentInterfaces.h" - -namespace media -{ - namespace eme - { - // Class used to query for EME keysystem support and attempt creation of a MediaKeySystemAccess object for a specified keysystem - class EmeFactory - { - public: - EmeFactory(CdmConfigurationProperties cdmConfigProperties); - virtual ~EmeFactory() = default; - - // Used to request creation of a MediaKeySystemAccess. Based on: - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-navigator-requestmediakeysystemaccess - // Returns: MediaKeySystemAccess on success, or nullptr if the keysystem or none of the specified configurations are supported - std::shared_ptr requestMediaKeySystemAccess(const std::wstring& keySystem, std::vector& configurations); - - private: - CdmConfigurationProperties m_cdmConfigProperties; - winrt::com_ptr m_classFactory; - winrt::com_ptr m_cdmFactory; - std::wstring m_cachedFactoryKeySystem; - }; - } -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.cpp deleted file mode 100644 index 768893d..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "MediaKeySession.h" - -namespace media -{ - -namespace eme -{ - -namespace -{ - class SessionCallbackHelper : public winrt::implements - { - public: - SessionCallbackHelper(MediaKeySession::KeyMessageCB&& keyMessageCB, MediaKeySession::KeyStatusChangedCB&& keyStatusChangedCB) : m_keyMessageCB(keyMessageCB), m_keyStatusChangedCB(keyStatusChangedCB) - { - THROW_HR_IF(E_INVALIDARG, !m_keyMessageCB); - THROW_HR_IF(E_INVALIDARG, !m_keyStatusChangedCB); - } - - virtual ~SessionCallbackHelper() - { - DetachParent(); - } - - void DetachParent() - { - auto lock = m_lock.lock(); - m_keyMessageCB = nullptr; - m_keyStatusChangedCB = nullptr; - } - - // Session callback interface - IFACEMETHODIMP KeyMessage(MF_MEDIAKEYSESSION_MESSAGETYPE messageType, const BYTE* message, DWORD messageSize, LPCWSTR destinationUrl) noexcept override - try - { - auto lock = m_lock.lock(); - m_keyMessageCB(messageType, gsl::span(const_cast(message), static_cast(messageSize)), destinationUrl); - return S_OK; - } - CATCH_RETURN(); - - IFACEMETHODIMP KeyStatusChanged() noexcept override - try - { - auto lock = m_lock.lock(); - m_keyStatusChangedCB(); - return S_OK; - } - CATCH_RETURN(); - - private: - wil::critical_section m_lock; - MediaKeySession::KeyMessageCB m_keyMessageCB; - MediaKeySession::KeyStatusChangedCB m_keyStatusChangedCB; - }; -} - -MediaKeySession::MediaKeySession(IMFContentDecryptionModule* cdm, MF_MEDIAKEYSESSION_TYPE sessionType) -{ - m_sessionCallbacks = winrt::make( - std::bind(&MediaKeySession::OnKeyMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - std::bind(&MediaKeySession::OnKeyStatusChanged, this)); - THROW_IF_FAILED(cdm->CreateSession(sessionType, m_sessionCallbacks.get(), m_cdmSession.put())); -} - -MediaKeySession::~MediaKeySession() -{ - static_cast(m_sessionCallbacks.get())->DetachParent(); -} - -wil::unique_cotaskmem_string MediaKeySession::sessionId() -{ - wil::unique_cotaskmem_string sessionIdStr; - THROW_IF_FAILED(m_cdmSession->GetSessionId(sessionIdStr.put())); - return sessionIdStr; -} - -double MediaKeySession::expiration() -{ - double expirationValue = 0.0; - THROW_IF_FAILED(m_cdmSession->GetExpiration(&expirationValue)); - return expirationValue; -} - -std::vector MediaKeySession::keyStatuses() -{ - wil::unique_cotaskmem_array_ptr abiKeyStatuses; - THROW_IF_FAILED(m_cdmSession->GetKeyStatuses(abiKeyStatuses.put(), abiKeyStatuses.size_address())); - std::vector statusPairs(abiKeyStatuses.size()); - for(size_t i = 0; i < abiKeyStatuses.size(); i++) - { - std::vector keyId(abiKeyStatuses[i].cbKeyId); - memcpy(&keyId[0], abiKeyStatuses[i].pbKeyId, keyId.size()); - statusPairs[i] = std::make_tuple(std::move(keyId), abiKeyStatuses[i].eMediaKeyStatus); - } - return statusPairs; -} - -bool MediaKeySession::load(const std::wstring& sessionId) -{ - BOOL loaded = FALSE; - THROW_IF_FAILED(m_cdmSession->Load(sessionId.c_str(), &loaded)); - return !!loaded; -} - -void MediaKeySession::onmessage(KeyMessageCB&& keyMessageCB) -{ - m_keyMessageCB = std::move(keyMessageCB); -} - -void MediaKeySession::onkeystatuseschange(KeyStatusChangedCB&& keyStatusChangedCB) -{ - m_keyStatusChangedCB = std::move(keyStatusChangedCB); -} - -void MediaKeySession::generateRequest(const std::wstring& initDataType, std::vector& initData) -{ - THROW_IF_FAILED(m_cdmSession->GenerateRequest(initDataType.c_str(), &initData[0], static_cast(initData.size()))); -} - -void MediaKeySession::update(gsl::span responseData) -{ - THROW_IF_FAILED(m_cdmSession->Update(responseData.data(), static_cast(responseData.size()))); -} - -void MediaKeySession::close() -{ - THROW_IF_FAILED(m_cdmSession->Close()); -} - -void MediaKeySession::remove() -{ - THROW_IF_FAILED(m_cdmSession->Remove()); -} - -// Private callback methods - -void MediaKeySession::OnKeyMessage(MF_MEDIAKEYSESSION_MESSAGETYPE messageType, gsl::span message, LPCWSTR destinationUrl) -{ - m_keyMessageCB(messageType, message, destinationUrl); -} - -void MediaKeySession::OnKeyStatusChanged() -{ - m_keyStatusChangedCB(); -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.h b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.h deleted file mode 100644 index 51343ba..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySession.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -namespace media -{ - -namespace eme -{ - -using MediaKeyStatusPair = std::tuple, MF_MEDIAKEY_STATUS>; - -// Based on the MediaKeySession interface in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession -class MediaKeySession -{ -public: - MediaKeySession(IMFContentDecryptionModule* cdm, MF_MEDIAKEYSESSION_TYPE sessionType); - virtual ~MediaKeySession(); - - - // A unique string identifier generated by the CDM that can be used by the application - // to identify session objects. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-sessionid - wil::unique_cotaskmem_string sessionId(); - - // The expiration time for all key(s) in the session, or NaN if no such time exists or - // if the license explicitly never expires, as determined by the CDM. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-expiration - // expiration is based on https://tc39.es/ecma262/#sec-time-values-and-time-range - double expiration(); - - // A reference to an array of key IDs known to the session to the current status - // of the associated key. Each entry MUST have a unique key ID. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-keystatuses - std::vector keyStatuses(); - - // Loads the data stored for the specified session into this object. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-load - bool load(const std::wstring& sessionId); - - // Callback registration methods - using KeyMessageCB = std::function, LPCWSTR)>; - void onmessage(KeyMessageCB&& keyMessageCB); - - using KeyStatusChangedCB = std::function; - void onkeystatuseschange(KeyStatusChangedCB&& keyStatusChangedCB); - - // Generates a license request based on the initData. A message of type "license-request" or - // "individualization-request" will always be queued if the algorithm succeeds - // and the promise is resolved. - // Structure for init data is based on initDataType - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-generaterequest - // https://www.w3.org/TR/eme-initdata-registry/ - void generateRequest(const std::wstring& initDataType, std::vector& initData); - - // Provides messages, including licenses, to the CDM. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-update - void update(gsl::span responseData); - - // Indicates that the application no longer needs the session and the CDM should release any resources - // associated with the session and close it. Persisted data should not be released or cleared. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-close - void close(); - - // Removes all license(s) and key(s) associated with the session. For persistent session types, - // other session data will be cleared as defined for each session type once a - // release message acknowledgment is processed by Update(). - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-remove - void remove(); - -private: - winrt::com_ptr m_cdmSession; - winrt::com_ptr m_sessionCallbacks; - - // Callbacks - KeyMessageCB m_keyMessageCB; - KeyStatusChangedCB m_keyStatusChangedCB; - void OnKeyMessage(MF_MEDIAKEYSESSION_MESSAGETYPE messageType, gsl::span message, LPCWSTR destinationUrl); - void OnKeyStatusChanged(); -}; - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.cpp deleted file mode 100644 index 1f35838..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "MediaKeySystemAccess.h" - -namespace media -{ - -namespace eme -{ - -MediaKeySystemAccess::MediaKeySystemAccess(CdmConfigurationProperties& cdmConfigProperties, IMFContentDecryptionModuleAccess* cdmAccess) -{ - m_cdmProperties = cdmConfigProperties.ConvertToPropertyStore(); - m_cdmAccess.copy_from(cdmAccess); - winrt::com_ptr configuration; - THROW_IF_FAILED(cdmAccess->GetConfiguration(configuration.put())); - m_keySystemConfiguration = MediaKeySystemConfiguration::FromPropertyStore(configuration); -} - -wil::unique_cotaskmem_string MediaKeySystemAccess::keySystem() -{ - wil::unique_cotaskmem_string keySystem; - THROW_IF_FAILED(m_cdmAccess->GetKeySystem(keySystem.put())); - return std::move(keySystem); -} - -const MediaKeySystemConfiguration& MediaKeySystemAccess::getConfiguration() -{ - return m_keySystemConfiguration; -} - -std::shared_ptr MediaKeySystemAccess::createMediaKeys() -{ - winrt::com_ptr cdm; - THROW_IF_FAILED(m_cdmAccess->CreateContentDecryptionModule(m_cdmProperties.get(), cdm.put())); - return std::make_shared(cdm.get()); -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.h b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.h deleted file mode 100644 index c1f67cb..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemAccess.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "media/eme/CdmConfigurationProperties.h" -#include "media/eme/MediaKeySystemConfiguration.h" -#include "media/eme/MediaKeys.h" - -namespace media -{ - namespace eme - { - // Based on the MediaKeySystemAccess interface in the EME specification: - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemaccess-interface - class MediaKeySystemAccess - { - public: - MediaKeySystemAccess(CdmConfigurationProperties& cdmConfigProperties, IMFContentDecryptionModuleAccess* cdmAccess); - - wil::unique_cotaskmem_string keySystem(); - const MediaKeySystemConfiguration& getConfiguration(); - std::shared_ptr createMediaKeys(); - - private: - winrt::com_ptr m_cdmProperties; - winrt::com_ptr m_cdmAccess; - MediaKeySystemConfiguration m_keySystemConfiguration; - }; - } -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.cpp deleted file mode 100644 index b27ef6f..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "MediaKeySystemConfiguration.h" - -namespace media -{ - -namespace eme -{ - -namespace -{ - wil::unique_prop_variant GenerateCapabilityProperty(const std::vector& capabilities) - { - auto capabilitiesArray = wil::make_unique_cotaskmem(capabilities.size()); - for(size_t i = 0; i < capabilities.size(); i++) - { - winrt::com_ptr capabilityProperties; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(capabilityProperties.put()))); - // Populate property store with robustness and content type BSTR prop variants - wil::unique_prop_variant robustness; - robustness.vt = VT_BSTR; - robustness.bstrVal = wil::make_bstr(capabilities[i].robustness.c_str()).release(); - THROW_IF_FAILED(capabilityProperties->SetValue(MF_EME_ROBUSTNESS, robustness)); - wil::unique_prop_variant contentType; - contentType.vt = VT_BSTR; - contentType.bstrVal = wil::make_bstr(capabilities[i].contentType.c_str()).release(); - THROW_IF_FAILED(capabilityProperties->SetValue(MF_EME_CONTENTTYPE, contentType)); - - wil::unique_prop_variant capabilityProperty; - capabilityProperty.vt = VT_UNKNOWN; - winrt::com_ptr capabilityPropertiesUnk = capabilityProperties.as(); - capabilityProperty.punkVal = capabilityPropertiesUnk.detach(); - capabilitiesArray[i] = capabilityProperty.release(); - } - wil::unique_prop_variant capabilitiesProperty; - capabilitiesProperty.vt = VT_VECTOR | VT_VARIANT; - capabilitiesProperty.capropvar.cElems = static_cast(capabilities.size()); - capabilitiesProperty.capropvar.pElems = capabilitiesArray.release(); - return std::move(capabilitiesProperty); - } - - void GetCapabilitiesFromPropVar(const PROPVARIANT& capabilitiesProperty, std::vector& capabilities) - { - capabilities = std::vector(capabilitiesProperty.capropvar.cElems); - for(size_t i = 0; i < capabilities.size(); i++) - { - PROPVARIANT& capabilityProperty = capabilitiesProperty.capropvar.pElems[i]; - winrt::com_ptr capabilityPropertyStore; - if(capabilityProperty.vt != VT_UNKNOWN || FAILED(capabilityProperty.punkVal->QueryInterface(IID_PPV_ARGS(capabilityPropertyStore.put())))) - { - continue; - } - - wil::unique_prop_variant robustness; - if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_ROBUSTNESS, &robustness)) && - robustness.vt == VT_BSTR) - { - capabilities[i].robustness = std::wstring(robustness.bstrVal); - } - - wil::unique_prop_variant contentType; - if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_CONTENTTYPE, &contentType)) && - contentType.vt == VT_BSTR) - { - capabilities[i].contentType = std::wstring(contentType.bstrVal); - } - } - } -} - -winrt::com_ptr MediaKeySystemConfiguration::ConvertToPropertyStore() -{ - winrt::com_ptr propertyStore; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(propertyStore.put()))); - - // Init data types - auto initDataTypeArray = wil::make_unique_cotaskmem(m_initDataTypes.size()); - for(size_t i = 0; i < m_initDataTypes.size(); i++) - { - initDataTypeArray[i] = wil::make_bstr(m_initDataTypes[i].c_str()).release(); - } - wil::unique_prop_variant initDataTypes; - initDataTypes.vt = VT_VECTOR | VT_BSTR; - initDataTypes.cabstr.cElems = static_cast(m_initDataTypes.size()); - initDataTypes.cabstr.pElems = initDataTypeArray.release(); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_INITDATATYPES, initDataTypes)); - - // Audio capabilities - wil::unique_prop_variant audioCapabilities = GenerateCapabilityProperty(m_audioCapabilities); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities)); - - // Video capabilities - wil::unique_prop_variant videoCapabilities = GenerateCapabilityProperty(m_videoCapabilities); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities)); - - // Distinctive ID - wil::unique_prop_variant distinctiveId; - distinctiveId.vt = VT_UI4; - distinctiveId.ulVal = static_cast(m_distinctiveIdRequirement); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_DISTINCTIVEID, distinctiveId)); - - // Persistent state - wil::unique_prop_variant persistentState; - persistentState.vt = VT_UI4; - persistentState.ulVal = static_cast(m_persistentStateRequirement); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_PERSISTEDSTATE, persistentState)); - - // Session types - auto sessionTypeArray = wil::make_unique_cotaskmem(m_sessionTypes.size()); - for(size_t i = 0; i < m_sessionTypes.size(); i++) - { - sessionTypeArray[i] = m_sessionTypes[i]; - } - wil::unique_prop_variant sessionTypes; - sessionTypes.vt = VT_VECTOR | VT_UI4; - sessionTypes.caul.cElems = static_cast(m_sessionTypes.size()); - sessionTypes.caul.pElems = sessionTypeArray.release(); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_SESSIONTYPES, sessionTypes)); - - return propertyStore; -} - -MediaKeySystemConfiguration MediaKeySystemConfiguration::FromPropertyStore(winrt::com_ptr propertyStore) -{ - MediaKeySystemConfiguration configuration; - - // Init data types - wil::unique_prop_variant initDataTypes; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_INITDATATYPES, &initDataTypes)) && - initDataTypes.vt == (VT_VECTOR | VT_BSTR)) - { - configuration.m_initDataTypes.resize(initDataTypes.cabstr.cElems); - for(size_t i = 0; i < initDataTypes.cabstr.cElems; i++) - { - configuration.m_initDataTypes[i] = std::wstring(initDataTypes.cabstr.pElems[i]); - } - } - - // Audio capabilities - wil::unique_prop_variant audioCapabilities; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_AUDIOCAPABILITIES, &audioCapabilities)) && - audioCapabilities.vt == (VT_VECTOR | VT_VARIANT)) - { - GetCapabilitiesFromPropVar(audioCapabilities, configuration.m_audioCapabilities); - } - - // Video capabilities - wil::unique_prop_variant videoCapabilities; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_VIDEOCAPABILITIES, &videoCapabilities)) && - videoCapabilities.vt == (VT_VECTOR | VT_VARIANT)) - { - GetCapabilitiesFromPropVar(videoCapabilities, configuration.m_videoCapabilities); - } - - // Distinctive ID - wil::unique_prop_variant distinctiveId; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_DISTINCTIVEID, &distinctiveId)) && - distinctiveId.vt == VT_UI4) - { - configuration.m_distinctiveIdRequirement = static_cast(distinctiveId.ulVal); - } - - // Persistent state - wil::unique_prop_variant persistentState; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_PERSISTEDSTATE, &persistentState)) && - persistentState.vt == VT_UI4) - { - configuration.m_persistentStateRequirement = static_cast(persistentState.ulVal); - } - - // Session types - wil::unique_prop_variant sessionTypes; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_SESSIONTYPES, &sessionTypes)) && - sessionTypes.vt == (VT_VECTOR | VT_UI4)) - { - configuration.m_sessionTypes = std::vector(sessionTypes.caul.cElems); - for(size_t i = 0; i < sessionTypes.caul.cElems; i++) - { - configuration.m_sessionTypes[i] = static_cast(sessionTypes.caul.pElems[i]); - } - } - - return configuration; -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.h b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.h deleted file mode 100644 index 6162692..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeySystemConfiguration.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "media/MediaEngineProtectedContentInterfaces.h" - -namespace media -{ - -namespace eme -{ - -// This struct represents a MediaKeySystemMediaCapability dictionary, as defined in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysystemmediacapability -struct MediaKeySystemMediaCapability -{ - std::wstring contentType; - std::wstring robustness; -}; - -// This class represents a MediaKeySystemConfiguration dictionary, as defined in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemconfiguration-dictionary -class MediaKeySystemConfiguration -{ -public: - MediaKeySystemConfiguration() = default; - ~MediaKeySystemConfiguration() = default; - - static MediaKeySystemConfiguration FromPropertyStore(winrt::com_ptr propertyStore); - winrt::com_ptr ConvertToPropertyStore(); - - void label(std::wstring&& label) { m_label = std::move(label); } - const std::wstring& label() { return m_label; } - - void initDataTypes(std::vector&& initDataTypes) { m_initDataTypes = std::move(initDataTypes); } - std::vector& initDataTypes() { return m_initDataTypes; } - - void audioCapabilities(std::vector&& audioCapabilities) { m_audioCapabilities = std::move(audioCapabilities); } - std::vector& audioCapabilities() { return m_audioCapabilities; } - - void videoCapabilities(std::vector&& videoCapabilities) { m_videoCapabilities = std::move(videoCapabilities); } - std::vector& videoCapabilities() { return m_videoCapabilities; } - - void distinctiveId(MF_MEDIAKEYS_REQUIREMENT requirement) { m_distinctiveIdRequirement = requirement; } - MF_MEDIAKEYS_REQUIREMENT distinctiveId() { return m_distinctiveIdRequirement; } - - void persistentState(MF_MEDIAKEYS_REQUIREMENT requirement) { m_persistentStateRequirement = requirement; } - MF_MEDIAKEYS_REQUIREMENT persistentState() { return m_persistentStateRequirement; } - - void sessionTypes(std::vector&& sessionTypes) { m_sessionTypes = std::move(sessionTypes); } - const std::vector& sessionTypes() { return m_sessionTypes; } - -private: - std::wstring m_label; - std::vector m_initDataTypes; - std::vector m_audioCapabilities; - std::vector m_videoCapabilities; - MF_MEDIAKEYS_REQUIREMENT m_distinctiveIdRequirement; - MF_MEDIAKEYS_REQUIREMENT m_persistentStateRequirement; - std::vector m_sessionTypes; - -}; - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.cpp b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.cpp deleted file mode 100644 index 3e063bb..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include "MediaKeys.h" - -namespace media -{ - -namespace eme -{ - -MediaKeys::MediaKeys(IMFContentDecryptionModule* cdm) -{ - m_cdm.copy_from(cdm); -} - -std::shared_ptr MediaKeys::createSession(MF_MEDIAKEYSESSION_TYPE sessionType) -{ - return std::make_shared(m_cdm.get(), sessionType); -} - -bool MediaKeys::setServerCertificate(std::vector& certificate) -{ - return SUCCEEDED(m_cdm->SetServerCertificate(&certificate[0], static_cast(certificate.size()))); -} - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.h b/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.h deleted file mode 100644 index 1c3d8ac..0000000 --- a/samples/MediaEngineEMEUWPSample/src/media/eme/MediaKeys.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "media/eme/MediaKeySession.h" - -namespace media -{ - -namespace eme -{ - -// Based on the MediaKeys interface in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeys -class MediaKeys -{ -public: - MediaKeys(IMFContentDecryptionModule* cdm); - std::shared_ptr createSession(MF_MEDIAKEYSESSION_TYPE sessionType = MF_MEDIAKEYSESSION_TYPE_TEMPORARY); - bool setServerCertificate(std::vector& certificate); - - winrt::com_ptr GetCDM() { return m_cdm; } - -private: - winrt::com_ptr m_cdm; -}; - -} - -} \ No newline at end of file diff --git a/samples/MediaEngineEMEUWPSample/src/pch.h b/samples/MediaEngineEMEUWPSample/src/pch.h index 332f814..e9065cc 100644 --- a/samples/MediaEngineEMEUWPSample/src/pch.h +++ b/samples/MediaEngineEMEUWPSample/src/pch.h @@ -2,8 +2,25 @@ // Licensed under the MIT License. #pragma once + +#include +#include +#include +#include +#include +#include + #include +#include +#include + +#include +#include +#include +#include +#include + // Include ABI composition headers for interop with DCOMP surface handle from MediaEngine #include #include @@ -26,28 +43,85 @@ #include #include -// Direct3D -#include - // Windows Implementation Library #include #include #include -// MediaFoundation headers -#include -#include -#include -#include -#include - -// STL headers -#include -#include -#include -#include -#include -#include - // GSL (C++ Guidelines Support Library) #include + +//////////////////////////////////////////////////////////////////////////////// + +struct SourceCodeReference +{ + SourceCodeReference(char const* File, int Line, char const* Function) : + File(File), + Line(Line), + Function(Function) + { + } + char const* FileName() const + { + if(NotShorten) + return File; + char const* Separator = strrchr(File, '\\'); + if(!Separator) + return File; + return Separator + 1; + } + + char const* File; + bool NotShorten = false; + int Line; + char const* Function; +}; + +struct TraceContext +{ + TraceContext(char const* File, int Line, char const* Function) : + m_SourceCodeReference(File, Line, Function) + { + } + explicit TraceContext(SourceCodeReference& SourceCodeReference) : + m_SourceCodeReference(SourceCodeReference) + { + } + + void operator () (wchar_t const* Format, ...) const + { + va_list Arguments; + va_start(Arguments, Format); + wchar_t TextA[8u << 10] { }; + vswprintf_s(TextA, Format, Arguments); + va_end(Arguments); + wchar_t TextB[8u << 10] { }; + swprintf_s(TextB, L"%hs(%d): %hs: ", m_SourceCodeReference.FileName(), m_SourceCodeReference.Line, m_SourceCodeReference.Function); + wcscat_s(TextB, TextA); + OutputDebugStringW(TextB); + } + + SourceCodeReference m_SourceCodeReference; +}; + +#if defined(DEVELOPMENT) || 1 + #define TRACE TraceContext(__FILE__, __LINE__, __FUNCTION__) +#else + #define TRACE 1 ? 0 : +#endif + +inline std::wstring ToString(GUID const& Value) +{ + OLECHAR ValueString[40]; + WI_VERIFY(StringFromGUID2(Value, ValueString, static_cast(std::size(ValueString)))); + return ValueString; +} + +template +inline std::string ToString(std::pair const (&Values)[t_Size], ValueType Value) +{ + for(auto&& ArrayValue: Values) + if(ArrayValue.first == Value) + return ArrayValue.second; + return ""; +} From fb66ab2ae1fca3c0acc7bb305d9e23bcfb3530b6 Mon Sep 17 00:00:00 2001 From: Roman Ryltsov Date: Thu, 2 Nov 2023 08:53:38 +0100 Subject: [PATCH 2/3] Added reproducer app for GenerateRequest AV --- .../ContentDecryption.h | 715 ++++++++++++++++++ .../ContentDecryptionModule01.cpp | 103 +++ .../ContentDecryptionModule01.manifest | 3 + .../ContentDecryptionModule01.rc | Bin 0 -> 3608 bytes .../ContentDecryptionModule01.sln | 31 + .../ContentDecryptionModule01.vcxproj | 96 +++ .../ContentDecryptionModule01.vcxproj.filters | 32 + samples/ContentDecryptionModule01/Helper.h | 78 ++ .../ContentDecryptionModule01/packages.config | 5 + samples/ContentDecryptionModule01/resource.h | 14 + 10 files changed, 1077 insertions(+) create mode 100644 samples/ContentDecryptionModule01/ContentDecryption.h create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.cpp create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.manifest create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.rc create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.sln create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.vcxproj create mode 100644 samples/ContentDecryptionModule01/ContentDecryptionModule01.vcxproj.filters create mode 100644 samples/ContentDecryptionModule01/Helper.h create mode 100644 samples/ContentDecryptionModule01/packages.config create mode 100644 samples/ContentDecryptionModule01/resource.h diff --git a/samples/ContentDecryptionModule01/ContentDecryption.h b/samples/ContentDecryptionModule01/ContentDecryption.h new file mode 100644 index 0000000..4d2ae9e --- /dev/null +++ b/samples/ContentDecryptionModule01/ContentDecryption.h @@ -0,0 +1,715 @@ +#pragma once + +#include +#include +#include + +// NOTE: +// https://github.com/microsoft/media-foundation/issues/37 +// https://github.com/chromium/chromium/commit/43ba4df2434db7cf1eba5698d1dd355061575459#diff-fea3e99ae70746938f3efcf994f1b385fc430770b2adcc15d2c9bfd99b1969f3 + +inline std::string ToString(MF_MEDIAKEYSESSION_TYPE Value) +{ + static std::pair constexpr const g_Items[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_TEMPORARY) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_LICENSE) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_RELEASE_MESSAGE) + IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_USAGE_RECORD) + #undef IDENFITIER + }; + return ToString(g_Items, Value); +} + +inline std::string ToString(MF_MEDIAKEY_STATUS Value) +{ + static std::pair constexpr const g_Items[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEY_STATUS_USABLE) + IDENFITIER(MF_MEDIAKEY_STATUS_EXPIRED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_DOWNSCALED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_NOT_ALLOWED) + IDENFITIER(MF_MEDIAKEY_STATUS_STATUS_PENDING) + IDENFITIER(MF_MEDIAKEY_STATUS_INTERNAL_ERROR) + IDENFITIER(MF_MEDIAKEY_STATUS_RELEASED) + IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_RESTRICTED) + #undef IDENFITIER + }; + return ToString(g_Items, Value); +} + +// MFMediaKeyStatus + +inline std::string ToString(MF_MEDIAKEYSESSION_MESSAGETYPE Value) +{ + static std::pair constexpr const g_Items[] + { + #define IDENFITIER(Name) std::make_pair(Name, #Name), + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RENEWAL) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RELEASE) + IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_INDIVIDUALIZATION_REQUEST) + #undef IDENFITIER + }; + return ToString(g_Items, Value); +} + +// MF_CROSS_ORIGIN_POLICY + +namespace Eme { + +inline void SetPropertyValue(wil::com_ptr const& PropertyStore, PROPERTYKEY const& Key, std::wstring const& Value) +{ + WI_ASSERT(PropertyStore); + wil::unique_prop_variant VariantValue; + VariantValue.vt = VT_BSTR; + VariantValue.bstrVal = wil::make_bstr(Value.c_str()).release(); + THROW_IF_FAILED(PropertyStore->SetValue(Key, VariantValue)); +} + +// NOTE: EME MediaKeySession interface +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession + +struct MediaKeySession : std::enable_shared_from_this +{ + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeystatusmap + using MediaKeyStatus = std::pair, MF_MEDIAKEY_STATUS>; + using MediaKeyStatusMap = std::vector; + + MediaKeySession() + { + TRACE(L"this %p\n", this); + } + ~MediaKeySession() + { + TRACE(L"this %p\n", this); + if(Callbacks) + Callbacks->Owner.reset(); + } + + void Initialize(wil::com_ptr const& ContentDecryptionModule, MF_MEDIAKEYSESSION_TYPE Type = MF_MEDIAKEYSESSION_TYPE_TEMPORARY) + { + TRACE(L"this %p, Type %hs\n", this, ToString(Type).c_str()); + WI_ASSERT(ContentDecryptionModule && !Callbacks); + Callbacks = winrt::make_self(shared_from_this()); + THROW_IF_FAILED(ContentDecryptionModule->CreateSession(Type, Callbacks.get(), ContentDecryptionModuleSession.put())); + TRACE(L"this %p, ContentDecryptionModuleSession %p\n", this, ContentDecryptionModuleSession.get()); + } + + std::wstring SessionId() const // sessionId + { + TRACE(L"this %p\n", this); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-sessionid + WI_ASSERT(ContentDecryptionModuleSession); + wil::unique_cotaskmem_string Result; + THROW_IF_FAILED(ContentDecryptionModuleSession->GetSessionId(Result.put())); + return Result.get(); + } + double Expiration() const // expiration + { + TRACE(L"this %p\n", this); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-expiration + // https://tc39.es/ecma262/#sec-time-values-and-time-range + WI_ASSERT(ContentDecryptionModuleSession); + double Result = 0.0; + THROW_IF_FAILED(ContentDecryptionModuleSession->GetExpiration(&Result)); + return Result; + } + // closed + + MediaKeyStatusMap KeyStatuses() const // keyStatuses + { + TRACE(L"this %p\n", this); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-keystatuses + WI_ASSERT(ContentDecryptionModuleSession); + wil::unique_cotaskmem_array_ptr ArrayResult; + THROW_IF_FAILED(ContentDecryptionModuleSession->GetKeyStatuses(ArrayResult.put(), ArrayResult.size_address())); + MediaKeyStatusMap Result; + Result.reserve(ArrayResult.size()); + for(size_t Index = 0; Index < ArrayResult.size(); Index++) + { + auto const& Element = ArrayResult[Index]; + std::vector KeyId(Element.pbKeyId, Element.pbKeyId + Element.cbKeyId); + Result.emplace_back(std::make_pair(std::move(KeyId), Element.eMediaKeyStatus)); + } + return Result; + } + + std::function const&, std::wstring)> OnMessage; // onmessage + std::function OnKeyStatusChange; // onkeystatuseschange + + void GenerateRequest(std::wstring const& InitializationDataType, std::vector const& InitializationData) // generateRequest + { + TRACE(L"this %p, InitializationDataType %ls, InitializationData %zu\n", this, InitializationDataType.c_str(), InitializationData.size()); + TRACE(L"SessionId %ls\n", SessionId().c_str()); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-generaterequest + // https://www.w3.org/TR/eme-initdata-registry/ + THROW_IF_FAILED(ContentDecryptionModuleSession->GenerateRequest(InitializationDataType.c_str(), InitializationData.data(), static_cast(InitializationData.size()))); + } + bool Load(std::wstring const& SessionIdentifier) // load + { + TRACE(L"this %p, SessionIdentifier %ls\n", this, SessionIdentifier.c_str()); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-load + WI_ASSERT(ContentDecryptionModuleSession); + BOOL Result = FALSE; + THROW_IF_FAILED(ContentDecryptionModuleSession->Load(SessionIdentifier.c_str(), &Result)); + return Result != FALSE; + } + void Update(std::vector const& ResponseData) // update + { + TRACE(L"this %p, ResponseData %zu\n", this, ResponseData.size()); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-update + WI_ASSERT(ContentDecryptionModuleSession); + THROW_IF_FAILED(ContentDecryptionModuleSession->Update(ResponseData.data(), static_cast(ResponseData.size()))); + } + void Close() // close + { + TRACE(L"this %p\n", this); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-close + WI_ASSERT(ContentDecryptionModuleSession); + THROW_IF_FAILED(ContentDecryptionModuleSession->Close()); + } + void Remove() // remove + { + TRACE(L"this %p\n", this); + // NOTE: https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-remove + WI_ASSERT(ContentDecryptionModuleSession); + THROW_IF_FAILED(ContentDecryptionModuleSession->Remove()); + } + + class SessionCallbacks : public winrt::implements + { + public: + SessionCallbacks(std::shared_ptr const& Owner) : + Owner(Owner) + { + TRACE(L"this %p\n", this); + } + ~SessionCallbacks() + { + TRACE(L"this %p\n", this); + } + + // IMFContentDecryptionModuleSessionCallbacks + IFACEMETHOD(KeyMessage)(MF_MEDIAKEYSESSION_MESSAGETYPE Type, BYTE const* Data, DWORD DataSize, LPCWSTR Location) override + { + TRACE(L"this %p, Type %hs, DataSize %u, Location \"%ls\"\n", this, ToString(Type).c_str(), DataSize, Location); + try + { + auto const Owner = this->Owner.lock(); + if(!Owner) + return S_FALSE; + if(Owner->OnMessage) + Owner->OnMessage(Type, std::vector(Data, Data + DataSize), Location); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(KeyStatusChanged)() override + { + TRACE(L"this %p\n", this); + try + { + auto const Owner = this->Owner.lock(); + if(!Owner) + return S_FALSE; + if(Owner->OnKeyStatusChange) + Owner->OnKeyStatusChange(); + } + CATCH_RETURN(); + return S_OK; + } + + std::weak_ptr Owner; + }; + + wil::com_ptr ContentDecryptionModuleSession; + winrt::com_ptr Callbacks; +}; + +// NOTE: EME MediaKeys interface +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeys + +struct MediaKeys +{ +public: + MediaKeys(wil::com_ptr const& ContentDecryptionModule) : + ContentDecryptionModule(ContentDecryptionModule) + { + TRACE(L"this %p\n", this); + } + ~MediaKeys() + { + TRACE(L"this %p\n", this); + } + + std::shared_ptr CreateSession(MF_MEDIAKEYSESSION_TYPE Type = MF_MEDIAKEYSESSION_TYPE_TEMPORARY) // createSession + { + auto const Result = std::make_shared(); + Result->Initialize(ContentDecryptionModule, Type); + return Result; + } + bool SetServerCertificate(std::vector& ServerCertificate) // setServerCertificate + { + return SUCCEEDED(ContentDecryptionModule->SetServerCertificate(ServerCertificate.data(), static_cast(ServerCertificate.size()))); + } + + wil::com_ptr const ContentDecryptionModule; +}; + +struct FactoryConfiguration +{ + wil::com_ptr CreatePropertyStore() const + { + wil::com_ptr PropertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); + SetPropertyValue(PropertyStore, MF_EME_CDM_STOREPATH, StoragePath); + if(!InPrivateStoragePath.empty()) + SetPropertyValue(PropertyStore, MF_EME_CDM_INPRIVATESTOREPATH, InPrivateStoragePath); + return PropertyStore; + } + + std::wstring StoragePath; + std::wstring InPrivateStoragePath; +}; + +// NOTE: EME MediaKeySystemMediaCapability dictionary +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysystemmediacapability + +struct MediaKeySystemMediaCapability +{ + MediaKeySystemMediaCapability() = default; + MediaKeySystemMediaCapability(wil::com_ptr const& PropertyStore) + { + WI_ASSERT(PropertyStore); + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_ROBUSTNESS, &Value)) && Value.vt == VT_BSTR) + Robustness = Value.bstrVal; + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_CONTENTTYPE, &Value)) && Value.vt == VT_BSTR) + ContentType = Value.bstrVal; + } + } + + wil::com_ptr CreatePropertyStore() const + { + wil::com_ptr PropertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); + SetPropertyValue(PropertyStore, MF_EME_ROBUSTNESS, Robustness); + SetPropertyValue(PropertyStore, MF_EME_CONTENTTYPE, ContentType); + return PropertyStore; + } + + std::wstring ContentType; // contentType + std::wstring Robustness; // robustness +}; + +// NOTE: EME MediaKeySystemConfiguration dictionary +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemconfiguration-dictionary + +struct MediaKeySystemConfiguration +{ + static std::vector CapabilityVectorFromVariant(PROPVARIANT const& Value) + { + std::vector Result; + Result.reserve(Value.capropvar.cElems); + for(ULONG Index = 0; Index < Value.capropvar.cElems; Index++) + { + PROPVARIANT& Element = Value.capropvar.pElems[Index]; + wil::com_ptr ElementPropertyStore; + if(Element.vt != VT_UNKNOWN || FAILED(Element.punkVal->QueryInterface(IID_PPV_ARGS(ElementPropertyStore.put())))) + continue; + Result.emplace_back(MediaKeySystemMediaCapability(ElementPropertyStore)); + } + return Result; + } + + MediaKeySystemConfiguration() = default; + MediaKeySystemConfiguration(wil::com_ptr const& PropertyStore) + { + WI_ASSERT(PropertyStore); + { + wil::unique_prop_variant Value; + if(SUCCEEDED(PropertyStore->GetValue(MF_EME_INITDATATYPES, &Value)) && Value.vt == (VT_VECTOR | VT_BSTR)) + { + InitDataTypeVector.reserve(Value.cabstr.cElems); + for(ULONG Index = 0; Index < Value.cabstr.cElems; Index++) + InitDataTypeVector.emplace_back(Value.cabstr.pElems[Index]); + } + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_AUDIOCAPABILITIES, &Value)) && Value.vt == (VT_VECTOR | VT_VARIANT)) + AudioCapabilityVector = CapabilityVectorFromVariant(Value); + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_VIDEOCAPABILITIES, &Value)) && Value.vt == (VT_VECTOR | VT_VARIANT)) + VideoCapabilityVector = CapabilityVectorFromVariant(Value); + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_DISTINCTIVEID, &Value)) && Value.vt == VT_UI4) + DistinctiveIdentifier = static_cast(Value.ulVal); + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_PERSISTEDSTATE, &Value)) && Value.vt == VT_UI4) + PersistentState = static_cast(Value.ulVal); + } + { + wil::unique_prop_variant Value; + if (SUCCEEDED(PropertyStore->GetValue(MF_EME_SESSIONTYPES, &Value)) && Value.vt == (VT_VECTOR | VT_UI4)) + { + SessionTypeVector.reserve(Value.caul.cElems); + for(ULONG Index = 0; Index < Value.caul.cElems; Index++) + SessionTypeVector.emplace_back(static_cast(Value.caul.pElems[Index])); + } + } + } + + template + static wil::unique_prop_variant VariantFromInteger(ValueType Value) + { + wil::unique_prop_variant Result; + Result.vt = VT_UI4; + Result.ulVal = static_cast(Value); + return Result; + } + static wil::unique_prop_variant VariantFromCapabilityVector(std::vector const& CapabilityVector) + { + auto Array = wil::make_unique_cotaskmem(CapabilityVector.size()); + for(size_t Index = 0; Index < CapabilityVector.size(); Index++) + { + auto& Capability = Array[Index]; + Capability.vt = VT_UNKNOWN; + Capability.punkVal = CapabilityVector[Index].CreatePropertyStore().detach(); + } + wil::unique_prop_variant Result; + Result.vt = VT_VECTOR | VT_VARIANT; + Result.capropvar.cElems = static_cast(CapabilityVector.size()); + Result.capropvar.pElems = Array.release(); + return Result; + } + wil::com_ptr CreatePropertyStore() const + { + wil::com_ptr PropertyStore; + THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); + { + auto Vector = wil::make_unique_cotaskmem(InitDataTypeVector.size()); + for(size_t Index = 0; Index < InitDataTypeVector.size(); Index++) + Vector[Index] = wil::make_bstr(InitDataTypeVector[Index].c_str()).release(); + wil::unique_prop_variant Value; + Value.vt = VT_VECTOR | VT_BSTR; + Value.cabstr.cElems = static_cast(InitDataTypeVector.size()); + Value.cabstr.pElems = Vector.release(); + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_INITDATATYPES, Value)); + } + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_AUDIOCAPABILITIES, VariantFromCapabilityVector(AudioCapabilityVector))); + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_VIDEOCAPABILITIES, VariantFromCapabilityVector(VideoCapabilityVector))); + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_DISTINCTIVEID, VariantFromInteger(DistinctiveIdentifier))); + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_PERSISTEDSTATE, VariantFromInteger(PersistentState))); + { + auto Vector = wil::make_unique_cotaskmem(SessionTypeVector.size()); + for(size_t Index = 0; Index < SessionTypeVector.size(); Index++) + Vector[Index] = static_cast(SessionTypeVector[Index]); + wil::unique_prop_variant Value; + Value.vt = VT_VECTOR | VT_UI4; + Value.caul.cElems = static_cast(SessionTypeVector.size()); + Value.caul.pElems = Vector.release(); + THROW_IF_FAILED(PropertyStore->SetValue(MF_EME_SESSIONTYPES, Value)); + } + return PropertyStore; + } + + std::wstring Label; // label + std::vector InitDataTypeVector; // Value + std::vector AudioCapabilityVector; // audioCapabilities + std::vector VideoCapabilityVector; // videoCapabilities + MF_MEDIAKEYS_REQUIREMENT DistinctiveIdentifier; // distinctiveIdentifier + MF_MEDIAKEYS_REQUIREMENT PersistentState; // persistentState + std::vector SessionTypeVector; // Value +}; + +// NOTE: EME MediaKeySystemAccess interface +// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemaccess-interface + +class MediaKeySystemAccess +{ +public: + MediaKeySystemAccess(FactoryConfiguration& Configuration, wil::com_ptr const& ContentDecryptionModuleAccess) : + m_ContentDecryptionModulePropertyStore(Configuration.CreatePropertyStore()), + m_ContentDecryptionModuleAccess(ContentDecryptionModuleAccess) + { + TRACE(L"this %p\n", this); + wil::com_ptr PropertyStore; + THROW_IF_FAILED(ContentDecryptionModuleAccess->GetConfiguration(PropertyStore.put())); + m_Configuration = MediaKeySystemConfiguration(PropertyStore); + } + ~MediaKeySystemAccess() + { + TRACE(L"this %p\n", this); + } + + std::wstring KeySystem() const // keySystem + { + wil::unique_cotaskmem_string Result; + THROW_IF_FAILED(m_ContentDecryptionModuleAccess->GetKeySystem(Result.put())); + return Result.get(); + } + MediaKeySystemConfiguration const& Configuration() const // getConfiguration + { + return m_Configuration; + } + std::shared_ptr CreateMediaKeys() const // createMediaKeys + { + wil::com_ptr ContentDecryptionModule; + THROW_IF_FAILED(m_ContentDecryptionModuleAccess->CreateContentDecryptionModule(m_ContentDecryptionModulePropertyStore.get(), ContentDecryptionModule.put())); + TRACE(L"this %p, ContentDecryptionModule %p\n", this, ContentDecryptionModule.get()); + return std::make_shared(ContentDecryptionModule); + } + +private: + wil::com_ptr const m_ContentDecryptionModulePropertyStore; + wil::com_ptr const m_ContentDecryptionModuleAccess; + MediaKeySystemConfiguration m_Configuration; +}; + +struct Factory +{ + Factory(FactoryConfiguration const& Configuration) : + Configuration(Configuration) + { + TRACE(L"this %p\n", this); + THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(ClassFactory4.put()))); + } + ~Factory() + { + TRACE(L"this %p\n", this); + } + + std::shared_ptr RequestMediaKeySystemAccess(std::wstring const& KeySystem, std::vector& ConfigurationVector) // requestMediaKeySystemAccess + { + // NOTE: EME Navigator Extension: requestMediaKeySystemAccess() + // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-navigator-requestmediakeysystemaccess + TRACE(L"this %p, KeySystem %ls, ConfigurationVector.size() %zu\n", this, KeySystem.c_str(), ConfigurationVector.size()); + LOG_HR_IF(E_NOTIMPL, KeySystem != L"com.microsoft.playready.recommendation"); + if(!ContentDecryptionModuleFactory || this->KeySystem != KeySystem) + { + auto const CreateContentDecryptionModuleFactoryResult = ClassFactory4->CreateContentDecryptionModuleFactory(KeySystem.c_str(), IID_PPV_ARGS(ContentDecryptionModuleFactory.put())); + LOG_IF_FAILED(CreateContentDecryptionModuleFactoryResult); + if(FAILED(CreateContentDecryptionModuleFactoryResult)) + return nullptr; + TRACE(L"this %p, ContentDecryptionModuleFactory %p\n", this, ContentDecryptionModuleFactory.get()); + this->KeySystem = KeySystem; + } + auto const IsTypeSupported = ContentDecryptionModuleFactory->IsTypeSupported(KeySystem.c_str(), nullptr); + LOG_HR_IF(E_FAIL, !IsTypeSupported); + if(!IsTypeSupported) + return nullptr; + wil::com_ptr ContentDecryptionModuleAccess; + std::vector> ConfigurationPropertyStoreVector; + ConfigurationPropertyStoreVector.reserve(ConfigurationVector.size()); + std::vector RawConfigurationPropertyStoreVector; + RawConfigurationPropertyStoreVector.reserve(ConfigurationVector.size()); + for(auto&& Configuration: ConfigurationVector) + { + ConfigurationPropertyStoreVector.emplace_back(Configuration.CreatePropertyStore()); + RawConfigurationPropertyStoreVector.emplace_back(ConfigurationPropertyStoreVector.back().get()); + } + auto const CreateContentDecryptionModuleAccessResult = ContentDecryptionModuleFactory->CreateContentDecryptionModuleAccess(KeySystem.c_str(), RawConfigurationPropertyStoreVector.data(), static_cast(RawConfigurationPropertyStoreVector.size()), ContentDecryptionModuleAccess.put()); + LOG_IF_FAILED(CreateContentDecryptionModuleAccessResult); + if(FAILED(CreateContentDecryptionModuleAccessResult)) + return nullptr; + TRACE(L"this %p, ContentDecryptionModuleAccess %p\n", this, ContentDecryptionModuleAccess.get()); + return std::make_shared(Configuration, ContentDecryptionModuleAccess); + } + + FactoryConfiguration Configuration; + wil::com_ptr ClassFactory4; + wil::com_ptr ContentDecryptionModuleFactory; + std::wstring KeySystem; +}; + +} // namespace Eme + +#include + +class InputTrustAuthorityMediaSource : public winrt::implements +{ +public: + InputTrustAuthorityMediaSource(wil::com_ptr const& MediaSource, wil::com_ptr const& ContentDecryptionModule) : + m_Inner(MediaSource), + m_ContentDecryptionModule(ContentDecryptionModule) + { + WI_ASSERT(m_Inner && m_ContentDecryptionModule); + } + +// IMFMediaSource + IFACEMETHOD(GetCharacteristics)(DWORD* Characteristics) override + { + //TRACE(L"this %p\n", this); + return m_Inner->GetCharacteristics(Characteristics); + } + IFACEMETHOD(CreatePresentationDescriptor)(IMFPresentationDescriptor** PresentationDescriptor) override + { + //TRACE(L"this %p\n", this); + return m_Inner->CreatePresentationDescriptor(PresentationDescriptor); + } + IFACEMETHOD(Start)(IMFPresentationDescriptor* PresentationDescriptor, GUID const* TimeFormat, PROPVARIANT const* StartPosition) override + { + TRACE(L"this %p\n", this); + return m_Inner->Start(PresentationDescriptor, TimeFormat, StartPosition); + } + IFACEMETHOD(Stop)() override + { + TRACE(L"this %p\n", this); + return m_Inner->Stop(); + } + IFACEMETHOD(Pause)() override + { + TRACE(L"this %p\n", this); + return m_Inner->Pause(); + } + IFACEMETHOD(Shutdown)() override + { + TRACE(L"this %p\n", this); + try + { + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + m_TrustedInput.reset(); + m_StreamTrustedInputMap.clear(); + return m_Inner->Shutdown(); + } + CATCH_RETURN(); + } + +// IMFMediaEventGenerator + IFACEMETHOD(GetEvent)(DWORD Flags, IMFMediaEvent** MediaEvent) override + { + //TRACE(L"this %p\n", this); + return m_Inner->GetEvent(Flags, MediaEvent); + } + IFACEMETHOD(BeginGetEvent)(IMFAsyncCallback* AsyncCallback, IUnknown* StateUnknown) override + { + //TRACE(L"this %p\n", this); + return m_Inner->BeginGetEvent(AsyncCallback, StateUnknown); + } + IFACEMETHOD(EndGetEvent)(IMFAsyncResult* AsyncResult, IMFMediaEvent** MediaEvent) override + { + //TRACE(L"this %p\n", this); + return m_Inner->EndGetEvent(AsyncResult, MediaEvent); + } + IFACEMETHOD(QueueEvent)(MediaEventType Type, REFGUID ExtendedType, HRESULT Status, PROPVARIANT const* Value) override + { + //TRACE(L"this %p\n", this); + return m_Inner->QueueEvent(Type, ExtendedType, Status, Value); + } + +// IMFTrustedInput + IFACEMETHOD(GetInputTrustAuthority)(DWORD StreamIdentifier, REFIID InterfaceIdentifier, IUnknown** Object) override + { + TRACE(L"this %p, StreamIdentifier %u, InterfaceIdentifier %ls\n", this, StreamIdentifier, ToString(InterfaceIdentifier).c_str()); + try + { + LOG_HR_IF(S_FALSE, InterfaceIdentifier != __uuidof(IMFInputTrustAuthority)); + WI_ASSERT(Object); + wil::com_ptr Unknown; + [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); + auto const Iterator = m_StreamTrustedInputMap.find(StreamIdentifier); + if(Iterator == m_StreamTrustedInputMap.end()) + { + if(!m_TrustedInput) + THROW_IF_FAILED(m_ContentDecryptionModule->CreateTrustedInput(nullptr, 0, m_TrustedInput.put())); + THROW_IF_FAILED(m_TrustedInput->GetInputTrustAuthority(StreamIdentifier, InterfaceIdentifier, Unknown.put())); + m_StreamTrustedInputMap.emplace(StreamIdentifier, Unknown); + } else + Unknown = Iterator->second; + *Object = Unknown.detach(); + } + CATCH_RETURN(); + return S_OK; + } + +private: + wil::com_ptr const m_ContentDecryptionModule; + wil::com_ptr const m_Inner; + mutable wil::srwlock m_DataMutex; + wil::com_ptr m_TrustedInput; + std::map> m_StreamTrustedInputMap; +}; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +class PmpHostApp : public winrt::implements +{ +public: + PmpHostApp(wil::com_ptr const& PmpHost) : + m_PmpHost(PmpHost) + { + } + +// IMFPMPHostApp + IFACEMETHOD(LockProcess)() override + { + TRACE(L"this %p\n", this); + try + { + THROW_IF_FAILED(m_PmpHost->LockProcess()); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(UnlockProcess)() override + { + TRACE(L"this %p\n", this); + try + { + THROW_IF_FAILED(m_PmpHost->UnlockProcess()); + } + CATCH_RETURN(); + return S_OK; + } + IFACEMETHOD(ActivateClassById)(LPCWSTR Identifier, IStream* Stream, REFIID InterfaceIdentifier, void** Object) override + { + TRACE(L"this %p, Identifier %ls\n", this, Identifier); + try + { + struct __declspec(uuid("{77631A31-E5E7-4785-BF17-20F57B224802}")) ClassName; + struct __declspec(uuid("{3E73735C-E6C0-481D-8260-EE5DB1343B5F}")) ObjectStream; + struct __declspec(uuid("{2DF7B51E-797B-4D06-BE71-D14A52CF8421}")) StoreActivate; + wil::com_ptr Attributes; + THROW_IF_FAILED(MFCreateAttributes(Attributes.put(), 3)); + THROW_IF_FAILED(Attributes->SetString(__uuidof(ClassName), Identifier)); + if(Stream) + { + STATSTG StatStg; + THROW_IF_FAILED(Stream->Stat(&StatStg, STATFLAG_NOOPEN | STATFLAG_NONAME)); + WI_ASSERT(!StatStg.cbSize.QuadPart); + std::vector Data(StatStg.cbSize.LowPart); + ULONG DataSize = 0; + THROW_IF_FAILED(Stream->Read(Data.data(), static_cast(Data.size()), &DataSize)); + THROW_IF_FAILED(Attributes->SetBlob(__uuidof(ObjectStream), Data.data(), DataSize)); + } + wil::com_ptr AttributeStream; + THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, AttributeStream.put())); + THROW_IF_FAILED(MFSerializeAttributesToStream(Attributes.get(), 0, AttributeStream.get())); + THROW_IF_FAILED(AttributeStream->Seek({ }, STREAM_SEEK_SET, nullptr)); + wil::com_ptr Activate; + THROW_IF_FAILED(m_PmpHost->CreateObjectByCLSID(__uuidof(StoreActivate), AttributeStream.get(), IID_PPV_ARGS(Activate.put()))); + THROW_IF_FAILED(Activate->ActivateObject(InterfaceIdentifier, Object)); + } + CATCH_RETURN(); + return S_OK; + } + +private: + wil::com_ptr m_PmpHost; +}; + +#endif \ No newline at end of file diff --git a/samples/ContentDecryptionModule01/ContentDecryptionModule01.cpp b/samples/ContentDecryptionModule01/ContentDecryptionModule01.cpp new file mode 100644 index 0000000..d597b9f --- /dev/null +++ b/samples/ContentDecryptionModule01/ContentDecryptionModule01.cpp @@ -0,0 +1,103 @@ +#include + +#include +#include + +#pragma comment(lib, "shlwapi.lib") + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "mfplat.lib") +#pragma comment(lib, "mfuuid.lib") + +#include +#include + +#include +#include +#include + +#include "Helper.h" +#include "ContentDecryption.h" + +wchar_t constexpr const g_Location[] = L"https://test.playready.microsoft.com/media/test/cbcs/bbb.main.30fps.1920x1080.h264.aac.cbcs.sameKeys.mp4"; + +// +// https://test.playready.microsoft.com/core/rightsmanager.asmx?cfg=(playenablers:(786627d8-c2a6-44be-8f88-08ae255b01a7),sl:150,ck:W31bfVt9W31bfVt9W31bfQ==,ckt:AES128BitCBC) +// +// +// +uint8_t constexpr const g_InitializationData[] +{ + 0x00, 0x00, 0x03, 0x54, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95, 0x00, 0x00, 0x03, 0x34, 0x34, 0x03, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x2a, 0x03, 0x3c, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x20, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x52, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x22, 0x00, 0x20, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x79, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x2f, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x3f, 0x00, 0x63, 0x00, 0x66, 0x00, 0x67, 0x00, 0x3d, 0x00, 0x28, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x28, 0x00, 0x37, 0x00, 0x38, 0x00, 0x36, 0x00, 0x36, 0x00, 0x32, 0x00, 0x37, 0x00, 0x64, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x63, 0x00, 0x32, 0x00, 0x61, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x34, 0x00, 0x62, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x66, 0x00, 0x38, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x38, 0x00, 0x61, 0x00, 0x65, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x62, 0x00, 0x30, 0x00, 0x31, 0x00, 0x61, 0x00, 0x37, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x73, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x3a, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x56, 0x00, 0x74, 0x00, 0x39, 0x00, 0x57, 0x00, 0x33, 0x00, 0x31, 0x00, 0x62, 0x00, 0x66, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x31, 0x00, 0x32, 0x00, 0x38, 0x00, 0x42, 0x00, 0x69, 0x00, 0x74, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x29, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x20, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x42, 0x00, 0x43, 0x00, 0x22, 0x00, 0x20, 0x00, 0x56, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x55, 0x00, 0x45, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x45, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x51, 0x00, 0x41, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x49, 0x00, 0x44, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x3e, 0x00, +}; + +struct Application : winrt::implements +{ + static std::wstring StoragePath() + { + wchar_t Path[MAX_PATH]; + WI_VERIFY(ExpandEnvironmentStringsW(L"%TEMP%", Path, static_cast(std::size(Path)))); // %LOCALAPPDATA%, %USERPROFILE% + WI_VERIFY(PathCombineW(Path, Path, L"ContentDecryptionModule01")); + return Path; + } + void HandleKeySessionMessage(MF_MEDIAKEYSESSION_MESSAGETYPE Type, std::vector const& Data, std::wstring const& Location) + { + std::wcout << L"HandleKeySessionMessage" << std::endl; + } + void Main() + { + Eme::FactoryConfiguration Configuration { }; + Configuration.StoragePath = StoragePath(); + std::wcout << L"Configuration.StoragePath: " << Configuration.StoragePath << std::endl; + CreateDirectoryW(Configuration.StoragePath.c_str(), nullptr); + Eme::Factory Factory(Configuration); + + // NOTE: This is equivalent to JS + // Navigator.requestMediaKeySystemAccess("com.microsoft.playready.recommendation", [{ initDataTypes: ['cenc'], persistentState: 'optional', distinctiveIdentifier: 'required']) + std::vector MediaKeySystemConfigurationVector(1); + auto& MediaKeySystemConfiguration = MediaKeySystemConfigurationVector[0]; + MediaKeySystemConfiguration.InitDataTypeVector.emplace_back(L"cenc"); + MediaKeySystemConfiguration.PersistentState = MF_MEDIAKEYS_REQUIREMENT_OPTIONAL; + MediaKeySystemConfiguration.DistinctiveIdentifier = MF_MEDIAKEYS_REQUIREMENT_REQUIRED; + auto const KeySystemAccess = Factory.RequestMediaKeySystemAccess(L"com.microsoft.playready.recommendation", MediaKeySystemConfigurationVector); + THROW_HR_IF_MSG(MF_E_UNSUPPORTED_SERVICE, !KeySystemAccess, "PlayReady key system configuration is not supported"); + + auto const MediaKeys = KeySystemAccess->CreateMediaKeys(); + MediaKeySession = MediaKeys->CreateSession(); + MediaKeySession->OnMessage = [&, StrongThis = get_strong()] (MF_MEDIAKEYSESSION_MESSAGETYPE Type, std::vector const& Data, std::wstring const& Location) { HandleKeySessionMessage(Type, Data, Location); }; + try + { + std::wcout << L"Before GenerateRequest" << std::endl; + MediaKeySession->GenerateRequest(L"cenc", std::vector(g_InitializationData, g_InitializationData + std::size(g_InitializationData))); + std::wcout << L"After GenerateRequest" << std::endl; + } + catch(...) + { + wchar_t Text[128]; + swprintf_s(Text, L"0x%08X", wil::ResultFromCaughtException()); + std::wcout << L"After GenerateRequest: " << Text << std::endl; + } + } + + std::shared_ptr MediaKeySession; +}; + +int wmain() +{ + try + { + winrt::init_apartment(); + THROW_IF_FAILED(MFStartup(MF_VERSION)); + auto&& Scope = wil::scope_exit([] { THROW_IF_FAILED(MFShutdown()); }); + auto const Application = winrt::make_self<::Application>(); + Application->Main(); + } + CATCH_RETURN_MSG("Fatal"); + return 0; +} diff --git a/samples/ContentDecryptionModule01/ContentDecryptionModule01.manifest b/samples/ContentDecryptionModule01/ContentDecryptionModule01.manifest new file mode 100644 index 0000000..d4a2710 --- /dev/null +++ b/samples/ContentDecryptionModule01/ContentDecryptionModule01.manifest @@ -0,0 +1,3 @@ + + + diff --git a/samples/ContentDecryptionModule01/ContentDecryptionModule01.rc b/samples/ContentDecryptionModule01/ContentDecryptionModule01.rc new file mode 100644 index 0000000000000000000000000000000000000000..4f7af4ba5507b07366c1c814c6a183c970fcd05a GIT binary patch literal 3608 zcmds)+invv5QhJ2B;H|3Zm2*FhbPF15V0+&o2@{K6qTl1B83!X6LG_{1K-$n$!>N9 z1cX+|a>kw=kH`NU?0oy$QAd$Zw9uv2x=~Kgw1IaHFV|KVS~BbF5iQcSuC$`B;cuXG zcx$Mw+cLUj95FL(3w=W7Sod|0T{@1o;jZ|)M0)OITYKw|^+7EsZ7bVOE_3TlcUNze zXrfdDjrCeHooNVn!1z?L-oZ)uJy8!%%sA|CHZ-pV>jI550+%wz87ST8Bj?|Mj^Qxj z)NlD6bLu|lG(3jzpV7-6`MD21JAD1RBL2-oELh~DV+&RmNg3xpEH(o2L2t8Yur4Cb zw`5k1r!F{iq=yC0QH9_0oh({7gdP;KX^xJrUJyqMQfi(nh{Si&GdeuMYSs@ z=r|*4O-D+;9l2d&xTkPa=*UH8-~_pZ*fp8Q=uVL9v729Gdk?Xz3pXal4Fm6OJ4L;> zEf>x?!z)$~+=^!Ta@U&R*FftsI(#>3{V8bG<-UFIarTj`3O0*wTXmJ4>d9@T9fF)k z%vMfX|RZE0yrjhYh0zRv%!vd*IC2U_&I`?Ou{os(l$Pkb}4Rn?aLXfy2U zH5{71yHV4uv7I;cW?8mtjag{(h|E~=f?49-oLkf?XbNX>_mW8%`FBn3>R$OC{nz!> zvNHXJdTMebD&?3u`m(Z~7A0nA_3xs9%`RTe83*eEwR>Vi<*+y8HU696t@rrz>aNzC zWa#yo_;=! + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {d08cb923-7f94-49c5-b177-3491b367a4e6} + ContentDecryptionModule01 + 10.0 + + + + Application + v143 + Unicode + true + true + + + + + + + + + + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + Level3 + true + %(PreprocessorDefinitions);_CONSOLE + %(PreprocessorDefinitions);WIN32 + %(PreprocessorDefinitions);_DEBUG + %(PreprocessorDefinitions);NDEBUG + true + stdcpp17 + $(SolutionDir)submodule\wil\include;$(SolutionDir)bin\$(Platform) + + + Console + true + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/samples/ContentDecryptionModule01/ContentDecryptionModule01.vcxproj.filters b/samples/ContentDecryptionModule01/ContentDecryptionModule01.vcxproj.filters new file mode 100644 index 0000000..621c0f1 --- /dev/null +++ b/samples/ContentDecryptionModule01/ContentDecryptionModule01.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/samples/ContentDecryptionModule01/Helper.h b/samples/ContentDecryptionModule01/Helper.h new file mode 100644 index 0000000..e90a44d --- /dev/null +++ b/samples/ContentDecryptionModule01/Helper.h @@ -0,0 +1,78 @@ +#pragma once + +#include + +struct SourceCodeReference +{ + SourceCodeReference(char const* File, int Line, char const* Function) : + File(File), + Line(Line), + Function(Function) + { + } + char const* FileName() const + { + char const* Separator = strrchr(File, '\\'); + if(!Separator) + return File; + return Separator + 1; + } + + char const* File; + int Line; + char const* Function; +}; + +struct TraceContext +{ + TraceContext(char const* File, int Line, char const* Function) : + m_SourceCodeReference(File, Line, Function) + { + } + explicit TraceContext(SourceCodeReference& SourceCodeReference) : + m_SourceCodeReference(SourceCodeReference) + { + } + + void operator () (wchar_t const* Format, ...) const + { + va_list Arguments; + va_start(Arguments, Format); + wchar_t TextA[8u << 10] { }; + vswprintf_s(TextA, Format, Arguments); + va_end(Arguments); + wchar_t TextB[8u << 10] { }; + swprintf_s(TextB, L"%hs(%d): %hs: ", m_SourceCodeReference.FileName(), m_SourceCodeReference.Line, m_SourceCodeReference.Function); + wcscat_s(TextB, TextA); + OutputDebugStringW(TextB); + } + + SourceCodeReference m_SourceCodeReference; +}; + +#if !defined(NDEBUG) || 1 + #define TRACE TraceContext(__FILE__, __LINE__, __FUNCTION__) +#else + #define TRACE(...) 1 ? 0 : std::ignore = std::make_tuple(__VA_ARGS__) +#endif + +template +inline std::string ToString(std::pair const (&Items)[t_ItemCount], ValueType Value) +{ + auto const Iterator = std::find_if(Items, Items + t_ItemCount, [=] (auto&& Item) { return Item.first == Value; }); + return (Iterator != Items + t_ItemCount) ? Iterator->second : std::to_string(Value); +} + +inline std::wstring ToString(GUID const& Value) +{ + OLECHAR ValueString[40]; + WI_VERIFY(StringFromGUID2(Value, ValueString, static_cast(std::size(ValueString)))); + return ValueString; +} + +template +inline std::wstring ToString(std::pair const (&Items)[t_ItemCount], GUID const& Value) +{ + auto const Iterator = std::find_if(Items, Items + t_ItemCount, [=] (auto&& Item) { return *Item.first == Value; }); + return (Iterator != Items + t_ItemCount) ? Iterator->second : ToString(Value); +} diff --git a/samples/ContentDecryptionModule01/packages.config b/samples/ContentDecryptionModule01/packages.config new file mode 100644 index 0000000..78f2204 --- /dev/null +++ b/samples/ContentDecryptionModule01/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/samples/ContentDecryptionModule01/resource.h b/samples/ContentDecryptionModule01/resource.h new file mode 100644 index 0000000..39f59ae --- /dev/null +++ b/samples/ContentDecryptionModule01/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ContentDecryptionModule01.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif From d7a6d4ef3ef0437e63be3b1b49e355a0c46f1e62 Mon Sep 17 00:00:00 2001 From: Roman Ryltsov Date: Thu, 2 Nov 2023 08:54:40 +0100 Subject: [PATCH 3/3] Updated MediaEngineEMEUWPSample sample to work with the same ContentDecryption.h --- samples/MediaEngineEMEUWPSample/src/App.cpp | 36 +- .../MediaEngineEMEUWPSample/src/EmeFactory.h | 671 ------------------ .../src/MediaEngineEMEUWPSample.vcxproj | 1 - .../MediaEngineEMEUWPSample.vcxproj.filters | 1 - .../src/MediaEnginePlayer.h | 19 +- samples/MediaEngineEMEUWPSample/src/pch.h | 73 +- 6 files changed, 31 insertions(+), 770 deletions(-) delete mode 100644 samples/MediaEngineEMEUWPSample/src/EmeFactory.h diff --git a/samples/MediaEngineEMEUWPSample/src/App.cpp b/samples/MediaEngineEMEUWPSample/src/App.cpp index a37ef7e..cb57b6f 100644 --- a/samples/MediaEngineEMEUWPSample/src/App.cpp +++ b/samples/MediaEngineEMEUWPSample/src/App.cpp @@ -2,7 +2,9 @@ // Licensed under the MIT License. #include "pch.h" -#include "EmeFactory.h" + +#include "..\..\ContentDecryptionModule01\ContentDecryption.h" + #include "MediaEnginePlayer.h" #include "MediaFoundationHelpers.h" @@ -48,7 +50,7 @@ struct App : implements winrt::com_ptr m_Player; // EME media members - std::shared_ptr m_MediaKeySession; + std::shared_ptr m_MediaKeySession; // Composition members mutable wil::critical_section m_compositionLock; @@ -85,9 +87,9 @@ struct App : implements winrt::com_ptr m_Owner; }; - void HandleKeySessionMessage(MF_MEDIAKEYSESSION_MESSAGETYPE Type, gsl::span Data, LPCWSTR Location) + void HandleKeySessionMessage(MF_MEDIAKEYSESSION_MESSAGETYPE Type, std::vector const& Data, std::wstring const& Location) { - TRACE(L"Type %hs, Data.size() %zu, Location \"%ls\"\n", ToString(Type).c_str(), Data.size(), Location); + TRACE(L"Type %hs, Data.size() %zu, Location \"%ls\"\n", ToString(Type).c_str(), Data.size(), Location.c_str()); // Parse challenge value and request headers from key message winrt::hstring messageString{ reinterpret_cast(const_cast(Data.data())), static_cast(Data.size() / sizeof(wchar_t)) }; @@ -147,36 +149,36 @@ struct App : implements TRACE(L"Type %hs, Response.Length() %u\n", ToString(Type).c_str(), Response.Length()); // Pass response data to update() method on the MediaKeySession - m_MediaKeySession->update(gsl::span(Response.data(), Response.Length())); + m_MediaKeySession->Update(std::vector(Response.data(), Response.data() + Response.Length())); } void InitializePlayback() { TRACE(L"...\n"); // Setup EME - media::eme::EmeFactoryConfiguration Configuration { }; + Eme::FactoryConfiguration Configuration { }; // Use the local cache folder which is not backed up / synced to the cloud for CDM storage - Configuration.storagePath = Windows::Storage::ApplicationData::Current().LocalCacheFolder().Path().c_str(); - media::eme::EmeFactory EmeFactory(Configuration); + Configuration.StoragePath = Windows::Storage::ApplicationData::Current().LocalCacheFolder().Path().c_str(); + Eme::Factory EmeFactory(Configuration); // Request access to the Playready key system. // This is equivalent to the following Javascript EME call: // Navigator.requestMediaKeySystemAccess("com.microsoft.playready.recommendation", [{ initDataTypes: ['cenc'], persistentState: 'optional', distinctiveIdentifier: 'required']) - std::vector MediaKeySystemConfigurationVector(1); + std::vector MediaKeySystemConfigurationVector(1); { auto& MediaKeySystemConfiguration = MediaKeySystemConfigurationVector[0]; - MediaKeySystemConfiguration.m_initDataTypes.emplace_back(L"cenc"); - MediaKeySystemConfiguration.m_persistentStateRequirement = MF_MEDIAKEYS_REQUIREMENT_OPTIONAL; - MediaKeySystemConfiguration.m_distinctiveIdRequirement = MF_MEDIAKEYS_REQUIREMENT_REQUIRED; + MediaKeySystemConfiguration.InitDataTypeVector.emplace_back(L"cenc"); + MediaKeySystemConfiguration.PersistentState = MF_MEDIAKEYS_REQUIREMENT_OPTIONAL; + MediaKeySystemConfiguration.DistinctiveIdentifier = MF_MEDIAKEYS_REQUIREMENT_REQUIRED; } - auto const KeySystemAccess = EmeFactory.requestMediaKeySystemAccess(L"com.microsoft.playready.recommendation", MediaKeySystemConfigurationVector); + auto const KeySystemAccess = EmeFactory.RequestMediaKeySystemAccess(L"com.microsoft.playready.recommendation", MediaKeySystemConfigurationVector); THROW_HR_IF_MSG(MF_E_UNSUPPORTED_SERVICE, !KeySystemAccess, "Key system configuration not supported."); // Create MediaKeys, MediaKeySession and setup the onmessage callback on the session (to handle license acquisition requests) - auto const MediaKeys = KeySystemAccess->createMediaKeys(); - m_MediaKeySession = MediaKeys->createSession(); - m_MediaKeySession->onmessage([&] (MF_MEDIAKEYSESSION_MESSAGETYPE Type, gsl::span Data, LPCWSTR Location) { HandleKeySessionMessage(Type, Data, Location); }); - m_MediaKeySession->generateRequest(L"cenc", std::vector { g_InitializationData, g_InitializationData + std::size(g_InitializationData) }); + auto const MediaKeys = KeySystemAccess->CreateMediaKeys(); + m_MediaKeySession = MediaKeys->CreateSession(); + m_MediaKeySession->OnMessage = [&] (MF_MEDIAKEYSESSION_MESSAGETYPE Type, std::vector const& Data, std::wstring const& Location) { HandleKeySessionMessage(Type, Data, Location); }; + m_MediaKeySession->GenerateRequest(L"cenc", std::vector { g_InitializationData, g_InitializationData + std::size(g_InitializationData) }); // Create a source resolver to create an IMFMediaSource for the content URL. // This will create an instance of an inbuilt OS media source for playback. diff --git a/samples/MediaEngineEMEUWPSample/src/EmeFactory.h b/samples/MediaEngineEMEUWPSample/src/EmeFactory.h deleted file mode 100644 index 759de66..0000000 --- a/samples/MediaEngineEMEUWPSample/src/EmeFactory.h +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -inline std::string ToString(MF_MEDIAKEYSESSION_TYPE Value) -{ - static std::pair constexpr const g_MessageTypes[] - { - #define IDENFITIER(Name) std::make_pair(Name, #Name), - IDENFITIER(MF_MEDIAKEYSESSION_TYPE_TEMPORARY) - IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_LICENSE) - IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_RELEASE_MESSAGE) - IDENFITIER(MF_MEDIAKEYSESSION_TYPE_PERSISTENT_USAGE_RECORD) - #undef IDENFITIER - }; - return ToString(g_MessageTypes, Value); -} - -inline std::string ToString(MF_MEDIAKEY_STATUS Value) -{ - static std::pair constexpr const g_MessageTypes[] - { - #define IDENFITIER(Name) std::make_pair(Name, #Name), - IDENFITIER(MF_MEDIAKEY_STATUS_USABLE) - IDENFITIER(MF_MEDIAKEY_STATUS_EXPIRED) - IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_DOWNSCALED) - IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_NOT_ALLOWED) - IDENFITIER(MF_MEDIAKEY_STATUS_STATUS_PENDING) - IDENFITIER(MF_MEDIAKEY_STATUS_INTERNAL_ERROR) - IDENFITIER(MF_MEDIAKEY_STATUS_RELEASED) - IDENFITIER(MF_MEDIAKEY_STATUS_OUTPUT_RESTRICTED) - #undef IDENFITIER - }; - return ToString(g_MessageTypes, Value); -} - -// MFMediaKeyStatus - -inline std::string ToString(MF_MEDIAKEYSESSION_MESSAGETYPE Value) -{ - static std::pair constexpr const g_MessageTypes[] - { - #define IDENFITIER(Name) std::make_pair(Name, #Name), - IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST) - IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RENEWAL) - IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RELEASE) - IDENFITIER(MF_MEDIAKEYSESSION_MESSAGETYPE_INDIVIDUALIZATION_REQUEST) - #undef IDENFITIER - }; - return ToString(g_MessageTypes, Value); -} - -// MF_CROSS_ORIGIN_POLICY - -namespace media { -namespace eme { - -// Based on the MediaKeySession interface in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession -class MediaKeySession -{ -public: - MediaKeySession(wil::com_ptr const& ContentDecryptionModule, MF_MEDIAKEYSESSION_TYPE SessionType) - { - WI_ASSERT(ContentDecryptionModule); - auto const OnKeyMessage = [&] (MF_MEDIAKEYSESSION_MESSAGETYPE messageType, gsl::span message, LPCWSTR destinationUrl) { m_keyMessageCB(messageType, message, destinationUrl); }; - auto const OnKeyStatusChanged = [&] () { m_keyStatusChangedCB(); }; - m_sessionCallbacks = winrt::make_self(OnKeyMessage, OnKeyStatusChanged); - TRACE(L"Before IMFContentDecryptionModule::CreateSession, this %p, SessionType %hs\n", this, ToString(SessionType).c_str()); - THROW_IF_FAILED(ContentDecryptionModule->CreateSession(SessionType, m_sessionCallbacks.get(), m_cdmSession.put())); - TRACE(L"this %p, IMFContentDecryptionModuleSession m_cdmSession %p\n", this, m_cdmSession.get()); - } - virtual ~MediaKeySession() - { - static_cast(m_sessionCallbacks.get())->DetachParent(); - } - - // A unique string identifier generated by the CDM that can be used by the application - // to identify session objects. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-sessionid - wil::unique_cotaskmem_string sessionId() const - { - wil::unique_cotaskmem_string Result; - THROW_IF_FAILED(m_cdmSession->GetSessionId(Result.put())); - TRACE(L"After IMFContentDecryptionModule::GetSessionId, this %p, Result %ls\n", this, Result.get()); - return Result; - } - - // The expiration time for all key(s) in the session, or NaN if no such time exists or - // if the license explicitly never expires, as determined by the CDM. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-expiration - // expiration is based on https://tc39.es/ecma262/#sec-time-values-and-time-range - double expiration() const - { - double Result = 0.0; - THROW_IF_FAILED(m_cdmSession->GetExpiration(&Result)); - TRACE(L"After IMFContentDecryptionModule::GetExpiration, this %p, Result %.3f\n", this, Result); - return Result; - } - - using MediaKeyStatusPair = std::tuple, MF_MEDIAKEY_STATUS>; - // A reference to an array of key IDs known to the session to the current status - // of the associated key. Each entry MUST have a unique key ID. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-keystatuses - std::vector keyStatuses() const - { - wil::unique_cotaskmem_array_ptr abiKeyStatuses; - THROW_IF_FAILED(m_cdmSession->GetKeyStatuses(abiKeyStatuses.put(), abiKeyStatuses.size_address())); - TRACE(L"After IMFContentDecryptionModule::GetKeyStatuses, this %p, abiKeyStatuses.size() %zu\n", this, abiKeyStatuses.size()); - std::vector statusPairs(abiKeyStatuses.size()); - for(size_t i = 0; i < abiKeyStatuses.size(); i++) - { - std::vector keyId(abiKeyStatuses[i].cbKeyId); - memcpy(&keyId[0], abiKeyStatuses[i].pbKeyId, keyId.size()); - statusPairs[i] = std::make_tuple(std::move(keyId), abiKeyStatuses[i].eMediaKeyStatus); - } - return statusPairs; - } - - // Loads the data stored for the specified session into this object. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-load - bool load(const std::wstring& SessionIdentifier) const - { - BOOL Result = FALSE; - THROW_IF_FAILED(m_cdmSession->Load(SessionIdentifier.c_str(), &Result)); - TRACE(L"After IMFContentDecryptionModule::Load, this %p, SessionIdentifier %ls, Result %d\n", this, SessionIdentifier.c_str(), Result); - return !!Result; - } - - // Callback registration methods - using KeyMessageCB = std::function, LPCWSTR)>; - void onmessage(KeyMessageCB&& keyMessageCB) - { - m_keyMessageCB = std::move(keyMessageCB); - } - - using KeyStatusChangedCB = std::function; - void onkeystatuseschange(KeyStatusChangedCB&& keyStatusChangedCB) - { - m_keyStatusChangedCB = std::move(keyStatusChangedCB); - } - - // Generates a license request based on the InitializationData. A message of type "license-request" or - // "individualization-request" will always be queued if the algorithm succeeds - // and the promise is resolved. - // Structure for init data is based on InitializationDataType - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-generaterequest - // https://www.w3.org/TR/eme-initdata-registry/ - void generateRequest(const std::wstring& InitializationDataType, std::vector const& InitializationData) - { - TRACE(L"Before IMFContentDecryptionModule::GenerateRequest, this %p, InitializationDataType %ls, InitializationData %zu\n", this, InitializationDataType.c_str(), InitializationData.size()); - THROW_IF_FAILED(m_cdmSession->GenerateRequest(InitializationDataType.c_str(), InitializationData.data(), static_cast(InitializationData.size()))); - } - - // Provides messages, including licenses, to the CDM. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-update - void update(gsl::span ResponseData) - { - TRACE(L"Before IMFContentDecryptionModule::Update, this %p, ResponseData %zu\n", this, ResponseData.size()); - THROW_IF_FAILED(m_cdmSession->Update(ResponseData.data(), static_cast(ResponseData.size()))); - } - - // Indicates that the application no longer needs the session and the CDM should release any resources - // associated with the session and close it. Persisted data should not be released or cleared. - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-close - void close() - { - TRACE(L"Before IMFContentDecryptionModule::Close, this %p\n", this); - THROW_IF_FAILED(m_cdmSession->Close()); - } - - // Removes all license(s) and key(s) associated with the session. For persistent session types, - // other session data will be cleared as defined for each session type once a - // release message acknowledgment is processed by Update(). - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysession-remove - void remove() - { - TRACE(L"Before IMFContentDecryptionModule::Remove, this %p\n", this); - THROW_IF_FAILED(m_cdmSession->Remove()); - } - -private: - - class SessionCallbackHelper : public winrt::implements - { - public: - SessionCallbackHelper(MediaKeySession::KeyMessageCB&& keyMessageCB, MediaKeySession::KeyStatusChangedCB&& keyStatusChangedCB) : - m_keyMessageCB(std::move(keyMessageCB)), - m_keyStatusChangedCB(std::move(keyStatusChangedCB)) - { - THROW_HR_IF(E_INVALIDARG, !m_keyMessageCB); - THROW_HR_IF(E_INVALIDARG, !m_keyStatusChangedCB); - } - ~SessionCallbackHelper() - { - DetachParent(); - } - - void DetachParent() - { - auto lock = m_lock.lock(); - m_keyMessageCB = nullptr; - m_keyStatusChangedCB = nullptr; - } - - // IMFContentDecryptionModuleSessionCallbacks - IFACEMETHOD(KeyMessage)(MF_MEDIAKEYSESSION_MESSAGETYPE Type, const BYTE* Data, DWORD DataSize, LPCWSTR Location) noexcept override - { - TRACE(L"this %p, Type %hs, DataSize %u, Location \"%ls\"\n", this, ToString(Type).c_str(), DataSize, Location); - try - { - auto lock = m_lock.lock(); - m_keyMessageCB(Type, gsl::span(const_cast(Data), static_cast(DataSize)), Location); - } - CATCH_RETURN(); - return S_OK; - } - IFACEMETHOD(KeyStatusChanged)() noexcept override - { - TRACE(L"this %p\n", this); - try - { - auto lock = m_lock.lock(); - m_keyStatusChangedCB(); - } - CATCH_RETURN(); - return S_OK; - } - - private: - wil::critical_section m_lock; - MediaKeySession::KeyMessageCB m_keyMessageCB; - MediaKeySession::KeyStatusChangedCB m_keyStatusChangedCB; - }; - - wil::com_ptr m_cdmSession; - winrt::com_ptr m_sessionCallbacks; - KeyMessageCB m_keyMessageCB; - KeyStatusChangedCB m_keyStatusChangedCB; -}; - -// Based on the MediaKeys interface in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeys -class MediaKeys -{ -public: - MediaKeys(wil::com_ptr const& ContentDecryptionModule) : - m_ContentDecryptionModule(ContentDecryptionModule) - { - } - - std::shared_ptr createSession(MF_MEDIAKEYSESSION_TYPE sessionType = MF_MEDIAKEYSESSION_TYPE_TEMPORARY) - { - return std::make_shared(m_ContentDecryptionModule, sessionType); - } - //bool SetServerCertificate(std::vector& ServerCertificate) - //{ - // return SUCCEEDED(m_ContentDecryptionModule->SetServerCertificate(ServerCertificate.data(), static_cast(ServerCertificate.size()))); - //} - - wil::com_ptr const m_ContentDecryptionModule; -}; - -// This struct represents a MediaKeySystemMediaCapability dictionary, as defined in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-mediakeysystemmediacapability -struct MediaKeySystemMediaCapability -{ - std::wstring contentType; - std::wstring robustness; -}; - -struct EmeFactoryConfiguration -{ - static void SetPropertyValue(wil::com_ptr const& PropertyStore, PROPERTYKEY const& Key, std::wstring const& Value) - { - wil::unique_prop_variant VariantValue; - VariantValue.vt = VT_BSTR; - VariantValue.bstrVal = wil::make_bstr(Value.c_str()).release(); - THROW_IF_FAILED(PropertyStore->SetValue(Key, VariantValue)); - } - - wil::com_ptr ConvertToPropertyStore() const - { - wil::com_ptr PropertyStore; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); - SetPropertyValue(PropertyStore, MF_EME_CDM_STOREPATH, storagePath); - if(!inPrivateStoragePath.empty()) - SetPropertyValue(PropertyStore, MF_EME_CDM_INPRIVATESTOREPATH, inPrivateStoragePath); - return PropertyStore; - } - - // Use this property to pass a storage location the CDM can use for initialization. - // This storage location will also be used for content specific data if INPRIVATESTOREPATH is not set. - // The app should not delete the store location to optimize performance of the CDM. - std::wstring storagePath; - // Use this property to pass a storage location the CDM can use for content specific data (e.g licenses) - // The app should delete the store location after the CDM object has been released. - std::wstring inPrivateStoragePath; -}; - -// This class represents a MediaKeySystemConfiguration dictionary, as defined in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemconfiguration-dictionary -class MediaKeySystemConfiguration -{ -public: - static MediaKeySystemConfiguration FromPropertyStore(wil::com_ptr const& propertyStore) - { - MediaKeySystemConfiguration configuration; - - // Init data types - wil::unique_prop_variant initDataTypes; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_INITDATATYPES, &initDataTypes)) && - initDataTypes.vt == (VT_VECTOR | VT_BSTR)) - { - configuration.m_initDataTypes.resize(initDataTypes.cabstr.cElems); - for(size_t i = 0; i < initDataTypes.cabstr.cElems; i++) - { - configuration.m_initDataTypes[i] = std::wstring(initDataTypes.cabstr.pElems[i]); - } - } - - // Audio capabilities - wil::unique_prop_variant audioCapabilities; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_AUDIOCAPABILITIES, &audioCapabilities)) && - audioCapabilities.vt == (VT_VECTOR | VT_VARIANT)) - { - GetCapabilitiesFromPropVar(audioCapabilities, configuration.m_audioCapabilities); - } - - // Video capabilities - wil::unique_prop_variant videoCapabilities; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_VIDEOCAPABILITIES, &videoCapabilities)) && - videoCapabilities.vt == (VT_VECTOR | VT_VARIANT)) - { - GetCapabilitiesFromPropVar(videoCapabilities, configuration.m_videoCapabilities); - } - - // Distinctive ID - wil::unique_prop_variant distinctiveId; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_DISTINCTIVEID, &distinctiveId)) && - distinctiveId.vt == VT_UI4) - { - configuration.m_distinctiveIdRequirement = static_cast(distinctiveId.ulVal); - } - - // Persistent state - wil::unique_prop_variant persistentState; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_PERSISTEDSTATE, &persistentState)) && - persistentState.vt == VT_UI4) - { - configuration.m_persistentStateRequirement = static_cast(persistentState.ulVal); - } - - // Session types - wil::unique_prop_variant sessionTypes; - if(SUCCEEDED(propertyStore->GetValue(MF_EME_SESSIONTYPES, &sessionTypes)) && - sessionTypes.vt == (VT_VECTOR | VT_UI4)) - { - configuration.m_sessionTypes = std::vector(sessionTypes.caul.cElems); - for(size_t i = 0; i < sessionTypes.caul.cElems; i++) - { - configuration.m_sessionTypes[i] = static_cast(sessionTypes.caul.pElems[i]); - } - } - - return configuration; - } - wil::com_ptr ConvertToPropertyStore() - { - wil::com_ptr propertyStore; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(propertyStore.put()))); - - // Init data types - auto initDataTypeArray = wil::make_unique_cotaskmem(m_initDataTypes.size()); - for(size_t i = 0; i < m_initDataTypes.size(); i++) - { - initDataTypeArray[i] = wil::make_bstr(m_initDataTypes[i].c_str()).release(); - } - wil::unique_prop_variant initDataTypes; - initDataTypes.vt = VT_VECTOR | VT_BSTR; - initDataTypes.cabstr.cElems = static_cast(m_initDataTypes.size()); - initDataTypes.cabstr.pElems = initDataTypeArray.release(); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_INITDATATYPES, initDataTypes)); - - // Audio capabilities - wil::unique_prop_variant audioCapabilities = GenerateCapabilityProperty(m_audioCapabilities); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_AUDIOCAPABILITIES, audioCapabilities)); - - // Video capabilities - wil::unique_prop_variant videoCapabilities = GenerateCapabilityProperty(m_videoCapabilities); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_VIDEOCAPABILITIES, videoCapabilities)); - - // Distinctive ID - wil::unique_prop_variant distinctiveId; - distinctiveId.vt = VT_UI4; - distinctiveId.ulVal = static_cast(m_distinctiveIdRequirement); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_DISTINCTIVEID, distinctiveId)); - - // Persistent state - wil::unique_prop_variant persistentState; - persistentState.vt = VT_UI4; - persistentState.ulVal = static_cast(m_persistentStateRequirement); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_PERSISTEDSTATE, persistentState)); - - // Session types - auto sessionTypeArray = wil::make_unique_cotaskmem(m_sessionTypes.size()); - for(size_t i = 0; i < m_sessionTypes.size(); i++) - { - sessionTypeArray[i] = m_sessionTypes[i]; - } - wil::unique_prop_variant sessionTypes; - sessionTypes.vt = VT_VECTOR | VT_UI4; - sessionTypes.caul.cElems = static_cast(m_sessionTypes.size()); - sessionTypes.caul.pElems = sessionTypeArray.release(); - THROW_IF_FAILED(propertyStore->SetValue(MF_EME_SESSIONTYPES, sessionTypes)); - - return propertyStore; - } - - std::wstring m_label; - std::vector m_initDataTypes; - std::vector m_audioCapabilities; - std::vector m_videoCapabilities; - MF_MEDIAKEYS_REQUIREMENT m_distinctiveIdRequirement; - MF_MEDIAKEYS_REQUIREMENT m_persistentStateRequirement; - std::vector m_sessionTypes; - -private: - static wil::unique_prop_variant GenerateCapabilityProperty(const std::vector& capabilities) - { - auto capabilitiesArray = wil::make_unique_cotaskmem(capabilities.size()); - for(size_t i = 0; i < capabilities.size(); i++) - { - auto& Capability = capabilitiesArray[i]; - wil::com_ptr PropertyStore; - THROW_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(PropertyStore.put()))); - auto const SetValue = [&](PROPERTYKEY const& Key, std::wstring const& Value) - { - wil::unique_prop_variant VariantValue; - VariantValue.vt = VT_BSTR; - VariantValue.bstrVal = wil::make_bstr(Value.c_str()).release(); - THROW_IF_FAILED(PropertyStore->SetValue(Key, VariantValue)); - }; - SetValue(MF_EME_ROBUSTNESS, capabilities[i].robustness); - SetValue(MF_EME_CONTENTTYPE, capabilities[i].contentType); - Capability.vt = VT_UNKNOWN; - Capability.punkVal = PropertyStore.query().detach(); - } - wil::unique_prop_variant capabilitiesProperty; - capabilitiesProperty.vt = VT_VECTOR | VT_VARIANT; - capabilitiesProperty.capropvar.cElems = static_cast(capabilities.size()); - capabilitiesProperty.capropvar.pElems = capabilitiesArray.release(); - return capabilitiesProperty; - } - static void GetCapabilitiesFromPropVar(const PROPVARIANT& capabilitiesProperty, std::vector& capabilities) - { - capabilities = std::vector(capabilitiesProperty.capropvar.cElems); - for(size_t i = 0; i < capabilities.size(); i++) - { - PROPVARIANT& capabilityProperty = capabilitiesProperty.capropvar.pElems[i]; - wil::com_ptr capabilityPropertyStore; - if(capabilityProperty.vt != VT_UNKNOWN || FAILED(capabilityProperty.punkVal->QueryInterface(IID_PPV_ARGS(capabilityPropertyStore.put())))) - continue; - wil::unique_prop_variant robustness; - if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_ROBUSTNESS, &robustness)) && robustness.vt == VT_BSTR) - capabilities[i].robustness = std::wstring(robustness.bstrVal); - wil::unique_prop_variant contentType; - if(SUCCEEDED(capabilityPropertyStore->GetValue(MF_EME_CONTENTTYPE, &contentType)) && contentType.vt == VT_BSTR) - capabilities[i].contentType = std::wstring(contentType.bstrVal); - } - } -}; - -// Based on the MediaKeySystemAccess interface in the EME specification: -// https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#mediakeysystemaccess-interface -class MediaKeySystemAccess -{ -public: - MediaKeySystemAccess(EmeFactoryConfiguration& cdmConfigProperties, wil::com_ptr const& cdmAccess) : - m_cdmAccess(cdmAccess) - { - m_cdmProperties = cdmConfigProperties.ConvertToPropertyStore(); - wil::com_ptr PropertyStore; - THROW_IF_FAILED(cdmAccess->GetConfiguration(PropertyStore.put())); - m_keySystemConfiguration = MediaKeySystemConfiguration::FromPropertyStore(PropertyStore); - } - - wil::unique_cotaskmem_string keySystem() const - { - wil::unique_cotaskmem_string keySystem; - THROW_IF_FAILED(m_cdmAccess->GetKeySystem(keySystem.put())); - return keySystem; - } - const MediaKeySystemConfiguration& getConfiguration() const - { - return m_keySystemConfiguration; - } - std::shared_ptr createMediaKeys() const - { - wil::com_ptr ContentDecryptionModule; - TRACE(L"Before IMFContentDecryptionModuleAccess::CreateContentDecryptionModule, this %p\n", this); - THROW_IF_FAILED(m_cdmAccess->CreateContentDecryptionModule(m_cdmProperties.get(), ContentDecryptionModule.put())); - TRACE(L"this %p, IMFContentDecryptionModule cdm %p\n", this, ContentDecryptionModule.get()); - return std::make_shared(ContentDecryptionModule.get()); - } - -private: - wil::com_ptr m_cdmProperties; - wil::com_ptr m_cdmAccess; - MediaKeySystemConfiguration m_keySystemConfiguration; -}; - -// Class used to query for EME keysystem support and attempt creation of a MediaKeySystemAccess object for a specified keysystem -class EmeFactory -{ -public: - EmeFactory(EmeFactoryConfiguration cdmConfigProperties) - { - TRACE(L"this %p\n", this); - THROW_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(m_classFactory.put()))); - m_cdmConfigProperties = cdmConfigProperties; - } - virtual ~EmeFactory() = default; - - // Used to request creation of a MediaKeySystemAccess. Based on: - // https://www.w3.org/TR/2017/REC-encrypted-media-20170918/#dom-navigator-requestmediakeysystemaccess - // Returns: MediaKeySystemAccess on success, or nullptr if the keysystem or none of the specified configurations are supported - std::shared_ptr requestMediaKeySystemAccess(const std::wstring& keySystem, std::vector& configurations) - { - TRACE(L"this %p, keySystem %ls, configurations.size() %zu\n", this, keySystem.c_str(), configurations.size()); - - // Use a cached CDM factory if client is querying for the same keysystem, otherwise create a new CDM factory - if(!m_cdmFactory || m_cachedFactoryKeySystem != keySystem) - { - TRACE(L"Before IMFMediaEngineClassFactory4::CreateContentDecryptionModuleFactory, this %p\n", this); - if(FAILED(LOG_IF_FAILED(m_classFactory->CreateContentDecryptionModuleFactory(keySystem.c_str(), IID_PPV_ARGS(m_cdmFactory.put()))))) - return nullptr; - TRACE(L"this %p, IMFContentDecryptionModuleFactory m_cdmFactory %p\n", this, m_cdmFactory.get()); - m_cachedFactoryKeySystem = keySystem; - } - - if(!m_cdmFactory->IsTypeSupported(keySystem.c_str(), nullptr)) - return nullptr; - wil::com_ptr ContentDecryptionModuleAccess; - std::vector> configurationPropertyStores; - for(auto& config : configurations) - { - wil::com_ptr_t propStore; - propStore.attach(config.ConvertToPropertyStore().detach()); - configurationPropertyStores.push_back(propStore); - } - if(FAILED(LOG_IF_FAILED(m_cdmFactory->CreateContentDecryptionModuleAccess(keySystem.c_str(), configurationPropertyStores[0].addressof(), static_cast(configurationPropertyStores.size()), ContentDecryptionModuleAccess.put())))) - return nullptr; - - return std::make_shared(m_cdmConfigProperties, ContentDecryptionModuleAccess.get()); - } - -private: - EmeFactoryConfiguration m_cdmConfigProperties; - wil::com_ptr m_classFactory; - wil::com_ptr m_cdmFactory; - std::wstring m_cachedFactoryKeySystem; -}; - -} // namespace eme - -// A custom MF media source wrapper which supports protected playback using a provided ContentDecryptionModule -// Demuxing is performed by the inner source provided by the application -class InputTrustAuthorityMediaSource : public winrt::implements -{ -public: - InputTrustAuthorityMediaSource(std::shared_ptr mediaKeys, wil::com_ptr const& MediaSource) : - m_Inner(MediaSource), - m_ContentDecryptionModule(mediaKeys->m_ContentDecryptionModule) - { - WI_ASSERT(m_Inner && m_ContentDecryptionModule); - } - -// IMFMediaSource - IFACEMETHOD(GetCharacteristics)(DWORD* characteristics) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->GetCharacteristics(characteristics); - } - IFACEMETHOD(CreatePresentationDescriptor)(IMFPresentationDescriptor** presentationDescriptorOut) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->CreatePresentationDescriptor(presentationDescriptorOut); - } - IFACEMETHOD(Start)(IMFPresentationDescriptor* PresentationDescriptor, GUID const* TimeFormat, PROPVARIANT const* StartPosition) noexcept override - { - TRACE(L"this %p\n", this); - return m_Inner->Start(PresentationDescriptor, TimeFormat, StartPosition); - } - IFACEMETHOD(Stop)() noexcept override - { - TRACE(L"this %p\n", this); - return m_Inner->Stop(); - } - IFACEMETHOD(Pause)() noexcept override - { - TRACE(L"this %p\n", this); - return m_Inner->Pause(); - } - IFACEMETHOD(Shutdown)() noexcept override - { - TRACE(L"this %p\n", this); - { - [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); - m_StreamTrustedInputMap.clear(); - } - return m_Inner->Shutdown(); - } - -// IMFMediaEventGenerator - IFACEMETHOD(GetEvent)(DWORD flags, IMFMediaEvent** eventOut) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->GetEvent(flags, eventOut); - } - IFACEMETHOD(BeginGetEvent)(IMFAsyncCallback* callback, IUnknown* state) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->BeginGetEvent(callback, state); - } - IFACEMETHOD(EndGetEvent)(IMFAsyncResult* result, IMFMediaEvent** eventOut) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->EndGetEvent(result, eventOut); - } - IFACEMETHOD(QueueEvent)(MediaEventType type, REFGUID extendedType, HRESULT status, const PROPVARIANT* value) noexcept override - { - //TRACE(L"this %p\n", this); - return m_Inner->QueueEvent(type, extendedType, status, value); - } - -// IMFTrustedInput - IFACEMETHOD(GetInputTrustAuthority)(DWORD StreamIdentifier, REFIID InterfaceIdentifier, IUnknown** Object) noexcept override - { - TRACE(L"this %p, StreamIdentifier %u, InterfaceIdentifier %ls\n", this, StreamIdentifier, ToString(InterfaceIdentifier).c_str()); - try - { - LOG_HR_IF(S_FALSE, InterfaceIdentifier != __uuidof(IMFInputTrustAuthority)); - WI_ASSERT(Object); - wil::com_ptr Unknown; - [[maybe_unused]] auto&& DataLock = m_DataMutex.lock_exclusive(); - auto const Iterator = m_StreamTrustedInputMap.find(StreamIdentifier); - if(Iterator == m_StreamTrustedInputMap.end()) - { - if(!m_TrustedInput) - THROW_IF_FAILED(m_ContentDecryptionModule->CreateTrustedInput(nullptr, 0, m_TrustedInput.put())); - THROW_IF_FAILED(m_TrustedInput->GetInputTrustAuthority(StreamIdentifier, InterfaceIdentifier, Unknown.put())); - m_StreamTrustedInputMap[StreamIdentifier] = Unknown; - } else - Unknown = Iterator->second; - *Object = Unknown.detach(); - } - CATCH_RETURN(); - return S_OK; - } - -private: - wil::com_ptr const m_ContentDecryptionModule; - wil::com_ptr const m_Inner; - mutable wil::srwlock m_DataMutex; - wil::com_ptr m_TrustedInput; - std::map> m_StreamTrustedInputMap; -}; - -} // namespace media diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj index 7b50316..aa60762 100644 --- a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj +++ b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj @@ -99,7 +99,6 @@ - diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters index 3159572..d45ea88 100644 --- a/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters +++ b/samples/MediaEngineEMEUWPSample/src/MediaEngineEMEUWPSample.vcxproj.filters @@ -6,7 +6,6 @@ - diff --git a/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h b/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h index e207675..35df244 100644 --- a/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h +++ b/samples/MediaEngineEMEUWPSample/src/MediaEnginePlayer.h @@ -3,12 +3,13 @@ #pragma once -#include "EmeFactory.h" +#include "..\..\ContentDecryptionModule01\ContentDecryption.h" + #include "MediaFoundationHelpers.h" inline std::string ToString(MF_MEDIA_ENGINE_EVENT Value) { - static std::pair constexpr const g_MessageTypes[] + static std::pair constexpr const g_Items[] { #define IDENFITIER(Name) std::make_pair(Name, #Name), IDENFITIER(MF_MEDIA_ENGINE_EVENT_LOADSTART) @@ -52,7 +53,7 @@ inline std::string ToString(MF_MEDIA_ENGINE_EVENT Value) IDENFITIER(MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE) #undef IDENFITIER }; - return ToString(g_MessageTypes, Value); + return ToString(g_Items, Value); } namespace media @@ -145,8 +146,8 @@ class MediaEngineExtension : public winrt::implements { public: - MediaEngineProtectionManager(std::shared_ptr const& mediaKeys) : - m_ContentDecryptionModule(mediaKeys->m_ContentDecryptionModule) + MediaEngineProtectionManager(std::shared_ptr const& mediaKeys) : + m_ContentDecryptionModule(mediaKeys->ContentDecryptionModule) { // Use the PMP server from the CDM - this ensures that the media engine uses the same protected process as the CDM auto const cdmServices = m_ContentDecryptionModule.query(); @@ -258,7 +259,7 @@ class __declspec(uuid("{4B134833-D245-4EEF-89F5-370DEE5D37DE}")) IMediaEnginePla class MediaEnginePlayer : public winrt::implements { public: - MediaEnginePlayer(IMediaEnginePlayerSite* Site, IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) : + MediaEnginePlayer(IMediaEnginePlayerSite* Site, IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) : m_Site(Site) { TRACE(L"...\n"); @@ -266,7 +267,7 @@ class MediaEnginePlayer : public winrt::implements Initialize(mediaSource, mediaKeys); } - void Initialize(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) + void Initialize(IMFMediaSource* mediaSource, std::shared_ptr mediaKeys) { RunSyncInMTA([&]() { @@ -432,7 +433,7 @@ class MediaEnginePlayer : public winrt::implements d3d11DeviceContext.query()->SetMultithreadProtected(TRUE); THROW_IF_FAILED(m_dxgiDeviceManager->ResetDevice(d3d11Device.get(), m_deviceResetToken)); } - void CreateMediaEngine(wil::com_ptr const& MediaSource, std::shared_ptr mediaKeys) + void CreateMediaEngine(wil::com_ptr const& MediaSource, std::shared_ptr mediaKeys) { WI_ASSERT(MediaSource); @@ -456,7 +457,7 @@ class MediaEnginePlayer : public winrt::implements wil::com_ptr ExtensionMediaSource; if(mediaKeys) - ExtensionMediaSource = winrt::make_self(mediaKeys, MediaSource).get(); // Also exposes an IMFTrustedInput to the pipeline + ExtensionMediaSource = winrt::make_self(MediaSource, mediaKeys->ContentDecryptionModule).get(); // Also exposes an IMFTrustedInput to the pipeline else ExtensionMediaSource = MediaSource; // Set the app-provided media source directly on the media engine extension m_Extension = winrt::make_self(ExtensionMediaSource); diff --git a/samples/MediaEngineEMEUWPSample/src/pch.h b/samples/MediaEngineEMEUWPSample/src/pch.h index e9065cc..0d3552c 100644 --- a/samples/MediaEngineEMEUWPSample/src/pch.h +++ b/samples/MediaEngineEMEUWPSample/src/pch.h @@ -53,75 +53,6 @@ //////////////////////////////////////////////////////////////////////////////// -struct SourceCodeReference -{ - SourceCodeReference(char const* File, int Line, char const* Function) : - File(File), - Line(Line), - Function(Function) - { - } - char const* FileName() const - { - if(NotShorten) - return File; - char const* Separator = strrchr(File, '\\'); - if(!Separator) - return File; - return Separator + 1; - } +#pragma warning(disable: 4458) // declaration of ... hides class member - char const* File; - bool NotShorten = false; - int Line; - char const* Function; -}; - -struct TraceContext -{ - TraceContext(char const* File, int Line, char const* Function) : - m_SourceCodeReference(File, Line, Function) - { - } - explicit TraceContext(SourceCodeReference& SourceCodeReference) : - m_SourceCodeReference(SourceCodeReference) - { - } - - void operator () (wchar_t const* Format, ...) const - { - va_list Arguments; - va_start(Arguments, Format); - wchar_t TextA[8u << 10] { }; - vswprintf_s(TextA, Format, Arguments); - va_end(Arguments); - wchar_t TextB[8u << 10] { }; - swprintf_s(TextB, L"%hs(%d): %hs: ", m_SourceCodeReference.FileName(), m_SourceCodeReference.Line, m_SourceCodeReference.Function); - wcscat_s(TextB, TextA); - OutputDebugStringW(TextB); - } - - SourceCodeReference m_SourceCodeReference; -}; - -#if defined(DEVELOPMENT) || 1 - #define TRACE TraceContext(__FILE__, __LINE__, __FUNCTION__) -#else - #define TRACE 1 ? 0 : -#endif - -inline std::wstring ToString(GUID const& Value) -{ - OLECHAR ValueString[40]; - WI_VERIFY(StringFromGUID2(Value, ValueString, static_cast(std::size(ValueString)))); - return ValueString; -} - -template -inline std::string ToString(std::pair const (&Values)[t_Size], ValueType Value) -{ - for(auto&& ArrayValue: Values) - if(ArrayValue.first == Value) - return ArrayValue.second; - return ""; -} +#include "..\..\ContentDecryptionModule01\Helper.h"