#include "media/cdm/cdm_module.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/not_fatal_until.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
#include "load_cdm_uma_helper.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
#include "base/feature_list.h"
#include "media/base/media_switches.h"
#include "media/cdm/cdm_host_files.h"
#endif
#define STRINGIFY(X) #X
#define MAKE_STRING(X) STRINGIFY(X)
namespace media {
namespace {
static CdmModule* g_cdm_module = nullptr;
const char kUmaPrefix[] = "Media.EME.Cdm";
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
void InitCdmHostVerification(
base::NativeLibrary cdm_library,
const base::FilePath& cdm_path,
const std::vector<CdmHostFilePath>& cdm_host_file_paths) {
CHECK(cdm_library);
CdmHostFiles cdm_host_files;
cdm_host_files.Initialize(cdm_path, cdm_host_file_paths);
auto status = cdm_host_files.InitVerification(cdm_library);
UMA_HISTOGRAM_ENUMERATION("Media.EME.CdmHostVerificationStatus", status,
CdmHostFiles::Status::kStatusCount);
}
#endif
}
CdmModule* CdmModule::GetInstance() {
if (!g_cdm_module)
g_cdm_module = new CdmModule();
return g_cdm_module;
}
void CdmModule::ResetInstanceForTesting() {
if (!g_cdm_module)
return;
delete g_cdm_module;
g_cdm_module = nullptr;
}
CdmModule::CdmModule() = default;
CdmModule::~CdmModule() {
if (deinitialize_cdm_module_func_)
deinitialize_cdm_module_func_();
}
CdmModule::CreateCdmFunc CdmModule::GetCreateCdmFunc() {
if (!initialized_) {
DLOG(ERROR) << __func__ << " called before CdmModule is initialized.";
return nullptr;
}
return create_cdm_func_;
}
void CdmModule::SetDebuggerAttached(bool is_debugger_attached) {
is_debugger_attached_ = is_debugger_attached;
}
bool CdmModule::GetDebuggerAttached() const {
return is_debugger_attached_;
}
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
bool CdmModule::Initialize(const base::FilePath& cdm_path,
std::vector<CdmHostFilePath> cdm_host_file_paths) {
#else
bool CdmModule::Initialize(const base::FilePath& cdm_path) {
#endif
DVLOG(1) << __func__ << ": cdm_path = " << cdm_path.value();
CHECK(!initialized_) << "CdmModule can only be initialized once!";
initialized_ = true;
cdm_path_ = cdm_path;
base::TimeTicks start = base::TimeTicks::Now();
library_ = base::ScopedNativeLibrary(cdm_path);
base::TimeDelta load_time = base::TimeTicks::Now() - start;
if (!library_.is_valid()) {
LOG(ERROR) << "CDM at " << cdm_path.value() << " could not be loaded.";
LOG(ERROR) << "Error: " << library_.GetError()->ToString();
ReportLoadResult(kUmaPrefix, base::PathExists(cdm_path)
? CdmLoadResult::kLoadFailed
: CdmLoadResult::kFileMissing);
ReportLoadErrorCode(kUmaPrefix, library_.GetError());
return false;
}
ReportLoadTime(kUmaPrefix, load_time);
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"));
if (!initialize_cdm_module_func_ || !deinitialize_cdm_module_func_ ||
!create_cdm_func_ || !get_cdm_version_func_) {
LOG(ERROR) << "Missing entry function in CDM at " << cdm_path.value();
initialize_cdm_module_func_ = nullptr;
deinitialize_cdm_module_func_ = nullptr;
create_cdm_func_ = nullptr;
get_cdm_version_func_ = nullptr;
library_.reset();
ReportLoadResult(kUmaPrefix, CdmLoadResult::kEntryPointMissing);
return false;
}
std::string cdm_version = get_cdm_version_func_();
DVLOG(2) << __func__ << ": cdm_version = " << cdm_version;
TRACE_EVENT1("media", "CdmModule::Initialize", "cdm_version", cdm_version);
static crash_reporter::CrashKeyString<32> cdm_version_key("cdm-version");
cdm_version_key.Set(cdm_version);
#if BUILDFLAG(IS_WIN)
::LoadLibraryA("dxva2.dll");
#endif
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
if (base::FeatureList::IsEnabled(media::kCdmHostVerification))
InitCdmHostVerification(library_.get(), cdm_path_, cdm_host_file_paths);
#endif
ReportLoadResult(kUmaPrefix, CdmLoadResult::kLoadSuccess);
return true;
}
void CdmModule::InitializeCdmModule() {
CHECK(initialized_);
CHECK(initialize_cdm_module_func_);
TRACE_EVENT0("media", "CdmModule::InitializeCdmModule");
initialize_cdm_module_func_();
}
}