Define COBJMACROS. Fixes compilation on Windows.
[wine] / dlls / quartz / pin.c
1 /*
2  * Generic Implementation of IPin Interface
3  *
4  * Copyright 2003 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "quartz_private.h"
22 #include "pin.h"
23
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26 #include "uuids.h"
27 #include "vfwmsgs.h"
28 #include <assert.h>
29
30 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
31
32 static const struct IPinVtbl InputPin_Vtbl;
33 static const struct IPinVtbl OutputPin_Vtbl;
34 static const struct IMemInputPinVtbl MemInputPin_Vtbl;
35 static const struct IPinVtbl PullPin_Vtbl;
36
37 #define ALIGNDOWN(value,boundary) ((value) & ~(boundary-1))
38 #define ALIGNUP(value,boundary) (ALIGNDOWN(value - 1, boundary) + boundary)
39
40 #define _IMemInputPin_Offset ((int)(&(((InputPin*)0)->lpVtblMemInput)))
41 #define ICOM_THIS_From_IMemInputPin(impl, iface) impl* This = (impl*)(((char*)iface)-_IMemInputPin_Offset);
42
43 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
44 {
45     /* Tempting to just do a memcpy, but the name field is
46        128 characters long! We will probably never exceed 10
47        most of the time, so we are better off copying 
48        each field manually */
49     strcpyW(pDest->achName, pSrc->achName);
50     pDest->dir = pSrc->dir;
51     pDest->pFilter = pSrc->pFilter;
52     IBaseFilter_AddRef(pDest->pFilter);
53 }
54
55 /* Function called as a helper to IPin_Connect */
56 /* specific AM_MEDIA_TYPE - it cannot be NULL */
57 /* NOTE: not part of standard interface */
58 static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
59 {
60     ICOM_THIS(OutputPin, iface);
61     HRESULT hr;
62     IMemAllocator * pMemAlloc = NULL;
63     ALLOCATOR_PROPERTIES actual; /* FIXME: should we put the actual props back in to This? */
64
65     TRACE("(%p, %p)\n", pReceivePin, pmt);
66     dump_AM_MEDIA_TYPE(pmt);
67
68     /* FIXME: call queryacceptproc */
69
70     This->pin.pConnectedTo = pReceivePin;
71     IPin_AddRef(pReceivePin);
72     CopyMediaType(&This->pin.mtCurrent, pmt);
73
74     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
75
76     /* get the IMemInputPin interface we will use to deliver samples to the
77      * connected pin */
78     if (SUCCEEDED(hr))
79     {
80         hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin);
81
82         if (SUCCEEDED(hr))
83             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pMemAlloc);
84
85         if (SUCCEEDED(hr))
86             hr = IMemAllocator_SetProperties(pMemAlloc, &This->allocProps, &actual);
87
88         if (pMemAlloc)
89             IMemAllocator_Release(pMemAlloc);
90
91         /* break connection if we couldn't get the allocator */
92         if (FAILED(hr))
93             IPin_Disconnect(pReceivePin);
94     }
95
96     if (FAILED(hr))
97     {
98         IPin_Release(This->pin.pConnectedTo);
99         This->pin.pConnectedTo = NULL;
100         DeleteMediaType(&This->pin.mtCurrent);
101     }
102
103     TRACE(" -- %lx\n", hr);
104     return hr;
105 }
106
107 HRESULT InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
108 {
109     InputPin * pPinImpl;
110
111     *ppPin = NULL;
112
113     if (pPinInfo->dir != PINDIR_INPUT)
114     {
115         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
116         return E_INVALIDARG;
117     }
118
119     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
120
121     if (!pPinImpl)
122         return E_OUTOFMEMORY;
123
124     if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
125     {
126         pPinImpl->pin.lpVtbl = &InputPin_Vtbl;
127         pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
128         
129         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
130         return S_OK;
131     }
132     return E_FAIL;
133 }
134
135 /* Note that we don't init the vtables here (like C++ constructor) */
136 HRESULT InputPin_Init(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, InputPin * pPinImpl)
137 {
138     TRACE("\n");
139
140     /* Common attributes */
141     pPinImpl->pin.refCount = 1;
142     pPinImpl->pin.pConnectedTo = NULL;
143     pPinImpl->pin.fnQueryAccept = pQueryAccept;
144     pPinImpl->pin.pUserData = pUserData;
145     pPinImpl->pin.pCritSec = pCritSec;
146     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
147
148     /* Input pin attributes */
149     pPinImpl->fnSampleProc = pSampleProc;
150     pPinImpl->pAllocator = NULL;
151     pPinImpl->tStart = 0;
152     pPinImpl->tStop = 0;
153     pPinImpl->dRate = 0;
154
155     return S_OK;
156 }
157
158 HRESULT OutputPin_Init(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES * props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, OutputPin * pPinImpl)
159 {
160     TRACE("\n");
161
162     /* Common attributes */
163     pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
164     pPinImpl->pin.refCount = 1;
165     pPinImpl->pin.pConnectedTo = NULL;
166     pPinImpl->pin.fnQueryAccept = pQueryAccept;
167     pPinImpl->pin.pUserData = pUserData;
168     pPinImpl->pin.pCritSec = pCritSec;
169     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
170
171     /* Output pin attributes */
172     pPinImpl->pMemInputPin = NULL;
173     pPinImpl->pConnectSpecific = OutputPin_ConnectSpecific;
174     if (props)
175     {
176         memcpy(&pPinImpl->allocProps, props, sizeof(pPinImpl->allocProps));
177         if (pPinImpl->allocProps.cbAlign == 0)
178             pPinImpl->allocProps.cbAlign = 1;
179     }
180     else
181         ZeroMemory(&pPinImpl->allocProps, sizeof(pPinImpl->allocProps));
182
183
184     return S_OK;
185 }
186
187 HRESULT OutputPin_Construct(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
188 {
189     OutputPin * pPinImpl;
190
191     *ppPin = NULL;
192
193     if (pPinInfo->dir != PINDIR_OUTPUT)
194     {
195         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
196         return E_INVALIDARG;
197     }
198
199     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
200
201     if (!pPinImpl)
202         return E_OUTOFMEMORY;
203
204     if (SUCCEEDED(OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pCritSec, pPinImpl)))
205     {
206         pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
207         
208         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
209         return S_OK;
210     }
211     return E_FAIL;
212 }
213
214 /*** Common pin functions ***/
215
216 ULONG WINAPI IPinImpl_AddRef(IPin * iface)
217 {
218     ICOM_THIS(IPinImpl, iface);
219     
220     TRACE("()\n");
221     
222     return InterlockedIncrement(&This->refCount);
223 }
224
225 HRESULT WINAPI IPinImpl_Disconnect(IPin * iface)
226 {
227     HRESULT hr;
228     ICOM_THIS(IPinImpl, iface);
229
230     TRACE("()\n");
231
232     EnterCriticalSection(This->pCritSec);
233     {
234         if (This->pConnectedTo)
235         {
236             IPin_Release(This->pConnectedTo);
237             This->pConnectedTo = NULL;
238             hr = S_OK;
239         }
240         else
241             hr = S_FALSE;
242     }
243     LeaveCriticalSection(This->pCritSec);
244     
245     return hr;
246 }
247
248 HRESULT WINAPI IPinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
249 {
250     HRESULT hr;
251     ICOM_THIS(IPinImpl, iface);
252
253 /*  TRACE("(%p)\n", ppPin);*/
254
255     EnterCriticalSection(This->pCritSec);
256     {
257         if (This->pConnectedTo)
258         {
259             *ppPin = This->pConnectedTo;
260             IPin_AddRef(*ppPin);
261             hr = S_OK;
262         }
263         else
264             hr = VFW_E_NOT_CONNECTED;
265     }
266     LeaveCriticalSection(This->pCritSec);
267
268     return hr;
269 }
270
271 HRESULT WINAPI IPinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
272 {
273     HRESULT hr;
274     ICOM_THIS(IPinImpl, iface);
275
276     TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
277
278     EnterCriticalSection(This->pCritSec);
279     {
280         if (This->pConnectedTo)
281         {
282             CopyMediaType(pmt, &This->mtCurrent);
283             hr = S_OK;
284         }
285         else
286         {
287             ZeroMemory(pmt, sizeof(*pmt));
288             hr = VFW_E_NOT_CONNECTED;
289         }
290     }
291     LeaveCriticalSection(This->pCritSec);
292
293     return hr;
294 }
295
296 HRESULT WINAPI IPinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
297 {
298     ICOM_THIS(IPinImpl, iface);
299
300     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
301
302     Copy_PinInfo(pInfo, &This->pinInfo);
303
304     return S_OK;
305 }
306
307 HRESULT WINAPI IPinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
308 {
309     ICOM_THIS(IPinImpl, iface);
310
311     TRACE("(%p/%p)->(%p)\n", This, iface, pPinDir);
312
313     *pPinDir = This->pinInfo.dir;
314
315     return S_OK;
316 }
317
318 HRESULT WINAPI IPinImpl_QueryId(IPin * iface, LPWSTR * Id)
319 {
320     ICOM_THIS(IPinImpl, iface);
321
322     TRACE("(%p/%p)->(%p)\n", This, iface, Id);
323
324     *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
325     if (!Id)
326         return E_OUTOFMEMORY;
327
328     strcpyW(*Id, This->pinInfo.achName);
329
330     return S_OK;
331 }
332
333 HRESULT WINAPI IPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
334 {
335     ICOM_THIS(IPinImpl, iface);
336
337     TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
338
339     return (This->fnQueryAccept(This->pUserData, pmt) == S_OK ? S_OK : S_FALSE);
340 }
341
342 HRESULT WINAPI IPinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
343 {
344     ICOM_THIS(IPinImpl, iface);
345     ENUMMEDIADETAILS emd;
346
347     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
348
349     /* override this method to allow enumeration of your types */
350     emd.cMediaTypes = 0;
351     emd.pMediaTypes = NULL;
352
353     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
354 }
355
356 HRESULT WINAPI IPinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
357 {
358     ICOM_THIS(IPinImpl, iface);
359
360     TRACE("(%p/%p)->(%p, %p)\n", This, iface, apPin, cPin);
361
362     return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
363 }
364
365 /*** IPin implementation for an input pin ***/
366
367 HRESULT WINAPI InputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
368 {
369     ICOM_THIS(InputPin, iface);
370
371     TRACE("(%p)->(%s, %p)\n", iface, qzdebugstr_guid(riid), ppv);
372
373     *ppv = NULL;
374
375     if (IsEqualIID(riid, &IID_IUnknown))
376         *ppv = (LPVOID)iface;
377     else if (IsEqualIID(riid, &IID_IPin))
378         *ppv = (LPVOID)iface;
379     else if (IsEqualIID(riid, &IID_IMemInputPin))
380         *ppv = (LPVOID)&This->lpVtblMemInput;
381
382     if (*ppv)
383     {
384         IUnknown_AddRef((IUnknown *)(*ppv));
385         return S_OK;
386     }
387
388     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
389
390     return E_NOINTERFACE;
391 }
392
393 ULONG WINAPI InputPin_Release(IPin * iface)
394 {
395     ICOM_THIS(InputPin, iface);
396     
397     TRACE("()\n");
398     
399     if (!InterlockedDecrement(&This->pin.refCount))
400     {
401         DeleteMediaType(&This->pin.mtCurrent);
402         if (This->pAllocator)
403             IMemAllocator_Release(This->pAllocator);
404         CoTaskMemFree(This);
405         return 0;
406     }
407     else
408         return This->pin.refCount;
409 }
410
411 HRESULT WINAPI InputPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
412 {
413     ERR("Outgoing connection on an input pin! (%p, %p)\n", pConnector, pmt);
414
415     return E_UNEXPECTED;
416 }
417
418
419 HRESULT WINAPI InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
420 {
421     ICOM_THIS(InputPin, iface);
422     PIN_DIRECTION pindirReceive;
423     HRESULT hr = S_OK;
424
425     TRACE("(%p, %p)\n", pReceivePin, pmt);
426     dump_AM_MEDIA_TYPE(pmt);
427
428     EnterCriticalSection(This->pin.pCritSec);
429     {
430         if (This->pin.pConnectedTo)
431             hr = VFW_E_ALREADY_CONNECTED;
432
433         if (SUCCEEDED(hr) && This->pin.fnQueryAccept(This->pin.pUserData, pmt) != S_OK)
434             hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto
435                                            * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */
436
437         if (SUCCEEDED(hr))
438         {
439             IPin_QueryDirection(pReceivePin, &pindirReceive);
440
441             if (pindirReceive != PINDIR_OUTPUT)
442             {
443                 ERR("Can't connect from non-output pin\n");
444                 hr = VFW_E_INVALID_DIRECTION;
445             }
446         }
447
448         if (SUCCEEDED(hr))
449         {
450             CopyMediaType(&This->pin.mtCurrent, pmt);
451             This->pin.pConnectedTo = pReceivePin;
452             IPin_AddRef(pReceivePin);
453         }
454     }
455     LeaveCriticalSection(This->pin.pCritSec);
456
457     return hr;
458 }
459
460 HRESULT WINAPI InputPin_EndOfStream(IPin * iface)
461 {
462     TRACE("()\n");
463
464     return S_OK;
465 }
466
467 HRESULT WINAPI InputPin_BeginFlush(IPin * iface)
468 {
469     FIXME("()\n");
470     return E_NOTIMPL;
471 }
472
473 HRESULT WINAPI InputPin_EndFlush(IPin * iface)
474 {
475     FIXME("()\n");
476     return E_NOTIMPL;
477 }
478
479 HRESULT WINAPI InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
480 {
481     ICOM_THIS(InputPin, iface);
482
483     TRACE("(%lx%08lx, %lx%08lx, %e)\n", (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
484
485     This->tStart = tStart;
486     This->tStop = tStop;
487     This->dRate = dRate;
488
489     return S_OK;
490 }
491
492 static const IPinVtbl InputPin_Vtbl = 
493 {
494     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
495     InputPin_QueryInterface,
496     IPinImpl_AddRef,
497     InputPin_Release,
498     InputPin_Connect,
499     InputPin_ReceiveConnection,
500     IPinImpl_Disconnect,
501     IPinImpl_ConnectedTo,
502     IPinImpl_ConnectionMediaType,
503     IPinImpl_QueryPinInfo,
504     IPinImpl_QueryDirection,
505     IPinImpl_QueryId,
506     IPinImpl_QueryAccept,
507     IPinImpl_EnumMediaTypes,
508     IPinImpl_QueryInternalConnections,
509     InputPin_EndOfStream,
510     InputPin_BeginFlush,
511     InputPin_EndFlush,
512     InputPin_NewSegment
513 };
514
515 /*** IMemInputPin implementation ***/
516
517 HRESULT WINAPI MemInputPin_QueryInterface(IMemInputPin * iface, REFIID riid, LPVOID * ppv)
518 {
519     ICOM_THIS_From_IMemInputPin(InputPin, iface);
520
521     return IPin_QueryInterface((IPin *)&This->pin, riid, ppv);
522 }
523
524 ULONG WINAPI MemInputPin_AddRef(IMemInputPin * iface)
525 {
526     ICOM_THIS_From_IMemInputPin(InputPin, iface);
527
528     return IPin_AddRef((IPin *)&This->pin);
529 }
530
531 ULONG WINAPI MemInputPin_Release(IMemInputPin * iface)
532 {
533     ICOM_THIS_From_IMemInputPin(InputPin, iface);
534
535     return IPin_Release((IPin *)&This->pin);
536 }
537
538 HRESULT WINAPI MemInputPin_GetAllocator(IMemInputPin * iface, IMemAllocator ** ppAllocator)
539 {
540     ICOM_THIS_From_IMemInputPin(InputPin, iface);
541
542     TRACE("(%p/%p)->(%p)\n", This, iface, ppAllocator);
543
544     *ppAllocator = This->pAllocator;
545     if (*ppAllocator)
546         IMemAllocator_AddRef(*ppAllocator);
547     
548     return *ppAllocator ? S_OK : VFW_E_NO_ALLOCATOR;
549 }
550
551 HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator * pAllocator, BOOL bReadOnly)
552 {
553     ICOM_THIS_From_IMemInputPin(InputPin, iface);
554
555     TRACE("(%p/%p)->(%p, %d)\n", This, iface, pAllocator, bReadOnly);
556
557     if (This->pAllocator)
558         IMemAllocator_Release(This->pAllocator);
559     This->pAllocator = pAllocator;
560     if (This->pAllocator)
561         IMemAllocator_AddRef(This->pAllocator);
562
563     return S_OK;
564 }
565
566 HRESULT WINAPI MemInputPin_GetAllocatorRequirements(IMemInputPin * iface, ALLOCATOR_PROPERTIES * pProps)
567 {
568     ICOM_THIS_From_IMemInputPin(InputPin, iface);
569
570     TRACE("(%p/%p)->(%p)\n", This, iface, pProps);
571
572     /* override this method if you have any specific requirements */
573
574     return E_NOTIMPL;
575 }
576
577 HRESULT WINAPI MemInputPin_Receive(IMemInputPin * iface, IMediaSample * pSample)
578 {
579     ICOM_THIS_From_IMemInputPin(InputPin, iface);
580
581     /* this trace commented out for performance reasons */
582     /*TRACE("(%p/%p)->(%p)\n", This, iface, pSample);*/
583
584     return This->fnSampleProc(This->pin.pUserData, pSample);
585 }
586
587 HRESULT WINAPI MemInputPin_ReceiveMultiple(IMemInputPin * iface, IMediaSample ** pSamples, long nSamples, long *nSamplesProcessed)
588 {
589     HRESULT hr = S_OK;
590     ICOM_THIS_From_IMemInputPin(InputPin, iface);
591
592     TRACE("(%p/%p)->(%p, %ld, %p)\n", This, iface, pSamples, nSamples, nSamplesProcessed);
593
594     for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; (*nSamplesProcessed)++)
595     {
596         hr = IMemInputPin_Receive(iface, pSamples[*nSamplesProcessed]);
597         if (hr != S_OK)
598             break;
599     }
600
601     return hr;
602 }
603
604 HRESULT WINAPI MemInputPin_ReceiveCanBlock(IMemInputPin * iface)
605 {
606     ICOM_THIS_From_IMemInputPin(InputPin, iface);
607
608     FIXME("(%p/%p)->()\n", This, iface);
609
610     /* FIXME: we should check whether any output pins will block */
611
612     return S_OK;
613 }
614
615 static const IMemInputPinVtbl MemInputPin_Vtbl = 
616 {
617     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
618     MemInputPin_QueryInterface,
619     MemInputPin_AddRef,
620     MemInputPin_Release,
621     MemInputPin_GetAllocator,
622     MemInputPin_NotifyAllocator,
623     MemInputPin_GetAllocatorRequirements,
624     MemInputPin_Receive,
625     MemInputPin_ReceiveMultiple,
626     MemInputPin_ReceiveCanBlock
627 };
628
629 HRESULT WINAPI OutputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
630 {
631     ICOM_THIS(OutputPin, iface);
632
633     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
634
635     *ppv = NULL;
636
637     if (IsEqualIID(riid, &IID_IUnknown))
638         *ppv = (LPVOID)iface;
639     else if (IsEqualIID(riid, &IID_IPin))
640         *ppv = (LPVOID)iface;
641
642     if (*ppv)
643     {
644         IUnknown_AddRef((IUnknown *)(*ppv));
645         return S_OK;
646     }
647
648     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
649
650     return E_NOINTERFACE;
651 }
652
653 ULONG WINAPI OutputPin_Release(IPin * iface)
654 {
655     ICOM_THIS(OutputPin, iface);
656     
657     TRACE("(%p/%p)->()\n", This, iface);
658     
659     if (!InterlockedDecrement(&This->pin.refCount))
660     {
661         DeleteMediaType(&This->pin.mtCurrent);
662         CoTaskMemFree(This);
663         return 0;
664     }
665     return This->pin.refCount;
666 }
667
668 HRESULT WINAPI OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
669 {
670     HRESULT hr;
671     ICOM_THIS(OutputPin, iface);
672
673     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
674     dump_AM_MEDIA_TYPE(pmt);
675
676     /* If we try to connect to ourself, we will definitely deadlock.
677      * There are other cases where we could deadlock too, but this
678      * catches the obvious case */
679     assert(pReceivePin != iface);
680
681     EnterCriticalSection(This->pin.pCritSec);
682     {
683         /* if we have been a specific type to connect with, then we can either connect
684          * with that or fail. We cannot choose different AM_MEDIA_TYPE */
685         if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
686             hr = This->pConnectSpecific(iface, pReceivePin, pmt);
687         else
688         {
689             /* negotiate media type */
690
691             IEnumMediaTypes * pEnumCandidates;
692             AM_MEDIA_TYPE * pmtCandidate; /* Candidate media type */
693
694             if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
695             {
696                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
697
698                 /* try this filter's media types first */
699                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
700                 {
701                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 
702                         (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
703                     {
704                         hr = S_OK;
705                         CoTaskMemFree(pmtCandidate);
706                         break;
707                     }
708                     CoTaskMemFree(pmtCandidate);
709                 }
710                 IEnumMediaTypes_Release(pEnumCandidates);
711             }
712
713             /* then try receiver filter's media types */
714             if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
715             {
716                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
717
718                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
719                 {
720                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 
721                         (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
722                     {
723                         hr = S_OK;
724                         CoTaskMemFree(pmtCandidate);
725                         break;
726                     }
727                     CoTaskMemFree(pmtCandidate);
728                 } /* while */
729                 IEnumMediaTypes_Release(pEnumCandidates);
730             } /* if not found */
731         } /* if negotiate media type */
732     } /* if succeeded */
733     LeaveCriticalSection(This->pin.pCritSec);
734
735     FIXME(" -- %lx\n", hr);
736     return hr;
737 }
738
739 HRESULT WINAPI OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
740 {
741     ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
742
743     return E_UNEXPECTED;
744 }
745
746 HRESULT WINAPI OutputPin_Disconnect(IPin * iface)
747 {
748     HRESULT hr;
749     ICOM_THIS(OutputPin, iface);
750
751     TRACE("()\n");
752
753     EnterCriticalSection(This->pin.pCritSec);
754     {
755         if (This->pMemInputPin)
756         {
757             IMemInputPin_Release(This->pMemInputPin);
758             This->pMemInputPin = NULL;
759         }
760         if (This->pin.pConnectedTo)
761         {
762             IPin_Release(This->pin.pConnectedTo);
763             This->pin.pConnectedTo = NULL;
764             hr = S_OK;
765         }
766         else
767             hr = S_FALSE;
768     }
769     LeaveCriticalSection(This->pin.pCritSec);
770     
771     return hr;
772 }
773
774 HRESULT WINAPI OutputPin_EndOfStream(IPin * iface)
775 {
776     TRACE("()\n");
777
778     /* not supposed to do anything in an output pin */
779
780     return E_UNEXPECTED;
781 }
782
783 HRESULT WINAPI OutputPin_BeginFlush(IPin * iface)
784 {
785     TRACE("(%p)->()\n", iface);
786
787     /* not supposed to do anything in an output pin */
788
789     return E_UNEXPECTED;
790 }
791
792 HRESULT WINAPI OutputPin_EndFlush(IPin * iface)
793 {
794     TRACE("(%p)->()\n", iface);
795
796     /* not supposed to do anything in an output pin */
797
798     return E_UNEXPECTED;
799 }
800
801 HRESULT WINAPI OutputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
802 {
803     TRACE("(%p)->(%lx%08lx, %lx%08lx, %e)\n", iface, (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
804
805     /* not supposed to do anything in an output pin */
806
807     return E_UNEXPECTED;
808 }
809
810 static const IPinVtbl OutputPin_Vtbl = 
811 {
812     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
813     OutputPin_QueryInterface,
814     IPinImpl_AddRef,
815     OutputPin_Release,
816     OutputPin_Connect,
817     OutputPin_ReceiveConnection,
818     OutputPin_Disconnect,
819     IPinImpl_ConnectedTo,
820     IPinImpl_ConnectionMediaType,
821     IPinImpl_QueryPinInfo,
822     IPinImpl_QueryDirection,
823     IPinImpl_QueryId,
824     IPinImpl_QueryAccept,
825     IPinImpl_EnumMediaTypes,
826     IPinImpl_QueryInternalConnections,
827     OutputPin_EndOfStream,
828     OutputPin_BeginFlush,
829     OutputPin_EndFlush,
830     OutputPin_NewSegment
831 };
832
833 HRESULT OutputPin_GetDeliveryBuffer(OutputPin * This, IMediaSample ** ppSample, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, DWORD dwFlags)
834 {
835     HRESULT hr;
836
837     TRACE("(%p, %p, %p, %lx)\n", ppSample, tStart, tStop, dwFlags);
838
839     EnterCriticalSection(This->pin.pCritSec);
840     {
841         if (!This->pin.pConnectedTo)
842             hr = VFW_E_NOT_CONNECTED;
843         else
844         {
845             IMemAllocator * pAlloc = NULL;
846             
847             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
848
849             if (SUCCEEDED(hr))
850                 hr = IMemAllocator_GetBuffer(pAlloc, ppSample, (REFERENCE_TIME *)tStart, (REFERENCE_TIME *)tStop, dwFlags);
851
852             if (SUCCEEDED(hr))
853                 hr = IMediaSample_SetTime(*ppSample, (REFERENCE_TIME *)tStart, (REFERENCE_TIME *)tStop);
854
855             if (pAlloc)
856                 IMemAllocator_Release(pAlloc);
857         }
858     }
859     LeaveCriticalSection(This->pin.pCritSec);
860     
861     return hr;
862 }
863
864 HRESULT OutputPin_SendSample(OutputPin * This, IMediaSample * pSample)
865 {
866     HRESULT hr = S_OK;
867     IMemInputPin * pMemConnected = NULL;
868
869     EnterCriticalSection(This->pin.pCritSec);
870     {
871         if (!This->pin.pConnectedTo || !This->pMemInputPin)
872             hr = VFW_E_NOT_CONNECTED;
873         else
874         {
875             /* we don't have the lock held when using This->pMemInputPin,
876              * so we need to AddRef it to stop it being deleted while we are
877              * using it. */
878             pMemConnected = This->pMemInputPin;
879             IMemInputPin_AddRef(pMemConnected);
880         }
881     }
882     LeaveCriticalSection(This->pin.pCritSec);
883
884     if (SUCCEEDED(hr))
885     {
886         /* NOTE: if we are in a critical section when Receive is called
887          * then it causes some problems (most notably with the native Video
888          * Renderer) if we are re-entered for whatever reason */
889         hr = IMemInputPin_Receive(pMemConnected, pSample);
890         IMemInputPin_Release(pMemConnected);
891     }
892     
893     return hr;
894 }
895
896 HRESULT OutputPin_DeliverNewSegment(OutputPin * This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
897 {
898     HRESULT hr;
899
900     EnterCriticalSection(This->pin.pCritSec);
901     {
902         if (!This->pin.pConnectedTo)
903             hr = VFW_E_NOT_CONNECTED;
904         else
905             hr = IPin_NewSegment(This->pin.pConnectedTo, tStart, tStop, dRate);
906     }
907     LeaveCriticalSection(This->pin.pCritSec);
908     
909     return hr;
910 }
911
912 HRESULT OutputPin_CommitAllocator(OutputPin * This)
913 {
914     HRESULT hr;
915
916     TRACE("(%p)->()\n", This);
917
918     EnterCriticalSection(This->pin.pCritSec);
919     {
920         if (!This->pin.pConnectedTo || !This->pMemInputPin)
921             hr = VFW_E_NOT_CONNECTED;
922         else
923         {
924             IMemAllocator * pAlloc = NULL;
925
926             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
927
928             if (SUCCEEDED(hr))
929                 hr = IMemAllocator_Commit(pAlloc);
930
931             if (pAlloc)
932                 IMemAllocator_Release(pAlloc);
933         }
934     }
935     LeaveCriticalSection(This->pin.pCritSec);
936     
937     return hr;
938 }
939
940 HRESULT OutputPin_DeliverDisconnect(OutputPin * This)
941 {
942     HRESULT hr;
943
944     TRACE("(%p)->()\n", This);
945
946     EnterCriticalSection(This->pin.pCritSec);
947     {
948         if (!This->pin.pConnectedTo || !This->pMemInputPin)
949             hr = VFW_E_NOT_CONNECTED;
950         else
951         {
952             IMemAllocator * pAlloc = NULL;
953
954             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
955
956             if (SUCCEEDED(hr))
957                 hr = IMemAllocator_Decommit(pAlloc);
958
959             if (pAlloc)
960                 IMemAllocator_Release(pAlloc);
961
962             if (SUCCEEDED(hr))
963                 hr = IPin_Disconnect(This->pin.pConnectedTo);
964         }
965     }
966     LeaveCriticalSection(This->pin.pCritSec);
967
968     return hr;
969 }
970
971
972 HRESULT PullPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
973 {
974     PullPin * pPinImpl;
975
976     *ppPin = NULL;
977
978     if (pPinInfo->dir != PINDIR_INPUT)
979     {
980         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
981         return E_INVALIDARG;
982     }
983
984     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
985
986     if (!pPinImpl)
987         return E_OUTOFMEMORY;
988
989     if (SUCCEEDED(PullPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
990     {
991         pPinImpl->pin.lpVtbl = &PullPin_Vtbl;
992         
993         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
994         return S_OK;
995     }
996     return E_FAIL;
997 }
998
999 HRESULT PullPin_Init(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, PullPin * pPinImpl)
1000 {
1001     /* Common attributes */
1002     pPinImpl->pin.refCount = 1;
1003     pPinImpl->pin.pConnectedTo = NULL;
1004     pPinImpl->pin.fnQueryAccept = pQueryAccept;
1005     pPinImpl->pin.pUserData = pUserData;
1006     pPinImpl->pin.pCritSec = pCritSec;
1007     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
1008
1009     /* Input pin attributes */
1010     pPinImpl->fnSampleProc = pSampleProc;
1011     pPinImpl->fnPreConnect = NULL;
1012     pPinImpl->pAlloc = NULL;
1013     pPinImpl->pReader = NULL;
1014     pPinImpl->hThread = NULL;
1015     pPinImpl->hEventStateChanged = CreateEventW(NULL, FALSE, TRUE, NULL);
1016
1017     pPinImpl->rtStart = 0;
1018     pPinImpl->rtStop = ((LONGLONG)0x7fffffff << 32) | 0xffffffff;
1019
1020     return S_OK;
1021 }
1022
1023 HRESULT WINAPI PullPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
1024 {
1025     PIN_DIRECTION pindirReceive;
1026     HRESULT hr = S_OK;
1027     ICOM_THIS(PullPin, iface);
1028
1029     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
1030     dump_AM_MEDIA_TYPE(pmt);
1031
1032     EnterCriticalSection(This->pin.pCritSec);
1033     {
1034         if (This->pin.pConnectedTo)
1035             hr = VFW_E_ALREADY_CONNECTED;
1036
1037         if (SUCCEEDED(hr) && (This->pin.fnQueryAccept(This->pin.pUserData, pmt) != S_OK))
1038             hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto 
1039                                            * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */
1040
1041         if (SUCCEEDED(hr))
1042         {
1043             IPin_QueryDirection(pReceivePin, &pindirReceive);
1044
1045             if (pindirReceive != PINDIR_OUTPUT)
1046             {
1047                 ERR("Can't connect from non-output pin\n");
1048                 hr = VFW_E_INVALID_DIRECTION;
1049             }
1050         }
1051
1052         if (SUCCEEDED(hr))
1053         {
1054             hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
1055         }
1056
1057         if (SUCCEEDED(hr))
1058         {
1059             ALLOCATOR_PROPERTIES props;
1060             props.cBuffers = 3;
1061             props.cbBuffer = 64 * 1024; /* 64k bytes */
1062             props.cbAlign = 1;
1063             props.cbPrefix = 0;
1064             hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
1065         }
1066
1067         if (SUCCEEDED(hr) && This->fnPreConnect)
1068         {
1069             hr = This->fnPreConnect(iface, pReceivePin);
1070         }
1071
1072         if (SUCCEEDED(hr))
1073         {
1074             CopyMediaType(&This->pin.mtCurrent, pmt);
1075             This->pin.pConnectedTo = pReceivePin;
1076             IPin_AddRef(pReceivePin);
1077         }
1078     }
1079     LeaveCriticalSection(This->pin.pCritSec);
1080     return hr;
1081 }
1082
1083 HRESULT WINAPI PullPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
1084 {
1085     ICOM_THIS(PullPin, iface);
1086
1087     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
1088
1089     *ppv = NULL;
1090
1091     if (IsEqualIID(riid, &IID_IUnknown))
1092         *ppv = (LPVOID)iface;
1093     else if (IsEqualIID(riid, &IID_IPin))
1094         *ppv = (LPVOID)iface;
1095
1096     if (*ppv)
1097     {
1098         IUnknown_AddRef((IUnknown *)(*ppv));
1099         return S_OK;
1100     }
1101
1102     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
1103
1104     return E_NOINTERFACE;
1105 }
1106
1107 ULONG WINAPI PullPin_Release(IPin * iface)
1108 {
1109     ICOM_THIS(PullPin, iface);
1110
1111     TRACE("(%p/%p)->()\n", This, iface);
1112
1113     if (!InterlockedDecrement(&This->pin.refCount))
1114     {
1115         if (This->hThread)
1116             PullPin_StopProcessing(This);
1117         IMemAllocator_Release(This->pAlloc);
1118         IAsyncReader_Release(This->pReader);
1119         CloseHandle(This->hEventStateChanged);
1120         CoTaskMemFree(This);
1121         return 0;
1122     }
1123     return This->pin.refCount;
1124 }
1125
1126 static DWORD WINAPI PullPin_Thread_Main(LPVOID pv)
1127 {
1128     for (;;)
1129         SleepEx(INFINITE, TRUE);
1130 }
1131
1132 static void CALLBACK PullPin_Thread_Process(ULONG_PTR iface)
1133 {
1134     ICOM_THIS(PullPin, iface);
1135     HRESULT hr;
1136
1137     REFERENCE_TIME rtCurrent;
1138     ALLOCATOR_PROPERTIES allocProps;
1139
1140     SetEvent(This->hEventStateChanged);
1141
1142     hr = IMemAllocator_GetProperties(This->pAlloc, &allocProps);
1143
1144     rtCurrent = MEDIATIME_FROM_BYTES(ALIGNDOWN(BYTES_FROM_MEDIATIME(This->rtStart), allocProps.cbAlign));
1145
1146     while (rtCurrent < This->rtStop)
1147     {
1148         /* FIXME: to improve performance by quite a bit this should be changed
1149          * so that one sample is processed while one sample is fetched. However,
1150          * it is harder to debug so for the moment it will stay as it is */
1151         IMediaSample * pSample = NULL;
1152         REFERENCE_TIME rtSampleStop;
1153         DWORD dwUser;
1154
1155         hr = IMemAllocator_GetBuffer(This->pAlloc, &pSample, NULL, NULL, 0);
1156
1157         if (SUCCEEDED(hr))
1158         {
1159             rtSampleStop = rtCurrent + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(pSample));
1160             if (rtSampleStop > This->rtStop)
1161                 rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(This->rtStop), allocProps.cbAlign));
1162             hr = IMediaSample_SetTime(pSample, &rtCurrent, &rtSampleStop);
1163             rtCurrent = rtSampleStop;
1164         }
1165
1166         if (SUCCEEDED(hr))
1167             hr = IAsyncReader_Request(This->pReader, pSample, (ULONG_PTR)0);
1168
1169         if (SUCCEEDED(hr))
1170             hr = IAsyncReader_WaitForNext(This->pReader, 10000, &pSample, &dwUser);
1171
1172         if (SUCCEEDED(hr))
1173             hr = This->fnSampleProc(This->pin.pUserData, pSample);
1174         else
1175             ERR("Processing error: %lx\n", hr);
1176         
1177         if (pSample)
1178             IMediaSample_Release(pSample);
1179     }
1180 }
1181
1182 static void CALLBACK PullPin_Thread_Stop(ULONG_PTR iface)
1183 {
1184     ICOM_THIS(PullPin, iface);
1185
1186     TRACE("(%p/%p)->()\n", This, (LPVOID)iface);
1187
1188     EnterCriticalSection(This->pin.pCritSec);
1189     {
1190         HRESULT hr;
1191
1192         CloseHandle(This->hThread);
1193         This->hThread = NULL;
1194         if (FAILED(hr = IMemAllocator_Decommit(This->pAlloc)))
1195             ERR("Allocator decommit failed with error %lx. Possible memory leak\n", hr);
1196     }
1197     LeaveCriticalSection(This->pin.pCritSec);
1198
1199     SetEvent(This->hEventStateChanged);
1200
1201     ExitThread(0);
1202 }
1203
1204 HRESULT PullPin_InitProcessing(PullPin * This)
1205 {
1206     HRESULT hr = S_OK;
1207
1208     TRACE("(%p)->()\n", This);
1209
1210     assert(!This->hThread);
1211
1212     /* if we are connected */
1213     if (This->pAlloc)
1214     {
1215         EnterCriticalSection(This->pin.pCritSec);
1216         {
1217             DWORD dwThreadId;
1218             assert(!This->hThread);
1219         
1220             This->hThread = CreateThread(NULL, 0, PullPin_Thread_Main, NULL, 0, &dwThreadId);
1221             if (!This->hThread)
1222                 hr = HRESULT_FROM_WIN32(GetLastError());
1223
1224             if (SUCCEEDED(hr))
1225                 hr = IMemAllocator_Commit(This->pAlloc);
1226         }
1227         LeaveCriticalSection(This->pin.pCritSec);
1228     }
1229
1230     TRACE(" -- %lx\n", hr);
1231
1232     return hr;
1233 }
1234
1235 HRESULT PullPin_StartProcessing(PullPin * This)
1236 {
1237     /* if we are connected */
1238     TRACE("(%p)->()\n", This);
1239     if(This->pAlloc)
1240     {
1241         assert(This->hThread);
1242         
1243         ResetEvent(This->hEventStateChanged);
1244         
1245         if (!QueueUserAPC(PullPin_Thread_Process, This->hThread, (ULONG_PTR)This))
1246             return HRESULT_FROM_WIN32(GetLastError());
1247     }
1248
1249     return S_OK;
1250 }
1251
1252 HRESULT PullPin_PauseProcessing(PullPin * This)
1253 {
1254     /* make the processing function exit its loop */
1255     This->rtStop = 0;
1256
1257     return S_OK;
1258 }
1259
1260 HRESULT PullPin_StopProcessing(PullPin * This)
1261 {
1262     /* if we are connected */
1263     if (This->pAlloc)
1264     {
1265         assert(This->hThread);
1266
1267         ResetEvent(This->hEventStateChanged);
1268
1269         PullPin_PauseProcessing(This);
1270
1271         if (!QueueUserAPC(PullPin_Thread_Stop, This->hThread, (ULONG_PTR)This))
1272             return HRESULT_FROM_WIN32(GetLastError());
1273     }
1274
1275     return S_OK;
1276 }
1277
1278 HRESULT PullPin_WaitForStateChange(PullPin * This, DWORD dwMilliseconds)
1279 {
1280     if (WaitForSingleObject(This->hEventStateChanged, dwMilliseconds) == WAIT_TIMEOUT)
1281         return S_FALSE;
1282     return S_OK;
1283 }
1284
1285 HRESULT PullPin_Seek(PullPin * This, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop)
1286 {
1287     FIXME("(%p)->(%lx%08lx, %lx%08lx)\n", This, (LONG)(rtStart >> 32), (LONG)rtStart, (LONG)(rtStop >> 32), (LONG)rtStop);
1288
1289     PullPin_BeginFlush((IPin *)This);
1290     /* FIXME: need critical section? */
1291     This->rtStart = rtStart;
1292     This->rtStop = rtStop;
1293     PullPin_EndFlush((IPin *)This);
1294
1295     return S_OK;
1296 }
1297
1298 HRESULT WINAPI PullPin_EndOfStream(IPin * iface)
1299 {
1300     FIXME("(%p)->()\n", iface);
1301     return E_NOTIMPL;
1302 }
1303
1304 HRESULT WINAPI PullPin_BeginFlush(IPin * iface)
1305 {
1306     FIXME("(%p)->()\n", iface);
1307     return E_NOTIMPL;
1308 }
1309
1310 HRESULT WINAPI PullPin_EndFlush(IPin * iface)
1311 {
1312     FIXME("(%p)->()\n", iface);
1313     return E_NOTIMPL;
1314 }
1315
1316 HRESULT WINAPI PullPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1317 {
1318     FIXME("(%p)->(%s, %s, %g)\n", iface, wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate);
1319     return E_NOTIMPL;
1320 }
1321
1322 static const IPinVtbl PullPin_Vtbl = 
1323 {
1324     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1325     PullPin_QueryInterface,
1326     IPinImpl_AddRef,
1327     PullPin_Release,
1328     OutputPin_Connect,
1329     PullPin_ReceiveConnection,
1330     IPinImpl_Disconnect,
1331     IPinImpl_ConnectedTo,
1332     IPinImpl_ConnectionMediaType,
1333     IPinImpl_QueryPinInfo,
1334     IPinImpl_QueryDirection,
1335     IPinImpl_QueryId,
1336     IPinImpl_QueryAccept,
1337     IPinImpl_EnumMediaTypes,
1338     IPinImpl_QueryInternalConnections,
1339     PullPin_EndOfStream,
1340     PullPin_BeginFlush,
1341     PullPin_EndFlush,
1342     PullPin_NewSegment
1343 };