VIDCAP.H

//==========================================================================; 
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;

//
// Video capture stream source filter
//

// - Uses the CSource/CSourceStream classes to create a video capture filter.
// - Uses the AVICap capXXX macros to capture from the first driver it finds.

// CLSID_VidCap
// {fd501040-8ebe-11ce-8183-00aa00577da1}
DEFINE_GUID(CLSID_VidCap,
0xfd501040, 0x8ebe, 0x11ce, 0x81, 0x83, 0x00, 0xaa, 0x00, 0x57, 0x7d, 0xa1);


// align a dw to an arbitrary size
#define ALIGNUP(dw,align) ((long)(((long)(dw)+(align)-1) / (align)) * (align))

// forward declarations
class CVidOverlay;
class CVidPreview;

// The buffer sizes required for driver name and version strings
const int giDriverNameStrLen = 80;
const int giDriverVerStrLen = 40;

class CVidStream; // manages the output stream & pin
class CVideoBufferList; // A place to store filled buffers before we can send
// them downstream


//
// CVidCap
//
// The VidCap filter object. Provides the IBaseFilter/IMediaFilter interfaces.
// IPersistPropertyBag must be supported to find out what device to use
// (this filter works with many video cards)
// CPersistStream must be supported so a .GRF file with this filter in it
// can be re-created with the right device
// IAMVfwCaptureDialogs is supported to bring up the VfW capture driver
// dialog boxes. A filter that does not work with the VfW capture drivers
// will NOT support this interface
class CVidCap : public CSource, public IPersistPropertyBag,
public IAMVfwCaptureDialogs, public CPersistStream
{

public:

// Construct a VidCap filter
static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);

CCritSec m_cStateLock; // Lock this when a function accesses
// the filter state.
// Generally _all_ functions, since access to
// this filter will be by multiple threads.

DECLARE_IUNKNOWN

// reveals our interfaces
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

int GetPinCount();
CBasePin * GetPin(int ix);

// overide the Run & Pause methods so that I can notify the pin
// of the state it should move to - also needed for CBaseStreamControl
STDMETHODIMP Run(REFERENCE_TIME tStart);
STDMETHODIMP Pause(void);
STDMETHODIMP Stop(void);

// live source - override GetState to return VFW_S_CANT_CUE when pausing
// since we won't be sending any data when paused
STDMETHODIMP GetState(DWORD dwMSecs, FILTER_STATE *State);

// IAMVfwCaptureDialogs stuff
STDMETHODIMP HasDialog(int iDialog);
STDMETHODIMP ShowDialog(int iDialog, HWND hwnd);
STDMETHODIMP SendDriverMessage(int iDialog, int uMsg, long dw1, long dw2);

// for IAMStreamControl
STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
STDMETHODIMP JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName);

// IPersistPropertyBag methods
STDMETHODIMP InitNew();
STDMETHODIMP Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog);
STDMETHODIMP Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties);

// CPersistStream
HRESULT WriteToStream(IStream *pStream);
HRESULT ReadFromStream(IStream *pStream);
int SizeMax();
STDMETHODIMP GetClassID(CLSID *pClsid);

STDMETHODIMP FindPin(
LPCWSTR Id,
IPin ** ppPin
);
private:

// During construction we create the single CVidStream object that provides
// the capture output pin.
CVidCap(TCHAR *, LPUNKNOWN, HRESULT *);
~CVidCap();
void CreatePins(HRESULT *phr);

CVidOverlay *CreateOverlayPin(HRESULT *phr);
CVidPreview *CreatePreviewPin(HRESULT *phr);

CVidStream *m_pCapturePin;// our capture pin
CVidOverlay *m_pOverlayPin;// our h/w overlay preview pin
CVidPreview *m_pPreviewPin;// our non h/w overlay preview pin

int m_iVideoId;// which device to use

friend class CVidStream;
friend class CVideoBufferList;
friend class CVidOverlay;
friend class CVidPreview;
friend class CVidOverlayNotify;
};


