Jestem w trakcie pisania przyspieszanego sprzętowo dekodera h264 przy użyciu czytnika źródeł Media Foundation, ale napotkałem problem. Śledziłem this tutorial i wsparłem się samymi próbkami Windows SDK Media Foundation.Jak prawidłowo korzystać z przyspieszanego sprzętowo czytnika źródłowego Media Foundation w celu odkodowania wideo?
Moja aplikacja działa poprawnie, gdy akceleracja sprzętowa jest wyłączona, ale nie zapewnia wymaganej wydajności. Kiedy włączam przyspieszanie, przekazując IMFDXGIDeviceManager
do IMFAttributes
używanego do tworzenia czytnika, sprawy stają się skomplikowane.
Jeśli utworzę ID3D11Device
przy użyciu sterownika D3D_DRIVER_TYPE_NULL
, aplikacja będzie działała poprawnie, a ramki będą przetwarzane szybciej niż w trybie programowym, ale sądząc po wykorzystaniu procesora i procesora graficznego, nadal wykonuje większość przetwarzania na procesorze.
Z drugiej strony, po utworzeniu ID3D11Device
za pomocą sterownika D3D_DRIVER_TYPE_HARDWARE
i uruchomienia aplikacji może się zdarzyć jedna z tych czterech rzeczy.
tylko uzyskać nieprzewidywalne ilości ramek (zwykle 1-3) przed
IMFMediaBuffer::Lock
zwraca 0x887a0005 która jest opisana jako „Numer urządzenia graficzny został zawieszony. Za pomocąGetDeviceRemovedReason
określić odpowiednie działania”. Kiedy dzwonię pod numerID3D11Device::GetDeviceRemovedReason
, otrzymuję 0x887a0020, który jest opisany jako "Sterownik napotkał problem i został umieszczony w stanie usuniętego urządzenia", co nie jest tak pomocne, jak chciałbym.Aplikacja ulega awarii w zewnętrznym pliku DLL wywołania
IMFMediaBuffer::Lock
. Wydaje się, że dll zależy od używanego GPU. Dla zintegrowanego procesora graficznego Intela jest to igd10iumd32.dll, a dla mobilnego GPU Nvidia to plik mfplat.dll. Komunikat dla tej konkretnej awarii jest następujący: "Wyjątek zgłoszony przy 0x53C6DB8C (mfplat.dll) w decoder_ tester.exe: 0xC0000005: Lokalizacja naruszenia dostępu odczytu 0x00000024". Adresy różnią się między egzekucjami, a czasem wymagają czytania, czasami pisania.Sterownik grafiki przestaje odpowiadać, system zawiesza się na krótki czas, a następnie awarii aplikacji jak w pkt 2 lub wykończenia, jak w punkcie 1.
Aplikacja działa bez zarzutu i przetwarza wszystkie klatki ze sprzętem przyśpieszenie.
Przez większość czasu jest to 1 lub 2, rzadko 3 lub 4.
Oto co użycie CPU/GPU jest jak podczas przetwarzania bez dławienia w różnych trybach na moim komputerze (Intel Core i5-6500 z HD Graphics 530, Windows 10 Pro).
- NULL - Procesor: ~ 90%, GPU: ~ 15%
- SPRZĘT - Procesor: ~ 15% Grafika: ~ 60%
- OPROGRAMOWANIE - Procesor: ~ 40%, GPU: ~ 7 %
Testowałem aplikację na trzech komputerach. Wszystkie miały zintegrowane procesory graficzne Intela (HD 4400, HD 4600, HD 530). Jeden z nich miał również przełączalny dedykowany procesor graficzny Nvidii (GF 840M). To samo dotyczy wszystkich z nich, jedyną różnicą jest to, że zawiesza się w innej bibliotece dll, gdy używana jest GPU Nvidii.
nie mam doświadczenia z COM lub DirectX, ale to wszystko jest niekonsekwentne i nieprzewidywalne, więc wygląda na korupcję pamięci do mnie. Wciąż nie wiem, gdzie popełniam błąd. Czy mógłbyś mi pomóc znaleźć to, co robię źle?
Ten minimalny przykład kodu, który mogłem wymyślić, znajduje się poniżej. Korzystam z programu Visual Studio Professional 2015, aby skompilować go jako projekt C++. Przygotowałem definicje, aby włączyć akcelerację sprzętową i wybrać sterownik sprzętu. Skomentuj je, aby zmienić zachowanie. Ponadto kod oczekuje, że this video file będzie obecny w katalogu projektu.
#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;
}
Możesz spróbować z MF_MT_SUBTYPE, MFVideoFormat_NV12. – VuVirt
Dzięki za podpowiedź! Rzeczywiście, zaczyna działać, gdy ustawię MFVideoFormat_NV12 jako podtyp wyjściowy. Użyłem DXVAChecker do wyświetlenia [możliwych formatów wyjściowych] (https://i.imgsafe.org/12d5643c00.png) dla dekodera, którego używam (przynajmniej tak myślę) i nie ma tam RGB32. Czy to znaczy, że nie mogę dekodować H264 bezpośrednio do RGB32 używając tego dekodera? Dlaczego więc czasami działałoby dobrze, jak opisano w punkcie 4 mojego pytania? A może byłby to rodzaj komunikatu o błędzie "nieobsługiwanego formatu wyjściowego"? Co dziwne, NV12 wydaje się być jedynym formatem z listy, który sprawia, że kod działa poprawnie. – Vennor
Proszę sprawdzić szczegółową odpowiedź – VuVirt