无法在 HEVC 解码器 IMFTransform 上设置输出类型

Posted

技术标签:

【中文标题】无法在 HEVC 解码器 IMFTransform 上设置输出类型【英文标题】:Can't set the output type on an HEVC decoder IMFTransform 【发布时间】:2020-05-25 05:09:04 【问题描述】:

我编写了这个程序来设置基于https://docs.microsoft.com/en-us/windows/win32/medfound/supporting-direct3d-11-video-decoding-in-media-foundation 的 HEVC 解码器。一切正常,直到我调用result = decoder->SetOutputType(0, media_type, 0); 这将返回错误MF_E_ATTRIBUTENOTFOUND。我不确定出了什么问题,SetOutputType 文档中没有描述此错误,我只找到了几个使用 MF 进行 HEVC 解码的示例,但没有一个描述这样的错误。

// WindowsProject5.cpp : Defines the entry point for the application.
//

#include <iostream>
#include <initguid.h>
#include <mfapi.h>
#include <mftransform.h>
#include <combaseapi.h>
#include <d3d11.h>
#include <optional>
#include <Mferror.h>

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  _In_opt_ HINSTANCE hPrevInstance,
  _In_ LPWSTR    lpCmdLine,
  _In_ int       nCmdShow)

  UNREFERENCED_PARAMETER(hPrevInstance);
  UNREFERENCED_PARAMETER(lpCmdLine);

  auto result = CoInitialize(NULL);
  if (result != S_OK) 
    std::cout << "CoInitialize failed" << std::endl;
    std::terminate();
  

  MSG msg;

  MFT_REGISTER_TYPE_INFO inputInfo MFMediaType_Video , MFVideoFormat_HEVC ;
  MFT_REGISTER_TYPE_INFO outputInfo MFMediaType_Video, MFVideoFormat_NV12 ;
  IMFActivate** activates;
  unsigned int numActivates = 255;

  result = MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_SYNCMFT, &inputInfo, nullptr, &activates, &numActivates);
  if (result != S_OK) 
    std::cout << "MFTEnum failed" << std::endl;
    std::terminate();
  

  std::cout << numActivates << std::endl;

  if (!numActivates) 
    std::cout << "No HEVC decoders found" << std::endl;
    std::terminate();
  

  IMFTransform* decoder;
  result = activates[0]->ActivateObject(IID_PPV_ARGS(&decoder));
  if (result != S_OK) 
    std::cout << "ActivateObject failed" << std::endl;
    std::terminate();
  

  IMFAttributes* attributes;
  result = decoder->GetAttributes(&attributes);
  if (result != S_OK) 
    std::cout << "GetAttributes failed" << std::endl;
    std::terminate();
  

  auto aware = 123456u;
  result = attributes->GetUINT32(MF_SA_D3D11_AWARE, &aware);
  if (result != S_OK) 
    std::cout << "GetAttributes failed" << std::endl;
    std::terminate();
  

  std::cout << "MF_SA_D3D11_AWARE = " << aware << std::endl;

  if (!aware) 
    std::cout << "HEVC decoder is not DirectX aware" << std::endl;
    std::terminate();
  

  unsigned int resetToken;
  IMFDXGIDeviceManager* deviceManager;

  result = MFCreateDXGIDeviceManager(&resetToken, &deviceManager);
  if (result != S_OK) 
    std::cout << "MFCreateDXGIDeviceManager failed" << std::endl;
    std::terminate();
  

  result = decoder->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager));
  if (result != S_OK) 
    std::cout << "ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER) failed" << std::endl;
    std::terminate();
  

  ID3D11Device* device;
  D3D_FEATURE_LEVEL featureLevel;
  ID3D11DeviceContext* deviceContext;
  result = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &device, &featureLevel, &deviceContext);
  if (result != S_OK) 
    std::cout << "D3D11CreateDevice failed" << std::endl;
    std::terminate();
  

  result = deviceManager->ResetDevice(device, resetToken);
  if (result != S_OK) 
    std::cout << "ResetDevice failed" << std::endl;
    std::terminate();
  

  HANDLE deviceHandle;
  result = deviceManager->OpenDeviceHandle(&deviceHandle);
  if (result != S_OK) 
    std::cout << "OpenDeviceHandle failed" << std::endl;
    std::terminate();
  

  ID3D11VideoDevice* videoDevice;
  result = deviceManager->GetVideoService(deviceHandle, IID_PPV_ARGS(&videoDevice));
  if (result != S_OK) 
    std::cout << "GetVideoService failed" << std::endl;
    std::terminate();
  

  ID3D11VideoContext* videoContext;
  result = deviceContext->QueryInterface(IID_PPV_ARGS(&videoContext));
  if (result != S_OK) 
    std::cout << "QueryInterface(videoContext) failed" << std::endl;
    std::terminate();
  


  ID3D10Multithread* multithreaded;
  result = device->QueryInterface(IID_PPV_ARGS(&multithreaded));
  if (result != S_OK) 
    std::cout << "QueryInterface(multithreaded) failed" << std::endl;
    std::terminate();
  

  multithreaded->SetMultithreadProtected(true);

  auto profileCount = videoDevice->GetVideoDecoderProfileCount();
  std::optional<GUID> selectedProfile;
  for (decltype(profileCount) i = 0; i < profileCount; ++i) 
    GUID profile;
    result = videoDevice->GetVideoDecoderProfile(i, &profile);
    if (result != S_OK) 
      std::cout << "GetVideoDecoderProfile(" << i << ") failed" << std::endl;
      std::terminate();
    
    if (profile == D3D11_DECODER_PROFILE_HEVC_VLD_MAIN) 
      selectedProfile = profile;
      std::cout << "D3D11_DECODER_PROFILE_HEVC_VLD_MAIN found" << std::endl;
    
    else if (profile == D3D11_DECODER_PROFILE_HEVC_VLD_MAIN10) 
      std::cout << "D3D11_DECODER_PROFILE_HEVC_VLD_MAIN10 found" << std::endl;
    
  

  if (!selectedProfile.has_value()) 
    std::cout << "No HEVC decoder profile found" << std::endl;
    std::terminate();
  

  BOOL supported;
  result = videoDevice->CheckVideoDecoderFormat(&selectedProfile.value(), DXGI_FORMAT_NV12, &supported);
  if (result != S_OK) 
    std::cout << "CheckVideoDecoderFormat failed" << std::endl;
    std::terminate();
  

  if (!supported) 
    std::cout << "Decoder format not supported" << std::endl;
    std::terminate();
  

  IMFMediaType* media_type;
  result = MFCreateMediaType(&media_type);
  if (result != S_OK) 
    std::cout << "MFCreateMediaType failed" << std::endl;
    std::terminate();
  
  result = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
  if (result != S_OK) 
    std::cout << "SetGUID(MF_MT_MAJOR_TYPE) failed" << std::endl;
    std::terminate();
  
  result = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_HEVC);
  if (result != S_OK) 
    std::cout << "SetGUID(MF_MT_SUBTYPE) failed" << std::endl;
    std::terminate();
  

  result = decoder->SetInputType(0, media_type, 0);  // No flags
  if (result != S_OK) 
    std::cout << "SetInputType failed" << std::endl;
    std::terminate();
  

  for (uint32_t i = 0;
    SUCCEEDED(decoder->GetOutputAvailableType(0, i, &media_type)); ++i) 
    GUID out_subtype =  0 ;
    result = media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype);
    if (result != S_OK) 
      std::cout << "GetGUID failed" << std::endl;
      std::terminate();
    

    if (out_subtype == MFVideoFormat_NV12) 
      result = decoder->SetOutputType(0, media_type, 0);  // No flags
      if (result != S_OK) 
        std::cout << "SetOutputType failed" << std::endl;
        std::terminate();
      
      return true;
    
  

  return 0;


【问题讨论】:

【参考方案1】:

即使 MSDN 没有在 H.265 / HEVC Video Decoder 中提及其他输入媒体类型属性,您也需要设置它们。请参阅H.264 decoder article 了解您需要哪些属性:MF_MT_FRAME_SIZE 和朋友。

您还可以使用MFTrace SDK 工具来检查在您遇到故障之前立即查询和报告为缺失的属性。

  // Have this line added:
  MFSetAttributeSize(media_type, MF_MT_FRAME_SIZE, 1280, 720);

  result = decoder->SetInputType(0, media_type, 0);  // No flags

【讨论】:

我将 MF_MT_FRAME_SIZE 添加到我的输入类型中并且它有效,但是,我没有发现 mftrace 很有帮助。当我使用“-v -l Debug”运行它时,它对 SetOutputType 调用所做的只是记录输出媒体类型的属性和退出错误代码,而没有任何关于查询导致错误的属性的信息。 添加 MF_MT_FRAME_SIZE 也为我修复了它。我创建了一个问题来记录这个和商店扩展here

以上是关于无法在 HEVC 解码器 IMFTransform 上设置输出类型的主要内容,如果未能解决你的问题,请参考以下文章

从 SinkWriter 或 ICodecAPI 或 IMFTransform 获取编码器名称

IMFTransform::ProcessOutput 返回 E_INVALIDARG

Windows Media Foundation 使用 IMFTransform 将 mp4 电影帧解码为 2D 纹理

JavaCV升级1.5.6之后遇到h265/hevc编码的视频无法打开编解码器avcodec_open2() error -1:Could not open video codec异常解决办法

JavaCV升级1.5.6之后遇到h265/hevc编码的视频无法打开编解码器avcodec_open2() error -1:Could not open video codec异常解决办法

JavaCV升级1.5.6之后遇到h265/hevc编码的视频无法打开编解码器avcodec_open2() error -1:Could not open video codec异常解决办法(代码片