tor-browser

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

VideoEncoder.cpp (23215B)


      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 "mozilla/dom/VideoEncoder.h"
      8 
      9 #include "EncoderConfig.h"
     10 #include "EncoderTraits.h"
     11 #include "ImageContainer.h"
     12 #include "VideoUtils.h"
     13 #include "mozilla/Assertions.h"
     14 #include "mozilla/Logging.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/dom/EncodedVideoChunk.h"
     17 #include "mozilla/dom/EncodedVideoChunkBinding.h"
     18 #include "mozilla/dom/ImageUtils.h"
     19 #include "mozilla/dom/Promise.h"
     20 #include "mozilla/dom/VideoColorSpace.h"
     21 #include "mozilla/dom/VideoColorSpaceBinding.h"
     22 #include "mozilla/dom/VideoEncoderBinding.h"
     23 #include "mozilla/dom/VideoFrame.h"
     24 #include "mozilla/dom/VideoFrameBinding.h"
     25 #include "mozilla/dom/WebCodecsUtils.h"
     26 #include "nsIGlobalObject.h"
     27 #include "nsPrintfCString.h"
     28 #include "nsRFPService.h"
     29 
     30 extern mozilla::LazyLogModule gWebCodecsLog;
     31 
     32 namespace mozilla::dom {
     33 
     34 #ifdef LOG_INTERNAL
     35 #  undef LOG_INTERNAL
     36 #endif  // LOG_INTERNAL
     37 #define LOG_INTERNAL(level, msg, ...) \
     38  MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
     39 
     40 #ifdef LOG
     41 #  undef LOG
     42 #endif  // LOG
     43 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
     44 
     45 #ifdef LOGW
     46 #  undef LOGW
     47 #endif  // LOGW
     48 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
     49 
     50 #ifdef LOGE
     51 #  undef LOGE
     52 #endif  // LOGE
     53 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
     54 
     55 #ifdef LOGV
     56 #  undef LOGV
     57 #endif  // LOGV
     58 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
     59 
     60 NS_IMPL_CYCLE_COLLECTION_INHERITED(VideoEncoder, DOMEventTargetHelper,
     61                                   mErrorCallback, mOutputCallback)
     62 NS_IMPL_ADDREF_INHERITED(VideoEncoder, DOMEventTargetHelper)
     63 NS_IMPL_RELEASE_INHERITED(VideoEncoder, DOMEventTargetHelper)
     64 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VideoEncoder)
     65 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     66 
     67 VideoEncoderConfigInternal::VideoEncoderConfigInternal(
     68    const nsAString& aCodec, uint32_t aWidth, uint32_t aHeight,
     69    Maybe<uint32_t>&& aDisplayWidth, Maybe<uint32_t>&& aDisplayHeight,
     70    Maybe<uint32_t>&& aBitrate, Maybe<double>&& aFramerate,
     71    const HardwareAcceleration& aHardwareAcceleration,
     72    const AlphaOption& aAlpha, Maybe<nsString>&& aScalabilityMode,
     73    const VideoEncoderBitrateMode& aBitrateMode,
     74    const LatencyMode& aLatencyMode, Maybe<nsString>&& aContentHint)
     75    : mCodec(aCodec),
     76      mWidth(aWidth),
     77      mHeight(aHeight),
     78      mDisplayWidth(std::move(aDisplayWidth)),
     79      mDisplayHeight(std::move(aDisplayHeight)),
     80      mBitrate(std::move(aBitrate)),
     81      mFramerate(std::move(aFramerate)),
     82      mHardwareAcceleration(aHardwareAcceleration),
     83      mAlpha(aAlpha),
     84      mScalabilityMode(std::move(aScalabilityMode)),
     85      mBitrateMode(aBitrateMode),
     86      mLatencyMode(aLatencyMode),
     87      mContentHint(std::move(aContentHint)) {}
     88 
     89 VideoEncoderConfigInternal::VideoEncoderConfigInternal(
     90    const VideoEncoderConfigInternal& aConfig)
     91    : mCodec(aConfig.mCodec),
     92      mWidth(aConfig.mWidth),
     93      mHeight(aConfig.mHeight),
     94      mDisplayWidth(aConfig.mDisplayWidth),
     95      mDisplayHeight(aConfig.mDisplayHeight),
     96      mBitrate(aConfig.mBitrate),
     97      mFramerate(aConfig.mFramerate),
     98      mHardwareAcceleration(aConfig.mHardwareAcceleration),
     99      mAlpha(aConfig.mAlpha),
    100      mScalabilityMode(aConfig.mScalabilityMode),
    101      mBitrateMode(aConfig.mBitrateMode),
    102      mLatencyMode(aConfig.mLatencyMode),
    103      mContentHint(aConfig.mContentHint),
    104      mAvc(aConfig.mAvc) {}
    105 
    106 VideoEncoderConfigInternal::VideoEncoderConfigInternal(
    107    const VideoEncoderConfig& aConfig)
    108    : mCodec(aConfig.mCodec),
    109      mWidth(aConfig.mWidth),
    110      mHeight(aConfig.mHeight),
    111      mDisplayWidth(OptionalToMaybe(aConfig.mDisplayWidth)),
    112      mDisplayHeight(OptionalToMaybe(aConfig.mDisplayHeight)),
    113      mBitrate(OptionalToMaybe(aConfig.mBitrate)),
    114      mFramerate(OptionalToMaybe(aConfig.mFramerate)),
    115      mHardwareAcceleration(aConfig.mHardwareAcceleration),
    116      mAlpha(aConfig.mAlpha),
    117      mScalabilityMode(OptionalToMaybe(aConfig.mScalabilityMode)),
    118      mBitrateMode(aConfig.mBitrateMode),
    119      mLatencyMode(aConfig.mLatencyMode),
    120      mContentHint(OptionalToMaybe(aConfig.mContentHint)),
    121      mAvc(OptionalToMaybe(aConfig.mAvc)) {}
    122 
    123 nsCString VideoEncoderConfigInternal::ToString() const {
    124  nsCString rv;
    125 
    126  rv.AppendLiteral("Codec: ");
    127  rv.Append(NS_ConvertUTF16toUTF8(mCodec));
    128  rv.AppendPrintf(" [%" PRIu32 "x%" PRIu32 "]", mWidth, mHeight);
    129  if (mDisplayWidth.isSome()) {
    130    rv.AppendPrintf(", display[%" PRIu32 "x%" PRIu32 "]", mDisplayWidth.value(),
    131                    mDisplayHeight.value());
    132  }
    133  if (mBitrate.isSome()) {
    134    rv.AppendPrintf(", %" PRIu32 "kbps", mBitrate.value());
    135  }
    136  if (mFramerate.isSome()) {
    137    rv.AppendPrintf(", %lfHz", mFramerate.value());
    138  }
    139  rv.AppendPrintf(", hw: %s", GetEnumString(mHardwareAcceleration).get());
    140  rv.AppendPrintf(", alpha: %s", GetEnumString(mAlpha).get());
    141  if (mScalabilityMode.isSome()) {
    142    rv.AppendPrintf(", scalability mode: %s",
    143                    NS_ConvertUTF16toUTF8(mScalabilityMode.value()).get());
    144  }
    145  rv.AppendPrintf(", bitrate mode: %s", GetEnumString(mBitrateMode).get());
    146  rv.AppendPrintf(", latency mode: %s", GetEnumString(mLatencyMode).get());
    147  if (mContentHint.isSome()) {
    148    rv.AppendPrintf(", content hint: %s",
    149                    NS_ConvertUTF16toUTF8(mContentHint.value()).get());
    150  }
    151  if (mAvc.isSome()) {
    152    rv.AppendPrintf(", avc-specific: %s", GetEnumString(mAvc->mFormat).get());
    153  }
    154 
    155  return rv;
    156 }
    157 
    158 template <typename T>
    159 bool MaybeAreEqual(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
    160  if (aLHS.isSome() && aRHS.isSome()) {
    161    return aLHS.value() == aRHS.value();
    162  }
    163  if (aLHS.isNothing() && aRHS.isNothing()) {
    164    return true;
    165  }
    166  return false;
    167 }
    168 
    169 bool VideoEncoderConfigInternal::Equals(
    170    const VideoEncoderConfigInternal& aOther) const {
    171  bool sameCodecSpecific = true;
    172  if ((mAvc.isSome() && aOther.mAvc.isSome() &&
    173       mAvc->mFormat != aOther.mAvc->mFormat) ||
    174      mAvc.isSome() != aOther.mAvc.isSome()) {
    175    sameCodecSpecific = false;
    176  }
    177  return mCodec.Equals(aOther.mCodec) && mWidth == aOther.mWidth &&
    178         mHeight == aOther.mHeight &&
    179         MaybeAreEqual(mDisplayWidth, aOther.mDisplayWidth) &&
    180         MaybeAreEqual(mDisplayHeight, aOther.mDisplayHeight) &&
    181         MaybeAreEqual(mBitrate, aOther.mBitrate) &&
    182         MaybeAreEqual(mFramerate, aOther.mFramerate) &&
    183         mHardwareAcceleration == aOther.mHardwareAcceleration &&
    184         mAlpha == aOther.mAlpha &&
    185         MaybeAreEqual(mScalabilityMode, aOther.mScalabilityMode) &&
    186         mBitrateMode == aOther.mBitrateMode &&
    187         mLatencyMode == aOther.mLatencyMode &&
    188         MaybeAreEqual(mContentHint, aOther.mContentHint) && sameCodecSpecific;
    189 }
    190 
    191 bool VideoEncoderConfigInternal::CanReconfigure(
    192    const VideoEncoderConfigInternal& aOther) const {
    193  return mCodec.Equals(aOther.mCodec) &&
    194         mHardwareAcceleration == aOther.mHardwareAcceleration;
    195 }
    196 
    197 EncoderConfig VideoEncoderConfigInternal::ToEncoderConfig() const {
    198  Usage usage;
    199  if (mLatencyMode == LatencyMode::Quality) {
    200    usage = Usage::Record;
    201  } else {
    202    usage = Usage::Realtime;
    203  }
    204  HardwarePreference hwPref = HardwarePreference::None;
    205  if (mHardwareAcceleration ==
    206      mozilla::dom::HardwareAcceleration::Prefer_hardware) {
    207    hwPref = HardwarePreference::RequireHardware;
    208  } else if (mHardwareAcceleration ==
    209             mozilla::dom::HardwareAcceleration::Prefer_software) {
    210    hwPref = HardwarePreference::RequireSoftware;
    211  }
    212  CodecType codecType;
    213  auto maybeCodecType = CodecStringToCodecType(mCodec);
    214  if (maybeCodecType.isSome()) {
    215    codecType = maybeCodecType.value();
    216  } else {
    217    MOZ_CRASH("The string should always contain a valid codec at this point.");
    218  }
    219  EncoderConfig::CodecSpecific specific{void_t{}};
    220  if (codecType == CodecType::H264) {
    221    uint8_t profile, constraints;
    222    H264_LEVEL level;
    223    H264BitStreamFormat format;
    224    if (mAvc) {
    225      format = mAvc->mFormat == AvcBitstreamFormat::Annexb
    226                   ? H264BitStreamFormat::ANNEXB
    227                   : H264BitStreamFormat::AVC;
    228    } else {
    229      format = H264BitStreamFormat::AVC;
    230    }
    231    if (ExtractH264CodecDetails(mCodec, profile, constraints, level,
    232                                H264CodecStringStrictness::Strict)) {
    233      if (profile == H264_PROFILE_BASE || profile == H264_PROFILE_MAIN ||
    234          profile == H264_PROFILE_EXTENDED || profile == H264_PROFILE_HIGH) {
    235        specific.emplace<H264Specific>(static_cast<H264_PROFILE>(profile),
    236                                       level, format);
    237      }
    238    }
    239  }
    240  uint8_t numTemporalLayers = 1;
    241  ScalabilityMode scalabilityMode;
    242  if (mScalabilityMode) {
    243    if (mScalabilityMode->EqualsLiteral("L1T2")) {
    244      scalabilityMode = ScalabilityMode::L1T2;
    245      numTemporalLayers = 2;
    246    } else if (mScalabilityMode->EqualsLiteral("L1T3")) {
    247      scalabilityMode = ScalabilityMode::L1T3;
    248      numTemporalLayers = 3;
    249    } else {
    250      scalabilityMode = ScalabilityMode::None;
    251    }
    252  } else {
    253    scalabilityMode = ScalabilityMode::None;
    254  }
    255  // Only for vp9, not vp8
    256  if (codecType == CodecType::VP9) {
    257    uint8_t profile, level, bitdepth, chromasubsampling;
    258    mozilla::VideoColorSpace colorspace;
    259    DebugOnly<bool> rv = ExtractVPXCodecDetails(
    260        mCodec, profile, level, bitdepth, chromasubsampling, colorspace);
    261 #ifdef DEBUG
    262    if (!rv) {
    263      LOGE("Error extracting VPX codec details, non fatal");
    264    }
    265 #endif
    266    specific.emplace<VP9Specific>(
    267        VPXComplexity::Normal, /* Complexity */
    268        true,                  /* Resilience */
    269        numTemporalLayers,     /* Number of temporal layers */
    270        true,                  /* Denoising */
    271        false,                 /* Auto resize */
    272        false,                 /* Frame dropping */
    273        true,                  /* Adaptive Qp */
    274        1,                     /* Number of spatial layers */
    275        false                  /* Flexible */
    276    );
    277  }
    278  // For real-time usage, typically used in web conferencing, YUV420 is the most
    279  // common format and is set as the default. Otherwise, Gecko's preferred
    280  // format, BGRA, is assumed.
    281  EncoderConfig::SampleFormat format(usage == Usage::Realtime
    282                                         ? dom::ImageBitmapFormat::YUV420P
    283                                         : dom::ImageBitmapFormat::BGRA32);
    284  if (usage == Usage::Realtime) {
    285    format.mColorSpace.mRange.emplace(gfx::ColorRange::LIMITED);
    286  }
    287  return EncoderConfig(codecType, {mWidth, mHeight}, usage, format,
    288                       SaturatingCast<uint32_t>(mFramerate.refOr(0.f)), 0,
    289                       mBitrate.refOr(0), 0, 0,
    290                       (mBitrateMode == VideoEncoderBitrateMode::Constant)
    291                           ? mozilla::BitrateMode::Constant
    292                           : mozilla::BitrateMode::Variable,
    293                       hwPref, scalabilityMode, specific);
    294 }
    295 already_AddRefed<WebCodecsConfigurationChangeList>
    296 VideoEncoderConfigInternal::Diff(
    297    const VideoEncoderConfigInternal& aOther) const {
    298  auto list = MakeRefPtr<WebCodecsConfigurationChangeList>();
    299  if (!mCodec.Equals(aOther.mCodec)) {
    300    list->Push(CodecChange{aOther.mCodec});
    301  }
    302  // Both must always be present, when a `VideoEncoderConfig` is passed to
    303  // `configure`.
    304  if (mWidth != aOther.mWidth || mHeight != aOther.mHeight) {
    305    list->Push(DimensionsChange{gfx::IntSize{aOther.mWidth, aOther.mHeight}});
    306  }
    307  // Similarly, both must always be present, when a `VideoEncoderConfig` is
    308  // passed to `configure`.
    309  if (!MaybeAreEqual(mDisplayWidth, aOther.mDisplayWidth) ||
    310      !MaybeAreEqual(mDisplayHeight, aOther.mDisplayHeight)) {
    311    Maybe<gfx::IntSize> displaySize =
    312        aOther.mDisplayWidth.isSome()
    313            ? Some(gfx::IntSize{aOther.mDisplayWidth.value(),
    314                                aOther.mDisplayHeight.value()})
    315            : Nothing();
    316    list->Push(DisplayDimensionsChange{displaySize});
    317  }
    318  if (!MaybeAreEqual(mBitrate, aOther.mBitrate)) {
    319    list->Push(BitrateChange{aOther.mBitrate});
    320  }
    321  if (!MaybeAreEqual(mFramerate, aOther.mFramerate)) {
    322    list->Push(FramerateChange{aOther.mFramerate});
    323  }
    324  if (mHardwareAcceleration != aOther.mHardwareAcceleration) {
    325    list->Push(HardwareAccelerationChange{aOther.mHardwareAcceleration});
    326  }
    327  if (mAlpha != aOther.mAlpha) {
    328    list->Push(AlphaChange{aOther.mAlpha});
    329  }
    330  if (!MaybeAreEqual(mScalabilityMode, aOther.mScalabilityMode)) {
    331    list->Push(ScalabilityModeChange{aOther.mScalabilityMode});
    332  }
    333  if (mBitrateMode != aOther.mBitrateMode) {
    334    list->Push(BitrateModeChange{aOther.mBitrateMode});
    335  }
    336  if (mLatencyMode != aOther.mLatencyMode) {
    337    list->Push(LatencyModeChange{aOther.mLatencyMode});
    338  }
    339  if (!MaybeAreEqual(mContentHint, aOther.mContentHint)) {
    340    list->Push(ContentHintChange{aOther.mContentHint});
    341  }
    342  return list.forget();
    343 }
    344 
    345 // https://w3c.github.io/webcodecs/#check-configuration-support
    346 static bool CanEncode(const RefPtr<VideoEncoderConfigInternal>& aConfig,
    347                      nsIGlobalObject* aGlobal) {
    348  // TODO: Enable WebCodecs on Android (Bug 1840508)
    349  if (IsOnAndroid()) {
    350    return false;
    351  }
    352  if (!IsSupportedVideoCodec(aConfig->mCodec)) {
    353    return false;
    354  }
    355  if (aConfig->mScalabilityMode.isSome()) {
    356    // Check if ScalabilityMode string is valid.
    357    if (!aConfig->mScalabilityMode->EqualsLiteral("L1T2") &&
    358        !aConfig->mScalabilityMode->EqualsLiteral("L1T3")) {
    359      LOGE("Scalability mode %s not supported for codec: %s",
    360           NS_ConvertUTF16toUTF8(aConfig->mScalabilityMode.value()).get(),
    361           NS_ConvertUTF16toUTF8(aConfig->mCodec).get());
    362      return false;
    363    }
    364  }
    365 
    366  ApplyResistFingerprintingIfNeeded(aConfig, aGlobal);
    367 
    368  return EncoderSupport::Supports(aConfig);
    369 }
    370 
    371 static Result<Ok, nsresult> CloneConfiguration(
    372    VideoEncoderConfig& aDest, JSContext* aCx,
    373    const VideoEncoderConfig& aConfig) {
    374  nsCString errorMessage;
    375  MOZ_ASSERT(VideoEncoderTraits::Validate(aConfig, errorMessage));
    376 
    377  aDest.mCodec = aConfig.mCodec;
    378  aDest.mWidth = aConfig.mWidth;
    379  aDest.mHeight = aConfig.mHeight;
    380  aDest.mAlpha = aConfig.mAlpha;
    381  if (aConfig.mBitrate.WasPassed()) {
    382    aDest.mBitrate.Construct(aConfig.mBitrate.Value());
    383  }
    384  aDest.mBitrateMode = aConfig.mBitrateMode;
    385  if (aConfig.mContentHint.WasPassed()) {
    386    aDest.mContentHint.Construct(aConfig.mContentHint.Value());
    387  }
    388  if (aConfig.mDisplayWidth.WasPassed()) {
    389    aDest.mDisplayWidth.Construct(aConfig.mDisplayWidth.Value());
    390  }
    391  if (aConfig.mDisplayHeight.WasPassed()) {
    392    aDest.mDisplayHeight.Construct(aConfig.mDisplayHeight.Value());
    393  }
    394  if (aConfig.mFramerate.WasPassed()) {
    395    aDest.mFramerate.Construct(aConfig.mFramerate.Value());
    396  }
    397  aDest.mHardwareAcceleration = aConfig.mHardwareAcceleration;
    398  aDest.mLatencyMode = aConfig.mLatencyMode;
    399  if (aConfig.mScalabilityMode.WasPassed()) {
    400    aDest.mScalabilityMode.Construct(aConfig.mScalabilityMode.Value());
    401  }
    402 
    403  // AVC specific
    404  if (aConfig.mAvc.WasPassed()) {
    405    aDest.mAvc.Construct(aConfig.mAvc.Value());
    406  }
    407 
    408  return Ok();
    409 }
    410 
    411 /* static */
    412 bool VideoEncoderTraits::IsSupported(
    413    const VideoEncoderConfigInternal& aConfig) {
    414  return CanEncode(MakeRefPtr<VideoEncoderConfigInternal>(aConfig), nullptr);
    415 }
    416 
    417 // https://w3c.github.io/webcodecs/#valid-videoencoderconfig
    418 /* static */
    419 bool VideoEncoderTraits::Validate(const VideoEncoderConfig& aConfig,
    420                                  nsCString& aErrorMessage) {
    421  Maybe<nsString> codec = ParseCodecString(aConfig.mCodec);
    422  // 1.
    423  if (!codec || codec->IsEmpty()) {
    424    aErrorMessage.AssignLiteral(
    425        "Invalid VideoEncoderConfig: invalid codec string");
    426    LOGE("%s", aErrorMessage.get());
    427    return false;
    428  }
    429 
    430  // 2.
    431  if (aConfig.mWidth == 0 || aConfig.mHeight == 0) {
    432    aErrorMessage.AppendPrintf("Invalid VideoEncoderConfig: %s equal to 0",
    433                               aConfig.mWidth == 0 ? "width" : "height");
    434    LOGE("%s", aErrorMessage.get());
    435    return false;
    436  }
    437 
    438  // 3.
    439  if (aConfig.mDisplayWidth.WasPassed() && aConfig.mDisplayWidth.Value() == 0) {
    440    aErrorMessage.AssignLiteral(
    441        "Invalid VideoEncoderConfig: displayWidth equal to 0");
    442    LOGE("%s", aErrorMessage.get());
    443    return false;
    444  }
    445  if (aConfig.mDisplayHeight.WasPassed() &&
    446      aConfig.mDisplayHeight.Value() == 0) {
    447    aErrorMessage.AssignLiteral(
    448        "Invalid VideoEncoderConfig: displayHeight equal to 0");
    449    LOGE("%s", aErrorMessage.get());
    450    return false;
    451  }
    452 
    453  // https://github.com/w3c/webcodecs/issues/816
    454  if ((aConfig.mBitrate.WasPassed() && aConfig.mBitrate.Value() == 0)) {
    455    aErrorMessage.AssignLiteral(
    456        "Invalid VideoEncoderConfig: bitrate equal to 0");
    457    LOGE("%s", aErrorMessage.get());
    458    return false;
    459  }
    460 
    461  return true;
    462 }
    463 
    464 /* static */
    465 RefPtr<VideoEncoderConfigInternal> VideoEncoderTraits::CreateConfigInternal(
    466    const VideoEncoderConfig& aConfig) {
    467  return MakeRefPtr<VideoEncoderConfigInternal>(aConfig);
    468 }
    469 
    470 /* static */
    471 RefPtr<mozilla::VideoData> VideoEncoderTraits::CreateInputInternal(
    472    const dom::VideoFrame& aInput,
    473    const dom::VideoEncoderEncodeOptions& aOptions) {
    474  media::TimeUnit duration =
    475      aInput.GetDuration().IsNull()
    476          ? media::TimeUnit::Zero()
    477          : media::TimeUnit::FromMicroseconds(
    478                AssertedCast<int64_t>(aInput.GetDuration().Value()));
    479  media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aInput.Timestamp());
    480  return VideoData::CreateFromImage(
    481      gfx::IntSize{aInput.DisplayWidth(), aInput.DisplayHeight()},
    482      0 /* bytestream offset */, pts, duration, aInput.GetImage(),
    483      aOptions.mKeyFrame, pts);
    484 }
    485 
    486 /*
    487 * Below are VideoEncoder implementation
    488 */
    489 
    490 VideoEncoder::VideoEncoder(
    491    nsIGlobalObject* aParent, RefPtr<WebCodecsErrorCallback>&& aErrorCallback,
    492    RefPtr<EncodedVideoChunkOutputCallback>&& aOutputCallback)
    493    : EncoderTemplate(aParent, std::move(aErrorCallback),
    494                      std::move(aOutputCallback)) {
    495  MOZ_ASSERT(mErrorCallback);
    496  MOZ_ASSERT(mOutputCallback);
    497  LOG("VideoEncoder %p ctor", this);
    498 }
    499 
    500 VideoEncoder::~VideoEncoder() {
    501  LOG("VideoEncoder %p dtor", this);
    502  (void)ResetInternal(NS_ERROR_DOM_ABORT_ERR);
    503 }
    504 
    505 JSObject* VideoEncoder::WrapObject(JSContext* aCx,
    506                                   JS::Handle<JSObject*> aGivenProto) {
    507  AssertIsOnOwningThread();
    508 
    509  return VideoEncoder_Binding::Wrap(aCx, this, aGivenProto);
    510 }
    511 
    512 // https://w3c.github.io/webcodecs/#dom-videoencoder-videoencoder
    513 /* static */
    514 already_AddRefed<VideoEncoder> VideoEncoder::Constructor(
    515    const GlobalObject& aGlobal, const VideoEncoderInit& aInit,
    516    ErrorResult& aRv) {
    517  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    518  if (!global) {
    519    aRv.Throw(NS_ERROR_FAILURE);
    520    return nullptr;
    521  }
    522 
    523  return MakeAndAddRef<VideoEncoder>(
    524      global.get(), RefPtr<WebCodecsErrorCallback>(aInit.mError),
    525      RefPtr<EncodedVideoChunkOutputCallback>(aInit.mOutput));
    526 }
    527 
    528 // https://w3c.github.io/webcodecs/#dom-videoencoder-isconfigsupported
    529 /* static */
    530 already_AddRefed<Promise> VideoEncoder::IsConfigSupported(
    531    const GlobalObject& aGlobal, const VideoEncoderConfig& aConfig,
    532    ErrorResult& aRv) {
    533  LOG("VideoEncoder::IsConfigSupported, config: %s",
    534      NS_ConvertUTF16toUTF8(aConfig.mCodec).get());
    535 
    536  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    537  if (!global) {
    538    aRv.Throw(NS_ERROR_FAILURE);
    539    return nullptr;
    540  }
    541 
    542  RefPtr<Promise> p = Promise::Create(global.get(), aRv);
    543  if (NS_WARN_IF(aRv.Failed())) {
    544    return p.forget();
    545  }
    546 
    547  nsCString errorMessage;
    548  if (!VideoEncoderTraits::Validate(aConfig, errorMessage)) {
    549    p->MaybeRejectWithTypeError(nsPrintfCString(
    550        "IsConfigSupported: config is invalid: %s", errorMessage.get()));
    551    return p.forget();
    552  }
    553 
    554  // TODO: Move the following works to another thread to unblock the current
    555  // thread, as what spec suggests.
    556 
    557  VideoEncoderConfig config;
    558  auto r = CloneConfiguration(config, aGlobal.Context(), aConfig);
    559  if (r.isErr()) {
    560    nsresult e = r.unwrapErr();
    561    LOGE("Failed to clone VideoEncoderConfig. Error: 0x%08" PRIx32,
    562         static_cast<uint32_t>(e));
    563    p->MaybeRejectWithTypeError("Failed to clone VideoEncoderConfig");
    564    aRv.Throw(e);
    565    return p.forget();
    566  }
    567 
    568  bool canEncode =
    569      CanEncode(MakeRefPtr<VideoEncoderConfigInternal>(config), global);
    570  VideoEncoderSupport s;
    571  s.mConfig.Construct(std::move(config));
    572  s.mSupported.Construct(canEncode);
    573 
    574  p->MaybeResolve(s);
    575  return p.forget();
    576 }
    577 
    578 RefPtr<EncodedVideoChunk> VideoEncoder::EncodedDataToOutputType(
    579    nsIGlobalObject* aGlobalObject, const RefPtr<MediaRawData>& aData) {
    580  AssertIsOnOwningThread();
    581 
    582  MOZ_RELEASE_ASSERT(aData->mType == MediaData::Type::RAW_DATA);
    583  // Package into an EncodedVideoChunk
    584  auto buffer =
    585      MakeRefPtr<MediaAlignedByteBuffer>(aData->Data(), aData->Size());
    586  auto encodedVideoChunk = MakeRefPtr<EncodedVideoChunk>(
    587      aGlobalObject, buffer.forget(),
    588      aData->mKeyframe ? EncodedVideoChunkType::Key
    589                       : EncodedVideoChunkType::Delta,
    590      aData->mTime.ToMicroseconds(),
    591      aData->mDuration.IsZero() ? Nothing()
    592                                : Some(aData->mDuration.ToMicroseconds()));
    593  return encodedVideoChunk;
    594 }
    595 
    596 void VideoEncoder::EncoderConfigToDecoderConfig(
    597    JSContext* aCx, const RefPtr<MediaRawData>& aRawData,
    598    const VideoEncoderConfigInternal& aSrcConfig,
    599    VideoDecoderConfig& aDestConfig) const {
    600  MOZ_ASSERT(aCx);
    601 
    602  aDestConfig.mCodec = aSrcConfig.mCodec;
    603  aDestConfig.mCodedHeight.Construct(aSrcConfig.mHeight);
    604  aDestConfig.mCodedWidth.Construct(aSrcConfig.mWidth);
    605 
    606  // Colorspace is mandatory when outputing a decoder config after encode
    607  RootedDictionary<VideoColorSpaceInit> colorSpace(aCx);
    608  colorSpace.mFullRange.SetValue(false);
    609  colorSpace.mMatrix.SetValue(VideoMatrixCoefficients::Bt709);
    610  colorSpace.mPrimaries.SetValue(VideoColorPrimaries::Bt709);
    611  colorSpace.mTransfer.SetValue(VideoTransferCharacteristics::Bt709);
    612  aDestConfig.mColorSpace.Construct(std::move(colorSpace));
    613 
    614  if (aRawData->mExtraData && !aRawData->mExtraData->IsEmpty()) {
    615    Span<const uint8_t> description(aRawData->mExtraData->Elements(),
    616                                    aRawData->mExtraData->Length());
    617    if (!CopyExtradataToDescription(aCx, description,
    618                                    aDestConfig.mDescription.Construct())) {
    619      LOGE("Failed to copy extra data");
    620    }
    621  }
    622 
    623  if (aSrcConfig.mDisplayHeight) {
    624    aDestConfig.mDisplayAspectHeight.Construct(
    625        aSrcConfig.mDisplayHeight.value());
    626  }
    627  if (aSrcConfig.mDisplayWidth) {
    628    aDestConfig.mDisplayAspectWidth.Construct(aSrcConfig.mDisplayWidth.value());
    629  }
    630  aDestConfig.mHardwareAcceleration = aSrcConfig.mHardwareAcceleration;
    631 }
    632 
    633 #undef LOG
    634 #undef LOGW
    635 #undef LOGE
    636 #undef LOGV
    637 #undef LOG_INTERNAL
    638 
    639 }  // namespace mozilla::dom