4

我正在使用第三方 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;

}

我没有内存泄漏。[内存没有增加]

但是当我在directshow过滤器中使用这个回调函数时,它会产生内存泄漏。[内存定期增加]

什么可能导致这种情况?我该如何解决?有任何想法吗?

更新:我的 DirectShow 过滤器结构

我所做的:

基本上

  • 我在“unsigned __stdcall DVRStreamThread(LPVOID pvParam)”函数中获取流,该函数回调 OnNewImage
  • 然后我在回调中将帧插入我的队列[OnNewImage]
  • 最后在 FillBuffer 我消耗队列中的帧。

它是 h264 流。我可以像这样设置简单的图表

MySourceFilter ---> H264 解码器 ---> 视频渲染器

这是我的 FilterSourceCode:

好吧,我有一个简单的队列,我插入传入的帧然后使用: SynchronisedQueue

template <typename T>

  class SynchronisedQueue
        {
        public:

        void Enqueue(const T& data)
        {
        boost::unique_lock<boost::mutex> lock(queueMutex);
        dataQueue.push(data);
        conditionVariable.notify_one();
    } 

    T Dequeue()
    {
        boost::unique_lock<boost::mutex> lock(queueMutex);

        while (dataQueue.size()==0) 
        {
            conditionVariable.wait(lock);
        }

        T result=dataQueue.front(); dataQueue.pop();
        return result;
    } 

    int Size()
    {
        boost::unique_lock<boost::mutex> lock(queueMutex);
        int size = dataQueue.size();
        return size;
    } 

private:

    std::queue<T> dataQueue; 
    boost::mutex queueMutex; 
    boost::condition_variable conditionVariable;  

};

然后我的过滤器:

DvrSourceFilter [标题]

#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;
};

DvrSourceFilter [实现]

#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); 
}

...

为了让directshow Filter简单[了解内存泄漏源],只需将OnNewImage函数和FillBufferFunction实现为“dummy”,但仍然存在内存泄漏:

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;
}
4

2 回答 2

2

在 DVRStreamThread 中,您有:

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

但我没有看到任何匹配的呼叫free()。当您的 DVRPin 对象被删除时,您必须显式释放其 m_RGB24Buffer 成员指向的数据。

于 2012-05-31T01:46:52.547 回答
1

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

于 2012-05-25T13:22:44.747 回答