c++ - 媒体基础多个视频播放导致内存泄漏和未定义时间段后崩溃

标签 c++ video-streaming ms-media-foundation

所以我们使用由 C++ 媒体基础代码组成的堆栈来播放视频文件。一个重要的要求是能够以不断重复的顺序播放这些视频,因此每个视频插槽都会定期更改正在播放的视频。在我们当前的示例中,我们正在创建 16 个 HWND 以将视频渲染到和 16 个相应的播放器对象中。主应用程序依次遍历所有这些并执行以下操作:

关闭最后一个播放器 释放对象 为新玩家共同创建实例 使用(旧的)HWND 初始化播放器 开始播放 媒体播放器称为“MediaPlayer2”,这需要构建并注册为 COM (regsvr32)。主要应用程序可在 TAPlayer2 项目中找到。它在注册表中搜索播放器的 CLSID 并将其实例化。作为当前测试文件,我们使用必须驻留在磁盘上的 test.mp4,如 C:\test.mp4

现在一切顺利。程序循环播放器,视频不断重新启动和播放。内存占用正常,一切顺利。在 20 分钟到 4 天之间的任何时间范围之后,所有突然的事情都会变得奇怪。在这一点上,EVR 对“InitializeRenderer”的调用似乎变慢了,最终根本不再通过。有了这个,线程数和内存占用也将开始急剧增加,并且在一定时间后,取决于现有 RAM,所有内存都将耗尽,我们的应用程序崩溃,通常在 GPU 驱动程序中或 EVR DLL 附近。

我很乐意尝试任何其他建议解决我的问题的代码示例:同时显示多个视频窗口,并像在播放列表中一样循环播放它们。需要在 Windows 10 上运行! 我已经这样做了很长一段时间了,而且很难坚持。我上传了上面提到的代码示例并添加了这篇文章的链接。这应该开箱即用。如果愿意,我还可以在线程的此处提供代码摘录。

感谢任何帮助,谢谢

托马斯

演示项目链接 (VS2015):https://filebin.net/l8gl79jrz6fd02vt

编辑:winmain.cpp末尾的以下代码用于重启播放器:

do
    {
    for (int i = 0; i < PLAYER_COUNT; i++)
    {
      hr = g_pPlayer[i]->Shutdown();
      SafeRelease(&g_pPlayer[i]);
      hr = CoCreateInstance(CLSID_AvasysPlayer,  // CLSID of the coclass
        NULL,                         // no aggregation
        CLSCTX_INPROC_SERVER,         // the server is in-proc
        __uuidof(IAvasysPlayer),      // IID of the interface we want
        (void**)&g_pPlayer[i]);         // address of our interface pointer

      hr = g_pPlayer[i]->InitPlayer(hwndPlayers[i]);

      hr = g_pPlayer[i]->OpenUrl(L"C:\\test.mp4");
    }

  } while (true);

最佳答案

像一些 MediaFoundation 接口(interface)

  • IMFMediaSource
  • IMFMediaSession
  • IMFMediaSink

在释放它们之前需要关闭。

At this point it seems as if calls to "InitializeRenderer" by the EVR slow down and eventually don't go through anymore at all. ... usually somewhere in the GPU driver or near the EVR DLL.

在您的代码中进行精确搜索的好方法。

在您的 PlayerTopoBuilder.cpp 文件中,位于 CPlayerTopoBuilder::AddBranchToPartialTopology 处:

if (bVideo)
{
  if (false) {
    BREAK_ON_FAIL(hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate));
    BREAK_ON_FAIL(hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode));
  }
    else {
      //// try directly create renderer
      BREAK_ON_FAIL(hr = MFCreateVideoRenderer(__uuidof(IMFMediaSink), (void**)&pMediaSink));
      CComQIPtr<IMFVideoRenderer> pRenderer = pMediaSink;
      BREAK_ON_FAIL(hr = pRenderer->InitializeRenderer(nullptr, nullptr));

      CComQIPtr<IMFGetService> getService(pRenderer);
      BREAK_ON_FAIL(hr = getService->GetService(MR_VIDEO_RENDER_SERVICE, __uuidof(IMFVideoDisplayControl), (void**)&pVideoDisplayControl));
      BREAK_ON_FAIL(hr = pVideoDisplayControl->SetVideoWindow(hVideoWnd));

      BREAK_ON_FAIL(hr = pMediaSink->GetStreamSinkByIndex(0, &pStreamSink));
      BREAK_ON_FAIL(hr = AddOutputNode(pTopology, 0, &pOutputNode, pStreamSink));
    }
}

您使用 MFCreateVideoRenderer 和 pMediaSink 创建一个 IMFMediaSink。 pMediaSink 因使用 CComPtr 而发布,但从未关闭。

您必须保留对媒体接收器的引用,并在播放器关闭时关闭/释放它。

或者您可以使用 MFCreateVideoRendererActivate 的不同方法。

IMFMediaSink::Shutdown

If the application creates the media sink, it is responsible for calling Shutdown to avoid memory or resource leaks. In most applications, however, the application creates an activation object for the media sink, and the Media Session uses that object to create the media sink. In that case, the Media Session — not the application — shuts down the media sink. (For more information, see Activation Objects.)

我还建议您在 CPlayer::CloseSession 的末尾使用这种代码(在释放所有其他对象之后):

if(m_pSession != NULL){

    hr = m_pSession->Shutdown();

    ULONG ulMFObjects = m_pSession->Release();
    m_pSession = NULL;

    assert(ulMFObjects == 0);
}

MFCreateVideoRendererActivate的使用可以看我的MFNodePlayer工程:

MFNodePlayer

编辑

我重写了你的程序,但我尽量保留你的逻辑和原始源代码,比如 CComPtr/Mutex...

MFMultiVideo

告诉我这个程序是否有内存泄漏。

这将取决于您的回答,但之后我们可以讨论 MediaFoundation 的最佳实践。

另一个想法:

您的程序使用 1 到 16 个 IMFMediaSession。在良好的计算机配置上,我认为您可以只使用一个 IMFMediasession(永远不要尝试聚合 16 个 MFSource)。

访问:

CustomVideoMixer

了解另一种方法。

我认为您使用 16 IMFMediasession 的方法不是现代计算机上的最佳方法。 VuVirt 谈论这个。

EDIT2

我已经使用工作队列更新了 MFMultiVideo。

我认为问题可能在于您为每个播放器调用了 MFStartup/MFShutdown。 例如,只需在 winmain.cpp 中调用 MFStartup/MFShutdown 一次,就像我的程序一样。

关于c++ - 媒体基础多个视频播放导致内存泄漏和未定义时间段后崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58912803/

相关文章:

C++ Hook 我自己的程序函数

Android VideoView保存RTSP流

ionic-framework - 是否可以在 ionic 上流式传输 RTSP iptv

c++ - 媒体基础自定义混合器 MFT 出现错误 MF_E_CANNOT_CREATE_SINK

winapi - MediaFoundation中是否有实时混音效果或MFT?

c++ - 如果初始化失败如何终止qt编程?

c++ - 简单的循环,哪一个我会获得更好的性能,哪一个被推荐?在循环内或循环外定义变量?

c++ - 在 C++ 中使用带有队列的 map

sockets - RST 在通过 TCP 套接字的视频流中?

c - 使用 Microsoft Media Foundation 从非文件源解码音频