Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mediacapabilities/MediaCapabilities.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "MediaCapabilities.h"
8
#include "AllocationPolicy.h"
9
#include "Benchmark.h"
10
#include "DecoderTraits.h"
11
#include "Layers.h"
12
#include "MediaInfo.h"
13
#include "MediaRecorder.h"
14
#include "PDMFactory.h"
15
#include "VPXDecoder.h"
16
#include "mozilla/Move.h"
17
#include "mozilla/StaticPrefs.h"
18
#include "mozilla/TaskQueue.h"
19
#include "mozilla/dom/DOMMozPromiseRequestHolder.h"
20
#include "mozilla/dom/MediaCapabilitiesBinding.h"
21
#include "mozilla/dom/MediaSource.h"
22
#include "mozilla/dom/Promise.h"
23
#include "mozilla/dom/WorkerPrivate.h"
24
#include "mozilla/dom/WorkerRef.h"
25
#include "mozilla/layers/KnowsCompositor.h"
26
#include "nsContentUtils.h"
27
28
#include <inttypes.h>
29
30
static mozilla::LazyLogModule sMediaCapabilitiesLog("MediaCapabilities");
31
32
#define LOG(msg, ...)                                                          \
33
0
  DDMOZ_LOG(sMediaCapabilitiesLog, LogLevel::Debug, msg, ##__VA_ARGS__)
34
35
namespace mozilla {
36
namespace dom {
37
38
static nsCString
39
VideoConfigurationToStr(const VideoConfiguration* aConfig)
40
0
{
41
0
  if (!aConfig) {
42
0
    return nsCString();
43
0
  }
44
0
  auto str = nsPrintfCString(
45
0
    "[contentType:%s width:%d height:%d bitrate:%" PRIu64 " framerate:%s]",
46
0
    NS_ConvertUTF16toUTF8(aConfig->mContentType.Value()).get(),
47
0
    aConfig->mWidth.Value(),
48
0
    aConfig->mHeight.Value(),
49
0
    aConfig->mBitrate.Value(),
50
0
    NS_ConvertUTF16toUTF8(aConfig->mFramerate.Value()).get());
51
0
  return std::move(str);
52
0
}
53
54
static nsCString
55
AudioConfigurationToStr(const AudioConfiguration* aConfig)
56
0
{
57
0
  if (!aConfig) {
58
0
    return nsCString();
59
0
  }
60
0
  auto str = nsPrintfCString(
61
0
    "[contentType:%s channels:%s bitrate:%" PRIu64 " samplerate:%d]",
62
0
    NS_ConvertUTF16toUTF8(aConfig->mContentType.Value()).get(),
63
0
    aConfig->mChannels.WasPassed()
64
0
      ? NS_ConvertUTF16toUTF8(aConfig->mChannels.Value()).get()
65
0
      : "?",
66
0
    aConfig->mBitrate.WasPassed() ? aConfig->mBitrate.Value() : 0,
67
0
    aConfig->mSamplerate.WasPassed() ? aConfig->mSamplerate.Value() : 0);
68
0
  return std::move(str);
69
0
}
70
71
static nsCString
72
MediaCapabilitiesInfoToStr(const MediaCapabilitiesInfo* aInfo)
73
0
{
74
0
  if (!aInfo) {
75
0
    return nsCString();
76
0
  }
77
0
  auto str = nsPrintfCString("[supported:%s smooth:%s powerEfficient:%s]",
78
0
                             aInfo->Supported() ? "true" : "false",
79
0
                             aInfo->Smooth() ? "true" : "false",
80
0
                             aInfo->PowerEfficient() ? "true" : "false");
81
0
  return std::move(str);
82
0
}
83
84
static nsCString
85
MediaDecodingConfigurationToStr(const MediaDecodingConfiguration& aConfig)
86
0
{
87
0
  nsCString str;
88
0
  str += NS_LITERAL_CSTRING("[");
89
0
  if (aConfig.mVideo.IsAnyMemberPresent()) {
90
0
    str +=
91
0
      NS_LITERAL_CSTRING("video:") + VideoConfigurationToStr(&aConfig.mVideo);
92
0
    if (aConfig.mAudio.IsAnyMemberPresent()) {
93
0
      str += NS_LITERAL_CSTRING(" ");
94
0
    }
95
0
  }
96
0
  if (aConfig.mAudio.IsAnyMemberPresent()) {
97
0
    str +=
98
0
      NS_LITERAL_CSTRING("audio:") + AudioConfigurationToStr(&aConfig.mAudio);
99
0
  }
100
0
  str += NS_LITERAL_CSTRING("]");
101
0
  return str;
102
0
}
103
104
MediaCapabilities::MediaCapabilities(nsIGlobalObject* aParent)
105
  : mParent(aParent)
106
0
{
107
0
}
108
109
static void
110
ThrowWithMemberName(ErrorResult& aRv,
111
                    const char* aCategory,
112
                    const char* aMember)
113
0
{
114
0
  auto str = nsPrintfCString("'%s' member of %s", aMember, aCategory);
115
0
  aRv.ThrowTypeError<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>(
116
0
    NS_ConvertUTF8toUTF16(str));
117
0
}
118
119
static void
120
CheckVideoConfigurationSanity(const VideoConfiguration& aConfig,
121
                              const char* aCategory,
122
                              ErrorResult& aRv)
123
0
{
124
0
  if (!aConfig.mContentType.WasPassed()) {
125
0
    ThrowWithMemberName(aRv, "contentType", aCategory);
126
0
    return;
127
0
  }
128
0
  if (!aConfig.mWidth.WasPassed()) {
129
0
    ThrowWithMemberName(aRv, "width", aCategory);
130
0
    return;
131
0
  }
132
0
  if (!aConfig.mHeight.WasPassed()) {
133
0
    ThrowWithMemberName(aRv, "height", aCategory);
134
0
    return;
135
0
  }
136
0
  if (!aConfig.mBitrate.WasPassed()) {
137
0
    ThrowWithMemberName(aRv, "bitrate", aCategory);
138
0
    return;
139
0
  }
140
0
  if (!aConfig.mFramerate.WasPassed()) {
141
0
    ThrowWithMemberName(aRv, "framerate", aCategory);
142
0
    return;
143
0
  }
144
0
}
145
146
static void
147
CheckAudioConfigurationSanity(const AudioConfiguration& aConfig,
148
                              const char* aCategory,
149
                              ErrorResult& aRv)
150
0
{
151
0
  if (!aConfig.mContentType.WasPassed()) {
152
0
    ThrowWithMemberName(aRv, "contentType", aCategory);
153
0
    return;
154
0
  }
155
0
}
156
157
already_AddRefed<Promise>
158
MediaCapabilities::DecodingInfo(
159
  const MediaDecodingConfiguration& aConfiguration,
160
  ErrorResult& aRv)
161
0
{
162
0
  RefPtr<Promise> promise = Promise::Create(mParent, aRv);
163
0
  if (aRv.Failed()) {
164
0
    return nullptr;
165
0
  }
166
0
167
0
  // If configuration is not a valid MediaConfiguration, return a Promise
168
0
  // rejected with a TypeError.
169
0
  if (!aConfiguration.IsAnyMemberPresent() ||
170
0
      (!aConfiguration.mVideo.IsAnyMemberPresent() &&
171
0
       !aConfiguration.mAudio.IsAnyMemberPresent())) {
172
0
    aRv.ThrowTypeError<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>(
173
0
      NS_LITERAL_STRING("'audio' or 'video'"));
174
0
    return nullptr;
175
0
  }
176
0
177
0
  // Here we will throw rather than rejecting a promise in order to simulate
178
0
  // optional dictionaries with required members (see bug 1368949)
179
0
  if (aConfiguration.mVideo.IsAnyMemberPresent()) {
180
0
    // Check that all VideoConfiguration required members are present.
181
0
    CheckVideoConfigurationSanity(
182
0
      aConfiguration.mVideo, "MediaDecodingConfiguration", aRv);
183
0
    if (aRv.Failed()) {
184
0
      return nullptr;
185
0
    }
186
0
  }
187
0
  if (aConfiguration.mAudio.IsAnyMemberPresent()) {
188
0
    // Check that all AudioConfiguration required members are present.
189
0
    CheckAudioConfigurationSanity(
190
0
      aConfiguration.mAudio, "MediaDecodingConfiguration", aRv);
191
0
    if (aRv.Failed()) {
192
0
      return nullptr;
193
0
    }
194
0
  }
195
0
196
0
  LOG("Processing %s", MediaDecodingConfigurationToStr(aConfiguration).get());
197
0
198
0
  bool supported = true;
199
0
  Maybe<MediaContainerType> videoContainer;
200
0
  Maybe<MediaContainerType> audioContainer;
201
0
202
0
  // If configuration.video is present and is not a valid video configuration,
203
0
  // return a Promise rejected with a TypeError.
204
0
  if (aConfiguration.mVideo.IsAnyMemberPresent()) {
205
0
    videoContainer = CheckVideoConfiguration(aConfiguration.mVideo);
206
0
    if (!videoContainer) {
207
0
      aRv.ThrowTypeError<MSG_INVALID_MEDIA_VIDEO_CONFIGURATION>();
208
0
      return nullptr;
209
0
    }
210
0
211
0
    // We have a video configuration and it is valid. Check if it is supported.
212
0
    supported &=
213
0
      aConfiguration.mType == MediaDecodingType::File
214
0
        ? CheckTypeForFile(aConfiguration.mVideo.mContentType.Value())
215
0
        : CheckTypeForMediaSource(aConfiguration.mVideo.mContentType.Value());
216
0
  }
217
0
  if (aConfiguration.mAudio.IsAnyMemberPresent()) {
218
0
    audioContainer = CheckAudioConfiguration(aConfiguration.mAudio);
219
0
    if (!audioContainer) {
220
0
      aRv.ThrowTypeError<MSG_INVALID_MEDIA_AUDIO_CONFIGURATION>();
221
0
      return nullptr;
222
0
    }
223
0
    // We have an audio configuration and it is valid. Check if it is supported.
224
0
    supported &=
225
0
      aConfiguration.mType == MediaDecodingType::File
226
0
        ? CheckTypeForFile(aConfiguration.mAudio.mContentType.Value())
227
0
        : CheckTypeForMediaSource(aConfiguration.mAudio.mContentType.Value());
228
0
  }
229
0
230
0
  if (!supported) {
231
0
    auto info = MakeUnique<MediaCapabilitiesInfo>(
232
0
      false /* supported */, false /* smooth */, false /* power efficient */);
233
0
    LOG("%s -> %s",
234
0
        MediaDecodingConfigurationToStr(aConfiguration).get(),
235
0
        MediaCapabilitiesInfoToStr(info.get()).get());
236
0
    promise->MaybeResolve(std::move(info));
237
0
    return promise.forget();
238
0
  }
239
0
240
0
  nsTArray<UniquePtr<TrackInfo>> tracks;
241
0
  if (aConfiguration.mVideo.IsAnyMemberPresent()) {
242
0
    MOZ_ASSERT(videoContainer.isSome(), "configuration is valid and supported");
243
0
    auto videoTracks = DecoderTraits::GetTracksInfo(*videoContainer);
244
0
    // If the MIME type does not imply a codec, the string MUST
245
0
    // also have one and only one parameter that is named codecs with a value
246
0
    // describing a single media codec. Otherwise, it MUST contain no
247
0
    // parameters.
248
0
    if (videoTracks.Length() != 1) {
249
0
      promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
250
0
      return promise.forget();
251
0
    }
252
0
    MOZ_DIAGNOSTIC_ASSERT(videoTracks.ElementAt(0),
253
0
                          "must contain a valid trackinfo");
254
0
    tracks.AppendElements(std::move(videoTracks));
255
0
  }
256
0
  if (aConfiguration.mAudio.IsAnyMemberPresent()) {
257
0
    MOZ_ASSERT(audioContainer.isSome(), "configuration is valid and supported");
258
0
    auto audioTracks = DecoderTraits::GetTracksInfo(*audioContainer);
259
0
    // If the MIME type does not imply a codec, the string MUST
260
0
    // also have one and only one parameter that is named codecs with a value
261
0
    // describing a single media codec. Otherwise, it MUST contain no
262
0
    // parameters.
263
0
    if (audioTracks.Length() != 1) {
264
0
      promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
265
0
      return promise.forget();
266
0
    }
267
0
    MOZ_DIAGNOSTIC_ASSERT(audioTracks.ElementAt(0),
268
0
                          "must contain a valid trackinfo");
269
0
    tracks.AppendElements(std::move(audioTracks));
270
0
  }
271
0
272
0
  typedef MozPromise<MediaCapabilitiesInfo,
273
0
                     MediaResult,
274
0
                     /* IsExclusive = */ true>
275
0
    CapabilitiesPromise;
276
0
  nsTArray<RefPtr<CapabilitiesPromise>> promises;
277
0
278
0
  RefPtr<TaskQueue> taskQueue =
279
0
    new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
280
0
                  "MediaCapabilities::TaskQueue");
281
0
  for (auto&& config : tracks) {
282
0
    TrackInfo::TrackType type =
283
0
      config->IsVideo() ? TrackInfo::kVideoTrack : TrackInfo::kAudioTrack;
284
0
285
0
    MOZ_ASSERT(type == TrackInfo::kAudioTrack ||
286
0
                 videoContainer->ExtendedType().GetFramerate().isSome(),
287
0
               "framerate is a required member of VideoConfiguration");
288
0
289
0
    if (type == TrackInfo::kAudioTrack) {
290
0
      // There's no need to create an audio decoder has we only want to know if
291
0
      // such codec is supported
292
0
      RefPtr<PDMFactory> pdm = new PDMFactory();
293
0
      if (!pdm->Supports(*config, nullptr /* decoder doctor */)) {
294
0
        auto info =
295
0
          MakeUnique<MediaCapabilitiesInfo>(false /* supported */,
296
0
                                            false /* smooth */,
297
0
                                            false /* power efficient */);
298
0
        LOG("%s -> %s",
299
0
            MediaDecodingConfigurationToStr(aConfiguration).get(),
300
0
            MediaCapabilitiesInfoToStr(info.get()).get());
301
0
        promise->MaybeResolve(std::move(info));
302
0
        return promise.forget();
303
0
      }
304
0
      // We can assume that if we could create the decoder, then we can play it.
305
0
      // We report that we can play it smoothly and in an efficient fashion.
306
0
      promises.AppendElement(CapabilitiesPromise::CreateAndResolve(
307
0
        MediaCapabilitiesInfo(
308
0
          true /* supported */, true /* smooth */, true /* power efficient */),
309
0
        __func__));
310
0
      continue;
311
0
    }
312
0
313
0
    // On Windows, the MediaDataDecoder expects to be created on a thread
314
0
    // supporting MTA, which the main thread doesn't. So we use our task queue
315
0
    // to create such decoder and perform initialization.
316
0
317
0
    RefPtr<layers::KnowsCompositor> compositor = GetCompositor();
318
0
    double frameRate = videoContainer->ExtendedType().GetFramerate().ref();
319
0
    promises.AppendElement(InvokeAsync(
320
0
      taskQueue,
321
0
      __func__,
322
0
      [taskQueue, frameRate, compositor, config = std::move(config)]() mutable
323
0
      -> RefPtr<CapabilitiesPromise> {
324
0
        // MediaDataDecoder keeps a reference to the config object, so we must
325
0
        // keep it alive until the decoder has been shutdown.
326
0
        CreateDecoderParams params{ *config,
327
0
                                    taskQueue,
328
0
                                    compositor,
329
0
                                    CreateDecoderParams::VideoFrameRate(
330
0
                                      frameRate),
331
0
                                    TrackInfo::kVideoTrack };
332
0
        return AllocationWrapper::CreateDecoder(params)->Then(
333
0
          taskQueue,
334
0
          __func__,
335
0
          [taskQueue, frameRate, config = std::move(config)](
336
0
            const AllocationWrapper::AllocateDecoderPromise::
337
0
              ResolveOrRejectValue& aValue) mutable {
338
0
            if (aValue.IsReject()) {
339
0
              return CapabilitiesPromise::CreateAndReject(aValue.RejectValue(),
340
0
                                                          __func__);
341
0
            }
342
0
            RefPtr<MediaDataDecoder> decoder = aValue.ResolveValue();
343
0
            // We now query the decoder to determine if it's power efficient.
344
0
            RefPtr<CapabilitiesPromise> p = decoder->Init()->Then(
345
0
              taskQueue,
346
0
              __func__,
347
0
              [taskQueue, decoder, frameRate, config = std::move(config)](
348
0
                const MediaDataDecoder::InitPromise::ResolveOrRejectValue&
349
0
                  aValue) mutable {
350
0
                RefPtr<CapabilitiesPromise> p;
351
0
                if (aValue.IsReject()) {
352
0
                  p = CapabilitiesPromise::CreateAndReject(aValue.RejectValue(),
353
0
                                                           __func__);
354
0
                } else {
355
0
                  MOZ_ASSERT(config->IsVideo());
356
0
                  nsAutoCString reason;
357
0
                  bool powerEfficient = true;
358
0
                  bool smooth = true;
359
0
                  if (config->GetAsVideoInfo()->mImage.height > 480) {
360
0
                    // Assume that we can do stuff at 480p or less in a power
361
0
                    // efficient manner and smoothly. If greater than 480p we
362
0
                    // assume that if the video decoding is hardware accelerated
363
0
                    // it will be smooth and power efficient, otherwise we use
364
0
                    // the benchmark to estimate
365
0
                    powerEfficient = decoder->IsHardwareAccelerated(reason);
366
0
                    if (!powerEfficient &&
367
0
                        VPXDecoder::IsVP9(config->mMimeType)) {
368
0
                      smooth =
369
0
                        VP9Benchmark::IsVP9DecodeFast(true /* default */);
370
0
                      uint32_t fps = VP9Benchmark::MediaBenchmarkVp9Fps();
371
0
                      if (!smooth && fps > 0) {
372
0
                        // The VP9 estimizer decode a 1280x720 video. Let's
373
0
                        // adjust the result for the resolution and frame rate
374
0
                        // of what we actually want. If the result is twice that
375
0
                        // we need we assume it will be smooth.
376
0
                        const auto& videoConfig = *config->GetAsVideoInfo();
377
0
                        double needed = ((1280.0 * 720.0) /
378
0
                                         (videoConfig.mImage.width *
379
0
                                          videoConfig.mImage.height) *
380
0
                                         fps) /
381
0
                                        frameRate;
382
0
                        smooth = needed > 2;
383
0
                      }
384
0
                    }
385
0
                  }
386
0
                  p = CapabilitiesPromise::CreateAndResolve(
387
0
                    MediaCapabilitiesInfo(
388
0
                      true /* supported */, smooth, powerEfficient),
389
0
                    __func__);
390
0
                }
391
0
                MOZ_ASSERT(p.get(), "the promise has been created");
392
0
                // Let's keep alive the decoder and the config object until the
393
0
                // decoder has shutdown.
394
0
                decoder->Shutdown()->Then(
395
0
                  taskQueue,
396
0
                  __func__,
397
0
                  [taskQueue, decoder, config = std::move(config)](
398
0
                    const ShutdownPromise::ResolveOrRejectValue& aValue) {});
399
0
                return p;
400
0
              });
401
0
            return p;
402
0
          });
403
0
      }));
404
0
  }
405
0
406
0
  auto holder =
407
0
    MakeRefPtr<DOMMozPromiseRequestHolder<CapabilitiesPromise::AllPromiseType>>(
408
0
      mParent);
409
0
  RefPtr<nsISerialEventTarget> targetThread;
410
0
  RefPtr<StrongWorkerRef> workerRef;
411
0
412
0
  if (NS_IsMainThread()) {
413
0
    targetThread = mParent->AbstractMainThreadFor(TaskCategory::Other);
414
0
  } else {
415
0
    WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
416
0
    MOZ_ASSERT(wp, "Must be called from a worker thread");
417
0
    targetThread = wp->HybridEventTarget();
418
0
    workerRef = StrongWorkerRef::Create(
419
0
      wp, "MediaCapabilities", [holder, targetThread]() {
420
0
        MOZ_ASSERT(targetThread->IsOnCurrentThread());
421
0
        holder->DisconnectIfExists();
422
0
      });
423
0
    if (NS_WARN_IF(!workerRef)) {
424
0
      // The worker is shutting down.
425
0
      aRv.Throw(NS_ERROR_FAILURE);
426
0
      return nullptr;
427
0
    }
428
0
  }
429
0
430
0
  MOZ_ASSERT(targetThread);
431
0
432
0
  // this is only captured for use with the LOG macro.
433
0
  RefPtr<MediaCapabilities> self = this;
434
0
435
0
  CapabilitiesPromise::All(targetThread, promises)
436
0
    ->Then(
437
0
      targetThread,
438
0
      __func__,
439
0
      [promise,
440
0
       tracks = std::move(tracks),
441
0
       workerRef,
442
0
       holder,
443
0
       aConfiguration,
444
0
       self,
445
0
       this](const CapabilitiesPromise::AllPromiseType::ResolveOrRejectValue&
446
0
               aValue) {
447
0
        holder->Complete();
448
0
        if (aValue.IsReject()) {
449
0
          auto info =
450
0
            MakeUnique<MediaCapabilitiesInfo>(false /* supported */,
451
0
                                              false /* smooth */,
452
0
                                              false /* power efficient */);
453
0
          LOG("%s -> %s",
454
0
              MediaDecodingConfigurationToStr(aConfiguration).get(),
455
0
              MediaCapabilitiesInfoToStr(info.get()).get());
456
0
          promise->MaybeResolve(std::move(info));
457
0
          return;
458
0
        }
459
0
        bool powerEfficient = true;
460
0
        bool smooth = true;
461
0
        for (auto&& capability : aValue.ResolveValue()) {
462
0
          smooth &= capability.Smooth();
463
0
          powerEfficient &= capability.PowerEfficient();
464
0
        }
465
0
        auto info = MakeUnique<MediaCapabilitiesInfo>(
466
0
          true /* supported */, smooth, powerEfficient);
467
0
        LOG("%s -> %s",
468
0
            MediaDecodingConfigurationToStr(aConfiguration).get(),
469
0
            MediaCapabilitiesInfoToStr(info.get()).get());
470
0
        promise->MaybeResolve(std::move(info));
471
0
      })
472
0
    ->Track(*holder);
473
0
474
0
  return promise.forget();
475
0
}
476
477
already_AddRefed<Promise>
478
MediaCapabilities::EncodingInfo(
479
  const MediaEncodingConfiguration& aConfiguration,
480
  ErrorResult& aRv)
481
0
{
482
0
  RefPtr<Promise> promise = Promise::Create(mParent, aRv);
483
0
  if (aRv.Failed()) {
484
0
    return nullptr;
485
0
  }
486
0
487
0
  // If configuration is not a valid MediaConfiguration, return a Promise
488
0
  // rejected with a TypeError.
489
0
  if (!aConfiguration.IsAnyMemberPresent() ||
490
0
      (!aConfiguration.mVideo.IsAnyMemberPresent() &&
491
0
       !aConfiguration.mAudio.IsAnyMemberPresent())) {
492
0
    aRv.ThrowTypeError<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>(
493
0
      NS_LITERAL_STRING("'audio' or 'video'"));
494
0
    return nullptr;
495
0
  }
496
0
497
0
  // Here we will throw rather than rejecting a promise in order to simulate
498
0
  // optional dictionaries with required members (see bug 1368949)
499
0
  if (aConfiguration.mVideo.IsAnyMemberPresent()) {
500
0
    // Check that all VideoConfiguration required members are present.
501
0
    CheckVideoConfigurationSanity(
502
0
      aConfiguration.mVideo, "MediaDecodingConfiguration", aRv);
503
0
    if (aRv.Failed()) {
504
0
      return nullptr;
505
0
    }
506
0
  }
507
0
  if (aConfiguration.mAudio.IsAnyMemberPresent()) {
508
0
    // Check that all AudioConfiguration required members are present.
509
0
    CheckAudioConfigurationSanity(
510
0
      aConfiguration.mAudio, "MediaDecodingConfiguration", aRv);
511
0
    if (aRv.Failed()) {
512
0
      return nullptr;
513
0
    }
514
0
  }
515
0
516
0
  bool supported = true;
517
0
518
0
  // If configuration.video is present and is not a valid video configuration,
519
0
  // return a Promise rejected with a TypeError.
520
0
  if (aConfiguration.mVideo.IsAnyMemberPresent()) {
521
0
    if (!CheckVideoConfiguration(aConfiguration.mVideo)) {
522
0
      aRv.ThrowTypeError<MSG_INVALID_MEDIA_VIDEO_CONFIGURATION>();
523
0
      return nullptr;
524
0
    }
525
0
    // We have a video configuration and it is valid. Check if it is supported.
526
0
    supported &=
527
0
      CheckTypeForEncoder(aConfiguration.mVideo.mContentType.Value());
528
0
  }
529
0
  if (aConfiguration.mAudio.IsAnyMemberPresent()) {
530
0
    if (!CheckAudioConfiguration(aConfiguration.mAudio)) {
531
0
      aRv.ThrowTypeError<MSG_INVALID_MEDIA_AUDIO_CONFIGURATION>();
532
0
      return nullptr;
533
0
    }
534
0
    // We have an audio configuration and it is valid. Check if it is supported.
535
0
    supported &=
536
0
      CheckTypeForEncoder(aConfiguration.mAudio.mContentType.Value());
537
0
  }
538
0
539
0
  auto info = MakeUnique<MediaCapabilitiesInfo>(supported, supported, false);
540
0
  promise->MaybeResolve(std::move(info));
541
0
542
0
  return promise.forget();
543
0
}
544
545
Maybe<MediaContainerType>
546
MediaCapabilities::CheckVideoConfiguration(
547
  const VideoConfiguration& aConfig) const
548
0
{
549
0
  Maybe<MediaExtendedMIMEType> container = MakeMediaExtendedMIMEType(aConfig);
550
0
  if (!container) {
551
0
    return Nothing();
552
0
  }
553
0
  // A valid video MIME type is a string that is a valid media MIME type and for
554
0
  // which the type per [RFC7231] is either video or application.
555
0
  if (!container->Type().HasVideoMajorType() &&
556
0
      !container->Type().HasApplicationMajorType()) {
557
0
    return Nothing();
558
0
  }
559
0
560
0
  // If the MIME type does not imply a codec, the string MUST also have one and
561
0
  // only one parameter that is named codecs with a value describing a single
562
0
  // media codec. Otherwise, it MUST contain no parameters.
563
0
  // TODO (nsIMOMEHeaderParam doesn't provide backend to count number of
564
0
  // parameters)
565
0
566
0
  return Some(MediaContainerType(std::move(*container)));
567
0
}
568
569
Maybe<MediaContainerType>
570
MediaCapabilities::CheckAudioConfiguration(
571
  const AudioConfiguration& aConfig) const
572
0
{
573
0
  Maybe<MediaExtendedMIMEType> container = MakeMediaExtendedMIMEType(aConfig);
574
0
  if (!container) {
575
0
    return Nothing();
576
0
  }
577
0
  // A valid audio MIME type is a string that is valid media MIME type and for
578
0
  // which the type per [RFC7231] is either audio or application.
579
0
  if (!container->Type().HasAudioMajorType() &&
580
0
      !container->Type().HasApplicationMajorType()) {
581
0
    return Nothing();
582
0
  }
583
0
584
0
  // If the MIME type does not imply a codec, the string MUST also have one and
585
0
  // only one parameter that is named codecs with a value describing a single
586
0
  // media codec. Otherwise, it MUST contain no parameters.
587
0
  // TODO (nsIMOMEHeaderParam doesn't provide backend to count number of
588
0
  // parameters)
589
0
590
0
  return Some(MediaContainerType(std::move(*container)));
591
0
}
592
593
bool
594
MediaCapabilities::CheckTypeForMediaSource(const nsAString& aType)
595
0
{
596
0
  return NS_SUCCEEDED(MediaSource::IsTypeSupported(
597
0
    aType, nullptr /* DecoderDoctorDiagnostics */));
598
0
}
599
600
bool
601
MediaCapabilities::CheckTypeForFile(const nsAString& aType)
602
0
{
603
0
  Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
604
0
  if (!containerType) {
605
0
    return false;
606
0
  }
607
0
608
0
  return DecoderTraits::CanHandleContainerType(
609
0
           *containerType, nullptr /* DecoderDoctorDiagnostics */) !=
610
0
         CANPLAY_NO;
611
0
}
612
613
bool
614
MediaCapabilities::CheckTypeForEncoder(const nsAString& aType)
615
0
{
616
0
  return MediaRecorder::IsTypeSupported(aType);
617
0
}
618
619
already_AddRefed<layers::KnowsCompositor>
620
MediaCapabilities::GetCompositor()
621
0
{
622
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
623
0
  if (NS_WARN_IF(!window)) {
624
0
    return nullptr;
625
0
  }
626
0
627
0
  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
628
0
  if (NS_WARN_IF(!doc)) {
629
0
    return nullptr;
630
0
  }
631
0
  RefPtr<layers::LayerManager> layerManager =
632
0
    nsContentUtils::LayerManagerForDocument(doc);
633
0
  if (NS_WARN_IF(!layerManager)) {
634
0
    return nullptr;
635
0
  }
636
0
  RefPtr<layers::KnowsCompositor> knows = layerManager->AsKnowsCompositor();
637
0
  if (NS_WARN_IF(!knows)) {
638
0
    return nullptr;
639
0
  }
640
0
  return knows->GetForMedia().forget();
641
0
}
642
643
bool
644
MediaCapabilities::Enabled(JSContext* aCx, JSObject* aGlobal)
645
0
{
646
0
  return StaticPrefs::MediaCapabilitiesEnabled();
647
0
}
648
649
JSObject*
650
MediaCapabilities::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
651
0
{
652
0
  return MediaCapabilities_Binding::Wrap(aCx, this, aGivenProto);
653
0
}
654
655
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaCapabilities)
656
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
657
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
658
0
NS_INTERFACE_MAP_END
659
660
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaCapabilities)
661
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaCapabilities)
662
663
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaCapabilities, mParent)
664
665
// MediaCapabilitiesInfo
666
bool
667
MediaCapabilitiesInfo::WrapObject(JSContext* aCx,
668
                                  JS::Handle<JSObject*> aGivenProto,
669
                                  JS::MutableHandle<JSObject*> aReflector)
670
0
{
671
0
  return MediaCapabilitiesInfo_Binding::Wrap(
672
0
    aCx, this, aGivenProto, aReflector);
673
0
}
674
675
} // namespace dom
676
} // namespace mozilla