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