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