tor-browser

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

MFTEncoder.cpp (61423B)


      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 "MFTEncoder.h"
      8 
      9 #include <comdef.h>
     10 
     11 #include "WMFUtils.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/StaticPrefs_media.h"
     14 #include "mozilla/WindowsProcessMitigations.h"
     15 #include "mozilla/dom/WebCodecsUtils.h"
     16 #include "mozilla/mscom/COMWrappers.h"
     17 #include "mozilla/mscom/Utils.h"
     18 
     19 using Microsoft::WRL::ComPtr;
     20 
     21 // Missing from MinGW.
     22 #ifndef CODECAPI_AVEncAdaptiveMode
     23 #  define STATIC_CODECAPI_AVEncAdaptiveMode \
     24    0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc, 0x1e, 0xfb, 0x1e
     25 DEFINE_CODECAPI_GUID(AVEncAdaptiveMode, "4419b185-da1f-4f53-bc76-097d0c1efb1e",
     26                     0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc,
     27                     0x1e, 0xfb, 0x1e)
     28 #  define CODECAPI_AVEncAdaptiveMode \
     29    DEFINE_CODECAPI_GUIDNAMED(AVEncAdaptiveMode)
     30 #endif
     31 #ifndef MF_E_NO_EVENTS_AVAILABLE
     32 #  define MF_E_NO_EVENTS_AVAILABLE _HRESULT_TYPEDEF_(0xC00D3E80L)
     33 #endif
     34 
     35 #define MFT_LOG_INTERNAL(level, msg, ...) \
     36  MOZ_LOG(mozilla::sPEMLog, LogLevel::level, (msg, ##__VA_ARGS__))
     37 
     38 #define MFT_ENC_LOG(level, msg, ...)                                    \
     39  MFT_LOG_INTERNAL(level, "MFTEncoder(0x%p)::%s: " msg, this, __func__, \
     40                   ##__VA_ARGS__)
     41 #define MFT_ENC_SLOG(level, msg, ...) \
     42  MFT_LOG_INTERNAL(level, "MFTEncoder::%s: " msg, __func__, ##__VA_ARGS__)
     43 
     44 #define MFT_ENC_LOGD(msg, ...) MFT_ENC_LOG(Debug, msg, ##__VA_ARGS__)
     45 #define MFT_ENC_LOGE(msg, ...) MFT_ENC_LOG(Error, msg, ##__VA_ARGS__)
     46 #define MFT_ENC_LOGW(msg, ...) MFT_ENC_LOG(Warning, msg, ##__VA_ARGS__)
     47 #define MFT_ENC_LOGV(msg, ...) MFT_ENC_LOG(Verbose, msg, ##__VA_ARGS__)
     48 
     49 #define MFT_ENC_SLOGD(msg, ...) MFT_ENC_SLOG(Debug, msg, ##__VA_ARGS__)
     50 #define MFT_ENC_SLOGE(msg, ...) MFT_ENC_SLOG(Error, msg, ##__VA_ARGS__)
     51 #define MFT_ENC_SLOGW(msg, ...) MFT_ENC_SLOG(Warning, msg, ##__VA_ARGS__)
     52 #define MFT_ENC_SLOGV(msg, ...) MFT_ENC_SLOG(Verbose, msg, ##__VA_ARGS__)
     53 
     54 #undef MFT_RETURN_IF_FAILED_IMPL
     55 #define MFT_RETURN_IF_FAILED_IMPL(x, log_macro)                            \
     56  do {                                                                     \
     57    HRESULT rv = x;                                                        \
     58    if (MOZ_UNLIKELY(FAILED(rv))) {                                        \
     59      _com_error error(rv);                                                \
     60      log_macro("(" #x ") failed, rv=%lx(%ls)", rv, error.ErrorMessage()); \
     61      return rv;                                                           \
     62    }                                                                      \
     63  } while (false)
     64 
     65 #undef MFT_RETURN_IF_FAILED
     66 #define MFT_RETURN_IF_FAILED(x) MFT_RETURN_IF_FAILED_IMPL(x, MFT_ENC_LOGE)
     67 
     68 #undef MFT_RETURN_IF_FAILED_S
     69 #define MFT_RETURN_IF_FAILED_S(x) MFT_RETURN_IF_FAILED_IMPL(x, MFT_ENC_SLOGE)
     70 
     71 #undef MFT_RETURN_VALUE_IF_FAILED_IMPL
     72 #define MFT_RETURN_VALUE_IF_FAILED_IMPL(x, ret, log_macro)                 \
     73  do {                                                                     \
     74    HRESULT rv = x;                                                        \
     75    if (MOZ_UNLIKELY(FAILED(rv))) {                                        \
     76      _com_error error(rv);                                                \
     77      log_macro("(" #x ") failed, rv=%lx(%ls)", rv, error.ErrorMessage()); \
     78      return ret;                                                          \
     79    }                                                                      \
     80  } while (false)
     81 
     82 #undef MFT_RETURN_VALUE_IF_FAILED
     83 #define MFT_RETURN_VALUE_IF_FAILED(x, r) \
     84  MFT_RETURN_VALUE_IF_FAILED_IMPL(x, r, MFT_ENC_LOGE)
     85 
     86 #undef MFT_RETURN_VALUE_IF_FAILED_S
     87 #define MFT_RETURN_VALUE_IF_FAILED_S(x, r) \
     88  MFT_RETURN_VALUE_IF_FAILED_IMPL(x, r, MFT_ENC_SLOGE)
     89 
     90 #undef MFT_RETURN_ERROR_IF_FAILED_IMPL
     91 #define MFT_RETURN_ERROR_IF_FAILED_IMPL(x, log_macro)                      \
     92  do {                                                                     \
     93    HRESULT rv = x;                                                        \
     94    if (MOZ_UNLIKELY(FAILED(rv))) {                                        \
     95      _com_error error(rv);                                                \
     96      log_macro("(" #x ") failed, rv=%lx(%ls)", rv, error.ErrorMessage()); \
     97      return Err(rv);                                                      \
     98    }                                                                      \
     99  } while (false)
    100 
    101 #undef MFT_RETURN_ERROR_IF_FAILED
    102 #define MFT_RETURN_ERROR_IF_FAILED(x) \
    103  MFT_RETURN_ERROR_IF_FAILED_IMPL(x, MFT_ENC_LOGE)
    104 
    105 #undef MFT_RETURN_ERROR_IF_FAILED_S
    106 #define MFT_RETURN_ERROR_IF_FAILED_S(x) \
    107  MFT_RETURN_ERROR_IF_FAILED_IMPL(x, MFT_ENC_SLOGE)
    108 
    109 #define AUTO_MFTENCODER_MARKER(desc) AUTO_WEBCODECS_MARKER("MFTEncoder", desc);
    110 
    111 namespace mozilla {
    112 extern LazyLogModule sPEMLog;
    113 
    114 static const char* ErrorStr(HRESULT hr) {
    115  switch (hr) {
    116    case S_OK:
    117      return "OK";
    118    case MF_E_INVALIDMEDIATYPE:
    119      return "INVALIDMEDIATYPE";
    120    case MF_E_INVALIDSTREAMNUMBER:
    121      return "INVALIDSTREAMNUMBER";
    122    case MF_E_INVALIDTYPE:
    123      return "INVALIDTYPE";
    124    case MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING:
    125      return "TRANSFORM_PROCESSING";
    126    case MF_E_TRANSFORM_ASYNC_LOCKED:
    127      return "TRANSFORM_ASYNC_LOCKED";
    128    case MF_E_TRANSFORM_NEED_MORE_INPUT:
    129      return "TRANSFORM_NEED_MORE_INPUT";
    130    case MF_E_TRANSFORM_STREAM_CHANGE:
    131      return "TRANSFORM_STREAM_CHANGE";
    132    case MF_E_TRANSFORM_TYPE_NOT_SET:
    133      return "TRANSFORM_TYPE_NO_SET";
    134    case MF_E_UNSUPPORTED_D3D_TYPE:
    135      return "UNSUPPORTED_D3D_TYPE";
    136    case E_INVALIDARG:
    137      return "INVALIDARG";
    138    case MF_E_MULTIPLE_SUBSCRIBERS:
    139      return "MULTIPLE_SUBSCRIBERS";
    140    case MF_E_NO_EVENTS_AVAILABLE:
    141      return "NO_EVENTS_AVAILABLE";
    142    case MF_E_NO_SAMPLE_DURATION:
    143      return "NO_SAMPLE_DURATION";
    144    case MF_E_NO_SAMPLE_TIMESTAMP:
    145      return "NO_SAMPLE_TIMESTAMP";
    146    case MF_E_NOTACCEPTING:
    147      return "NOTACCEPTING";
    148    case MF_E_ATTRIBUTENOTFOUND:
    149      return "NOTFOUND";
    150    case MF_E_BUFFERTOOSMALL:
    151      return "BUFFERTOOSMALL";
    152    case E_NOTIMPL:
    153      return "NOTIMPL";
    154    default:
    155      return "OTHER";
    156  }
    157 }
    158 
    159 static const char* MediaEventTypeStr(MediaEventType aType) {
    160 #define ENUM_TO_STR(enumVal) \
    161  case enumVal:              \
    162    return #enumVal
    163  switch (aType) {
    164    ENUM_TO_STR(MEUnknown);
    165    ENUM_TO_STR(METransformUnknown);
    166    ENUM_TO_STR(METransformNeedInput);
    167    ENUM_TO_STR(METransformHaveOutput);
    168    ENUM_TO_STR(METransformDrainComplete);
    169    ENUM_TO_STR(METransformMarker);
    170    ENUM_TO_STR(METransformInputStreamStateChanged);
    171    default:
    172      break;
    173  }
    174  return "Unknown MediaEventType";
    175 
    176 #undef ENUM_TO_STR
    177 }
    178 
    179 static nsCString ErrorMessage(HRESULT hr) {
    180  nsCString msg(ErrorStr(hr));
    181  _com_error err(hr);
    182  msg.AppendFmt(" ({})", NS_ConvertUTF16toUTF8(err.ErrorMessage()).get());
    183  return msg;
    184 }
    185 
    186 static const char* CodecStr(const GUID& aGUID) {
    187  if (IsEqualGUID(aGUID, MFVideoFormat_H264)) {
    188    return "H.264";
    189  } else if (IsEqualGUID(aGUID, MFVideoFormat_VP80)) {
    190    return "VP8";
    191  } else if (IsEqualGUID(aGUID, MFVideoFormat_VP90)) {
    192    return "VP9";
    193  } else {
    194    return "Unsupported codec";
    195  }
    196 }
    197 
    198 static Result<nsCString, HRESULT> GetStringFromAttributes(
    199    IMFAttributes* aAttributes, REFGUID aGuidKey) {
    200  UINT32 len = 0;
    201  MFT_RETURN_ERROR_IF_FAILED_S(aAttributes->GetStringLength(aGuidKey, &len));
    202 
    203  nsCString str;
    204  if (len > 0) {
    205    ++len;  // '\0'.
    206    WCHAR buffer[len];
    207    MFT_RETURN_ERROR_IF_FAILED_S(
    208        aAttributes->GetString(aGuidKey, buffer, len, nullptr));
    209    str.Append(NS_ConvertUTF16toUTF8(buffer));
    210  }
    211 
    212  return str;
    213 }
    214 
    215 static Result<nsCString, HRESULT> GetFriendlyName(IMFActivate* aActivate) {
    216  return GetStringFromAttributes(aActivate, MFT_FRIENDLY_NAME_Attribute)
    217      .map([](const nsCString& aName) {
    218        return aName.IsEmpty() ? "Unknown MFT"_ns : aName;
    219      });
    220 }
    221 
    222 static Result<MFTEncoder::Factory::Provider, HRESULT> GetHardwareVendor(
    223    IMFActivate* aActivate) {
    224  nsCString vendor = MOZ_TRY(GetStringFromAttributes(
    225      aActivate, MFT_ENUM_HARDWARE_VENDOR_ID_Attribute));
    226 
    227  if (vendor == "VEN_1002"_ns) {
    228    return MFTEncoder::Factory::Provider::HW_AMD;
    229  } else if (vendor == "VEN_10DE"_ns) {
    230    return MFTEncoder::Factory::Provider::HW_NVIDIA;
    231  } else if (vendor == "VEN_8086"_ns) {
    232    return MFTEncoder::Factory::Provider::HW_Intel;
    233  } else if (vendor == "VEN_QCOM"_ns) {
    234    return MFTEncoder::Factory::Provider::HW_Qualcomm;
    235  }
    236 
    237  MFT_ENC_SLOGD("Undefined hardware vendor id: %s", vendor.get());
    238  return MFTEncoder::Factory::Provider::HW_Unknown;
    239 }
    240 
    241 static Result<nsTArray<ComPtr<IMFActivate>>, HRESULT> EnumMFT(
    242    GUID aCategory, UINT32 aFlags, const MFT_REGISTER_TYPE_INFO* aInType,
    243    const MFT_REGISTER_TYPE_INFO* aOutType) {
    244  nsTArray<ComPtr<IMFActivate>> activates;
    245 
    246  IMFActivate** enumerated;
    247  UINT32 num = 0;
    248  MFT_RETURN_ERROR_IF_FAILED_S(
    249      wmf::MFTEnumEx(aCategory, aFlags, aInType, aOutType, &enumerated, &num));
    250  for (UINT32 i = 0; i < num; ++i) {
    251    activates.AppendElement(ComPtr<IMFActivate>(enumerated[i]));
    252    // MFTEnumEx increments the reference count for each IMFActivate; decrement
    253    // here so ComPtr manages the lifetime correctly
    254    enumerated[i]->Release();
    255  }
    256  if (enumerated) {
    257    mscom::wrapped::CoTaskMemFree(enumerated);
    258  }
    259  return activates;
    260 }
    261 
    262 MFTEncoder::Factory::Factory(Provider aProvider,
    263                             ComPtr<IMFActivate>&& aActivate)
    264    : mProvider(aProvider), mActivate(std::move(aActivate)) {
    265  mName = mozilla::GetFriendlyName(mActivate.Get()).unwrapOr("Unknown"_ns);
    266 }
    267 
    268 MFTEncoder::Factory::~Factory() { Shutdown(); }
    269 
    270 HRESULT MFTEncoder::Factory::Shutdown() {
    271  HRESULT hr = S_OK;
    272  if (mActivate) {
    273    MFT_ENC_LOGE("Shutdown %s encoder %s",
    274                 MFTEncoder::Factory::EnumValueToString(mProvider),
    275                 mName.get());
    276    // Release MFT resources via activation object.
    277    hr = mActivate->ShutdownObject();
    278    if (FAILED(hr)) {
    279      MFT_ENC_LOGE("Failed to shutdown MFT: %s", ErrorStr(hr));
    280    }
    281  }
    282  mActivate.Reset();
    283  mName.Truncate();
    284  return hr;
    285 }
    286 
    287 static nsTArray<MFTEncoder::Factory> IntoFactories(
    288    nsTArray<ComPtr<IMFActivate>>&& aActivates, bool aIsHardware) {
    289  nsTArray<MFTEncoder::Factory> factories;
    290  for (auto& activate : aActivates) {
    291    if (activate) {
    292      MFTEncoder::Factory::Provider provider =
    293          aIsHardware ? GetHardwareVendor(activate.Get())
    294                            .unwrapOr(MFTEncoder::Factory::Provider::HW_Unknown)
    295                      : MFTEncoder::Factory::Provider::SW;
    296      factories.AppendElement(
    297          MFTEncoder::Factory(provider, std::move(activate)));
    298    }
    299  }
    300  return factories;
    301 }
    302 
    303 static nsTArray<MFTEncoder::Factory> EnumEncoders(
    304    const GUID& aSubtype, const MFTEncoder::HWPreference aHWPreference) {
    305  MFT_REGISTER_TYPE_INFO inType = {.guidMajorType = MFMediaType_Video,
    306                                   .guidSubtype = MFVideoFormat_NV12};
    307  MFT_REGISTER_TYPE_INFO outType = {.guidMajorType = MFMediaType_Video,
    308                                    .guidSubtype = aSubtype};
    309 
    310  auto log = [&](const nsTArray<MFTEncoder::Factory>& aActivates) {
    311    for (const auto& activate : aActivates) {
    312      MFT_ENC_SLOGD("Found %s encoders: %s",
    313                    MFTEncoder::Factory::EnumValueToString(activate.mProvider),
    314                    activate.mName.get());
    315    }
    316  };
    317 
    318  nsTArray<MFTEncoder::Factory> swFactories;
    319  nsTArray<MFTEncoder::Factory> hwFactories;
    320 
    321  if (aHWPreference != MFTEncoder::HWPreference::SoftwareOnly) {
    322    // Some HW encoders use DXGI API and crash when locked down.
    323    // TODO: move HW encoding out of content process (bug 1754531).
    324    if (IsWin32kLockedDown()) {
    325      MFT_ENC_SLOGD("Don't use HW encoder when win32k locked down.");
    326    } else {
    327      auto r = EnumMFT(MFT_CATEGORY_VIDEO_ENCODER,
    328                       MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER,
    329                       &inType, &outType);
    330      if (r.isErr()) {
    331        MFT_ENC_SLOGE("enumerate HW encoder for %s: error=%s",
    332                      CodecStr(aSubtype), ErrorMessage(r.unwrapErr()).get());
    333      } else {
    334        hwFactories.AppendElements(
    335            IntoFactories(r.unwrap(), true /* aIsHardware */));
    336        log(hwFactories);
    337      }
    338    }
    339  }
    340 
    341  if (aHWPreference != MFTEncoder::HWPreference::HardwareOnly) {
    342    auto r = EnumMFT(MFT_CATEGORY_VIDEO_ENCODER,
    343                     MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT |
    344                         MFT_ENUM_FLAG_SORTANDFILTER,
    345                     &inType, &outType);
    346    if (r.isErr()) {
    347      MFT_ENC_SLOGE("enumerate SW encoder for %s: error=%s", CodecStr(aSubtype),
    348                    ErrorMessage(r.unwrapErr()).get());
    349    } else {
    350      swFactories.AppendElements(
    351          IntoFactories(r.unwrap(), false /* aIsHardware */));
    352      log(swFactories);
    353    }
    354  }
    355 
    356  nsTArray<MFTEncoder::Factory> factories;
    357 
    358  switch (aHWPreference) {
    359    case MFTEncoder::HWPreference::HardwareOnly:
    360      return hwFactories;
    361    case MFTEncoder::HWPreference::SoftwareOnly:
    362      return swFactories;
    363    case MFTEncoder::HWPreference::PreferHardware:
    364      factories.AppendElements(std::move(hwFactories));
    365      factories.AppendElements(std::move(swFactories));
    366      break;
    367    case MFTEncoder::HWPreference::PreferSoftware:
    368      factories.AppendElements(std::move(swFactories));
    369      factories.AppendElements(std::move(hwFactories));
    370      break;
    371  }
    372 
    373  return factories;
    374 }
    375 
    376 static void PopulateEncoderInfo(const GUID& aSubtype,
    377                                nsTArray<MFTEncoder::Info>& aInfos) {
    378  nsTArray<MFTEncoder::Factory> factories =
    379      EnumEncoders(aSubtype, MFTEncoder::HWPreference::PreferHardware);
    380  for (const auto& factory : factories) {
    381    MFTEncoder::Info info = {.mSubtype = aSubtype, .mName = factory.mName};
    382    aInfos.AppendElement(info);
    383    MFT_ENC_SLOGD("<ENC> [%s] %s\n", CodecStr(aSubtype), info.mName.Data());
    384  }
    385 }
    386 
    387 Maybe<MFTEncoder::Info> MFTEncoder::GetInfo(const GUID& aSubtype) {
    388  nsTArray<Info>& infos = Infos();
    389 
    390  for (auto i : infos) {
    391    if (IsEqualGUID(aSubtype, i.mSubtype)) {
    392      return Some(i);
    393    }
    394  }
    395  return Nothing();
    396 }
    397 
    398 nsCString MFTEncoder::GetFriendlyName(const GUID& aSubtype) {
    399  Maybe<Info> info = GetInfo(aSubtype);
    400 
    401  return info ? info.ref().mName : "???"_ns;
    402 }
    403 
    404 // Called only once by Infos().
    405 nsTArray<MFTEncoder::Info> MFTEncoder::Enumerate() {
    406  nsTArray<Info> infos;
    407 
    408  if (!wmf::MediaFoundationInitializer::HasInitialized()) {
    409    MFT_ENC_SLOGE("cannot init Media Foundation");
    410    return infos;
    411  }
    412 
    413  PopulateEncoderInfo(MFVideoFormat_H264, infos);
    414  PopulateEncoderInfo(MFVideoFormat_VP90, infos);
    415  PopulateEncoderInfo(MFVideoFormat_VP80, infos);
    416 
    417  return infos;
    418 }
    419 
    420 nsTArray<MFTEncoder::Info>& MFTEncoder::Infos() {
    421  static nsTArray<Info> infos = Enumerate();
    422  return infos;
    423 }
    424 
    425 static Result<Ok, nsCString> IsSupported(
    426    const MFTEncoder::Factory& aFactory, const GUID& aSubtype,
    427    const gfx::IntSize& aFrameSize,
    428    const EncoderConfig::CodecSpecific& aCodecSpecific) {
    429  bool isH264HighProfile = IsEqualGUID(aSubtype, MFVideoFormat_H264) &&
    430                           aCodecSpecific.is<H264Specific>() &&
    431                           aCodecSpecific.as<H264Specific>().mProfile ==
    432                               H264_PROFILE::H264_PROFILE_HIGH;
    433  // This is an empirically safe limit.
    434  bool isFrameSizeGreaterThan4K =
    435      aFrameSize.width > 3840 || aFrameSize.height > 2160;
    436 
    437  // For Intel and AMD hardware encoders, initializing the H.264 High profile
    438  // with large frame sizes such as 7680×4320 may cause SetOutputType to fail or
    439  // prevent the encoder from producing output.
    440  if (aFactory.mProvider != MFTEncoder::Factory::Provider::SW &&
    441      isH264HighProfile && isFrameSizeGreaterThan4K) {
    442    return Err(nsFmtCString(
    443        FMT_STRING(
    444            "{} encoder {} does not support H.264 high profile for 4K+ video"),
    445        MFTEncoder::Factory::EnumValueToString(aFactory.mProvider),
    446        aFactory.mName.get()));
    447  }
    448  // TODO: Check the SVC support from different HW encoders.
    449  return Ok();
    450 }
    451 
    452 HRESULT MFTEncoder::Create(const GUID& aSubtype, const gfx::IntSize& aFrameSize,
    453                           const EncoderConfig::CodecSpecific& aCodecSpecific) {
    454  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    455  MOZ_ASSERT(!mEncoder);
    456 
    457  AUTO_MFTENCODER_MARKER("::Create");
    458 
    459  auto cleanup = MakeScopeExit([&] {
    460    mEncoder = nullptr;
    461    mFactory.reset();
    462    mConfig = nullptr;
    463  });
    464 
    465  nsTArray<MFTEncoder::Factory> factories =
    466      EnumEncoders(aSubtype, mHWPreference);
    467  for (auto& f : factories) {
    468    MOZ_ASSERT(f);
    469    if (auto r = IsSupported(f, aSubtype, aFrameSize, aCodecSpecific);
    470        r.isErr()) {
    471      nsCString errorMsg = r.unwrapErr();
    472      MFT_ENC_LOGE("Skip %s encoder %s for %s: %s",
    473                   MFTEncoder::Factory::EnumValueToString(f.mProvider),
    474                   f.mName.get(), CodecStr(aSubtype), errorMsg.get());
    475      continue;
    476    }
    477 
    478    RefPtr<IMFTransform> encoder;
    479    // Create the MFT activation object.
    480    HRESULT hr = f.mActivate->ActivateObject(
    481        IID_PPV_ARGS(static_cast<IMFTransform**>(getter_AddRefs(encoder))));
    482    if (SUCCEEDED(hr) && encoder) {
    483      MFT_ENC_LOGD("%s for %s is activated", f.mName.get(), CodecStr(aSubtype));
    484      mFactory.emplace(std::move(f));
    485      mEncoder = std::move(encoder);
    486      break;
    487    }
    488    _com_error error(hr);
    489    MFT_ENC_LOGE("ActivateObject %s error = 0x%lX, %ls", f.mName.get(), hr,
    490                 error.ErrorMessage());
    491  }
    492 
    493  if (!mFactory || !mEncoder) {
    494    MFT_ENC_LOGE("Failed to create MFT for %s", CodecStr(aSubtype));
    495    return E_FAIL;
    496  }
    497 
    498  RefPtr<ICodecAPI> config;
    499  // Avoid IID_PPV_ARGS() here for MingGW fails to declare UUID for ICodecAPI.
    500  MFT_RETURN_IF_FAILED(
    501      mEncoder->QueryInterface(IID_ICodecAPI, getter_AddRefs(config)));
    502  mConfig = std::move(config);
    503 
    504  SetState(State::Initializing);
    505  cleanup.release();
    506  return S_OK;
    507 }
    508 
    509 HRESULT
    510 MFTEncoder::Destroy() {
    511  if (!mEncoder) {
    512    return S_OK;
    513  }
    514 
    515  MaybeResolveOrRejectAnyPendingPromise(MediaResult(
    516      NS_ERROR_DOM_MEDIA_CANCELED, RESULT_DETAIL("Canceled by Destroy")));
    517  mPendingError = NS_OK;
    518 
    519  mAsyncEventSource = nullptr;
    520  mEncoder = nullptr;
    521  mConfig = nullptr;
    522  HRESULT hr = mFactory ? S_OK : mFactory->Shutdown();
    523  mFactory.reset();
    524  // TODO: If Factory::Shutdown() fails and the encoder is not reusable, set the
    525  // state to error.
    526  SetState(State::Uninited);
    527 
    528  return hr;
    529 }
    530 
    531 HRESULT
    532 MFTEncoder::SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType) {
    533  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    534  MOZ_ASSERT(aInputType && aOutputType);
    535  MOZ_ASSERT(mFactory);
    536  MOZ_ASSERT(mEncoder);
    537  MOZ_ASSERT(mState == State::Initializing);
    538 
    539  AUTO_MFTENCODER_MARKER("::SetMediaTypes");
    540 
    541  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
    542 
    543  AsyncMFTResult asyncMFT = AttemptEnableAsync();
    544  if (asyncMFT.isErr()) {
    545    HRESULT hr = asyncMFT.inspectErr();
    546    MFT_ENC_LOGE("AttemptEnableAsync error: %s", ErrorMessage(hr).get());
    547    return hr;
    548  }
    549  bool isAsync = asyncMFT.unwrap();
    550  MFT_ENC_LOGD("%s encoder %s is %s",
    551               MFTEncoder::Factory::EnumValueToString(mFactory->mProvider),
    552               mFactory->mName.get(), isAsync ? "asynchronous" : "synchronous");
    553 
    554  MFT_RETURN_IF_FAILED(GetStreamIDs());
    555 
    556  // Always set encoder output type before input.
    557  MFT_RETURN_IF_FAILED(
    558      mEncoder->SetOutputType(mOutputStreamID, aOutputType, 0));
    559 
    560  if (MatchInputSubtype(aInputType) == GUID_NULL) {
    561    MFT_ENC_LOGE("Input type does not match encoder input subtype");
    562    return MF_E_INVALIDMEDIATYPE;
    563  }
    564 
    565  MFT_RETURN_IF_FAILED(mEncoder->SetInputType(mInputStreamID, aInputType, 0));
    566 
    567  MFT_RETURN_IF_FAILED(
    568      mEncoder->GetInputStreamInfo(mInputStreamID, &mInputStreamInfo));
    569 
    570  MFT_RETURN_IF_FAILED(
    571      mEncoder->GetOutputStreamInfo(mInputStreamID, &mOutputStreamInfo));
    572 
    573  mOutputStreamProvidesSample =
    574      IsFlagSet(mOutputStreamInfo.dwFlags, MFT_OUTPUT_STREAM_PROVIDES_SAMPLES);
    575 
    576  if (isAsync) {
    577    MFT_ENC_LOGD("Setting event source w/%s callback", mIsRealtime ? "" : "o");
    578    RefPtr<IMFMediaEventGenerator> source;
    579    MFT_RETURN_IF_FAILED(mEncoder->QueryInterface(IID_PPV_ARGS(
    580        static_cast<IMFMediaEventGenerator**>(getter_AddRefs(source)))));
    581    // TODO: Consider always using MFTEventSource with callbacks if it does not
    582    // introduce performance regressions for overall video encoding duration.
    583    if (mIsRealtime) {
    584      mAsyncEventSource = MakeRefPtr<MFTEventSource>(this, source.forget());
    585      mAsyncEventSource->BeginEventListening();
    586    } else {
    587      mAsyncEventSource = MakeRefPtr<MFTEventSource>(source.forget());
    588    }
    589  }
    590 
    591  MFT_RETURN_IF_FAILED(SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0));
    592 
    593  MFT_RETURN_IF_FAILED(SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0));
    594 
    595  SetState(State::Inited);
    596  exitWithError.release();
    597  mNumNeedInput = 0;
    598  return S_OK;
    599 }
    600 
    601 // Async MFT won't work without unlocking. See
    602 // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#unlocking-asynchronous-mfts
    603 MFTEncoder::AsyncMFTResult MFTEncoder::AttemptEnableAsync() {
    604  ComPtr<IMFAttributes> attributes = nullptr;
    605  HRESULT hr = mEncoder->GetAttributes(&attributes);
    606  if (FAILED(hr)) {
    607    MFT_ENC_LOGE("Encoder->GetAttribute error");
    608    return AsyncMFTResult(hr);
    609  }
    610 
    611  // Retrieve `MF_TRANSFORM_ASYNC` using `MFGetAttributeUINT32` rather than
    612  // `attributes->GetUINT32`, since `MF_TRANSFORM_ASYNC` may not be present in
    613  // the attributes.
    614  bool async =
    615      MFGetAttributeUINT32(attributes.Get(), MF_TRANSFORM_ASYNC, FALSE) == TRUE;
    616  if (!async) {
    617    MFT_ENC_LOGD("Encoder is not async");
    618    return AsyncMFTResult(false);
    619  }
    620 
    621  hr = attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
    622  if (FAILED(hr)) {
    623    MFT_ENC_LOGE("SetUINT32 async unlock error");
    624    return AsyncMFTResult(hr);
    625  }
    626 
    627  return AsyncMFTResult(true);
    628 }
    629 
    630 HRESULT MFTEncoder::GetStreamIDs() {
    631  DWORD numIns;
    632  DWORD numOuts;
    633  MFT_RETURN_IF_FAILED(mEncoder->GetStreamCount(&numIns, &numOuts));
    634  MFT_ENC_LOGD("input stream count: %lu, output stream count: %lu", numIns,
    635               numOuts);
    636  if (numIns < 1 || numOuts < 1) {
    637    MFT_ENC_LOGE("stream count error");
    638    return MF_E_INVALIDSTREAMNUMBER;
    639  }
    640 
    641  DWORD inIDs[numIns];
    642  DWORD outIDs[numOuts];
    643  HRESULT hr = mEncoder->GetStreamIDs(numIns, inIDs, numOuts, outIDs);
    644  if (SUCCEEDED(hr)) {
    645    mInputStreamID = inIDs[0];
    646    mOutputStreamID = outIDs[0];
    647  } else if (hr == E_NOTIMPL) {
    648    mInputStreamID = 0;
    649    mOutputStreamID = 0;
    650  } else {
    651    MFT_ENC_LOGE("failed to get stream IDs: %s", ErrorMessage(hr).get());
    652    return hr;
    653  }
    654  MFT_ENC_LOGD("input stream ID: %lu, output stream ID: %lu", mInputStreamID,
    655               mOutputStreamID);
    656  return S_OK;
    657 }
    658 
    659 GUID MFTEncoder::MatchInputSubtype(IMFMediaType* aInputType) {
    660  MOZ_ASSERT(mEncoder);
    661  MOZ_ASSERT(aInputType);
    662 
    663  GUID desired = GUID_NULL;
    664  MFT_RETURN_VALUE_IF_FAILED(aInputType->GetGUID(MF_MT_SUBTYPE, &desired),
    665                             GUID_NULL);
    666  MOZ_ASSERT(desired != GUID_NULL);
    667 
    668  DWORD i = 0;
    669  RefPtr<IMFMediaType> inputType;
    670  GUID preferred = GUID_NULL;
    671  while (true) {
    672    HRESULT hr = mEncoder->GetInputAvailableType(mInputStreamID, i,
    673                                                 getter_AddRefs(inputType));
    674    if (hr == MF_E_NO_MORE_TYPES) {
    675      break;
    676    }
    677    if (FAILED(hr)) {
    678      MFT_ENC_LOGE("GetInputAvailableType error: %s", ErrorMessage(hr).get());
    679      return GUID_NULL;
    680    }
    681 
    682    GUID sub = GUID_NULL;
    683    MFT_RETURN_VALUE_IF_FAILED(inputType->GetGUID(MF_MT_SUBTYPE, &sub),
    684                               GUID_NULL);
    685 
    686    if (IsEqualGUID(desired, sub)) {
    687      preferred = desired;
    688      break;
    689    }
    690    ++i;
    691  }
    692 
    693  return IsEqualGUID(preferred, desired) ? preferred : GUID_NULL;
    694 }
    695 
    696 HRESULT
    697 MFTEncoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData) {
    698  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    699  MOZ_ASSERT(mEncoder);
    700 
    701  return mEncoder->ProcessMessage(aMsg, aData);
    702 }
    703 
    704 HRESULT MFTEncoder::SetModes(const EncoderConfig& aConfig) {
    705  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    706  MOZ_ASSERT(mConfig);
    707  MOZ_ASSERT(mState == State::Initializing);
    708 
    709  AUTO_MFTENCODER_MARKER("::SetModes");
    710 
    711  VARIANT var;
    712  var.vt = VT_UI4;
    713  switch (aConfig.mBitrateMode) {
    714    case BitrateMode::Constant:
    715      var.ulVal = eAVEncCommonRateControlMode_CBR;
    716      break;
    717    case BitrateMode::Variable:
    718      if (aConfig.mCodec == CodecType::VP8 ||
    719          aConfig.mCodec == CodecType::VP9) {
    720        MFT_ENC_LOGE(
    721            "Overriding requested VRB bitrate mode, forcing CBR for VP8/VP9 "
    722            "encoding.");
    723        var.ulVal = eAVEncCommonRateControlMode_CBR;
    724      } else {
    725        var.ulVal = eAVEncCommonRateControlMode_PeakConstrainedVBR;
    726      }
    727      break;
    728  }
    729  MFT_RETURN_IF_FAILED(
    730      mConfig->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var));
    731 
    732  if (aConfig.mBitrate) {
    733    var.ulVal = aConfig.mBitrate;
    734    MFT_RETURN_IF_FAILED(
    735        mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var));
    736  }
    737 
    738  switch (aConfig.mScalabilityMode) {
    739    case ScalabilityMode::None:
    740      var.ulVal = 1;
    741      break;
    742    case ScalabilityMode::L1T2:
    743      var.ulVal = 2;
    744      break;
    745    case ScalabilityMode::L1T3:
    746      var.ulVal = 3;
    747      break;
    748  }
    749 
    750  // TODO check this and replace it with mFactory->mProvider
    751  bool isIntel = false;
    752  if (aConfig.mScalabilityMode != ScalabilityMode::None || isIntel) {
    753    MFT_RETURN_IF_FAILED(
    754        mConfig->SetValue(&CODECAPI_AVEncVideoTemporalLayerCount, &var));
    755  }
    756 
    757  if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVEncAdaptiveMode))) {
    758    var.ulVal = eAVEncAdaptiveMode_Resolution;
    759    MFT_RETURN_IF_FAILED(mConfig->SetValue(&CODECAPI_AVEncAdaptiveMode, &var));
    760  }
    761 
    762  if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVLowLatencyMode))) {
    763    var.vt = VT_BOOL;
    764    var.boolVal =
    765        aConfig.mUsage == Usage::Realtime ? VARIANT_TRUE : VARIANT_FALSE;
    766    MFT_RETURN_IF_FAILED(mConfig->SetValue(&CODECAPI_AVLowLatencyMode, &var));
    767  }
    768 
    769  uint32_t interval = SaturatingCast<uint32_t>(aConfig.mKeyframeInterval);
    770  if (interval != 0) {
    771    var.vt = VT_UI4;
    772    var.ulVal = interval;
    773    if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVEncMPVGOPSize))) {
    774      MFT_RETURN_IF_FAILED(mConfig->SetValue(&CODECAPI_AVEncMPVGOPSize, &var));
    775      MFT_ENC_LOGD("Set GOPSize to %lu", var.ulVal);
    776    }
    777    // Set keyframe distance through both media type and codec API for better
    778    // compatibility. Some encoders may only support one of these methods.
    779    // `MF_MT_MAX_KEYFRAME_SPACING` is set in `CreateOutputType`.
    780    if (SUCCEEDED(
    781            mConfig->IsModifiable(&CODECAPI_AVEncVideoMaxKeyframeDistance))) {
    782      MFT_RETURN_IF_FAILED(
    783          mConfig->SetValue(&CODECAPI_AVEncVideoMaxKeyframeDistance, &var));
    784      MFT_ENC_LOGD("Set MaxKeyframeDistance to %lu", var.ulVal);
    785    }
    786  }
    787 
    788  mIsRealtime = aConfig.mUsage == Usage::Realtime;
    789 
    790  return S_OK;
    791 }
    792 
    793 HRESULT
    794 MFTEncoder::SetBitrate(UINT32 aBitsPerSec) {
    795  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    796  MOZ_ASSERT(mConfig);
    797 
    798  VARIANT var = {.vt = VT_UI4, .ulVal = aBitsPerSec};
    799  return mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
    800 }
    801 
    802 bool MFTEncoder::IsHardwareAccelerated() const {
    803  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    804 
    805  return mFactory && mFactory->mProvider != MFTEncoder::Factory::Provider::SW;
    806 }
    807 
    808 template <typename T, typename E, bool IsExclusive = true>
    809 static auto ResultToPromise(Result<T, E>&& aResult) {
    810  if (aResult.isErr()) {
    811    return MozPromise<T, E, IsExclusive>::CreateAndReject(aResult.unwrapErr(),
    812                                                          __func__);
    813  }
    814  return MozPromise<T, E, IsExclusive>::CreateAndResolve(aResult.unwrap(),
    815                                                         __func__);
    816 };
    817 
    818 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::Encode(
    819    nsTArray<InputSample>&& aInputs) {
    820  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    821  MOZ_ASSERT(mEncoder);
    822 
    823  if (!IsAsync()) {
    824    return ResultToPromise(EncodeSync(std::move(aInputs)));
    825  }
    826  if (!mIsRealtime) {
    827    return ResultToPromise(EncodeAsync(std::move(aInputs)));
    828  }
    829  return EncodeWithAsyncCallback(std::move(aInputs));
    830 }
    831 
    832 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::Drain() {
    833  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    834  MOZ_ASSERT(mEncoder);
    835 
    836  if (!IsAsync()) {
    837    return ResultToPromise(DrainSync());
    838  }
    839  if (!mIsRealtime) {
    840    return ResultToPromise(DrainAsync());
    841  }
    842  return DrainWithAsyncCallback();
    843 }
    844 
    845 static HRESULT CreateSample(RefPtr<IMFSample>* aOutSample, DWORD aSize,
    846                            DWORD aAlignment) {
    847  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    848 
    849  RefPtr<IMFSample> sample;
    850  MFT_RETURN_IF_FAILED_S(wmf::MFCreateSample(getter_AddRefs(sample)));
    851 
    852  RefPtr<IMFMediaBuffer> buffer;
    853  MFT_RETURN_IF_FAILED_S(wmf::MFCreateAlignedMemoryBuffer(
    854      aSize, aAlignment, getter_AddRefs(buffer)));
    855 
    856  MFT_RETURN_IF_FAILED_S(sample->AddBuffer(buffer));
    857 
    858  *aOutSample = sample.forget();
    859 
    860  return S_OK;
    861 }
    862 
    863 HRESULT
    864 MFTEncoder::CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize) {
    865  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    866 
    867  return CreateSample(
    868      aSample, aSize,
    869      mInputStreamInfo.cbAlignment > 0 ? mInputStreamInfo.cbAlignment - 1 : 0);
    870 }
    871 
    872 Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::EncodeSync(
    873    nsTArray<InputSample>&& aInputs) {
    874  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    875  MOZ_ASSERT(mEncoder);
    876  MOZ_ASSERT(mState == State::Inited);
    877 
    878  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
    879  SetState(State::Encoding);
    880 
    881  EncodedData outputs;
    882 
    883  // Follow steps in
    884  // https://learn.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data
    885  for (auto& input : aInputs) {
    886    HRESULT hr = ProcessInput(std::move(input));
    887    if (FAILED(hr)) {
    888      return Err(MediaResult(
    889          NS_ERROR_DOM_MEDIA_FATAL_ERR,
    890          RESULT_DETAIL("ProcessInput error: %s", ErrorMessage(hr).get())));
    891    }
    892 
    893    DWORD flags = 0;
    894    hr = mEncoder->GetOutputStatus(&flags);
    895    if (FAILED(hr) && hr != E_NOTIMPL) {
    896      return Err(MediaResult(
    897          NS_ERROR_DOM_MEDIA_FATAL_ERR,
    898          RESULT_DETAIL("GetOutputStatus error: %s", ErrorMessage(hr).get())));
    899    }
    900 
    901    if (hr == E_NOTIMPL ||
    902        (hr == S_OK && (flags & MFT_OUTPUT_STATUS_SAMPLE_READY))) {
    903      outputs.AppendElements(MOZ_TRY(PullOutputs().mapErr([](HRESULT e) {
    904        return MediaResult(
    905            NS_ERROR_DOM_MEDIA_FATAL_ERR,
    906            RESULT_DETAIL("PullOutputs error: %s", ErrorMessage(e).get()));
    907      })));
    908    }
    909  }
    910 
    911  exitWithError.release();
    912  SetState(State::Inited);
    913  return outputs;
    914 }
    915 
    916 Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::DrainSync() {
    917  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    918  MOZ_ASSERT(mEncoder);
    919  MOZ_ASSERT(mState == State::Inited);
    920 
    921  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
    922  SetState(State::Draining);
    923 
    924  // Follow step 7 in
    925  // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data
    926  HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
    927  if (FAILED(hr)) {
    928    return Err(MediaResult(
    929        NS_ERROR_DOM_MEDIA_FATAL_ERR,
    930        RESULT_DETAIL("SendMFTMessage MFT_MESSAGE_COMMAND_DRAIN error: %s",
    931                      ErrorMessage(hr).get())));
    932  }
    933 
    934  EncodedData outputs = MOZ_TRY(PullOutputs().mapErr([](HRESULT e) {
    935    return MediaResult(
    936        NS_ERROR_DOM_MEDIA_FATAL_ERR,
    937        RESULT_DETAIL("PullOutputs error: %s", ErrorMessage(e).get()));
    938  }));
    939  exitWithError.release();
    940  SetState(State::Inited);
    941  return outputs;
    942 }
    943 
    944 Result<MFTEncoder::EncodedData, HRESULT> MFTEncoder::PullOutputs() {
    945  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    946  MOZ_ASSERT(mEncoder);
    947 
    948  EncodedData outputs;
    949  MPEGHeader header;
    950  while (true) {
    951    auto r = GetOutputOrNewHeader();
    952    if (r.isErr()) {
    953      HRESULT e = r.unwrapErr();
    954      if (e == MF_E_TRANSFORM_NEED_MORE_INPUT) {
    955        MFT_ENC_LOGD("Need more inputs");
    956        // Step 4 or 8 in
    957        // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data
    958        break;
    959      }
    960      MFT_ENC_LOGE("GetOutputOrNewHeader failed: %s", ErrorMessage(e).get());
    961      return Err(e);
    962    }
    963 
    964    OutputResult result = r.unwrap();
    965    if (result.IsHeader()) {
    966      header = result.TakeHeader();
    967      MFT_ENC_LOGD(
    968          "Obtained new MPEG header, attempting to retrieve output again");
    969      continue;
    970    }
    971 
    972    MOZ_ASSERT(result.IsSample());
    973    outputs.AppendElement(OutputSample{.mSample = result.TakeSample()});
    974    if (!header.IsEmpty()) {
    975      outputs.LastElement().mHeader = std::move(header);
    976    }
    977  }
    978 
    979  MFT_ENC_LOGV("%zu outputs pulled", outputs.Length());
    980  return outputs;
    981 }
    982 
    983 Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::EncodeAsync(
    984    nsTArray<InputSample>&& aInputs) {
    985  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
    986  MOZ_ASSERT(mEncoder);
    987  MOZ_ASSERT(mState == State::Inited);
    988 
    989  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
    990  SetState(State::Encoding);
    991 
    992  size_t inputCounts = aInputs.Length();
    993  for (auto& input : aInputs) {
    994    mPendingInputs.push_back(std::move(input));
    995  }
    996 
    997  MOZ_TRY(ProcessPendingInputs().mapErr([](HRESULT hr) {
    998    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    999                       RESULT_DETAIL("ProcessPendingInputs error: %s",
   1000                                     ErrorMessage(hr).get()));
   1001  }));
   1002  MFT_ENC_LOGV("%zu inputs processed, %zu inputs remain, inputs needed: %zu",
   1003               inputCounts - mPendingInputs.size(), mPendingInputs.size(),
   1004               mNumNeedInput);
   1005 
   1006  // If the underlying system signaled that more input is needed, continue
   1007  // processing inputs until either no more input is required or there are no
   1008  // pending inputs left.
   1009  MOZ_TRY(ProcessPendingEvents().mapErr([](HRESULT hr) {
   1010    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1011                       RESULT_DETAIL("ProcessPendingEvents error: %s",
   1012                                     ErrorMessage(hr).get()));
   1013  }));
   1014  MOZ_ASSERT(mNumNeedInput == 0 || mPendingInputs.empty());
   1015 
   1016  exitWithError.release();
   1017  SetState(State::Inited);
   1018  EncodedData outputs = std::move(mOutputs);
   1019  return outputs;
   1020 }
   1021 
   1022 Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::DrainAsync() {
   1023  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1024  MOZ_ASSERT(mEncoder);
   1025  MOZ_ASSERT(mState == State::Inited);
   1026 
   1027  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
   1028  SetState(mPendingInputs.empty() ? State::Draining : State::PreDraining);
   1029 
   1030  // Ensure all pending inputs are processed before initiating the drain. If any
   1031  // pending inputs remain, the input-needed count must be zero; otherwise, they
   1032  // would have been processed in Encode().
   1033  MOZ_ASSERT_IF(!mPendingInputs.empty(), mNumNeedInput == 0);
   1034  while (!mPendingInputs.empty()) {
   1035    MFT_ENC_LOGV("Pending inputs: %zu, inputs needed: %zu",
   1036                 mPendingInputs.size(), mNumNeedInput);
   1037    // Prompt the MFT to process pending inputs or collect any pending outputs,
   1038    // which may allow more inputs to be accepted.
   1039    MOZ_TRY(ProcessPendingEvents().mapErr([](HRESULT hr) {
   1040      return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1041                         RESULT_DETAIL("ProcessPendingEvents error: %s",
   1042                                       ErrorMessage(hr).get()));
   1043    }));
   1044  }
   1045 
   1046  if (mState == State::PreDraining) {
   1047    SetState(State::Draining);
   1048  }
   1049 
   1050  HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
   1051  if (FAILED(hr)) {
   1052    return Err(MediaResult(
   1053        NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1054        RESULT_DETAIL("SendMFTMessage MFT_MESSAGE_COMMAND_DRAIN error: %s",
   1055                      ErrorMessage(hr).get())));
   1056  }
   1057 
   1058  ProcessedResults results;
   1059  do {
   1060    results = MOZ_TRY(ProcessPendingEvents().mapErr([](HRESULT hr) {
   1061      return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1062                         RESULT_DETAIL("ProcessPendingEvents error: %s",
   1063                                       ErrorMessage(hr).get()));
   1064    }));
   1065  } while (!results.contains(ProcessedResult::DrainComplete));
   1066 
   1067  exitWithError.release();
   1068  SetState(State::Inited);
   1069  EncodedData outputs = std::move(mOutputs);
   1070  return outputs;
   1071 }
   1072 
   1073 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::EncodeWithAsyncCallback(
   1074    nsTArray<InputSample>&& aInputs) {
   1075  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1076  MOZ_ASSERT(mEncoder);
   1077  MOZ_ASSERT(mEncodePromise.IsEmpty());
   1078  MOZ_ASSERT(mState == State::Inited);
   1079 
   1080  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
   1081  SetState(State::Encoding);
   1082 
   1083  size_t inputCounts = aInputs.Length();
   1084  for (auto& input : aInputs) {
   1085    mPendingInputs.push_back(std::move(input));
   1086  }
   1087 
   1088  auto inputsProcessed = ProcessPendingInputs();
   1089  if (inputsProcessed.isErr()) {
   1090    return EncodePromise::CreateAndReject(
   1091        MediaResult(
   1092            NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1093            RESULT_DETAIL("ProcessPendingInputs error: %s",
   1094                          ErrorMessage(inputsProcessed.unwrapErr()).get())),
   1095        __func__);
   1096  }
   1097  MFT_ENC_LOGV("%zu inputs processed, %zu inputs remain, inputs needed: %zu",
   1098               inputCounts - mPendingInputs.size(), mPendingInputs.size(),
   1099               mNumNeedInput);
   1100 
   1101  RefPtr<MFTEncoder::EncodePromise> p = mEncodePromise.Ensure(__func__);
   1102  exitWithError.release();
   1103 
   1104  // TODO: Calculate time duration based on frame rate instead of a fixed value.
   1105  auto timerResult = NS_NewTimerWithCallback(
   1106      [self = RefPtr{this}](nsITimer* aTimer) {
   1107        if (!self->mEncoder) {
   1108          MFT_ENC_SLOGW(
   1109              "Timer callback aborted: encoder has already been shut down");
   1110          return;
   1111        }
   1112 
   1113        MFT_ENC_SLOGV("Timer callback: resolving pending encode promise");
   1114        self->MaybeResolveOrRejectEncodePromise();
   1115      },
   1116      TimeDuration::FromMilliseconds(20), nsITimer::TYPE_ONE_SHOT,
   1117      "EncodingProgressChecker"_ns, GetCurrentSerialEventTarget());
   1118  if (timerResult.isErr()) {
   1119    MFT_ENC_LOGE(
   1120        "Failed to set an encoding progress checker. Resolve encode promise "
   1121        "directly");
   1122    MaybeResolveOrRejectEncodePromise();
   1123    return p;
   1124  }
   1125 
   1126  mTimer = timerResult.unwrap();
   1127  return p;
   1128 }
   1129 
   1130 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::DrainWithAsyncCallback() {
   1131  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1132  MOZ_ASSERT(mEncoder);
   1133 
   1134  return PrepareForDrain()->Then(
   1135      GetCurrentSerialEventTarget(), __func__,
   1136      [self = RefPtr{this}](MFTEncoder::EncodedData&& aOutput) {
   1137        MFT_ENC_SLOGV("All pending inputs are processed, now starts draining");
   1138        self->mOutputs.AppendElements(std::move(aOutput));
   1139        return self->StartDraining();
   1140      },
   1141      [self = RefPtr{this}](const MediaResult& aError) {
   1142        MFT_ENC_SLOGE("PrepareForDrain failed: %s", aError.Description().get());
   1143        return EncodePromise::CreateAndReject(aError, __func__);
   1144      });
   1145 }
   1146 
   1147 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::PrepareForDrain() {
   1148  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1149  MOZ_ASSERT(mEncoder);
   1150  MOZ_ASSERT(mPreDrainPromise.IsEmpty());
   1151  MOZ_ASSERT(mState == State::Inited);
   1152 
   1153  SetState(State::PreDraining);
   1154  MFT_ENC_LOGV("Pending inputs: %zu, inputs needed: %zu", mPendingInputs.size(),
   1155               mNumNeedInput);
   1156 
   1157  if (mPendingInputs.empty()) {
   1158    MFT_ENC_LOGV("No pending inputs, leave %s state immediately",
   1159                 EnumValueToString(mState));
   1160    SetState(State::Inited);
   1161    return EncodePromise::CreateAndResolve(std::move(mOutputs), __func__);
   1162  }
   1163 
   1164  MOZ_ASSERT(mNumNeedInput == 0);
   1165  MFT_ENC_LOGV("Waiting for %zu pending inputs to be processed",
   1166               mPendingInputs.size());
   1167 
   1168  return mPreDrainPromise.Ensure(__func__);
   1169 }
   1170 
   1171 RefPtr<MFTEncoder::EncodePromise> MFTEncoder::StartDraining() {
   1172  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1173  MOZ_ASSERT(mEncoder);
   1174  MOZ_ASSERT(mDrainPromise.IsEmpty());
   1175  MOZ_ASSERT(mPendingInputs.empty());
   1176  MOZ_ASSERT(mState == State::Inited);
   1177 
   1178  auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
   1179  SetState(State::Draining);
   1180 
   1181  HRESULT r = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
   1182  if (FAILED(r)) {
   1183    return EncodePromise::CreateAndReject(
   1184        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1185                    RESULT_DETAIL("SendMFTMessage COMMAND_DRAIN failed: %s",
   1186                                  ErrorMessage(r).get())),
   1187        __func__);
   1188  }
   1189 
   1190  RefPtr<MFTEncoder::EncodePromise> p = mDrainPromise.Ensure(__func__);
   1191  exitWithError.release();
   1192  return p;
   1193 }
   1194 
   1195 void MFTEncoder::EventHandler(MediaEventType aEventType, HRESULT aStatus) {
   1196  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1197 
   1198  MFT_ENC_LOGV("[state: %s] Get event: %s, status: %s",
   1199               EnumValueToString(mState), MediaEventTypeStr(aEventType),
   1200               ErrorMessage(aStatus).get());
   1201 
   1202  if (!mAsyncEventSource) {
   1203    MFT_ENC_LOGW("Async event source is not initialized or destroyed");
   1204    return;
   1205  }
   1206 
   1207  MOZ_ASSERT(mState != State::Uninited);
   1208 
   1209  auto errorHandler = [&](MediaResult&& aError) {
   1210    MFT_ENC_LOGE("%s", aError.Message().get());
   1211    mPendingError = aError;
   1212    switch (mState) {
   1213      case State::Encoding:
   1214        MaybeResolveOrRejectEncodePromise();
   1215        break;
   1216      case State::Draining:
   1217        MaybeResolveOrRejectDrainPromise();
   1218        break;
   1219      case State::PreDraining:
   1220        MaybeResolveOrRejectPreDrainPromise();
   1221        break;
   1222      default:
   1223        MFT_ENC_LOGW("Received error in state %s", EnumValueToString(mState));
   1224    }
   1225  };
   1226 
   1227  if (FAILED(aStatus)) {
   1228    errorHandler(
   1229        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1230                    RESULT_DETAIL("Received error status: %s for event %s",
   1231                                  ErrorMessage(aStatus).get(),
   1232                                  MediaEventTypeStr(aEventType))));
   1233    return;
   1234  }
   1235 
   1236  auto processed = ProcessEvent(aEventType);
   1237  if (processed.isErr()) {
   1238    HRESULT hr = processed.unwrapErr();
   1239    errorHandler(MediaResult(
   1240        NS_ERROR_DOM_MEDIA_FATAL_ERR,
   1241        RESULT_DETAIL("ProcessEvent error: %s for event %s",
   1242                      ErrorMessage(hr).get(), MediaEventTypeStr(aEventType))));
   1243    return;
   1244  }
   1245 
   1246  const bool waitForOutput =
   1247      StaticPrefs::media_wmf_encoder_realtime_wait_for_output();
   1248 
   1249  ProcessedResult result = processed.unwrap();
   1250  MFT_ENC_LOGV(
   1251      "%s processed: %s\n\tpending inputs: %zu\n\tinput needed: %zu\n\tpending "
   1252      "outputs: %zu (waitForOutput=%s)",
   1253      MediaEventTypeStr(aEventType), MFTEncoder::EnumValueToString(result),
   1254      mPendingInputs.size(), mNumNeedInput, mOutputs.Length(),
   1255      waitForOutput ? "yes" : "no");
   1256  switch (result) {
   1257    case ProcessedResult::AllAvailableInputsProcessed:
   1258      // Since mNumNeedInput was incremented in ProcessEvent(), before calling
   1259      // ProcessInput(), a result indicating no input was processed means there
   1260      // were not enough pending inputs in the queue.
   1261      MOZ_ASSERT(mPendingInputs.empty());
   1262      // If EventHandler is in the PreDraining state here, it means there were
   1263      // pending inputs to process before draining started. Processing those
   1264      // inputs should have produced InputProcessed results, and the state
   1265      // should have transitioned out of PreDraining. Therefore, we should not
   1266      // still be in PreDraining at this point.
   1267      MOZ_ASSERT(mState != State::PreDraining);
   1268      [[fallthrough]];
   1269    case ProcessedResult::InputProcessed:
   1270      if (mState == State::Encoding) {
   1271        // In realtime mode, we could resolve the encode promise only upon
   1272        // receiving an output. However, since the performance gain is minor,
   1273        // unless the wait-for-output setting is enabled, it's better to prevent
   1274        // the encode promise from being resolved by the timer callback if no
   1275        // output is produced in time.
   1276        if (!waitForOutput) {
   1277          MaybeResolveOrRejectEncodePromise();
   1278        }
   1279      } else if (mState == State::PreDraining) {
   1280        if (mPendingInputs.empty()) {
   1281          MaybeResolveOrRejectPreDrainPromise();
   1282        }
   1283      }
   1284      break;
   1285    case ProcessedResult::OutputHeaderYielded:
   1286      if (mState == State::Encoding) {
   1287        if (!waitForOutput) {
   1288          MaybeResolveOrRejectEncodePromise();
   1289        }
   1290      }
   1291      break;
   1292    case ProcessedResult::OutputDataYielded:
   1293      if (mState == State::Encoding) {
   1294        MaybeResolveOrRejectEncodePromise();
   1295      }
   1296      break;
   1297    case ProcessedResult::DrainComplete:
   1298      MOZ_ASSERT(mState == State::Draining);
   1299      MaybeResolveOrRejectDrainPromise();
   1300      break;
   1301    default:
   1302      MOZ_ASSERT_UNREACHABLE(
   1303          "Unexpected ProcessedResult value in EventHandler");
   1304  }
   1305 
   1306  mAsyncEventSource->BeginEventListening();
   1307 }
   1308 
   1309 void MFTEncoder::MaybeResolveOrRejectEncodePromise() {
   1310  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1311  MOZ_ASSERT(mEncoder);
   1312 
   1313  if (mEncodePromise.IsEmpty()) {
   1314    MFT_ENC_LOGV("[%s] No encode promise to resolve or reject",
   1315                 EnumValueToString(mState));
   1316    return;
   1317  }
   1318 
   1319  MOZ_ASSERT(mState == State::Encoding);
   1320 
   1321  MFT_ENC_LOGV("Resolving (%zu outputs ) or rejecting encode promise (%s)",
   1322               mOutputs.Length(),
   1323               NS_FAILED(mPendingError.Code())
   1324                   ? mPendingError.Description().get()
   1325                   : "no error");
   1326 
   1327  if (mTimer) {
   1328    mTimer->Cancel();
   1329    mTimer = nullptr;
   1330    MFT_ENC_LOGV("Encode timer cancelled");
   1331  }
   1332 
   1333  if (NS_FAILED(mPendingError.Code())) {
   1334    SetState(State::Error);
   1335    mEncodePromise.Reject(mPendingError, __func__);
   1336    mPendingError = NS_OK;
   1337    return;
   1338  }
   1339 
   1340  mEncodePromise.Resolve(std::move(mOutputs), __func__);
   1341  SetState(State::Inited);
   1342 }
   1343 
   1344 void MFTEncoder::MaybeResolveOrRejectDrainPromise() {
   1345  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1346  MOZ_ASSERT(mEncoder);
   1347 
   1348  if (mDrainPromise.IsEmpty()) {
   1349    MFT_ENC_LOGV("[%s] No drain promise to resolve or reject",
   1350                 EnumValueToString(mState));
   1351    return;
   1352  }
   1353 
   1354  MOZ_ASSERT(mState == State::Draining);
   1355 
   1356  MFT_ENC_LOGV("Resolving (%zu outputs ) or rejecting drain promise (%s)",
   1357               mOutputs.Length(),
   1358               NS_FAILED(mPendingError.Code())
   1359                   ? mPendingError.Description().get()
   1360                   : "no error");
   1361 
   1362  if (NS_FAILED(mPendingError.Code())) {
   1363    SetState(State::Error);
   1364    mDrainPromise.Reject(mPendingError, __func__);
   1365    mPendingError = NS_OK;
   1366    return;
   1367  }
   1368 
   1369  mDrainPromise.Resolve(std::move(mOutputs), __func__);
   1370  SetState(State::Inited);
   1371 }
   1372 
   1373 void MFTEncoder::MaybeResolveOrRejectPreDrainPromise() {
   1374  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1375  MOZ_ASSERT(mEncoder);
   1376 
   1377  if (mPreDrainPromise.IsEmpty()) {
   1378    MFT_ENC_LOGV("[%s] No pre-drain promise to resolve or reject",
   1379                 EnumValueToString(mState));
   1380    return;
   1381  }
   1382 
   1383  MOZ_ASSERT(mState == State::PreDraining);
   1384 
   1385  MFT_ENC_LOGV("Resolving pre-drain promise (%zu outputs ) or rejecting (%s)",
   1386               mOutputs.Length(),
   1387               NS_FAILED(mPendingError.Code())
   1388                   ? mPendingError.Description().get()
   1389                   : "no error");
   1390 
   1391  if (NS_FAILED(mPendingError.Code())) {
   1392    SetState(State::Error);
   1393    mPreDrainPromise.Reject(mPendingError, __func__);
   1394    mPendingError = NS_OK;
   1395    return;
   1396  }
   1397 
   1398  MOZ_ASSERT(mPendingInputs.empty());
   1399  mPreDrainPromise.Resolve(std::move(mOutputs), __func__);
   1400  SetState(State::Inited);
   1401 }
   1402 
   1403 void MFTEncoder::MaybeResolveOrRejectAnyPendingPromise(
   1404    const MediaResult& aResult) {
   1405  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1406 
   1407  if (NS_FAILED(aResult.Code())) {
   1408    MFT_ENC_LOGW(
   1409        "[%s] Rejecting pending promises with error: %s (previous pending "
   1410        "error: %s)",
   1411        EnumValueToString(mState), aResult.Description().get(),
   1412        mPendingError.Description().get());
   1413    mPendingError = aResult;
   1414  }
   1415 
   1416  MaybeResolveOrRejectEncodePromise();
   1417  MaybeResolveOrRejectPreDrainPromise();
   1418  MaybeResolveOrRejectDrainPromise();
   1419 }
   1420 
   1421 Result<MFTEncoder::ProcessedResults, HRESULT>
   1422 MFTEncoder::ProcessPendingEvents() {
   1423  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1424  MOZ_ASSERT(mEncoder);
   1425  MOZ_ASSERT(mAsyncEventSource);
   1426 
   1427  ProcessedResults results;
   1428  while (true) {
   1429    auto got = GetPendingEvent();
   1430    if (got.isErr()) {
   1431      HRESULT hr = got.unwrapErr();
   1432      if (hr == MF_E_NO_EVENTS_AVAILABLE) {
   1433        MFT_ENC_LOGV("No more pending events");
   1434        break;
   1435      }
   1436      MFT_ENC_LOGE("GetPendingEvent error: %s", ErrorMessage(hr).get());
   1437      return Err(hr);
   1438    }
   1439 
   1440    MediaEventType event = got.unwrap();
   1441    MFT_ENC_LOGV("Processing pending event: %s", MediaEventTypeStr(event));
   1442    ProcessedResult result = MOZ_TRY(ProcessEvent(event));
   1443    MFT_ENC_LOGV("event processed: %s", MFTEncoder::EnumValueToString(result));
   1444    results += result;
   1445  }
   1446 
   1447  return results;
   1448 }
   1449 
   1450 Result<MFTEncoder::ProcessedResult, HRESULT> MFTEncoder::ProcessEvent(
   1451    MediaEventType aType) {
   1452  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1453  MOZ_ASSERT(mEncoder);
   1454 
   1455  switch (aType) {
   1456    case METransformNeedInput:
   1457      ++mNumNeedInput;
   1458      return ProcessInput();
   1459    case METransformHaveOutput:
   1460      return ProcessOutput();
   1461    case METransformDrainComplete:
   1462      return ProcessDrainComplete();
   1463    default:
   1464      MFT_ENC_LOGE("Unsupported event type: %s", MediaEventTypeStr(aType));
   1465      break;
   1466  }
   1467  return Err(E_UNEXPECTED);
   1468 }
   1469 
   1470 Result<MFTEncoder::ProcessedResult, HRESULT> MFTEncoder::ProcessInput() {
   1471  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1472  MOZ_ASSERT(mEncoder);
   1473 
   1474  MFT_ENC_LOGV("Inputs needed: %zu, pending inputs: %zu", mNumNeedInput,
   1475               mPendingInputs.size());
   1476  if (mNumNeedInput == 0 || mPendingInputs.empty()) {
   1477    return ProcessedResult::AllAvailableInputsProcessed;
   1478  }
   1479 
   1480  auto input = mPendingInputs.front();
   1481  mPendingInputs.pop_front();
   1482  MFT_RETURN_ERROR_IF_FAILED(ProcessInput(std::move(input)));
   1483  --mNumNeedInput;
   1484 
   1485  return ProcessedResult::InputProcessed;
   1486 }
   1487 
   1488 Result<MFTEncoder::ProcessedResult, HRESULT> MFTEncoder::ProcessOutput() {
   1489  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1490  MOZ_ASSERT(mEncoder);
   1491 
   1492  OutputResult result = MOZ_TRY(GetOutputOrNewHeader());
   1493  if (result.IsHeader()) {
   1494    mOutputHeader = result.TakeHeader();
   1495    MFT_ENC_LOGD("Got new MPEG header, size: %zu", mOutputHeader.Length());
   1496    return ProcessedResult::OutputHeaderYielded;
   1497  }
   1498 
   1499  MOZ_ASSERT(result.IsSample());
   1500  mOutputs.AppendElement(OutputSample{.mSample = result.TakeSample()});
   1501  if (!mOutputHeader.IsEmpty()) {
   1502    mOutputs.LastElement().mHeader = std::move(mOutputHeader);
   1503  }
   1504  return ProcessedResult::OutputDataYielded;
   1505 }
   1506 
   1507 Result<MFTEncoder::ProcessedResult, HRESULT>
   1508 MFTEncoder::ProcessDrainComplete() {
   1509  // After draining is complete, the MFT will not emit another
   1510  // METransformNeedInput event until it receives an
   1511  // MFT_MESSAGE_NOTIFY_START_OF_STREAM message.
   1512  MFT_RETURN_ERROR_IF_FAILED(
   1513      SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0));
   1514  MFT_ENC_LOGV("Drain complete, resetting inputs needed(%zu) to 0",
   1515               mNumNeedInput);
   1516  mNumNeedInput = 0;
   1517  return ProcessedResult::DrainComplete;
   1518 }
   1519 
   1520 Result<MFTEncoder::ProcessedResult, HRESULT>
   1521 MFTEncoder::ProcessPendingInputs() {
   1522  while (!mPendingInputs.empty()) {
   1523    auto r = MOZ_TRY(ProcessInput());
   1524    if (r == ProcessedResult::AllAvailableInputsProcessed) {
   1525      break;
   1526    }
   1527  }
   1528  return ProcessedResult::AllAvailableInputsProcessed;
   1529 }
   1530 
   1531 Result<MediaEventType, HRESULT> MFTEncoder::GetPendingEvent() {
   1532  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1533  MOZ_ASSERT(mEncoder);
   1534  MOZ_ASSERT(mAsyncEventSource);
   1535  MOZ_ASSERT(!mIsRealtime);
   1536  return mAsyncEventSource->GetEvent(MF_EVENT_FLAG_NO_WAIT);
   1537 }
   1538 
   1539 Result<MFTEncoder::OutputResult, HRESULT> MFTEncoder::GetOutputOrNewHeader() {
   1540  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1541  MOZ_ASSERT(mEncoder);
   1542 
   1543  RefPtr<IMFSample> sample;
   1544  DWORD status = 0;
   1545  DWORD bufStatus = 0;
   1546 
   1547  HRESULT hr = ProcessOutput(sample, status, bufStatus);
   1548  MFT_ENC_LOGV(
   1549      "output processed: %s, status: 0x%lx, output buffer status: 0x%lx",
   1550      ErrorMessage(hr).get(), status, bufStatus);
   1551 
   1552  if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
   1553    if (bufStatus & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) {
   1554      MFT_ENC_LOGW("output buffer format changed, updating output type");
   1555      MFT_RETURN_ERROR_IF_FAILED(UpdateOutputType());
   1556      return OutputResult(MOZ_TRY(GetMPEGSequenceHeader()));
   1557    }
   1558    // TODO: We should query for updated stream identifiers here. For now,
   1559    // handle this as an error.
   1560    MFT_ENC_LOGE("Stream identifiers changed");
   1561    return Err(hr);
   1562  }
   1563 
   1564  if (FAILED(hr)) {
   1565    return Err(hr);
   1566  }
   1567 
   1568  MOZ_ASSERT(sample);
   1569  return OutputResult(sample.forget());
   1570 }
   1571 
   1572 HRESULT MFTEncoder::UpdateOutputType() {
   1573  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1574  MOZ_ASSERT(mEncoder);
   1575  // Per Microsoft's documentation:
   1576  // https://docs.microsoft.com/en-us/windows/win32/medfound/handling-stream-changes#output-type
   1577  RefPtr<IMFMediaType> outputType;
   1578  MFT_RETURN_IF_FAILED(mEncoder->GetOutputAvailableType(
   1579      mOutputStreamID, 0, getter_AddRefs(outputType)));
   1580  MFT_RETURN_IF_FAILED(mEncoder->SetOutputType(mOutputStreamID, outputType, 0));
   1581  MFT_ENC_LOGW("stream format has been renegotiated for output stream %lu",
   1582               mOutputStreamID);
   1583  return S_OK;
   1584 }
   1585 
   1586 HRESULT MFTEncoder::ProcessOutput(RefPtr<IMFSample>& aSample,
   1587                                  DWORD& aOutputStatus, DWORD& aBufferStatus) {
   1588  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1589  MOZ_ASSERT(mEncoder);
   1590 
   1591  MFT_OUTPUT_DATA_BUFFER output = {.dwStreamID = mOutputStreamID,
   1592                                   .pSample = nullptr,
   1593                                   .dwStatus = 0,
   1594                                   .pEvents = nullptr};
   1595  RefPtr<IMFSample> sample;
   1596  if (!mOutputStreamProvidesSample) {
   1597    MFT_RETURN_IF_FAILED(CreateSample(&sample, mOutputStreamInfo.cbSize,
   1598                                      mOutputStreamInfo.cbAlignment > 1
   1599                                          ? mOutputStreamInfo.cbAlignment - 1
   1600                                          : 0));
   1601    output.pSample = sample;
   1602  }
   1603 
   1604  HRESULT hr = mEncoder->ProcessOutput(0, 1, &output, &aOutputStatus);
   1605  aBufferStatus = output.dwStatus;
   1606  if (output.pEvents) {
   1607    MFT_ENC_LOGW("Discarding events from ProcessOutput");
   1608    output.pEvents->Release();
   1609    output.pEvents = nullptr;
   1610  }
   1611 
   1612  if (FAILED(hr)) {
   1613    return hr;
   1614  }
   1615 
   1616  aSample = output.pSample;
   1617  if (mOutputStreamProvidesSample) {
   1618    // Release MFT provided sample.
   1619    output.pSample->Release();
   1620    output.pSample = nullptr;
   1621  }
   1622 
   1623  return hr;
   1624 }
   1625 
   1626 HRESULT MFTEncoder::ProcessInput(InputSample&& aInput) {
   1627  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1628  MOZ_ASSERT(mEncoder);
   1629 
   1630  if (aInput.mKeyFrameRequested) {
   1631    VARIANT v = {.vt = VT_UI4, .ulVal = 1};
   1632    mConfig->SetValue(&CODECAPI_AVEncVideoForceKeyFrame, &v);
   1633  }
   1634  MFT_RETURN_IF_FAILED(
   1635      mEncoder->ProcessInput(mInputStreamID, aInput.mSample, 0));
   1636  return S_OK;
   1637 }
   1638 
   1639 Result<nsTArray<UINT8>, HRESULT> MFTEncoder::GetMPEGSequenceHeader() {
   1640  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1641  MOZ_ASSERT(mEncoder);
   1642 
   1643  RefPtr<IMFMediaType> outputType;
   1644  MFT_RETURN_ERROR_IF_FAILED(mEncoder->GetOutputCurrentType(
   1645      mOutputStreamID, getter_AddRefs(outputType)));
   1646  UINT32 length = 0;
   1647  HRESULT hr = outputType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &length);
   1648  if (hr == MF_E_ATTRIBUTENOTFOUND) {
   1649    MFT_ENC_LOGW("GetBlobSize MF_MT_MPEG_SEQUENCE_HEADER: not found");
   1650    return nsTArray<UINT8>();
   1651  }
   1652  if (FAILED(hr)) {
   1653    MFT_ENC_LOGE("GetBlobSize MF_MT_MPEG_SEQUENCE_HEADER error: %s",
   1654                 ErrorMessage(hr).get());
   1655    return Err(hr);
   1656  }
   1657  if (length == 0) {
   1658    MFT_ENC_LOGW("GetBlobSize MF_MT_MPEG_SEQUENCE_HEADER: no header");
   1659    return nsTArray<UINT8>();
   1660  }
   1661  MFT_ENC_LOGD("GetBlobSize MF_MT_MPEG_SEQUENCE_HEADER: %u", length);
   1662 
   1663  nsTArray<UINT8> header;
   1664  header.SetCapacity(length);
   1665  hr = outputType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, header.Elements(),
   1666                           length, nullptr);
   1667  header.SetLength(SUCCEEDED(hr) ? length : 0);
   1668 
   1669  return header;
   1670 }
   1671 
   1672 void MFTEncoder::SetState(State aState) {
   1673  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
   1674 
   1675  MFT_ENC_LOGD("SetState: %s -> %s", EnumValueToString(mState),
   1676               EnumValueToString(aState));
   1677  mState = aState;
   1678 }
   1679 
   1680 #define MFT_EVTSRC_LOG(level, msg, ...)                                     \
   1681  MFT_LOG_INTERNAL(level, "MFTEventSource(0x%p)::%s: " msg, this, __func__, \
   1682                   ##__VA_ARGS__)
   1683 #define MFT_EVTSRC_SLOG(level, msg, ...) \
   1684  MFT_LOG_INTERNAL(level, "MFTEventSource::%s: " msg, __func__, ##__VA_ARGS__)
   1685 
   1686 #define MFT_EVTSRC_LOGD(msg, ...) MFT_EVTSRC_LOG(Debug, msg, ##__VA_ARGS__)
   1687 #define MFT_EVTSRC_LOGE(msg, ...) MFT_EVTSRC_LOG(Error, msg, ##__VA_ARGS__)
   1688 #define MFT_EVTSRC_LOGW(msg, ...) MFT_EVTSRC_LOG(Warning, msg, ##__VA_ARGS__)
   1689 #define MFT_EVTSRC_LOGV(msg, ...) MFT_EVTSRC_LOG(Verbose, msg, ##__VA_ARGS__)
   1690 
   1691 #define MFT_EVTSRC_SLOGW(msg, ...) MFT_EVTSRC_SLOG(Warning, msg, ##__VA_ARGS__)
   1692 
   1693 #define MFT_EVTSRC_RETURN_IF_FAILED(x) \
   1694  MFT_RETURN_IF_FAILED_IMPL(x, MFT_EVTSRC_LOGE)
   1695 #define MFT_EVTSRC_RETURN_ERROR_IF_FAILED(x) \
   1696  MFT_RETURN_ERROR_IF_FAILED_IMPL(x, MFT_EVTSRC_LOGE)
   1697 
   1698 MFTEventSource::MFTEventSource(
   1699    nsISerialEventTarget* aEncoderThread, MFTEncoder* aEncoder,
   1700    already_AddRefed<IMFMediaEventGenerator> aEventGenerator)
   1701    : mId(GenerateId()),
   1702      mEncoderThread(aEncoderThread),
   1703      mEncoder(aEncoder),
   1704      mEventGenerator(aEventGenerator, "MFTEventSource::mEventGenerator") {
   1705  MOZ_ASSERT(mEncoderThread);
   1706  auto g = mEventGenerator.Lock();
   1707  MOZ_ASSERT(!!g.ref());
   1708  MFT_EVTSRC_LOGD("(id %zu) created", mId);
   1709 }
   1710 
   1711 MFTEventSource::~MFTEventSource() {
   1712  MFT_EVTSRC_LOGD("(id %zu) destroyed", mId);
   1713  auto g = mEventGenerator.Lock();
   1714  *g = nullptr;
   1715 }
   1716 
   1717 Result<MediaEventType, HRESULT> MFTEventSource::GetEvent(DWORD aFlags) {
   1718  MOZ_ASSERT(mEncoderThread->IsOnCurrentThread());
   1719  MOZ_ASSERT(!CanForwardEvents());
   1720 
   1721  HRESULT hr = S_OK;
   1722  RefPtr<IMFMediaEvent> event;
   1723  {
   1724    auto g = mEventGenerator.Lock();
   1725    hr = g.ref()->GetEvent(aFlags, getter_AddRefs(event));
   1726  }
   1727  if (FAILED(hr)) {
   1728    if (hr == MF_E_NO_EVENTS_AVAILABLE) {
   1729      MFT_EVTSRC_LOGV("GetEvent: %s", ErrorMessage(hr).get());
   1730    } else {
   1731      MFT_EVTSRC_LOGE("GetEvent error: %s", ErrorMessage(hr).get());
   1732    }
   1733    return Err(hr);
   1734  }
   1735  MediaEventType type = MEUnknown;
   1736  MFT_EVTSRC_RETURN_ERROR_IF_FAILED(event->GetType(&type));
   1737  return type;
   1738 }
   1739 
   1740 HRESULT MFTEventSource::BeginEventListening() {
   1741  MOZ_ASSERT(mEncoderThread->IsOnCurrentThread());
   1742  MOZ_ASSERT(CanForwardEvents());
   1743 
   1744  MFT_EVTSRC_LOGV("(id %zu) starts waiting for event", mId);
   1745  HRESULT hr = S_OK;
   1746  {
   1747    auto g = mEventGenerator.Lock();
   1748    hr = g.ref()->BeginGetEvent(this, nullptr);
   1749  }
   1750  return hr;
   1751 }
   1752 
   1753 STDMETHODIMP MFTEventSource::GetParameters(DWORD* aFlags, DWORD* aQueue) {
   1754  MOZ_ASSERT(aFlags);
   1755  MOZ_ASSERT(aQueue);
   1756  *aFlags = MFASYNC_FAST_IO_PROCESSING_CALLBACK;
   1757  *aQueue = MFASYNC_CALLBACK_QUEUE_TIMER;
   1758  return S_OK;
   1759 }
   1760 
   1761 STDMETHODIMP MFTEventSource::Invoke(IMFAsyncResult* aResult) {
   1762  RefPtr<IMFMediaEvent> event;
   1763  {
   1764    auto g = mEventGenerator.Lock();
   1765    MFT_EVTSRC_RETURN_IF_FAILED(
   1766        g.ref()->EndGetEvent(aResult, getter_AddRefs(event)));
   1767  }
   1768 
   1769  MediaEventType type = MEUnknown;
   1770  MFT_EVTSRC_RETURN_IF_FAILED(event->GetType(&type));
   1771 
   1772  MFT_EVTSRC_LOGV("(id %zu) received event: %s", mId, MediaEventTypeStr(type));
   1773 
   1774  HRESULT status = S_OK;
   1775  MFT_EVTSRC_RETURN_IF_FAILED(event->GetStatus(&status));
   1776 
   1777  mEncoderThread->Dispatch(
   1778      NS_NewRunnableFunction(__func__,
   1779                             [type, status, id = mId, encoder = mEncoder]() {
   1780                               if (!encoder->mAsyncEventSource ||
   1781                                   encoder->mAsyncEventSource->mId != id) {
   1782                                 MFT_EVTSRC_SLOGW(
   1783                                     "Event %s from source %zu is stale",
   1784                                     MediaEventTypeStr(type), id);
   1785                                 return;
   1786                               }
   1787                               encoder->EventHandler(type, status);
   1788                             }),
   1789      NS_DISPATCH_NORMAL);
   1790 
   1791  return status;
   1792 }
   1793 
   1794 STDMETHODIMP MFTEventSource::QueryInterface(REFIID aIID, void** aPPV) {
   1795  const IID IID_IMFAsyncCallback = __uuidof(IMFAsyncCallback);
   1796  if (aIID == IID_IUnknown || aIID == IID_IMFAsyncCallback) {
   1797    *aPPV = static_cast<IMFAsyncCallback*>(this);
   1798    AddRef();
   1799    return S_OK;
   1800  }
   1801 
   1802  return E_NOINTERFACE;
   1803 }
   1804 
   1805 #undef MFT_EVTSRC_LOG
   1806 #undef MFT_EVTSRC_SLOG
   1807 #undef MFT_EVTSRC_LOGE
   1808 #undef MFT_EVTSRC_RETURN_IF_FAILED
   1809 #undef MFT_EVTSRC_RETURN_ERROR_IF_FAILED
   1810 
   1811 }  // namespace mozilla
   1812 
   1813 #undef MFT_ENC_SLOGE
   1814 #undef MFT_ENC_SLOGD
   1815 #undef MFT_ENC_LOGE
   1816 #undef MFT_ENC_LOGW
   1817 #undef MFT_ENC_LOGV
   1818 #undef MFT_ENC_LOGD
   1819 #undef MFT_RETURN_IF_FAILED
   1820 #undef MFT_RETURN_IF_FAILED_S
   1821 #undef MFT_RETURN_VALUE_IF_FAILED
   1822 #undef MFT_RETURN_VALUE_IF_FAILED_S
   1823 #undef MFT_RETURN_ERROR_IF_FAILED
   1824 #undef MFT_RETURN_ERROR_IF_FAILED_S
   1825 #undef MFT_RETURN_IF_FAILED_IMPL
   1826 #undef MFT_RETURN_VALUE_IF_FAILED_IMPL
   1827 #undef MFT_RETURN_ERROR_IF_FAILED_IMPL
   1828 #undef MFT_ENC_LOG
   1829 #undef MFT_ENC_SLOG
   1830 #undef MFT_LOG_INTERNAL
   1831 #undef AUTO_MFTENCODER_MARKER