Index: content/common/media/cdm_host_files.cc |
diff --git a/content/common/media/cdm_host_files.cc b/content/common/media/cdm_host_files.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3b35ff3c4e1108c1f796fe9562813bea4b8bd98c |
--- /dev/null |
+++ b/content/common/media/cdm_host_files.cc |
@@ -0,0 +1,273 @@ |
+// Copyright 2016 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 "content/common/media/cdm_host_files.h" |
+ |
+#include <map> |
+#include <memory> |
+#include <vector> |
+ |
+#include "base/files/file.h" |
+#include "base/files/file_path.h" |
+#include "base/lazy_instance.h" |
+#include "base/logging.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/native_library.h" |
+#include "base/path_service.h" |
+#include "content/common/media/cdm_host_file.h" |
+#include "media/cdm/cdm_paths.h" |
+// TODO(xhwang): This should be replaced with "media/cdm/api/..." |
+#include "media/cdm/content_decryption_module_ext.h" |
+ |
+#if defined(POSIX_WITH_ZYGOTE) |
+#include "content/common/pepper_plugin_list.h" |
+#include "content/public/common/pepper_plugin_info.h" |
+#endif |
+ |
+#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+// Returns the CDM library file name given the |cdm_adapter_file_name|. Returns |
+// nullptr if |cdm_adapter_file_name| does not correspond to a known CDM. |
+const char* GetCdmFileName(const base::FilePath& cdm_adapter_file_name) { |
Greg K
2017/01/09 21:45:14
How is the CDM library file name different from th
xhwang
2017/01/12 20:15:02
For example:
Widevine CDM adapter file name: libwi
|
+#if defined(WIDEVINE_CDM_AVAILABLE) |
+ if (cdm_adapter_file_name == |
+ base::FilePath::FromUTF8Unsafe(kWidevineCdmAdapterFileName)) |
+ return kWidevineCdmLibraryName; |
+#endif |
+ |
+ // Clear Key CDM. For test only. |
+ if (cdm_adapter_file_name == |
+ base::FilePath::FromUTF8Unsafe(media::kClearKeyCdmAdapterFileName)) |
+ return media::kClearKeyCdmLibraryName; |
+ |
+ return nullptr; |
+} |
+ |
+// Returns the path to the CDM binary given the |cdm_adapter_path|. Returns an |
+// empty path if |cdm_adapter_path| does not correspond to a known CDM. |
+base::FilePath GetCdmPath(const base::FilePath& cdm_adapter_path) { |
+ const char* cdm_file_name = GetCdmFileName(cdm_adapter_path.BaseName()); |
+ if (!cdm_file_name) |
+ return base::FilePath(); |
+ |
+ return cdm_adapter_path.DirName().AppendASCII( |
+ base::GetNativeLibraryName(cdm_file_name)); |
jrummell
2016/12/16 20:21:30
Is this needed? kClearKeyCdmAdapterFileName return
xhwang
2017/01/12 20:15:02
GetCdmFileName() returns kClearKeyCdmLibraryName w
|
+} |
+ |
+// From the list of registered plugins, finds all registered CDMs and fills |
+// |cdm_adapter_paths| with found CDM adapters paths. |
+void GetRegisteredCdms(std::vector<base::FilePath>* cdm_adapter_paths) { |
+ std::vector<PepperPluginInfo> plugins; |
+ ComputePepperPluginList(&plugins); |
+ for (const auto& plugin : plugins) { |
+ // CDM is not an internal plugin. |
+ if (plugin.is_internal) |
+ continue; |
+ |
+ if (IsCdm(plugin.path)) |
+ cdm_adapter_paths->push_back(plugin.path); |
+ } |
+} |
+ |
+#if defined(POSIX_WITH_ZYGOTE) |
+// A global instance used on Linux where we have to open the files in the |
+// Zygote process. |
+// TODO(xhwang): Make this Linux only. |
+base::LazyInstance<std::unique_ptr<CdmHostFiles>> g_cdm_host_files = |
+ LAZY_INSTANCE_INITIALIZER; |
+#endif |
+ |
+} // namespace |
+ |
+CdmHostFiles::CdmHostFiles() { |
+ DVLOG(1) << __func__; |
+} |
+ |
+CdmHostFiles::~CdmHostFiles() { |
+ DVLOG(1) << __func__; |
+} |
+ |
+#if defined(POSIX_WITH_ZYGOTE) |
+// static |
+void CdmHostFiles::CreateGlobalInstance() { |
+ DVLOG(1) << __func__; |
+ DCHECK(!g_cdm_host_files.Get().get()); |
+ |
+ std::unique_ptr<CdmHostFiles> cdm_host_files = |
+ base::MakeUnique<CdmHostFiles>(); |
+ if (!cdm_host_files->OpenFilesForAllRegisteredCdms()) { |
+ DVLOG(1) << __func__ << " failed."; |
+ cdm_host_files.reset(); |
+ return; |
+ } |
+ |
+ g_cdm_host_files.Get().reset(cdm_host_files.release()); |
+} |
+ |
+// static |
+std::unique_ptr<CdmHostFiles> CdmHostFiles::TakeGlobalInstance() { |
+ return std::move(g_cdm_host_files.Get()); |
+} |
+#endif |
+ |
+// static |
+std::unique_ptr<CdmHostFiles> CdmHostFiles::Create( |
+ const base::FilePath& cdm_adapter_path) { |
+ DVLOG(1) << __func__; |
+ std::unique_ptr<CdmHostFiles> cdm_host_files = |
+ base::MakeUnique<CdmHostFiles>(); |
+ if (!cdm_host_files->OpenFiles(cdm_adapter_path)) { |
+ cdm_host_files.reset(); |
+ return nullptr; |
+ } |
+ |
+ return cdm_host_files; |
+} |
+ |
+void CdmHostFiles::VerifyFiles(base::NativeLibrary library, |
+ const base::FilePath& cdm_adapter_path) { |
+ DCHECK(library); |
+ |
+ // Get VerifyHost() function pointer exported by the CDM. |
+ using VerifyHostFilesFunc = |
+ bool (*)(const cdm::CdmHostFile* cdm_host_files, uint32_t num_files); |
+ static const char kVerifyHostFilesFuncName[] = "VerifyHostFiles"; |
+ |
+ // On POSIX, the |library| is the adapter. This works because "the dlsym() |
+ // function shall search for the named symbol in all objects loaded |
+ // automatically as a result of loading the object referenced by handle". |
+ // In this case, the CDM is loaded automatically as a result of loading the |
+ // CDM adapter. |
+ // TODO(xhwang): This may not work on Windows. If so, the caller should load |
+ // the CDM explicitly and pass the CDM |library| in this call. |
+ VerifyHostFilesFunc verify_host_files_func = |
+ reinterpret_cast<VerifyHostFilesFunc>( |
+ base::GetFunctionPointerFromNativeLibrary(library, |
+ kVerifyHostFilesFuncName)); |
+ if (!verify_host_files_func) { |
+ LOG(ERROR) << "Function " << kVerifyHostFilesFuncName << " not found."; |
+ CloseAllFiles(); |
+ return; |
+ } |
+ |
+ std::vector<cdm::CdmHostFile> cdm_host_files; |
+ if (TakePlatformFiles(cdm_adapter_path, &cdm_host_files)) |
Greg K
2017/01/09 21:45:14
I'm not sure what's going on here, so a comment mi
xhwang
2017/01/12 20:15:02
Will do.
xhwang
2017/01/18 06:03:58
Done.
|
+ verify_host_files_func(cdm_host_files.data(), cdm_host_files.size()); |
+ |
+ // Close all files not passed to the CDM. |
+ CloseAllFiles(); |
+} |
+ |
+#if defined(POSIX_WITH_ZYGOTE) |
+bool CdmHostFiles::OpenFilesForAllRegisteredCdms() { |
+ std::vector<base::FilePath> cdm_adapter_paths; |
+ GetRegisteredCdms(&cdm_adapter_paths); |
+ if (cdm_adapter_paths.empty()) { |
+ DVLOG(1) << "No CDM registered."; |
+ return false; |
+ } |
+ |
+ for (auto& cdm_adapter_path : cdm_adapter_paths) |
+ ignore_result(OpenCdmFiles(cdm_adapter_path)); |
+ |
+ if (cdm_specific_files_map_.empty()) { |
+ DVLOG(1) << "CDM specific files cannot be opened for any registered CDM."; |
+ return false; |
+ } |
+ |
+ return OpenCommonFiles(); |
+} |
+#endif |
+ |
+bool CdmHostFiles::OpenFiles(const base::FilePath& cdm_adapter_path) { |
+ if (!OpenCdmFiles(cdm_adapter_path)) |
+ return false; |
+ |
+ return OpenCommonFiles(); |
+} |
+ |
+bool CdmHostFiles::OpenCommonFiles() { |
+ DCHECK(common_files_.empty()); |
+ |
+ base::FilePath file_exe_path; |
+ base::PathService::Get(base::FILE_EXE, &file_exe_path); |
+ std::unique_ptr<CdmHostFile> file_exe = CdmHostFile::Create(file_exe_path); |
+ if (!file_exe) { |
+ CloseAllFiles(); |
+ return false; |
+ } |
+ |
+ // TODO(xhwang): Open other related files. |
+ |
+ common_files_.push_back(std::move(file_exe)); |
+ return true; |
+} |
+ |
+bool CdmHostFiles::OpenCdmFiles(const base::FilePath& cdm_adapter_path) { |
+ DCHECK(!cdm_adapter_path.empty()); |
+ DCHECK(!cdm_specific_files_map_.count(cdm_adapter_path)); |
+ |
+ std::unique_ptr<CdmHostFile> cdm_adapter_file = |
+ CdmHostFile::Create(cdm_adapter_path); |
+ if (!cdm_adapter_file) |
+ return false; |
+ |
+ std::unique_ptr<CdmHostFile> cdm_file = |
+ CdmHostFile::Create(GetCdmPath(cdm_adapter_path)); |
+ if (!cdm_file) |
+ return false; |
+ |
+ ScopedFileVector cdm_specific_files; |
+ cdm_specific_files.push_back(std::move(cdm_adapter_file)); |
+ cdm_specific_files.push_back(std::move(cdm_file)); |
+ |
+ cdm_specific_files_map_[cdm_adapter_path] = std::move(cdm_specific_files); |
+ return true; |
+} |
+ |
+bool CdmHostFiles::TakePlatformFiles( |
+ const base::FilePath& cdm_adapter_path, |
+ std::vector<cdm::CdmHostFile>* cdm_host_files) { |
+ DCHECK(cdm_host_files->empty()); |
+ |
+ DCHECK(!common_files_.empty()); |
+ |
+ // Check whether CDM specific files exist. |
+ const auto& iter = cdm_specific_files_map_.find(cdm_adapter_path); |
+ if (iter == cdm_specific_files_map_.end()) { |
+ // This could happen on Linux where CDM files fail to open for Foo CDM, but |
+ // now we hit Bar CDM. |
+ LOG(ERROR) << "No CDM specific files for " << cdm_adapter_path.value(); |
+ CloseAllFiles(); |
+ return false; |
+ } |
+ |
+ const ScopedFileVector& cdm_specific_files = iter->second; |
+ |
+ // Populate an array of cdm::CdmHostFile. |
+ for (const auto& file : common_files_) |
+ cdm_host_files->push_back(file->TakePlatformFile()); |
+ |
+ for (const auto& file : cdm_specific_files) |
+ cdm_host_files->push_back(file->TakePlatformFile()); |
+ |
+ return true; |
+} |
+ |
+void CdmHostFiles::CloseAllFiles() { |
+ common_files_.clear(); |
+ cdm_specific_files_map_.clear(); |
+} |
+ |
+// Question(xhwang): Any better way to check whether a plugin is a CDM? Maybe |
+// when we register the plugin we can set some flag explicitly? |
+bool IsCdm(const base::FilePath& cdm_adapter_path) { |
+ return !GetCdmPath(cdm_adapter_path).empty(); |
+} |
+ |
+} // namespace content |