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