Coverage Report

Created: 2018-09-25 14:53

/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