Guest
Ist der Nvidia H.264 -Encoder MFT undicht Ressourcen?
Post
by Guest » 09 Feb 2025, 11:48
Ich habe mit einem Ressourcenleck zu kämpfen, das anscheinend durch den H.264 -Encoder MFT von Nvidia verursacht wird. Jedes Mal, wenn ein Frame an den Encoder übermittelt wird, wird die Referenzzahl meines D3D -Geräts durch 1 erhöht, und diese Referenz wird auch nach dem Herunterfahren der MFT nicht aufgegeben. Eine Reihe von Threads sind ebenfalls durchgesickert. vermisst. Weitere Informationen finden Sie in meiner Implementierung unten - ich habe versucht, sie so präzise und klar wie möglich zu halten. >
Dies geschieht nur mit Nvidia's Encoder. Beim Laufen wird kein Leck beobachtet, z. Intels QuickSync.
Ich habe versucht, einen Sinkwriter zu verwenden, um DXGI -Oberflächen in ähnlicher Weise in eine Datei zu schreiben, und hier ist das Leck nicht vorhanden. Leider habe ich keinen Zugriff auf den Quellcode des Sinkwriter. Ich würde mich sehr freuen, wenn mich jemand auf einen funktionierenden Beispielcode verweisen könnte, mit dem ich vergleichen könnte. < /Li>
< /ul>
Code: Select all
#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
#include
// Windows
#include
#include
// DirectX
#include
// Media Foundation
#include
#include
#include
#include
#include
// 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 device;
CComPtr context;
CComPtr deviceManager;
CComPtr allocator;
CComPtr transform;
CComPtr transformAttrs;
CComQIPtr 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 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 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 dxgiDevice(device);
CHECK(dxgiDevice);
CComPtr adapter;
CHECK_HR(dxgiDevice->GetAdapter(&adapter));
DXGI_ADAPTER_DESC adapterDesc;
CHECK_HR(adapter->GetDesc(&adapterDesc));
CComPtr 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 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(deviceManager.p)));
// Set output type
CComPtr 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 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 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 event;
CHECK_HR(eventGen->GetEvent(0, &event));
MediaEventType eventType;
CHECK_HR(event->GetType(&eventType));
switch (eventType)
{
case METransformNeedInput:
{
CComPtr 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 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(nullptr)));
//transform->SetInputType(inputStreamID, NULL, 0);
//transform->SetOutputType(outputStreamID, NULL, 0);
//transform->DeleteInputStream(inputStreamID);
//deviceManager->ResetDevice(NULL, resetToken);
CHECK_HR(MFShutdownObject(transform));
}
1739098123
Guest
Ich habe mit einem Ressourcenleck zu kämpfen, das anscheinend durch den H.264 -Encoder MFT von Nvidia verursacht wird. Jedes Mal, wenn ein Frame an den Encoder übermittelt wird, wird die Referenzzahl meines D3D -Geräts durch 1 erhöht, und diese Referenz wird auch nach dem Herunterfahren der MFT nicht aufgegeben. Eine Reihe von Threads sind ebenfalls durchgesickert. vermisst. Weitere Informationen finden Sie in meiner Implementierung unten - ich habe versucht, sie so präzise und klar wie möglich zu halten. > Dies geschieht nur mit Nvidia's Encoder. Beim Laufen wird kein Leck beobachtet, z. Intels QuickSync. Ich habe versucht, einen Sinkwriter zu verwenden, um DXGI -Oberflächen in ähnlicher Weise in eine Datei zu schreiben, und hier ist das Leck nicht vorhanden. Leider habe ich keinen Zugriff auf den Quellcode des Sinkwriter. Ich würde mich sehr freuen, wenn mich jemand auf einen funktionierenden Beispielcode verweisen könnte, mit dem ich vergleichen könnte. < /Li> < /ul> [code]#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 #include // Windows #include #include // DirectX #include // Media Foundation #include #include #include #include #include // 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 device; CComPtr context; CComPtr deviceManager; CComPtr allocator; CComPtr transform; CComPtr transformAttrs; CComQIPtr 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 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 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 dxgiDevice(device); CHECK(dxgiDevice); CComPtr adapter; CHECK_HR(dxgiDevice->GetAdapter(&adapter)); DXGI_ADAPTER_DESC adapterDesc; CHECK_HR(adapter->GetDesc(&adapterDesc)); CComPtr 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 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(deviceManager.p))); // Set output type CComPtr 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 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 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 event; CHECK_HR(eventGen->GetEvent(0, &event)); MediaEventType eventType; CHECK_HR(event->GetType(&eventType)); switch (eventType) { case METransformNeedInput: { CComPtr 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 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(nullptr))); //transform->SetInputType(inputStreamID, NULL, 0); //transform->SetOutputType(outputStreamID, NULL, 0); //transform->DeleteInputStream(inputStreamID); //deviceManager->ResetDevice(NULL, resetToken); CHECK_HR(MFShutdownObject(transform)); } [/code]