[最新版中已修正]Media extensions示例代码中的内存泄露问题 (Windows 8.1)
Media extensions sample演示了如何在windows store应用程序中实现和使用media foundation (MF)过滤器,这对想要实现自己的播放器软件的开发者来说很有参考意义。但是这个示例代码有一个问题,当视频停止播放的时候不会调用CMPEG1Source的析构函数,这样就会导致内存泄露。
实际上这个问题是由于MF过滤器中的COM对象的引用计数没有匹配导致的。当MediaElement对象的source设为null的时候,CMPEG1Source对象的引用计数并没有减为0,这就导致了CMPEG1Source对象无法及时释放。
如果我们检查CMPEG1Source对象的引用计数,我们会发现在CMPEG1Stream的构造函数中会增加CMPEG1Source对象的引用计数:
CMPEG1Stream::CMPEG1Stream(CMPEG1Source *pSource, IMFStreamDescriptor *pSD, HRESULT& hr) :
m_cRef(1),
m_pEventQueue(NULL),
m_state(STATE_STOPPED),
m_bActive(FALSE),
m_bEOS(FALSE),
m_flRate(1.0f)
{
…
m_pSource = pSource;
m_pSource->AddRef();
…
}
这个引用计数会在CMPEG1Stream的析构函数中被减去:
CMPEG1Stream::~CMPEG1Stream()
{
assert(m_state == STATE_SHUTDOWN);
SafeRelease(&m_pSource);
…
}
但是CMPEG1Stream的析构函数也没有被调用到,这就意味着实际上CMPEG1Stream对象也存在着内存泄露。
那么,让我们再来检查一下 CMPEG1Stream对象的引用计数。当我们在StreamList对象的AddStream函数中增加一个stream的时候,CMPEG1Stream的引用计数会加1:
HRESULT AddStream(BYTE id, CMPEG1Stream *pStream)
{
…
m_streams[m_count] = pStream;
pStream->AddRef();
…
}
然后CMPEG1Stream的引用计数会在StreamList的Clear函数中减1:
void Clear()
{
for (UINT32 i = 0; i < MAX_STREAMS; i++)
{
SafeRelease(&m_streams[i]);
}
m_count = 0;
}
不幸的是,当我们关闭CMPEG1Source对象时,StreamList的Clear函数并没有被调用到。所以解决方案就是我们需要在CMPEG1Source::Shutdown函数中加上一行来清除StreamList中CMPEG1Stream对象的引用计数:
HRESULT CMPEG1Source::Shutdown()
{
…
if (SUCCEEDED(hr))
{
// Shut down the stream objects.
for (DWORD i = 0; i < m_streams.GetCount(); i++)
{
(void)m_streams[i]->Shutdown();
}
m_streams.Clear();
…
}
…
}
当我们再Shutdown函数中加上了m_streams.Clear()这一行以后,这个内存泄露问题就得到解决了,示例程序也能够正常工作了。