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