/***************************************************
This is the main Grabber code. It builds the Direct Show graphs
and then inserts SampleGrabbers right before the renderers. It
then changes all of the renderers to NullRenderers. This code
supports any number of audio or video streams. Raw midi streams
are not supported -- I didn't think I should bother.
This code was intended to be used inside of a matlab interface,
but can be used as a generic grabber class for anyone who needs
one.
Written by Micah Richert.
07/14/2005
Modified 09/21/2005, fixed a bug that when no frames are specified it wouldn't capture all frames
**************************************************/
#ifdef MATLAB_MEX_FILE
#include "mex.h"
#endif
#include "atlbase.h"
#include "DDGrab.h"
// unfortunately to detect memory leaks, we have to put code in each .cpp file
#if defined(_DEBUG) && defined(_MSC_VER)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
#include <math.h>
#include <assert.h>
GUID NULLGUID = {0};
CRITICAL_SECTION CriticalSection;
CSampleGrabberCB::CSampleGrabberCB()
{
pbFormat = NULL;
frameNr = 0;
disabled = false;
done = false;
bytesPerWORD = 0;
rate = 0;
startTime = 0;
stopTime = 0;
isAudio = false;
}
CSampleGrabberCB::~CSampleGrabberCB()
{
if (pbFormat) free(pbFormat);
_RPT1(_CRT_WARN,"Freeing %d frames\n",frames.size());
for (int f=0;f<frames.size();f++) if (frames.at(f))
{
delete frames.at(f);
}
frames.clear();
frameBytes.clear();
frameNrs.clear();
frameTimes.clear();
Release();
}
// Fake out any COM QI'ing
//
STDMETHODIMP CSampleGrabberCB::QueryInterface(REFIID riid, void ** ppv)
{
if (!ppv) return E_POINTER;
if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown )
{
*ppv = (void *) static_cast<ISampleGrabberCB*> ( this );
return NOERROR;
}
return E_NOINTERFACE;
}
// The sample grabber is calling us back on its deliver thread.
// This is NOT the main app thread!
//
STDMETHODIMP CSampleGrabberCB::BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
{
if (disabled) return 0;
if (!pBuffer) return E_POINTER;
frameNr++;
int nrWORDs = lBufferSize/bytesPerWORD;
int offset=0, len=0;
_RPT2(_CRT_WARN,"dblSampleTime: %f frameNr: %d\n",(float)dblSampleTime,frameNr);
if (frameNrs.size() > 0)
{
//frames are being specified, so we must be a videoCB...
// check to see if the frame is in our list
bool foundNr = false;
int lastFrameNr = 0;
for (int i=0;i<frameNrs.size();i++)
{
if (frameNrs.at(i) == frameNr) foundNr = true;
if (frameNrs.at(i) > lastFrameNr) lastFrameNr = frameNrs.at(i);
}
_RPT2(_CRT_WARN,"frameNr: %d lastFrameNr: %d\n",frameNr,lastFrameNr);
if (frameNr > lastFrameNr)
{
done = true;
return 0;
}
if (foundNr) len = lBufferSize; // since this has to be a video frame, we just capture everything
} else {
// either no frames are specified (capture all), or we have time specified
if (stopTime)
{
if (isAudio)
{
// time is being used...
if (dblSampleTime>=startTime)
{
// if we've reached the start...
offset = min(max(0,((int)((startTime-dblSampleTime)*rate))*bytesPerWORD),lBufferSize+1);
len = min(lBufferSize,((int)((stopTime-dblSampleTime)*rate))*bytesPerWORD);
// if we have gone past our stop time...
_RPT2(_CRT_WARN," startTime-dblSampleTime: %d stopTime-dblSampleTime: %d\n",((int)((startTime-dblSampleTime)*rate))*bytesPerWORD,((int)((stopTime-dblSampleTime)*rate))*bytesPerWORD);
_RPT4(_CRT_WARN,"startTime: %f stopTime: %f dblSampleTime: %f offset: %d",startTime,stopTime,dblSampleTime,offset);
_RPT1(_CRT_WARN," len: %d \n",len);
if (len < 0)
{
_RPT0(_CRT_WARN,"We should stop now!!! \n");
done = true;
return 0;
}
}
} else {
done = stopTime <= dblSampleTime;
len = (startTime <= dblSampleTime)?lBufferSize:0;
if (done) return 0;
}
} else {
// capture everything... video or audio
len = lBufferSize;
_RPT0(_CRT_WARN,"capture everything\n");
}
}
if (len)
{
// _ASSERTE( _CrtCheckMemory( ) );
EnterCriticalSection(&CriticalSection);
BYTE* tmp = (BYTE*)malloc(len);
if (!tmp) return E_OUTOFMEMORY;
memcpy(tmp,pBuffer+offset,len);
frames.add(tmp);
frameBytes.add(len);
frameTimes.add(dblSampleTime);
LeaveCriticalSection(&CriticalSection);
}
return 0;
}
DDGrabber::DDGrabber()
{
stopForced = false;
tryseeking = true;
_CrtSetDbgFlag( _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) | _CRTDBG_CHECK_ALWAYS_DF);// | _CRTDBG_DELAY_FREE_MEM_DF);
}
void DDGrabber::cleanUp()
{
IMediaControl* pMediaControl;
IEnumFilters* filterList;
IBaseFilter* filt;
ULONG tmp;
int i;
for (i=0; i<VideoCBs.size(); i++) delete VideoCBs.at(i);
for (i=0; i<AudioCBs.size(); i++) delete AudioCBs.at(i);
VideoCBs.clear();
AudioCBs.clear();
// make sure the graph is stopped
pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Stop();
pMediaControl->Release();
_RPT0(_CRT_WARN,"Releasing... \n");
if (SUCCEEDED(pGraphBuilder->EnumFilters(&filterList)))
{
while (filterList->Next(1, &filt, NULL) == S_OK)
{
#ifdef _DEBUG
FILTER_INFO info;
filt->QueryFilterInfo(&info);
char str[100];
WideCharToMultiByte( CP_ACP, 0, info.achName, -1, str, 100, NULL, NULL );
_RPT1(_CRT_WARN,"Releasing: %s\n",str);
#endif
pGraphBuilder->RemoveFilter(filt);
filterList->Reset();
filt->Release();
}
filterList->Release();
}
pGraphBuilder->Release();
pGraphBuilder = NULL;
#ifdef MATLAB_MEX_FILE
if (matlabCommand) free(matlabCommand);
matlabCommand = NULL;
#endif
_CrtDumpMemoryLeaks();
}
void DDGrabber::MyFreeMediaType(AM_MEDIA_TYPE& mt)
{
if (mt.cbFormat != 0)
{
CoTaskMemFree((PVOID)mt.pbFormat);
mt.cbFormat = 0;
mt.pbFormat = NULL;
}
if (mt.pUnk != NULL)
{
// Unecessary because pUnk should not be used, but safest.
mt.pUnk->Release();
mt.pUnk = NULL;
}
}
PIN_INFO DDGrabber::getPinInfo(IPin* pin)
{
PIN_INFO info = {0};
if (pin)
{
if (SUCCEEDED(pin->QueryPinInfo(&info)))
{
info.pFilter->Release();
}
}
return info;
}
IPin* DDGrabber::getInputPin(IBaseFilter* filt)
{
IPin* pin = NULL;
IEnumPins* pinList;
ULONG tmp;
if (!filt) return NULL;
//get the input
if (SUCCEEDED(filt->EnumPins(&pinList)))
{
pinList->Reset();
while (pinList->Next(1, &pin, &tmp) == S_OK && getPinInfo(pin).dir != PINDIR_INPUT);
pinList->Release();
if (getPinInfo(pin).dir != PINDIR_INPUT) return NULL;
}
return pin;
}
IPin* DDGrabber::getOutputPin(IBaseFilter* filt)
{
IPin* pin = NULL;
IEnumPins* pinList;
ULONG tmp;
if (!filt) return NULL;
//get the output
if (SUCCEEDED(filt->EnumPins(&pinList)))
{
pinList->Reset();
while (pinList->Next(1, &pin, &tmp) == S_OK && getPinInfo(pin).dir != PINDIR_OUTPUT);
pinList->Release();
if (getPinInfo(pin).dir != PINDIR_OUTPUT) return NULL;
}
return pin;
}
bool DDGrabber::isRenderer(IBaseFilter* filt)
{
if (!filt) return false;
IEnumPins* pinList;
int nrOutput = 0;
int nrInput = 0;
IPin* pin = NULL;
ULONG tmp;
if (FAILED(filt->EnumPins(&pinList))) return false;
pinList->Reset();
while (pinList->Next(1, &pin, &tmp) == S_OK)
{
if (getPinInfo(pin).dir == PINDIR_OUTPUT) nrOutput++;
else nrInput++;
pin->Release();
}
pinList->Release();
#ifdef _DEBUG
FILTER_INFO info;
filt->QueryFilterInfo(&info);
char str[100];
WideCharToMultiByte( CP_ACP, 0, info.achName, -1, str, 100, NULL, NULL );
_RPT0(_CRT_WARN,str);
_RPT2(_CRT_WARN," %d %d\n", nrOutput, nrInput);
#