/src/mozilla-central/dom/media/platforms/PDMFactory.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "PDMFactory.h" |
8 | | |
9 | | #ifdef XP_WIN |
10 | | #include "WMFDecoderModule.h" |
11 | | #include "mozilla/WindowsVersion.h" |
12 | | #endif |
13 | | #ifdef MOZ_FFVPX |
14 | | #include "FFVPXRuntimeLinker.h" |
15 | | #endif |
16 | | #ifdef MOZ_FFMPEG |
17 | | #include "FFmpegRuntimeLinker.h" |
18 | | #endif |
19 | | #ifdef MOZ_APPLEMEDIA |
20 | | #include "AppleDecoderModule.h" |
21 | | #endif |
22 | | #ifdef MOZ_WIDGET_ANDROID |
23 | | #include "AndroidDecoderModule.h" |
24 | | #endif |
25 | | #ifdef MOZ_OMX |
26 | | #include "OmxDecoderModule.h" |
27 | | #endif |
28 | | #include "GMPDecoderModule.h" |
29 | | |
30 | | #include "mozilla/CDMProxy.h" |
31 | | #include "mozilla/ClearOnShutdown.h" |
32 | | #include "mozilla/SharedThreadPool.h" |
33 | | #include "mozilla/StaticPtr.h" |
34 | | #include "mozilla/StaticPrefs.h" |
35 | | #include "mozilla/SyncRunnable.h" |
36 | | #include "mozilla/TaskQueue.h" |
37 | | |
38 | | #include "MediaInfo.h" |
39 | | #include "H264Converter.h" |
40 | | |
41 | | #include "AgnosticDecoderModule.h" |
42 | | #include "EMEDecoderModule.h" |
43 | | |
44 | | #include "DecoderDoctorDiagnostics.h" |
45 | | |
46 | | #include "MP4Decoder.h" |
47 | | #include "mozilla/dom/RemoteVideoDecoder.h" |
48 | | |
49 | | #include "H264.h" |
50 | | |
51 | | #include <functional> |
52 | | |
53 | | namespace mozilla { |
54 | | |
55 | | extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule(); |
56 | | extern already_AddRefed<PlatformDecoderModule> CreateNullDecoderModule(); |
57 | | |
58 | | class PDMFactoryImpl final |
59 | | { |
60 | | public: |
61 | | PDMFactoryImpl() |
62 | 0 | { |
63 | | #ifdef XP_WIN |
64 | | WMFDecoderModule::Init(); |
65 | | #endif |
66 | | #ifdef MOZ_APPLEMEDIA |
67 | | AppleDecoderModule::Init(); |
68 | | #endif |
69 | | #ifdef MOZ_OMX |
70 | | OmxDecoderModule::Init(); |
71 | | #endif |
72 | | #ifdef MOZ_FFVPX |
73 | 0 | FFVPXRuntimeLinker::Init(); |
74 | 0 | #endif |
75 | 0 | #ifdef MOZ_FFMPEG |
76 | 0 | FFmpegRuntimeLinker::Init(); |
77 | 0 | #endif |
78 | 0 | } |
79 | | }; |
80 | | |
81 | | StaticAutoPtr<PDMFactoryImpl> PDMFactory::sInstance; |
82 | | StaticMutex PDMFactory::sMonitor; |
83 | | |
84 | | class SupportChecker |
85 | | { |
86 | | public: |
87 | | enum class Reason : uint8_t |
88 | | { |
89 | | kSupported, |
90 | | kVideoFormatNotSupported, |
91 | | kAudioFormatNotSupported, |
92 | | kUnknown, |
93 | | }; |
94 | | |
95 | | struct CheckResult |
96 | | { |
97 | | explicit CheckResult(Reason aReason, |
98 | | MediaResult aResult = MediaResult(NS_OK)) |
99 | | : mReason(aReason), |
100 | | mMediaResult(std::move(aResult)) |
101 | 0 | { |
102 | 0 | } |
103 | | CheckResult(const CheckResult& aOther) = default; |
104 | | CheckResult(CheckResult&& aOther) = default; |
105 | | CheckResult& operator=(const CheckResult& aOther) = default; |
106 | | CheckResult& operator=(CheckResult&& aOther) = default; |
107 | | |
108 | | Reason mReason; |
109 | | MediaResult mMediaResult; |
110 | | }; |
111 | | |
112 | | template<class Func> |
113 | | void |
114 | | AddToCheckList(Func&& aChecker) |
115 | 0 | { |
116 | 0 | mCheckerList.AppendElement(std::forward<Func>(aChecker)); |
117 | 0 | } |
118 | | |
119 | | void |
120 | | AddMediaFormatChecker(const TrackInfo& aTrackConfig) |
121 | 0 | { |
122 | 0 | if (aTrackConfig.IsVideo()) { |
123 | 0 | auto mimeType = aTrackConfig.GetAsVideoInfo()->mMimeType; |
124 | 0 | RefPtr<MediaByteBuffer> extraData = |
125 | 0 | aTrackConfig.GetAsVideoInfo()->mExtraData; |
126 | 0 | AddToCheckList([mimeType, extraData]() { |
127 | 0 | if (MP4Decoder::IsH264(mimeType)) { |
128 | 0 | SPSData spsdata; |
129 | 0 | // WMF H.264 Video Decoder and Apple ATDecoder |
130 | 0 | // do not support YUV444 format. |
131 | 0 | // For consistency, all decoders should be checked. |
132 | 0 | if (H264::DecodeSPSFromExtraData(extraData, spsdata) && |
133 | 0 | (spsdata.profile_idc == 244 /* Hi444PP */ || |
134 | 0 | spsdata.chroma_format_idc == PDMFactory::kYUV444)) { |
135 | 0 | return CheckResult( |
136 | 0 | SupportChecker::Reason::kVideoFormatNotSupported, |
137 | 0 | MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, |
138 | 0 | RESULT_DETAIL("Decoder may not have the capability " |
139 | 0 | "to handle the requested video format " |
140 | 0 | "with YUV444 chroma subsampling."))); |
141 | 0 | } |
142 | 0 | } |
143 | 0 | return CheckResult(SupportChecker::Reason::kSupported); |
144 | 0 | }); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | SupportChecker::CheckResult |
149 | | Check() |
150 | 0 | { |
151 | 0 | for (auto& checker : mCheckerList) { |
152 | 0 | auto result = checker(); |
153 | 0 | if (result.mReason != SupportChecker::Reason::kSupported) { |
154 | 0 | return result; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | return CheckResult(SupportChecker::Reason::kSupported); |
158 | 0 | } |
159 | | |
160 | 0 | void Clear() { mCheckerList.Clear(); } |
161 | | |
162 | | private: |
163 | | nsTArray<std::function<CheckResult()>> mCheckerList; |
164 | | }; // SupportChecker |
165 | | |
166 | | PDMFactory::PDMFactory() |
167 | 0 | { |
168 | 0 | EnsureInit(); |
169 | 0 | CreatePDMs(); |
170 | 0 | CreateNullPDM(); |
171 | 0 | } |
172 | | |
173 | | PDMFactory::~PDMFactory() |
174 | 0 | { |
175 | 0 | } |
176 | | |
177 | | void |
178 | | PDMFactory::EnsureInit() const |
179 | 0 | { |
180 | 0 | { |
181 | 0 | StaticMutexAutoLock mon(sMonitor); |
182 | 0 | if (sInstance) { |
183 | 0 | // Quick exit if we already have an instance. |
184 | 0 | return; |
185 | 0 | } |
186 | 0 | if (NS_IsMainThread()) { |
187 | 0 | // On the main thread and holding the lock -> Create instance. |
188 | 0 | sInstance = new PDMFactoryImpl(); |
189 | 0 | ClearOnShutdown(&sInstance); |
190 | 0 | return; |
191 | 0 | } |
192 | 0 | } |
193 | 0 | |
194 | 0 | // Not on the main thread -> Sync-dispatch creation to main thread. |
195 | 0 | nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget(); |
196 | 0 | nsCOMPtr<nsIRunnable> runnable = |
197 | 0 | NS_NewRunnableFunction("PDMFactory::EnsureInit", []() { |
198 | 0 | StaticMutexAutoLock mon(sMonitor); |
199 | 0 | if (!sInstance) { |
200 | 0 | sInstance = new PDMFactoryImpl(); |
201 | 0 | ClearOnShutdown(&sInstance); |
202 | 0 | } |
203 | 0 | }); |
204 | 0 | SyncRunnable::DispatchToThread(mainTarget, runnable); |
205 | 0 | } |
206 | | |
207 | | already_AddRefed<MediaDataDecoder> |
208 | | PDMFactory::CreateDecoder(const CreateDecoderParams& aParams) |
209 | 0 | { |
210 | 0 | if (aParams.mUseNullDecoder.mUse) { |
211 | 0 | MOZ_ASSERT(mNullPDM); |
212 | 0 | return CreateDecoderWithPDM(mNullPDM, aParams); |
213 | 0 | } |
214 | 0 |
|
215 | 0 | const TrackInfo& config = aParams.mConfig; |
216 | 0 | bool isEncrypted = mEMEPDM && config.mCrypto.mValid; |
217 | 0 |
|
218 | 0 | if (isEncrypted) { |
219 | 0 | return CreateDecoderWithPDM(mEMEPDM, aParams); |
220 | 0 | } |
221 | 0 | |
222 | 0 | DecoderDoctorDiagnostics* diagnostics = aParams.mDiagnostics; |
223 | 0 | if (diagnostics) { |
224 | 0 | // If libraries failed to load, the following loop over mCurrentPDMs |
225 | 0 | // will not even try to use them. So we record failures now. |
226 | 0 | if (mWMFFailedToLoad) { |
227 | 0 | diagnostics->SetWMFFailedToLoad(); |
228 | 0 | } |
229 | 0 | if (mFFmpegFailedToLoad) { |
230 | 0 | diagnostics->SetFFmpegFailedToLoad(); |
231 | 0 | } |
232 | 0 | if (mGMPPDMFailedToStartup) { |
233 | 0 | diagnostics->SetGMPPDMFailedToStartup(); |
234 | 0 | } |
235 | 0 | } |
236 | 0 |
|
237 | 0 | for (auto& current : mCurrentPDMs) { |
238 | 0 | if (!current->Supports(config, diagnostics)) { |
239 | 0 | continue; |
240 | 0 | } |
241 | 0 | RefPtr<MediaDataDecoder> m = CreateDecoderWithPDM(current, aParams); |
242 | 0 | if (m) { |
243 | 0 | return m.forget(); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | NS_WARNING("Unable to create a decoder, no platform found."); |
247 | 0 | return nullptr; |
248 | 0 | } |
249 | | |
250 | | already_AddRefed<MediaDataDecoder> |
251 | | PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, |
252 | | const CreateDecoderParams& aParams) |
253 | 0 | { |
254 | 0 | MOZ_ASSERT(aPDM); |
255 | 0 | RefPtr<MediaDataDecoder> m; |
256 | 0 | MediaResult* result = aParams.mError; |
257 | 0 |
|
258 | 0 | SupportChecker supportChecker; |
259 | 0 | const TrackInfo& config = aParams.mConfig; |
260 | 0 | supportChecker.AddMediaFormatChecker(config); |
261 | 0 |
|
262 | 0 | auto checkResult = supportChecker.Check(); |
263 | 0 | if (checkResult.mReason != SupportChecker::Reason::kSupported) { |
264 | 0 | DecoderDoctorDiagnostics* diagnostics = aParams.mDiagnostics; |
265 | 0 | if (checkResult.mReason |
266 | 0 | == SupportChecker::Reason::kVideoFormatNotSupported) { |
267 | 0 | if (diagnostics) { |
268 | 0 | diagnostics->SetVideoNotSupported(); |
269 | 0 | } |
270 | 0 | if (result) { |
271 | 0 | *result = checkResult.mMediaResult; |
272 | 0 | } |
273 | 0 | } else if (checkResult.mReason |
274 | 0 | == SupportChecker::Reason::kAudioFormatNotSupported) { |
275 | 0 | if (diagnostics) { |
276 | 0 | diagnostics->SetAudioNotSupported(); |
277 | 0 | } |
278 | 0 | if (result) { |
279 | 0 | *result = checkResult.mMediaResult; |
280 | 0 | } |
281 | 0 | } |
282 | 0 | return nullptr; |
283 | 0 | } |
284 | 0 |
|
285 | 0 | if (config.IsAudio()) { |
286 | 0 | m = aPDM->CreateAudioDecoder(aParams); |
287 | 0 | return m.forget(); |
288 | 0 | } |
289 | 0 | |
290 | 0 | if (!config.IsVideo()) { |
291 | 0 | *result = MediaResult( |
292 | 0 | NS_ERROR_DOM_MEDIA_FATAL_ERR, |
293 | 0 | RESULT_DETAIL("Decoder configuration error, expected audio or video.")); |
294 | 0 | return nullptr; |
295 | 0 | } |
296 | 0 |
|
297 | 0 | if (MP4Decoder::IsH264(config.mMimeType) && !aParams.mUseNullDecoder.mUse && |
298 | 0 | !aParams.mNoWrapper.mDontUseWrapper) { |
299 | 0 | RefPtr<H264Converter> h = new H264Converter(aPDM, aParams); |
300 | 0 | const MediaResult result = h->GetLastError(); |
301 | 0 | if (NS_SUCCEEDED(result) || result == NS_ERROR_NOT_INITIALIZED) { |
302 | 0 | // The H264Converter either successfully created the wrapped decoder, |
303 | 0 | // or there wasn't enough AVCC data to do so. Otherwise, there was some |
304 | 0 | // problem, for example WMF DLLs were missing. |
305 | 0 | m = h.forget(); |
306 | 0 | } else if (aParams.mError) { |
307 | 0 | *aParams.mError = result; |
308 | 0 | } |
309 | 0 | } else { |
310 | 0 | m = aPDM->CreateVideoDecoder(aParams); |
311 | 0 | } |
312 | 0 |
|
313 | 0 | return m.forget(); |
314 | 0 | } |
315 | | |
316 | | bool |
317 | | PDMFactory::SupportsMimeType(const nsACString& aMimeType, |
318 | | DecoderDoctorDiagnostics* aDiagnostics) const |
319 | 0 | { |
320 | 0 | UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType); |
321 | 0 | if (!trackInfo) { |
322 | 0 | return false; |
323 | 0 | } |
324 | 0 | return Supports(*trackInfo, aDiagnostics); |
325 | 0 | } |
326 | | |
327 | | bool |
328 | | PDMFactory::Supports(const TrackInfo& aTrackInfo, |
329 | | DecoderDoctorDiagnostics* aDiagnostics) const |
330 | 0 | { |
331 | 0 | if (mEMEPDM) { |
332 | 0 | return mEMEPDM->Supports(aTrackInfo, aDiagnostics); |
333 | 0 | } |
334 | 0 | RefPtr<PlatformDecoderModule> current = GetDecoder(aTrackInfo, aDiagnostics); |
335 | 0 | return !!current; |
336 | 0 | } |
337 | | |
338 | | void |
339 | | PDMFactory::CreatePDMs() |
340 | 0 | { |
341 | 0 | RefPtr<PlatformDecoderModule> m; |
342 | 0 |
|
343 | 0 | if (StaticPrefs::MediaUseBlankDecoder()) { |
344 | 0 | m = CreateBlankDecoderModule(); |
345 | 0 | StartupPDM(m); |
346 | 0 | // The Blank PDM SupportsMimeType reports true for all codecs; the creation |
347 | 0 | // of its decoder is infallible. As such it will be used for all media, we |
348 | 0 | // can stop creating more PDM from this point. |
349 | 0 | return; |
350 | 0 | } |
351 | 0 | |
352 | | #ifdef XP_WIN |
353 | | if (StaticPrefs::MediaWmfEnabled() && !IsWin7AndPre2000Compatible()) { |
354 | | m = new WMFDecoderModule(); |
355 | | RefPtr<PlatformDecoderModule> remote = new dom::RemoteDecoderModule(m); |
356 | | StartupPDM(remote); |
357 | | mWMFFailedToLoad = !StartupPDM(m); |
358 | | } else { |
359 | | mWMFFailedToLoad = |
360 | | StaticPrefs::MediaDecoderDoctorWmfDisabledIsFailure(); |
361 | | } |
362 | | #endif |
363 | | #ifdef MOZ_OMX |
364 | | if (StaticPrefs::MediaOmxEnabled()) { |
365 | | m = OmxDecoderModule::Create(); |
366 | | StartupPDM(m); |
367 | | } |
368 | | #endif |
369 | | #ifdef MOZ_FFVPX |
370 | 0 | if (StaticPrefs::MediaFfvpxEnabled()) { |
371 | 0 | m = FFVPXRuntimeLinker::CreateDecoderModule(); |
372 | 0 | StartupPDM(m); |
373 | 0 | } |
374 | 0 | #endif |
375 | 0 | #ifdef MOZ_FFMPEG |
376 | 0 | if (StaticPrefs::MediaFfmpegEnabled()) { |
377 | 0 | m = FFmpegRuntimeLinker::CreateDecoderModule(); |
378 | 0 | mFFmpegFailedToLoad = !StartupPDM(m); |
379 | 0 | } else { |
380 | 0 | mFFmpegFailedToLoad = false; |
381 | 0 | } |
382 | 0 | #endif |
383 | | #ifdef MOZ_APPLEMEDIA |
384 | | m = new AppleDecoderModule(); |
385 | | StartupPDM(m); |
386 | | #endif |
387 | | #ifdef MOZ_WIDGET_ANDROID |
388 | | if (StaticPrefs::MediaAndroidMediaCodecEnabled()) { |
389 | | m = new AndroidDecoderModule(); |
390 | | StartupPDM(m, StaticPrefs::MediaAndroidMediaCodecPreferred()); |
391 | | } |
392 | | #endif |
393 | |
|
394 | 0 | m = new AgnosticDecoderModule(); |
395 | 0 | StartupPDM(m); |
396 | 0 |
|
397 | 0 | if (StaticPrefs::MediaGmpDecoderEnabled()) { |
398 | 0 | m = new GMPDecoderModule(); |
399 | 0 | mGMPPDMFailedToStartup = !StartupPDM(m); |
400 | 0 | } else { |
401 | 0 | mGMPPDMFailedToStartup = false; |
402 | 0 | } |
403 | 0 | } |
404 | | |
405 | | void |
406 | | PDMFactory::CreateNullPDM() |
407 | 0 | { |
408 | 0 | mNullPDM = CreateNullDecoderModule(); |
409 | 0 | MOZ_ASSERT(mNullPDM && NS_SUCCEEDED(mNullPDM->Startup())); |
410 | 0 | } |
411 | | |
412 | | bool |
413 | | PDMFactory::StartupPDM(PlatformDecoderModule* aPDM, bool aInsertAtBeginning) |
414 | 0 | { |
415 | 0 | if (aPDM && NS_SUCCEEDED(aPDM->Startup())) { |
416 | 0 | if (aInsertAtBeginning) { |
417 | 0 | mCurrentPDMs.InsertElementAt(0, aPDM); |
418 | 0 | } else { |
419 | 0 | mCurrentPDMs.AppendElement(aPDM); |
420 | 0 | } |
421 | 0 | return true; |
422 | 0 | } |
423 | 0 | return false; |
424 | 0 | } |
425 | | |
426 | | already_AddRefed<PlatformDecoderModule> |
427 | | PDMFactory::GetDecoder(const TrackInfo& aTrackInfo, |
428 | | DecoderDoctorDiagnostics* aDiagnostics) const |
429 | 0 | { |
430 | 0 | if (aDiagnostics) { |
431 | 0 | // If libraries failed to load, the following loop over mCurrentPDMs |
432 | 0 | // will not even try to use them. So we record failures now. |
433 | 0 | if (mWMFFailedToLoad) { |
434 | 0 | aDiagnostics->SetWMFFailedToLoad(); |
435 | 0 | } |
436 | 0 | if (mFFmpegFailedToLoad) { |
437 | 0 | aDiagnostics->SetFFmpegFailedToLoad(); |
438 | 0 | } |
439 | 0 | if (mGMPPDMFailedToStartup) { |
440 | 0 | aDiagnostics->SetGMPPDMFailedToStartup(); |
441 | 0 | } |
442 | 0 | } |
443 | 0 |
|
444 | 0 | RefPtr<PlatformDecoderModule> pdm; |
445 | 0 | for (auto& current : mCurrentPDMs) { |
446 | 0 | if (current->Supports(aTrackInfo, aDiagnostics)) { |
447 | 0 | pdm = current; |
448 | 0 | break; |
449 | 0 | } |
450 | 0 | } |
451 | 0 | return pdm.forget(); |
452 | 0 | } |
453 | | |
454 | | void |
455 | | PDMFactory::SetCDMProxy(CDMProxy* aProxy) |
456 | 0 | { |
457 | 0 | MOZ_ASSERT(aProxy); |
458 | 0 |
|
459 | | #ifdef MOZ_WIDGET_ANDROID |
460 | | if (IsWidevineKeySystem(aProxy->KeySystem())) { |
461 | | mEMEPDM = new AndroidDecoderModule(aProxy); |
462 | | return; |
463 | | } |
464 | | #endif |
465 | | RefPtr<PDMFactory> m = new PDMFactory(); |
466 | 0 | mEMEPDM = new EMEDecoderModule(aProxy, m); |
467 | 0 | } |
468 | | |
469 | | } // namespace mozilla |