媒体基础 多个视频播放导致内存泄漏和崩溃



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

关闭最后一个播放器释放对象CoCreateinstance for a new player使用(旧)HWND初始化播放器开始播放媒体播放器名为"MediaPlayer2",需要构建并注册为COM(regsvr32)。主要应用程序可在TAPlayer2项目中找到。它在注册表中搜索播放器的CLSID并实例化它。作为当前的测试文件,我们使用test.mp4,它必须像C:\test.mp4一样驻留在磁盘上

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

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

感谢提供的任何帮助

Thomas

链接到演示项目(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接口,如

  • IMFMediaSource
  • IMF媒体会话
  • IMF媒体接收器

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

在这一点上,EVR对"InitializeRenderer"的调用似乎变慢了,最终根本无法通过。…通常位于GPU驱动程序中或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以避免内存或资源泄漏。但是,在大多数应用程序中,应用程序会为媒体接收器创建一个激活对象,而媒体会话会使用该对象来创建媒体接收器。在这种情况下,媒体会话(而不是应用程序)会关闭媒体接收器。(有关更多信息,请参阅激活对象。)

我还建议您在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)。

访问:

自定义视频混合器

了解另一种方法。

我认为你使用16 IMFMediasession的方法不是现代计算机上最好的方法。VuVirt谈到了这一点。

EDIT2

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

我认为问题可能是你为每个玩家调用MFStartup/MFShutdown。例如,只需在winmain.cpp中调用MFStartup/MFShutdown一次,就像我的程序一样。

最新更新