blob: b633095a045c8b85570f544068b29f8ff34db013 [file] [log] [blame]
// Copyright 2021 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_module.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_hstring.h"
#include "media/base/win/mf_helpers.h"
namespace media {
namespace {
using Microsoft::WRL::ComPtr;
static MediaFoundationCdmModule* g_cdm_module = nullptr;
} // namespace
// static
MediaFoundationCdmModule* MediaFoundationCdmModule::GetInstance() {
if (!g_cdm_module)
g_cdm_module = new MediaFoundationCdmModule();
return g_cdm_module;
}
MediaFoundationCdmModule::MediaFoundationCdmModule() = default;
MediaFoundationCdmModule::~MediaFoundationCdmModule() = default;
void MediaFoundationCdmModule::Initialize(const base::FilePath& cdm_path) {
DVLOG(1) << __func__ << ": cdm_path=" << cdm_path.value();
CHECK(!initialized_)
<< "MediaFoundationCdmModule can only be initialized once!";
initialized_ = true;
cdm_path_ = cdm_path;
// If `cdm_path_` is not empty, load the CDM before the sandbox is sealed.
if (!cdm_path_.empty()) {
library_ = base::ScopedNativeLibrary(cdm_path_);
LOG_IF(ERROR, !library_.is_valid())
<< __func__ << ": Failed to load CDM at " << cdm_path_.value()
<< " (Error: " << library_.GetError()->ToString() << ")";
}
}
HRESULT MediaFoundationCdmModule::GetCdmFactory(
const std::string& key_system,
Microsoft::WRL::ComPtr<IMFContentDecryptionModuleFactory>& cdm_factory) {
if (!initialized_) {
DLOG(ERROR) << __func__ << " failed: Not initialized";
return E_NOT_VALID_STATE;
}
if (key_system.empty()) {
DLOG(ERROR) << __func__ << " failed: Empty key system";
return ERROR_INVALID_PARAMETER;
}
if (key_system_.empty())
key_system_ = key_system;
if (key_system != key_system_) {
DLOG(ERROR) << __func__ << " failed: key system mismatch";
return E_NOT_VALID_STATE;
}
if (!cdm_factory_)
RETURN_IF_FAILED(ActivateCdmFactory());
cdm_factory = cdm_factory_;
return S_OK;
}
HRESULT MediaFoundationCdmModule::ActivateCdmFactory() {
DCHECK(initialized_);
// For OS or store CDM, the `cdm_path_` is empty. Just use default creation.
if (cdm_path_.empty()) {
DCHECK(!library_.is_valid());
ComPtr<IMFMediaEngineClassFactory4> class_factory;
RETURN_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&class_factory)));
auto key_system_str = base::UTF8ToWide(key_system_);
RETURN_IF_FAILED(class_factory->CreateContentDecryptionModuleFactory(
key_system_str.c_str(), IID_PPV_ARGS(&cdm_factory_)));
return S_OK;
}
if (!library_.is_valid()) {
LOG(ERROR) << "CDM failed to load previously";
return E_FAIL;
}
// Initialization required to call base::win::ScopedHString::Create();
if (!base::win::ScopedHString::ResolveCoreWinRTStringDelayload())
return E_FAIL;
// Get function pointer to the activation factory.
using GetActivationFactoryFunc =
HRESULT(WINAPI*)(_In_ HSTRING activatible_class_id,
_COM_Outptr_ IActivationFactory * *factory);
const char kDllGetActivationFactory[] = "DllGetActivationFactory";
auto get_activation_factory_func = reinterpret_cast<GetActivationFactoryFunc>(
library_.GetFunctionPointer(kDllGetActivationFactory));
if (!get_activation_factory_func) {
LOG(ERROR) << "Cannot get function " << kDllGetActivationFactory;
return E_FAIL;
}
// Activate CdmFactory. Assuming the class ID is always in the format
// "<key_system>.ContentDecryptionModuleFactory".
auto class_name = base::win::ScopedHString::Create(
base::StringPiece(key_system_ + ".ContentDecryptionModuleFactory"));
ComPtr<IActivationFactory> activation_factory;
RETURN_IF_FAILED(
get_activation_factory_func(class_name.get(), &activation_factory));
ComPtr<IInspectable> inspectable_factory;
RETURN_IF_FAILED(activation_factory->ActivateInstance(&inspectable_factory));
RETURN_IF_FAILED(inspectable_factory.As(&cdm_factory_));
return S_OK;
}
} // namespace media