//==========================================================================; // // 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 - 1998 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 BOOL m_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 BOOL m_fSyncPoint; // for setting sample flags BOOL m_fDiscontinuity;// for setting sample flags friend class CVideoBufferList; }; private: CCritSec m_ListCrit; // Serialize lists CAMEvent m_evList; // New element on list CGenericList m_lFilled; CGenericList m_lFree; CVidCap *m_pFilter; BOOL m_FirstBuffer; DWORD m_dwMicroSecPerFrame; CRefTime m_rtStartTime; BOOL m_fLastSampleDiscarded; // the Time and MediaTime of the last frame delivered REFERENCE_TIME m_rtLastStartTime; LONGLONG m_llLastFrame; // Did somebody do Run->Pause->Run to the graph? BOOL m_fReRun; // frame offset to add to the MediaTime - explained in the code LONGLONG m_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 BOOL m_fRunning; // am I running? HWND m_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; HANDLE m_hThread; DWORD m_tid; HANDLE m_hEventRun; HANDLE m_hEventActiveChanged; CAMEvent m_EventAdvise; DWORD m_dwAdvise; BOOL m_fCapturing; // is the streaming pin active? BOOL m_fLastSampleDiscarded; // for IAMStreamControl HWND m_hwndCap; // !!! these have to be public so the video callback can get at them public: LPVOID m_lpFrame; BOOL m_fRunning; // am I streaming? int m_iFrameSize; BOOL m_fFrameValid; HANDLE m_hEventFrameValid; // capGrabFrame callback static LRESULT CALLBACK VideoCallback(HWND hwnd, LPVIDEOHDR lpVHdr); friend class CVidCap; friend class CVidStream; friend class CVideoBufferList; };