//
// CVidStream
//
// Manages the output pin and the video capture device.
// Video capture pins are supposed to support all of these interfaces
class CVidStream : public CSourceStream, public CBaseStreamControl,
public IAMVideoCompression, public IAMStreamConfig,
public IAMDroppedFrames, public IAMBufferNegotiation,
public IKsPropertySet
{

public:

CVidStream( TCHAR *pObjectName
, HRESULT *phr
, CVidCap *pParentFilter
, unsigned int uiDriverIndex
, LPCWSTR pPinName
);

~CVidStream();

//
// --- CSourceStream implementation ---
//
public:

HRESULT DecideBufferSize(IMemAllocator *pAlloc,
ALLOCATOR_PROPERTIES *pProperties);

// helper reconnect function. We can reconnect everything, or just
// the preview pin because the capture pin changed
void Reconnect(BOOL fCapturePinToo);

// IKsPropertySet stuff - to tell the world we are a "capture" type pin
STDMETHODIMP Set(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData);
STDMETHODIMP Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData,
DWORD *pcbReturned);
STDMETHODIMP QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
DWORD *pTypeSupport);

// IAMStreamConfig stuff
STDMETHODIMP SetFormat(AM_MEDIA_TYPE *pmt);
STDMETHODIMP GetFormat(AM_MEDIA_TYPE **ppmt);
STDMETHODIMP GetNumberOfCapabilities(int *piCount, int *piSize);
STDMETHODIMP GetStreamCaps(int i, AM_MEDIA_TYPE **ppmt, LPBYTE pSCC);

/* IAMVideoCompression methods */
STDMETHODIMP put_KeyFrameRate(long KeyFrameRate) {return E_NOTIMPL;};
STDMETHODIMP get_KeyFrameRate(long *pKeyFrameRate) {return E_NOTIMPL;};
STDMETHODIMP put_WindowSize(DWORDLONG WindowSize) {return E_NOTIMPL;};
STDMETHODIMP get_WindowSize(DWORDLONG *pWindowSize) {return E_NOTIMPL;};
STDMETHODIMP put_PFramesPerKeyFrame(long PFramesPerKeyFrame)
{return E_NOTIMPL;};
STDMETHODIMP get_PFramesPerKeyFrame(long *pPFramesPerKeyFrame)
{return E_NOTIMPL;};
STDMETHODIMP put_Quality(double Quality) {return E_NOTIMPL;};
STDMETHODIMP get_Quality(double *pQuality) {return E_NOTIMPL;};
STDMETHODIMP OverrideKeyFrame(long FrameNumber) {return E_NOTIMPL;};
STDMETHODIMP OverrideFrameSize(long FrameNumber, long Size)
{return E_NOTIMPL;};
STDMETHODIMP GetInfo(LPWSTR pstrVersion,
int *pcbVersion,
LPWSTR pstrDescription,
int *pcbDescription,
long *pDefaultKeyFrameRate,
long *pDefaultPFramesPerKey,
double *pDefaultQuality,
long *pCapabilities);

/* IAMBufferNegotiation methods */
STDMETHODIMP SuggestAllocatorProperties(const ALLOCATOR_PROPERTIES *pprop);
STDMETHODIMP GetAllocatorProperties(ALLOCATOR_PROPERTIES *pprop);


/* IAMDroppedFrames methods */
STDMETHODIMP GetNumDropped(long *plDropped);
STDMETHODIMP GetNumNotDropped(long *plNotDropped);
STDMETHODIMP GetDroppedInfo(long lSize, long *plArray,
long *plNumCopied);
STDMETHODIMP GetAverageFrameSize(long *plAverageSize);

//
// --- Worker Thread fn's ---
//
HRESULT OnThreadCreate(void);
HRESULT OnThreadDestroy(void);

HRESULT DoBufferProcessingLoop(void);
HRESULT FillBuffer(IMediaSample *pSamp);

HRESULT Inactive(void);

// Override to handle quality messages
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);


