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