Implemented VfwCapture interface.
[wine] / dlls / qcap / vfwcapture.c
1 /* Video For Windows Steering structure
2  *
3  * Copyright 2005 Maarten Lankhorst
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #define NONAMELESSSTRUCT
22 #define NONAMELESSUNION
23 #define COBJMACROS
24
25 #include "config.h"
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wtypes.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "dshow.h"
34
35 #include "qcap_main.h"
36 #include "wine/debug.h"
37
38 #include "pin.h"
39 #include "capture.h"
40 #include "uuids.h"
41 #include "mmreg.h"
42 #include "vfwmsgs.h"
43 #include "amvideo.h"
44 #include "strmif.h"
45 #include "ddraw.h"
46 #include "ocidl.h"
47 #include "oleauto.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
50
51 #define ICOM_THIS_MULTI(impl,field,iface) \
52     impl* const This=(impl*)((char*)(iface) - offsetof(impl,field))
53
54 static const IBaseFilterVtbl VfwCapture_Vtbl;
55 static const IAMStreamConfigVtbl IAMStreamConfig_VTable;
56 static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable;
57 static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable;
58 static const IPinVtbl VfwPin_Vtbl;
59
60 static HRESULT VfwPin_Construct( IBaseFilter *, LPCRITICAL_SECTION, IPin ** );
61
62 typedef struct VfwCapture
63 {
64     const struct IBaseFilterVtbl * lpVtbl;
65     const struct IAMStreamConfigVtbl * IAMStreamConfig_vtbl;
66     const struct IAMVideoProcAmpVtbl * IAMVideoProcAmp_vtbl;
67     const struct IPersistPropertyBagVtbl * IPersistPropertyBag_vtbl;
68
69     BOOL init;
70     Capture *myCap;
71     ULONG refCount;
72     FILTER_INFO filterInfo;
73     FILTER_STATE state;
74     CRITICAL_SECTION csFilter;
75
76     IPin * pOutputPin;
77 } VfwCapture;
78
79 /* VfwPin implementation */
80 typedef struct VfwPinImpl
81 {
82     OutputPin pin;
83     Capture *myCap;
84     IKsPropertySetVtbl * KSP_VT;
85 } VfwPinImpl;
86
87 static const Video_Init Constructors[] =
88 {
89     /* V4l_Init, */
90     NULL
91 };
92
93 static HRESULT Capture_Initialise(Capture **dimi, IPin *pOut, USHORT card)
94 {
95     HRESULT r = E_FAIL;
96     Capture *driver;
97     int i;
98
99     TRACE("%p %p %hu\n", dimi, pOut, card);
100
101     driver = CoTaskMemAlloc( sizeof(Capture) );
102     if (!driver)
103         return E_OUTOFMEMORY;
104
105     for( i=0; FAILED(r) && Constructors[i]; i++ )
106         r = Constructors[i]( driver, pOut, card );
107
108     if( SUCCEEDED(r) )
109         *dimi = driver;
110     else
111         CoTaskMemFree( driver );
112
113     return r;
114 }
115
116 IUnknown * WINAPI QCAP_createVFWCaptureFilter(IUnknown *pUnkOuter, HRESULT *phr)
117 {
118     VfwCapture *pVfwCapture;
119     HRESULT hr;
120
121     TRACE("%p - %p\n", pUnkOuter, phr);
122
123     *phr = CLASS_E_NOAGGREGATION;
124     if (pUnkOuter)
125         return NULL;
126     *phr = E_OUTOFMEMORY;
127
128     pVfwCapture = CoTaskMemAlloc( sizeof(VfwCapture) );
129
130     if (!pVfwCapture)
131         return NULL;
132
133     pVfwCapture->lpVtbl = &VfwCapture_Vtbl;
134     pVfwCapture->IAMStreamConfig_vtbl = &IAMStreamConfig_VTable;
135     pVfwCapture->IAMVideoProcAmp_vtbl = &IAMVideoProcAmp_VTable;
136     pVfwCapture->IPersistPropertyBag_vtbl = &IPersistPropertyBag_VTable;
137     pVfwCapture->refCount = 1;
138     pVfwCapture->filterInfo.achName[0] = '\0';
139     pVfwCapture->filterInfo.pGraph = NULL;
140     pVfwCapture->state = State_Stopped;
141     pVfwCapture->init = FALSE;
142     InitializeCriticalSection(&pVfwCapture->csFilter);
143     hr = VfwPin_Construct((IBaseFilter *)&pVfwCapture->lpVtbl,
144                    &pVfwCapture->csFilter, &pVfwCapture->pOutputPin);
145     if (!SUCCEEDED(hr))
146     {
147         CoTaskMemFree(pVfwCapture);
148         return NULL;
149     }
150     TRACE("-- created at %p\n", pVfwCapture);
151
152     ObjectRefCount(TRUE);
153     *phr = S_OK;
154     return (IUnknown *)pVfwCapture;
155 }
156
157 static HRESULT WINAPI VfwCapture_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
158 {
159     VfwCapture *This = (VfwCapture *)iface;
160     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
161     *ppv = NULL;
162
163     if (IsEqualIID(riid, &IID_IUnknown) ||
164         IsEqualIID(riid, &IID_IPersist) ||
165         IsEqualIID(riid, &IID_IMediaFilter) ||
166         IsEqualIID(riid, &IID_IBaseFilter))
167     {
168         *ppv = This;
169     }
170     else if (IsEqualIID(riid, &IID_IAMStreamConfig))
171         *ppv = &(This->IAMStreamConfig_vtbl);
172     else if (IsEqualIID(riid, &IID_IAMVideoProcAmp))
173         *ppv = &(This->IAMVideoProcAmp_vtbl);
174     else if (IsEqualIID(riid, &IID_IPersistPropertyBag))
175         *ppv = &(This->IPersistPropertyBag_vtbl);
176
177     if (!IsEqualIID(riid, &IID_IUnknown) &&
178         !IsEqualIID(riid, &IID_IPersist) &&
179         !IsEqualIID(riid, &IID_IPersistPropertyBag) &&
180         !This->init)
181     {
182         FIXME("Capture system not initialised when looking for %s, "
183               "trying it on primary device now\n", debugstr_guid(riid));
184         if (FAILED(Capture_Initialise(&This->myCap, (IPin *)This->pOutputPin, 0)))
185         {
186             ERR("VfwCapture initialisation failed\n");
187             return E_UNEXPECTED;
188         }
189         This->init = TRUE;
190     }
191
192     if (*ppv)
193     {
194         TRACE("Returning %s interface\n", debugstr_guid(riid));
195         IUnknown_AddRef((IUnknown *)(*ppv));
196         return S_OK;
197     }
198
199     FIXME("No interface for %s!\n", debugstr_guid(riid));
200     return E_NOINTERFACE;
201 }
202
203 static ULONG WINAPI VfwCapture_AddRef(IBaseFilter * iface)
204 {
205     VfwCapture *This = (VfwCapture *)iface;
206     ULONG refCount = InterlockedIncrement(&This->refCount);
207
208     TRACE("%p->() New refcount: %ld\n", This, refCount);
209
210     return refCount;
211 }
212
213 static ULONG WINAPI VfwCapture_Release(IBaseFilter * iface)
214 {
215     VfwCapture *This = (VfwCapture *)iface;
216     ULONG refCount = InterlockedDecrement(&This->refCount);
217
218     TRACE("%p->() New refcount: %ld\n", This, refCount);
219
220     if (!refCount)
221     {
222         IPinImpl *pin;
223
224         TRACE("destroying everything\n");
225         if (This->init)
226         {
227             if (This->state != State_Stopped)
228                 INVOKE(This->myCap, Stop, &This->state);
229             INVOKENP(This->myCap, Destroy);
230         }
231         pin = (IPinImpl*) This->pOutputPin;
232         if (pin->pConnectedTo != NULL)
233         {
234             IPin_Disconnect(pin->pConnectedTo);
235             IPin_Disconnect(This->pOutputPin);
236         }
237         IPin_Release(This->pOutputPin);
238         DeleteCriticalSection(&This->csFilter);
239         This->lpVtbl = NULL;
240         CoTaskMemFree(This);
241         ObjectRefCount(FALSE);
242     }
243     return refCount;
244 }
245
246 /** IPersist methods **/
247
248 static HRESULT WINAPI VfwCapture_GetClassID(IBaseFilter * iface, CLSID * pClsid)
249 {
250     TRACE("(%p)\n", pClsid);
251     *pClsid = CLSID_VfwCapture;
252     return S_OK;
253 }
254
255 /** IMediaFilter methods **/
256
257 static HRESULT WINAPI VfwCapture_Stop(IBaseFilter * iface)
258 {
259     VfwCapture *This = (VfwCapture *)iface;
260
261     TRACE("()\n");
262     return INVOKE(This->myCap, Stop, &This->state);
263 }
264
265 static HRESULT WINAPI VfwCapture_Pause(IBaseFilter * iface)
266 {
267     VfwCapture *This = (VfwCapture *)iface;
268
269     TRACE("()\n");
270     return INVOKE(This->myCap, Pause, &This->state);
271 }
272
273 static HRESULT WINAPI VfwCapture_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
274 {
275     VfwCapture *This = (VfwCapture *)iface;
276     TRACE("(%lx%08lx)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
277     return INVOKE(This->myCap, Run, &This->state);
278 }
279
280 static HRESULT WINAPI
281 VfwCapture_GetState( IBaseFilter * iface, DWORD dwMilliSecsTimeout,
282                      FILTER_STATE *pState )
283 {
284     VfwCapture *This = (VfwCapture *)iface;
285
286     TRACE("(%lu, %p)\n", dwMilliSecsTimeout, pState);
287
288     *pState = This->state;
289     return S_OK;
290 }
291
292 static HRESULT WINAPI
293 VfwCapture_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
294 {
295     TRACE("(%p)\n", pClock);
296
297     return S_OK;
298 }
299
300 static HRESULT WINAPI
301 VfwCapture_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
302 {
303     TRACE("(%p)\n", ppClock);
304
305     return S_OK;
306 }
307
308 /** IBaseFilter methods **/
309
310 static HRESULT WINAPI
311 VfwCapture_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
312 {
313     ENUMPINDETAILS epd;
314     VfwCapture *This = (VfwCapture *)iface;
315
316     TRACE("(%p)\n", ppEnum);
317
318     epd.cPins = 1;
319     epd.ppPins = &This->pOutputPin;
320     return IEnumPinsImpl_Construct(&epd, ppEnum);
321 }
322
323 static HRESULT WINAPI VfwCapture_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
324 {
325     FIXME("(%s, %p) - stub\n", debugstr_w(Id), ppPin);
326     return E_NOTIMPL;
327 }
328
329 static HRESULT WINAPI VfwCapture_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
330 {
331     VfwCapture *This = (VfwCapture *)iface;
332
333     TRACE("(%p)\n", pInfo);
334
335     lstrcpyW(pInfo->achName, This->filterInfo.achName);
336     pInfo->pGraph = This->filterInfo.pGraph;
337
338     if (pInfo->pGraph)
339         IFilterGraph_AddRef(pInfo->pGraph);
340     return S_OK;
341 }
342
343 static HRESULT WINAPI
344 VfwCapture_JoinFilterGraph( IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName )
345 {
346     VfwCapture *This = (VfwCapture *)iface;
347
348     TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
349
350     if (pName)
351         lstrcpyW(This->filterInfo.achName, pName);
352     else
353         *This->filterInfo.achName = 0;
354     This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
355
356     return S_OK;
357 }
358
359 static HRESULT WINAPI
360 VfwCapture_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
361 {
362     FIXME("(%p) - stub\n", pVendorInfo);
363     return E_NOTIMPL;
364 }
365
366 static const IBaseFilterVtbl VfwCapture_Vtbl =
367 {
368     VfwCapture_QueryInterface,
369     VfwCapture_AddRef,
370     VfwCapture_Release,
371     VfwCapture_GetClassID,
372     VfwCapture_Stop,
373     VfwCapture_Pause,
374     VfwCapture_Run,
375     VfwCapture_GetState,
376     VfwCapture_SetSyncSource,
377     VfwCapture_GetSyncSource,
378     VfwCapture_EnumPins,
379     VfwCapture_FindPin,
380     VfwCapture_QueryFilterInfo,
381     VfwCapture_JoinFilterGraph,
382     VfwCapture_QueryVendorInfo
383 };
384
385 /* AMStreamConfig interface, we only need to implement {G,S}etFormat */
386 static HRESULT WINAPI
387 AMStreamConfig_QueryInterface( IAMStreamConfig * iface, REFIID riid, LPVOID * ppv )
388 {
389     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
390
391     TRACE("%p --> %s\n", This, debugstr_guid(riid));
392
393     if (IsEqualIID(riid, &IID_IUnknown) ||
394         IsEqualIID(riid, &IID_IAMStreamConfig))
395     {
396         IAMStreamConfig_AddRef(iface);
397         *ppv = iface;
398         return S_OK;
399     }
400
401     FIXME("No interface for iid %s\n", debugstr_guid(riid));
402     return E_NOINTERFACE;
403 }
404
405 static ULONG WINAPI AMStreamConfig_AddRef( IAMStreamConfig * iface )
406 {
407     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
408
409     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
410     return IUnknown_AddRef((IUnknown *)This);
411 }
412
413 static ULONG WINAPI AMStreamConfig_Release( IAMStreamConfig * iface )
414 {
415     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
416
417     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
418     return IUnknown_Release((IUnknown *)This);
419 }
420
421 static HRESULT WINAPI
422 AMStreamConfig_SetFormat(IAMStreamConfig *iface, AM_MEDIA_TYPE *pmt)
423 {
424     HRESULT hr;
425     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
426     IPinImpl *pin;
427
428     TRACE("(%p): %p->%p\n", iface, pmt, pmt->pbFormat);
429
430     if (This->state != State_Stopped)
431     {
432         TRACE("Returning not stopped error\n");
433         return VFW_E_NOT_STOPPED;
434     }
435
436     dump_AM_MEDIA_TYPE(pmt);
437
438     pin = (IPinImpl *)This->pOutputPin;
439     if (pin->pConnectedTo != NULL)
440     {
441         hr = IPin_QueryAccept(pin->pConnectedTo, pmt);
442         TRACE("Would accept: %ld\n", hr);
443         if (hr == S_FALSE)
444             return VFW_E_INVALIDMEDIATYPE;
445     }
446
447     hr = INVOKE(This->myCap, SetFormat, pmt);
448     if (SUCCEEDED(hr) && This->filterInfo.pGraph && pin->pConnectedTo )
449     {
450         hr = IFilterGraph_Reconnect(This->filterInfo.pGraph, This->pOutputPin);
451         if (SUCCEEDED(hr))
452             TRACE("Reconnection completed, with new media format..\n");
453     }
454     TRACE("Returning: %ld\n", hr);
455     return hr;
456 }
457
458 static HRESULT WINAPI
459 AMStreamConfig_GetFormat( IAMStreamConfig *iface, AM_MEDIA_TYPE **pmt )
460 {
461     ICOM_THIS_MULTI(VfwCapture, IAMStreamConfig_vtbl, iface);
462
463     TRACE("%p -> (%p)\n", iface, pmt);
464     return INVOKE(This->myCap, GetFormat, pmt);
465 }
466
467 static HRESULT WINAPI
468 AMStreamConfig_GetNumberOfCapabilities( IAMStreamConfig *iface, int *piCount,
469                                         int *piSize )
470 {
471     FIXME("%p: %p %p - stub, intentional\n", iface, piCount, piSize);
472     return E_NOTIMPL; /* Not implemented for this interface */
473 }
474
475 static HRESULT WINAPI
476 AMStreamConfig_GetStreamCaps( IAMStreamConfig *iface, int iIndex,
477                               AM_MEDIA_TYPE **pmt, BYTE *pSCC )
478 {
479     FIXME("%p: %d %p %p - stub, intentional\n", iface, iIndex, pmt, pSCC);
480     return E_NOTIMPL; /* Not implemented for this interface */
481 }
482
483 static const IAMStreamConfigVtbl IAMStreamConfig_VTable =
484 {
485     AMStreamConfig_QueryInterface,
486     AMStreamConfig_AddRef,
487     AMStreamConfig_Release,
488     AMStreamConfig_SetFormat,
489     AMStreamConfig_GetFormat,
490     AMStreamConfig_GetNumberOfCapabilities,
491     AMStreamConfig_GetStreamCaps
492 };
493
494 static HRESULT WINAPI
495 AMVideoProcAmp_QueryInterface( IAMVideoProcAmp * iface, REFIID riid,
496                                LPVOID * ppv )
497 {
498     if (IsEqualIID(riid, &IID_IUnknown) ||
499         IsEqualIID(riid, &IID_IAMVideoProcAmp))
500     {
501         *ppv = iface;
502         IAMVideoProcAmp_AddRef( iface );
503         return S_OK;
504     }
505
506     FIXME("No interface for iid %s\n", debugstr_guid(riid));
507     return E_NOINTERFACE;
508 }
509
510 static ULONG WINAPI AMVideoProcAmp_AddRef(IAMVideoProcAmp * iface)
511 {
512     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface);
513
514     return IUnknown_AddRef((IUnknown *)This);
515 }
516
517 static ULONG WINAPI AMVideoProcAmp_Release(IAMVideoProcAmp * iface)
518 {
519     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface);
520
521     return IUnknown_Release((IUnknown *)This);
522 }
523
524 static HRESULT WINAPI
525 AMVideoProcAmp_GetRange( IAMVideoProcAmp * iface, long Property, long *pMin,
526         long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags )
527 {
528     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface);
529
530     return INVOKE( This->myCap, GetPropRange, Property, pMin, pMax,
531                    pSteppingDelta, pDefault, pCapsFlags );
532 }
533
534 static HRESULT WINAPI
535 AMVideoProcAmp_Set( IAMVideoProcAmp * iface, long Property, long lValue,
536                     long Flags )
537 {
538     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface);
539
540     return INVOKE(This->myCap, Set_Prop, Property, lValue, Flags);
541 }
542
543 static HRESULT WINAPI
544 AMVideoProcAmp_Get( IAMVideoProcAmp * iface, long Property, long *lValue,
545                     long *Flags )
546 {
547     ICOM_THIS_MULTI(VfwCapture, IAMVideoProcAmp_vtbl, iface);
548
549     return INVOKE(This->myCap, Get_Prop, Property, lValue, Flags);
550 }
551
552 static const IAMVideoProcAmpVtbl IAMVideoProcAmp_VTable =
553 {
554     AMVideoProcAmp_QueryInterface,
555     AMVideoProcAmp_AddRef,
556     AMVideoProcAmp_Release,
557     AMVideoProcAmp_GetRange,
558     AMVideoProcAmp_Set,
559     AMVideoProcAmp_Get,
560 };
561
562 static HRESULT WINAPI
563 PPB_QueryInterface( IPersistPropertyBag * iface, REFIID riid, LPVOID * ppv )
564 {
565     if (IsEqualIID(riid, &IID_IUnknown) ||
566         IsEqualIID(riid, &IID_IPersist) ||
567         IsEqualIID(riid, &IID_IPersistPropertyBag))
568     {
569         IPersistPropertyBag_AddRef(iface);
570         *ppv = iface;
571         return S_OK;
572     }
573     if (IsEqualIID(riid, &IID_IBaseFilter))
574     {
575         /* FIXME: native devenum asks for IBaseFilter, should we return it? */
576         IPersistPropertyBag_AddRef(iface);
577         *ppv = iface;
578         return S_OK;
579     }
580
581     FIXME("No interface for iid %s\n", debugstr_guid(riid));
582     return E_NOINTERFACE;
583 }
584
585 static ULONG WINAPI PPB_AddRef(IPersistPropertyBag * iface)
586 {
587     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
588
589     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
590
591     return IUnknown_AddRef((IUnknown *)This);
592 }
593
594 static ULONG WINAPI PPB_Release(IPersistPropertyBag * iface)
595 {
596     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
597
598     TRACE("%p --> Forwarding to VfwCapture (%p)\n", iface, This);
599
600     return IUnknown_Release((IUnknown *)This);
601 }
602
603 static HRESULT WINAPI
604 PPB_GetClassID( IPersistPropertyBag * iface, CLSID * pClassID )
605 {
606     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
607
608     FIXME("%p - stub\n", This);
609
610     return E_NOTIMPL;
611 }
612
613 static HRESULT WINAPI PPB_InitNew(IPersistPropertyBag * iface)
614 {
615     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
616
617     FIXME("%p - stub\n", This);
618
619     return E_NOTIMPL;
620 }
621
622 static HRESULT WINAPI
623 PPB_Load( IPersistPropertyBag * iface, IPropertyBag *pPropBag,
624           IErrorLog *pErrorLog )
625 {
626     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
627     HRESULT hr;
628     VARIANT var;
629     const OLECHAR VFWIndex[] = {'V','F','W','I','n','d','e','x',0};
630
631     TRACE("%p/%p-> (%p, %p)\n", iface, This, pPropBag, pErrorLog);
632
633     V_VT(&var) = VT_I4;
634     hr = IPropertyBag_Read(pPropBag, (LPCOLESTR)VFWIndex, &var, pErrorLog);
635
636     if (SUCCEEDED(hr))
637     {
638         VfwPinImpl *pin;
639
640         hr = Capture_Initialise(&This->myCap, This->pOutputPin,
641                (USHORT)var.__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.ulVal);
642         pin = (VfwPinImpl *)This->pOutputPin;
643         pin->myCap = This->myCap;
644         if (SUCCEEDED(hr))
645             This->init = TRUE;
646     }
647
648     return hr;
649 }
650
651 static HRESULT WINAPI
652 PPB_Save( IPersistPropertyBag * iface, IPropertyBag *pPropBag,
653           BOOL fClearDirty, BOOL fSaveAllProperties )
654 {
655     ICOM_THIS_MULTI(VfwCapture, IPersistPropertyBag_vtbl, iface);
656     FIXME("%p - stub\n", This);
657     return E_NOTIMPL;
658 }
659
660 static const IPersistPropertyBagVtbl IPersistPropertyBag_VTable =
661 {
662     PPB_QueryInterface,
663     PPB_AddRef,
664     PPB_Release,
665     PPB_GetClassID,
666     PPB_InitNew,
667     PPB_Load,
668     PPB_Save
669 };
670
671 /* IKsPropertySet interface */
672 static HRESULT WINAPI
673 KSP_QueryInterface( IKsPropertySet * iface, REFIID riid, LPVOID * ppv )
674 {
675     if (IsEqualIID(riid, &IID_IUnknown) ||
676         IsEqualIID(riid, &IID_IKsPropertySet))
677     {
678         *ppv = (LPVOID)iface;
679         IKsPropertySet_AddRef( iface );
680         return S_OK;
681     }
682
683     FIXME("No interface for iid %s\n", debugstr_guid(riid));
684     return E_NOINTERFACE;
685 }
686
687 static ULONG WINAPI KSP_AddRef(IKsPropertySet * iface)
688 {
689     ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface);
690
691     TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This);
692
693     return IUnknown_AddRef((IUnknown *)This);
694 }
695
696 static ULONG WINAPI KSP_Release(IKsPropertySet * iface)
697 {
698     ICOM_THIS_MULTI(VfwPinImpl, KSP_VT, iface);
699
700     TRACE("%p --> Forwarding to VfwPin (%p)\n", iface, This);
701
702     return IUnknown_Release((IUnknown *)This);
703 }
704
705 static HRESULT WINAPI
706 KSP_Set( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID,
707          LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
708          DWORD cbPropData )
709 {
710     FIXME("%p: stub\n", iface);
711     return E_NOTIMPL;
712 }
713
714 static HRESULT WINAPI
715 KSP_Get( IKsPropertySet * iface, REFGUID guidPropSet, DWORD dwPropID,
716          LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData,
717          DWORD cbPropData, DWORD *pcbReturned )
718 {
719     LPGUID pGuid;
720
721     TRACE("()\n");
722
723     if (!IsEqualIID(guidPropSet, &AMPROPSETID_Pin))
724         return E_PROP_SET_UNSUPPORTED;
725     if (pPropData == NULL && pcbReturned == NULL)
726         return E_POINTER;
727     if (pcbReturned)
728         *pcbReturned = sizeof(GUID);
729     if (pPropData == NULL)
730         return S_OK;
731     if (cbPropData < sizeof(GUID))
732         return E_UNEXPECTED;
733     pGuid = pPropData;
734     *pGuid = PIN_CATEGORY_PREVIEW;
735     FIXME("() Not adding a pin with PIN_CATEGORY_CAPTURE\n");
736     return S_OK;
737 }
738
739 static HRESULT WINAPI
740 KSP_QuerySupported( IKsPropertySet * iface, REFGUID guidPropSet,
741                     DWORD dwPropID, DWORD *pTypeSupport )
742 {
743    FIXME("%p: stub\n", iface);
744    return E_NOTIMPL;
745 }
746
747 static IKsPropertySetVtbl KSP_VTable =
748 {
749    KSP_QueryInterface,
750    KSP_AddRef,
751    KSP_Release,
752    KSP_Set,
753    KSP_Get,
754    KSP_QuerySupported
755 };
756
757 static HRESULT
758 VfwPin_Construct( IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec,
759                   IPin ** ppPin )
760 {
761     static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
762     ALLOCATOR_PROPERTIES ap;
763     VfwPinImpl * pPinImpl;
764     PIN_INFO piOutput;
765     HRESULT hr;
766
767     pPinImpl = CoTaskMemAlloc( sizeof(*pPinImpl) );
768     if (!pPinImpl)
769         return E_OUTOFMEMORY;
770
771     /* What we put here doesn't matter, the
772        driver function should override it then commit */
773     ap.cBuffers = 3;
774     ap.cbBuffer = 230400;
775     ap.cbAlign = 1;
776     ap.cbPrefix = 0;
777
778     piOutput.dir = PINDIR_OUTPUT;
779     piOutput.pFilter = pBaseFilter;
780     lstrcpyW(piOutput.achName, wszOutputPinName);
781     ObjectRefCount(TRUE);
782
783     hr = OutputPin_Init(&piOutput, &ap, pBaseFilter, NULL, pCritSec, &pPinImpl->pin);
784     if (SUCCEEDED(hr))
785     {
786         pPinImpl->KSP_VT = &KSP_VTable;
787         pPinImpl->pin.pin.lpVtbl = &VfwPin_Vtbl;
788         *ppPin = (IPin *)(&pPinImpl->pin.pin.lpVtbl);
789         return S_OK;
790     }
791     return E_FAIL;
792 }
793
794 static HRESULT WINAPI VfwPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
795 {
796     VfwPinImpl *This = (VfwPinImpl *)iface;
797
798     TRACE("%s %p\n", debugstr_guid(riid), ppv);
799
800     *ppv = NULL;
801     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPin))
802         *ppv = (LPVOID)This;
803     else if (IsEqualIID(riid, &IID_IKsPropertySet))
804         *ppv = (LPVOID)&(This->KSP_VT);
805
806     if (*ppv)
807     {
808         IUnknown_AddRef((IUnknown *)(*ppv));
809         return S_OK;
810     }
811
812     FIXME("No interface for %s!\n", debugstr_guid(riid));
813     return E_NOINTERFACE;
814 }
815
816 static ULONG WINAPI VfwPin_AddRef(IPin * iface)
817 {
818     VfwPinImpl *This = (VfwPinImpl *)iface;
819     ULONG refCount = InterlockedIncrement(&This->pin.pin.refCount);
820
821     TRACE("() -> new refcount: %lu\n", refCount);
822
823     return refCount;
824 }
825
826 static ULONG WINAPI
827 VfwPin_Release(IPin * iface)
828 {
829    VfwPinImpl *This = (VfwPinImpl *)iface;
830    ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
831
832    TRACE("() -> new refcount: %lu\n", refCount);
833
834    if (!refCount)
835    {
836       CoTaskMemFree(This);
837       ObjectRefCount(FALSE);
838    }
839    return refCount;
840 }
841
842 static HRESULT WINAPI
843 VfwPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
844 {
845     ENUMMEDIADETAILS emd;
846     AM_MEDIA_TYPE *pmt;
847     HRESULT hr;
848
849     VfwPinImpl *This = (VfwPinImpl *)iface;
850     emd.cMediaTypes = 1;
851     hr = INVOKE(This->myCap, GetFormat, &pmt);
852     emd.pMediaTypes = pmt;
853     if (SUCCEEDED(hr))
854         hr = IEnumMediaTypesImpl_Construct(&emd, ppEnum);
855     TRACE("%p -- %lx\n", This, hr);
856     DeleteMediaType(pmt);
857     return hr;
858 }
859
860 static HRESULT WINAPI
861 VfwPin_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
862 {
863     TRACE("(%p)->(%p, %p)\n", iface, apPin, cPin);
864     return E_NOTIMPL;
865 }
866
867 static HRESULT WINAPI VfwPin_EndOfStream(IPin * iface)
868 {
869     TRACE("()\n");
870     return E_UNEXPECTED;
871 }
872
873 static HRESULT WINAPI VfwPin_BeginFlush(IPin * iface)
874 {
875     TRACE("(%p)->()\n", iface);
876     return E_UNEXPECTED;
877 }
878
879 static HRESULT WINAPI VfwPin_EndFlush(IPin * iface)
880 {
881     TRACE("(%p)->()\n", iface);
882     return E_UNEXPECTED;
883 }
884
885 static HRESULT WINAPI
886 VfwPin_NewSegment(IPin * iface, REFERENCE_TIME tStart,
887                   REFERENCE_TIME tStop, double dRate)
888 {
889     TRACE("(%p)->(%s, %s, %e)\n", iface, wine_dbgstr_longlong(tStart),
890            wine_dbgstr_longlong(tStop), dRate);
891     return E_UNEXPECTED;
892 }
893
894 static const IPinVtbl VfwPin_Vtbl =
895 {
896     VfwPin_QueryInterface,
897     VfwPin_AddRef,
898     VfwPin_Release,
899     OutputPin_Connect,
900     OutputPin_ReceiveConnection,
901     OutputPin_Disconnect,
902     IPinImpl_ConnectedTo,
903     IPinImpl_ConnectionMediaType,
904     IPinImpl_QueryPinInfo,
905     IPinImpl_QueryDirection,
906     IPinImpl_QueryId,
907     IPinImpl_QueryAccept,
908     VfwPin_EnumMediaTypes,
909     VfwPin_QueryInternalConnections,
910     VfwPin_EndOfStream,
911     VfwPin_BeginFlush,
912     VfwPin_EndFlush,
913     VfwPin_NewSegment
914 };