tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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