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