2016-12-01 52 views
5

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.

  1. 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 numer ID3D11Device::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.

  2. 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.

  3. 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.

  4. 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; 
} 
+1

Możesz spróbować z MF_MT_SUBTYPE, MFVideoFormat_NV12. – VuVirt

+0

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

+0

Proszę sprawdzić szczegółową odpowiedź – VuVirt

Odpowiedz

2

Twój kod jest poprawny, koncepcyjnie, z jedyną uwagą - i nie jest to całkiem oczywiste - że dekoder Media Foundation jest wielowątkowy. Karmisz go jedną gwintowaną wersją urządzenia Direct3D. Trzeba to obejść lub uzyskać to, co aktualnie otrzymujesz: naruszenia i zamrożenie dostępu, czyli niezdefiniowane zachowanie.

// 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<ID3D10Multithread> pMultithread = device; 
    pMultithread->SetMultithreadProtected(TRUE); 

Należy również zauważyć, że ten prosty przykład kodu ma wąskie gardło wydajności wokół linii dodanych do uzyskania sąsiedniego bufora. Najwyraźniej jest to twój ruch, aby uzyskać dostęp do danych ... jednak zachowanie według projektu jest takie, że zdekodowane dane są już w pamięci wideo, a przeniesienie do pamięci systemowej jest kosztowną operacją. Oznacza to, że dodałeś do pętli mocne uderzenie wydajności. Będziesz zainteresowany sprawdzaniem poprawności danych w ten sposób, a jeśli chodzi o benchmarking wydajności, powinieneś to skomentować.

+1

Przetestowałem kod z kopalnią i sugestiami. Moja sugestia (NV12) działa bez tworzenia i ustawiania urządzenia wielowątkowego. Twoja sugestia działa, gdy ustawiono RGB32, a procesor wideo MFT jest włączony przez MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING. Które IMHO oznacza, że ​​jest to procesor wideo MFT, który jest wielowątkowy, a nie dekoder H264 (używam dekodera sprzętowego NVidia btw). Proszę popraw mnie jeżeli się mylę. +1 i tak. – VuVirt

+2

@VuVirt: są one wielowątkowe, ponieważ aktywnie używają kolejek roboczych wewnętrznie. Problem pojawia się, gdy występuje kolizja w korzystaniu z urządzenia Direct3D. Istnieje znacznie mniejsza szansa na kolizję z NV12, ponieważ nie jest wymagana żadna konwersja po dekodowaniu. –

+0

Dziękujemy! Czy jest jakiś sposób, aby wiedzieć wcześniej, czy dekoder lub procesor jest wielowątkowy? Nie pamiętam, aby było to wspomniane w dokumentacji, samouczku lub przykładach. Teraz, gdy patrzę na opis dekodera na MSDN, jest parametr 'CODECAPI_AVDecNumWorkerThreads', który powinien być wskazówką, ale czy jest jasno określony w dowolnym miejscu? – Vennor

1

Rodzaje wyjściowe dekodera wideo H264 można znaleźć tutaj: https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx. RGB32 nie jest jednym z nich. W tym przypadku aplikacja wykorzystuje procesor wideo MFT do konwersji z dowolnego z MFVideoFormat_I420, MFVideoFormat_IYUV, MFVideoFormat_NV12, MFVideoFormat_YUY2, MFVideoFormat_YV12 na RGB32. Przypuszczam, że to procesor wideo MFT działa dziwnie i powoduje, że twój program źle się zachowuje. Dlatego ustawiając NV12 jako podtypu wyjściowego dla dekodera będziesz pozbyć MFT procesor wideo oraz następujące linie kodu są coraz bezużyteczne, a także:

handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE)); 

i

handle_result(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE)); 

Ponadto , jak zauważyłeś, NV12 to jedyny format, który działa poprawnie. Myślę, że powodem jest to, że jest to jedyny, który jest używany w przyspieszonych scenariuszach przez menedżera urządzeń D3D i DXGI.