Memory leaks 英伟达H.264编码器MFT是否泄漏资源?

Memory leaks 英伟达H.264编码器MFT是否泄漏资源?,memory-leaks,directx,nvidia,ms-media-foundation,Memory Leaks,Directx,Nvidia,Ms Media Foundation,我一直在努力解决一个似乎由NVIDIA的h.264编码器MFT引起的资源泄漏问题。每次向编码器提交一帧时,我的D3D设备的参考计数都会增加1,即使关闭MFT,也不会放弃该参考。一堆线程也泄漏了 我几乎准备好向NVIDIA提出这一点,但我想首先确保没有任何明显的遗漏。请看下面我的实现——我已经尽力让它尽可能简洁明了 NVIDIA编码器可能出现问题的原因: 这只发生在NVIDIA的编码器上。在例如Intel的QuickSync上运行时,未观察到泄漏 我的代码中出现问题的原因的参数: 我曾尝试使

我一直在努力解决一个似乎由NVIDIA的h.264编码器MFT引起的资源泄漏问题。每次向编码器提交一帧时,我的D3D设备的参考计数都会增加1,即使关闭MFT,也不会放弃该参考。一堆线程也泄漏了

我几乎准备好向NVIDIA提出这一点,但我想首先确保没有任何明显的遗漏。请看下面我的实现——我已经尽力让它尽可能简洁明了

NVIDIA编码器可能出现问题的原因:

  • 这只发生在NVIDIA的编码器上。在例如Intel的QuickSync上运行时,未观察到泄漏
我的代码中出现问题的原因的参数:

  • 我曾尝试使用SinkWriter以类似的方式将DXGI曲面写入到文件中,但这里没有泄漏。不幸的是,我无法访问SinkWriter的源代码。如果有人能给我指出一些我可以比较的工作示例代码,我将非常高兴
