Ven*_*nor 5 c++ com directx hardware-acceleration ms-media-foundation
我正在使用Media Foundation的Source Reader编写硬件加速的h264解码器,但遇到了问题.我按照本教程使用Windows SDK Media Foundation示例支持自己.
当硬件加速关闭时,我的应用似乎工作正常,但它不提供我需要的性能.当我通过传递一个用来创建阅读器IMFDXGIDeviceManager
来加速加速时IMFAttributes
,事情就变得复杂了.
如果我创建ID3D11Device
使用D3D_DRIVER_TYPE_NULL
驱动程序,应用程序工作正常,帧处理速度比在软件模式下更快,但从CPU和GPU使用情况来看,它仍然在CPU上进行大部分处理.
另一方面,当我创建ID3D11Device
使用D3D_DRIVER_TYPE_HARDWARE
驱动程序并运行应用程序时,可能会发生以下四种情况之一.
在IMFMediaBuffer::Lock
函数返回0x887a0005 之前,我只得到一个不可预测的帧数(通常为1-3),这被描述为"GPU设备实例已被暂停.GetDeviceRemovedReason
用于确定适当的操作".当我打电话时ID3D11Device::GetDeviceRemovedReason
,我得到0x887a0020,这被描述为"驱动程序遇到问题,并被置于设备移除状态",这没有我希望的那样有用.
应用程序在外部dll中随时崩溃IMFMediaBuffer::Lock
.似乎dll取决于所使用的GPU.对于Intel集成GPU,它是igd10iumd32.dll,对于Nvidia移动GPU,它是mfplat.dll.此特定崩溃的消息如下:"在decoder_ tester.exe中0x53C6DB8C(mfplat.dll)抛出异常:0xC0000005:访问冲突读取位置0x00000024".执行之间的地址不同,有时涉及阅读,有时甚至是写作.
图形驱动程序停止响应,系统暂停一小段时间,然后应用程序崩溃,如第2点或完成第1点.
该应用程序工作正常,并通过硬件加速处理所有帧.
大部分时间是1或2,很少3或4.
以下是我的机器上不同模式下处理时不受限制的CPU/GPU使用情况(英特尔酷睿i5-6500与HD Graphics 530,Windows 10 Pro).
我在三台机器上测试了应用程序.他们都拥有英特尔集成GPU(HD 4400,HD 4600,HD 530).其中一个还有可切换的Nvidia专用GPU(GF 840M).它在所有这些方面完全相同,唯一的区别是当使用Nvidia的GPU时它会在不同的dll中崩溃.
我以前没有使用COM或DirectX的经验,但所有这些都是不一致和不可预测的,所以它看起来像是一个内存损坏我.不过,我不知道我在哪里弄错了.你能帮我找一下我做错的事吗?
我能想出的最小代码示例如下.我正在使用Visual Studio Professional 2015将其编译为C++项目.我准备了定义以启用硬件加速并选择硬件驱动程序.评论它们以改变行为.此外,代码期望此视频文件存在于项目目录中.
#include <iostream>
#include <string>
#include <atlbase.h>
#include <d3d11.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <windows.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#define ENABLE_HW_ACCELERATION
#define ENABLE_HW_DRIVER
void handle_result(HRESULT hr)
{
if (SUCCEEDED(hr))
return;
WCHAR message[512];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), message, ARRAYSIZE(message), nullptr);
printf("%ls", message);
abort();
}
int main(int argc, char** argv)
{
handle_result(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
handle_result(MFStartup(MF_VERSION));
{
CComPtr<IMFAttributes> attributes;
handle_result(MFCreateAttributes(&attributes, 3));
#if defined(ENABLE_HW_ACCELERATION)
CComPtr<ID3D11Device> device;
D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0 };
#if defined(ENABLE_HW_DRIVER)
handle_result(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, &device, nullptr, nullptr));
#else
handle_result(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_NULL, nullptr, D3D11_CREATE_DEVICE_SINGLETHREADED,
levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, &device, nullptr, nullptr));
#endif
UINT token;
CComPtr<IMFDXGIDeviceManager> manager;
handle_result(MFCreateDXGIDeviceManager(&token, &manager));
handle_result(manager->ResetDevice(device, token));
handle_result(attributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, manager));
handle_result(attributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE));
handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE));
#else
handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE));
#endif
CComPtr<IMFSourceReader> reader;
handle_result(MFCreateSourceReaderFromURL(L"Rogue One - A Star Wars Story - Trailer.mp4", attributes, &reader));
CComPtr<IMFMediaType> output_type;
handle_result(MFCreateMediaType(&output_type));
handle_result(output_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
handle_result(output_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32));
handle_result(reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, output_type));
unsigned int frame_count{};
std::cout << "Started processing frames" << std::endl;
while (true)
{
CComPtr<IMFSample> sample;
DWORD flags;
handle_result(reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, nullptr, &flags, nullptr, &sample));
if (flags & MF_SOURCE_READERF_ENDOFSTREAM || sample == nullptr)
break;
std::cout << "Frame " << frame_count++ << std::endl;
CComPtr<IMFMediaBuffer> buffer;
BYTE* data;
handle_result(sample->ConvertToContiguousBuffer(&buffer));
handle_result(buffer->Lock(&data, nullptr, nullptr));
// Use the frame here.
buffer->Unlock();
}
std::cout << "Finished processing frames" << std::endl;
}
MFShutdown();
CoUninitialize();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
从概念上讲,您的代码是正确的,唯一的注释 - 而且不太明显 - 媒体基础解码器是多线程的。您正在使用单线程版本的 Direct3D 设备为其提供数据。您必须解决这个问题,否则您将得到当前所得到的结果:访问冲突和冻结,这是未定义的行为。
// NOTE: No single threading
handle_result(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
(0 * D3D11_CREATE_DEVICE_SINGLETHREADED) | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, &device, nullptr, nullptr));
// NOTE: Getting ready for multi-threaded operation
const CComQIPtr<ID3D11Multithread> pMultithread = device;
pMultithread->SetMultithreadProtected(TRUE);
Run Code Online (Sandbox Code Playgroud)
另请注意,这个简单的代码示例在您为获取连续缓冲区而添加的行周围存在性能瓶颈。显然,您需要采取行动来访问数据...但是,设计的行为是解码的数据已经在视频内存中,并且您传输到系统内存是一项昂贵的操作。也就是说,您给循环添加了严重的性能损失。您会对以这种方式检查数据的有效性感兴趣,当涉及到性能基准测试时,您应该将其注释掉。
归档时间: |
|
查看次数: |
3180 次 |
最近记录: |