I use a third-party API that I get from the callback function
int OnNewImage(BYTE *pData, int nLen)
When I run a simple C ++ trial program from the console
int continue = 1; int OnNewImage(BYTE *pData, int nLen) { std::cout << "On new image is called" << std::endl; return continue; } int main() {
I have no memory leaks. [memory does not increase]
But when I use this callback function in a direct filter, it causes memory leaks. [regular increase in memory]
What could be the reason for this? And how can I fix this? Any ideas?
UPDATE: Structure of my DirectShow Filter
What am I doing:
Primarily
- I get threads in "unsigned __stdcall DVRStreamThread (LPVOID pvParam)" that return OnNewImage
- Then I insert the frames in turn inside this callback [OnNewImage]
- Finally, in FillBuffer, I use frames from the queue.
This is a h264 stream. I can set a simple graph as follows
MySourceFilter ---> H264 Decoder ---> Video Renderer
Here is my FilterSourceCode:
Well, I have a simple queue into which I insert incoming frames and then consume: SynchronizedQueue
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; };
Then my filter:
DvrSourceFilter [header]
#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 [implementation]
#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); }
...
To make Directshow Filter simple [to understand the source of a memory leak], simply implement the OnNewImage and FillBufferFunction functions as "dummy", but still have a memory leak:
int DVRPin::OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle) { return 1;