From 5d16ab67f98447e32dfaa532f1e1207193e09e39 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 27 May 2005 19:22:39 +0000 Subject: [PATCH] Implemented VfwCapture interface. --- dlls/qcap/Makefile.in | 1 + dlls/qcap/capture.h | 51 +++ dlls/qcap/qcap_main.c | 6 +- dlls/qcap/vfwcapture.c | 914 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 969 insertions(+), 3 deletions(-) create mode 100644 dlls/qcap/capture.h create mode 100644 dlls/qcap/vfwcapture.c diff --git a/dlls/qcap/Makefile.in b/dlls/qcap/Makefile.in index a225edc5bad..f22cca431db 100644 --- a/dlls/qcap/Makefile.in +++ b/dlls/qcap/Makefile.in @@ -14,6 +14,7 @@ C_SRCS = \ enumpins.c \ pin.c \ qcap_main.c \ + vfwcapture.c \ yuv.c RC_SRCS = version.rc diff --git a/dlls/qcap/capture.h b/dlls/qcap/capture.h new file mode 100644 index 00000000000..150c5f1cde8 --- /dev/null +++ b/dlls/qcap/capture.h @@ -0,0 +1,51 @@ +/* DirectShow private capture header (QCAP.DLL) + * + * Copyright 2005 Maarten Lankhorst + * + * This file contains the (internal) driver registration functions, + * driver enumeration APIs and DirectDraw creation functions. + * + * 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 + */ + +#ifndef __QCAP_CAPTURE_H__ +#define __QCAP_CAPTURE_H__ + +typedef HRESULT (* Video_Destroy)(void *pBox); +typedef HRESULT (* Video_SetMediaType)(void *pBox, AM_MEDIA_TYPE * mT); +typedef HRESULT (* Video_GetMediaType)(void *pBox, AM_MEDIA_TYPE ** mT); +typedef HRESULT (* Video_GetPropRange)(void *pBox, long Property, long *pMin, long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags); +typedef HRESULT (* Video_GetProp)(void *pBox, long Property, long *lValue, long *Flags); +typedef HRESULT (* Video_SetProp)(void *pBox, long Property, long lValue, long Flags); +typedef HRESULT (* Video_Control)(void *pBox, FILTER_STATE *state); + +typedef struct capturefunctions { + Video_Destroy Destroy; + Video_SetMediaType SetFormat; + Video_GetMediaType GetFormat; + Video_GetPropRange GetPropRange; + Video_GetProp Get_Prop; + Video_SetProp Set_Prop; + Video_Control Run, Pause, Stop; + void *pMine; +} Capture; + +typedef HRESULT (* Video_Init)(Capture *pBox, IPin *pOut, USHORT card); +HRESULT V4l_Init(Capture *pBox, IPin *pOut, USHORT card); + +#define INVOKE(from, func, para...) from->func(from->pMine, para) +#define INVOKENP(from, func) from->func(from->pMine) + +#endif /* __QCAP_CAPTURE_H__ */ diff --git a/dlls/qcap/qcap_main.c b/dlls/qcap/qcap_main.c index b3e4ad946e9..44565a0d04a 100644 --- a/dlls/qcap/qcap_main.c +++ b/dlls/qcap/qcap_main.c @@ -89,12 +89,12 @@ static CFactoryTemplate const g_cTemplates[] = { &CLSID_AVICompressor, QCAP_createAVICompressor, NULL - },{ + },*/{ wVFWCaptFilter, - &CLSID_VFWCaptureFilter, + &CLSID_VfwCapture, QCAP_createVFWCaptureFilter, NULL - },{ + },/*{ wVFWCaptFilterProp, &CLSID_VFWCaptureFilterPropertyPage, QCAP_createVFWCaptureFilterPropertyPage, diff --git a/dlls/qcap/vfwcapture.c b/dlls/qcap/vfwcapture.c new file mode 100644 index 00000000000..e0162d4d0c0 --- /dev/null +++ b/dlls/qcap/vfwcapture.c @@ -0,0 +1,914 @@ +/* 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 + +#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 struct IBaseFilterVtbl * lpVtbl; + const struct IAMStreamConfigVtbl * IAMStreamConfig_vtbl; + const struct IAMVideoProcAmpVtbl * IAMVideoProcAmp_vtbl; + const struct IPersistPropertyBagVtbl * IPersistPropertyBag_vtbl; + + BOOL init; + Capture *myCap; + ULONG refCount; + FILTER_INFO filterInfo; + FILTER_STATE state; + CRITICAL_SECTION csFilter; + + IPin * pOutputPin; +} VfwCapture; + +/* VfwPin implementation */ +typedef struct VfwPinImpl +{ + OutputPin pin; + Capture *myCap; + IKsPropertySetVtbl * KSP_VT; +} VfwPinImpl; + +static const Video_Init Constructors[] = +{ + /* V4l_Init, */ + NULL +}; + +static HRESULT Capture_Initialise(Capture **dimi, IPin *pOut, USHORT card) +{ + HRESULT r = E_FAIL; + Capture *driver; + int i; + + TRACE("%p %p %hu\n", dimi, pOut, card); + + driver = CoTaskMemAlloc( sizeof(Capture) ); + if (!driver) + return E_OUTOFMEMORY; + + for( i=0; FAILED(r) && Constructors[i]; i++ ) + r = Constructors[i]( driver, pOut, card ); + + if( SUCCEEDED(r) ) + *dimi = driver; + else + CoTaskMemFree( driver ); + + return r; +} + +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)); + if (FAILED(Capture_Initialise(&This->myCap, (IPin *)This->pOutputPin, 0))) + { + 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) + INVOKE(This->myCap, Stop, &This->state); + INVOKENP(This->myCap, Destroy); + } + 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 INVOKE(This->myCap, Stop, &This->state); +} + +static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface) +{ + VfwCapture *This = (VfwCapture *)iface; + + TRACE("()\n"); + return INVOKE(This->myCap, Pause, &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 INVOKE(This->myCap, Run, &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 = INVOKE(This->myCap, SetFormat, 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 INVOKE(This->myCap, GetFormat, 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 INVOKE( This->myCap, GetPropRange, 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 INVOKE(This->myCap, Set_Prop, Property, lValue, Flags); +} + +static HRESULT WINAPI +AMVideoProcAmp_Get( IAMVideoProcAmp * iface, long Property, long *lValue, + long *Flags ) +{ + ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface); + + return INVOKE(This->myCap, Get_Prop, 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; + + hr = Capture_Initialise(&This->myCap, This->pOutputPin, + (USHORT)var.__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.ulVal); + pin = (VfwPinImpl *)This->pOutputPin; + pin->myCap = This->myCap; + if (SUCCEEDED(hr)) + This->init = TRUE; + } + + 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 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 = INVOKE(This->myCap, GetFormat, &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 +};