Visual c++ 在directshow筛选器中使用回调函数会导致内存泄漏

Visual c++ 在directshow筛选器中使用回调函数会导致内存泄漏,visual-c++,callback,directshow,Visual C++,Callback,Directshow,我使用的是第三方API,我从回调函数中获取流 int OnNewImage(BYTE *pData, int nLen) < >从控制台运行一个简单的C++示例程序 int continue = 1; int OnNewImage(BYTE *pData, int nLen) { std::cout << "On new image is called" << std::endl; return continue; } int main() { //

我使用的是第三方API,我从回调函数中获取流

int OnNewImage(BYTE *pData, int nLen)
< >从控制台

运行一个简单的C++示例程序
int continue = 1;

int OnNewImage(BYTE *pData, int nLen)
{
  std::cout << "On new image is called" << std::endl;
  return continue;
}


int main()
{

  // This will block
  int result = DownloadStream(/*params*/...,OnNewImage /*callbackfunction*/);

  return 0;

}

我看到的第一件事是,你的析构函数不是虚拟的。当使用继承时未发生释放时,这可能是导致泄漏的原因。请参阅虚拟析构函数的必要性。

在DVRStreamThread中,您有:

streamReader->m_RGB24Buffer = (BYTE*)malloc(streamReader->m_RGB24BufferSize);

但我在任何地方都看不到对
free()
的匹配调用。删除DVRPin对象时,您必须显式释放其m_RGB24Buffer成员指向的数据。

第三方API中的错误?嗯,Wimmel…它不会在简单的控制台应用程序…或winform应用程序中生成“内存”泄漏…因此我不能责怪第三方API。在这个简单的用例中不会泄漏,否则使用时可能会泄漏。如果有什么东西在泄漏,而您没有设法获得怀疑特定组件的详细信息,那么您需要深入调查并隔离泄漏。好吧,我隔离了它的用法。我有一个简单的队列,我插入传入的帧。与main使用相同的代码不会产生任何泄漏。所以我只是想知道directshow框架是否有这样的影响副作用?如果directshow有影响-这对我来说已经很奇怪了-那么它无论如何是第三方API中的一个bug。如果DirectShow fitler(据称是您的)没有内存泄漏,他们就做错了。无论如何,在三个:DirectShow、过滤器(在自定义过滤器的情况下)和第三方API中,DirectShow是问题的最不可能的原因。嗯,对于一般C++来说是个好点。但是对于我的情况来说,这不是真的:当我评论第三部分API下载流函数时,我没有内存问题。它与虚无关:我让析构函数虚拟化但没有改变。这就是为什么我说它“可能是一个原因…”,而C++中,它是不释放对象的主要原因之一。另一个原因可能是,复制了DirectX COM对象(因此AddReFor()),因此增加了ReFiCube。这可能是接口未被释放的原因。您可以尝试在队列中的接口上调用Release()。内存使用量正在严格“增加”,尽管我没有向队列中插入任何内容。[队列是我的代码,我禁用了与队列相关的所有内容]……嗯,这是一次性的“分配”。我不担心它。问题是,当过滤器工作时,“内存消耗”会随着时间“增加”,这是这行代码无法做到的。一次性分配不会随着时间的推移而增加,这是真的。但是,我刚刚注意到,在您调用
pSample->Release()的
FillBuffer
的简短版本中
,在您的长版本中也被调用。
FillBuffer
不应释放
IMediaSample
DoBufferProcessingLoop
FillBuffer
返回后需要它,以将其传递给
Deliver
。您的
DoBufferProcessingLoop
Deliver
返回后正确地释放它urns。所以,您要释放它两次。可能这会扰乱
IMemAllocator
对缓冲池的管理。从
FillBuffer
中取出
pSample->Release()
,看看这是否有帮助。我删除了pSample->Release()只需从FillBuffer返回S_OK;memeory仍然会增加。伙计,你得到了一个很难的结果。好吧,你已经将你的过滤器削减到几乎为零,为
FillBuffer
和你的
OnNewImage
回调提供了一个存根。我注意到的一件事是
OnNewImage
的调用签名在你的控制台程序f中是不同的rom你的过滤代码是什么。这是为什么?我在你仍然重复运行的代码中看到的唯一重要区别是调用
IncomingFramesQueue.WaitUntilHaveElements()
在您的
DoBufferProcessingLoop
中。我知道您说您禁用了队列代码,但您能完全取消该调用吗?我的控制台和筛选器回调签名是相同的。但为了简单起见,我没有在控制台应用程序中显示完整的签名。
#define DVRSourceFilterName L"DVRDirectShowFilter"

#include <streams.h>
#include <process.h>
#include <MyDvrApi.h>
#include "SynchronisedQueue.h"



// {F89A85DA-F77C-4d44-893B-CCA43A49E7EF}
DEFINE_GUID(CLSID_DVRSourceFilter, 
0xf89a85da, 0xf77c, 0x4d44, 0x89, 0x3b, 0xcc, 0xa4, 0x3a, 0x49, 0xe7, 0xef);

class DECLSPEC_UUID("34363248-0000-0010-8000-00AA00389B71") Subtype_H264;

class DVRSourceFilter;


using namespace std;


/*
 * **********************
 * DVRPin
 * **********************
 */

class DVRPin : public CSourceStream
{
public: 

    DVRPin(HRESULT *phr, DVRSourceFilter *pFilter);
    ~DVRPin();

    // Override the version that offers exactly one media type
    HRESULT GetMediaType(CMediaType *pMediaType);
    HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest);
    HRESULT FillBuffer(IMediaSample *pSample);

    static int OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle);

    // Setters 
    void SetDvrIp(char* dvrIp);
    void SetDvrPort( int dvrPort);
    void SetDvrUserName( char * userName);
    void SetDvrPassword(char* password);
    void SetStartTime(int startTime);
    void SetMilliSecond(int milliSecond);
    void SetChannelNumber(int channelNumber);
    void SetSize(int width, int height);

    // Getters
    char* GetDvrIp();
    int   GetDvrPort();
    char* GetDvrUserName();
    char* GetDvrPassword();
    int   GetStartTime();
    int   GetMilliSecond();
    int   GetChannelNumber();
    int   GetMode();

