Skip to content

Commit

Permalink
media: Add MediaFoundationCdmFactory
Browse files Browse the repository at this point in the history
MediaFoundationCdmFactory is a media::CdmFactory implementation that
can create MediaFoundationCdm to be used on Windows platform.

NOPRESUBMIT=true

Bug: 999747
Change-Id: I197bc876bab5006d915d8bc0c0ae43641200746d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2278589
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Reviewed-by: John Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#785875}
  • Loading branch information
xhwang-chromium authored and Commit Bot committed Jul 7, 2020
1 parent bdd02f4 commit 857388c
Show file tree
Hide file tree
Showing 6 changed files with 476 additions and 0 deletions.
6 changes: 6 additions & 0 deletions media/base/win/mf_mocks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

namespace media {

MockMFCdmFactory::MockMFCdmFactory() = default;
MockMFCdmFactory::~MockMFCdmFactory() = default;

MockMFCdmAccess::MockMFCdmAccess() = default;
MockMFCdmAccess::~MockMFCdmAccess() = default;

MockMFCdm::MockMFCdm() = default;
MockMFCdm::~MockMFCdm() = default;

Expand Down
34 changes: 34 additions & 0 deletions media/base/win/mf_mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,40 @@

namespace media {

class MockMFCdmFactory
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IMFContentDecryptionModuleFactory> {
public:
MockMFCdmFactory();
~MockMFCdmFactory() override;

// IMFContentDecryptionModuleFactory methods
MOCK_STDCALL_METHOD2(IsTypeSupported,
BOOL(LPCWSTR key_system, LPCWSTR content_type));
MOCK_STDCALL_METHOD4(CreateContentDecryptionModuleAccess,
HRESULT(LPCWSTR key_system,
IPropertyStore** configurations,
DWORD num_configurations,
IMFContentDecryptionModuleAccess** cdm_access));
};

class MockMFCdmAccess
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IMFContentDecryptionModuleAccess> {
public:
MockMFCdmAccess();
~MockMFCdmAccess() override;

// IMFContentDecryptionModuleAccess methods
MOCK_STDCALL_METHOD2(CreateContentDecryptionModule,
HRESULT(IPropertyStore* properties,
IMFContentDecryptionModule** cdm));
MOCK_STDCALL_METHOD1(GetConfiguration, HRESULT(IPropertyStore** config));
MOCK_STDCALL_METHOD1(GetKeySystem, HRESULT(LPWSTR* key_system));
};

class MockMFCdm
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
Expand Down
5 changes: 5 additions & 0 deletions media/cdm/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,14 @@ source_set("cdm") {
sources += [
"win/media_foundation_cdm.cc",
"win/media_foundation_cdm.h",
"win/media_foundation_cdm_factory.cc",
"win/media_foundation_cdm_factory.h",
"win/media_foundation_cdm_session.cc",
"win/media_foundation_cdm_session.h",
]

libs = [ "Propsys.lib" ]

deps += [ "//media/base/win:media_foundation_util" ]
}
}
Expand Down Expand Up @@ -179,6 +183,7 @@ source_set("unit_tests") {

if (is_win) {
sources += [
"win/media_foundation_cdm_factory_unittest.cc",
"win/media_foundation_cdm_session_unittest.cc",
"win/media_foundation_cdm_unittest.cc",
]
Expand Down
223 changes: 223 additions & 0 deletions media/cdm/win/media_foundation_cdm_factory.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cdm/win/media_foundation_cdm_factory.h"

#include <combaseapi.h>
#include <mferror.h>
#include <mfmediaengine.h>
#include <propvarutil.h>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_propvariant.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_config.h"
#include "media/base/win/mf_helpers.h"
#include "media/cdm/win/media_foundation_cdm.h"

namespace media {

namespace {

using Microsoft::WRL::ComPtr;

void SetBSTR(const wchar_t* str, PROPVARIANT* propvariant) {
propvariant->vt = VT_BSTR;
propvariant->bstrVal = SysAllocString(str);
}

// Returns a property store similar to EME MediaKeySystemMediaCapability.
HRESULT CreateVideoCapability(const CdmConfig& cdm_config,
ComPtr<IPropertyStore>& video_capability) {
ComPtr<IPropertyStore> temp_video_capability;
RETURN_IF_FAILED(
PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_video_capability)));

base::win::ScopedPropVariant robustness;
if (cdm_config.use_hw_secure_codecs) {
// TODO(xhwang): Provide a way to support other robustness strings.
SetBSTR(L"HW_SECURE_ALL", robustness.Receive());
RETURN_IF_FAILED(
temp_video_capability->SetValue(MF_EME_ROBUSTNESS, robustness.get()));
}
video_capability = temp_video_capability;
return S_OK;
}

// Returns a property store similar to EME MediaKeySystemConfigurations.
// What really matters here are video robustness, persistent state and
// distinctive identifier.
HRESULT BuildCdmAccessConfigurations(const CdmConfig& cdm_config,
ComPtr<IPropertyStore>& configurations) {
ComPtr<IPropertyStore> temp_configurations;

RETURN_IF_FAILED(
PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_configurations)));

// Add an empty audio capability.
base::win::ScopedPropVariant audio_capabilities;
PROPVARIANT* var_to_set = audio_capabilities.Receive();
var_to_set->vt = VT_VARIANT | VT_VECTOR;
var_to_set->capropvar.cElems = 0;
RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_AUDIOCAPABILITIES,
audio_capabilities.get()));