STDMETHODIMP QueryId(
LPWSTR * Id
);

public:

DECLARE_IUNKNOWN;

// reveals our interfaces
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

private: // State shared between worker & client
CCritSec m_cSharedState; // Lock this to access this state,
// shared with the worker thread

unsigned int m_uiDriverIndex; // the device to open when active
HWND m_hwCapCapturing; // The AVI capture window,
// used whilst active
CVideoBufferList *m_plFilled; // The buffers AVICap has filled,
// but that haven't been sent yet.
// NB This has its own critical
// section, so m_cSharedState does
// not need to be locked for access

DWORD m_dwMicroSecPerFrame; // The current number of
// microseconds between each frame
ALLOCATOR_PROPERTIES m_propSuggested; // IAMBufferNegotiation
ALLOCATOR_PROPERTIES m_propActual; // what the allocator is using
BOOLm_fSetFormatCalled; // restricted to using this?

private: // thread state. No peeking/writing by anyone else!

enum ThreadState { Stopped, // will exit soon, or just started
Paused, // generate a poster frame if entered
// from stop
Running // streaming data downstream
};
ThreadState m_ThreadState;


private: // Capture Support

HRESULT GetMediaType(CMediaType *pmt);
HRESULT CheckMediaType(const CMediaType *pmt);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT BreakConnect();

long GetSampleSize(LPBITMAPINFOHEADER pbmi); // Calculate the sample
// size needed.

HWND CreateCaptureWindow(long lBufferCount);// Create the AVICap window
// invisibly. Balance with
// calls to DestroyCaptureWindow.
// This is the function that grabs
// resources. The filter holds no
// resources (the capture driver)
// until it is called
BOOL DestroyCaptureWindow(HWND hwnd); // Destroy an AVICap window,
// release resources

// put the buffer we are given onto the filled list for delivery downstream
static LRESULT CALLBACK VideoCallback(HWND hwnd, LPVIDEOHDR lpVHdr);


private:

WCHAR m_szName[giDriverNameStrLen]; // The driver's name
WCHAR m_szVersion[giDriverVerStrLen]; // The driver's version

BOOL m_SupportsVideoSourceDialog; // The dialogs this driver supports
BOOL m_SupportsVideoDisplayDialog; //
BOOL m_SupportsVideoFormatDialog; //
BOOL m_HasOverlay; // device does h/w overlay
#if 0
BOOL m_UsesPalettes;// does the driver use a palette?
BOOL m_SuppliesPalettes;// can the driver give us a palette?
#endif

// for IAMDroppedFrames
unsigned int m_uiFramesCaptured;
LONGLONG m_llTotalFrameSize;
unsigned int m_uiFramesSkipped;
unsigned int m_uiFramesDelivered;

friend class CVidCap;
friend class CVideoBufferList;
friend class CVidOverlay;
friend class CVidPreview;
friend class CVidOverlayNotify;
};


