[最新版中已修正]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()这一行以后,这个内存泄露问题就得到解决了,示例程序也能够正常工作了。