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