public: 


    char* dvrIp;
    int dvrPort;
    char* userName;
    char* password;
    int startTime;
    int milliSecond;
    int channelNumber;


    BITMAPINFOHEADER m_bmpInfo;
    BYTE* m_RGB24Buffer;
    DWORD m_RGB24BufferSize;
    bool streamCompleted;
    int hDecHandle;

    HANDLE m_hDVRStreamThreadHandle;
    unsigned int m_dwThreadID;

    SynchronisedQueue<std::vector<BYTE>> IncomingFramesQueue;

protected: 

    virtual HRESULT OnThreadCreate();
    virtual HRESULT OnThreadDestroy();
    virtual HRESULT DoBufferProcessingLoop();

};

/*
 * **********************
 * DVRSourceFilter
 * *********************
 *
 */

class DVRSourceFilter : public CSource
{

public: 

    DECLARE_IUNKNOWN;

    static CUnknown * WINAPI CreateInstance(IUnknown *pUnk, HRESULT *phr);  
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);

    void SetDVRLiveParameters(char* dvrIP, int dvrPort, char* userName, char* password, int channelNumber, int width, int height); 

private: 
    DVRSourceFilter(IUnknown *pUnk, HRESULT *phr);
    ~DVRSourceFilter();

private: 

    DVRPin *m_pPin;
};
#include "DvrSourceFilter.h"


unsigned __stdcall DVRStreamThread(LPVOID pvParam)
{

    DVRPin* streamReader = (DVRPin*)pvParam;

    int channelBits = 1 << (streamReader->channelNumber - 1);
    streamReader->m_RGB24BufferSize = streamReader->m_bmpInfo.biWidth * streamReader->m_bmpInfo.biHeight * 3;
    streamReader->m_RGB24Buffer = (BYTE*)malloc(streamReader->m_RGB24BufferSize);

    DownloadStream((LPCTSTR)streamReader->dvrIp,  
        streamReader->dvrPort , (LPCTSTR)streamReader->userName , 
        (LPCTSTR)streamReader->password , channelBits, channelBits,
        streamReader->startTime, streamReader->milliSecond, 
        streamReader->OnNewImage, (void*)streamReader);

    streamReader->startTime = -2; // End Of Stream

    return 0;
}


/*
 * ******************
 * DVRPin Class
 * ******************
 */

DVRPin::DVRPin(HRESULT *phr, DVRSourceFilter *pFilter)
: CSourceStream(NAME("DVR Source Bitmap"), phr, pFilter, L"Out")
{


    m_bmpInfo.biSize = sizeof(BITMAPINFOHEADER);
    m_bmpInfo.biCompression = BI_RGB;
    m_bmpInfo.biBitCount = 24;
    m_bmpInfo.biPlanes = 1;
    m_bmpInfo.biClrImportant = 0;
    m_bmpInfo.biClrUsed = 0;
    m_bmpInfo.biXPelsPerMeter = 0;
    m_bmpInfo.biYPelsPerMeter = 0;

    hDecHandle = 0;
    m_RGB24Buffer = NULL;
    m_RGB24BufferSize = 0;
    streamCompleted = false;
    startTime = -1; // Live Stream


    *phr = S_OK;
}


