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