Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[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
36 #define _IMemInputPin_Offset ((int)(&(((InputPin*)0)->lpVtblMemInput)))
37 #define ICOM_THIS_From_IMemInputPin(impl, iface) impl* This = (impl*)(((char*)iface)-_IMemInputPin_Offset);
38
39 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
40 {
41     /* Tempting to just do a memcpy, but the name field is
42        128 characters long! We will probably never exceed 10
43        most of the time, so we are better off copying 
44        each field manually */
45     strcpyW(pDest->achName, pSrc->achName);
46     pDest->dir = pSrc->dir;
47     pDest->pFilter = pSrc->pFilter;
48     IBaseFilter_AddRef(pDest->pFilter);
49 }
50
51 /* Internal function called as a helper to IPin_Connect */
52 /* specific AM_MEDIA_TYPE - it cannot be NULL */
53 /* NOTE: not part of standard interface */
54 static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
55 {
56     HRESULT hr;
57     ICOM_THIS(OutputPin, iface);
58
59     TRACE("(%p, %p)\n", pReceivePin, pmt);
60     dump_AM_MEDIA_TYPE(pmt);
61
62     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
63
64     if (SUCCEEDED(hr))
65     {
66         This->pin.pConnectedTo = pReceivePin;
67         IPin_AddRef(pReceivePin);
68         CopyMediaType(&This->pin.mtCurrent, pmt);
69     }
70     else
71         This->pin.pConnectedTo = NULL;
72
73     TRACE("-- %lx\n", hr);
74     return hr;
75 }
76
77 HRESULT InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
78 {
79     InputPin * pPinImpl;
80
81     *ppPin = NULL;
82
83     if (pPinInfo->dir != PINDIR_INPUT)
84     {
85         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
86         return E_INVALIDARG;
87     }
88
89     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
90
91     if (!pPinImpl)
92         return E_OUTOFMEMORY;
93
94     if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
95     {
96         pPinImpl->pin.lpVtbl = &InputPin_Vtbl;
97         pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
98         
99         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
100         return S_OK;
101     }
102     return E_FAIL;
103 }
104
105 /* Note that we don't init the vtables here (like C++ constructor) */
106 HRESULT InputPin_Init(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, InputPin * pPinImpl)
107 {
108     /* Common attributes */
109     pPinImpl->pin.refCount = 1;
110     pPinImpl->pin.pConnectedTo = NULL;
111     pPinImpl->pin.pQueryAccept = pQueryAccept;
112     pPinImpl->pin.pUserData = pUserData;
113     pPinImpl->pin.pCritSec = pCritSec;
114     InitializeCriticalSection(pPinImpl->pin.pCritSec);
115     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
116
117     /* Input pin attributes */
118     pPinImpl->pSampleProc = pSampleProc;
119     pPinImpl->pAllocator = NULL;
120     pPinImpl->tStart = 0;
121     pPinImpl->tStop = 0;
122     pPinImpl->dRate = 0;
123
124     return S_OK;
125 }
126
127 HRESULT OutputPin_Init(const PIN_INFO * pPinInfo, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, OutputPin * pPinImpl)
128 {
129     /* Common attributes */
130     pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
131     pPinImpl->pin.refCount = 1;
132     pPinImpl->pin.pConnectedTo = NULL;
133     pPinImpl->pin.pQueryAccept = pQueryAccept;
134     pPinImpl->pin.pUserData = pUserData;
135     pPinImpl->pin.pCritSec = pCritSec;
136     InitializeCriticalSection(pPinImpl->pin.pCritSec);
137     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
138
139     /* Output pin attributes */
140     pPinImpl->pMemInputPin = NULL;
141     pPinImpl->pConnectSpecific = OutputPin_ConnectSpecific;
142
143     return S_OK;
144 }
145
146 HRESULT OutputPin_Construct(const PIN_INFO * pPinInfo, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
147 {
148     OutputPin * pPinImpl;
149
150     *ppPin = NULL;
151
152     if (pPinInfo->dir != PINDIR_OUTPUT)
153     {
154         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
155         return E_INVALIDARG;
156     }
157
158     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
159
160     if (!pPinImpl)
161         return E_OUTOFMEMORY;
162
163     if (SUCCEEDED(OutputPin_Init(pPinInfo, pUserData, pQueryAccept, pCritSec, pPinImpl)))
164     {
165         pPinImpl->pin.lpVtbl = &OutputPin_Vtbl;
166         
167         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
168         return S_OK;
169     }
170     return E_FAIL;
171 }
172
173 /*** Common pin functions ***/
174
175 ULONG WINAPI IPinImpl_AddRef(IPin * iface)
176 {
177     ICOM_THIS(IPinImpl, iface);
178     
179     TRACE("()\n");
180     
181     return InterlockedIncrement(&This->refCount);
182 }
183
184 HRESULT WINAPI IPinImpl_Disconnect(IPin * iface)
185 {
186     HRESULT hr;
187     ICOM_THIS(IPinImpl, iface);
188
189     TRACE("()\n");
190
191     EnterCriticalSection(This->pCritSec);
192     {
193         if (This->pConnectedTo)
194         {
195             IPin_Release(This->pConnectedTo);
196             This->pConnectedTo = NULL;
197             hr = S_OK;
198         }
199         else
200             hr = S_FALSE;
201     }
202     LeaveCriticalSection(This->pCritSec);
203     
204     return hr;
205 }
206
207 HRESULT WINAPI IPinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
208 {
209     HRESULT hr = S_OK;
210     ICOM_THIS(IPinImpl, iface);
211
212 /*  TRACE("(%p)\n", ppPin);*/
213
214     EnterCriticalSection(This->pCritSec);
215     {
216         if (!This->pConnectedTo)
217         {
218             *ppPin = NULL;
219             hr = VFW_E_NOT_CONNECTED;
220         }
221         else
222         {
223             *ppPin = This->pConnectedTo;
224             IPin_AddRef(*ppPin);
225         }
226     }
227     LeaveCriticalSection(This->pCritSec);
228
229     return hr;
230 }
231
232 HRESULT WINAPI IPinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
233 {
234     HRESULT hr;
235     ICOM_THIS(IPinImpl, iface);
236
237     TRACE("(%p)\n", pmt);
238
239     EnterCriticalSection(This->pCritSec);
240     {
241         if (This->pConnectedTo)
242         {
243             CopyMediaType(pmt, &This->mtCurrent);
244             hr = S_OK;
245         }
246         else
247         {
248             ZeroMemory(pmt, sizeof(*pmt));
249             hr = VFW_E_NOT_CONNECTED;
250         }
251     }
252     LeaveCriticalSection(This->pCritSec);
253
254     return hr;
255 }
256
257 HRESULT WINAPI IPinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
258 {
259     ICOM_THIS(IPinImpl, iface);
260
261     TRACE("(%p)\n", pInfo);
262
263     Copy_PinInfo(pInfo, &This->pinInfo);
264
265     return S_OK;
266 }
267
268 HRESULT WINAPI IPinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
269 {
270     ICOM_THIS(IPinImpl, iface);
271
272     TRACE("(%p)\n", pPinDir);
273
274     *pPinDir = This->pinInfo.dir;
275
276     return S_OK;
277 }
278
279 HRESULT WINAPI IPinImpl_QueryId(IPin * iface, LPWSTR * Id)
280 {
281     ICOM_THIS(IPinImpl, iface);
282
283     TRACE("(%p)\n", Id);
284
285     *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
286     if (!Id)
287         return E_OUTOFMEMORY;
288
289     strcpyW(*Id, This->pinInfo.achName);
290
291     return S_OK;
292 }
293
294 HRESULT WINAPI IPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
295 {
296     ICOM_THIS(IPinImpl, iface);
297
298     TRACE("(%p)\n", pmt);
299
300     return (This->pQueryAccept(This->pUserData, pmt) == S_OK ? S_OK : S_FALSE);
301 }
302
303 HRESULT WINAPI IPinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
304 {
305     ENUMMEDIADETAILS emd;
306
307     TRACE("(%p)\n", ppEnum);
308
309     /* override this method to allow enumeration of your types */
310     emd.cMediaTypes = 0;
311     emd.pMediaTypes = NULL;
312
313     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
314 }
315
316 HRESULT WINAPI IPinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
317 {
318     TRACE("(%p, %p)\n", apPin, cPin);
319
320     return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
321 }
322
323 /*** IPin implementation for an input pin ***/
324
325 HRESULT WINAPI InputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
326 {
327     ICOM_THIS(InputPin, iface);
328
329     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
330
331     *ppv = NULL;
332
333     if (IsEqualIID(riid, &IID_IUnknown))
334         *ppv = (LPVOID)iface;
335     else if (IsEqualIID(riid, &IID_IPin))
336         *ppv = (LPVOID)iface;
337     else if (IsEqualIID(riid, &IID_IMemInputPin))
338         *ppv = (LPVOID)&This->lpVtblMemInput;
339
340     if (*ppv)
341     {
342         IUnknown_AddRef((IUnknown *)(*ppv));
343         return S_OK;
344     }
345
346     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
347
348     return E_NOINTERFACE;
349 }
350
351 ULONG WINAPI InputPin_Release(IPin * iface)
352 {
353     ICOM_THIS(InputPin, iface);
354     
355     TRACE("()\n");
356     
357     if (!InterlockedDecrement(&This->pin.refCount))
358     {
359         if (This->pAllocator)
360             IMemAllocator_Release(This->pAllocator);
361         CoTaskMemFree(This);
362         return 0;
363     }
364     else
365         return This->pin.refCount;
366 }
367
368 HRESULT WINAPI InputPin_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
369 {
370     ERR("Outgoing connection on an input pin! (%p, %p)\n", pConnector, pmt);
371
372     return E_UNEXPECTED;
373 }
374
375
376 HRESULT WINAPI InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
377 {
378     PIN_DIRECTION pindirReceive;
379     ICOM_THIS(InputPin, iface);
380     HRESULT hr = S_OK;
381
382     TRACE("(%p, %p)\n", pReceivePin, pmt);
383     dump_AM_MEDIA_TYPE(pmt);
384
385     EnterCriticalSection(This->pin.pCritSec);
386     {
387         if (This->pin.pConnectedTo)
388             hr = VFW_E_ALREADY_CONNECTED;
389
390         if (SUCCEEDED(hr) && This->pin.pQueryAccept(This->pin.pUserData, pmt) != S_OK)
391             hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto 
392                                            * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */
393
394         if (SUCCEEDED(hr))
395         {
396             IPin_QueryDirection(pReceivePin, &pindirReceive);
397
398             if (pindirReceive != PINDIR_OUTPUT)
399             {
400                 ERR("Can't connect from non-output pin\n");
401                 hr = VFW_E_INVALID_DIRECTION;
402             }
403         }
404
405         if (SUCCEEDED(hr))
406         {
407             CopyMediaType(&This->pin.mtCurrent, pmt);
408             This->pin.pConnectedTo = pReceivePin;
409             IPin_AddRef(pReceivePin);
410         }
411     }
412     LeaveCriticalSection(This->pin.pCritSec);
413
414     return hr;
415 }
416
417 HRESULT WINAPI InputPin_EndOfStream(IPin * iface)
418 {
419     TRACE("()\n");
420
421     return S_OK;
422 }
423
424 HRESULT WINAPI InputPin_BeginFlush(IPin * iface)
425 {
426     FIXME("()\n");
427     return E_NOTIMPL;
428 }
429
430 HRESULT WINAPI InputPin_EndFlush(IPin * iface)
431 {
432     FIXME("()\n");
433     return E_NOTIMPL;
434 }
435
436 HRESULT WINAPI InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
437 {
438     ICOM_THIS(InputPin, iface);
439
440     TRACE("(%lx%08lx, %lx%08lx, %e)\n", (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
441
442     This->tStart = tStart;
443     This->tStop = tStop;
444     This->dRate = dRate;
445
446     return S_OK;
447 }
448
449 static const IPinVtbl InputPin_Vtbl = 
450 {
451     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
452     InputPin_QueryInterface,
453     IPinImpl_AddRef,
454     InputPin_Release,
455     InputPin_Connect,
456     InputPin_ReceiveConnection,
457     IPinImpl_Disconnect,
458     IPinImpl_ConnectedTo,
459     IPinImpl_ConnectionMediaType,
460     IPinImpl_QueryPinInfo,
461     IPinImpl_QueryDirection,
462     IPinImpl_QueryId,
463     IPinImpl_QueryAccept,
464     IPinImpl_EnumMediaTypes,
465     IPinImpl_QueryInternalConnections,
466     InputPin_EndOfStream,
467     InputPin_BeginFlush,
468     InputPin_EndFlush,
469     InputPin_NewSegment
470 };
471
472 /*** IMemInputPin implementation ***/
473
474 HRESULT WINAPI MemInputPin_QueryInterface(IMemInputPin * iface, REFIID riid, LPVOID * ppv)
475 {
476     ICOM_THIS_From_IMemInputPin(InputPin, iface);
477
478     return IPin_QueryInterface((IPin *)&This->pin, riid, ppv);
479 }
480
481 ULONG WINAPI MemInputPin_AddRef(IMemInputPin * iface)
482 {
483     ICOM_THIS_From_IMemInputPin(InputPin, iface);
484
485     return IPin_AddRef((IPin *)&This->pin);
486 }
487
488 ULONG WINAPI MemInputPin_Release(IMemInputPin * iface)
489 {
490     ICOM_THIS_From_IMemInputPin(InputPin, iface);
491
492     return IPin_Release((IPin *)&This->pin);
493 }
494
495 HRESULT WINAPI MemInputPin_GetAllocator(IMemInputPin * iface, IMemAllocator ** ppAllocator)
496 {
497     ICOM_THIS_From_IMemInputPin(InputPin, iface);
498
499     TRACE("MemInputPin_GetAllocator()\n");
500
501     *ppAllocator = This->pAllocator;
502     if (*ppAllocator)
503         IMemAllocator_AddRef(*ppAllocator);
504     
505     return *ppAllocator ? S_OK : VFW_E_NO_ALLOCATOR;
506 }
507
508 HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator * pAllocator, BOOL bReadOnly)
509 {
510     ICOM_THIS_From_IMemInputPin(InputPin, iface);
511
512     TRACE("MemInputPin_NotifyAllocator()\n");
513
514     if (This->pAllocator)
515         IMemAllocator_Release(This->pAllocator);
516     This->pAllocator = pAllocator;
517     if (This->pAllocator)
518         IMemAllocator_AddRef(This->pAllocator);
519
520     return S_OK;
521 }
522
523 HRESULT WINAPI MemInputPin_GetAllocatorRequirements(IMemInputPin * iface, ALLOCATOR_PROPERTIES * pProps)
524 {
525     TRACE("(%p)\n", pProps);
526
527     /* override this method if you have any specific requirements */
528
529     return E_NOTIMPL;
530 }
531
532 HRESULT WINAPI MemInputPin_Receive(IMemInputPin * iface, IMediaSample * pSample)
533 {
534     ICOM_THIS_From_IMemInputPin(InputPin, iface);
535
536     /* this trace commented out for performance reasons */
537 /*  TRACE("(%p)\n", pSample);*/
538
539     return This->pSampleProc(This->pin.pUserData, pSample);
540 }
541
542 HRESULT WINAPI MemInputPin_ReceiveMultiple(IMemInputPin * iface, IMediaSample ** pSamples, long nSamples, long *nSamplesProcessed)
543 {
544     HRESULT hr = S_OK;
545     TRACE("(%p, %ld, %p)\n", pSamples, nSamples, nSamplesProcessed);
546
547     for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; (*nSamplesProcessed)++)
548     {
549         hr = IMemInputPin_Receive(iface, pSamples[*nSamplesProcessed]);
550         if (hr != S_OK)
551             break;
552     }
553
554     return hr;
555 }
556
557 HRESULT WINAPI MemInputPin_ReceiveCanBlock(IMemInputPin * iface)
558 {
559     FIXME("()\n");
560
561     /* FIXME: we should check whether any output pins will block */
562
563     return S_OK;
564 }
565
566 static const IMemInputPinVtbl MemInputPin_Vtbl = 
567 {
568     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
569     MemInputPin_QueryInterface,
570     MemInputPin_AddRef,
571     MemInputPin_Release,
572     MemInputPin_GetAllocator,
573     MemInputPin_NotifyAllocator,
574     MemInputPin_GetAllocatorRequirements,
575     MemInputPin_Receive,
576     MemInputPin_ReceiveMultiple,
577     MemInputPin_ReceiveCanBlock
578 };
579
580 HRESULT WINAPI OutputPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
581 {
582     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
583
584     *ppv = NULL;
585
586     if (IsEqualIID(riid, &IID_IUnknown))
587         *ppv = (LPVOID)iface;
588     else if (IsEqualIID(riid, &IID_IPin))
589         *ppv = (LPVOID)iface;
590
591     if (*ppv)
592     {
593         IUnknown_AddRef((IUnknown *)(*ppv));
594         return S_OK;
595     }
596
597     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
598
599     return E_NOINTERFACE;
600 }
601
602 ULONG WINAPI OutputPin_Release(IPin * iface)
603 {
604     ICOM_THIS(OutputPin, iface);
605     
606     TRACE("()\n");
607     
608     if (!InterlockedDecrement(&This->pin.refCount))
609     {
610         CoTaskMemFree(This);
611         return 0;
612     }
613     return This->pin.refCount;
614 }
615
616 HRESULT WINAPI OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
617 {
618     HRESULT hr;
619     ICOM_THIS(OutputPin, iface);
620
621     TRACE("(%p, %p)\n", pReceivePin, pmt);
622     dump_AM_MEDIA_TYPE(pmt);
623
624     /* If we try to connect to ourself, we will definitely deadlock.
625      * There are other cases where we could deadlock too, but this
626      * catches the obvious case */
627     assert(pReceivePin != iface);
628
629     EnterCriticalSection(This->pin.pCritSec);
630     {
631         /* get the IMemInputPin interface we will use to deliver samples to the
632          * connected pin */
633         hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID *)&This->pMemInputPin);
634
635         /* if we have been a specific type to connect with, then we can either connect
636          * with that or fail. We cannot choose different AM_MEDIA_TYPE */
637         if (SUCCEEDED(hr))
638         {
639             if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
640                 hr = This->pConnectSpecific(iface, pReceivePin, pmt);
641             else
642             {
643                 /* negotiate media type */
644
645                 IEnumMediaTypes * pEnumCandidates;
646                 AM_MEDIA_TYPE * pmtCandidate; /* Candidate media type */
647
648                 if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
649                 {
650                     hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
651
652                     /* try this filter's media types first */
653                     while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
654                     {
655                         if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 
656                             (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
657                         {
658                             hr = S_OK;
659                             CoTaskMemFree(pmtCandidate);
660                             break;
661                         }
662                         CoTaskMemFree(pmtCandidate);
663                     }
664                     IEnumMediaTypes_Release(pEnumCandidates);
665                 }
666
667                 /* then try receiver filter's media types */
668                 if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
669                 {
670                     while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
671                     {
672                         if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) && 
673                             (This->pConnectSpecific(iface, pReceivePin, pmtCandidate) == S_OK))
674                         {
675                             hr = S_OK;
676                             CoTaskMemFree(pmtCandidate);
677                             break;
678                         }
679                         CoTaskMemFree(pmtCandidate);
680                     } /* while */
681                     IEnumMediaTypes_Release(pEnumCandidates);
682                 } /* if not found */
683             } /* if negotiate media type */
684         } /* if succeeded */
685     } /* if succeeded */
686     LeaveCriticalSection(This->pin.pCritSec);
687
688     return hr;
689 }
690
691 HRESULT WINAPI OutputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
692 {
693     ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
694
695     return E_UNEXPECTED;
696 }
697
698 HRESULT WINAPI OutputPin_Disconnect(IPin * iface)
699 {
700     HRESULT hr;
701     ICOM_THIS(OutputPin, iface);
702
703     TRACE("()\n");
704
705     EnterCriticalSection(This->pin.pCritSec);
706     {
707         if (This->pMemInputPin)
708         {
709             IMemInputPin_Release(This->pMemInputPin);
710             This->pMemInputPin = NULL;
711         }
712         if (This->pin.pConnectedTo)
713         {
714             IPin_Release(This->pin.pConnectedTo);
715             This->pin.pConnectedTo = NULL;
716             hr = S_OK;
717         }
718         else
719             hr = S_FALSE;
720     }
721     LeaveCriticalSection(This->pin.pCritSec);
722     
723     return hr;
724 }
725
726 HRESULT WINAPI OutputPin_EndOfStream(IPin * iface)
727 {
728     TRACE("()\n");
729
730     /* not supposed to do anything in an output pin */
731
732     return E_UNEXPECTED;
733 }
734
735 HRESULT WINAPI OutputPin_BeginFlush(IPin * iface)
736 {
737     TRACE("()\n");
738
739     /* not supposed to do anything in an output pin */
740
741     return E_UNEXPECTED;
742 }
743
744 HRESULT WINAPI OutputPin_EndFlush(IPin * iface)
745 {
746     TRACE("()\n");
747
748     /* not supposed to do anything in an output pin */
749
750     return E_UNEXPECTED;
751 }
752
753 HRESULT WINAPI OutputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
754 {
755     TRACE("(%lx%08lx, %lx%08lx, %e)\n", (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
756
757     /* not supposed to do anything in an output pin */
758
759     return E_UNEXPECTED;
760 }
761
762 static const IPinVtbl OutputPin_Vtbl = 
763 {
764     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
765     OutputPin_QueryInterface,
766     IPinImpl_AddRef,
767     OutputPin_Release,
768     OutputPin_Connect,
769     OutputPin_ReceiveConnection,
770     OutputPin_Disconnect,
771     IPinImpl_ConnectedTo,
772     IPinImpl_ConnectionMediaType,
773     IPinImpl_QueryPinInfo,
774     IPinImpl_QueryDirection,
775     IPinImpl_QueryId,
776     IPinImpl_QueryAccept,
777     IPinImpl_EnumMediaTypes,
778     IPinImpl_QueryInternalConnections,
779     OutputPin_EndOfStream,
780     OutputPin_BeginFlush,
781     OutputPin_EndFlush,
782     OutputPin_NewSegment
783 };