Como usar corretamente um Media Foundation Source Reader acelerado por hardware para decodificar um vídeo?

Estou escrevendo um decodificador h264 acelerado por hardware usando o Source Reader da Media Foundation, mas encontrei um problema. eu seguieste tutorial e me apoiei com amostras do Windows SDK Media Foundation.

Meu aplicativo parece funcionar bem quando a aceleração de hardware está desativada, mas não fornece o desempenho necessário. Quando ligo a aceleração passando umIMFDXGIDeviceManager paraIMFAttributes usado para criar o leitor, as coisas ficam complicadas.

Se eu criar oID3D11Device usando umD3D_DRIVER_TYPE_NULL driver, o aplicativo funciona bem e os quadros são processados mais rapidamente do que no modo de software, mas, a julgar pelo uso da CPU e da GPU, ele ainda faz a maior parte do processamento na CPU.

Por outro lado, quando eu crio oID3D11Device usando umD3D_DRIVER_TYPE_HARDWARE driver e execute o aplicativo, uma dessas quatro coisas pode acontecer.

Eu só recebo um número imprevisível de quadros (geralmente 1-3) antesIMFMediaBuffer::Lock A função retorna 0x887a0005, descrita como "A instância do dispositivo GPU foi suspensa.GetDeviceRemovedReason para determinar a ação apropriada ". Quando ligoID3D11Device::GetDeviceRemovedReason, Recebo 0x887a0020, que é descrito como "O driver encontrou um problema e foi colocado no estado de remoção do dispositivo", o que não é tão útil quanto eu gostaria que fosse.

O aplicativo trava em uma dll externa emIMFMediaBuffer::Lock ligar. Parece que a dll depende da GPU usada. Para a GPU integrada da Intel, é igd10iumd32.dll e para a GPU móvel da Nvidia, é mfplat.dll. A mensagem para esta falha específica é a seguinte: "Exceção lançada em 0x53C6DB8C (mfplat.dll) em decoder_ tester.exe: 0xC0000005: violação de acesso ao local de leitura 0x00000024". Os endereços são diferentes entre as execuções e, algumas vezes, envolve leitura, outras, escrita.

O driver gráfico pára de responder, o sistema trava por um curto período de tempo e o aplicativo trava como no ponto 2 ou termina como no ponto 1.

O aplicativo funciona bem e processa todos os quadros com aceleração de hardware.

Na maioria das vezes é 1 ou 2, raramente 3 ou 4.

Veja como é o uso da CPU / GPU ao processar sem estrangular em diferentes modos na minha máquina (Intel Core i5-6500 com HD Graphics 530, Windows 10 Pro).

NULL - CPU: ~ 90%, GPU: ~ 15%HARDWARE - CPU: ~ 15%, GPU: ~ 60%SOFTWARE - CPU: ~ 40%, GPU: ~ 7%

Testei o aplicativo em três máquinas. Todos eles tinham GPUs integradas da Intel (HD 4400, HD 4600, HD 530). Um deles também possuía GPU dedicada Nvidia comutável (GF 840M). Ele se comporta de forma idêntica em todos eles, a única diferença é que ele trava em uma dll diferente quando a GPU da Nvidia é usada.

Não tenho experiência anterior com COM ou DirectX, mas tudo isso é inconsistente e imprevisível, portanto, parece-me uma corrupção de memória. Ainda assim, não sei onde estou cometendo o erro. Você poderia me ajudar a descobrir o que estou fazendo de errado?

O exemplo de código mínimo que eu poderia criar está abaixo. Estou usando o Visual Studio Professional 2015 para compilá-lo como um projeto C ++. Preparei definições para ativar a aceleração de hardware e selecionar o driver de hardware. Comente-os para mudar o comportamento. Além disso, o código esperaeste arquivo de vídeo estar presente no diretório do projeto.

#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;
}

questionAnswers(2)

yourAnswerToTheQuestion