/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MFTEncoder.h" #include "mozilla/Logging.h" #include "mozilla/WindowsProcessMitigations.h" #include "mozilla/StaticPrefs_media.h" #include "mozilla/mscom/Utils.h" #include "WMFUtils.h" // Missing from MinGW. #ifndef CODECAPI_AVEncAdaptiveMode # define STATIC_CODECAPI_AVEncAdaptiveMode \ 0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc, 0x1e, 0xfb, 0x1e DEFINE_CODECAPI_GUID(AVEncAdaptiveMode, "4419b185-da1f-4f53-bc76-097d0c1efb1e", 0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc, 0x1e, 0xfb, 0x1e) # define CODECAPI_AVEncAdaptiveMode \ DEFINE_CODECAPI_GUIDNAMED(AVEncAdaptiveMode) #endif #ifndef MF_E_NO_EVENTS_AVAILABLE # define MF_E_NO_EVENTS_AVAILABLE _HRESULT_TYPEDEF_(0xC00D3E80L) #endif #define MFT_ENC_LOGD(arg, ...) \ MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Debug, \ ("MFTEncoder(0x%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) #define MFT_ENC_LOGE(arg, ...) \ MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \ ("MFTEncoder(0x%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) #define MFT_ENC_SLOGD(arg, ...) \ MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Debug, \ ("MFTEncoder::%s: " arg, __func__, ##__VA_ARGS__)) #define MFT_ENC_SLOGE(arg, ...) \ MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \ ("MFTEncoder::%s: " arg, __func__, ##__VA_ARGS__)) namespace mozilla { extern LazyLogModule sPEMLog; static const char* ErrorStr(HRESULT hr) { switch (hr) { case S_OK: return "OK"; case MF_E_INVALIDMEDIATYPE: return "INVALIDMEDIATYPE"; case MF_E_INVALIDSTREAMNUMBER: return "INVALIDSTREAMNUMBER"; case MF_E_INVALIDTYPE: return "INVALIDTYPE"; case MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING: return "TRANSFORM_PROCESSING"; case MF_E_TRANSFORM_TYPE_NOT_SET: return "TRANSFORM_TYPE_NO_SET"; case MF_E_UNSUPPORTED_D3D_TYPE: return "UNSUPPORTED_D3D_TYPE"; case E_INVALIDARG: return "INVALIDARG"; case MF_E_NO_SAMPLE_DURATION: return "NO_SAMPLE_DURATION"; case MF_E_NO_SAMPLE_TIMESTAMP: return "NO_SAMPLE_TIMESTAMP"; case MF_E_NOTACCEPTING: return "NOTACCEPTING"; case MF_E_ATTRIBUTENOTFOUND: return "NOTFOUND"; case MF_E_BUFFERTOOSMALL: return "BUFFERTOOSMALL"; case E_NOTIMPL: return "NOTIMPL"; default: return "OTHER"; } } static const char* CodecStr(const GUID& aGUID) { if (IsEqualGUID(aGUID, MFVideoFormat_H264)) { return "H.264"; } else if (IsEqualGUID(aGUID, MFVideoFormat_VP80)) { return "VP8"; } else if (IsEqualGUID(aGUID, MFVideoFormat_VP90)) { return "VP9"; } else { return "Unsupported codec"; } } static UINT32 EnumEncoders(const GUID& aSubtype, IMFActivate**& aActivates, const bool aUseHW = true) { UINT32 num = 0; MFT_REGISTER_TYPE_INFO inType = {.guidMajorType = MFMediaType_Video, .guidSubtype = MFVideoFormat_NV12}; MFT_REGISTER_TYPE_INFO outType = {.guidMajorType = MFMediaType_Video, .guidSubtype = aSubtype}; HRESULT hr = S_OK; if (aUseHW) { if (IsWin32kLockedDown()) { // Some HW encoders use DXGI API and crash when locked down. // TODO: move HW encoding out of content process (bug 1754531). MFT_ENC_SLOGD("Don't use HW encoder when win32k locked down."); return 0; } hr = wmf::MFTEnumEx(MFT_CATEGORY_VIDEO_ENCODER, MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER, &inType, &outType, &aActivates, &num); if (FAILED(hr)) { MFT_ENC_SLOGE("enumerate HW encoder for %s: error=%s", CodecStr(aSubtype), ErrorStr(hr)); return 0; } if (num > 0) { return num; } } // Try software MFTs. hr = wmf::MFTEnumEx(MFT_CATEGORY_VIDEO_ENCODER, MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_SORTANDFILTER, &inType, &outType, &aActivates, &num); if (FAILED(hr)) { MFT_ENC_SLOGE("enumerate SW encoder for %s: error=%s", CodecStr(aSubtype), ErrorStr(hr)); return 0; } if (num == 0) { MFT_ENC_SLOGD("cannot find encoder for %s", CodecStr(aSubtype)); } return num; } static HRESULT GetFriendlyName(IMFActivate* aAttributes, nsCString& aName) { UINT32 len = 0; HRESULT hr = aAttributes->GetStringLength(MFT_FRIENDLY_NAME_Attribute, &len); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); if (len > 0) { ++len; // '\0'. WCHAR name[len]; if (SUCCEEDED(aAttributes->GetString(MFT_FRIENDLY_NAME_Attribute, name, len, nullptr))) { aName.Append(NS_ConvertUTF16toUTF8(name)); } } if (aName.Length() == 0) { aName.Append("Unknown MFT"); } return S_OK; } static void PopulateEncoderInfo(const GUID& aSubtype, nsTArray& aInfos) { IMFActivate** activates = nullptr; UINT32 num = EnumEncoders(aSubtype, activates); for (UINT32 i = 0; i < num; ++i) { MFTEncoder::Info info = {.mSubtype = aSubtype}; GetFriendlyName(activates[i], info.mName); aInfos.AppendElement(info); MFT_ENC_SLOGD(" [%s] %s\n", CodecStr(aSubtype), info.mName.Data()); activates[i]->Release(); activates[i] = nullptr; } CoTaskMemFree(activates); } Maybe MFTEncoder::GetInfo(const GUID& aSubtype) { nsTArray& infos = Infos(); for (auto i : infos) { if (IsEqualGUID(aSubtype, i.mSubtype)) { return Some(i); } } return Nothing(); } nsCString MFTEncoder::GetFriendlyName(const GUID& aSubtype) { Maybe info = GetInfo(aSubtype); return info ? info.ref().mName : "???"_ns; } // Called only once by Infos(). nsTArray MFTEncoder::Enumerate() { nsTArray infos; if (!wmf::MediaFoundationInitializer::HasInitialized()) { MFT_ENC_SLOGE("cannot init Media Foundation"); return infos; } PopulateEncoderInfo(MFVideoFormat_H264, infos); PopulateEncoderInfo(MFVideoFormat_VP90, infos); PopulateEncoderInfo(MFVideoFormat_VP80, infos); return infos; } nsTArray& MFTEncoder::Infos() { static nsTArray infos = Enumerate(); return infos; } already_AddRefed MFTEncoder::CreateFactory(const GUID& aSubtype) { IMFActivate** activates = nullptr; UINT32 num = EnumEncoders(aSubtype, activates, !mHardwareNotAllowed); if (num == 0) { return nullptr; } // Keep the first and throw out others, if there is any. RefPtr factory = activates[0]; activates[0] = nullptr; for (UINT32 i = 1; i < num; ++i) { activates[i]->Release(); activates[i] = nullptr; } CoTaskMemFree(activates); return factory.forget(); } HRESULT MFTEncoder::Create(const GUID& aSubtype) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(!mEncoder); RefPtr factory = CreateFactory(aSubtype); if (!factory) { return E_FAIL; } // Create MFT via the activation object. RefPtr encoder; HRESULT hr = factory->ActivateObject( IID_PPV_ARGS(static_cast(getter_AddRefs(encoder)))); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); RefPtr config; // Avoid IID_PPV_ARGS() here for MingGW fails to declare UUID for ICodecAPI. hr = encoder->QueryInterface(IID_ICodecAPI, getter_AddRefs(config)); if (FAILED(hr)) { encoder = nullptr; factory->ShutdownObject(); return hr; } mFactory = std::move(factory); mEncoder = std::move(encoder); mConfig = std::move(config); return S_OK; } HRESULT MFTEncoder::Destroy() { if (!mEncoder) { return S_OK; } mEncoder = nullptr; mConfig = nullptr; // Release MFT resources via activation object. HRESULT hr = mFactory->ShutdownObject(); mFactory = nullptr; return hr; } HRESULT MFTEncoder::SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(aInputType && aOutputType); AsyncMFTResult asyncMFT = AttemptEnableAsync(); NS_ENSURE_TRUE(asyncMFT.isOk(), asyncMFT.unwrapErr()); HRESULT hr = GetStreamIDs(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); // Always set encoder output type before input. hr = mEncoder->SetOutputType(mOutputStreamID, aOutputType, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); NS_ENSURE_TRUE(MatchInputSubtype(aInputType) != GUID_NULL, MF_E_INVALIDMEDIATYPE); hr = mEncoder->SetInputType(mInputStreamID, aInputType, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); hr = mEncoder->GetInputStreamInfo(mInputStreamID, &mInputStreamInfo); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); hr = mEncoder->GetOutputStreamInfo(mInputStreamID, &mOutputStreamInfo); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); mOutputStreamProvidesSample = IsFlagSet(mOutputStreamInfo.dwFlags, MFT_OUTPUT_STREAM_PROVIDES_SAMPLES); hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); if (asyncMFT.unwrap()) { RefPtr source; hr = mEncoder->QueryInterface(IID_PPV_ARGS( static_cast(getter_AddRefs(source)))); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); mEventSource.SetAsyncEventGenerator(source.forget()); } else { mEventSource.InitSyncMFTEventQueue(); } mNumNeedInput = 0; return S_OK; } // Async MFT won't work without unlocking. See // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#unlocking-asynchronous-mfts MFTEncoder::AsyncMFTResult MFTEncoder::AttemptEnableAsync() { IMFAttributes* pAttributes = nullptr; HRESULT hr = mEncoder->GetAttributes(&pAttributes); if (FAILED(hr)) { return AsyncMFTResult(hr); } bool async = MFGetAttributeUINT32(pAttributes, MF_TRANSFORM_ASYNC, FALSE) == TRUE; if (async) { hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE); } else { hr = S_OK; } pAttributes->Release(); return SUCCEEDED(hr) ? AsyncMFTResult(async) : AsyncMFTResult(hr); } HRESULT MFTEncoder::GetStreamIDs() { DWORD numIns; DWORD numOuts; HRESULT hr = mEncoder->GetStreamCount(&numIns, &numOuts); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); if (numIns < 1 || numOuts < 1) { MFT_ENC_LOGE("stream count error"); return MF_E_INVALIDSTREAMNUMBER; } DWORD inIDs[numIns]; DWORD outIDs[numOuts]; hr = mEncoder->GetStreamIDs(numIns, inIDs, numOuts, outIDs); if (SUCCEEDED(hr)) { mInputStreamID = inIDs[0]; mOutputStreamID = outIDs[0]; } else if (hr == E_NOTIMPL) { mInputStreamID = 0; mOutputStreamID = 0; } else { MFT_ENC_LOGE("failed to get stream IDs"); return hr; } return S_OK; } GUID MFTEncoder::MatchInputSubtype(IMFMediaType* aInputType) { MOZ_ASSERT(mEncoder); MOZ_ASSERT(aInputType); GUID desired = GUID_NULL; HRESULT hr = aInputType->GetGUID(MF_MT_SUBTYPE, &desired); NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL); MOZ_ASSERT(desired != GUID_NULL); DWORD i = 0; IMFMediaType* inputType = nullptr; GUID preferred = GUID_NULL; while (true) { hr = mEncoder->GetInputAvailableType(mInputStreamID, i, &inputType); if (hr == MF_E_NO_MORE_TYPES) { break; } NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL); GUID sub = GUID_NULL; hr = inputType->GetGUID(MF_MT_SUBTYPE, &sub); NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL); if (IsEqualGUID(desired, sub)) { preferred = desired; break; } ++i; } return IsEqualGUID(preferred, desired) ? preferred : GUID_NULL; } HRESULT MFTEncoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); return mEncoder->ProcessMessage(aMsg, aData); } HRESULT MFTEncoder::SetModes(UINT32 aBitsPerSec) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mConfig); VARIANT var; var.vt = VT_UI4; var.ulVal = eAVEncCommonRateControlMode_CBR; HRESULT hr = mConfig->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); var.ulVal = aBitsPerSec; hr = mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVEncAdaptiveMode))) { var.ulVal = eAVEncAdaptiveMode_Resolution; hr = mConfig->SetValue(&CODECAPI_AVEncAdaptiveMode, &var); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVLowLatencyMode))) { var.vt = VT_BOOL; var.boolVal = VARIANT_TRUE; hr = mConfig->SetValue(&CODECAPI_AVLowLatencyMode, &var); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } return S_OK; } HRESULT MFTEncoder::SetBitrate(UINT32 aBitsPerSec) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mConfig); VARIANT var = {.vt = VT_UI4, .ulVal = aBitsPerSec}; return mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var); } static HRESULT CreateSample(RefPtr* aOutSample, DWORD aSize, DWORD aAlignment) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); HRESULT hr; RefPtr sample; hr = wmf::MFCreateSample(getter_AddRefs(sample)); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); RefPtr buffer; hr = wmf::MFCreateAlignedMemoryBuffer(aSize, aAlignment, getter_AddRefs(buffer)); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); hr = sample->AddBuffer(buffer); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); *aOutSample = sample.forget(); return S_OK; } HRESULT MFTEncoder::CreateInputSample(RefPtr* aSample, size_t aSize) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); return CreateSample( aSample, aSize, mInputStreamInfo.cbAlignment > 0 ? mInputStreamInfo.cbAlignment - 1 : 0); } HRESULT MFTEncoder::PushInput(RefPtr&& aInput) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); MOZ_ASSERT(aInput); mPendingInputs.Push(aInput.forget()); if (mEventSource.IsSync() && mNumNeedInput == 0) { // To step 2 in // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data mNumNeedInput++; } HRESULT hr = ProcessInput(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); return ProcessEvents(); } HRESULT MFTEncoder::ProcessInput() { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); if (mNumNeedInput == 0 || mPendingInputs.GetSize() == 0) { return S_OK; } RefPtr input = mPendingInputs.PopFront(); HRESULT hr = mEncoder->ProcessInput(mInputStreamID, input, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); --mNumNeedInput; if (!mEventSource.IsSync()) { return S_OK; } // For sync MFT: Step 3 in // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data DWORD flags = 0; hr = mEncoder->GetOutputStatus(&flags); MediaEventType evType = MEUnknown; switch (hr) { case S_OK: evType = flags == MFT_OUTPUT_STATUS_SAMPLE_READY ? METransformHaveOutput // To step 4: ProcessOutput(). : METransformNeedInput; // To step 2: ProcessInput(). break; case E_NOTIMPL: evType = METransformHaveOutput; // To step 4: ProcessOutput(). break; default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("undefined output status"); return hr; } return mEventSource.QueueSyncMFTEvent(evType); } HRESULT MFTEncoder::ProcessEvents() { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); HRESULT hr = E_FAIL; while (true) { Event event = mEventSource.GetEvent(); if (event.isErr()) { hr = event.unwrapErr(); break; } MediaEventType evType = event.unwrap(); switch (evType) { case METransformNeedInput: ++mNumNeedInput; hr = ProcessInput(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); break; case METransformHaveOutput: hr = ProcessOutput(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); break; case METransformDrainComplete: mDrainState = DrainState::DRAINED; break; default: MFT_ENC_LOGE("unsupported event: %lx", evType); } } switch (hr) { case MF_E_NO_EVENTS_AVAILABLE: return S_OK; case MF_E_MULTIPLE_SUBSCRIBERS: default: MFT_ENC_LOGE("failed to get event: %s", ErrorStr(hr)); return hr; } } HRESULT MFTEncoder::ProcessOutput() { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); MFT_OUTPUT_DATA_BUFFER output = {.dwStreamID = mOutputStreamID, .pSample = nullptr, .dwStatus = 0, .pEvents = nullptr}; RefPtr sample; HRESULT hr = E_FAIL; if (!mOutputStreamProvidesSample) { hr = CreateSample(&sample, mOutputStreamInfo.cbSize, mOutputStreamInfo.cbAlignment > 1 ? mOutputStreamInfo.cbAlignment - 1 : 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); output.pSample = sample; } DWORD status = 0; hr = mEncoder->ProcessOutput(0, 1, &output, &status); if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { MFT_ENC_LOGD("output stream change"); if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) { // Follow the instructions in Microsoft doc: // https://docs.microsoft.com/en-us/windows/win32/medfound/handling-stream-changes#output-type IMFMediaType* outputType = nullptr; hr = mEncoder->GetOutputAvailableType(mOutputStreamID, 0, &outputType); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); hr = mEncoder->SetOutputType(mOutputStreamID, outputType, 0); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } return MF_E_TRANSFORM_STREAM_CHANGE; } // Step 8 in // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { MOZ_ASSERT(mEventSource.IsSync()); MOZ_ASSERT(mDrainState == DrainState::DRAINING); mEventSource.QueueSyncMFTEvent(METransformDrainComplete); return S_OK; } NS_ENSURE_TRUE(SUCCEEDED(hr), hr); mOutputs.AppendElement(output.pSample); if (mOutputStreamProvidesSample) { // Release MFT provided sample. output.pSample->Release(); output.pSample = nullptr; } return S_OK; } HRESULT MFTEncoder::TakeOutput(nsTArray>& aOutput) { MOZ_ASSERT(aOutput.Length() == 0); aOutput.SwapElements(mOutputs); return S_OK; } HRESULT MFTEncoder::Drain(nsTArray>& aOutput) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); MOZ_ASSERT(aOutput.Length() == 0); switch (mDrainState) { case DrainState::DRAINABLE: // Exhaust pending inputs. while (mPendingInputs.GetSize() > 0) { if (mEventSource.IsSync()) { // Step 5 in // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data mEventSource.QueueSyncMFTEvent(METransformNeedInput); } HRESULT hr = ProcessEvents(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); mDrainState = DrainState::DRAINING; [[fallthrough]]; // To collect and return outputs. case DrainState::DRAINING: // Collect remaining outputs. while (mOutputs.Length() == 0 && mDrainState != DrainState::DRAINED) { if (mEventSource.IsSync()) { // Step 8 in // https://docs.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data mEventSource.QueueSyncMFTEvent(METransformHaveOutput); } HRESULT hr = ProcessEvents(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); } [[fallthrough]]; // To return outputs. case DrainState::DRAINED: aOutput.SwapElements(mOutputs); return S_OK; } } HRESULT MFTEncoder::GetMPEGSequenceHeader(nsTArray& aHeader) { MOZ_ASSERT(mscom::IsCurrentThreadMTA()); MOZ_ASSERT(mEncoder); MOZ_ASSERT(aHeader.Length() == 0); RefPtr outputType; HRESULT hr = mEncoder->GetOutputCurrentType(mOutputStreamID, getter_AddRefs(outputType)); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); UINT32 length = 0; hr = outputType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &length); if (hr == MF_E_ATTRIBUTENOTFOUND || length == 0) { return S_OK; } NS_ENSURE_TRUE(SUCCEEDED(hr), hr); aHeader.SetCapacity(length); hr = outputType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, aHeader.Elements(), length, nullptr); aHeader.SetLength(SUCCEEDED(hr) ? length : 0); return hr; } MFTEncoder::Event MFTEncoder::EventSource::GetEvent() { if (IsSync()) { return GetSyncMFTEvent(); } RefPtr event; HRESULT hr = mImpl.as>()->GetEvent( MF_EVENT_FLAG_NO_WAIT, getter_AddRefs(event)); MediaEventType type = MEUnknown; if (SUCCEEDED(hr)) { hr = event->GetType(&type); } return SUCCEEDED(hr) ? Event{type} : Event{hr}; } HRESULT MFTEncoder::EventSource::QueueSyncMFTEvent(MediaEventType aEventType) { MOZ_ASSERT(IsSync()); MOZ_ASSERT(IsOnCurrentThread()); auto q = mImpl.as>().get(); q->push(aEventType); return S_OK; } MFTEncoder::Event MFTEncoder::EventSource::GetSyncMFTEvent() { MOZ_ASSERT(IsOnCurrentThread()); auto q = mImpl.as>().get(); if (q->empty()) { return Event{MF_E_NO_EVENTS_AVAILABLE}; } MediaEventType type = q->front(); q->pop(); return Event{type}; } #ifdef DEBUG bool MFTEncoder::EventSource::IsOnCurrentThread() { if (!mThread) { mThread = GetCurrentSerialEventTarget(); } return mThread->IsOnCurrentThread(); } #endif } // namespace mozilla #undef MFT_ENC_SLOGE #undef MFT_ENC_SLOGD #undef MFT_ENC_LOGE #undef MFT_ENC_LOGD