Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/common/media/cdm_host_files.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <memory> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/files/file.h" | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/lazy_instance.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/native_library.h" | |
| 17 #include "base/path_service.h" | |
| 18 #include "content/common/media/cdm_host_file.h" | |
| 19 #include "media/cdm/cdm_paths.h" | |
| 20 // TODO(xhwang): This should be replaced with "media/cdm/api/..." | |
| 21 #include "media/cdm/content_decryption_module_ext.h" | |
| 22 | |
| 23 #if defined(POSIX_WITH_ZYGOTE) | |
| 24 #include "content/common/pepper_plugin_list.h" | |
| 25 #include "content/public/common/pepper_plugin_info.h" | |
| 26 #endif | |
| 27 | |
| 28 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. | |
| 29 | |
| 30 namespace content { | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 // Returns the CDM library file name given the |cdm_adapter_file_name|. Returns | |
| 35 // nullptr if |cdm_adapter_file_name| does not correspond to a known CDM. | |
| 36 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
| |
| 37 #if defined(WIDEVINE_CDM_AVAILABLE) | |
| 38 if (cdm_adapter_file_name == | |
| 39 base::FilePath::FromUTF8Unsafe(kWidevineCdmAdapterFileName)) | |
| 40 return kWidevineCdmLibraryName; | |
| 41 #endif | |
| 42 | |
| 43 // Clear Key CDM. For test only. | |
| 44 if (cdm_adapter_file_name == | |
| 45 base::FilePath::FromUTF8Unsafe(media::kClearKeyCdmAdapterFileName)) | |
| 46 return media::kClearKeyCdmLibraryName; | |
| 47 | |
| 48 return nullptr; | |
| 49 } | |
| 50 | |
| 51 // Returns the path to the CDM binary given the |cdm_adapter_path|. Returns an | |
| 52 // empty path if |cdm_adapter_path| does not correspond to a known CDM. | |
| 53 base::FilePath GetCdmPath(const base::FilePath& cdm_adapter_path) { | |
| 54 const char* cdm_file_name = GetCdmFileName(cdm_adapter_path.BaseName()); | |
| 55 if (!cdm_file_name) | |
| 56 return base::FilePath(); | |
| 57 | |
| 58 return cdm_adapter_path.DirName().AppendASCII( | |
| 59 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
| |
| 60 } | |
| 61 | |
| 62 // From the list of registered plugins, finds all registered CDMs and fills | |
| 63 // |cdm_adapter_paths| with found CDM adapters paths. | |
| 64 void GetRegisteredCdms(std::vector<base::FilePath>* cdm_adapter_paths) { | |
| 65 std::vector<PepperPluginInfo> plugins; | |
| 66 ComputePepperPluginList(&plugins); | |
| 67 for (const auto& plugin : plugins) { | |
| 68 // CDM is not an internal plugin. | |
| 69 if (plugin.is_internal) | |
| 70 continue; | |
| 71 | |
| 72 if (IsCdm(plugin.path)) | |
| 73 cdm_adapter_paths->push_back(plugin.path); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 #if defined(POSIX_WITH_ZYGOTE) | |
| 78 // A global instance used on Linux where we have to open the files in the | |
| 79 // Zygote process. | |
| 80 // TODO(xhwang): Make this Linux only. | |
| 81 base::LazyInstance<std::unique_ptr<CdmHostFiles>> g_cdm_host_files = | |
| 82 LAZY_INSTANCE_INITIALIZER; | |
| 83 #endif | |
| 84 | |
| 85 } // namespace | |
| 86 | |
| 87 CdmHostFiles::CdmHostFiles() { | |
| 88 DVLOG(1) << __func__; | |
| 89 } | |
| 90 | |
| 91 CdmHostFiles::~CdmHostFiles() { | |
| 92 DVLOG(1) << __func__; | |
| 93 } | |
| 94 | |
| 95 #if defined(POSIX_WITH_ZYGOTE) | |
| 96 // static | |
| 97 void CdmHostFiles::CreateGlobalInstance() { | |
| 98 DVLOG(1) << __func__; | |
| 99 DCHECK(!g_cdm_host_files.Get().get()); | |
| 100 | |
| 101 std::unique_ptr<CdmHostFiles> cdm_host_files = | |
| 102 base::MakeUnique<CdmHostFiles>(); | |
| 103 if (!cdm_host_files->OpenFilesForAllRegisteredCdms()) { | |
| 104 DVLOG(1) << __func__ << " failed."; | |
| 105 cdm_host_files.reset(); | |
| 106 return; | |
| 107 } | |
| 108 | |
| 109 g_cdm_host_files.Get().reset(cdm_host_files.release()); | |
| 110 } | |
| 111 | |
| 112 // static | |
| 113 std::unique_ptr<CdmHostFiles> CdmHostFiles::TakeGlobalInstance() { | |
| 114 return std::move(g_cdm_host_files.Get()); | |
| 115 } | |
| 116 #endif | |
| 117 | |
| 118 // static | |
| 119 std::unique_ptr<CdmHostFiles> CdmHostFiles::Create( | |
| 120 const base::FilePath& cdm_adapter_path) { | |
| 121 DVLOG(1) << __func__; | |
| 122 std::unique_ptr<CdmHostFiles> cdm_host_files = | |
| 123 base::MakeUnique<CdmHostFiles>(); | |
| 124 if (!cdm_host_files->OpenFiles(cdm_adapter_path)) { | |
| 125 cdm_host_files.reset(); | |
| 126 return nullptr; | |
| 127 } | |
| 128 | |
| 129 return cdm_host_files; | |
| 130 } | |
| 131 | |
| 132 void CdmHostFiles::VerifyFiles(base::NativeLibrary library, | |
| 133 const base::FilePath& cdm_adapter_path) { | |
| 134 DCHECK(library); | |
| 135 | |
| 136 // Get VerifyHost() function pointer exported by the CDM. | |
| 137 using VerifyHostFilesFunc = | |
| 138 bool (*)(const cdm::CdmHostFile* cdm_host_files, uint32_t num_files); | |
| 139 static const char kVerifyHostFilesFuncName[] = "VerifyHostFiles"; | |
| 140 | |
| 141 // On POSIX, the |library| is the adapter. This works because "the dlsym() | |
| 142 // function shall search for the named symbol in all objects loaded | |
| 143 // automatically as a result of loading the object referenced by handle". | |
| 144 // In this case, the CDM is loaded automatically as a result of loading the | |
| 145 // CDM adapter. | |
| 146 // TODO(xhwang): This may not work on Windows. If so, the caller should load | |
| 147 // the CDM explicitly and pass the CDM |library| in this call. | |
| 148 VerifyHostFilesFunc verify_host_files_func = | |
| 149 reinterpret_cast<VerifyHostFilesFunc>( | |
| 150 base::GetFunctionPointerFromNativeLibrary(library, | |
| 151 kVerifyHostFilesFuncName)); | |
| 152 if (!verify_host_files_func) { | |
| 153 LOG(ERROR) << "Function " << kVerifyHostFilesFuncName << " not found."; | |
| 154 CloseAllFiles(); | |
| 155 return; | |
| 156 } | |
| 157 | |
| 158 std::vector<cdm::CdmHostFile> cdm_host_files; | |
| 159 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.
| |
| 160 verify_host_files_func(cdm_host_files.data(), cdm_host_files.size()); | |
| 161 | |
| 162 // Close all files not passed to the CDM. | |
| 163 CloseAllFiles(); | |
| 164 } | |
| 165 | |
| 166 #if defined(POSIX_WITH_ZYGOTE) | |
| 167 bool CdmHostFiles::OpenFilesForAllRegisteredCdms() { | |
| 168 std::vector<base::FilePath> cdm_adapter_paths; | |
| 169 GetRegisteredCdms(&cdm_adapter_paths); | |
| 170 if (cdm_adapter_paths.empty()) { | |
| 171 DVLOG(1) << "No CDM registered."; | |
| 172 return false; | |
| 173 } | |
| 174 | |
| 175 for (auto& cdm_adapter_path : cdm_adapter_paths) | |
| 176 ignore_result(OpenCdmFiles(cdm_adapter_path)); | |
| 177 | |
| 178 if (cdm_specific_files_map_.empty()) { | |
| 179 DVLOG(1) << "CDM specific files cannot be opened for any registered CDM."; | |
| 180 return false; | |
| 181 } | |
| 182 | |
| 183 return OpenCommonFiles(); | |
| 184 } | |
| 185 #endif | |
| 186 | |
| 187 bool CdmHostFiles::OpenFiles(const base::FilePath& cdm_adapter_path) { | |
| 188 if (!OpenCdmFiles(cdm_adapter_path)) | |
| 189 return false; | |
| 190 | |
| 191 return OpenCommonFiles(); | |
| 192 } | |
| 193 | |
| 194 bool CdmHostFiles::OpenCommonFiles() { | |
| 195 DCHECK(common_files_.empty()); | |
| 196 | |
| 197 base::FilePath file_exe_path; | |
| 198 base::PathService::Get(base::FILE_EXE, &file_exe_path); | |
| 199 std::unique_ptr<CdmHostFile> file_exe = CdmHostFile::Create(file_exe_path); | |
| 200 if (!file_exe) { | |
| 201 CloseAllFiles(); | |
| 202 return false; | |
| 203 } | |
| 204 | |
| 205 // TODO(xhwang): Open other related files. | |
| 206 | |
| 207 common_files_.push_back(std::move(file_exe)); | |
| 208 return true; | |
| 209 } | |
| 210 | |
| 211 bool CdmHostFiles::OpenCdmFiles(const base::FilePath& cdm_adapter_path) { | |
| 212 DCHECK(!cdm_adapter_path.empty()); | |
| 213 DCHECK(!cdm_specific_files_map_.count(cdm_adapter_path)); | |
| 214 | |
| 215 std::unique_ptr<CdmHostFile> cdm_adapter_file = | |
| 216 CdmHostFile::Create(cdm_adapter_path); | |
| 217 if (!cdm_adapter_file) | |
| 218 return false; | |
| 219 | |
| 220 std::unique_ptr<CdmHostFile> cdm_file = | |
| 221 CdmHostFile::Create(GetCdmPath(cdm_adapter_path)); | |
| 222 if (!cdm_file) | |
| 223 return false; | |
| 224 | |
| 225 ScopedFileVector cdm_specific_files; | |
| 226 cdm_specific_files.push_back(std::move(cdm_adapter_file)); | |
| 227 cdm_specific_files.push_back(std::move(cdm_file)); | |
| 228 | |
| 229 cdm_specific_files_map_[cdm_adapter_path] = std::move(cdm_specific_files); | |
| 230 return true; | |
| 231 } | |
| 232 | |
| 233 bool CdmHostFiles::TakePlatformFiles( | |
| 234 const base::FilePath& cdm_adapter_path, | |
| 235 std::vector<cdm::CdmHostFile>* cdm_host_files) { | |
| 236 DCHECK(cdm_host_files->empty()); | |
| 237 | |
| 238 DCHECK(!common_files_.empty()); | |
| 239 | |
| 240 // Check whether CDM specific files exist. | |
| 241 const auto& iter = cdm_specific_files_map_.find(cdm_adapter_path); | |
| 242 if (iter == cdm_specific_files_map_.end()) { | |
| 243 // This could happen on Linux where CDM files fail to open for Foo CDM, but | |
| 244 // now we hit Bar CDM. | |
| 245 LOG(ERROR) << "No CDM specific files for " << cdm_adapter_path.value(); | |
| 246 CloseAllFiles(); | |
| 247 return false; | |
| 248 } | |
| 249 | |
| 250 const ScopedFileVector& cdm_specific_files = iter->second; | |
| 251 | |
| 252 // Populate an array of cdm::CdmHostFile. | |
| 253 for (const auto& file : common_files_) | |
| 254 cdm_host_files->push_back(file->TakePlatformFile()); | |
| 255 | |
| 256 for (const auto& file : cdm_specific_files) | |
| 257 cdm_host_files->push_back(file->TakePlatformFile()); | |
| 258 | |
| 259 return true; | |
| 260 } | |
| 261 | |
| 262 void CdmHostFiles::CloseAllFiles() { | |
| 263 common_files_.clear(); | |
| 264 cdm_specific_files_map_.clear(); | |
| 265 } | |
| 266 | |
| 267 // Question(xhwang): Any better way to check whether a plugin is a CDM? Maybe | |
| 268 // when we register the plugin we can set some flag explicitly? | |
| 269 bool IsCdm(const base::FilePath& cdm_adapter_path) { | |
| 270 return !GetCdmPath(cdm_adapter_path).empty(); | |
| 271 } | |
| 272 | |
| 273 } // namespace content | |
| OLD | NEW |