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