【chromium cdm 模块源码分析】

widevine 作为动态库被 chromium 动态加载,比如 linux 使用 dlopen。

CdmModule 初始化

// chromium\src\media\cdm\cdm_module.cc
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.cc
ScopedNativeLibrary::ScopedNativeLibrary(const FilePath& library_path)
    : ScopedNativeLibrary() {
  reset(LoadNativeLibrary(library_path, &error_));
}

// chromium\src\base\native_library.cc
NativeLibrary LoadNativeLibrary(const FilePath& library_path,
                                NativeLibraryLoadError* error) {
  return LoadNativeLibraryWithOptions(
      library_path, NativeLibraryOptions(), error);
}
LoadNativeLibraryWithOptions() 以 linux 平台为例进行分析,其实是对 dlopen 进行封装。
// chromium\src\base\native_library_posix.cc
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.cc
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.cc
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.cc
// 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 库进行交互。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值