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