簡體   English   中英

如何正確使用硬件加速的 Media Foundation Source Reader 解碼視頻?

[英]How to properly use a hardware accelerated Media Foundation Source Reader to decode a video?

我正在使用 Media Foundation 的 Source Reader 編寫硬件加速的 h264 解碼器,但遇到了問題。 我遵循本教程並使用 Windows SDK Media Foundation 示例支持自己。


當硬件加速關閉時,我的應用程序似乎工作正常,但它沒有提供我需要的性能。 當我傳遞一個打開加速IMFDXGIDeviceManagerIMFAttributes用於創建讀者,事情變得復雜。

如果我使用D3D_DRIVER_TYPE_NULL驅動程序創建ID3D11Device ,該應用程序運行良好,幀處理速度比在軟件模式下更快,但從 CPU 和 GPU 使用情況來看,它仍然在 CPU 上進行大部分處理。

另一方面,當我使用D3D_DRIVER_TYPE_HARDWARE驅動程序創建ID3D11Device並運行該應用程序時,可能會發生這四種情況之一。

  1. IMFMediaBuffer::Lock函數返回 0x887a0005 之前,我只得到不可預測的幀數(通常為 1-3),這被描述為“GPU 設備實例已暫停。使用GetDeviceRemovedReason確定適當的操作”。 當我調用ID3D11Device::GetDeviceRemovedReason ,我得到 0x887a0020,它被描述為“驅動程序遇到問題並被置於設備刪除狀態”,這並不像我希望的那樣有用。

  2. 應用程序在IMFMediaBuffer::Lock調用的外部 dll 中崩潰。 dll 似乎取決於所使用的 GPU。 對於 Intel 集成 GPU,它是 igd10iumd32.dll,對於 Nvidia 移動 GPU,它是 mfplat.dll。 此特定崩潰的消息如下:“在decoder_tester.exe 中的 0x53C6DB8C (mfplat.dll) 處拋出異常:0xC0000005:訪問沖突讀取位置 0x00000024”。 執行之間的地址不同,有時涉及讀取,有時涉及寫入。

  3. 圖形驅動程序停止響應,系統掛起一小段時間,然后應用程序像第 2 點那樣崩潰或像第 1 點那樣結束。

  4. 該應用程序運行良好,並使用硬件加速處理所有幀。

大多數時候是 1 或 2,很少是 3 或 4。


以下是在我的機器(Intel Core i5-6500 with HD Graphics 530、Windows 10 Pro)上以不同模式進行處理時 CPU/GPU 的使用情況。

  • NULL - CPU:~90%,GPU:~15%
  • 硬件 - CPU:~15%,GPU:~60%
  • 軟件 - CPU:~40%,GPU:~7%

我在三台機器上測試了該應用程序。 它們都具有 Intel 集成 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;
}

您的代碼在概念上是正確的,唯一的評論 - 而且不是很明顯 - Media Foundation 解碼器是多線程的。 您正在使用 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);

另請注意,這個簡單的代碼示例在您為獲取連續緩沖區而添加的行周圍存在性能瓶頸。 顯然,這是您訪問數據的舉動……但是設計行為是解碼數據已經在視頻內存中,並且您傳輸到系統內存是一項昂貴的操作。 也就是說,您向循環添加了嚴重的性能損失。 您將對以這種方式檢查數據的有效性感興趣,而當涉及到性能基准測試時,您應該將其注釋掉。

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 設備管理器在加速場景中唯一使用的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM