我正在使用媒体基金会的源代码阅读器编写一个硬件加速的h264解码器,但遇到了一个问题。我跟踪本教程并使用示例支持自己。
当硬件加速关闭时,我的应用程序似乎工作得很好,但是它没有提供我所需要的性能。当我通过将一个IMFDXGIDeviceManager
传递给用来创建阅读器的IMFAttributes
来打开加速时,事情就变得复杂了。
如果我使用一个ID3D11Device
驱动程序创建D3D_DRIVER_TYPE_NULL
,应用程序工作正常,帧处理速度比软件模式快,但从CPU和GPU的使用情况来看,它仍然可以完成大部分的处理。
另一方面,当我使用ID3D11Device
驱动程序创建D3D_DRIVER_TYPE_HARDWARE
并运行应用程序时,这四种情况之一可能会发生。
IMFMediaBuffer::Lock
函数返回0x887a0005之前,我只得到一个不可预测的帧数(通常为1-3帧),它被描述为“GPU设备实例已被挂起。使用GetDeviceRemovedReason
来确定适当的操作”。当我调用ID3D11Device::GetDeviceRemovedReason
时,我得到0x887a0020,它被描述为“驱动程序遇到了问题,并被放入设备删除状态”,这并没有我希望的那么有帮助。IMFMediaBuffer::Lock
调用时崩溃在外部dll中。看来dll依赖于所使用的GPU。对于英特尔集成GPU,它是igd10iumd32.dll,对于Nvidia移动GPU,它是mfplat.dll。此特定崩溃的消息如下:“decoder_ tester.exe: 0xC0000005:访问冲突读取位置0x000024中的0x53C6DB8C (Decoder_)引发的异常”。执行之间的地址是不同的,有时涉及阅读,有时涉及写作。大多数时候是1或2,很少是3或4。
这是CPU/GPU的使用情况,当在我的机器上不节流的情况下处理时(Intel Core i5-6500与HD 530,Windows 10 Pro)。
我在三台机器上测试了这个应用程序。它们都有英特尔集成的GPU (HD 4400,HD 4600,HD 530)。其中一个还拥有可切换的Nvidia专用GPU (GF840m)。它在所有这些方面都是相同的,唯一的区别是,当使用Nvidia的GPU时,它会在不同的dll中崩溃。
我以前没有使用COM或DirectX的经验,但所有这些都是不一致和不可预测的,因此在我看来,这就像是内存损坏。尽管如此,我还是不知道我在哪里犯了这个错误。你能帮我找出我做错了什么吗?
下面是我可以提出的最低限度的代码示例。我正在使用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;
}
发布于 2016-12-03 10:41:47
在概念上,您的代码是正确的,唯一的注意是--而且并不是很明显-- Media解码器是多线程的。您使用的是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);
还请注意,这个简单的代码示例在您为获得连续缓冲区而添加的行周围有一个性能瓶颈。很明显这是你的行动去获取数据..。然而,从设计上看,解码后的数据已经存在于视频内存中,而将数据传输到系统内存则是一项昂贵的操作。也就是说,您在循环中添加了一个严重的性能打击。您会对以这种方式检查数据的有效性感兴趣,当涉及到性能基准时,您应该将其注释掉。
发布于 2016-12-02 09:55:53
H264视频解码器的输出类型可以在这里找到:https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx。RGB32不是其中之一。在这种情况下,应用程序依赖于视频处理器MFT来完成从MFVideoFormat_I420、MFVideoFormat_IYUV、MFVideoFormat_NV12、MFVideoFormat_YUY2、MFVideoFormat_YV12到RGB32的转换。我认为是视频处理器MFT的行为奇怪,并导致您的程序行为不当。这就是为什么通过将NV12设置为解码器的输出子类型,您将摆脱视频处理器MFT,下面的代码行也变得毫无用处:
handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE));
和
handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE));
此外,正如您注意到的,NV12是唯一正常工作的格式。我认为这是因为它是D3D和DXGI设备管理器在加速场景中使用的唯一一种。
https://stackoverflow.com/questions/40913196
复制相似问题