DVRPin::~DVRPin()
{   
}


int DVRPin::OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle)
{

    DVRPin* reader = (DVRPin*)pUser;

    if(reader->streamCompleted)
    {
        return false;
    }

    if(pData) 
    {

        std::vector<BYTE> vecFrame(pData, pData + nLen/sizeof(pData[0]));
        reader->IncomingFramesQueue.Enqueue(vecFrame);

    }


    return  !reader->streamCompleted;
}
HRESULT DVRPin::OnThreadCreate() 
{
    m_hDVRStreamThreadHandle = 
        (HANDLE)_beginthreadex(NULL, 0, &DVRStreamThread, (void*)this, 0, &m_dwThreadID);

    return S_OK;
}

HRESULT DVRPin::OnThreadDestroy() {


    streamCompleted = true;
    _endthreadex(0);

    CloseHandle(m_hDVRStreamThreadHandle);

    return S_OK;
}


HRESULT DVRPin::GetMediaType(CMediaType *pMediaType)
{

    CAutoLock cAutoLock(m_pFilter->pStateLock());

    CheckPointer(pMediaType, E_POINTER);

    VIDEOINFOHEADER* pvi = (VIDEOINFOHEADER*)pMediaType->AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
    if (pvi == 0) 
        return(E_OUTOFMEMORY);

    ZeroMemory(pvi, pMediaType->cbFormat);   

    pvi->bmiHeader = m_bmpInfo;
    pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);


    SetRectEmpty(&(pvi->rcSource)); 
    SetRectEmpty(&(pvi->rcTarget)); 

    pMediaType->SetType(&MEDIATYPE_Video);
    pMediaType->SetFormatType(&FORMAT_VideoInfo);
    pMediaType->SetTemporalCompression(FALSE);

    // Work out the GUID for the subtype from the header info.
    const GUID SubTypeGUID = __uuidof(Subtype_H264);//GetBitmapSubtype(&pvi->bmiHeader);
    pMediaType->SetSubtype(&SubTypeGUID);
    pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage);

    return S_OK;
}

HRESULT DVRPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest)
{

    HRESULT hr;
    CAutoLock cAutoLock(m_pFilter->pStateLock());

    CheckPointer(pAlloc, E_POINTER);
    CheckPointer(pRequest, E_POINTER);

    VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER*) m_mt.Format();

    if (pRequest->cBuffers == 0)
    {
        pRequest->cBuffers = 2;
    }
    pRequest->cbBuffer = pvi->bmiHeader.biSizeImage;

    ALLOCATOR_PROPERTIES Actual;
    hr = pAlloc->SetProperties(pRequest, &Actual);
    if (FAILED(hr)) 
    {
        return hr;
    }

    if (Actual.cbBuffer < pRequest->cbBuffer) 
    {
        return E_FAIL;
    }

    return S_OK;
}



HRESULT DVRPin::FillBuffer(IMediaSample *pSample)
{

    if(!streamCompleted) 
    {
        CAutoLock cAutoLock(m_pLock);
        HRESULT hr;

        BYTE* pData = NULL;


        hr = pSample->GetPointer(&pData);
        if(FAILED(hr))
        {
            pSample->Release();
            return hr;
        }

        if(IncomingFramesQueue.Size() <= 0) {
            return S_OK;
        }

        vector<BYTE> data = IncomingFramesQueue.Dequeue();
        int dataSize = (int)data.size();

        if(dataSize <= 0 || dataSize > 1000000) 
        {
            return S_OK;
        }

        memcpy(pData, &data[0], dataSize);

        hr = pSample->SetActualDataLength(dataSize);
        if(FAILED(hr))
        {
            pSample->Release();
            return hr;
        }



        hr = pSample->SetSyncPoint(TRUE);
        if(FAILED(hr))
        {
            pSample->Release();
            return hr;
        }

        pSample->Release();

    }
    return S_OK;
}

