WebCodecsUtils.cpp (23849B)
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 "WebCodecsUtils.h" 8 9 #include "DecoderTypes.h" 10 #include "EncoderTypes.h" 11 #include "PlatformEncoderModule.h" 12 #include "VideoUtils.h" 13 #include "js/experimental/TypedData.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/StaticPrefs_dom.h" 16 #include "mozilla/StaticPrefs_media.h" 17 #include "mozilla/ToString.h" 18 #include "mozilla/dom/EncoderTypes.h" 19 #include "mozilla/dom/ImageBitmapBinding.h" 20 #include "mozilla/dom/UnionTypes.h" 21 #include "mozilla/dom/VideoColorSpaceBinding.h" 22 #include "mozilla/dom/VideoFrameBinding.h" 23 #include "mozilla/gfx/Types.h" 24 #include "nsDebug.h" 25 #include "nsIGlobalObject.h" 26 #include "nsString.h" 27 28 extern mozilla::LazyLogModule gWebCodecsLog; 29 30 #ifdef LOG_INTERNAL 31 # undef LOG_INTERNAL 32 #endif // LOG_INTERNAL 33 #define LOG_INTERNAL(level, msg, ...) \ 34 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__)) 35 #ifdef LOG 36 # undef LOG 37 #endif // LOG 38 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 39 #ifdef LOGW 40 # undef LOGW 41 #endif // LOGW 42 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__) 43 44 namespace mozilla { 45 std::atomic<WebCodecsId> sNextId = 0; 46 }; 47 48 namespace mozilla::dom { 49 50 /* 51 * The followings are helpers for AudioDecoder and VideoDecoder methods 52 */ 53 54 nsTArray<nsCString> GuessContainers(const nsAString& aCodec) { 55 if (IsAV1CodecString(aCodec)) { 56 return {"mp4"_ns, "webm"_ns}; 57 } 58 59 if (IsVP9CodecString(aCodec)) { 60 return {"mp4"_ns, "webm"_ns, "ogg"_ns}; 61 } 62 63 if (IsVP8CodecString(aCodec)) { 64 return {"webm"_ns, "ogg"_ns, "3gpp"_ns, "3gpp2"_ns, "3gp2"_ns}; 65 } 66 67 if (IsH264CodecString(aCodec)) { 68 return {"mp4"_ns, "3gpp"_ns, "3gpp2"_ns, "3gp2"_ns}; 69 } 70 71 if (IsH265CodecString(aCodec)) { 72 return {"mp4"_ns}; 73 } 74 75 if (IsAACCodecString(aCodec)) { 76 return {"adts"_ns, "mp4"_ns}; 77 } 78 79 if (aCodec.EqualsLiteral("vorbis") || aCodec.EqualsLiteral("opus")) { 80 return {"ogg"_ns}; 81 } 82 83 if (aCodec.EqualsLiteral("flac")) { 84 return {"flac"_ns}; 85 } 86 87 if (aCodec.EqualsLiteral("mp3")) { 88 return {"mp3"_ns}; 89 } 90 91 if (aCodec.EqualsLiteral("ulaw") || aCodec.EqualsLiteral("alaw") || 92 aCodec.EqualsLiteral("pcm-u8") || aCodec.EqualsLiteral("pcm-s16") || 93 aCodec.EqualsLiteral("pcm-s24") || aCodec.EqualsLiteral("pcm-s32") || 94 aCodec.EqualsLiteral("pcm-f32")) { 95 return {"x-wav"_ns}; 96 } 97 98 return {}; 99 } 100 101 Maybe<nsString> ParseCodecString(const nsAString& aCodec) { 102 // Trim the spaces on each end. 103 nsString str(aCodec); 104 str.Trim(" "); 105 nsTArray<nsString> codecs; 106 if (!ParseCodecsString(str, codecs) || codecs.Length() != 1 || 107 codecs[0] != str) { 108 return Nothing(); 109 } 110 return Some(codecs[0]); 111 } 112 113 /* 114 * The below are helpers to operate ArrayBuffer or ArrayBufferView. 115 */ 116 117 static std::tuple<JS::ArrayBufferOrView, size_t, size_t> GetArrayBufferInfo( 118 JSContext* aCx, const OwningAllowSharedBufferSource& aBuffer) { 119 if (aBuffer.IsArrayBuffer()) { 120 const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer(); 121 size_t length; 122 { 123 bool isShared; 124 uint8_t* data; 125 JS::GetArrayBufferMaybeSharedLengthAndData(buffer.Obj(), &length, 126 &isShared, &data); 127 } 128 return std::make_tuple(JS::ArrayBufferOrView::fromObject(buffer.Obj()), 129 (size_t)0, length); 130 } 131 132 MOZ_ASSERT(aBuffer.IsArrayBufferView()); 133 const ArrayBufferView& view = aBuffer.GetAsArrayBufferView(); 134 bool isSharedMemory; 135 JS::Rooted<JSObject*> obj(aCx, view.Obj()); 136 return std::make_tuple( 137 JS::ArrayBufferOrView::fromObject( 138 JS_GetArrayBufferViewBuffer(aCx, obj, &isSharedMemory)), 139 JS_GetArrayBufferViewByteOffset(obj), 140 JS_GetArrayBufferViewByteLength(obj)); 141 } 142 143 Result<Ok, nsresult> CloneBuffer(JSContext* aCx, 144 OwningAllowSharedBufferSource& aDest, 145 const OwningAllowSharedBufferSource& aSrc, 146 ErrorResult& aRv) { 147 std::tuple<JS::ArrayBufferOrView, size_t, size_t> info = 148 GetArrayBufferInfo(aCx, aSrc); 149 JS::Rooted<JS::ArrayBufferOrView> abov(aCx); 150 abov.set(std::get<0>(info)); 151 size_t offset = std::get<1>(info); 152 size_t len = std::get<2>(info); 153 if (NS_WARN_IF(!abov)) { 154 return Err(NS_ERROR_UNEXPECTED); 155 } 156 157 JS::Rooted<JSObject*> obj(aCx, abov.asObject()); 158 JS::Rooted<JSObject*> cloned(aCx, 159 JS::ArrayBufferClone(aCx, obj, offset, len)); 160 if (NS_WARN_IF(!cloned)) { 161 aRv.MightThrowJSException(); 162 aRv.StealExceptionFromJSContext(aCx); 163 return Err(NS_ERROR_OUT_OF_MEMORY); 164 } 165 166 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*cloned)); 167 if (NS_WARN_IF(!aDest.Init(aCx, value))) { 168 return Err(NS_ERROR_UNEXPECTED); 169 } 170 return Ok(); 171 } 172 173 Result<RefPtr<MediaByteBuffer>, nsresult> GetExtraDataFromArrayBuffer( 174 const OwningAllowSharedBufferSource& aBuffer) { 175 RefPtr<MediaByteBuffer> data = MakeRefPtr<MediaByteBuffer>(); 176 if (!AppendTypedArrayDataTo(aBuffer, *data)) { 177 return Err(NS_ERROR_OUT_OF_MEMORY); 178 } 179 return data->Length() > 0 ? data : nullptr; 180 } 181 182 bool CopyExtradataToDescription(JSContext* aCx, Span<const uint8_t>& aSrc, 183 OwningAllowSharedBufferSource& aDest) { 184 MOZ_ASSERT(!aSrc.IsEmpty()); 185 186 MOZ_ASSERT(aCx); 187 188 size_t lengthBytes = aSrc.Length(); 189 190 UniquePtr<uint8_t[], JS::FreePolicy> extradata( 191 js_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena, lengthBytes)); 192 193 if (!extradata) { 194 return false; 195 } 196 197 PodCopy(extradata.get(), aSrc.Elements(), lengthBytes); 198 199 JS::Rooted<JSObject*> data(aCx, JS::NewArrayBufferWithContents( 200 aCx, lengthBytes, std::move(extradata))); 201 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*data)); 202 return aDest.Init(aCx, value); 203 } 204 205 /* 206 * The following are utilities to convert between VideoColorSpace values to 207 * gfx's values. 208 */ 209 210 VideoColorSpaceInternal::VideoColorSpaceInternal( 211 const VideoColorSpaceInit& aColorSpaceInit) 212 : mFullRange(NullableToMaybe(aColorSpaceInit.mFullRange)), 213 mMatrix(NullableToMaybe(aColorSpaceInit.mMatrix)), 214 mPrimaries(NullableToMaybe(aColorSpaceInit.mPrimaries)), 215 mTransfer(NullableToMaybe(aColorSpaceInit.mTransfer)) {} 216 217 VideoColorSpaceInit VideoColorSpaceInternal::ToColorSpaceInit() const { 218 VideoColorSpaceInit init; 219 init.mFullRange = MaybeToNullable(mFullRange); 220 init.mMatrix = MaybeToNullable(mMatrix); 221 init.mPrimaries = MaybeToNullable(mPrimaries); 222 init.mTransfer = MaybeToNullable(mTransfer); 223 return init; 224 } 225 226 nsCString VideoColorSpaceInternal::ToString() const { 227 nsCString rv("VideoColorSpace"); 228 rv.AppendPrintf(" range: %s", 229 mFullRange ? mFullRange.value() ? "true" : "false" : "none"); 230 rv.AppendPrintf(" matrix: %s", 231 mMatrix ? GetEnumString(mMatrix.value()).get() : "none"); 232 rv.AppendPrintf( 233 " primaries: %s", 234 mPrimaries ? GetEnumString(mPrimaries.value()).get() : "none"); 235 rv.AppendPrintf(" transfer: %s", 236 mTransfer ? GetEnumString(mTransfer.value()).get() : "none"); 237 238 return rv; 239 } 240 241 gfx::ColorRange ToColorRange(bool aIsFullRange) { 242 return aIsFullRange ? gfx::ColorRange::FULL : gfx::ColorRange::LIMITED; 243 } 244 245 gfx::YUVColorSpace ToColorSpace(VideoMatrixCoefficients aMatrix) { 246 switch (aMatrix) { 247 case VideoMatrixCoefficients::Rgb: 248 return gfx::YUVColorSpace::Identity; 249 case VideoMatrixCoefficients::Bt709: 250 case VideoMatrixCoefficients::Bt470bg: 251 return gfx::YUVColorSpace::BT709; 252 case VideoMatrixCoefficients::Smpte170m: 253 return gfx::YUVColorSpace::BT601; 254 case VideoMatrixCoefficients::Bt2020_ncl: 255 return gfx::YUVColorSpace::BT2020; 256 } 257 MOZ_ASSERT_UNREACHABLE("unsupported VideoMatrixCoefficients"); 258 return gfx::YUVColorSpace::Default; 259 } 260 261 gfx::TransferFunction ToTransferFunction( 262 VideoTransferCharacteristics aTransfer) { 263 switch (aTransfer) { 264 case VideoTransferCharacteristics::Bt709: 265 case VideoTransferCharacteristics::Smpte170m: 266 return gfx::TransferFunction::BT709; 267 case VideoTransferCharacteristics::Iec61966_2_1: 268 return gfx::TransferFunction::SRGB; 269 case VideoTransferCharacteristics::Pq: 270 return gfx::TransferFunction::PQ; 271 case VideoTransferCharacteristics::Hlg: 272 return gfx::TransferFunction::HLG; 273 case VideoTransferCharacteristics::Linear: 274 return gfx::TransferFunction::Default; 275 } 276 MOZ_ASSERT_UNREACHABLE("unsupported VideoTransferCharacteristics"); 277 return gfx::TransferFunction::Default; 278 } 279 280 gfx::ColorSpace2 ToPrimaries(VideoColorPrimaries aPrimaries) { 281 switch (aPrimaries) { 282 case VideoColorPrimaries::Bt709: 283 return gfx::ColorSpace2::BT709; 284 case VideoColorPrimaries::Bt470bg: 285 return gfx::ColorSpace2::BT601_625; 286 case VideoColorPrimaries::Smpte170m: 287 return gfx::ColorSpace2::BT601_525; 288 case VideoColorPrimaries::Bt2020: 289 return gfx::ColorSpace2::BT2020; 290 case VideoColorPrimaries::Smpte432: 291 return gfx::ColorSpace2::DISPLAY_P3; 292 } 293 MOZ_ASSERT_UNREACHABLE("unsupported VideoTransferCharacteristics"); 294 return gfx::ColorSpace2::UNKNOWN; 295 } 296 297 bool ToFullRange(const gfx::ColorRange& aColorRange) { 298 return aColorRange == gfx::ColorRange::FULL; 299 } 300 301 Maybe<VideoMatrixCoefficients> ToMatrixCoefficients( 302 const gfx::YUVColorSpace& aColorSpace) { 303 switch (aColorSpace) { 304 case gfx::YUVColorSpace::BT601: 305 return Some(VideoMatrixCoefficients::Smpte170m); 306 case gfx::YUVColorSpace::BT709: 307 return Some(VideoMatrixCoefficients::Bt709); 308 case gfx::YUVColorSpace::BT2020: 309 return Some(VideoMatrixCoefficients::Bt2020_ncl); 310 case gfx::YUVColorSpace::Identity: 311 return Some(VideoMatrixCoefficients::Rgb); 312 } 313 MOZ_ASSERT_UNREACHABLE("unsupported gfx::YUVColorSpace"); 314 return Nothing(); 315 } 316 317 Maybe<VideoTransferCharacteristics> ToTransferCharacteristics( 318 const gfx::TransferFunction& aTransferFunction) { 319 switch (aTransferFunction) { 320 case gfx::TransferFunction::BT709: 321 return Some(VideoTransferCharacteristics::Bt709); 322 case gfx::TransferFunction::SRGB: 323 return Some(VideoTransferCharacteristics::Iec61966_2_1); 324 case gfx::TransferFunction::PQ: 325 return Some(VideoTransferCharacteristics::Pq); 326 case gfx::TransferFunction::HLG: 327 return Some(VideoTransferCharacteristics::Hlg); 328 } 329 MOZ_ASSERT_UNREACHABLE("unsupported gfx::TransferFunction"); 330 return Nothing(); 331 } 332 333 Maybe<VideoColorPrimaries> ToPrimaries(const gfx::ColorSpace2& aColorSpace) { 334 switch (aColorSpace) { 335 case gfx::ColorSpace2::UNKNOWN: 336 return Nothing(); 337 case gfx::ColorSpace2::DISPLAY_P3: 338 return Some(VideoColorPrimaries::Smpte432); 339 case gfx::ColorSpace2::BT601_525: 340 return Some(VideoColorPrimaries::Smpte170m); 341 case gfx::ColorSpace2::SRGB: 342 case gfx::ColorSpace2::BT709: 343 return Some(VideoColorPrimaries::Bt709); 344 case gfx::ColorSpace2::BT2020: 345 return Some(VideoColorPrimaries::Bt2020); 346 } 347 MOZ_ASSERT_UNREACHABLE("unsupported gfx::ColorSpace2"); 348 return Nothing(); 349 } 350 351 /* 352 * The following are utilities to convert from gfx's formats to 353 * VideoPixelFormats. 354 */ 355 356 Maybe<VideoPixelFormat> SurfaceFormatToVideoPixelFormat( 357 gfx::SurfaceFormat aFormat) { 358 switch (aFormat) { 359 case gfx::SurfaceFormat::B8G8R8A8: 360 return Some(VideoPixelFormat::BGRA); 361 case gfx::SurfaceFormat::B8G8R8X8: 362 return Some(VideoPixelFormat::BGRX); 363 case gfx::SurfaceFormat::R8G8B8A8: 364 return Some(VideoPixelFormat::RGBA); 365 case gfx::SurfaceFormat::R8G8B8X8: 366 return Some(VideoPixelFormat::RGBX); 367 case gfx::SurfaceFormat::YUV420: 368 return Some(VideoPixelFormat::I420); 369 case gfx::SurfaceFormat::YUV422P10: 370 return Some(VideoPixelFormat::I422P10); 371 case gfx::SurfaceFormat::NV12: 372 return Some(VideoPixelFormat::NV12); 373 374 default: 375 break; 376 } 377 return Nothing(); 378 } 379 380 Maybe<VideoPixelFormat> ImageBitmapFormatToVideoPixelFormat( 381 ImageBitmapFormat aFormat) { 382 switch (aFormat) { 383 case ImageBitmapFormat::RGBA32: 384 return Some(VideoPixelFormat::RGBA); 385 case ImageBitmapFormat::BGRA32: 386 return Some(VideoPixelFormat::BGRA); 387 case ImageBitmapFormat::YUV444P: 388 return Some(VideoPixelFormat::I444); 389 case ImageBitmapFormat::YUV422P: 390 return Some(VideoPixelFormat::I422); 391 case ImageBitmapFormat::YUV420P: 392 return Some(VideoPixelFormat::I420); 393 case ImageBitmapFormat::YUV420SP_NV12: 394 return Some(VideoPixelFormat::NV12); 395 default: 396 break; 397 } 398 return Nothing(); 399 } 400 401 bool IsOnAndroid() { 402 #if defined(ANDROID) 403 return true; 404 #else 405 return false; 406 #endif 407 } 408 409 bool IsOnMacOS() { 410 #if defined(XP_MACOSX) 411 return true; 412 #else 413 return false; 414 #endif 415 } 416 417 bool IsOnLinux() { 418 #if defined(XP_LINUX) 419 return true; 420 #else 421 return false; 422 #endif 423 } 424 425 template <typename T> 426 nsCString MaybeToString(const Maybe<T>& aMaybe) { 427 return nsPrintfCString( 428 "%s", aMaybe.isSome() ? ToString(aMaybe.value()).c_str() : "nothing"); 429 } 430 431 struct ConfigurationChangeToString { 432 nsCString operator()(const CodecChange& aCodecChange) { 433 return nsPrintfCString("Codec: %s", 434 NS_ConvertUTF16toUTF8(aCodecChange.get()).get()); 435 } 436 nsCString operator()(const DimensionsChange& aDimensionChange) { 437 return nsPrintfCString("Dimensions: %dx%d", aDimensionChange.get().width, 438 aDimensionChange.get().height); 439 } 440 nsCString operator()(const DisplayDimensionsChange& aDisplayDimensionChange) { 441 if (aDisplayDimensionChange.get().isNothing()) { 442 return nsPrintfCString("Display dimensions: nothing"); 443 } 444 gfx::IntSize displayDimensions = aDisplayDimensionChange.get().value(); 445 return nsPrintfCString("Dimensions: %dx%d", displayDimensions.width, 446 displayDimensions.height); 447 } 448 nsCString operator()(const BitrateChange& aBitrateChange) { 449 return nsPrintfCString("Bitrate: %skbps", 450 MaybeToString(aBitrateChange.get()).get()); 451 } 452 nsCString operator()(const FramerateChange& aFramerateChange) { 453 return nsPrintfCString("Framerate: %sHz", 454 MaybeToString(aFramerateChange.get()).get()); 455 } 456 nsCString operator()( 457 const HardwareAccelerationChange& aHardwareAccelerationChange) { 458 return nsPrintfCString( 459 "HW acceleration: %s", 460 dom::GetEnumString(aHardwareAccelerationChange.get()).get()); 461 } 462 nsCString operator()(const AlphaChange& aAlphaChange) { 463 return nsPrintfCString("Alpha: %s", 464 dom::GetEnumString(aAlphaChange.get()).get()); 465 } 466 nsCString operator()(const ScalabilityModeChange& aScalabilityModeChange) { 467 if (aScalabilityModeChange.get().isNothing()) { 468 return nsCString("Scalability mode: nothing"); 469 } 470 return nsPrintfCString( 471 "Scalability mode: %s", 472 NS_ConvertUTF16toUTF8(aScalabilityModeChange.get().value()).get()); 473 } 474 nsCString operator()(const BitrateModeChange& aBitrateModeChange) { 475 return nsPrintfCString("Bitrate mode: %s", 476 dom::GetEnumString(aBitrateModeChange.get()).get()); 477 } 478 nsCString operator()(const LatencyModeChange& aLatencyModeChange) { 479 return nsPrintfCString("Latency mode: %s", 480 dom::GetEnumString(aLatencyModeChange.get()).get()); 481 } 482 nsCString operator()(const ContentHintChange& aContentHintChange) { 483 return nsPrintfCString("Content hint: %s", 484 MaybeToString(aContentHintChange.get()).get()); 485 } 486 template <typename T> 487 nsCString operator()(const T& aNewBitrate) { 488 return nsPrintfCString("Not implemented"); 489 } 490 }; 491 492 nsCString WebCodecsConfigurationChangeList::ToString() const { 493 nsCString rv; 494 for (const WebCodecsEncoderConfigurationItem& change : mChanges) { 495 nsCString str = change.match(ConfigurationChangeToString()); 496 rv.AppendPrintf("- %s\n", str.get()); 497 } 498 return rv; 499 } 500 501 bool WebCodecsConfigurationChangeList::CanAttemptReconfigure() const { 502 for (const auto& change : mChanges) { 503 if (change.is<CodecChange>() || change.is<HardwareAccelerationChange>() || 504 change.is<AlphaChange>() || change.is<ScalabilityModeChange>()) { 505 return false; 506 } 507 } 508 return true; 509 } 510 511 RefPtr<EncoderConfigurationChangeList> 512 WebCodecsConfigurationChangeList::ToPEMChangeList() const { 513 auto rv = MakeRefPtr<EncoderConfigurationChangeList>(); 514 MOZ_ASSERT(CanAttemptReconfigure()); 515 for (const auto& change : mChanges) { 516 if (change.is<dom::DimensionsChange>()) { 517 rv->Push(mozilla::DimensionsChange(change.as<DimensionsChange>().get())); 518 } else if (change.is<dom::DisplayDimensionsChange>()) { 519 rv->Push(mozilla::DisplayDimensionsChange( 520 change.as<DisplayDimensionsChange>().get())); 521 } else if (change.is<dom::BitrateChange>()) { 522 rv->Push(mozilla::BitrateChange(change.as<BitrateChange>().get())); 523 } else if (change.is<FramerateChange>()) { 524 rv->Push(mozilla::FramerateChange(change.as<FramerateChange>().get())); 525 } else if (change.is<dom::BitrateModeChange>()) { 526 mozilla::BitrateMode mode; 527 if (change.as<dom::BitrateModeChange>().get() == 528 dom::VideoEncoderBitrateMode::Constant) { 529 mode = mozilla::BitrateMode::Constant; 530 } else if (change.as<BitrateModeChange>().get() == 531 dom::VideoEncoderBitrateMode::Variable) { 532 mode = mozilla::BitrateMode::Variable; 533 } else { 534 // Quantizer, not underlying support yet. 535 mode = mozilla::BitrateMode::Variable; 536 } 537 rv->Push(mozilla::BitrateModeChange(mode)); 538 } else if (change.is<LatencyModeChange>()) { 539 Usage usage; 540 if (change.as<LatencyModeChange>().get() == dom::LatencyMode::Quality) { 541 usage = Usage::Record; 542 } else { 543 usage = Usage::Realtime; 544 } 545 rv->Push(UsageChange(usage)); 546 } else if (change.is<ContentHintChange>()) { 547 rv->Push( 548 mozilla::ContentHintChange(change.as<ContentHintChange>().get())); 549 } 550 } 551 return rv.forget(); 552 } 553 554 RefPtr<TaskQueue> GetWebCodecsEncoderTaskQueue() { 555 return TaskQueue::Create( 556 GetMediaThreadPool(MediaThreadType::PLATFORM_ENCODER), 557 "WebCodecs encoding", false); 558 } 559 560 VideoColorSpaceInternal FallbackColorSpaceForVideoContent() { 561 // If we're unable to determine the color space, but we think this is video 562 // content (e.g. because it's in YUV or NV12 or something like that, 563 // consider it's in BT709). 564 // This is step 3 of 565 // https://w3c.github.io/webcodecs/#videoframe-pick-color-space 566 return VideoColorSpaceInternal(false, VideoMatrixCoefficients::Bt709, 567 VideoColorPrimaries::Bt709, 568 VideoTransferCharacteristics::Bt709); 569 } 570 VideoColorSpaceInternal FallbackColorSpaceForWebContent() { 571 // If we're unable to determine the color space, but we think this is from 572 // Web content (canvas, image, svg, etc.), consider it's in sRGB. 573 // This is step 2 of 574 // https://w3c.github.io/webcodecs/#videoframe-pick-color-space 575 return VideoColorSpaceInternal(true, VideoMatrixCoefficients::Rgb, 576 VideoColorPrimaries::Bt709, 577 VideoTransferCharacteristics::Iec61966_2_1); 578 } 579 580 Maybe<CodecType> CodecStringToCodecType(const nsAString& aCodecString) { 581 if (StringBeginsWith(aCodecString, u"av01"_ns)) { 582 return Some(CodecType::AV1); 583 } 584 if (StringBeginsWith(aCodecString, u"vp8"_ns)) { 585 return Some(CodecType::VP8); 586 } 587 if (StringBeginsWith(aCodecString, u"vp09"_ns)) { 588 return Some(CodecType::VP9); 589 } 590 if (StringBeginsWith(aCodecString, u"avc1"_ns) || 591 StringBeginsWith(aCodecString, u"avc3"_ns)) { 592 return Some(CodecType::H264); 593 } 594 if (StringBeginsWith(aCodecString, u"hev1"_ns) || 595 StringBeginsWith(aCodecString, u"hvc1"_ns)) { 596 return Some(CodecType::H265); 597 } 598 return Nothing(); 599 } 600 601 nsCString ConfigToString(const VideoDecoderConfig& aConfig) { 602 nsString rv; 603 604 auto internal = VideoDecoderConfigInternal::Create(aConfig); 605 606 return internal->ToString(); 607 } 608 609 bool IsSupportedVideoCodec(const nsAString& aCodec) { 610 LOG("IsSupportedVideoCodec: %s", NS_ConvertUTF16toUTF8(aCodec).get()); 611 // The only codec string accepted for vp8 is "vp8" 612 if (!IsVP9CodecString(aCodec) && !IsH264CodecString(aCodec) && 613 !IsAV1CodecString(aCodec) && !aCodec.EqualsLiteral("vp8")) { 614 if (IsH265CodecString(aCodec)) { 615 // H265 is supported only on MacOS in Nightly for now. 616 return StaticPrefs::dom_media_webcodecs_h265_enabled() && 617 StaticPrefs::media_hevc_enabled() && (IsOnMacOS() || IsOnLinux()); 618 } 619 return false; 620 } 621 622 // Gecko allows codec string starts with vp9 or av1 but Webcodecs requires to 623 // starts with av01 and vp09. 624 // https://w3c.github.io/webcodecs/codec_registry.html. 625 if (StringBeginsWith(aCodec, u"vp9"_ns) || 626 StringBeginsWith(aCodec, u"av1"_ns)) { 627 return false; 628 } 629 630 return true; 631 } 632 633 nsCString ConvertCodecName(const nsCString& aContainer, 634 const nsCString& aCodec) { 635 if (!aContainer.EqualsLiteral("x-wav")) { 636 return aCodec; 637 } 638 639 // https://www.rfc-editor.org/rfc/rfc2361.txt 640 if (aCodec.EqualsLiteral("ulaw")) { 641 return nsCString("7"); 642 } 643 if (aCodec.EqualsLiteral("alaw")) { 644 return nsCString("6"); 645 } 646 if (aCodec.Find("f32")) { 647 return nsCString("3"); 648 } 649 // Linear PCM 650 return nsCString("1"); 651 } 652 653 bool IsSupportedAudioCodec(const nsAString& aCodec) { 654 LOG("IsSupportedAudioCodec: %s", NS_ConvertUTF16toUTF8(aCodec).get()); 655 return aCodec.EqualsLiteral("flac") || aCodec.EqualsLiteral("mp3") || 656 IsAACCodecString(aCodec) || aCodec.EqualsLiteral("vorbis") || 657 aCodec.EqualsLiteral("opus") || aCodec.EqualsLiteral("ulaw") || 658 aCodec.EqualsLiteral("alaw") || aCodec.EqualsLiteral("pcm-u8") || 659 aCodec.EqualsLiteral("pcm-s16") || aCodec.EqualsLiteral("pcm-s24") || 660 aCodec.EqualsLiteral("pcm-s32") || aCodec.EqualsLiteral("pcm-f32"); 661 } 662 663 uint32_t BytesPerSamples(const mozilla::dom::AudioSampleFormat& aFormat) { 664 switch (aFormat) { 665 case AudioSampleFormat::U8: 666 case AudioSampleFormat::U8_planar: 667 return sizeof(uint8_t); 668 case AudioSampleFormat::S16: 669 case AudioSampleFormat::S16_planar: 670 return sizeof(int16_t); 671 case AudioSampleFormat::S32: 672 case AudioSampleFormat::F32: 673 case AudioSampleFormat::S32_planar: 674 case AudioSampleFormat::F32_planar: 675 return sizeof(float); 676 } 677 MOZ_ASSERT_UNREACHABLE("Invalid enum value"); 678 return 0; 679 } 680 681 template <typename T> 682 void ApplyResistFingerprintingImpl(const RefPtr<T>& aConfig, 683 nsIGlobalObject* aGlobal) { 684 // When resisting fingerprinting, don't reveal information about the host 685 // hardware. 686 if (nsContentUtils::ShouldResistFingerprinting( 687 aGlobal, RFPTarget::MediaCapabilities)) { 688 if (aConfig->mHardwareAcceleration != HardwareAcceleration::No_preference) { 689 LOGW( 690 "Resist fingerprinting (MediaCapabilities) enabled, overriding " 691 "hardware preference"); 692 aConfig->mHardwareAcceleration = HardwareAcceleration::No_preference; 693 } 694 } 695 } 696 697 void ApplyResistFingerprintingIfNeeded( 698 const RefPtr<VideoEncoderConfigInternal>& aConfig, 699 nsIGlobalObject* aGlobal) { 700 ApplyResistFingerprintingImpl(aConfig, aGlobal); 701 } 702 703 void ApplyResistFingerprintingIfNeeded( 704 const RefPtr<VideoDecoderConfigInternal>& aConfig, 705 nsIGlobalObject* aGlobal) { 706 ApplyResistFingerprintingImpl(aConfig, aGlobal); 707 } 708 709 } // namespace mozilla::dom