#pragma注释(lib,“D3D11.lib”)
#pragma注释(lib,“mfplat.lib”)
#pragma注释(lib,“mf.lib”)
#pragma注释(lib,“evr.lib”)
#pragma注释(lib,“mfuuid.lib”)
#pragma注释(lib,“Winmm.lib”)
//性病
#包括
#包括
//窗户
#包括
#包括
//DirectX
#包括
/媒体基金会
#包括
#包括
#包括
#包括
#包括
//错误处理
#如果(!(x)){printf(“%s(%d)%s”为false\n“,_文件,_线,__线,#x),则定义检查(x);抛出std::exception()}
#定义CHECK_HR(x){HRESULT HR_=(x);if(FAILED(HR_)){printf(“%s(%d)%s失败,出现0x%x\n“,uuuu文件,uuuu行,uuuuuu行,#x,HR_u);抛出std::exception()}
//常数
constexpr UINT ENCODE_WIDTH=1920;
constexpr UINT ENCODE_HEIGHT=1080;
constexpr UINT ENCODE_FRAMES=120;
void runEncode();
int main()
{
检查_HR(coinitializex(NULL,COINIT_APARTMENTTHREADED));
检查_HR(MFStartup(MF_版本));
对于(;;)
{
runEncode();
如果(getchar()=='q')
打破
}
检查_HR(MFShutdown());
返回0;
}
void runEncode()
{
CComPtr装置;
CComPtr上下文;
计算机设备管理器;
CComPtr分配器;
CComPtr变换;
CComPtr transformAttrs;
CComQIPtr-eventGen;
DWORD输入流ID;
DWORD输出流ID;
// ------------------------------------------------------------------------
//初始化D3D11
// ------------------------------------------------------------------------
检查\u HR(D3D11CreateDevice(NULL,D3D\u驱动程序\u类型\u硬件,NULL,D3D11\u创建\u设备\u视频\u支持\124; D3D11\u创建\u设备\u调试,NULL,0,D3D11\u SDK\u版本,&设备,NULL,&上下文));
{
//在本应用程序中可能不需要,但MFT可能需要它?
CComQIPtr mt(设备);
支票(mt);
mt->SetMultithreadProtected(真);
}
//创建设备管理器
UINT重置令牌;
检查_HR(MFCreateDXGIDeviceManager(&resetToken,&deviceManager));
检查_HR(设备管理器->重置设备(设备,重置令牌));
// ------------------------------------------------------------------------
//初始化硬件编码器MFT
// ------------------------------------------------------------------------
{
//找到编码器
CCOMHEAPTR活化剂;
UINT32 activateCount=0;
//输入和输出类型
MFT_寄存器_TYPE_INFO={MFMediaType_Video,MFVideoFormat_NV12};
MFT_寄存器_TYPE_INFO outInfo={MFMediaType_Video,MFVideoFormat_H264};
//查询适配器LUID以获取设备的匹配编码器。
CComQIPtr DXG设备(设备);
检查(DXG设备);
CComPtr适配器;
选中_HR(dxgiDevice->GetAdapter(&adapter));
DXGI_适配器_描述适配器DESC;
选中_HR(适配器->GetDesc(&adapterDesc));
CComPtr枚举属性;
检查_HR(MFCreateAttributes(&enumAttrs,1));
检查\u HR(enumAttrs->SetBlob(MFT\u ENUM\u ADAPTER\u LUID,(字节*)&adapterDesc.AdapterLuid,sizeof(LUID));
检查\u HR(MFTEnum2(MFT\u类别\u视频\u编码器、MFT\u枚举\u标志\u硬件、MFT\u枚举\u标志\u排序过滤器、&inInfo、&outInfo、enumAttrs、&activateRaw和activateCount));
检查(activateCount!=0);
//选择第一个返回的编码器
CComPtr activate=activateRaw[0];
//内存管理
对于(UINT32 i=0;iRelease();
//激活
选中_HR(activate->ActivateObject(IID_PPV_ARGS(&transform));
//获取属性
选中(transform->GetAttributes(&transformAttrs));
}
// ------------------------------------------------------------------------
//查询编码器名称(不是必需的,但很好)并解锁以供异步使用
// ------------------------------------------------------------------------
{
UINT32名称长度=0;
std::wstring名称;
选中_HR(transformAttrs->GetStringLength(MFT_友好的名称属性和名称长度));
//IMFAttributes::GetString返回以null结尾的宽字符串
名称。调整大小((大小)名称长度+1);
选中\u HR(transformAttrs->GetString(MFT\u友好的\u NAME\u属性,&NAME[0],(UINT32)NAME.size(),&nameLength));
名称。调整大小(名称长度);
printf(“使用%ls\n”,name.c_str());
//解锁转换以供异步使用并获取事件生成器
检查HR(transformAttrs->SetUINT32(MF\u TRANSFORM\u ASYNC\u UNLOCK,TRUE));
检查(eventGen=transform);
}
//获取流ID(1个输入流和1个输出流除外)
{
HRESULT hr=transform->getStreamId(1,&inputStreamID,1,&outputStreamID);
如果(hr==E_NOTIMPL)
{
inputStreamID=0;
outputStreamID=0;
hr=S_OK;
}
检查人力资源(HR);
}
// ----------------------------
#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "Winmm.lib")

// std
#include <iostream>
#include <string>

// Windows
#include <windows.h>
#include <atlbase.h>

// DirectX
#include <d3d11.h>

// Media Foundation
#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include <Codecapi.h>

// Error handling
#define CHECK(x) if (!(x)) { printf("%s(%d) %s was false\n", __FILE__, __LINE__, #x); throw std::exception(); }
#define CHECK_HR(x) { HRESULT hr_ = (x); if (FAILED(hr_)) { printf("%s(%d) %s failed with 0x%x\n", __FILE__, __LINE__, #x, hr_); throw std::exception(); } }

// Constants
constexpr UINT ENCODE_WIDTH = 1920;
constexpr UINT ENCODE_HEIGHT = 1080;
constexpr UINT ENCODE_FRAMES = 120;

void runEncode();

int main()
{
    CHECK_HR(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
    CHECK_HR(MFStartup(MF_VERSION));

    for (;;)
    {
        runEncode();
        if (getchar() == 'q')
            break;
    }

    CHECK_HR(MFShutdown());

    return 0;
}

void runEncode()
{
    CComPtr<ID3D11Device> device;
    CComPtr<ID3D11DeviceContext> context;
    CComPtr<IMFDXGIDeviceManager> deviceManager;

    CComPtr<IMFVideoSampleAllocatorEx> allocator;
    CComPtr<IMFTransform> transform;
    CComPtr<IMFAttributes> transformAttrs;
    CComQIPtr<IMFMediaEventGenerator> eventGen;
    DWORD inputStreamID;
    DWORD outputStreamID;


    // ------------------------------------------------------------------------
    // Initialize D3D11
    // ------------------------------------------------------------------------

    CHECK_HR(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_DEBUG, NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context));

    {
        // Probably not necessary in this application, but maybe the MFT requires it?
        CComQIPtr<ID3D10Multithread> mt(device);
        CHECK(mt);
        mt->SetMultithreadProtected(TRUE);
    }

    // Create device manager
    UINT resetToken;
    CHECK_HR(MFCreateDXGIDeviceManager(&resetToken, &deviceManager));
    CHECK_HR(deviceManager->ResetDevice(device, resetToken));


    // ------------------------------------------------------------------------
    // Initialize hardware encoder MFT
    // ------------------------------------------------------------------------

    {
        // Find the encoder
        CComHeapPtr<IMFActivate*> activateRaw;
        UINT32 activateCount = 0;

        // Input & output types
        MFT_REGISTER_TYPE_INFO inInfo = { MFMediaType_Video, MFVideoFormat_NV12 };
        MFT_REGISTER_TYPE_INFO outInfo = { MFMediaType_Video, MFVideoFormat_H264 };

        // Query for the adapter LUID to get a matching encoder for the device.
        CComQIPtr<IDXGIDevice> dxgiDevice(device);
        CHECK(dxgiDevice);
        CComPtr<IDXGIAdapter> adapter;
        CHECK_HR(dxgiDevice->GetAdapter(&adapter));

        DXGI_ADAPTER_DESC adapterDesc;
        CHECK_HR(adapter->GetDesc(&adapterDesc));

        CComPtr<IMFAttributes> enumAttrs;
        CHECK_HR(MFCreateAttributes(&enumAttrs, 1));
        CHECK_HR(enumAttrs->SetBlob(MFT_ENUM_ADAPTER_LUID, (BYTE*)&adapterDesc.AdapterLuid, sizeof(LUID)));

        CHECK_HR(MFTEnum2(MFT_CATEGORY_VIDEO_ENCODER, MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER, &inInfo, &outInfo, enumAttrs, &activateRaw, &activateCount));

        CHECK(activateCount != 0);

        // Choose the first returned encoder
        CComPtr<IMFActivate> activate = activateRaw[0];

        // Memory management
        for (UINT32 i = 0; i < activateCount; i++)
            activateRaw[i]->Release();

        // Activate
        CHECK_HR(activate->ActivateObject(IID_PPV_ARGS(&transform)));

        // Get attributes
        CHECK_HR(transform->GetAttributes(&transformAttrs));
    }


    // ------------------------------------------------------------------------
    // Query encoder name (not necessary, but nice) and unlock for async use
    // ------------------------------------------------------------------------

    {

        UINT32 nameLength = 0;
        std::wstring name;

        CHECK_HR(transformAttrs->GetStringLength(MFT_FRIENDLY_NAME_Attribute, &nameLength));

        // IMFAttributes::GetString returns a null-terminated wide string
        name.resize((size_t)nameLength + 1);
        CHECK_HR(transformAttrs->GetString(MFT_FRIENDLY_NAME_Attribute, &name[0], (UINT32)name.size(), &nameLength));
        name.resize(nameLength);

        printf("Using %ls\n", name.c_str());

        // Unlock the transform for async use and get event generator
        CHECK_HR(transformAttrs->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE));
        CHECK(eventGen = transform);
    }

    // Get stream IDs (expect 1 input and 1 output stream)
    {
        HRESULT hr = transform->GetStreamIDs(1, &inputStreamID, 1, &outputStreamID);
        if (hr == E_NOTIMPL)
        {
            inputStreamID = 0;
            outputStreamID = 0;
            hr = S_OK;
        }
        CHECK_HR(hr);
    }


    // ------------------------------------------------------------------------
    // Configure hardware encoder MFT
    // ------------------------------------------------------------------------

    // Set D3D manager
    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager.p)));

    // Set output type
    CComPtr<IMFMediaType> outputType;
    CHECK_HR(MFCreateMediaType(&outputType));

    CHECK_HR(outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
    CHECK_HR(outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
    CHECK_HR(outputType->SetUINT32(MF_MT_AVG_BITRATE, 30000000));
    CHECK_HR(MFSetAttributeSize(outputType, MF_MT_FRAME_SIZE, ENCODE_WIDTH, ENCODE_HEIGHT));
    CHECK_HR(MFSetAttributeRatio(outputType, MF_MT_FRAME_RATE, 60, 1));
    CHECK_HR(outputType->SetUINT32(MF_MT_INTERLACE_MODE, 2));
    CHECK_HR(outputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));

    CHECK_HR(transform->SetOutputType(outputStreamID, outputType, 0));

    // Set input type
    CComPtr<IMFMediaType> inputType;
    CHECK_HR(transform->GetInputAvailableType(inputStreamID, 0, &inputType));

    CHECK_HR(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
    CHECK_HR(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
    CHECK_HR(MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, ENCODE_WIDTH, ENCODE_HEIGHT));
    CHECK_HR(MFSetAttributeRatio(inputType, MF_MT_FRAME_RATE, 60, 1));

    CHECK_HR(transform->SetInputType(inputStreamID, inputType, 0));


    // ------------------------------------------------------------------------
    // Create sample allocator
    // ------------------------------------------------------------------------

    {
        MFCreateVideoSampleAllocatorEx(IID_PPV_ARGS(&allocator));
        CHECK(allocator);

        CComPtr<IMFAttributes> allocAttrs;
        MFCreateAttributes(&allocAttrs, 2);

        CHECK_HR(allocAttrs->SetUINT32(MF_SA_D3D11_BINDFLAGS, D3D11_BIND_RENDER_TARGET));
        CHECK_HR(allocAttrs->SetUINT32(MF_SA_D3D11_USAGE, D3D11_USAGE_DEFAULT));

        CHECK_HR(allocator->SetDirectXManager(deviceManager));
        CHECK_HR(allocator->InitializeSampleAllocatorEx(1, 2, allocAttrs, inputType));
    }


    // ------------------------------------------------------------------------
    // Start encoding
    // ------------------------------------------------------------------------

    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL));
    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL));
    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL));

    // Encode loop
    for (int i = 0; i < ENCODE_FRAMES; i++)
    {
        // Get next event
        CComPtr<IMFMediaEvent> event;
        CHECK_HR(eventGen->GetEvent(0, &event));

        MediaEventType eventType;
        CHECK_HR(event->GetType(&eventType));

        switch (eventType)
        {
        case METransformNeedInput:
        {
            CComPtr<IMFSample> sample;
            CHECK_HR(allocator->AllocateSample(&sample));
            CHECK_HR(transform->ProcessInput(inputStreamID, sample, 0));

            // Dereferencing the device once after feeding each frame "fixes" the leak.
            //device.p->Release();

            break;
        }

        case METransformHaveOutput:
        {
            DWORD status;
            MFT_OUTPUT_DATA_BUFFER outputBuffer = {};
            outputBuffer.dwStreamID = outputStreamID;

            CHECK_HR(transform->ProcessOutput(0, 1, &outputBuffer, &status));

            DWORD bufCount;
            DWORD bufLength;
            CHECK_HR(outputBuffer.pSample->GetBufferCount(&bufCount));

            CComPtr<IMFMediaBuffer> outBuffer;
            CHECK_HR(outputBuffer.pSample->GetBufferByIndex(0, &outBuffer));
            CHECK_HR(outBuffer->GetCurrentLength(&bufLength));

            printf("METransformHaveOutput buffers=%d, bytes=%d\n", bufCount, bufLength);

            // Release the sample as it is not processed further.
            if (outputBuffer.pSample)
                outputBuffer.pSample->Release();
            if (outputBuffer.pEvents)
                outputBuffer.pEvents->Release();
            break;
        }
        }
    }

    // ------------------------------------------------------------------------
    // Finish encoding
    // ------------------------------------------------------------------------

    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL));
    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, NULL));
    CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, NULL));

    // Shutdown
    printf("Finished encoding\n");

    // I've tried all kinds of things...
    //CHECK_HR(transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(nullptr)));

    //transform->SetInputType(inputStreamID, NULL, 0);
    //transform->SetOutputType(outputStreamID, NULL, 0);

    //transform->DeleteInputStream(inputStreamID);

    //deviceManager->ResetDevice(NULL, resetToken);

    CHECK_HR(MFShutdownObject(transform));
}