HRESULT DVRPin::DoBufferProcessingLoop() {


    Command com;
    REFERENCE_TIME rtNow = 0L;
    REFERENCE_TIME rtAdvise = 0L;

    OnThreadStartPlay();

    do {
        while (!streamCompleted && !CheckRequest(&com)) {
            IncomingFramesQueue.WaitUntilHaveElements();

            IMediaSample *pSample;

            HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,FALSE);
            if (FAILED(hr)) {
                continue;   // go round again. Perhaps the error will go away
                // or the allocator is decommited & we will be asked to
                // exit soon.
            }


            hr = FillBuffer(pSample);


            if (hr == S_OK) {
                Deliver(pSample);
            } else if (hr == S_FALSE) {
                pSample->Release();
                DeliverEndOfStream();
                return S_FALSE;
            } else {

                // Log Error
            }

            pSample->Release();
        }

        if (com == CMD_RUN || com == CMD_PAUSE)
            com = GetRequest(); // throw command away
        else if (com != CMD_STOP) 
        {

            // Log Error

        }
    } while (!streamCompleted && com != CMD_STOP);

    return S_OK;
}

void DVRPin::SetDvrIp( char* dvrIp )
{
    this->dvrIp = dvrIp;
}

void DVRPin::SetDvrPort( int dvrPort )
{
       this->dvrPort = dvrPort;
}

void DVRPin::SetDvrUserName( char * userName )
{
       this->userName = userName;
}

void DVRPin::SetDvrPassword( char* password )
{
        this->password = password;
}

void DVRPin::SetStartTime( int startTime )
{
    this->startTime = startTime;
}

void DVRPin::SetMilliSecond( int milliSecond )
{
    this->milliSecond = milliSecond;
}

void DVRPin::SetSize(int width, int height) {
    m_bmpInfo.biWidth = width;
    m_bmpInfo.biHeight = height;
    m_bmpInfo.biSizeImage = GetBitmapSize(&m_bmpInfo);
}


char* DVRPin::GetDvrIp()
{
    return dvrIp;
}

int DVRPin::GetDvrPort()
{
    return dvrPort;
}

char* DVRPin::GetDvrUserName()
{
    return userName;

}

char* DVRPin::GetDvrPassword()
{
    return password;
}

int DVRPin::GetStartTime()
{
    return startTime;
}

int DVRPin::GetMilliSecond()
{
    return milliSecond;
}

void DVRPin::SetChannelNumber( int channelNumber )
{
    this->channelNumber = channelNumber;
}

int DVRPin::GetChannelNumber()
{
    return channelNumber;
}



/*
 * ****************************
 * DVRSourceFilter Class
 * ***************************
 */


DVRSourceFilter::DVRSourceFilter(IUnknown *pUnk, HRESULT *phr)
: CSource(NAME("DVRSourceBitmap"), pUnk, CLSID_DVRSourceFilter)
{


    // The pin magically adds itself to our pin array.
    m_pPin = new DVRPin(phr, this);

    // Just for test at graph studio
    SetDVRLiveParameters("192.168.3.151", 7000, "admin", "000000", 3, 352, 288);

    if (phr)
    {
        if (m_pPin == NULL)
            *phr = E_OUTOFMEMORY;
        else
            *phr = S_OK;
    }
}

DVRSourceFilter::~DVRSourceFilter()
{
    delete m_pPin;
}

CUnknown * WINAPI DVRSourceFilter::CreateInstance(IUnknown *pUnk, HRESULT *phr)
{
    DVRSourceFilter *pNewFilter = new DVRSourceFilter(pUnk, phr);

    if (phr)
    {
        if (pNewFilter == NULL) 
            *phr = E_OUTOFMEMORY;
        else
            *phr = S_OK;
    }

    return pNewFilter;
}

STDMETHODIMP DVRSourceFilter::NonDelegatingQueryInterface( REFIID riid, void **ppv )
{
    return CSource::NonDelegatingQueryInterface(riid, ppv);
}

void DVRSourceFilter::SetDVRLiveParameters( char* dvrIP, int dvrPort, char* userName, char* password, int channelNumber, int width, int height )
{
    m_pPin->SetDvrIp(dvrIP);
    m_pPin->SetDvrPort(dvrPort);
    m_pPin->SetDvrUserName(userName);
    m_pPin->SetDvrPassword(password);
    m_pPin->SetChannelNumber(channelNumber);
    m_pPin->SetStartTime(-1);// Live Stream
    m_pPin->SetMilliSecond(0);
    m_pPin->SetSize(width, height); 
}
int DVRPin::OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle)
{

    return 1; // for not to end call back
}


HRESULT DVRPin::FillBuffer(IMediaSample *pSample)
{
     pSample->Release();
     return S_OK;
}
streamReader->m_RGB24Buffer = (BYTE*)malloc(streamReader->m_RGB24BufferSize);