// Add a video capability so we can pass the correct robustness level.
ComPtr<IPropertyStore> video_capability;
RETURN_IF_FAILED(CreateVideoCapability(cdm_config, video_capability));

base::win::ScopedPropVariant video_config;
auto* video_config_ptr = video_config.Receive();
video_config_ptr->vt = VT_UNKNOWN;
video_config_ptr->punkVal = video_capability.Detach();

base::win::ScopedPropVariant video_capabilities;
var_to_set = video_capabilities.Receive();
var_to_set->vt = VT_VARIANT | VT_VECTOR;
var_to_set->capropvar.cElems = 1;
var_to_set->capropvar.pElems =
reinterpret_cast<PROPVARIANT*>(CoTaskMemAlloc(sizeof(PROPVARIANT)));
PropVariantCopy(var_to_set->capropvar.pElems, video_config.ptr());
RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_VIDEOCAPABILITIES,
video_capabilities.get()));

// Add persistent state.
DCHECK(cdm_config.allow_persistent_state);
base::win::ScopedPropVariant persisted_state;
RETURN_IF_FAILED(InitPropVariantFromUInt32(MF_MEDIAKEYS_REQUIREMENT_REQUIRED,
persisted_state.Receive()));
RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_PERSISTEDSTATE,
persisted_state.get()));

// Add distinctive identifier.
DCHECK(cdm_config.allow_distinctive_identifier);
base::win::ScopedPropVariant distinctive_identifier;
RETURN_IF_FAILED(InitPropVariantFromUInt32(MF_MEDIAKEYS_REQUIREMENT_REQUIRED,
distinctive_identifier.Receive()));
RETURN_IF_FAILED(temp_configurations->SetValue(MF_EME_DISTINCTIVEID,
distinctive_identifier.get()));

configurations = temp_configurations;
return S_OK;
}

HRESULT BuildCdmProperties(ComPtr<IPropertyStore>& properties) {
ComPtr<IPropertyStore> temp_properties;
RETURN_IF_FAILED(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&temp_properties)));

// TODO(xhwang): Provide per-user, per-profile and per-key-system path here.
base::FilePath temp_dir;
CHECK(base::GetTempDir(&temp_dir));
CHECK(base::DirectoryExists(temp_dir));

base::win::ScopedPropVariant propvar;
SetBSTR(temp_dir.value().c_str(), propvar.Receive());
// TODO(xhwang): Replace with MF_CONTENTDECRYPTIONMODULE_STOREPATH.
RETURN_IF_FAILED(
temp_properties->SetValue(MF_EME_CDM_STOREPATH, propvar.get()));

properties = temp_properties;
return S_OK;
}

} // namespace

MediaFoundationCdmFactory::MediaFoundationCdmFactory() = default;

MediaFoundationCdmFactory::~MediaFoundationCdmFactory() = default;

void MediaFoundationCdmFactory::SetCreateCdmFactoryCallback(
const std::string& key_system,
CreateCdmFactoryCB create_cdm_factory_cb) {
DCHECK(!create_cdm_factory_cbs_.count(key_system));
create_cdm_factory_cbs_[key_system] = std::move(create_cdm_factory_cb);
}

void MediaFoundationCdmFactory::Create(
const std::string& key_system,
const CdmConfig& cdm_config,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
CdmCreatedCB cdm_created_cb) {
DVLOG_FUNC(1) << "key_system=" << key_system;

// IMFContentDecryptionModule CDMs typically require persistent storage and
// distinctive identifier and this should be guaranteed by key system support
// code. Update this if there are new CDMs that doesn't require these.
DCHECK(cdm_config.allow_persistent_state);
DCHECK(cdm_config.allow_distinctive_identifier);

ComPtr<IMFContentDecryptionModule> mf_cdm;
if (FAILED(CreateCdmInternal(key_system, cdm_config, mf_cdm))) {
BindToCurrentLoop(std::move(cdm_created_cb))
.Run(nullptr, "Failed to create CDM");
return;
}

auto cdm = base::MakeRefCounted<MediaFoundationCdm>(
std::move(mf_cdm), session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb);
BindToCurrentLoop(std::move(cdm_created_cb)).Run(cdm, "");
}

HRESULT MediaFoundationCdmFactory::CreateMFCdmFactory(
const std::string& key_system,
Microsoft::WRL::ComPtr<IMFContentDecryptionModuleFactory>& cdm_factory) {
// Use key system specific `create_cdm_factory_cb` if there's one registered.
auto itr = create_cdm_factory_cbs_.find(key_system);
if (itr != create_cdm_factory_cbs_.end()) {
auto& create_cdm_factory_cb = itr->second;
if (!create_cdm_factory_cb)
return E_FAIL;

RETURN_IF_FAILED(create_cdm_factory_cb.Run(cdm_factory));
return S_OK;
}

// Otherwise, use the default creation.
ComPtr<IMFMediaEngineClassFactory4> class_factory;
RETURN_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&class_factory)));
auto key_system_str = base::UTF8ToUTF16(key_system);
RETURN_IF_FAILED(class_factory->CreateContentDecryptionModuleFactory(
key_system_str.c_str(), IID_PPV_ARGS(&cdm_factory)));
return S_OK;
}

HRESULT MediaFoundationCdmFactory::CreateCdmInternal(
const std::string& key_system,
const CdmConfig& cdm_config,
ComPtr<IMFContentDecryptionModule>& mf_cdm) {
ComPtr<IMFContentDecryptionModuleFactory> cdm_factory;
RETURN_IF_FAILED(CreateMFCdmFactory(key_system, cdm_factory));

auto key_system_str = base::UTF8ToUTF16(key_system);
if (!cdm_factory->IsTypeSupported(key_system_str.c_str(), nullptr)) {
DLOG(ERROR) << key_system << " not supported by MF CdmFactory";
return MF_NOT_SUPPORTED_ERR;
}

ComPtr<IPropertyStore> configurations;
ComPtr<IMFContentDecryptionModuleAccess> cdm_access;
RETURN_IF_FAILED(BuildCdmAccessConfigurations(cdm_config, configurations));
RETURN_IF_FAILED(cdm_factory->CreateContentDecryptionModuleAccess(
key_system_str.c_str(), configurations.GetAddressOf(), 1u, &cdm_access));

ComPtr<IPropertyStore> cdm_properties;
ComPtr<IMFContentDecryptionModule> cdm;
RETURN_IF_FAILED(BuildCdmProperties(cdm_properties));
RETURN_IF_FAILED(
cdm_access->CreateContentDecryptionModule(cdm_properties.Get(), &cdm));

mf_cdm.Swap(cdm);
return S_OK;
}

} // namespace media
64 changes: 64 additions & 0 deletions media/cdm/win/media_foundation_cdm_factory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_CDM_WIN_MEDIA_FOUNDATION_CDM_FACTORY_H_
#define MEDIA_CDM_WIN_MEDIA_FOUNDATION_CDM_FACTORY_H_

#include <mfcontentdecryptionmodule.h>
#include <wrl.h>

#include <map>
#include <string>

#include "base/callback.h"
#include "base/win/scoped_com_initializer.h"
#include "media/base/cdm_factory.h"
#include "media/base/media_export.h"

namespace media {

class MEDIA_EXPORT MediaFoundationCdmFactory : public CdmFactory {
public:
MediaFoundationCdmFactory();
MediaFoundationCdmFactory(const MediaFoundationCdmFactory&) = delete;
MediaFoundationCdmFactory& operator=(const MediaFoundationCdmFactory&) =
delete;
~MediaFoundationCdmFactory() final;

// Provides a way to customize IMFContentDecryptionModuleFactory creation to
// support different key systems and for testing.
using CreateCdmFactoryCB = base::RepeatingCallback<HRESULT(
Microsoft::WRL::ComPtr<IMFContentDecryptionModuleFactory>& factory)>;
void SetCreateCdmFactoryCallback(const std::string& key_system,
CreateCdmFactoryCB create_cdm_factory_cb);

// CdmFactory implementation.
void Create(const std::string& key_system,
const CdmConfig& cdm_config,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
CdmCreatedCB cdm_created_cb) final;

private:
HRESULT CreateMFCdmFactory(
const std::string& key_system,
Microsoft::WRL::ComPtr<IMFContentDecryptionModuleFactory>& cdm_factory);
HRESULT CreateCdmInternal(
const std::string& key_system,
const CdmConfig& cdm_config,
Microsoft::WRL::ComPtr<IMFContentDecryptionModule>& mf_cdm);

// IMFContentDecryptionModule implementations typically require MTA to run.
base::win::ScopedCOMInitializer com_initializer_{
base::win::ScopedCOMInitializer::kMTA};

// Key system to CreateCdmFactoryCB mapping.
std::map<std::string, CreateCdmFactoryCB> create_cdm_factory_cbs_;
};

} // namespace media

#endif // MEDIA_CDM_WIN_MEDIA_FOUNDATION_CDM_FACTORY_H_

0 comments on commit 857388c

Please sign in to comment.