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