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