/* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * */ #define NONAMELESSSTRUCT #define NONAMELESSUNION #define COBJMACROS #include "config.h" #include #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 "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; VfwCapture *parent; 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); pVfwCapture->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": VfwCapture.csFilter"); hr = VfwPin_Construct((IBaseFilter *)&pVfwCapture->lpVtbl, &pVfwCapture->csFilter, &pVfwCapture->pOutputPin); if (FAILED(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: %d\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: %d\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); This->csFilter.DebugInfo->Spare[0] = 0; 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("(%x%08x)\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("(%u, %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 ? pmt->pbFormat : NULL); if (This->state != State_Stopped) { TRACE("Returning not stopped error\n"); return VFW_E_NOT_STOPPED; } if (!pmt) { TRACE("pmt is NULL\n"); return E_POINTER; } dump_AM_MEDIA_TYPE(pmt); pin = (IPinImpl *)This->pOutputPin; if (pin->pConnectedTo != NULL) { hr = IPin_QueryAccept(pin->pConnectedTo, pmt); TRACE("Would accept: %d\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: %d\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, 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; pin->parent = This; 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 = 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; } CoTaskMemFree(pPinImpl); 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 = This; else if (IsEqualIID(riid, &IID_IKsPropertySet)) *ppv = &(This->KSP_VT); else if (IsEqualIID(riid, &IID_IAMStreamConfig)) return IUnknown_QueryInterface((IUnknown *)This->parent, riid, ppv); 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: %u\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: %u\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 -- %x\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 };