2012-05-18 4 views
4

Я использую API сторонних разработчиков, которые я получаю потоки из функции обратного вызоваИспользование функции обратного вызова в DirectShow фильтр причиной утечки памяти

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 фильтр, он Устранение утечек памяти. [Память увеличивается регулярно]

Что может быть причиной этого? И как я могу это исправить? Есть идеи?

UPDATE: функция

В основном

  • Я получаю потоки на "неподписанный __stdcall DVRStreamThread (LPVOID pvParam)", который называют: Моей структуру DirectShow фильтра

    Что делать назад OnNewImage

  • Затем я вставляю кадры в свою очередь внутри tha t callback [OnNewImage]
  • Наконец, в FillBuffer я использую кадры из очереди.

Это поток h264. Я могу в состоянии установить простой график, как этот

MySourceFilter ---> H264 Decoder ---> Видео Renderer

Вот мой 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 фильтр простой [понять источник утечки памяти], просто реализовать OnNewImage функции и FillBufferFunction как «манекен», но по-прежнему имеет место утечка памяти:

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; 
} 
+0

ошибка в стороннем API? – wimh

+0

Ну, Wimmel ... Он не генерирует утечку «памяти» в простом консольном приложении ... или winform-приложении ... Поэтому я не могу винить сторонний API. – Novalis

+0

Не просачивается в этот простой вариант использования и может протекать при использовании в противном случае. Если что-то просачивается, и вам не удалось получить детали, чтобы заподозрить определенные компоненты, вам нужно исследовать его глубже и изолировать утечку. –

ответ

1

Первое, что я вижу, является то, что ваши деструкторы не являются виртуальными. Это может быть причиной утечек, когда освобождение не происходит, когда используется наследование. См. related article о необходимости виртуальных деструкторов.

+0

Ну, хорошая точка для общего C++. Но это не так для моего случая: когда я комментирую функцию третьей части API DownloadStream, у меня нет проблем с памятью. Это не связано с виртуальным: я делаю деструкторы виртуальными, но ничего не изменилось. – Novalis

+0

Вот почему я сказал, что это «может быть причиной ..», а на C++ это одна из основных причин того, что объекты не выпущены. – mox

+0

Другая причина может заключаться в том, что объект DirectX COM копируется (таким образом, AddRef()), и, таким образом, RefCounting увеличивается. Это может быть причиной того, что ваши интерфейсы не освобождены. Вы можете попытаться вызвать Release() на интерфейсе в вашей очереди. – mox

2

В DVRStreamThread, у вас есть:

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

Но я не вижу, соответствующий вызов free() в любом месте. Когда ваш объект DVRPin будет удален, вам придется явно освободить данные, на которые указывает его член m_RGB24Buffer.

+0

Ну, это разовое «распределение». Я не беспокоюсь о Это. Проблема заключается в том, что «потребление памяти увеличивается« по времени, пока работает фильтр, который не может быть по этой строке кода ». – Novalis

+0

Одноразовое распределение со временем не будет расти, это правда. Но я просто заметил в вашем коротком версию 'FillBuffer', которую вы называете' pSample-> Release() ', которая также вызывается в вашей длинной версии.' FillBuffer' не должен выпускать 'IMediaSample'.' DoBufferProcessingLoop' понадобится после 'FillBuffer' возвращается, чтобы передать его в 'Deliver'. Ваш' DoBufferProcessingLoop' корректно выпускает его после того, как 'Deliver' возвращает.Таким образом, вы выпускаете его дважды. Возможно, это запутывает' IMemAllocator' управление пулом буферов. Возьмите ' pSample-> Release() 'из' FillBuffer' и посмотреть, помогает ли это. –

+0

Я удалил pSample-> Release(), просто вернул S_OK из FillBuffer, а memeory все еще увеличивается. – Novalis

Смежные вопросы