//
// CVideoBufferList
//
// This list is a place to store a buffer that AVIcap gives to us, before we can
// pass them on to the next filter downstream.
// Constructs a list of free buffers on construction.
class CVideoBufferList {

public:

CVideoBufferList( int iBufferSize
, DWORD dwMicroSecPerFrame
, CVidCap *pFilter
, int iBuffers
);
~CVideoBufferList();

HRESULT Add(LPVIDEOHDR lpVHdr);
HRESULT RemoveHeadIntoSample(IMediaSample *pSample);

HANDLE GetWaitHandle() {
return (HANDLE)m_evList;
}

// *
// * CBuffer
// *

// This is a class to store the data given to us by AVICap.
// This must copy the data it is given, as we don't know what AVICap
// does to its buffers.
class CBuffer {

public:

CBuffer(int iBufferSize); // prepares an iBufferSize'd memory buffer
~CBuffer(); // frees the data buffer

BYTE *GetPointer() const {return m_pData;}
void CopyBuffer(LPVIDEOHDR lpVHdr, CRefTime& rt, LONGLONG llTime);
int GetSize() const {return m_iCaptureDataLength;}
// The time for this sample to be presented
CRefTime GetCaptureTime() const {return m_rt;}
// The frame number
LONGLONG GetCaptureFrame() const {return m_llFrame;}

private:

BYTE *m_pData; // The data stored in this buffer
int m_iDataLength; // m_pData's length
int m_iCaptureDataLength; // length returned from device

CRefTime m_rt; // The stream time for this sample.
LONGLONG m_llFrame; // The frame number
BOOLm_fSyncPoint;// for setting sample flags
BOOLm_fDiscontinuity;// for setting sample flags

friend class CVideoBufferList;
};

private:

CCritSec m_ListCrit; // Serialize lists
CAMEvent m_evList; // New element on list
CGenericList<CBuffer> m_lFilled;
CGenericList<CBuffer> m_lFree;

CVidCap *m_pFilter;

BOOL m_FirstBuffer;

DWORD m_dwMicroSecPerFrame;
CRefTime m_rtStartTime;
BOOLm_fLastSampleDiscarded;

// the Time and MediaTime of the last frame delivered
REFERENCE_TIMEm_rtLastStartTime;
LONGLONGm_llLastFrame;

// Did somebody do Run->Pause->Run to the graph?
BOOLm_fReRun;

// frame offset to add to the MediaTime - explained in the code
LONGLONGm_llFrameOffset;

int m_iPreviewCount;// send a preview frame every 30th frame

friend class CVidStream;
};


// CVidOverlayNotify
// where the video renderer informs us of window moves/clips so we can fix
// the overlay
//
class CVidOverlayNotify : public CUnknown, public IOverlayNotify
{
public:
/* Constructor and destructor */
CVidOverlayNotify(TCHAR *pName,
CVidCap *pFilter,
LPUNKNOWN pUnk,
HRESULT *phr);
~CVidOverlayNotify();

/* Unknown methods */

DECLARE_IUNKNOWN

STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) NonDelegatingRelease();
STDMETHODIMP_(ULONG) NonDelegatingAddRef();

/* IOverlayNotify methods */

STDMETHODIMP OnColorKeyChange(
const COLORKEY *pColorKey); // Defines new colour key

STDMETHODIMP OnClipChange(
const RECT *pSourceRect, // Area of video to play
const RECT *pDestinationRect, // Area of video to play
const RGNDATA *pRegionData); // Header describing clipping

STDMETHODIMP OnPaletteChange(
DWORD dwColors, // Number of colours present
const PALETTEENTRY *pPalette); // Array of palette colours

STDMETHODIMP OnPositionChange(
const RECT *pSourceRect, // Area of video to play with
const RECT *pDestinationRect); // Area video goes

private:
CVidCap *m_pFilter;
RECT m_rcClient;// how big the preview window is

} ;


// CVidOverlay
// If the capture card supports hardware overlay, this kind of pin will
// be made for a preview pin and connect to the renderer using IOverlay
// Hardware overlay preview is free... there's no point ever turning it
// off, so we don't support CBaseStreamControl (IAMStreamControl)
//
class CVidOverlay : public CBaseOutputPin, public IKsPropertySet
{
public:
CVidOverlay(
TCHAR *pObjectName,
CVidCap *pCapture,
HRESULT * phr,
LPCWSTR pName);

virtual ~CVidOverlay();

DECLARE_IUNKNOWN

// reveals our interfaces
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

// IKsPropertySet stuff - to tell the world we are a "preview" type pin
STDMETHODIMP Set(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData);
STDMETHODIMP Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData,
DWORD *pcbReturned);
STDMETHODIMP QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
DWORD *pTypeSupport);

HRESULT GetMediaType(int iPosition, CMediaType* pt);

// check if the pin can support this specific proposed type&format
HRESULT CheckMediaType(const CMediaType*);

// override this to not do anything with allocators
HRESULT DecideAllocator(IMemInputPin *pPin,
IMemAllocator **ppAlloc);

