//==========================================================================;
//
// 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;
};