winhttp: Add support for resolve timeouts.
[wine] / dlls / qedit / samplegrabber.c
1 /*              DirectShow Sample Grabber object (QEDIT.DLL)
2  *
3  * Copyright 2009 Paul Chitescu
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 #include <assert.h>
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29
30 #include "qedit_private.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(qedit);
34
35 static WCHAR const vendor_name[] = { 'W', 'i', 'n', 'e', 0 };
36 static WCHAR const pin_in_name[] = { 'I', 'n', 0 };
37 static WCHAR const pin_out_name[] = { 'O', 'u', 't', 0 };
38
39 IEnumPins *pinsenum_create(IBaseFilter *filter, IPin **pins, ULONG pinCount);
40 IEnumMediaTypes *mediaenum_create(AM_MEDIA_TYPE *mtype);
41
42 /* Fixed pins enumerator, holds filter referenced */
43 typedef struct _PE_Impl {
44     IEnumPins pe;
45     IBaseFilter *filter;
46     LONG refCount;
47     ULONG numPins;
48     ULONG index;
49     IPin *pins[0];
50 } PE_Impl;
51
52
53 /* IEnumPins interface implementation */
54
55 /* IUnknown */
56 static ULONG WINAPI
57 Fixed_IEnumPins_AddRef(IEnumPins *iface)
58 {
59     PE_Impl *This = (PE_Impl *)iface;
60     ULONG refCount = InterlockedIncrement(&This->refCount);
61     TRACE("(%p) new ref = %u\n", This, refCount);
62     return refCount;
63 }
64
65 /* IUnknown */
66 static ULONG WINAPI
67 Fixed_IEnumPins_Release(IEnumPins *iface)
68 {
69     PE_Impl *This = (PE_Impl *)iface;
70     ULONG refCount = InterlockedDecrement(&This->refCount);
71     TRACE("(%p) new ref = %u\n", This, refCount);
72     if (refCount == 0)
73     {
74         IBaseFilter_Release(This->filter);
75         CoTaskMemFree(This);
76         return 0;
77     }
78     return refCount;
79 }
80
81 /* IUnknown */
82 static HRESULT WINAPI
83 Fixed_IEnumPins_QueryInterface(IEnumPins *iface, REFIID riid, void **ppvObject)
84 {
85     PE_Impl *This = (PE_Impl *)iface;
86     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
87
88     if (IsEqualIID(riid, &IID_IUnknown) ||
89         IsEqualIID(riid, &IID_IEnumPins)) {
90         Fixed_IEnumPins_AddRef(iface);
91         *ppvObject = &(This->pins);
92         return S_OK;
93     }
94     *ppvObject = NULL;
95     WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject);
96     return E_NOINTERFACE;
97 }
98
99 /* IEnumPins */
100 static HRESULT WINAPI
101 Fixed_IEnumPins_Next(IEnumPins *iface, ULONG nPins, IPin **pins, ULONG *fetched)
102 {
103     PE_Impl *This = (PE_Impl *)iface;
104     ULONG count = 0;
105     TRACE("(%p)->(%u, %p, %p) index = %u\n", This, nPins, pins, fetched, This->index);
106     if (!nPins)
107         return E_INVALIDARG;
108     if (!pins || ((nPins != 1) && !fetched))
109         return E_POINTER;
110     while ((count < nPins) && (This->index < This->numPins)) {
111         IPin *pin = This->pins[This->index++];
112         IPin_AddRef(pin);
113         pins[count++] = pin;
114     }
115     if (fetched)
116         *fetched = count;
117     return (count == nPins) ? S_OK : S_FALSE;
118 }
119
120 /* IEnumPins */
121 static HRESULT WINAPI
122 Fixed_IEnumPins_Skip(IEnumPins *iface, ULONG nPins)
123 {
124     PE_Impl *This = (PE_Impl *)iface;
125     TRACE("(%p)->(%u) index = %u\n", This, nPins, This->index);
126     nPins += This->index;
127     if (nPins >= This->numPins) {
128         This->index = This->numPins;
129         return S_FALSE;
130     }
131     This->index = nPins;
132     return S_OK;
133 }
134
135 /* IEnumPins */
136 static HRESULT WINAPI
137 Fixed_IEnumPins_Reset(IEnumPins *iface)
138 {
139     PE_Impl *This = (PE_Impl *)iface;
140     TRACE("(%p)->() index = %u\n", This, This->index);
141     This->index = 0;
142     return S_OK;
143 }
144
145 /* IEnumPins */
146 static HRESULT WINAPI
147 Fixed_IEnumPins_Clone(IEnumPins *iface, IEnumPins **pins)
148 {
149     PE_Impl *This = (PE_Impl *)iface;
150     TRACE("(%p)->(%p) index = %u\n", This, pins, This->index);
151     if (!pins)
152         return E_POINTER;
153     *pins = pinsenum_create(This->filter, This->pins, This->numPins);
154     if (!*pins)
155         return E_OUTOFMEMORY;
156     ((PE_Impl *)*pins)->index = This->index;
157     return S_OK;
158 }
159
160
161 /* Virtual tables and constructor */
162
163 static const IEnumPinsVtbl IEnumPins_VTable =
164 {
165     Fixed_IEnumPins_QueryInterface,
166     Fixed_IEnumPins_AddRef,
167     Fixed_IEnumPins_Release,
168     Fixed_IEnumPins_Next,
169     Fixed_IEnumPins_Skip,
170     Fixed_IEnumPins_Reset,
171     Fixed_IEnumPins_Clone,
172 };
173
174 IEnumPins *pinsenum_create(IBaseFilter *filter, IPin **pins, ULONG pinCount)
175 {
176     ULONG len = sizeof(PE_Impl) + (pinCount * sizeof(IPin *));
177     PE_Impl *obj = CoTaskMemAlloc(len);
178     if (obj) {
179         ULONG i;
180         ZeroMemory(obj, len);
181         obj->pe.lpVtbl = &IEnumPins_VTable;
182         obj->refCount = 1;
183         obj->filter = filter;
184         obj->numPins = pinCount;
185         obj->index = 0;
186         for (i=0; i<pinCount; i++)
187             obj->pins[i] = pins[i];
188         IBaseFilter_AddRef(filter);
189     }
190     return &obj->pe;
191 }
192
193
194 /* Single media type enumerator */
195 typedef struct _ME_Impl {
196     IEnumMediaTypes me;
197     LONG refCount;
198     BOOL past;
199     AM_MEDIA_TYPE mtype;
200 } ME_Impl;
201
202
203 /* IEnumMediaTypes interface implementation */
204
205 /* IUnknown */
206 static ULONG WINAPI
207 Single_IEnumMediaTypes_AddRef(IEnumMediaTypes *iface)
208 {
209     ME_Impl *This = (ME_Impl *)iface;
210     ULONG refCount = InterlockedIncrement(&This->refCount);
211     TRACE("(%p) new ref = %u\n", This, refCount);
212     return refCount;
213 }
214
215 /* IUnknown */
216 static ULONG WINAPI
217 Single_IEnumMediaTypes_Release(IEnumMediaTypes *iface)
218 {
219     ME_Impl *This = (ME_Impl *)iface;
220     ULONG refCount = InterlockedDecrement(&This->refCount);
221     TRACE("(%p) new ref = %u\n", This, refCount);
222     if (refCount == 0)
223     {
224         if (This->mtype.pbFormat)
225             CoTaskMemFree(This->mtype.pbFormat);
226         CoTaskMemFree(This);
227         return 0;
228     }
229     return refCount;
230 }
231
232 /* IUnknown */
233 static HRESULT WINAPI
234 Single_IEnumMediaTypes_QueryInterface(IEnumMediaTypes *iface, REFIID riid, void **ppvObject)
235 {
236     ME_Impl *This = (ME_Impl *)iface;
237     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
238
239     if (IsEqualIID(riid, &IID_IUnknown) ||
240         IsEqualIID(riid, &IID_IEnumMediaTypes)) {
241         Single_IEnumMediaTypes_AddRef(iface);
242         *ppvObject = &(This->me);
243         return S_OK;
244     }
245     *ppvObject = NULL;
246     WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject);
247     return E_NOINTERFACE;
248 }
249
250 /* IEnumMediaTypes */
251 static HRESULT WINAPI
252 Single_IEnumMediaTypes_Next(IEnumMediaTypes *iface, ULONG nTypes, AM_MEDIA_TYPE **types, ULONG *fetched)
253 {
254     ME_Impl *This = (ME_Impl *)iface;
255     ULONG count = 0;
256     TRACE("(%p)->(%u, %p, %p)\n", This, nTypes, types, fetched);
257     if (!nTypes)
258         return E_INVALIDARG;
259     if (!types || ((nTypes != 1) && !fetched))
260         return E_POINTER;
261     if (!This->past) {
262         AM_MEDIA_TYPE *mtype = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
263         *mtype = This->mtype;
264         if (mtype->cbFormat) {
265             mtype->pbFormat = CoTaskMemAlloc(mtype->cbFormat);
266             CopyMemory(mtype->pbFormat, This->mtype.pbFormat, mtype->cbFormat);
267         }
268         *types = mtype;
269         This->past = TRUE;
270         count = 1;
271     }
272     if (fetched)
273         *fetched = count;
274     return (count == nTypes) ? S_OK : S_FALSE;
275 }
276
277 /* IEnumMediaTypes */
278 static HRESULT WINAPI
279 Single_IEnumMediaTypes_Skip(IEnumMediaTypes *iface, ULONG nTypes)
280 {
281     ME_Impl *This = (ME_Impl *)iface;
282     TRACE("(%p)->(%u)\n", This, nTypes);
283     if (nTypes)
284         This->past = TRUE;
285     return This->past ? S_FALSE : S_OK;
286 }
287
288 /* IEnumMediaTypes */
289 static HRESULT WINAPI
290 Single_IEnumMediaTypes_Reset(IEnumMediaTypes *iface)
291 {
292     ME_Impl *This = (ME_Impl *)iface;
293     TRACE("(%p)->()\n", This);
294     This->past = FALSE;
295     return S_OK;
296 }
297
298 /* IEnumMediaTypes */
299 static HRESULT WINAPI
300 Single_IEnumMediaTypes_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **me)
301 {
302     ME_Impl *This = (ME_Impl *)iface;
303     TRACE("(%p)->(%p)\n", This, me);
304     if (!me)
305         return E_POINTER;
306     *me = mediaenum_create(&This->mtype);
307     if (!*me)
308         return E_OUTOFMEMORY;
309     ((ME_Impl *)*me)->past = This->past;
310     return S_OK;
311 }
312
313
314 /* Virtual tables and constructor */
315
316 static const IEnumMediaTypesVtbl IEnumMediaTypes_VTable =
317 {
318     Single_IEnumMediaTypes_QueryInterface,
319     Single_IEnumMediaTypes_AddRef,
320     Single_IEnumMediaTypes_Release,
321     Single_IEnumMediaTypes_Next,
322     Single_IEnumMediaTypes_Skip,
323     Single_IEnumMediaTypes_Reset,
324     Single_IEnumMediaTypes_Clone,
325 };
326
327 IEnumMediaTypes *mediaenum_create(AM_MEDIA_TYPE *mtype)
328 {
329     ME_Impl *obj = CoTaskMemAlloc(sizeof(ME_Impl));
330     if (obj) {
331         ZeroMemory(obj, sizeof(ME_Impl));
332         obj->me.lpVtbl = &IEnumMediaTypes_VTable;
333         obj->refCount = 1;
334         obj->past = FALSE;
335         obj->mtype = *mtype;
336         obj->mtype.pUnk = NULL;
337         if (mtype->cbFormat) {
338             obj->mtype.pbFormat = CoTaskMemAlloc(mtype->cbFormat);
339             CopyMemory(obj->mtype.pbFormat, mtype->pbFormat, mtype->cbFormat);
340         }
341         else
342             obj->mtype.pbFormat = NULL;
343     }
344     return &obj->me;
345 }
346
347
348 /* Sample Grabber pin implementation */
349 typedef struct _SG_Pin {
350     const IPinVtbl* lpVtbl;
351     PIN_DIRECTION dir;
352     WCHAR const *name;
353     struct _SG_Impl *sg;
354     IPin *pair;
355 } SG_Pin;
356
357 /* Sample Grabber filter implementation */
358 typedef struct _SG_Impl {
359     const IBaseFilterVtbl* IBaseFilter_Vtbl;
360     const ISampleGrabberVtbl* ISampleGrabber_Vtbl;
361     const IMemInputPinVtbl* IMemInputPin_Vtbl;
362     /* TODO: IMediaPosition, IMediaSeeking, IQualityControl */
363     LONG refCount;
364     FILTER_INFO info;
365     FILTER_STATE state;
366     AM_MEDIA_TYPE mtype;
367     SG_Pin pin_in;
368     SG_Pin pin_out;
369     IMemAllocator *allocator;
370     IReferenceClock *refClock;
371     IMemInputPin *memOutput;
372     ISampleGrabberCB *grabberIface;
373     LONG grabberMethod;
374     LONG oneShot;
375 } SG_Impl;
376
377 enum {
378     OneShot_None,
379     OneShot_Wait,
380     OneShot_Past,
381 };
382
383 /* Get the SampleGrabber implementation This pointer from various interface pointers */
384 static inline SG_Impl *impl_from_IBaseFilter(IBaseFilter *iface)
385 {
386     return (SG_Impl *)((char*)iface - FIELD_OFFSET(SG_Impl, IBaseFilter_Vtbl));
387 }
388
389 static inline SG_Impl *impl_from_ISampleGrabber(ISampleGrabber *iface)
390 {
391     return (SG_Impl *)((char*)iface - FIELD_OFFSET(SG_Impl, ISampleGrabber_Vtbl));
392 }
393
394 static inline SG_Impl *impl_from_IMemInputPin(IMemInputPin *iface)
395 {
396     return (SG_Impl *)((char*)iface - FIELD_OFFSET(SG_Impl, IMemInputPin_Vtbl));
397 }
398
399
400 /* Cleanup at end of life */
401 static void SampleGrabber_cleanup(SG_Impl *This)
402 {
403     TRACE("(%p)\n", This);
404     if (This->info.pGraph)
405         WARN("(%p) still joined to filter graph %p\n", This, This->info.pGraph);
406     if (This->allocator)
407         IMemAllocator_Release(This->allocator);
408     if (This->refClock)
409         IReferenceClock_Release(This->refClock);
410     if (This->memOutput)
411         IMemInputPin_Release(This->memOutput);
412     if (This->grabberIface)
413         ISampleGrabberCB_Release(This->grabberIface);
414     if (This->mtype.pbFormat)
415         CoTaskMemFree(This->mtype.pbFormat);
416 }
417
418 /* Common helper AddRef called from all interfaces */
419 static ULONG SampleGrabber_addref(SG_Impl *This)
420 {
421     ULONG refCount = InterlockedIncrement(&This->refCount);
422     TRACE("(%p) new ref = %u\n", This, refCount);
423     return refCount;
424 }
425
426 /* Common helper Release called from all interfaces */
427 static ULONG SampleGrabber_release(SG_Impl *This)
428 {
429     ULONG refCount = InterlockedDecrement(&This->refCount);
430     TRACE("(%p) new ref = %u\n", This, refCount);
431     if (refCount == 0)
432     {
433         SampleGrabber_cleanup(This);
434         CoTaskMemFree(This);
435         return 0;
436     }
437     return refCount;
438 }
439
440 /* Common helper QueryInterface called from all interfaces */
441 static HRESULT SampleGrabber_query(SG_Impl *This, REFIID riid, void **ppvObject)
442 {
443     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
444
445     if (IsEqualIID(riid, &IID_IUnknown) ||
446         IsEqualIID(riid, &IID_IPersist) ||
447         IsEqualIID(riid, &IID_IMediaFilter) ||
448         IsEqualIID(riid, &IID_IBaseFilter)) {
449         SampleGrabber_addref(This);
450         *ppvObject = &(This->IBaseFilter_Vtbl);
451         return S_OK;
452     }
453     else if (IsEqualIID(riid, &IID_ISampleGrabber)) {
454         SampleGrabber_addref(This);
455         *ppvObject = &(This->ISampleGrabber_Vtbl);
456         return S_OK;
457     }
458     else if (IsEqualIID(riid, &IID_IMemInputPin)) {
459         SampleGrabber_addref(This);
460         *ppvObject = &(This->IMemInputPin_Vtbl);
461         return S_OK;
462     }
463     else if (IsEqualIID(riid, &IID_IMediaPosition))
464         FIXME("IMediaPosition not implemented\n");
465     else if (IsEqualIID(riid, &IID_IMediaSeeking))
466         FIXME("IMediaSeeking not implemented\n");
467     else if (IsEqualIID(riid, &IID_IQualityControl))
468         FIXME("IQualityControl not implemented\n");
469     *ppvObject = NULL;
470     WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject);
471     return E_NOINTERFACE;
472 }
473
474 /* Helper that calls installed sample callbacks */
475 static void SampleGrabber_callback(SG_Impl *This, IMediaSample *sample)
476 {
477     double time = 0.0;
478     REFERENCE_TIME tStart, tEnd;
479     if (SUCCEEDED(IMediaSample_GetTime(sample, &tStart, &tEnd)))
480         time = 1e-7 * tStart;
481     switch (This->grabberMethod) {
482         case 0:
483             {
484                 ULONG ref = IMediaSample_AddRef(sample);
485                 ISampleGrabberCB_SampleCB(This->grabberIface, time, sample);
486                 ref = IMediaSample_Release(sample) + 1 - ref;
487                 if (ref)
488                 {
489                     ERR("(%p) Callback referenced sample %p by %u\n", This, sample, ref);
490                     /* ugly as hell but some apps are sooo buggy */
491                     while (ref--)
492                         IMediaSample_Release(sample);
493                 }
494             }
495             break;
496         case 1:
497             {
498                 BYTE *data = 0;
499                 long size = IMediaSample_GetActualDataLength(sample);
500                 if (size && SUCCEEDED(IMediaSample_GetPointer(sample, &data)) && data)
501                     ISampleGrabberCB_BufferCB(This->grabberIface, time, data, size);
502             }
503             break;
504         case -1:
505             break;
506         default:
507             FIXME("unsupported method %ld\n", (long int)This->grabberMethod);
508             /* do not bother us again */
509             This->grabberMethod = -1;
510     }
511 }
512
513
514 /* SampleGrabber implementation of IBaseFilter interface */
515
516 /* IUnknown */
517 static HRESULT WINAPI
518 SampleGrabber_IBaseFilter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppvObject)
519 {
520     return SampleGrabber_query(impl_from_IBaseFilter(iface), riid, ppvObject);
521 }
522
523 /* IUnknown */
524 static ULONG WINAPI
525 SampleGrabber_IBaseFilter_AddRef(IBaseFilter *iface)
526 {
527     return SampleGrabber_addref(impl_from_IBaseFilter(iface));
528 }
529
530 /* IUnknown */
531 static ULONG WINAPI
532 SampleGrabber_IBaseFilter_Release(IBaseFilter *iface)
533 {
534     return SampleGrabber_release(impl_from_IBaseFilter(iface));
535 }
536
537 /* IPersist */
538 static HRESULT WINAPI
539 SampleGrabber_IBaseFilter_GetClassID(IBaseFilter *iface, CLSID *pClassID)
540 {
541     TRACE("(%p)\n", pClassID);
542     if (!pClassID)
543         return E_POINTER;
544     *pClassID = CLSID_SampleGrabber;
545     return S_OK;
546 }
547
548 /* IMediaFilter */
549 static HRESULT WINAPI
550 SampleGrabber_IBaseFilter_Stop(IBaseFilter *iface)
551 {
552     SG_Impl *This = impl_from_IBaseFilter(iface);
553     TRACE("(%p)\n", This);
554     This->state = State_Stopped;
555     return S_OK;
556 }
557
558 /* IMediaFilter */
559 static HRESULT WINAPI
560 SampleGrabber_IBaseFilter_Pause(IBaseFilter *iface)
561 {
562     SG_Impl *This = impl_from_IBaseFilter(iface);
563     TRACE("(%p)\n", This);
564     This->state = State_Paused;
565     return S_OK;
566 }
567
568 /* IMediaFilter */
569 static HRESULT WINAPI
570 SampleGrabber_IBaseFilter_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
571 {
572     SG_Impl *This = impl_from_IBaseFilter(iface);
573     TRACE("(%p)\n", This);
574     This->state = State_Running;
575     return S_OK;
576 }
577
578 /* IMediaFilter */
579 static HRESULT WINAPI
580 SampleGrabber_IBaseFilter_GetState(IBaseFilter *iface, DWORD msTout, FILTER_STATE *state)
581 {
582     SG_Impl *This = impl_from_IBaseFilter(iface);
583     TRACE("(%p)->(%u, %p)\n", This, msTout, state);
584     if (!state)
585         return E_POINTER;
586     *state = This->state;
587     return S_OK;
588 }
589
590 /* IMediaFilter */
591 static HRESULT WINAPI
592 SampleGrabber_IBaseFilter_SetSyncSource(IBaseFilter *iface, IReferenceClock *clock)
593 {
594     SG_Impl *This = impl_from_IBaseFilter(iface);
595     TRACE("(%p)->(%p)\n", This, clock);
596     if (clock != This->refClock)
597     {
598         if (clock)
599             IReferenceClock_AddRef(clock);
600         if (This->refClock)
601             IReferenceClock_Release(This->refClock);
602         This->refClock = clock;
603     }
604     return S_OK;
605 }
606
607 /* IMediaFilter */
608 static HRESULT WINAPI
609 SampleGrabber_IBaseFilter_GetSyncSource(IBaseFilter *iface, IReferenceClock **clock)
610 {
611     SG_Impl *This = impl_from_IBaseFilter(iface);
612     TRACE("(%p)->(%p)\n", This, clock);
613     if (!clock)
614         return E_POINTER;
615     if (This->refClock)
616         IReferenceClock_AddRef(This->refClock);
617     *clock = This->refClock;
618     return S_OK;
619 }
620
621 /* IBaseFilter */
622 static HRESULT WINAPI
623 SampleGrabber_IBaseFilter_EnumPins(IBaseFilter *iface, IEnumPins **pins)
624 {
625     SG_Impl *This = impl_from_IBaseFilter(iface);
626     IPin *pin[2];
627     TRACE("(%p)->(%p)\n", This, pins);
628     if (!pins)
629         return E_POINTER;
630     pin[0] = (IPin*)&This->pin_in.lpVtbl;
631     pin[1] = (IPin*)&This->pin_out.lpVtbl;
632     *pins = pinsenum_create(iface, pin, 2);
633     return *pins ? S_OK : E_OUTOFMEMORY;
634 }
635
636 /* IBaseFilter */
637 static HRESULT WINAPI
638 SampleGrabber_IBaseFilter_FindPin(IBaseFilter *iface, LPCWSTR id, IPin **pin)
639 {
640     SG_Impl *This = impl_from_IBaseFilter(iface);
641     TRACE("(%p)->(%s, %p)\n", This, debugstr_w(id), pin);
642     if (!id || !pin)
643         return E_POINTER;
644     if (!lstrcmpiW(id,pin_in_name))
645     {
646         SampleGrabber_addref(This);
647         *pin = (IPin*)&(This->pin_in.lpVtbl);
648         return S_OK;
649     }
650     else if (!lstrcmpiW(id,pin_out_name))
651     {
652         SampleGrabber_addref(This);
653         *pin = (IPin*)&(This->pin_out.lpVtbl);
654         return S_OK;
655     }
656     *pin = NULL;
657     return VFW_E_NOT_FOUND;
658 }
659
660 /* IBaseFilter */
661 static HRESULT WINAPI
662 SampleGrabber_IBaseFilter_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *info)
663 {
664     SG_Impl *This = impl_from_IBaseFilter(iface);
665     TRACE("(%p)->(%p)\n", This, info);
666     if (!info)
667         return E_POINTER;
668     if (This->info.pGraph)
669         IFilterGraph_AddRef(This->info.pGraph);
670     *info = This->info;
671     return S_OK;
672 }
673
674 /* IBaseFilter */
675 static HRESULT WINAPI
676 SampleGrabber_IBaseFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, LPCWSTR name)
677 {
678     SG_Impl *This = impl_from_IBaseFilter(iface);
679     TRACE("(%p)->(%p, %s)\n", This, graph, debugstr_w(name));
680     This->info.pGraph = graph;
681     if (name)
682         lstrcpynW(This->info.achName,name,MAX_FILTER_NAME);
683     This->oneShot = OneShot_None;
684     return S_OK;
685 }
686
687 /* IBaseFilter */
688 static HRESULT WINAPI
689 SampleGrabber_IBaseFilter_QueryVendorInfo(IBaseFilter *iface, LPWSTR *vendor)
690 {
691     TRACE("(%p)\n", vendor);
692     if (!vendor)
693         return E_POINTER;
694     *vendor = CoTaskMemAlloc(sizeof(vendor_name));
695     CopyMemory(*vendor, vendor_name, sizeof(vendor_name));
696     return S_OK;
697 }
698
699
700 /* SampleGrabber implementation of ISampleGrabber interface */
701
702 /* IUnknown */
703 static HRESULT WINAPI
704 SampleGrabber_ISampleGrabber_QueryInterface(ISampleGrabber *iface, REFIID riid, void **ppvObject)
705 {
706     return SampleGrabber_query(impl_from_ISampleGrabber(iface), riid, ppvObject);
707 }
708
709 /* IUnknown */
710 static ULONG WINAPI
711 SampleGrabber_ISampleGrabber_AddRef(ISampleGrabber *iface)
712 {
713     return SampleGrabber_addref(impl_from_ISampleGrabber(iface));
714 }
715
716 /* IUnknown */
717 static ULONG WINAPI
718 SampleGrabber_ISampleGrabber_Release(ISampleGrabber *iface)
719 {
720     return SampleGrabber_release(impl_from_ISampleGrabber(iface));
721 }
722
723 /* ISampleGrabber */
724 static HRESULT WINAPI
725 SampleGrabber_ISampleGrabber_SetOneShot(ISampleGrabber *iface, BOOL oneShot)
726 {
727     SG_Impl *This = impl_from_ISampleGrabber(iface);
728     TRACE("(%p)->(%u)\n", This, oneShot);
729     This->oneShot = oneShot ? OneShot_Wait : OneShot_None;
730     return S_OK;
731 }
732
733 /* ISampleGrabber */
734 static HRESULT WINAPI
735 SampleGrabber_ISampleGrabber_SetMediaType(ISampleGrabber *iface, const AM_MEDIA_TYPE *type)
736 {
737     SG_Impl *This = impl_from_ISampleGrabber(iface);
738     TRACE("(%p)->(%p)\n", This, type);
739     if (!type)
740         return E_POINTER;
741     TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n",
742         debugstr_guid(&type->majortype), debugstr_guid(&type->subtype),
743         type->lSampleSize,
744         debugstr_guid(&type->formattype), type->cbFormat);
745     if (This->mtype.pbFormat)
746         CoTaskMemFree(This->mtype.pbFormat);
747     This->mtype = *type;
748     This->mtype.pUnk = NULL;
749     if (type->cbFormat) {
750         This->mtype.pbFormat = CoTaskMemAlloc(type->cbFormat);
751         CopyMemory(This->mtype.pbFormat, type->pbFormat, type->cbFormat);
752     }
753     else
754         This->mtype.pbFormat = NULL;
755     return S_OK;
756 }
757
758 /* ISampleGrabber */
759 static HRESULT WINAPI
760 SampleGrabber_ISampleGrabber_GetConnectedMediaType(ISampleGrabber *iface, AM_MEDIA_TYPE *type)
761 {
762     SG_Impl *This = impl_from_ISampleGrabber(iface);
763     TRACE("(%p)->(%p)\n", This, type);
764     if (!type)
765         return E_POINTER;
766     if (!This->pin_in.pair)
767         return VFW_E_NOT_CONNECTED;
768     *type = This->mtype;
769     if (type->cbFormat) {
770         type->pbFormat = CoTaskMemAlloc(type->cbFormat);
771         CopyMemory(type->pbFormat, This->mtype.pbFormat, type->cbFormat);
772     }
773     return S_OK;
774 }
775
776 /* ISampleGrabber */
777 static HRESULT WINAPI
778 SampleGrabber_ISampleGrabber_SetBufferSamples(ISampleGrabber *iface, BOOL bufferEm)
779 {
780     TRACE("(%u)\n", bufferEm);
781     if (bufferEm) {
782         FIXME("buffering not implemented\n");
783         return E_NOTIMPL;
784     }
785     return S_OK;
786 }
787
788 /* ISampleGrabber */
789 static HRESULT WINAPI
790 SampleGrabber_ISampleGrabber_GetCurrentBuffer(ISampleGrabber *iface, LONG *bufSize, LONG *buffer)
791 {
792     FIXME("(%p, %p): stub\n", bufSize, buffer);
793     if (!bufSize)
794         return E_POINTER;
795     return E_INVALIDARG;
796 }
797
798 /* ISampleGrabber */
799 static HRESULT WINAPI
800 SampleGrabber_ISampleGrabber_GetCurrentSample(ISampleGrabber *iface, IMediaSample **sample)
801 {
802     /* MS doesn't implement it either, noone should call it */
803     WARN("(%p): not implemented\n", sample);
804     return E_NOTIMPL;
805 }
806
807 /* ISampleGrabber */
808 static HRESULT WINAPI
809 SampleGrabber_ISampleGrabber_SetCallback(ISampleGrabber *iface, ISampleGrabberCB *cb, LONG whichMethod)
810 {
811     SG_Impl *This = impl_from_ISampleGrabber(iface);
812     TRACE("(%p)->(%p, %u)\n", This, cb, whichMethod);
813     if (This->grabberIface)
814         ISampleGrabberCB_Release(This->grabberIface);
815     This->grabberIface = cb;
816     This->grabberMethod = whichMethod;
817     if (cb)
818         ISampleGrabberCB_AddRef(cb);
819     return S_OK;
820 }
821
822
823 /* SampleGrabber implementation of IMemInputPin interface */
824
825 /* IUnknown */
826 static HRESULT WINAPI
827 SampleGrabber_IMemInputPin_QueryInterface(IMemInputPin *iface, REFIID riid, void **ppvObject)
828 {
829     return SampleGrabber_query(impl_from_IMemInputPin(iface), riid, ppvObject);
830 }
831
832 /* IUnknown */
833 static ULONG WINAPI
834 SampleGrabber_IMemInputPin_AddRef(IMemInputPin *iface)
835 {
836     return SampleGrabber_addref(impl_from_IMemInputPin(iface));
837 }
838
839 /* IUnknown */
840 static ULONG WINAPI
841 SampleGrabber_IMemInputPin_Release(IMemInputPin *iface)
842 {
843     return SampleGrabber_release(impl_from_IMemInputPin(iface));
844 }
845
846 /* IMemInputPin */
847 static HRESULT WINAPI
848 SampleGrabber_IMemInputPin_GetAllocator(IMemInputPin *iface, IMemAllocator **allocator)
849 {
850     SG_Impl *This = impl_from_IMemInputPin(iface);
851     TRACE("(%p)->(%p) allocator = %p\n", This, allocator, This->allocator);
852     if (!allocator)
853         return E_POINTER;
854     *allocator = This->allocator;
855     if (!*allocator)
856         return VFW_E_NO_ALLOCATOR;
857     IMemAllocator_AddRef(*allocator);
858     return S_OK;
859 }
860
861 /* IMemInputPin */
862 static HRESULT WINAPI
863 SampleGrabber_IMemInputPin_NotifyAllocator(IMemInputPin *iface, IMemAllocator *allocator, BOOL readOnly)
864 {
865     SG_Impl *This = impl_from_IMemInputPin(iface);
866     TRACE("(%p)->(%p, %u) allocator = %p\n", This, allocator, readOnly, This->allocator);
867     if (This->allocator == allocator)
868         return S_OK;
869     if (This->allocator)
870         IMemAllocator_Release(This->allocator);
871     This->allocator = allocator;
872     if (allocator)
873         IMemAllocator_AddRef(allocator);
874     return S_OK;
875 }
876
877 /* IMemInputPin */
878 static HRESULT WINAPI
879 SampleGrabber_IMemInputPin_GetAllocatorRequirements(IMemInputPin *iface, ALLOCATOR_PROPERTIES *props)
880 {
881     SG_Impl *This = impl_from_IMemInputPin(iface);
882     FIXME("(%p)->(%p): semi-stub\n", This, props);
883     if (!props)
884         return E_POINTER;
885     return This->memOutput ? IMemInputPin_GetAllocatorRequirements(This->memOutput, props) : E_NOTIMPL;
886 }
887
888 /* IMemInputPin */
889 static HRESULT WINAPI
890 SampleGrabber_IMemInputPin_Receive(IMemInputPin *iface, IMediaSample *sample)
891 {
892     SG_Impl *This = impl_from_IMemInputPin(iface);
893     HRESULT hr;
894     TRACE("(%p)->(%p) output = %p, grabber = %p\n", This, sample, This->memOutput, This->grabberIface);
895     if (!sample)
896         return E_POINTER;
897     if ((This->state != State_Running) || (This->oneShot == OneShot_Past))
898         return S_FALSE;
899     if (This->grabberIface)
900         SampleGrabber_callback(This, sample);
901     hr = This->memOutput ? IMemInputPin_Receive(This->memOutput, sample) : S_OK;
902     if (This->oneShot == OneShot_Wait) {
903         This->oneShot = OneShot_Past;
904         hr = S_FALSE;
905         if (This->pin_out.pair)
906             IPin_EndOfStream(This->pin_out.pair);
907     }
908     return hr;
909 }
910
911 /* IMemInputPin */
912 static HRESULT WINAPI
913 SampleGrabber_IMemInputPin_ReceiveMultiple(IMemInputPin *iface, IMediaSample **samples, LONG nSamples, LONG *nProcessed)
914 {
915     SG_Impl *This = impl_from_IMemInputPin(iface);
916     TRACE("(%p)->(%p, %u, %p) output = %p, grabber = %p\n", This, samples, nSamples, nProcessed, This->memOutput, This->grabberIface);
917     if (!samples || !nProcessed)
918         return E_POINTER;
919     if ((This->state != State_Running) || (This->oneShot == OneShot_Past))
920         return S_FALSE;
921     if (This->grabberIface) {
922         LONG idx;
923         for (idx = 0; idx < nSamples; idx++)
924             SampleGrabber_callback(This, samples[idx]);
925     }
926     return This->memOutput ? IMemInputPin_ReceiveMultiple(This->memOutput, samples, nSamples, nProcessed) : S_OK;
927 }
928
929 /* IMemInputPin */
930 static HRESULT WINAPI
931 SampleGrabber_IMemInputPin_ReceiveCanBlock(IMemInputPin *iface)
932 {
933     SG_Impl *This = impl_from_IMemInputPin(iface);
934     TRACE("(%p)\n", This);
935     return This->memOutput ? IMemInputPin_ReceiveCanBlock(This->memOutput) : S_OK;
936 }
937
938
939 /* SampleGrabber member pin implementation */
940
941 /* IUnknown */
942 static ULONG WINAPI
943 SampleGrabber_IPin_AddRef(IPin *iface)
944 {
945     return SampleGrabber_addref(((SG_Pin *)iface)->sg);
946 }
947
948 /* IUnknown */
949 static ULONG WINAPI
950 SampleGrabber_IPin_Release(IPin *iface)
951 {
952     return SampleGrabber_release(((SG_Pin *)iface)->sg);
953 }
954
955 /* IUnknown */
956 static HRESULT WINAPI
957 SampleGrabber_IPin_QueryInterface(IPin *iface, REFIID riid, void **ppvObject)
958 {
959     SG_Pin *This = (SG_Pin *)iface;
960     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
961
962     if (IsEqualIID(riid, &IID_IUnknown) ||
963         IsEqualIID(riid, &IID_IPin)) {
964         SampleGrabber_addref(This->sg);
965         *ppvObject = This;
966         return S_OK;
967     }
968     else if (IsEqualIID(riid, &IID_IMemInputPin)) {
969         SampleGrabber_addref(This->sg);
970         *ppvObject = &(This->sg->IMemInputPin_Vtbl);
971         return S_OK;
972     }
973     *ppvObject = NULL;
974     WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject);
975     return E_NOINTERFACE;
976 }
977
978 /* IPin - input pin */
979 static HRESULT WINAPI
980 SampleGrabber_In_IPin_Connect(IPin *iface, IPin *receiver, const AM_MEDIA_TYPE *mtype)
981 {
982     WARN("(%p, %p): unexpected\n", receiver, mtype);
983     return E_UNEXPECTED;
984 }
985
986 /* IPin - output pin */
987 static HRESULT WINAPI
988 SampleGrabber_Out_IPin_Connect(IPin *iface, IPin *receiver, const AM_MEDIA_TYPE *type)
989 {
990     SG_Pin *This = (SG_Pin *)iface;
991     HRESULT hr;
992     TRACE("(%p)->(%p, %p)\n", This, receiver, type);
993     if (!receiver)
994         return E_POINTER;
995     if (This->pair)
996         return VFW_E_ALREADY_CONNECTED;
997     if (This->sg->state != State_Stopped)
998         return VFW_E_NOT_STOPPED;
999     if (type) {
1000         TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n",
1001             debugstr_guid(&type->majortype), debugstr_guid(&type->subtype),
1002             type->lSampleSize,
1003             debugstr_guid(&type->formattype), type->cbFormat);
1004         if (!IsEqualGUID(&This->sg->mtype.majortype,&GUID_NULL) &&
1005             !IsEqualGUID(&This->sg->mtype.majortype,&type->majortype))
1006             return VFW_E_TYPE_NOT_ACCEPTED;
1007         if (!IsEqualGUID(&This->sg->mtype.subtype,&MEDIASUBTYPE_None) &&
1008             !IsEqualGUID(&This->sg->mtype.subtype,&type->subtype))
1009             return VFW_E_TYPE_NOT_ACCEPTED;
1010         if (!IsEqualGUID(&This->sg->mtype.formattype,&GUID_NULL) &&
1011             !IsEqualGUID(&This->sg->mtype.formattype,&FORMAT_None) &&
1012             !IsEqualGUID(&This->sg->mtype.formattype,&type->formattype))
1013             return VFW_E_TYPE_NOT_ACCEPTED;
1014     }
1015     else
1016         type = &This->sg->mtype;
1017     hr = IPin_ReceiveConnection(receiver,(IPin*)&This->lpVtbl,type);
1018     if (FAILED(hr))
1019         return hr;
1020     This->pair = receiver;
1021     if (This->sg->memOutput) {
1022         IMemInputPin_Release(This->sg->memOutput);
1023         This->sg->memOutput = NULL;
1024     }
1025     IPin_QueryInterface(receiver,&IID_IMemInputPin,(void **)&(This->sg->memOutput));
1026     TRACE("(%p) Accepted IPin %p, IMemInputPin %p\n", This, receiver, This->sg->memOutput);
1027     return S_OK;
1028 }
1029
1030 /* IPin - input pin */
1031 static HRESULT WINAPI
1032 SampleGrabber_In_IPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *type)
1033 {
1034     SG_Pin *This = (SG_Pin *)iface;
1035     TRACE("(%p)->(%p, %p)\n", This, connector, type);
1036     if (!connector)
1037         return E_POINTER;
1038     if (This->pair)
1039         return VFW_E_ALREADY_CONNECTED;
1040     if (This->sg->state != State_Stopped)
1041         return VFW_E_NOT_STOPPED;
1042     if (type) {
1043         TRACE("Media type: %s/%s ssize: %u format: %s (%u bytes)\n",
1044             debugstr_guid(&type->majortype), debugstr_guid(&type->subtype),
1045             type->lSampleSize,
1046             debugstr_guid(&type->formattype), type->cbFormat);
1047         if (!IsEqualGUID(&This->sg->mtype.majortype,&GUID_NULL) &&
1048             !IsEqualGUID(&This->sg->mtype.majortype,&type->majortype))
1049             return VFW_E_TYPE_NOT_ACCEPTED;
1050         if (!IsEqualGUID(&This->sg->mtype.subtype,&MEDIASUBTYPE_None) &&
1051             !IsEqualGUID(&This->sg->mtype.subtype,&type->subtype))
1052             return VFW_E_TYPE_NOT_ACCEPTED;
1053         if (!IsEqualGUID(&This->sg->mtype.formattype,&GUID_NULL) &&
1054             !IsEqualGUID(&This->sg->mtype.formattype,&FORMAT_None) &&
1055             !IsEqualGUID(&This->sg->mtype.formattype,&type->formattype))
1056             return VFW_E_TYPE_NOT_ACCEPTED;
1057         if (This->sg->mtype.pbFormat)
1058             CoTaskMemFree(This->sg->mtype.pbFormat);
1059         This->sg->mtype = *type;
1060         This->sg->mtype.pUnk = NULL;
1061         if (type->cbFormat) {
1062             This->sg->mtype.pbFormat = CoTaskMemAlloc(type->cbFormat);
1063             CopyMemory(This->sg->mtype.pbFormat, type->pbFormat, type->cbFormat);
1064         }
1065         else
1066             This->sg->mtype.pbFormat = NULL;
1067     }
1068     This->pair = connector;
1069     TRACE("(%p) Accepted IPin %p\n", This, connector);
1070     return S_OK;
1071 }
1072
1073 /* IPin - output pin */
1074 static HRESULT WINAPI
1075 SampleGrabber_Out_IPin_ReceiveConnection(IPin *iface, IPin *connector, const AM_MEDIA_TYPE *mtype)
1076 {
1077     WARN("(%p, %p): unexpected\n", connector, mtype);
1078     return E_UNEXPECTED;
1079 }
1080
1081 /* IPin - input pin */
1082 static HRESULT WINAPI
1083 SampleGrabber_In_IPin_Disconnect(IPin *iface)
1084 {
1085     SG_Pin *This = (SG_Pin *)iface;
1086     TRACE("(%p)->() pair = %p\n", This, This->pair);
1087     if (This->sg->state != State_Stopped)
1088         return VFW_E_NOT_STOPPED;
1089     if (This->pair) {
1090         This->pair = NULL;
1091         return S_OK;
1092     }
1093     return S_FALSE;
1094 }
1095
1096 /* IPin - output pin */
1097 static HRESULT WINAPI
1098 SampleGrabber_Out_IPin_Disconnect(IPin *iface)
1099 {
1100     SG_Pin *This = (SG_Pin *)iface;
1101     TRACE("(%p)->() pair = %p\n", This, This->pair);
1102     if (This->sg->state != State_Stopped)
1103         return VFW_E_NOT_STOPPED;
1104     if (This->pair) {
1105         This->pair = NULL;
1106         if (This->sg->memOutput) {
1107             IMemInputPin_Release(This->sg->memOutput);
1108             This->sg->memOutput = NULL;
1109         }
1110         return S_OK;
1111     }
1112     return S_FALSE;
1113 }
1114
1115 /* IPin */
1116 static HRESULT WINAPI
1117 SampleGrabber_IPin_ConnectedTo(IPin *iface, IPin **pin)
1118 {
1119     SG_Pin *This = (SG_Pin *)iface;
1120     TRACE("(%p)->(%p) pair = %p\n", This, pin, This->pair);
1121     if (!pin)
1122         return E_POINTER;
1123     *pin = This->pair;
1124     if (*pin) {
1125         IPin_AddRef(*pin);
1126         return S_OK;
1127     }
1128     return VFW_E_NOT_CONNECTED;
1129 }
1130
1131 /* IPin */
1132 static HRESULT WINAPI
1133 SampleGrabber_IPin_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mtype)
1134 {
1135     SG_Pin *This = (SG_Pin *)iface;
1136     TRACE("(%p)->(%p)\n", This, mtype);
1137     if (!mtype)
1138         return E_POINTER;
1139     if (!This->pair)
1140         return VFW_E_NOT_CONNECTED;
1141     *mtype = This->sg->mtype;
1142     if (mtype->cbFormat) {
1143         mtype->pbFormat = CoTaskMemAlloc(mtype->cbFormat);
1144         CopyMemory(mtype->pbFormat, This->sg->mtype.pbFormat, mtype->cbFormat);
1145     }
1146     return S_OK;
1147 }
1148
1149 /* IPin */
1150 static HRESULT WINAPI
1151 SampleGrabber_IPin_QueryPinInfo(IPin *iface, PIN_INFO *info)
1152 {
1153     SG_Pin *This = (SG_Pin *)iface;
1154     TRACE("(%p)->(%p)\n", This, info);
1155     if (!info)
1156         return E_POINTER;
1157     SampleGrabber_addref(This->sg);
1158     info->pFilter = (IBaseFilter *)This->sg;
1159     info->dir = This->dir;
1160     lstrcpynW(info->achName,This->name,MAX_PIN_NAME);
1161     return S_OK;
1162 }
1163
1164 /* IPin */
1165 static HRESULT WINAPI
1166 SampleGrabber_IPin_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
1167 {
1168     SG_Pin *This = (SG_Pin *)iface;
1169     TRACE("(%p)->(%p)\n", This, dir);
1170     if (!dir)
1171         return E_POINTER;
1172     *dir = This->dir;
1173     return S_OK;
1174 }
1175
1176 /* IPin */
1177 static HRESULT WINAPI
1178 SampleGrabber_IPin_QueryId(IPin *iface, LPWSTR *id)
1179 {
1180     SG_Pin *This = (SG_Pin *)iface;
1181     int len;
1182     TRACE("(%p)->(%p)\n", This, id);
1183     if (!id)
1184         return E_POINTER;
1185     len = sizeof(WCHAR)*(1+lstrlenW(This->name));
1186     *id = CoTaskMemAlloc(len);
1187     CopyMemory(*id, This->name, len);
1188     return S_OK;
1189 }
1190
1191 /* IPin */
1192 static HRESULT WINAPI
1193 SampleGrabber_IPin_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mtype)
1194 {
1195     TRACE("(%p)\n", mtype);
1196     return S_OK;
1197 }
1198
1199 /* IPin */
1200 static HRESULT WINAPI
1201 SampleGrabber_IPin_EnumMediaTypes(IPin *iface, IEnumMediaTypes **mtypes)
1202 {
1203     SG_Pin *This = (SG_Pin *)iface;
1204     TRACE("(%p)->(%p)\n", This, mtypes);
1205     if (!mtypes)
1206         return E_POINTER;
1207     *mtypes = mediaenum_create(&This->sg->mtype);
1208     return *mtypes ? S_OK : E_OUTOFMEMORY;
1209 }
1210
1211 /* IPin - input pin */
1212 static HRESULT WINAPI
1213 SampleGrabber_In_IPin_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *nPins)
1214 {
1215     SG_Pin *This = (SG_Pin *)iface;
1216     TRACE("(%p)->(%p, %p) size = %u\n", This, pins, nPins, (nPins ? *nPins : 0));
1217     if (!nPins)
1218         return E_POINTER;
1219     if (*nPins) {
1220         if (!pins)
1221             return E_POINTER;
1222         IPin_AddRef((IPin*)&This->sg->pin_out.lpVtbl);
1223         *pins = (IPin*)&This->sg->pin_out.lpVtbl;
1224         *nPins = 1;
1225         return S_OK;
1226     }
1227     *nPins = 1;
1228     return S_FALSE;
1229 }
1230
1231 /* IPin - output pin */
1232 static HRESULT WINAPI
1233 SampleGrabber_Out_IPin_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *nPins)
1234 {
1235     WARN("(%p, %p): unexpected\n", pins, nPins);
1236     if (nPins)
1237         *nPins = 0;
1238     return E_NOTIMPL;
1239 }
1240
1241 /* IPin */
1242 static HRESULT WINAPI
1243 SampleGrabber_IPin_EndOfStream(IPin *iface)
1244 {
1245     FIXME(": stub\n");
1246     return S_OK;
1247 }
1248
1249 /* IPin */
1250 static HRESULT WINAPI
1251 SampleGrabber_IPin_BeginFlush(IPin *iface)
1252 {
1253     FIXME(": stub\n");
1254     return S_OK;
1255 }
1256
1257 /* IPin */
1258 static HRESULT WINAPI
1259 SampleGrabber_IPin_EndFlush(IPin *iface)
1260 {
1261     FIXME(": stub\n");
1262     return S_OK;
1263 }
1264
1265 /* IPin */
1266 static HRESULT WINAPI
1267 SampleGrabber_IPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double rate)
1268 {
1269     FIXME(": stub\n");
1270     return S_OK;
1271 }
1272
1273
1274 /* SampleGrabber vtables and constructor */
1275
1276 static const IBaseFilterVtbl IBaseFilter_VTable =
1277 {
1278     SampleGrabber_IBaseFilter_QueryInterface,
1279     SampleGrabber_IBaseFilter_AddRef,
1280     SampleGrabber_IBaseFilter_Release,
1281     SampleGrabber_IBaseFilter_GetClassID,
1282     SampleGrabber_IBaseFilter_Stop,
1283     SampleGrabber_IBaseFilter_Pause,
1284     SampleGrabber_IBaseFilter_Run,
1285     SampleGrabber_IBaseFilter_GetState,
1286     SampleGrabber_IBaseFilter_SetSyncSource,
1287     SampleGrabber_IBaseFilter_GetSyncSource,
1288     SampleGrabber_IBaseFilter_EnumPins,
1289     SampleGrabber_IBaseFilter_FindPin,
1290     SampleGrabber_IBaseFilter_QueryFilterInfo,
1291     SampleGrabber_IBaseFilter_JoinFilterGraph,
1292     SampleGrabber_IBaseFilter_QueryVendorInfo,
1293 };
1294
1295 static const ISampleGrabberVtbl ISampleGrabber_VTable =
1296 {
1297     SampleGrabber_ISampleGrabber_QueryInterface,
1298     SampleGrabber_ISampleGrabber_AddRef,
1299     SampleGrabber_ISampleGrabber_Release,
1300     SampleGrabber_ISampleGrabber_SetOneShot,
1301     SampleGrabber_ISampleGrabber_SetMediaType,
1302     SampleGrabber_ISampleGrabber_GetConnectedMediaType,
1303     SampleGrabber_ISampleGrabber_SetBufferSamples,
1304     SampleGrabber_ISampleGrabber_GetCurrentBuffer,
1305     SampleGrabber_ISampleGrabber_GetCurrentSample,
1306     SampleGrabber_ISampleGrabber_SetCallback,
1307 };
1308
1309 static const IMemInputPinVtbl IMemInputPin_VTable =
1310 {
1311     SampleGrabber_IMemInputPin_QueryInterface,
1312     SampleGrabber_IMemInputPin_AddRef,
1313     SampleGrabber_IMemInputPin_Release,
1314     SampleGrabber_IMemInputPin_GetAllocator,
1315     SampleGrabber_IMemInputPin_NotifyAllocator,
1316     SampleGrabber_IMemInputPin_GetAllocatorRequirements,
1317     SampleGrabber_IMemInputPin_Receive,
1318     SampleGrabber_IMemInputPin_ReceiveMultiple,
1319     SampleGrabber_IMemInputPin_ReceiveCanBlock,
1320 };
1321
1322 static const IPinVtbl IPin_In_VTable =
1323 {
1324     SampleGrabber_IPin_QueryInterface,
1325     SampleGrabber_IPin_AddRef,
1326     SampleGrabber_IPin_Release,
1327     SampleGrabber_In_IPin_Connect,
1328     SampleGrabber_In_IPin_ReceiveConnection,
1329     SampleGrabber_In_IPin_Disconnect,
1330     SampleGrabber_IPin_ConnectedTo,
1331     SampleGrabber_IPin_ConnectionMediaType,
1332     SampleGrabber_IPin_QueryPinInfo,
1333     SampleGrabber_IPin_QueryDirection,
1334     SampleGrabber_IPin_QueryId,
1335     SampleGrabber_IPin_QueryAccept,
1336     SampleGrabber_IPin_EnumMediaTypes,
1337     SampleGrabber_In_IPin_QueryInternalConnections,
1338     SampleGrabber_IPin_EndOfStream,
1339     SampleGrabber_IPin_BeginFlush,
1340     SampleGrabber_IPin_EndFlush,
1341     SampleGrabber_IPin_NewSegment,
1342 };
1343
1344 static const IPinVtbl IPin_Out_VTable =
1345 {
1346     SampleGrabber_IPin_QueryInterface,
1347     SampleGrabber_IPin_AddRef,
1348     SampleGrabber_IPin_Release,
1349     SampleGrabber_Out_IPin_Connect,
1350     SampleGrabber_Out_IPin_ReceiveConnection,
1351     SampleGrabber_Out_IPin_Disconnect,
1352     SampleGrabber_IPin_ConnectedTo,
1353     SampleGrabber_IPin_ConnectionMediaType,
1354     SampleGrabber_IPin_QueryPinInfo,
1355     SampleGrabber_IPin_QueryDirection,
1356     SampleGrabber_IPin_QueryId,
1357     SampleGrabber_IPin_QueryAccept,
1358     SampleGrabber_IPin_EnumMediaTypes,
1359     SampleGrabber_Out_IPin_QueryInternalConnections,
1360     SampleGrabber_IPin_EndOfStream,
1361     SampleGrabber_IPin_BeginFlush,
1362     SampleGrabber_IPin_EndFlush,
1363     SampleGrabber_IPin_NewSegment,
1364 };
1365
1366 HRESULT SampleGrabber_create(IUnknown *pUnkOuter, LPVOID *ppv)
1367 {
1368     SG_Impl* obj = NULL;
1369
1370     TRACE("(%p,%p)\n", ppv, pUnkOuter);
1371
1372     if (pUnkOuter)
1373         return CLASS_E_NOAGGREGATION;
1374
1375     obj = CoTaskMemAlloc(sizeof(SG_Impl));
1376     if (NULL == obj) {
1377         *ppv = NULL;
1378         return E_OUTOFMEMORY;
1379     }
1380     ZeroMemory(obj, sizeof(SG_Impl));
1381
1382     obj->refCount = 1;
1383     obj->IBaseFilter_Vtbl = &IBaseFilter_VTable;
1384     obj->ISampleGrabber_Vtbl = &ISampleGrabber_VTable;
1385     obj->IMemInputPin_Vtbl = &IMemInputPin_VTable;
1386     obj->pin_in.lpVtbl = &IPin_In_VTable;
1387     obj->pin_in.dir = PINDIR_INPUT;
1388     obj->pin_in.name = pin_in_name;
1389     obj->pin_in.sg = obj;
1390     obj->pin_in.pair = NULL;
1391     obj->pin_out.lpVtbl = &IPin_Out_VTable;
1392     obj->pin_out.dir = PINDIR_OUTPUT;
1393     obj->pin_out.name = pin_out_name;
1394     obj->pin_out.sg = obj;
1395     obj->pin_out.pair = NULL;
1396     obj->info.achName[0] = 0;
1397     obj->info.pGraph = NULL;
1398     obj->state = State_Stopped;
1399     obj->mtype.majortype = GUID_NULL;
1400     obj->mtype.subtype = MEDIASUBTYPE_None;
1401     obj->mtype.formattype = FORMAT_None;
1402     obj->allocator = NULL;
1403     obj->refClock = NULL;
1404     obj->memOutput = NULL;
1405     obj->grabberIface = NULL;
1406     obj->grabberMethod = -1;
1407     obj->oneShot = OneShot_None;
1408     *ppv = obj;
1409
1410     return S_OK;
1411 }