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 |