tor-browser

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

WMFDataEncoderUtils.cpp (8887B)


      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 "WMFDataEncoderUtils.h"
      8 
      9 #include "EncoderConfig.h"
     10 #include "MFTEncoder.h"
     11 #include "MediaData.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/gfx/gfxVars.h"
     14 
     15 using mozilla::media::EncodeSupport;
     16 using mozilla::media::EncodeSupportSet;
     17 
     18 namespace mozilla {
     19 
     20 #define WMF_ENC_LOG(arg, ...)                         \
     21  MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \
     22          ("WMFDataEncoderUtils::%s: " arg, __func__, ##__VA_ARGS__))
     23 
     24 GUID CodecToSubtype(CodecType aCodec) {
     25  switch (aCodec) {
     26    case CodecType::H264:
     27      return MFVideoFormat_H264;
     28    case CodecType::VP8:
     29      return MFVideoFormat_VP80;
     30    case CodecType::VP9:
     31      return MFVideoFormat_VP90;
     32    default:
     33      return GUID_NULL;
     34  }
     35 }
     36 
     37 static bool CanUseWMFHwEncoder(CodecType aCodec) {
     38  if (!gfx::gfxVars::IsInitialized()) {
     39    return false;
     40  }
     41 
     42  switch (aCodec) {
     43    case CodecType::H264:
     44      return gfx::gfxVars::UseH264HwEncode();
     45    case CodecType::VP8:
     46      return gfx::gfxVars::UseVP8HwEncode();
     47    case CodecType::VP9:
     48      return gfx::gfxVars::UseVP9HwEncode();
     49    default:
     50      return false;
     51  }
     52 }
     53 
     54 EncodeSupportSet CanCreateWMFEncoder(const EncoderConfig& aConfig) {
     55  EncodeSupportSet supports;
     56  mscom::EnsureMTA([&]() {
     57    if (!wmf::MediaFoundationInitializer::HasInitialized()) {
     58      return;
     59    }
     60    // Try HW encoder if allowed by graphics and not disallowed by the caller.
     61    if (aConfig.mHardwarePreference != HardwarePreference::RequireSoftware) {
     62      if (CanUseWMFHwEncoder(aConfig.mCodec)) {
     63        auto hwEnc =
     64            MakeRefPtr<MFTEncoder>(MFTEncoder::HWPreference::HardwareOnly);
     65        if (SUCCEEDED(hwEnc->Create(CodecToSubtype(aConfig.mCodec),
     66                                    aConfig.mSize, aConfig.mCodecSpecific))) {
     67          supports += EncodeSupport::HardwareEncode;
     68        }
     69      } else {
     70        WMF_ENC_LOG("HW encoder is disabled for %s",
     71                    EnumValueToString(aConfig.mCodec));
     72      }
     73    }
     74    if (aConfig.mHardwarePreference != HardwarePreference::RequireHardware) {
     75      // Try SW encoder if not disallowed by the caller.
     76      auto swEnc =
     77          MakeRefPtr<MFTEncoder>(MFTEncoder::HWPreference::SoftwareOnly);
     78      if (SUCCEEDED(swEnc->Create(CodecToSubtype(aConfig.mCodec), aConfig.mSize,
     79                                  aConfig.mCodecSpecific))) {
     80        supports += EncodeSupport::SoftwareEncode;
     81      }
     82    }
     83 
     84    WMF_ENC_LOG(
     85        "%s encoder support for %s",
     86        supports.contains(EncodeSupportSet(EncodeSupport::HardwareEncode,
     87                                           EncodeSupport::SoftwareEncode))
     88            ? "HW | SW"
     89        : supports.contains(EncodeSupport::HardwareEncode) ? "HW"
     90        : supports.contains(EncodeSupport::SoftwareEncode) ? "SW"
     91                                                           : "No",
     92        aConfig.ToString().get());
     93  });
     94  return supports;
     95 }
     96 
     97 static already_AddRefed<MediaByteBuffer> ParseH264Parameters(
     98    const nsTArray<uint8_t>& aHeader, const bool aAsAnnexB) {
     99  if (!aAsAnnexB) {
    100    return AnnexB::ExtractExtraDataForAVCC(aHeader).forget();
    101  }
    102  size_t length = aHeader.Length();
    103  auto annexB = MakeRefPtr<MediaByteBuffer>(length);
    104  PodCopy(annexB->Elements(), aHeader.Elements(), length);
    105  annexB->SetLength(length);
    106  return annexB.forget();
    107 }
    108 
    109 static uint32_t GetProfile(H264_PROFILE aProfileLevel) {
    110  switch (aProfileLevel) {
    111    case H264_PROFILE_BASE:
    112      return eAVEncH264VProfile_Base;
    113    case H264_PROFILE_MAIN:
    114      return eAVEncH264VProfile_Main;
    115    case H264_PROFILE_HIGH:
    116      return eAVEncH264VProfile_High;
    117    default:
    118      return eAVEncH264VProfile_unknown;
    119  }
    120 }
    121 
    122 already_AddRefed<IMFMediaType> CreateInputType(EncoderConfig& aConfig) {
    123  RefPtr<IMFMediaType> type;
    124  HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type));
    125  if (FAILED(hr)) {
    126    WMF_ENC_LOG("MFCreateMediaType (input) error: %lx", hr);
    127    return nullptr;
    128  }
    129  hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    130  if (FAILED(hr)) {
    131    WMF_ENC_LOG("Create input type: SetGUID (major type) error: %lx", hr);
    132    return nullptr;
    133  }
    134  // Always NV12 input
    135  hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
    136  if (FAILED(hr)) {
    137    WMF_ENC_LOG("Create input type: SetGUID (subtype) error: %lx", hr);
    138    return nullptr;
    139  }
    140  hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    141  if (FAILED(hr)) {
    142    WMF_ENC_LOG("Create input type: interlace mode (input) error: %lx", hr);
    143    return nullptr;
    144  }
    145  // WMF requires a framerate to intialize properly. Provide something
    146  // reasonnable if not provided.
    147  if (!aConfig.mFramerate) {
    148    aConfig.mFramerate = 30;
    149  }
    150  if (aConfig.mFramerate) {
    151    hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1);
    152    if (FAILED(hr)) {
    153      WMF_ENC_LOG("Create input type: frame rate (input) error: %lx", hr);
    154      return nullptr;
    155    }
    156  }
    157  hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width,
    158                          aConfig.mSize.height);
    159  if (FAILED(hr)) {
    160    WMF_ENC_LOG("Create input type: frame size (input) error: %lx", hr);
    161    return nullptr;
    162  }
    163  return type.forget();
    164 }
    165 
    166 already_AddRefed<IMFMediaType> CreateOutputType(EncoderConfig& aConfig) {
    167  RefPtr<IMFMediaType> type;
    168  HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(type));
    169  if (FAILED(hr)) {
    170    WMF_ENC_LOG("MFCreateMediaType (output) error: %lx", hr);
    171    return nullptr;
    172  }
    173  hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    174  if (FAILED(hr)) {
    175    WMF_ENC_LOG("Create output type: set major type error: %lx", hr);
    176    return nullptr;
    177  }
    178  hr = type->SetGUID(MF_MT_SUBTYPE, CodecToSubtype(aConfig.mCodec));
    179  if (FAILED(hr)) {
    180    WMF_ENC_LOG("Create output type: set subtype error: %lx", hr);
    181    return nullptr;
    182  }
    183  // A bitrate need to be set here, attempt to make an educated guess if none
    184  // is provided. This could be per codec to have nicer defaults.
    185  size_t longDimension = std::max(aConfig.mSize.width, aConfig.mSize.height);
    186  if (!aConfig.mBitrate) {
    187    if (longDimension < 720) {
    188      aConfig.mBitrate = 2000000;
    189    } else if (longDimension < 1080) {
    190      aConfig.mBitrate = 4000000;
    191    } else {
    192      aConfig.mBitrate = 8000000;
    193    }
    194  }
    195  // No way to set variable / constant here.
    196  hr = type->SetUINT32(MF_MT_AVG_BITRATE, aConfig.mBitrate);
    197  if (FAILED(hr)) {
    198    WMF_ENC_LOG("Create output type: set bitrate error: %lx", hr);
    199    return nullptr;
    200  }
    201  hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    202  if (FAILED(hr)) {
    203    WMF_ENC_LOG("Create output type set interlave mode error: %lx", hr);
    204    return nullptr;
    205  }
    206  // A positive rate must always be preset here, see the Input config part.
    207  MOZ_ASSERT(aConfig.mFramerate);
    208  if (aConfig.mFramerate) {
    209    hr = MFSetAttributeRatio(type, MF_MT_FRAME_RATE, aConfig.mFramerate, 1);
    210    if (FAILED(hr)) {
    211      WMF_ENC_LOG("Create output type set frame rate error: %lx", hr);
    212      return nullptr;
    213    }
    214  }
    215  // Required
    216  hr = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, aConfig.mSize.width,
    217                          aConfig.mSize.height);
    218  if (FAILED(hr)) {
    219    WMF_ENC_LOG("Create output type set frame size error: %lx", hr);
    220    return nullptr;
    221  }
    222 
    223  if (aConfig.mCodecSpecific.is<H264Specific>()) {
    224    MOZ_ASSERT(aConfig.mCodec == CodecType::H264);
    225    hr = type->SetUINT32(
    226        MF_MT_MPEG2_PROFILE,
    227        GetProfile(aConfig.mCodecSpecific.as<H264Specific>().mProfile));
    228    if (FAILED(hr)) {
    229      WMF_ENC_LOG("Create output type set profile error: %lx", hr);
    230      return nullptr;
    231    }
    232  }
    233 
    234  // Set keyframe distance through both media type and codec API for better
    235  // compatibility. Some encoders may only support one of these methods.
    236  // `AVEncVideoMaxKeyframeDistance` is set in `MFTEncoder::SetModes`.
    237  uint32_t interval = SaturatingCast<uint32_t>(aConfig.mKeyframeInterval);
    238  if (interval > 0) {
    239    hr = type->SetUINT32(MF_MT_MAX_KEYFRAME_SPACING, interval);
    240    if (FAILED(hr)) {
    241      WMF_ENC_LOG("Create output type set keyframe interval error: %lx", hr);
    242      return nullptr;
    243    }
    244    WMF_ENC_LOG("Set MAX_KEYFRAME_SPACING to %u", interval);
    245  }
    246 
    247  return type.forget();
    248 }
    249 
    250 HRESULT SetMediaTypes(RefPtr<MFTEncoder>& aEncoder, EncoderConfig& aConfig) {
    251  RefPtr<IMFMediaType> inputType = CreateInputType(aConfig);
    252  if (!inputType) {
    253    return E_FAIL;
    254  }
    255 
    256  RefPtr<IMFMediaType> outputType = CreateOutputType(aConfig);
    257  if (!outputType) {
    258    return E_FAIL;
    259  }
    260 
    261  return aEncoder->SetMediaTypes(inputType, outputType);
    262 }
    263 
    264 }  // namespace mozilla