Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/platforms/wmf/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 24 kB image not shown  

Quelle  MFTEncoder.cpp   Sprache: C

 
/* -*- 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/COMWrappers.h"
#include "mozilla/mscom/Utils.h"
#include "WMFUtils.h"
#include <comdef.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<MFTEncoder::Info>& 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;
  }
  mscom::wrapped::CoTaskMemFree(activates);
}

Maybe<MFTEncoder::Info> MFTEncoder::GetInfo(const GUID& aSubtype) {
  nsTArray<Info>& infos = Infos();

  for (auto i : infos) {
    if (IsEqualGUID(aSubtype, i.mSubtype)) {
      return Some(i);
    }
  }
  return Nothing();
}

nsCString MFTEncoder::GetFriendlyName(const GUID& aSubtype) {
  Maybe<Info> info = GetInfo(aSubtype);

  return info ? info.ref().mName : "???"_ns;
}

// Called only once by Infos().
nsTArray<MFTEncoder::Info> MFTEncoder::Enumerate() {
  nsTArray<Info> 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::Info>& MFTEncoder::Infos() {
  static nsTArray<Info> infos = Enumerate();
  return infos;
}

already_AddRefed<IMFActivate> 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<IMFActivate> factory = activates[0];
  activates[0] = nullptr;
  for (UINT32 i = 1; i < num; ++i) {
    activates[i]->Release();
    activates[i] = nullptr;
  }
  mscom::wrapped::CoTaskMemFree(activates);

  return factory.forget();
}

HRESULT MFTEncoder::Create(const GUID& aSubtype) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  MOZ_ASSERT(!mEncoder);

  RefPtr<IMFActivate> factory = CreateFactory(aSubtype);
  if (!factory) {
    MFT_ENC_LOGE("CreateFactory error");
    return E_FAIL;
  }

  // Create MFT via the activation object.
  RefPtr<IMFTransform> encoder;
  HRESULT hr = factory->ActivateObject(
      IID_PPV_ARGS(static_cast<IMFTransform**>(getter_AddRefs(encoder))));
  if (FAILED(hr)) {
    _com_error error(hr);
    MFT_ENC_LOGE("MFTEncoder::Create: error = 0x%lX, %ls", hr,
                 error.ErrorMessage());
    return hr;
  }

  RefPtr<ICodecAPI> 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)) {
    MFT_ENC_LOGE("QueryInterface IID_ICodecAPI error");
    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();
  if (asyncMFT.isErr()) {
    HRESULT hr = asyncMFT.inspectErr();
    _com_error error(hr);
    MFT_ENC_LOGE("AttemptEnableAsync error: %ls", error.ErrorMessage());
    return asyncMFT.inspectErr();
  }

  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<IMFMediaEventGenerator> source;
    hr = mEncoder->QueryInterface(IID_PPV_ARGS(
        static_cast<IMFMediaEventGenerator**>(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)) {
    MFT_ENC_LOGE("Encoder->GetAttribute error");
    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();

  if (FAILED(hr)) {
    MFT_ENC_LOGE("Setting async unlock");
  }

  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(const EncoderConfig& aConfig) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  MOZ_ASSERT(mConfig);

  VARIANT var;
  var.vt = VT_UI4;
  switch (aConfig.mBitrateMode) {
    case BitrateMode::Constant:
      var.ulVal = eAVEncCommonRateControlMode_CBR;
      break;
    case BitrateMode::Variable:
      if (aConfig.mCodec == CodecType::VP8 ||
          aConfig.mCodec == CodecType::VP9) {
        MFT_ENC_LOGE(
            "Overriding requested VRB bitrate mode, forcing CBR for VP8/VP9 "
            "encoding.");
        var.ulVal = eAVEncCommonRateControlMode_CBR;
      } else {
        var.ulVal = eAVEncCommonRateControlMode_PeakConstrainedVBR;
      }
      break;
  }
  HRESULT hr = mConfig->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var);
  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);

  if (aConfig.mBitrate) {
    var.ulVal = aConfig.mBitrate;
    hr = mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
    if (FAILED(hr)) {
      MFT_ENC_LOGE("Couln't set bitrate to %d", aConfig.mBitrate);
      return hr;
    }
  }

  switch (aConfig.mScalabilityMode) {
    case ScalabilityMode::None:
      var.ulVal = 1;
      break;
    case ScalabilityMode::L1T2:
      var.ulVal = 2;
      break;
    case ScalabilityMode::L1T3:
      var.ulVal = 3;
      break;
  }

  bool isIntel = false;  // TODO check this
  if (aConfig.mScalabilityMode != ScalabilityMode::None || isIntel) {
    hr = mConfig->SetValue(&CODECAPI_AVEncVideoTemporalLayerCount, &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 =
        aConfig.mUsage == Usage::Realtime ? VARIANT_TRUE : VARIANT_FALSE;
    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<IMFSample>* aOutSample, DWORD aSize,
                            DWORD aAlignment) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());

  HRESULT hr;
  RefPtr<IMFSample> sample;
  hr = wmf::MFCreateSample(getter_AddRefs(sample));
  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);

  RefPtr<IMFMediaBuffer> 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<IMFSample>* aSample, size_t aSize) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());

  return CreateSample(
      aSample, aSize,
      mInputStreamInfo.cbAlignment > 0 ? mInputStreamInfo.cbAlignment - 1 : 0);
}

HRESULT
MFTEncoder::PushInput(const InputSample& aInput) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  MOZ_ASSERT(mEncoder);

  mPendingInputs.push_back(aInput);
  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.empty()) {
    return S_OK;
  }

  auto input = mPendingInputs.front();
  mPendingInputs.pop_front();

  HRESULT hr = mEncoder->ProcessInput(mInputStreamID, input.mSample, 0);

  if (input.mKeyFrameRequested) {
    VARIANT v = {.vt = VT_UI4, .ulVal = 1};
    mConfig->SetValue(&CODECAPI_AVEncVideoForceKeyFrame, &v);
  }
  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:
        MFT_ENC_LOGD("State is now DrainState::DRAINED");
        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<IMFSample> 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<RefPtr<IMFSample>>& aOutput) {
  MOZ_ASSERT(aOutput.Length() == 0);
  aOutput.SwapElements(mOutputs);
  return S_OK;
}

HRESULT MFTEncoder::Drain(nsTArray<RefPtr<IMFSample>>& aOutput) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  MOZ_ASSERT(mEncoder);
  MOZ_ASSERT(aOutput.Length() == 0);

  switch (mDrainState) {
    case DrainState::DRAINABLE:
      // Exhaust pending inputs.
      while (!mPendingInputs.empty()) {
        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);
      MFT_ENC_LOGD("State is now DrainState::DRAINING");
      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);
      mDrainState = DrainState::DRAINABLE;
      return S_OK;
  }
}

HRESULT MFTEncoder::GetMPEGSequenceHeader(nsTArray<UINT8>& aHeader) {
  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
  MOZ_ASSERT(mEncoder);
  MOZ_ASSERT(aHeader.Length() == 0);

  RefPtr<IMFMediaType> 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<IMFMediaEvent> event;
  HRESULT hr = mImpl.as<RefPtr<IMFMediaEventGenerator>>()->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<UniquePtr<EventQueue>>().get();
  q->push(aEventType);
  return S_OK;
}

MFTEncoder::Event MFTEncoder::EventSource::GetSyncMFTEvent() {
  MOZ_ASSERT(IsOnCurrentThread());

  auto q = mImpl.as<UniquePtr<EventQueue>>().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

Messung V0.5
C=92 H=92 G=91

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.