// override these to use IOverlay, not IMemInputPin
STDMETHODIMP Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt);
HRESULT BreakConnect();
HRESULT CheckConnect(IPin *pPin);

HRESULT Active();// Stop-->Pause
HRESULT Inactive();// Pause-->Stop
HRESULT ActiveRun(REFERENCE_TIME tStart);// Pause-->Run
HRESULT ActivePause();// Run-->Pause

// say how big our buffers should be and how many we want
HRESULT DecideBufferSize(IMemAllocator * pAllocator,
ALLOCATOR_PROPERTIES *pProperties)
{
return NOERROR;
};

private:
HWND CreateCaptureWindow(HWND hwnd);
BOOL DestroyCaptureWindow(HWND hwnd);

CVidCap * m_pCap; // parent
IOverlay * m_pOverlay; // Overlay window on output pin
CVidOverlayNotify m_OverlayNotify; // Notify object
BOOL m_bAdvise; // Advise id
BOOLm_fRunning; // am I running?
HWNDm_hwndCap; // the capture window to use

friend class CVidCap;
friend class CVidOverlayNotify;
};


// CVidPreview
// for devices that do NOT do hardware overlay, we will be nice and provide
// a preview ourselves, even though the device doesn't support it. We'll do
// it by noticing when we have free time, and copying some frames and sending
// them out the preview pin. We'll make sure we never hurt capture
// performance by doing so.
//
class CVidPreview : public CBaseOutputPin, public CBaseStreamControl,
public IKsPropertySet
{
public:
CVidPreview(
TCHAR *pObjectName,
CVidCap *pCapture,
HRESULT * phr,
LPCWSTR pName);

virtual ~CVidPreview();

DECLARE_IUNKNOWN

// override this to say what interfaces we support where
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

// IKsPropertySet stuff - to tell the world we are a "preview" type pin
STDMETHODIMP Set(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData);
STDMETHODIMP Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData,
DWORD *pcbReturned);
STDMETHODIMP QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
DWORD *pTypeSupport);

HRESULT GetMediaType(int iPosition, CMediaType* pt);

// check if the pin can support this specific proposed type&format
HRESULT CheckMediaType(const CMediaType*);

HRESULT ActiveRun(REFERENCE_TIME tStart);// Pause-->Run
HRESULT ActivePause();// Run-->Pause
HRESULT Active();// Stop-->Pause
HRESULT Inactive();// Pause-->Stop

STDMETHODIMP Notify(IBaseFilter *pFilter, Quality q);

// say how big our buffers should be and how many we want
HRESULT DecideBufferSize(IMemAllocator * pAllocator,
ALLOCATOR_PROPERTIES *pProperties);


private:
static DWORD WINAPI ThreadProcInit(void *pv);
DWORD ThreadProc();
HRESULT CapturePinActive(BOOL fActive);
HRESULT ReceivePreviewFrame(LPVOID lpFrame, int iSize);
HWND CreateCaptureWindow();
BOOL DestroyCaptureWindow(HWND hwnd);

CVidCap *m_pCap; // parent
REFERENCE_TIME m_rtRun;
HANDLEm_hThread;
DWORDm_tid;
HANDLEm_hEventRun;
HANDLEm_hEventActiveChanged;
CAMEvent m_EventAdvise;
DWORDm_dwAdvise;
BOOLm_fCapturing;// is the streaming pin active?
BOOLm_fLastSampleDiscarded;// for IAMStreamControl
HWNDm_hwndCap;

// !!! these have to be public so the video callback can get at them
public:
LPVOIDm_lpFrame;
BOOLm_fRunning; // am I streaming?
intm_iFrameSize;
BOOLm_fFrameValid;
HANDLEm_hEventFrameValid;

// capGrabFrame callback
static LRESULT CALLBACK VideoCallback(HWND hwnd, LPVIDEOHDR lpVHdr);

friend class CVidCap;
friend class CVidStream;
friend class CVideoBufferList;
};