| /* Video For Windows Steering structure | 
 |  * | 
 |  * Copyright 2005 Maarten Lankhorst | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  */ | 
 |  | 
 | #define NONAMELESSSTRUCT | 
 | #define NONAMELESSUNION | 
 | #define COBJMACROS | 
 |  | 
 | #include "config.h" | 
 | #include <stdarg.h> | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "wtypes.h" | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 | #include "dshow.h" | 
 |  | 
 | #include "qcap_main.h" | 
 | #include "wine/debug.h" | 
 |  | 
 | #include "pin.h" | 
 | #include "capture.h" | 
 | #include "uuids.h" | 
 | #include "mmreg.h" | 
 | #include "vfwmsgs.h" | 
 | #include "amvideo.h" | 
 | #include "strmif.h" | 
 | #include "ddraw.h" | 
 | #include "ocidl.h" | 
 | #include "oleauto.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(qcap); | 
 |  | 
 | #define ICOM_THIS_MULTI(impl,field,iface) \ | 
 |     impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) | 
 |  | 
 | static const IBaseFilterVtbl VfwCapture_Vtbl; | 
 | static const IAMStreamConfigVtbl IAMStreamConfig_VTable; | 
 | static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable; | 
 | static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable; | 
 | static const IPinVtbl VfwPin_Vtbl; | 
 |  | 
 | static HRESULT VfwPin_Construct( IBaseFilter *, LPCRITICAL_SECTION, IPin ** ); | 
 |  | 
 | typedef struct VfwCapture | 
 | { | 
 |     const IBaseFilterVtbl * lpVtbl; | 
 |     const IAMStreamConfigVtbl * IAMStreamConfig_vtbl; | 
 |     const IAMVideoProcAmpVtbl * IAMVideoProcAmp_vtbl; | 
 |     const IPersistPropertyBagVtbl * IPersistPropertyBag_vtbl; | 
 |  | 
 |     BOOL init; | 
 |     Capture *driver_info; | 
 |     LONG refCount; | 
 |     FILTER_INFO filterInfo; | 
 |     FILTER_STATE state; | 
 |     CRITICAL_SECTION csFilter; | 
 |  | 
 |     IPin * pOutputPin; | 
 | } VfwCapture; | 
 |  | 
 | /* VfwPin implementation */ | 
 | typedef struct VfwPinImpl | 
 | { | 
 |     OutputPin pin; | 
 |     Capture *driver_info; | 
 |     const IKsPropertySetVtbl * KSP_VT; | 
 | } VfwPinImpl; | 
 |  | 
 |  | 
 | IUnknown * WINAPI QCAP_createVFWCaptureFilter(IUnknown *pUnkOuter, HRESULT *phr) | 
 | { | 
 |     VfwCapture *pVfwCapture; | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("%p - %p\n", pUnkOuter, phr); | 
 |  | 
 |     *phr = CLASS_E_NOAGGREGATION; | 
 |     if (pUnkOuter) | 
 |         return NULL; | 
 |     *phr = E_OUTOFMEMORY; | 
 |  | 
 |     pVfwCapture = CoTaskMemAlloc( sizeof(VfwCapture) ); | 
 |  | 
 |     if (!pVfwCapture) | 
 |         return NULL; | 
 |  | 
 |     pVfwCapture->lpVtbl = &VfwCapture_Vtbl; | 
 |     pVfwCapture->IAMStreamConfig_vtbl = &IAMStreamConfig_VTable; | 
 |     pVfwCapture->IAMVideoProcAmp_vtbl = &IAMVideoProcAmp_VTable; | 
 |     pVfwCapture->IPersistPropertyBag_vtbl = &IPersistPropertyBag_VTable; | 
 |     pVfwCapture->refCount = 1; | 
 |     pVfwCapture->filterInfo.achName[0] = '\0'; | 
 |     pVfwCapture->filterInfo.pGraph = NULL; | 
 |     pVfwCapture->state = State_Stopped; | 
 |     pVfwCapture->init = FALSE; | 
 |     InitializeCriticalSection(&pVfwCapture->csFilter); | 
 |     hr = VfwPin_Construct((IBaseFilter *)&pVfwCapture->lpVtbl, | 
 |                    &pVfwCapture->csFilter, &pVfwCapture->pOutputPin); | 
 |     if (!SUCCEEDED(hr)) | 
 |     { | 
 |         CoTaskMemFree(pVfwCapture); | 
 |         return NULL; | 
 |     } | 
 |     TRACE("-- created at %p\n", pVfwCapture); | 
 |  | 
 |     ObjectRefCount(TRUE); | 
 |     *phr = S_OK; | 
 |     return (IUnknown *)pVfwCapture; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwCapture_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv); | 
 |     *ppv = NULL; | 
 |  | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || | 
 |         IsEqualIID(riid, &IID_IPersist) || | 
 |         IsEqualIID(riid, &IID_IMediaFilter) || | 
 |         IsEqualIID(riid, &IID_IBaseFilter)) | 
 |     { | 
 |         *ppv = This; | 
 |     } | 
 |     else if (IsEqualIID(riid, &IID_IAMStreamConfig)) | 
 |         *ppv = &(This->IAMStreamConfig_vtbl); | 
 |     else if (IsEqualIID(riid, &IID_IAMVideoProcAmp)) | 
 |         *ppv = &(This->IAMVideoProcAmp_vtbl); | 
 |     else if (IsEqualIID(riid, &IID_IPersistPropertyBag)) | 
 |         *ppv = &(This->IPersistPropertyBag_vtbl); | 
 |  | 
 |     if (!IsEqualIID(riid, &IID_IUnknown) && | 
 |         !IsEqualIID(riid, &IID_IPersist) && | 
 |         !IsEqualIID(riid, &IID_IPersistPropertyBag) && | 
 |         !This->init) | 
 |     { | 
 |         FIXME("Capture system not initialised when looking for %s, " | 
 |               "trying it on primary device now\n", debugstr_guid(riid)); | 
 |         This->driver_info = qcap_driver_init( This->pOutputPin, 0 ); | 
 |         if (!This->driver_info) | 
 |         { | 
 |             ERR("VfwCapture initialisation failed\n"); | 
 |             return E_UNEXPECTED; | 
 |         } | 
 |         This->init = TRUE; | 
 |     } | 
 |  | 
 |     if (*ppv) | 
 |     { | 
 |         TRACE("Returning %s interface\n", debugstr_guid(riid)); | 
 |         IUnknown_AddRef((IUnknown *)(*ppv)); | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for %s!\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI VfwCapture_AddRef(IBaseFilter * iface) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |     ULONG refCount = InterlockedIncrement(&This->refCount); | 
 |  | 
 |     TRACE("%p->() New refcount: %ld\n", This, refCount); | 
 |  | 
 |     return refCount; | 
 | } | 
 |  | 
 | static ULONG WINAPI VfwCapture_Release(IBaseFilter * iface) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |     ULONG refCount = InterlockedDecrement(&This->refCount); | 
 |  | 
 |     TRACE("%p->() New refcount: %ld\n", This, refCount); | 
 |  | 
 |     if (!refCount) | 
 |     { | 
 |         IPinImpl *pin; | 
 |  | 
 |         TRACE("destroying everything\n"); | 
 |         if (This->init) | 
 |         { | 
 |             if (This->state != State_Stopped) | 
 |                 qcap_driver_stop(This->driver_info, &This->state); | 
 |             qcap_driver_destroy(This->driver_info); | 
 |         } | 
 |         pin = (IPinImpl*) This->pOutputPin; | 
 |         if (pin->pConnectedTo != NULL) | 
 |         { | 
 |             IPin_Disconnect(pin->pConnectedTo); | 
 |             IPin_Disconnect(This->pOutputPin); | 
 |         } | 
 |         IPin_Release(This->pOutputPin); | 
 |         DeleteCriticalSection(&This->csFilter); | 
 |         This->lpVtbl = NULL; | 
 |         CoTaskMemFree(This); | 
 |         ObjectRefCount(FALSE); | 
 |     } | 
 |     return refCount; | 
 | } | 
 |  | 
 | /** IPersist methods **/ | 
 |  | 
 | static HRESULT WINAPI VfwCapture_GetClassID(IBaseFilter * iface, CLSID * pClsid) | 
 | { | 
 |     TRACE("(%p)\n", pClsid); | 
 |     *pClsid = CLSID_VfwCapture; | 
 |     return S_OK; | 
 | } | 
 |  | 
 | /** IMediaFilter methods **/ | 
 |  | 
 | static HRESULT WINAPI VfwCapture_Stop(IBaseFilter * iface) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("()\n"); | 
 |     return qcap_driver_stop(This->driver_info, &This->state); | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("()\n"); | 
 |     return qcap_driver_pause(This->driver_info, &This->state); | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwCapture_Run(IBaseFilter * iface, REFERENCE_TIME tStart) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |     TRACE("(%lx%08lx)\n", (ULONG)(tStart >> 32), (ULONG)tStart); | 
 |     return qcap_driver_run(This->driver_info, &This->state); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_GetState( IBaseFilter * iface, DWORD dwMilliSecsTimeout, | 
 |                      FILTER_STATE *pState ) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("(%lu, %p)\n", dwMilliSecsTimeout, pState); | 
 |  | 
 |     *pState = This->state; | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock) | 
 | { | 
 |     TRACE("(%p)\n", pClock); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock) | 
 | { | 
 |     TRACE("(%p)\n", ppClock); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | /** IBaseFilter methods **/ | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum) | 
 | { | 
 |     ENUMPINDETAILS epd; | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("(%p)\n", ppEnum); | 
 |  | 
 |     epd.cPins = 1; | 
 |     epd.ppPins = &This->pOutputPin; | 
 |     return IEnumPinsImpl_Construct(&epd, ppEnum); | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwCapture_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin) | 
 | { | 
 |     FIXME("(%s, %p) - stub\n", debugstr_w(Id), ppPin); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwCapture_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("(%p)\n", pInfo); | 
 |  | 
 |     lstrcpyW(pInfo->achName, This->filterInfo.achName); | 
 |     pInfo->pGraph = This->filterInfo.pGraph; | 
 |  | 
 |     if (pInfo->pGraph) | 
 |         IFilterGraph_AddRef(pInfo->pGraph); | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_JoinFilterGraph( IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName ) | 
 | { | 
 |     VfwCapture *This = (VfwCapture *)iface; | 
 |  | 
 |     TRACE("(%p, %s)\n", pGraph, debugstr_w(pName)); | 
 |  | 
 |     if (pName) | 
 |         lstrcpyW(This->filterInfo.achName, pName); | 
 |     else | 
 |         *This->filterInfo.achName = 0; | 
 |     This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */ | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwCapture_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo) | 
 | { | 
 |     FIXME("(%p) - stub\n", pVendorInfo); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IBaseFilterVtbl VfwCapture_Vtbl = | 
 | { | 
 |     VfwCapture_QueryInterface, | 
 |     VfwCapture_AddRef, | 
 |     VfwCapture_Release, | 
 |     VfwCapture_GetClassID, | 
 |     VfwCapture_Stop, | 
 |     VfwCapture_Pause, | 
 |     VfwCapture_Run, | 
 |     VfwCapture_GetState, | 
 |     VfwCapture_SetSyncSource, | 
 |     VfwCapture_GetSyncSource, | 
 |     VfwCapture_EnumPins, | 
 |     VfwCapture_FindPin, | 
 |     VfwCapture_QueryFilterInfo, | 
 |     VfwCapture_JoinFilterGraph, | 
 |     VfwCapture_QueryVendorInfo | 
 | }; | 
 |  | 
 | /* AMStreamConfig interface, we only need to implement {G,S}etFormat */ | 
 | static HRESULT WINAPI | 
 | AMStreamConfig_QueryInterface( IAMStreamConfig * iface, REFIID riid, LPVOID * ppv ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface); | 
 |  | 
 |     TRACE("%p --> %s\n", This, debugstr_guid(riid)); | 
 |  | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || | 
 |         IsEqualIID(riid, &IID_IAMStreamConfig)) | 
 |     { | 
 |         IAMStreamConfig_AddRef(iface); | 
 |         *ppv = iface; | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for iid %s\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI AMStreamConfig_AddRef( IAMStreamConfig * iface ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This); | 
 |     return IUnknown_AddRef((IUnknown *)This); | 
 | } | 
 |  | 
 | static ULONG WINAPI AMStreamConfig_Release( IAMStreamConfig * iface ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This); | 
 |     return IUnknown_Release((IUnknown *)This); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMStreamConfig_SetFormat(IAMStreamConfig *iface, AM_MEDIA_TYPE *pmt) | 
 | { | 
 |     HRESULT hr; | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface); | 
 |     IPinImpl *pin; | 
 |  | 
 |     TRACE("(%p): %p->%p\n", iface, pmt, pmt->pbFormat); | 
 |  | 
 |     if (This->state != State_Stopped) | 
 |     { | 
 |         TRACE("Returning not stopped error\n"); | 
 |         return VFW_E_NOT_STOPPED; | 
 |     } | 
 |  | 
 |     dump_AM_MEDIA_TYPE(pmt); | 
 |  | 
 |     pin = (IPinImpl *)This->pOutputPin; | 
 |     if (pin->pConnectedTo != NULL) | 
 |     { | 
 |         hr = IPin_QueryAccept(pin->pConnectedTo, pmt); | 
 |         TRACE("Would accept: %ld\n", hr); | 
 |         if (hr == S_FALSE) | 
 |             return VFW_E_INVALIDMEDIATYPE; | 
 |     } | 
 |  | 
 |     hr = qcap_driver_set_format(This->driver_info, pmt); | 
 |     if (SUCCEEDED(hr) && This->filterInfo.pGraph && pin->pConnectedTo ) | 
 |     { | 
 |         hr = IFilterGraph_Reconnect(This->filterInfo.pGraph, This->pOutputPin); | 
 |         if (SUCCEEDED(hr)) | 
 |             TRACE("Reconnection completed, with new media format..\n"); | 
 |     } | 
 |     TRACE("Returning: %ld\n", hr); | 
 |     return hr; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMStreamConfig_GetFormat( IAMStreamConfig *iface, AM_MEDIA_TYPE **pmt ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface); | 
 |  | 
 |     TRACE("%p -> (%p)\n", iface, pmt); | 
 |     return qcap_driver_get_format(This->driver_info, pmt); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMStreamConfig_GetNumberOfCapabilities( IAMStreamConfig *iface, int *piCount, | 
 |                                         int *piSize ) | 
 | { | 
 |     FIXME("%p: %p %p - stub, intentional\n", iface, piCount, piSize); | 
 |     return E_NOTIMPL; /* Not implemented for this interface */ | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMStreamConfig_GetStreamCaps( IAMStreamConfig *iface, int iIndex, | 
 |                               AM_MEDIA_TYPE **pmt, BYTE *pSCC ) | 
 | { | 
 |     FIXME("%p: %d %p %p - stub, intentional\n", iface, iIndex, pmt, pSCC); | 
 |     return E_NOTIMPL; /* Not implemented for this interface */ | 
 | } | 
 |  | 
 | static const IAMStreamConfigVtbl IAMStreamConfig_VTable = | 
 | { | 
 |     AMStreamConfig_QueryInterface, | 
 |     AMStreamConfig_AddRef, | 
 |     AMStreamConfig_Release, | 
 |     AMStreamConfig_SetFormat, | 
 |     AMStreamConfig_GetFormat, | 
 |     AMStreamConfig_GetNumberOfCapabilities, | 
 |     AMStreamConfig_GetStreamCaps | 
 | }; | 
 |  | 
 | static HRESULT WINAPI | 
 | AMVideoProcAmp_QueryInterface( IAMVideoProcAmp * iface, REFIID riid, | 
 |                                LPVOID * ppv ) | 
 | { | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || | 
 |         IsEqualIID(riid, &IID_IAMVideoProcAmp)) | 
 |     { | 
 |         *ppv = iface; | 
 |         IAMVideoProcAmp_AddRef( iface ); | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for iid %s\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI AMVideoProcAmp_AddRef(IAMVideoProcAmp * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); | 
 |  | 
 |     return IUnknown_AddRef((IUnknown *)This); | 
 | } | 
 |  | 
 | static ULONG WINAPI AMVideoProcAmp_Release(IAMVideoProcAmp * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); | 
 |  | 
 |     return IUnknown_Release((IUnknown *)This); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMVideoProcAmp_GetRange( IAMVideoProcAmp * iface, long Property, long *pMin, | 
 |         long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); | 
 |  | 
 |     return qcap_driver_get_prop_range( This->driver_info, Property, pMin, pMax, | 
 |                    pSteppingDelta, pDefault, pCapsFlags ); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMVideoProcAmp_Set( IAMVideoProcAmp * iface, long Property, long lValue, | 
 |                     long Flags ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); | 
 |  | 
 |     return qcap_driver_set_prop(This->driver_info, Property, lValue, Flags); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | AMVideoProcAmp_Get( IAMVideoProcAmp * iface, long Property, long *lValue, | 
 |                     long *Flags ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); | 
 |  | 
 |     return qcap_driver_get_prop(This->driver_info, Property, lValue, Flags); | 
 | } | 
 |  | 
 | static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable = | 
 | { | 
 |     AMVideoProcAmp_QueryInterface, | 
 |     AMVideoProcAmp_AddRef, | 
 |     AMVideoProcAmp_Release, | 
 |     AMVideoProcAmp_GetRange, | 
 |     AMVideoProcAmp_Set, | 
 |     AMVideoProcAmp_Get, | 
 | }; | 
 |  | 
 | static HRESULT WINAPI | 
 | PPB_QueryInterface( IPersistPropertyBag * iface, REFIID riid, LPVOID * ppv ) | 
 | { | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || | 
 |         IsEqualIID(riid, &IID_IPersist) || | 
 |         IsEqualIID(riid, &IID_IPersistPropertyBag)) | 
 |     { | 
 |         IPersistPropertyBag_AddRef(iface); | 
 |         *ppv = iface; | 
 |         return S_OK; | 
 |     } | 
 |     if (IsEqualIID(riid, &IID_IBaseFilter)) | 
 |     { | 
 |         /* FIXME: native devenum asks for IBaseFilter, should we return it? */ | 
 |         IPersistPropertyBag_AddRef(iface); | 
 |         *ppv = iface; | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for iid %s\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI PPB_AddRef(IPersistPropertyBag * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This); | 
 |  | 
 |     return IUnknown_AddRef((IUnknown *)This); | 
 | } | 
 |  | 
 | static ULONG WINAPI PPB_Release(IPersistPropertyBag * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This); | 
 |  | 
 |     return IUnknown_Release((IUnknown *)This); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | PPB_GetClassID( IPersistPropertyBag * iface, CLSID * pClassID ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |  | 
 |     FIXME("%p - stub\n", This); | 
 |  | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |  | 
 |     FIXME("%p - stub\n", This); | 
 |  | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | PPB_Load( IPersistPropertyBag * iface, IPropertyBag *pPropBag, | 
 |           IErrorLog *pErrorLog ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |     HRESULT hr; | 
 |     VARIANT var; | 
 |     const OLECHAR VFWIndex[] = {'V','F','W','I','n','d','e','x',0}; | 
 |  | 
 |     TRACE("%p/%p-> (%p, %p)\n", iface, This, pPropBag, pErrorLog); | 
 |  | 
 |     V_VT(&var) = VT_I4; | 
 |     hr = IPropertyBag_Read(pPropBag, (LPCOLESTR)VFWIndex, &var, pErrorLog); | 
 |  | 
 |     if (SUCCEEDED(hr)) | 
 |     { | 
 |         VfwPinImpl *pin; | 
 |  | 
 |         This->driver_info = qcap_driver_init( This->pOutputPin, | 
 |                var.__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.ulVal ); | 
 |         if (This->driver_info) | 
 |         { | 
 |             pin = (VfwPinImpl *)This->pOutputPin; | 
 |             pin->driver_info = This->driver_info; | 
 |             This->init = TRUE; | 
 |             hr = S_OK; | 
 |         } | 
 |         else | 
 |             hr = E_FAIL; | 
 |     } | 
 |  | 
 |     return hr; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | PPB_Save( IPersistPropertyBag * iface, IPropertyBag *pPropBag, | 
 |           BOOL fClearDirty, BOOL fSaveAllProperties ) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface); | 
 |     FIXME("%p - stub\n", This); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable = | 
 | { | 
 |     PPB_QueryInterface, | 
 |     PPB_AddRef, | 
 |     PPB_Release, | 
 |     PPB_GetClassID, | 
 |     PPB_InitNew, | 
 |     PPB_Load, | 
 |     PPB_Save | 
 | }; | 
 |  | 
 | /* IKsPropertySet interface */ | 
 | static HRESULT WINAPI | 
 | KSP_QueryInterface( IKsPropertySet * iface, REFIID riid, LPVOID * ppv ) | 
 | { | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || | 
 |         IsEqualIID(riid, &IID_IKsPropertySet)) | 
 |     { | 
 |         *ppv = (LPVOID)iface; | 
 |         IKsPropertySet_AddRef( iface ); | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for iid %s\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This); | 
 |  | 
 |     return IUnknown_AddRef((IUnknown *)This); | 
 | } | 
 |  | 
 | static ULONG WINAPI KSP_Release(IKsPropertySet * iface) | 
 | { | 
 |     ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface); | 
 |  | 
 |     TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This); | 
 |  | 
 |     return IUnknown_Release((IUnknown *)This); | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | KSP_Set( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, | 
 |          LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, | 
 |          DWORD cbPropData ) | 
 | { | 
 |     FIXME("%p: stub\n", iface); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | KSP_Get( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID, | 
 |          LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, | 
 |          DWORD cbPropData, DWORD *pcbReturned ) | 
 | { | 
 |     LPGUID pGuid; | 
 |  | 
 |     TRACE("()\n"); | 
 |  | 
 |     if (!IsEqualIID(guidPropSet, &ROPSETID_Pin)) | 
 |         return E_PROP_SET_UNSUPPORTED; | 
 |     if (pPropData == NULL && pcbReturned == NULL) | 
 |         return E_POINTER; | 
 |     if (pcbReturned) | 
 |         *pcbReturned = sizeof(GUID); | 
 |     if (pPropData == NULL) | 
 |         return S_OK; | 
 |     if (cbPropData < sizeof(GUID)) | 
 |         return E_UNEXPECTED; | 
 |     pGuid = pPropData; | 
 |     *pGuid = PIN_CATEGORY_PREVIEW; | 
 |     FIXME("() Not adding a pin with PIN_CATEGORY_CAPTURE\n"); | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | KSP_QuerySupported( IKsPropertySet * iface, REFGUID guidPropSet, | 
 |                     DWORD dwPropID, DWORD *pTypeSupport ) | 
 | { | 
 |    FIXME("%p: stub\n", iface); | 
 |    return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IKsPropertySetVtbl KSP_VTable = | 
 | { | 
 |    KSP_QueryInterface, | 
 |    KSP_AddRef, | 
 |    KSP_Release, | 
 |    KSP_Set, | 
 |    KSP_Get, | 
 |    KSP_QuerySupported | 
 | }; | 
 |  | 
 | static HRESULT | 
 | VfwPin_Construct( IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, | 
 |                   IPin ** ppPin ) | 
 | { | 
 |     static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 }; | 
 |     ALLOCATOR_PROPERTIES ap; | 
 |     VfwPinImpl * pPinImpl; | 
 |     PIN_INFO piOutput; | 
 |     HRESULT hr; | 
 |  | 
 |     pPinImpl = CoTaskMemAlloc( sizeof(*pPinImpl) ); | 
 |     if (!pPinImpl) | 
 |         return E_OUTOFMEMORY; | 
 |  | 
 |     /* What we put here doesn't matter, the | 
 |        driver function should override it then commit */ | 
 |     ap.cBuffers = 3; | 
 |     ap.cbBuffer = 230400; | 
 |     ap.cbAlign = 1; | 
 |     ap.cbPrefix = 0; | 
 |  | 
 |     piOutput.dir = PINDIR_OUTPUT; | 
 |     piOutput.pFilter = pBaseFilter; | 
 |     lstrcpyW(piOutput.achName, wszOutputPinName); | 
 |     ObjectRefCount(TRUE); | 
 |  | 
 |     hr = OutputPin_Init(&piOutput, &ap, pBaseFilter, NULL, pCritSec, &pPinImpl->pin); | 
 |     if (SUCCEEDED(hr)) | 
 |     { | 
 |         pPinImpl->KSP_VT = &KSP_VTable; | 
 |         pPinImpl->pin.pin.lpVtbl = &VfwPin_Vtbl; | 
 |         *ppPin = (IPin *)(&pPinImpl->pin.pin.lpVtbl); | 
 |         return S_OK; | 
 |     } | 
 |     return E_FAIL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv) | 
 | { | 
 |     VfwPinImpl *This = (VfwPinImpl *)iface; | 
 |  | 
 |     TRACE("%s %p\n", debugstr_guid(riid), ppv); | 
 |  | 
 |     *ppv = NULL; | 
 |     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin)) | 
 |         *ppv = (LPVOID)This; | 
 |     else if (IsEqualIID(riid, &IID_IKsPropertySet)) | 
 |         *ppv = (LPVOID)&(This->KSP_VT); | 
 |  | 
 |     if (*ppv) | 
 |     { | 
 |         IUnknown_AddRef((IUnknown *)(*ppv)); | 
 |         return S_OK; | 
 |     } | 
 |  | 
 |     FIXME("No interface for %s!\n", debugstr_guid(riid)); | 
 |     return E_NOINTERFACE; | 
 | } | 
 |  | 
 | static ULONG WINAPI VfwPin_AddRef(IPin * iface) | 
 | { | 
 |     VfwPinImpl *This = (VfwPinImpl *)iface; | 
 |     ULONG refCount = InterlockedIncrement(&This->pin.pin.refCount); | 
 |  | 
 |     TRACE("() -> new refcount: %lu\n", refCount); | 
 |  | 
 |     return refCount; | 
 | } | 
 |  | 
 | static ULONG WINAPI | 
 | VfwPin_Release(IPin * iface) | 
 | { | 
 |    VfwPinImpl *This = (VfwPinImpl *)iface; | 
 |    ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount); | 
 |  | 
 |    TRACE("() -> new refcount: %lu\n", refCount); | 
 |  | 
 |    if (!refCount) | 
 |    { | 
 |       CoTaskMemFree(This); | 
 |       ObjectRefCount(FALSE); | 
 |    } | 
 |    return refCount; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum) | 
 | { | 
 |     ENUMMEDIADETAILS emd; | 
 |     AM_MEDIA_TYPE *pmt; | 
 |     HRESULT hr; | 
 |  | 
 |     VfwPinImpl *This = (VfwPinImpl *)iface; | 
 |     emd.cMediaTypes = 1; | 
 |     hr = qcap_driver_get_format(This->driver_info, &pmt); | 
 |     emd.pMediaTypes = pmt; | 
 |     if (SUCCEEDED(hr)) | 
 |         hr = IEnumMediaTypesImpl_Construct(&emd, ppEnum); | 
 |     TRACE("%p -- %lx\n", This, hr); | 
 |     DeleteMediaType(pmt); | 
 |     return hr; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwPin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin) | 
 | { | 
 |     TRACE("(%p)->(%p, %p)\n", iface, apPin, cPin); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwPin_EndOfStream(IPin * iface) | 
 | { | 
 |     TRACE("()\n"); | 
 |     return E_UNEXPECTED; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwPin_BeginFlush(IPin * iface) | 
 | { | 
 |     TRACE("(%p)->()\n", iface); | 
 |     return E_UNEXPECTED; | 
 | } | 
 |  | 
 | static HRESULT WINAPI VfwPin_EndFlush(IPin * iface) | 
 | { | 
 |     TRACE("(%p)->()\n", iface); | 
 |     return E_UNEXPECTED; | 
 | } | 
 |  | 
 | static HRESULT WINAPI | 
 | VfwPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, | 
 |                   REFERENCE_TIME tStop, double dRate) | 
 | { | 
 |     TRACE("(%p)->(%s, %s, %e)\n", iface, wine_dbgstr_longlong(tStart), | 
 |            wine_dbgstr_longlong(tStop), dRate); | 
 |     return E_UNEXPECTED; | 
 | } | 
 |  | 
 | static const IPinVtbl VfwPin_Vtbl = | 
 | { | 
 |     VfwPin_QueryInterface, | 
 |     VfwPin_AddRef, | 
 |     VfwPin_Release, | 
 |     OutputPin_Connect, | 
 |     OutputPin_ReceiveConnection, | 
 |     OutputPin_Disconnect, | 
 |     IPinImpl_ConnectedTo, | 
 |     IPinImpl_ConnectionMediaType, | 
 |     IPinImpl_QueryPinInfo, | 
 |     IPinImpl_QueryDirection, | 
 |     IPinImpl_QueryId, | 
 |     IPinImpl_QueryAccept, | 
 |     VfwPin_EnumMediaTypes, | 
 |     VfwPin_QueryInternalConnections, | 
 |     VfwPin_EndOfStream, | 
 |     VfwPin_BeginFlush, | 
 |     VfwPin_EndFlush, | 
 |     VfwPin_NewSegment | 
 | }; |