media_foundation_cdm_util.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/cdm/win/media_foundation_cdm_util.h"
  5. #include <combaseapi.h>
  6. #include <initguid.h> // Needed for DEFINE_PROPERTYKEY to work properly.
  7. #include <mferror.h>
  8. #include <propkeydef.h> // Needed for DEFINE_PROPERTYKEY.
  9. #include "base/files/file_util.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/win/propvarutil.h"
  12. #include "base/win/scoped_propvariant.h"
  13. #include "media/base/win/mf_helpers.h"
  14. #include "media/cdm/cdm_paths.h"
  15. #include "media/cdm/win/media_foundation_cdm.h"
  16. namespace media {
  17. namespace {
  18. using Microsoft::WRL::ComPtr;
  19. // Key to the CDM Origin ID to be passed to the CDM for privacy purposes. The
  20. // same value is also used in MediaFoundation CDMs. Do NOT change this value!
  21. DEFINE_PROPERTYKEY(EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID,
  22. 0x1218a3e2,
  23. 0xcfb0,
  24. 0x4c98,
  25. 0x90,
  26. 0xe5,
  27. 0x5f,
  28. 0x58,
  29. 0x18,
  30. 0xd4,
  31. 0xb6,
  32. 0x7e,
  33. PID_FIRST_USABLE);
  34. void SetBSTR(const wchar_t* str, PROPVARIANT* propvariant) {
  35. propvariant->vt = VT_BSTR;
  36. propvariant->bstrVal = SysAllocString(str);
  37. }
  38. // Returns a property store similar to EME MediaKeySystemMediaCapability.
  39. HRESULT CreateVideoCapability(const CdmConfig& cdm_config,
  40. ComPtr<IPropertyStore>& video_capability) {
  41. ComPtr<IPropertyStore> temp_video_capability;
  42. RETURN_IF_FAILED(
  43. PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_video_capability)));
  44. base::win::ScopedPropVariant robustness;
  45. if (cdm_config.use_hw_secure_codecs) {
  46. // TODO(xhwang): Provide a way to support other robustness strings.
  47. SetBSTR(L"HW_SECURE_ALL", robustness.Receive());
  48. RETURN_IF_FAILED(
  49. temp_video_capability->SetValue(MF_EME_ROBUSTNESS, robustness.get()));
  50. }
  51. video_capability = temp_video_capability;
  52. return S_OK;
  53. }
  54. // Returns a property store similar to EME MediaKeySystemConfigurations.
  55. // What really matters here are video robustness, persistent state and
  56. // distinctive identifier.
  57. HRESULT BuildCdmAccessConfigurations(const CdmConfig& cdm_config,
  58. ComPtr<IPropertyStore>& configurations) {
  59. ComPtr<IPropertyStore> temp_configurations;
  60. RETURN_IF_FAILED(
  61. PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_configurations)));
  62. // Add an empty audio capability.
  63. base::win::ScopedPropVariant audio_capabilities;
  64. PROPVARIANT* var_to_set = audio_capabilities.Receive();
  65. var_to_set->vt = VT_VARIANT | VT_VECTOR;
  66. var_to_set->capropvar.cElems = 0;
  67. RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_AUDIOCAPABILITIES,
  68. audio_capabilities.get()));
  69. // Add a video capability so we can pass the correct robustness level.
  70. ComPtr<IPropertyStore> video_capability;
  71. RETURN_IF_FAILED(CreateVideoCapability(cdm_config, video_capability));
  72. base::win::ScopedPropVariant video_config;
  73. auto* video_config_ptr = video_config.Receive();
  74. video_config_ptr->vt = VT_UNKNOWN;
  75. video_config_ptr->punkVal = video_capability.Detach();
  76. base::win::ScopedPropVariant video_capabilities;
  77. var_to_set = video_capabilities.Receive();
  78. var_to_set->vt = VT_VARIANT | VT_VECTOR;
  79. var_to_set->capropvar.cElems = 1;
  80. var_to_set->capropvar.pElems =
  81. reinterpret_cast<PROPVARIANT*>(CoTaskMemAlloc(sizeof(PROPVARIANT)));
  82. PropVariantCopy(var_to_set->capropvar.pElems, video_config.ptr());
  83. RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_VIDEOCAPABILITIES,
  84. video_capabilities.get()));
  85. // Add persistent state.
  86. DCHECK(cdm_config.allow_persistent_state);
  87. base::win::ScopedPropVariant persisted_state;
  88. RETURN_IF_FAILED(InitPropVariantFromUInt32(MF_MEDIAKEYS_REQUIREMENT_REQUIRED,
  89. persisted_state.Receive()));
  90. RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_PERSISTEDSTATE,
  91. persisted_state.get()));
  92. // Add distinctive identifier.
  93. DCHECK(cdm_config.allow_distinctive_identifier);
  94. base::win::ScopedPropVariant distinctive_identifier;
  95. RETURN_IF_FAILED(InitPropVariantFromUInt32(MF_MEDIAKEYS_REQUIREMENT_REQUIRED,
  96. distinctive_identifier.Receive()));
  97. RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_DISTINCTIVEID,
  98. distinctive_identifier.get()));
  99. configurations = temp_configurations;
  100. return S_OK;
  101. }
  102. HRESULT BuildCdmProperties(
  103. const base::UnguessableToken& origin_id,
  104. const absl::optional<std::vector<uint8_t>>& client_token,
  105. const base::FilePath& store_path,
  106. ComPtr<IPropertyStore>& properties) {
  107. DCHECK(!origin_id.is_empty());
  108. ComPtr<IPropertyStore> temp_properties;
  109. RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_properties)));
  110. base::win::ScopedPropVariant origin_id_var;
  111. RETURN_IF_FAILED(InitPropVariantFromString(
  112. base::UTF8ToWide(origin_id.ToString()).c_str(), origin_id_var.Receive()));
  113. RETURN_IF_FAILED(temp_properties->SetValue(
  114. EME_CONTENTDECRYPTIONMODULE_ORIGIN_ID, origin_id_var.get()));
  115. if (client_token) {
  116. base::win::ScopedPropVariant client_token_var;
  117. PROPVARIANT* client_token_propvar = client_token_var.Receive();
  118. client_token_propvar->vt = VT_VECTOR | VT_UI1;
  119. client_token_propvar->caub.cElems = client_token->size();
  120. client_token_propvar->caub.pElems = reinterpret_cast<unsigned char*>(
  121. CoTaskMemAlloc(client_token->size() * sizeof(char)));
  122. memcpy(client_token_propvar->caub.pElems, client_token->data(),
  123. client_token->size());
  124. RETURN_IF_FAILED(temp_properties->SetValue(
  125. EME_CONTENTDECRYPTIONMODULE_CLIENT_TOKEN, client_token_var.get()));
  126. }
  127. base::win::ScopedPropVariant store_path_var;
  128. RETURN_IF_FAILED(InitPropVariantFromString(store_path.value().c_str(),
  129. store_path_var.Receive()));
  130. RETURN_IF_FAILED(temp_properties->SetValue(
  131. MF_CONTENTDECRYPTIONMODULE_STOREPATH, store_path_var.get()));
  132. properties = temp_properties;
  133. return S_OK;
  134. }
  135. } // namespace
  136. HRESULT CreateMediaFoundationCdm(
  137. ComPtr<IMFContentDecryptionModuleFactory> cdm_factory,
  138. const CdmConfig& cdm_config,
  139. const base::UnguessableToken& cdm_origin_id,
  140. const absl::optional<std::vector<uint8_t>>& cdm_client_token,
  141. const base::FilePath& cdm_store_path_root,
  142. ComPtr<IMFContentDecryptionModule>& mf_cdm) {
  143. DVLOG(1) << __func__ << ": cdm_config=" << cdm_config
  144. << ", cdm_origin_id=" << cdm_origin_id.ToString()
  145. << ", cdm_store_path_root=" << cdm_store_path_root;
  146. DCHECK(!cdm_origin_id.is_empty());
  147. const auto key_system = cdm_config.key_system;
  148. auto key_system_str = base::UTF8ToWide(key_system);
  149. if (!cdm_factory->IsTypeSupported(key_system_str.c_str(), nullptr)) {
  150. DLOG(ERROR) << key_system << " not supported by MF CdmFactory";
  151. return MF_NOT_SUPPORTED_ERR;
  152. }
  153. ComPtr<IPropertyStore> property_store;
  154. RETURN_IF_FAILED(BuildCdmAccessConfigurations(cdm_config, property_store));
  155. IPropertyStore* configurations[] = {property_store.Get()};
  156. ComPtr<IMFContentDecryptionModuleAccess> cdm_access;
  157. RETURN_IF_FAILED(cdm_factory->CreateContentDecryptionModuleAccess(
  158. key_system_str.c_str(), configurations, ARRAYSIZE(configurations),
  159. &cdm_access));
  160. // Provide a per-user, per-arch, per-origin and per-key-system path.
  161. auto store_path =
  162. GetCdmStorePath(cdm_store_path_root, cdm_origin_id, key_system);
  163. DVLOG(1) << "store_path=" << store_path;
  164. // Ensure the path exists. If it already exists, this call will do nothing.
  165. base::File::Error file_error;
  166. if (!base::CreateDirectoryAndGetError(store_path, &file_error)) {
  167. DLOG(ERROR) << "Create CDM store path failed with " << file_error;
  168. return MF_INVALID_ACCESS_ERR;
  169. }
  170. ComPtr<IPropertyStore> cdm_properties;
  171. ComPtr<IMFContentDecryptionModule> cdm;
  172. RETURN_IF_FAILED(BuildCdmProperties(cdm_origin_id, cdm_client_token,
  173. store_path, cdm_properties));
  174. RETURN_IF_FAILED(
  175. cdm_access->CreateContentDecryptionModule(cdm_properties.Get(), &cdm));
  176. mf_cdm.Swap(cdm);
  177. return S_OK;
  178. }
  179. } // namespace media