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