widevine 作为动态库被 chromium 动态加载,比如 linux 使用 dlopen。admin管理员组文章数量:1622631
CdmModule 初始化
// chromium\src\media\cdm\cdm_module
bool CdmModule::Initialize(const base::FilePath& cdm_path) {
...
// Load the CDM.
library_ = base::ScopedNativeLibrary(cdm_path);
...
// Get function pointers.
initialize_cdm_module_func_ = reinterpret_cast<InitializeCdmModuleFunc>(
library_.GetFunctionPointer(MAKE_STRING(INITIALIZE_CDM_MODULE)));
deinitialize_cdm_module_func_ = reinterpret_cast<DeinitializeCdmModuleFunc>(
library_.GetFunctionPointer("DeinitializeCdmModule"));
create_cdm_func_ = reinterpret_cast<CreateCdmFunc>(
library_.GetFunctionPointer("CreateCdmInstance"));
get_cdm_version_func_ = reinterpret_cast<GetCdmVersionFunc>(
library_.GetFunctionPointer("GetCdmVersion"));
...
return true;
}
CdmModule::CreateCdmFunc CdmModule::GetCreateCdmFunc() {
if (!initialized_) {
DLOG(ERROR) << __func__ << " called before CdmModule is initialized.";
return nullptr;
}
// If initialization failed, nullptr will be returned.
return create_cdm_func_;
}
// static
CdmModule* CdmModule::GetInstance() {
...
if (!g_cdm_module)
g_cdm_module = new CdmModule();
return g_cdm_module;
}
CdmModule 的 create_cdm_func 成员,类型是什么呢,其实它是函数指针,并且在 cdm_module.h 中被简单地指定为 void*。
// chromium\src\media\cdm\api\content_decryption_module.h
CDM_API void* CreateCdmInstance(int cdm_interface_version,
const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
void* user_data);
// chromium\src\media\cdm\cdm_module.h
...
#include "media/cdm/api/content_decryption_module.h"
...
class MEDIA_EXPORT CdmModule {
public:
...
using CreateCdmFunc = decltype(&::CreateCdmInstance);
// chromium\src\base\scoped_native_library
ScopedNativeLibrary::ScopedNativeLibrary(const FilePath& library_path)
: ScopedNativeLibrary() {
reset(LoadNativeLibrary(library_path, &error_));
}
// chromium\src\base\native_library
NativeLibrary LoadNativeLibrary(const FilePath& library_path,
NativeLibraryLoadError* error) {
return LoadNativeLibraryWithOptions(
library_path, NativeLibraryOptions(), error);
}
LoadNativeLibraryWithOptions() 以 linux 平台为例进行分析,其实是对 dlopen 进行封装。
// chromium\src\base\native_library_posix
NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
const NativeLibraryOptions& options,
NativeLibraryLoadError* error) {
// dlopen() opens the file off disk.
...
void* dl = dlopen(library_path.value().c_str(), flags);
if (!dl && error)
error->message = dlerror();
return dl;
}
void UnloadNativeLibrary(NativeLibrary library) {
int ret = dlclose(library);
if (ret < 0) {
DLOG(ERROR) << "dlclose failed: " << dlerror();
NOTREACHED();
}
}
void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
StringPiece name) {
return dlsym(library, name.data());
}
到这里,我们知道 CdmModule::GetCreateCdmFunc() 返回的是从 widevine 中加载出来的 CreateCdmInstance 函数指针,并且它被简单地指定为 void* 类型。
谁通过 widevine 动态库的 CreateCdmInstance 获取 cdm 实例
CdmAdapter,它借助 CdmWrapper 来生成 CdmWrapper 自己。这个设计模式,估计是因为考虑到以后有多种不同的 CdmWrapper,所以让它们由 CdmAdapter 进行统一管理,通过 CdmAdapter::CreateCdmInstance() 来生成 CdmWrapper,并且 CdmAdapter 将自己的 create_cdm_func_ 成员传递给了 CdmWrapper。// chromium\src\media\cdm\cdm_adapter
CdmWrapper* CdmAdapter::CreateCdmInstance(const std::string& key_system) {
...
CdmWrapper* cdm = CdmWrapper::Create(create_cdm_func_, key_system.data(),
key_system.size(), GetCdmHost, this);
...
return cdm;
}
// chromium\src\media\cdm\cdm_adapter.h
class MEDIA_EXPORT CdmAdapter final : public ContentDecryptionModule,
public CdmContext,
public Decryptor,
public cdm::Host_10,
public cdm::Host_11 {
public:
using CreateCdmFunc = void* (*)(int cdm_interface_version,
const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
void* user_data);
...
private:
CreateCdmFunc create_cdm_func_;
}
CdmWrapper::Create() 又调用了 CdmWrapperImpl<int>::Create() ,所以,最终是 CdmWrapperImpl<int>::Create() 调用了 create_cdm_func 来创建 cdm 实例,并且将它转换为 CdmInterface* 类型并赋值给成员 cdm_。
// chromium\src\media\cdm\cdm_wrapper.h
// static
CdmWrapper* CdmWrapper::Create(CreateCdmFunc create_cdm_func,
const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
void* user_data) {
...
CdmWrapper* cdm_wrapper = nullptr;
if (IsSupportedAndEnabledCdmInterfaceVersion(11)) {
cdm_wrapper =
CdmWrapperImpl<11>::Create(create_cdm_func, key_system, key_system_size,
get_cdm_host_func, user_data);
}
if (!cdm_wrapper && IsSupportedAndEnabledCdmInterfaceVersion(10)) {
cdm_wrapper =
CdmWrapperImpl<10>::Create(create_cdm_func, key_system, key_system_size,
get_cdm_host_func, user_data);
}
return cdm_wrapper;
}
template <int CdmInterfaceVersion>
class CdmWrapperImpl : public CdmWrapper {
public:
using CdmInterface =
typename CdmInterfaceTraits<CdmInterfaceVersion>::CdmInterface;
static_assert(CdmInterfaceVersion == CdmInterface::kVersion,
"CDM interface version mismatch.");
static CdmWrapper* Create(CreateCdmFunc create_cdm_func,
const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func,
void* user_data) {
void* cdm_instance =
create_cdm_func(CdmInterfaceVersion, key_system, key_system_size,
get_cdm_host_func, user_data);
if (!cdm_instance)
return nullptr;
return new CdmWrapperImpl<CdmInterfaceVersion>(
static_cast<CdmInterface*>(cdm_instance));
...
private:
CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm) { DCHECK(cdm_); }
CdmInterface* cdm_;
}
CdmInterface 是 ContentDecryptionModule_11 (或 10)的别名,所以 cdm_ 实现了 widevine 动态库对外提供的接口。
// chromium\src\media\cdm\supported_cdm_versions.h
template <>
struct CdmInterfaceTraits<11> {
using CdmInterface = cdm::ContentDecryptionModule_11;
static_assert(CdmInterface::kVersion == 11, "CDM interface version mismatch");
static_assert(IsSupportedCdmHostVersion(CdmInterface::Host::kVersion),
"Host not supported");
static_assert(
CdmInterface::kIsStable ||
!IsCdmInterfaceVersionEnabledByDefault(CdmInterface::kVersion),
"Experimental CDM interface should not be enabled by default");
};
// chromium\src\media\cdm\api\content_decryption_module.h
class CDM_CLASS_API ContentDecryptionModule_11 {
public:
static const int kVersion = 11;
static const bool kIsStable = false;
typedef Host_11 Host;
...
virtual void Initialize(bool allow_distinctive_identifier,
bool allow_persistent_state,
bool use_hw_secure_codecs) = 0;
...
virtual void GetStatusForPolicy(uint32_t promise_id,
const Policy& policy) = 0;
...
virtual void SetServerCertificate(uint32_t promise_id,
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) = 0;
...
};
到这里,存在的疑惑是 CdmAdapter 的 create_cdm_func_ 成员,是怎么被赋值的,它应该就是从 CdmModule::GetCreateCdmFunc() 得到的。
从 CdmAdapter::Create() 中可以猜出,这是 CdmAdapter 从外界获取 create_cdm_func 的唯一方法,因为它的 CdmAdapter::CdmAdapter() 被设置为 private 了,所以该构造函数不可能被外界调用。
// chromium\src\media\cdm\cdm_adapter.h
class MEDIA_EXPORT CdmAdapter final : public ContentDecryptionModule,
public CdmContext,
public Decryptor,
public cdm::Host_10,
public cdm::Host_11 {
public:
...
static void Create(
const std::string& key_system,
const CdmConfig& cdm_config,
CreateCdmFunc create_cdm_func,
std::unique_ptr<CdmAuxiliaryHelper> helper,
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);
所以我们搜索 CdmAdapter::Create() 被调用的地方,发现了 CdmAdapterFactory::Create()。
CdmAdapterFactory::Create() 调用了 CdmModule::GetInstance()->GetCreateCdmFunc() 来获取 create_cdm_func,并且随后调用了 CdmAdapter::Create() 并传入 create_cdm_func,所以 CdmAdapter 就获得了create_cdm_func,而从上面的分析中得知 CdmAdapter 又将 create_cdm_func 传递给 CdmWrapper。到这里,CdmWrapper 就获得了 widevine 动态库中的 CreateCdmInstance 函数指针,并且保存在自己的 create_cdm_func 成员中。
回顾:CdmModule::GetInstance()->GetCreateCdmFunc() 返回的是 widevine 动态库中的 CreateCdmInstance 函数指针。
// chromium\src\media\cdm\cdm_adapter_factory
void CdmAdapterFactory::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(1) << __func__ << ": key_system=" << key_system;
CdmAdapter::CreateCdmFunc create_cdm_func =
CdmModule::GetInstance()->GetCreateCdmFunc();
...
CdmAdapter::Create(key_system, cdm_config, create_cdm_func,
std::move(cdm_helper), session_message_cb,
session_closed_cb, session_keys_change_cb,
session_expiration_update_cb, std::move(cdm_created_cb));
}
到这里,先看下完整的调用链。
void CdmAdapterFactory::Create() -> CdmModule::GetInstance()->GetCreateCdmFunc(),获得了 create_cdm_func
-> void CdmAdapter::Create()
CdmWrapper* CdmAdapter::CreateCdmInstance() -> CdmWrapper::Create() -> CdmWrapperImpl<int>::Create()
看完后发现,CdmAdapterFactory::Create() 好像跟 CdmAdapter::CreateCdmInstance() 并没有什么关联性,因为 create_cdm_func_ 并不是 CdmAdapter 的静态成员,所以 CdmAdapter::Create() 执行结束后,CdmAdapterFactory::Create() 传给它的 create_cdm_func_ 去哪了?
重新细看 CdmAdapter::Create() 的实现,发现其中调用了 private 属性的 CdmAdapter::CdmAdapter(),并且是用 new,所以就有一个 CdmAdapter 实例存在堆上了,而它被谁维护着、什么时候释放,就不清楚了,这部分的源码先追到这里。
另外 CdmAdapter::Create() 中执行了 cdm->Initialize(),它又调用了 CdmAdapter::CreateCdmInstance(),所以上面两条调用链就衔接起来了。
// chromium\src\media\cdm\cdm_adapter
// static
void CdmAdapter::Create(
const std::string& key_system,
const CdmConfig& cdm_config,
CreateCdmFunc create_cdm_func,
std::unique_ptr<CdmAuxiliaryHelper> helper,
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) {
...
scoped_refptr<CdmAdapter> cdm =
/* private 类型的构造器 */
new CdmAdapter(key_system, cdm_config, create_cdm_func, std::move(helper),
session_message_cb, session_closed_cb,
session_keys_change_cb, session_expiration_update_cb);
// |cdm| ownership passed to the promise.
cdm->Initialize(
std::make_unique<CdmInitializedPromise>(std::move(cdm_created_cb), cdm));
}
void CdmAdapter::Initialize(std::unique_ptr<media::SimpleCdmPromise> promise) {
...
cdm_.reset(CreateCdmInstance(key_system_));
if (!cdm_) {
promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0,
"Unable to create CDM.");
return;
}
init_promise_id_ = cdm_promise_adapter_.SavePromise(std::move(promise));
// CdmWrapper::Initialize()
if (!cdm_->Initialize(cdm_config_.allow_distinctive_identifier,
cdm_config_.allow_persistent_state,
cdm_config_.use_hw_secure_codecs)) {
// OnInitialized() will not be called by the CDM, which is the case for
// CDM interfaces prior to CDM_10.
OnInitialized(true);
return;
}
// OnInitialized() will be called by the CDM.
}
// chromium\src\media\cdm\cdm_adapter.h
class MEDIA_EXPORT CdmAdapter final : public ContentDecryptionModule,
public CdmContext,
public Decryptor,
public cdm::Host_10,
public cdm::Host_11 {
...
private:
// Declare |cdm_| after other member variables to avoid the CDM accessing
// deleted objects (e.g. |helper_|) during destruction.
std::unique_ptr<CdmWrapper> cdm_;
...
};
到这里,发现最终是 CdmWrapperImpl<int> 通过调用 create_cdm_func 从 widevine 动态库中得到 cdm_(类型为 CdmInterface*)实例,并且 CdmWrapperImpl<int> 实例运行在 cdm 进程中,其他进程通过 IDL (详情参考 chromium 的 mojo -> mojom)与 cdm 进程中的 CdmWrapperImpl<int> 实例进行通信,而 CdmWrapperImpl<int> 实例又通过 cdm_ 成员与 widevine 库进行交互。版权声明:本文标题:【chromium cdm 模块源码分析】 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1728871672a1177340.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论