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