xref: /reactos/sdk/lib/3rdparty/strmbase/pin.c (revision cc439606)
1 /*
2  * Generic Implementation of IPin Interface
3  *
4  * Copyright 2003 Robert Shearman
5  * Copyright 2010 Aric Stewart, CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #define COBJMACROS
23 
24 #include "dshow.h"
25 #include "wine/debug.h"
26 #include "wine/unicode.h"
27 #include "wine/strmbase.h"
28 #include "uuids.h"
29 #include "vfwmsgs.h"
30 #include <assert.h>
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
33 
34 static const IMemInputPinVtbl MemInputPin_Vtbl;
35 
36 typedef HRESULT (*SendPinFunc)( IPin *to, LPVOID arg );
37 
38 static inline BasePin *impl_from_IPin( IPin *iface )
39 {
40     return CONTAINING_RECORD(iface, BasePin, IPin_iface);
41 }
42 
43 /** Helper function, there are a lot of places where the error code is inherited
44  * The following rules apply:
45  *
46  * Return the first received error code (E_NOTIMPL is ignored)
47  * If no errors occur: return the first received non-error-code that isn't S_OK
48  */
49 static HRESULT updatehres( HRESULT original, HRESULT new )
50 {
51     if (FAILED( original ) || new == E_NOTIMPL)
52         return original;
53 
54     if (FAILED( new ) || original == S_OK)
55         return new;
56 
57     return original;
58 }
59 
60 /** Sends a message from a pin further to other, similar pins
61  * fnMiddle is called on each pin found further on the stream.
62  * fnEnd (can be NULL) is called when the message can't be sent any further (this is a renderer or source)
63  *
64  * If the pin given is an input pin, the message will be sent downstream to other input pins
65  * If the pin given is an output pin, the message will be sent upstream to other output pins
66  */
67 static HRESULT SendFurther( IPin *from, SendPinFunc fnMiddle, LPVOID arg, SendPinFunc fnEnd )
68 {
69     PIN_INFO pin_info;
70     ULONG amount = 0;
71     HRESULT hr = S_OK;
72     HRESULT hr_return = S_OK;
73     IEnumPins *enumpins = NULL;
74     BOOL foundend = TRUE;
75     PIN_DIRECTION from_dir;
76 
77     IPin_QueryDirection( from, &from_dir );
78 
79     hr = IPin_QueryInternalConnections( from, NULL, &amount );
80     if (hr != E_NOTIMPL && amount)
81         FIXME("Use QueryInternalConnections!\n");
82 
83     pin_info.pFilter = NULL;
84     hr = IPin_QueryPinInfo( from, &pin_info );
85     if (FAILED(hr))
86         goto out;
87 
88     hr = IBaseFilter_EnumPins( pin_info.pFilter, &enumpins );
89     if (FAILED(hr))
90         goto out;
91 
92     hr = IEnumPins_Reset( enumpins );
93     while (hr == S_OK) {
94         IPin *pin = NULL;
95         hr = IEnumPins_Next( enumpins, 1, &pin, NULL );
96         if (hr == VFW_E_ENUM_OUT_OF_SYNC)
97         {
98             hr = IEnumPins_Reset( enumpins );
99             continue;
100         }
101         if (pin)
102         {
103             PIN_DIRECTION dir;
104 
105             IPin_QueryDirection( pin, &dir );
106             if (dir != from_dir)
107             {
108                 IPin *connected = NULL;
109 
110                 foundend = FALSE;
111                 IPin_ConnectedTo( pin, &connected );
112                 if (connected)
113                 {
114                     HRESULT hr_local;
115 
116                     hr_local = fnMiddle( connected, arg );
117                     hr_return = updatehres( hr_return, hr_local );
118                     IPin_Release(connected);
119                 }
120             }
121             IPin_Release( pin );
122         }
123         else
124         {
125             hr = S_OK;
126             break;
127         }
128     }
129 
130     if (!foundend)
131         hr = hr_return;
132     else if (fnEnd) {
133         HRESULT hr_local;
134 
135         hr_local = fnEnd( from, arg );
136         hr_return = updatehres( hr_return, hr_local );
137     }
138     IEnumPins_Release(enumpins);
139 
140 out:
141     if (pin_info.pFilter)
142         IBaseFilter_Release( pin_info.pFilter );
143     return hr;
144 }
145 
146 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
147 {
148     /* Tempting to just do a memcpy, but the name field is
149        128 characters long! We will probably never exceed 10
150        most of the time, so we are better off copying
151        each field manually */
152     strcpyW(pDest->achName, pSrc->achName);
153     pDest->dir = pSrc->dir;
154     pDest->pFilter = pSrc->pFilter;
155 }
156 
157 static void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt)
158 {
159     if (!pmt)
160         return;
161     TRACE("\t%s\n\t%s\n\t...\n\t%s\n", debugstr_guid(&pmt->majortype), debugstr_guid(&pmt->subtype), debugstr_guid(&pmt->formattype));
162 }
163 
164 static BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
165 {
166     TRACE("pmt1: ");
167     dump_AM_MEDIA_TYPE(pmt1);
168     TRACE("pmt2: ");
169     dump_AM_MEDIA_TYPE(pmt2);
170     return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) &&
171             ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL)   || IsEqualGUID(&pmt2->subtype, &GUID_NULL)))   || IsEqualGUID(&pmt1->subtype, &pmt2->subtype)));
172 }
173 
174 /*** Common Base Pin function */
175 HRESULT WINAPI BasePinImpl_GetMediaType(BasePin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
176 {
177     if (iPosition < 0)
178         return E_INVALIDARG;
179     return VFW_S_NO_MORE_ITEMS;
180 }
181 
182 LONG WINAPI BasePinImpl_GetMediaTypeVersion(BasePin *iface)
183 {
184     return 1;
185 }
186 
187 ULONG WINAPI BasePinImpl_AddRef(IPin * iface)
188 {
189     BasePin *This = impl_from_IPin(iface);
190     ULONG refCount = InterlockedIncrement(&This->refCount);
191 
192     TRACE("(%p)->() AddRef from %d\n", iface, refCount - 1);
193 
194     return refCount;
195 }
196 
197 HRESULT WINAPI BasePinImpl_Disconnect(IPin * iface)
198 {
199     HRESULT hr;
200     BasePin *This = impl_from_IPin(iface);
201 
202     TRACE("()\n");
203 
204     EnterCriticalSection(This->pCritSec);
205     {
206         if (This->pConnectedTo)
207         {
208             IPin_Release(This->pConnectedTo);
209             This->pConnectedTo = NULL;
210             FreeMediaType(&This->mtCurrent);
211             ZeroMemory(&This->mtCurrent, sizeof(This->mtCurrent));
212             hr = S_OK;
213         }
214         else
215             hr = S_FALSE;
216     }
217     LeaveCriticalSection(This->pCritSec);
218 
219     return hr;
220 }
221 
222 HRESULT WINAPI BasePinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
223 {
224     HRESULT hr;
225     BasePin *This = impl_from_IPin(iface);
226 
227     TRACE("(%p)\n", ppPin);
228 
229     EnterCriticalSection(This->pCritSec);
230     {
231         if (This->pConnectedTo)
232         {
233             *ppPin = This->pConnectedTo;
234             IPin_AddRef(*ppPin);
235             hr = S_OK;
236         }
237         else
238         {
239             hr = VFW_E_NOT_CONNECTED;
240             *ppPin = NULL;
241         }
242     }
243     LeaveCriticalSection(This->pCritSec);
244 
245     return hr;
246 }
247 
248 HRESULT WINAPI BasePinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
249 {
250     HRESULT hr;
251     BasePin *This = impl_from_IPin(iface);
252 
253     TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
254 
255     EnterCriticalSection(This->pCritSec);
256     {
257         if (This->pConnectedTo)
258         {
259             CopyMediaType(pmt, &This->mtCurrent);
260             hr = S_OK;
261         }
262         else
263         {
264             ZeroMemory(pmt, sizeof(*pmt));
265             hr = VFW_E_NOT_CONNECTED;
266         }
267     }
268     LeaveCriticalSection(This->pCritSec);
269 
270     return hr;
271 }
272 
273 HRESULT WINAPI BasePinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
274 {
275     BasePin *This = impl_from_IPin(iface);
276 
277     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
278 
279     Copy_PinInfo(pInfo, &This->pinInfo);
280     IBaseFilter_AddRef(pInfo->pFilter);
281 
282     return S_OK;
283 }
284 
285 HRESULT WINAPI BasePinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
286 {
287     BasePin *This = impl_from_IPin(iface);
288 
289     TRACE("(%p/%p)->(%p)\n", This, iface, pPinDir);
290 
291     *pPinDir = This->pinInfo.dir;
292 
293     return S_OK;
294 }
295 
296 HRESULT WINAPI BasePinImpl_QueryId(IPin * iface, LPWSTR * Id)
297 {
298     BasePin *This = impl_from_IPin(iface);
299 
300     TRACE("(%p/%p)->(%p)\n", This, iface, Id);
301 
302     *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
303     if (!*Id)
304         return E_OUTOFMEMORY;
305 
306     strcpyW(*Id, This->pinInfo.achName);
307 
308     return S_OK;
309 }
310 
311 HRESULT WINAPI BasePinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
312 {
313     TRACE("(%p)->(%p)\n", iface, pmt);
314 
315     return S_OK;
316 }
317 
318 HRESULT WINAPI BasePinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
319 {
320     BasePin *This = impl_from_IPin(iface);
321 
322     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
323 
324     /* override this method to allow enumeration of your types */
325 
326     return EnumMediaTypes_Construct(This, This->pFuncsTable->pfnGetMediaType, This->pFuncsTable->pfnGetMediaTypeVersion , ppEnum);
327 }
328 
329 HRESULT WINAPI BasePinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
330 {
331     BasePin *This = impl_from_IPin(iface);
332 
333     TRACE("(%p/%p)->(%p, %p)\n", This, iface, apPin, cPin);
334 
335     return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
336 }
337 
338 HRESULT WINAPI BasePinImpl_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
339 {
340     BasePin *This = impl_from_IPin(iface);
341 
342     TRACE("(%s, %s, %e)\n", wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate);
343 
344     This->tStart = tStart;
345     This->tStop = tStop;
346     This->dRate = dRate;
347 
348     return S_OK;
349 }
350 
351 /*** OutputPin implementation ***/
352 
353 static inline BaseOutputPin *impl_BaseOutputPin_from_IPin( IPin *iface )
354 {
355     return CONTAINING_RECORD(iface, BaseOutputPin, pin.IPin_iface);
356 }
357 
358 static inline BaseOutputPin *impl_BaseOutputPin_from_BasePin( BasePin *iface )
359 {
360     return CONTAINING_RECORD(iface, BaseOutputPin, pin);
361 }
362 
363 HRESULT WINAPI BaseOutputPinImpl_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
364 {
365     BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface);
366 
367     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
368 
369     *ppv = NULL;
370 
371     if (IsEqualIID(riid, &IID_IUnknown))
372         *ppv = iface;
373     else if (IsEqualIID(riid, &IID_IPin))
374         *ppv = iface;
375     else if (IsEqualIID(riid, &IID_IMediaSeeking) ||
376              IsEqualIID(riid, &IID_IQualityControl))
377     {
378         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, riid, ppv);
379     }
380 
381     if (*ppv)
382     {
383         IUnknown_AddRef((IUnknown *)(*ppv));
384         return S_OK;
385     }
386 
387     FIXME("No interface for %s!\n", debugstr_guid(riid));
388 
389     return E_NOINTERFACE;
390 }
391 
392 ULONG WINAPI BaseOutputPinImpl_Release(IPin * iface)
393 {
394     BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface);
395     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
396 
397     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
398 
399     if (!refCount)
400         BaseOutputPin_Destroy(This);
401 
402     return refCount;
403 }
404 
405 HRESULT WINAPI BaseOutputPinImpl_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
406 {
407     HRESULT hr;
408     BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface);
409 
410     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
411     dump_AM_MEDIA_TYPE(pmt);
412 
413     if (!pReceivePin)
414         return E_POINTER;
415 
416     /* If we try to connect to ourselves, we will definitely deadlock.
417      * There are other cases where we could deadlock too, but this
418      * catches the obvious case */
419     assert(pReceivePin != iface);
420 
421     EnterCriticalSection(This->pin.pCritSec);
422     {
423         /* if we have been a specific type to connect with, then we can either connect
424          * with that or fail. We cannot choose different AM_MEDIA_TYPE */
425         if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
426             hr = This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmt);
427         else
428         {
429             /* negotiate media type */
430 
431             IEnumMediaTypes * pEnumCandidates;
432             AM_MEDIA_TYPE * pmtCandidate = NULL; /* Candidate media type */
433 
434             if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
435             {
436                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
437 
438                 /* try this filter's media types first */
439                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
440                 {
441                     assert(pmtCandidate);
442                     dump_AM_MEDIA_TYPE(pmtCandidate);
443                     if (!IsEqualGUID(&FORMAT_None, &pmtCandidate->formattype)
444                         && !IsEqualGUID(&GUID_NULL, &pmtCandidate->formattype))
445                         assert(pmtCandidate->pbFormat);
446                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
447                         (This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmtCandidate) == S_OK))
448                     {
449                         hr = S_OK;
450                         DeleteMediaType(pmtCandidate);
451                         break;
452                     }
453                     DeleteMediaType(pmtCandidate);
454                     pmtCandidate = NULL;
455                 }
456                 IEnumMediaTypes_Release(pEnumCandidates);
457             }
458 
459             /* then try receiver filter's media types */
460             if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
461             {
462                 ULONG fetched;
463 
464                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
465 
466                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, &fetched))
467                 {
468                     assert(pmtCandidate);
469                     dump_AM_MEDIA_TYPE(pmtCandidate);
470                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
471                         (This->pin.pFuncsTable->pfnAttemptConnection(&This->pin, pReceivePin, pmtCandidate) == S_OK))
472                     {
473                         hr = S_OK;
474                         DeleteMediaType(pmtCandidate);
475                         break;
476                     }
477                     DeleteMediaType(pmtCandidate);
478                     pmtCandidate = NULL;
479                 } /* while */
480                 IEnumMediaTypes_Release(pEnumCandidates);
481             } /* if not found */
482         } /* if negotiate media type */
483     } /* if succeeded */
484     LeaveCriticalSection(This->pin.pCritSec);
485 
486     TRACE(" -- %x\n", hr);
487     return hr;
488 }
489 
490 HRESULT WINAPI BaseOutputPinImpl_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
491 {
492     ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
493 
494     return E_UNEXPECTED;
495 }
496 
497 HRESULT WINAPI BaseOutputPinImpl_Disconnect(IPin * iface)
498 {
499     HRESULT hr;
500     BaseOutputPin *This = impl_BaseOutputPin_from_IPin(iface);
501 
502     TRACE("()\n");
503 
504     EnterCriticalSection(This->pin.pCritSec);
505     {
506         if (This->pMemInputPin)
507         {
508             IMemInputPin_Release(This->pMemInputPin);
509             This->pMemInputPin = NULL;
510         }
511         if (This->pin.pConnectedTo)
512         {
513             IPin_Release(This->pin.pConnectedTo);
514             This->pin.pConnectedTo = NULL;
515             FreeMediaType(&This->pin.mtCurrent);
516             ZeroMemory(&This->pin.mtCurrent, sizeof(This->pin.mtCurrent));
517             hr = S_OK;
518         }
519         else
520             hr = S_FALSE;
521     }
522     LeaveCriticalSection(This->pin.pCritSec);
523 
524     return hr;
525 }
526 
527 HRESULT WINAPI BaseOutputPinImpl_EndOfStream(IPin * iface)
528 {
529     TRACE("()\n");
530 
531     /* not supposed to do anything in an output pin */
532 
533     return E_UNEXPECTED;
534 }
535 
536 HRESULT WINAPI BaseOutputPinImpl_BeginFlush(IPin * iface)
537 {
538     TRACE("(%p)->()\n", iface);
539 
540     /* not supposed to do anything in an output pin */
541 
542     return E_UNEXPECTED;
543 }
544 
545 HRESULT WINAPI BaseOutputPinImpl_EndFlush(IPin * iface)
546 {
547     TRACE("(%p)->()\n", iface);
548 
549     /* not supposed to do anything in an output pin */
550 
551     return E_UNEXPECTED;
552 }
553 
554 HRESULT WINAPI BaseOutputPinImpl_GetDeliveryBuffer(BaseOutputPin *This, IMediaSample ** ppSample, REFERENCE_TIME * tStart, REFERENCE_TIME * tStop, DWORD dwFlags)
555 {
556     HRESULT hr;
557 
558     TRACE("(%p, %p, %p, %x)\n", ppSample, tStart, tStop, dwFlags);
559 
560     if (!This->pin.pConnectedTo)
561         hr = VFW_E_NOT_CONNECTED;
562     else
563     {
564         hr = IMemAllocator_GetBuffer(This->pAllocator, ppSample, tStart, tStop, dwFlags);
565 
566         if (SUCCEEDED(hr))
567             hr = IMediaSample_SetTime(*ppSample, tStart, tStop);
568     }
569 
570     return hr;
571 }
572 
573 /* replaces OutputPin_SendSample */
574 HRESULT WINAPI BaseOutputPinImpl_Deliver(BaseOutputPin *This, IMediaSample * pSample)
575 {
576     IMemInputPin * pMemConnected = NULL;
577     PIN_INFO pinInfo;
578     HRESULT hr;
579 
580     EnterCriticalSection(This->pin.pCritSec);
581     {
582         if (!This->pin.pConnectedTo || !This->pMemInputPin)
583             hr = VFW_E_NOT_CONNECTED;
584         else
585         {
586             /* we don't have the lock held when using This->pMemInputPin,
587              * so we need to AddRef it to stop it being deleted while we are
588              * using it. Same with its filter. */
589             pMemConnected = This->pMemInputPin;
590             IMemInputPin_AddRef(pMemConnected);
591             hr = IPin_QueryPinInfo(This->pin.pConnectedTo, &pinInfo);
592         }
593     }
594     LeaveCriticalSection(This->pin.pCritSec);
595 
596     if (SUCCEEDED(hr))
597     {
598         /* NOTE: if we are in a critical section when Receive is called
599          * then it causes some problems (most notably with the native Video
600          * Renderer) if we are re-entered for whatever reason */
601         hr = IMemInputPin_Receive(pMemConnected, pSample);
602 
603         /* If the filter's destroyed, tell upstream to stop sending data */
604         if(IBaseFilter_Release(pinInfo.pFilter) == 0 && SUCCEEDED(hr))
605             hr = S_FALSE;
606     }
607     if (pMemConnected)
608         IMemInputPin_Release(pMemConnected);
609 
610     return hr;
611 }
612 
613 /* replaces OutputPin_CommitAllocator */
614 HRESULT WINAPI BaseOutputPinImpl_Active(BaseOutputPin *This)
615 {
616     HRESULT hr;
617 
618     TRACE("(%p)->()\n", This);
619 
620     EnterCriticalSection(This->pin.pCritSec);
621     {
622         if (!This->pin.pConnectedTo || !This->pMemInputPin)
623             hr = VFW_E_NOT_CONNECTED;
624         else
625             hr = IMemAllocator_Commit(This->pAllocator);
626     }
627     LeaveCriticalSection(This->pin.pCritSec);
628 
629     TRACE("--> %08x\n", hr);
630     return hr;
631 }
632 
633 /* replaces OutputPin_DecommitAllocator */
634 HRESULT WINAPI BaseOutputPinImpl_Inactive(BaseOutputPin *This)
635 {
636     HRESULT hr;
637 
638     TRACE("(%p)->()\n", This);
639 
640     EnterCriticalSection(This->pin.pCritSec);
641     {
642         if (!This->pin.pConnectedTo || !This->pMemInputPin)
643             hr = VFW_E_NOT_CONNECTED;
644         else
645             hr = IMemAllocator_Decommit(This->pAllocator);
646     }
647     LeaveCriticalSection(This->pin.pCritSec);
648 
649     TRACE("--> %08x\n", hr);
650     return hr;
651 }
652 
653 /* replaces OutputPin_DeliverDisconnect */
654 HRESULT WINAPI BaseOutputPinImpl_BreakConnect(BaseOutputPin *This)
655 {
656     HRESULT hr;
657 
658     TRACE("(%p)->()\n", This);
659 
660     EnterCriticalSection(This->pin.pCritSec);
661     {
662         if (!This->pin.pConnectedTo || !This->pMemInputPin)
663             hr = VFW_E_NOT_CONNECTED;
664         else
665         {
666             hr = IMemAllocator_Decommit(This->pAllocator);
667 
668             if (SUCCEEDED(hr))
669                 hr = IPin_Disconnect(This->pin.pConnectedTo);
670         }
671         IPin_Disconnect(&This->pin.IPin_iface);
672     }
673     LeaveCriticalSection(This->pin.pCritSec);
674 
675     return hr;
676 }
677 
678 HRESULT WINAPI BaseOutputPinImpl_InitAllocator(BaseOutputPin *This, IMemAllocator **pMemAlloc)
679 {
680     return CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)pMemAlloc);
681 }
682 
683 HRESULT WINAPI BaseOutputPinImpl_DecideAllocator(BaseOutputPin *This, IMemInputPin *pPin, IMemAllocator **pAlloc)
684 {
685     HRESULT hr;
686 
687     hr = IMemInputPin_GetAllocator(pPin, pAlloc);
688 
689     if (hr == VFW_E_NO_ALLOCATOR)
690         /* Input pin provides no allocator, use standard memory allocator */
691         hr = BaseOutputPinImpl_InitAllocator(This, pAlloc);
692 
693     if (SUCCEEDED(hr))
694     {
695         ALLOCATOR_PROPERTIES rProps;
696         ZeroMemory(&rProps, sizeof(ALLOCATOR_PROPERTIES));
697 
698         IMemInputPin_GetAllocatorRequirements(pPin, &rProps);
699         hr = This->pFuncsTable->pfnDecideBufferSize(This, *pAlloc, &rProps);
700     }
701 
702     if (SUCCEEDED(hr))
703         hr = IMemInputPin_NotifyAllocator(pPin, *pAlloc, FALSE);
704 
705     return hr;
706 }
707 
708 /*** The Construct functions ***/
709 
710 /* Function called as a helper to IPin_Connect */
711 /* specific AM_MEDIA_TYPE - it cannot be NULL */
712 HRESULT WINAPI BaseOutputPinImpl_AttemptConnection(BasePin* iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
713 {
714     BaseOutputPin *This = impl_BaseOutputPin_from_BasePin(iface);
715     HRESULT hr;
716     IMemAllocator * pMemAlloc = NULL;
717 
718     TRACE("(%p, %p)\n", pReceivePin, pmt);
719     dump_AM_MEDIA_TYPE(pmt);
720 
721     /* FIXME: call queryacceptproc */
722 
723     This->pin.pConnectedTo = pReceivePin;
724     IPin_AddRef(pReceivePin);
725     CopyMediaType(&This->pin.mtCurrent, pmt);
726 
727     hr = IPin_ReceiveConnection(pReceivePin, &iface->IPin_iface, pmt);
728 
729     /* get the IMemInputPin interface we will use to deliver samples to the
730      * connected pin */
731     if (SUCCEEDED(hr))
732     {
733         This->pMemInputPin = NULL;
734         hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin);
735 
736         if (SUCCEEDED(hr))
737         {
738             hr = This->pFuncsTable->pfnDecideAllocator(This, This->pMemInputPin, &pMemAlloc);
739             if (SUCCEEDED(hr))
740                 This->pAllocator = pMemAlloc;
741             else if (pMemAlloc)
742                 IMemAllocator_Release(pMemAlloc);
743         }
744 
745         /* break connection if we couldn't get the allocator */
746         if (FAILED(hr))
747         {
748             if (This->pMemInputPin)
749                 IMemInputPin_Release(This->pMemInputPin);
750             This->pMemInputPin = NULL;
751 
752             IPin_Disconnect(pReceivePin);
753         }
754     }
755 
756     if (FAILED(hr))
757     {
758         IPin_Release(This->pin.pConnectedTo);
759         This->pin.pConnectedTo = NULL;
760         FreeMediaType(&This->pin.mtCurrent);
761     }
762 
763     TRACE(" -- %x\n", hr);
764     return hr;
765 }
766 
767 static HRESULT OutputPin_Init(const IPinVtbl *OutputPin_Vtbl, const PIN_INFO * pPinInfo, const BaseOutputPinFuncTable* vtbl,  LPCRITICAL_SECTION pCritSec, BaseOutputPin * pPinImpl)
768 {
769     TRACE("\n");
770 
771     /* Common attributes */
772     pPinImpl->pin.IPin_iface.lpVtbl = OutputPin_Vtbl;
773     pPinImpl->pin.refCount = 1;
774     pPinImpl->pin.pConnectedTo = NULL;
775     pPinImpl->pin.pCritSec = pCritSec;
776     pPinImpl->pin.tStart = 0;
777     pPinImpl->pin.tStop = 0;
778     pPinImpl->pin.dRate = 1.0;
779     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
780     pPinImpl->pin.pFuncsTable = &vtbl->base;
781     ZeroMemory(&pPinImpl->pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
782 
783     /* Output pin attributes */
784     pPinImpl->pMemInputPin = NULL;
785     pPinImpl->pAllocator = NULL;
786     pPinImpl->pFuncsTable = vtbl;
787 
788     return S_OK;
789 }
790 
791 HRESULT WINAPI BaseOutputPin_Construct(const IPinVtbl *OutputPin_Vtbl, LONG outputpin_size, const PIN_INFO * pPinInfo, const BaseOutputPinFuncTable* vtbl, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
792 {
793     BaseOutputPin * pPinImpl;
794 
795     *ppPin = NULL;
796 
797     if (pPinInfo->dir != PINDIR_OUTPUT)
798     {
799         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
800         return E_INVALIDARG;
801     }
802 
803     assert(outputpin_size >= sizeof(BaseOutputPin));
804     assert(vtbl->base.pfnAttemptConnection);
805 
806     pPinImpl = CoTaskMemAlloc(outputpin_size);
807 
808     if (!pPinImpl)
809         return E_OUTOFMEMORY;
810 
811     if (SUCCEEDED(OutputPin_Init(OutputPin_Vtbl, pPinInfo, vtbl, pCritSec, pPinImpl)))
812     {
813         *ppPin = &pPinImpl->pin.IPin_iface;
814         return S_OK;
815     }
816 
817     CoTaskMemFree(pPinImpl);
818     return E_FAIL;
819 }
820 
821 HRESULT WINAPI BaseOutputPin_Destroy(BaseOutputPin *This)
822 {
823     FreeMediaType(&This->pin.mtCurrent);
824     if (This->pAllocator)
825         IMemAllocator_Release(This->pAllocator);
826     This->pAllocator = NULL;
827     CoTaskMemFree(This);
828     return S_OK;
829 }
830 
831 /*** Input Pin implementation ***/
832 
833 static inline BaseInputPin *impl_BaseInputPin_from_IPin( IPin *iface )
834 {
835     return CONTAINING_RECORD(iface, BaseInputPin, pin.IPin_iface);
836 }
837 
838 HRESULT WINAPI BaseInputPinImpl_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
839 {
840     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
841 
842     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
843 
844     *ppv = NULL;
845 
846     if (IsEqualIID(riid, &IID_IUnknown))
847         *ppv = iface;
848     else if (IsEqualIID(riid, &IID_IPin))
849         *ppv = iface;
850     else if (IsEqualIID(riid, &IID_IMemInputPin))
851         *ppv = &This->IMemInputPin_iface;
852     else if (IsEqualIID(riid, &IID_IMediaSeeking))
853     {
854         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
855     }
856 
857     if (*ppv)
858     {
859         IUnknown_AddRef((IUnknown *)(*ppv));
860         return S_OK;
861     }
862 
863     FIXME("No interface for %s!\n", debugstr_guid(riid));
864 
865     return E_NOINTERFACE;
866 }
867 
868 ULONG WINAPI BaseInputPinImpl_Release(IPin * iface)
869 {
870     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
871     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
872 
873     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
874 
875     if (!refCount)
876         BaseInputPin_Destroy(This);
877 
878     return refCount;
879 }
880 
881 HRESULT WINAPI BaseInputPinImpl_Connect(IPin * iface, IPin * pConnector, const AM_MEDIA_TYPE * pmt)
882 {
883     ERR("Outgoing connection on an input pin! (%p, %p)\n", pConnector, pmt);
884 
885     return E_UNEXPECTED;
886 }
887 
888 
889 HRESULT WINAPI BaseInputPinImpl_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
890 {
891     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
892     PIN_DIRECTION pindirReceive;
893     HRESULT hr = S_OK;
894 
895     TRACE("(%p, %p)\n", pReceivePin, pmt);
896     dump_AM_MEDIA_TYPE(pmt);
897 
898     EnterCriticalSection(This->pin.pCritSec);
899     {
900         if (This->pin.pConnectedTo)
901             hr = VFW_E_ALREADY_CONNECTED;
902 
903         if (SUCCEEDED(hr) && This->pin.pFuncsTable->pfnCheckMediaType(&This->pin, pmt) != S_OK)
904             hr = VFW_E_TYPE_NOT_ACCEPTED; /* FIXME: shouldn't we just map common errors onto
905                                            * VFW_E_TYPE_NOT_ACCEPTED and pass the value on otherwise? */
906 
907         if (SUCCEEDED(hr))
908         {
909             IPin_QueryDirection(pReceivePin, &pindirReceive);
910 
911             if (pindirReceive != PINDIR_OUTPUT)
912             {
913                 ERR("Can't connect from non-output pin\n");
914                 hr = VFW_E_INVALID_DIRECTION;
915             }
916         }
917 
918         if (SUCCEEDED(hr))
919         {
920             CopyMediaType(&This->pin.mtCurrent, pmt);
921             This->pin.pConnectedTo = pReceivePin;
922             IPin_AddRef(pReceivePin);
923         }
924     }
925     LeaveCriticalSection(This->pin.pCritSec);
926 
927     return hr;
928 }
929 
930 static HRESULT deliver_endofstream(IPin* pin, LPVOID unused)
931 {
932     return IPin_EndOfStream( pin );
933 }
934 
935 HRESULT WINAPI BaseInputPinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
936 {
937     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
938 
939     TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
940 
941     return (This->pin.pFuncsTable->pfnCheckMediaType(&This->pin, pmt) == S_OK ? S_OK : S_FALSE);
942 }
943 
944 HRESULT WINAPI BaseInputPinImpl_EndOfStream(IPin * iface)
945 {
946     HRESULT hr = S_OK;
947     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
948 
949     TRACE("(%p)\n", This);
950 
951     EnterCriticalSection(This->pin.pCritSec);
952     if (This->flushing)
953         hr = S_FALSE;
954     else
955         This->end_of_stream = TRUE;
956     LeaveCriticalSection(This->pin.pCritSec);
957 
958     if (hr == S_OK)
959         hr = SendFurther( iface, deliver_endofstream, NULL, NULL );
960     return hr;
961 }
962 
963 static HRESULT deliver_beginflush(IPin* pin, LPVOID unused)
964 {
965     return IPin_BeginFlush( pin );
966 }
967 
968 HRESULT WINAPI BaseInputPinImpl_BeginFlush(IPin * iface)
969 {
970     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
971     HRESULT hr;
972     TRACE("() semi-stub\n");
973 
974     EnterCriticalSection(This->pin.pCritSec);
975     This->flushing = TRUE;
976 
977     hr = SendFurther( iface, deliver_beginflush, NULL, NULL );
978     LeaveCriticalSection(This->pin.pCritSec);
979 
980     return hr;
981 }
982 
983 static HRESULT deliver_endflush(IPin* pin, LPVOID unused)
984 {
985     return IPin_EndFlush( pin );
986 }
987 
988 HRESULT WINAPI BaseInputPinImpl_EndFlush(IPin * iface)
989 {
990     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
991     HRESULT hr;
992     TRACE("(%p)\n", This);
993 
994     EnterCriticalSection(This->pin.pCritSec);
995     This->flushing = This->end_of_stream = FALSE;
996 
997     hr = SendFurther( iface, deliver_endflush, NULL, NULL );
998     LeaveCriticalSection(This->pin.pCritSec);
999 
1000     return hr;
1001 }
1002 
1003 typedef struct newsegmentargs
1004 {
1005     REFERENCE_TIME tStart, tStop;
1006     double rate;
1007 } newsegmentargs;
1008 
1009 static HRESULT deliver_newsegment(IPin *pin, LPVOID data)
1010 {
1011     newsegmentargs *args = data;
1012     return IPin_NewSegment(pin, args->tStart, args->tStop, args->rate);
1013 }
1014 
1015 HRESULT WINAPI BaseInputPinImpl_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
1016 {
1017     BaseInputPin *This = impl_BaseInputPin_from_IPin(iface);
1018     newsegmentargs args;
1019 
1020     TRACE("(%s, %s, %e)\n", wine_dbgstr_longlong(tStart), wine_dbgstr_longlong(tStop), dRate);
1021 
1022     args.tStart = This->pin.tStart = tStart;
1023     args.tStop = This->pin.tStop = tStop;
1024     args.rate = This->pin.dRate = dRate;
1025 
1026     return SendFurther( iface, deliver_newsegment, &args, NULL );
1027 }
1028 
1029 /*** IMemInputPin implementation ***/
1030 
1031 static inline BaseInputPin *impl_from_IMemInputPin( IMemInputPin *iface )
1032 {
1033     return CONTAINING_RECORD(iface, BaseInputPin, IMemInputPin_iface);
1034 }
1035 
1036 static HRESULT WINAPI MemInputPin_QueryInterface(IMemInputPin * iface, REFIID riid, LPVOID * ppv)
1037 {
1038     BaseInputPin *This = impl_from_IMemInputPin(iface);
1039 
1040     return IPin_QueryInterface(&This->pin.IPin_iface, riid, ppv);
1041 }
1042 
1043 static ULONG WINAPI MemInputPin_AddRef(IMemInputPin * iface)
1044 {
1045     BaseInputPin *This = impl_from_IMemInputPin(iface);
1046 
1047     return IPin_AddRef(&This->pin.IPin_iface);
1048 }
1049 
1050 static ULONG WINAPI MemInputPin_Release(IMemInputPin * iface)
1051 {
1052     BaseInputPin *This = impl_from_IMemInputPin(iface);
1053 
1054     return IPin_Release(&This->pin.IPin_iface);
1055 }
1056 
1057 static HRESULT WINAPI MemInputPin_GetAllocator(IMemInputPin * iface, IMemAllocator ** ppAllocator)
1058 {
1059     BaseInputPin *This = impl_from_IMemInputPin(iface);
1060 
1061     TRACE("(%p/%p)->(%p)\n", This, iface, ppAllocator);
1062 
1063     *ppAllocator = This->pAllocator;
1064     if (*ppAllocator)
1065         IMemAllocator_AddRef(*ppAllocator);
1066 
1067     return *ppAllocator ? S_OK : VFW_E_NO_ALLOCATOR;
1068 }
1069 
1070 static HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator * pAllocator, BOOL bReadOnly)
1071 {
1072     BaseInputPin *This = impl_from_IMemInputPin(iface);
1073 
1074     TRACE("(%p/%p)->(%p, %d)\n", This, iface, pAllocator, bReadOnly);
1075 
1076     if (bReadOnly)
1077         FIXME("Read only flag not handled yet!\n");
1078 
1079     /* FIXME: Should we release the allocator on disconnection? */
1080     if (!pAllocator)
1081     {
1082         WARN("Null allocator\n");
1083         return E_POINTER;
1084     }
1085 
1086     if (This->preferred_allocator && pAllocator != This->preferred_allocator)
1087         return E_FAIL;
1088 
1089     if (This->pAllocator)
1090         IMemAllocator_Release(This->pAllocator);
1091     This->pAllocator = pAllocator;
1092     if (This->pAllocator)
1093         IMemAllocator_AddRef(This->pAllocator);
1094 
1095     return S_OK;
1096 }
1097 
1098 static HRESULT WINAPI MemInputPin_GetAllocatorRequirements(IMemInputPin * iface, ALLOCATOR_PROPERTIES * pProps)
1099 {
1100     BaseInputPin *This = impl_from_IMemInputPin(iface);
1101 
1102     TRACE("(%p/%p)->(%p)\n", This, iface, pProps);
1103 
1104     /* override this method if you have any specific requirements */
1105 
1106     return E_NOTIMPL;
1107 }
1108 
1109 static HRESULT WINAPI MemInputPin_Receive(IMemInputPin * iface, IMediaSample * pSample)
1110 {
1111     BaseInputPin *This = impl_from_IMemInputPin(iface);
1112     HRESULT hr = S_FALSE;
1113 
1114     /* this trace commented out for performance reasons */
1115     /*TRACE("(%p/%p)->(%p)\n", This, iface, pSample);*/
1116     if (This->pFuncsTable->pfnReceive)
1117         hr = This->pFuncsTable->pfnReceive(This, pSample);
1118     return hr;
1119 }
1120 
1121 static HRESULT WINAPI MemInputPin_ReceiveMultiple(IMemInputPin * iface, IMediaSample ** pSamples, LONG nSamples, LONG *nSamplesProcessed)
1122 {
1123     HRESULT hr = S_OK;
1124     BaseInputPin *This = impl_from_IMemInputPin(iface);
1125 
1126     TRACE("(%p/%p)->(%p, %d, %p)\n", This, iface, pSamples, nSamples, nSamplesProcessed);
1127 
1128     for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; (*nSamplesProcessed)++)
1129     {
1130         hr = IMemInputPin_Receive(iface, pSamples[*nSamplesProcessed]);
1131         if (hr != S_OK)
1132             break;
1133     }
1134 
1135     return hr;
1136 }
1137 
1138 static HRESULT WINAPI MemInputPin_ReceiveCanBlock(IMemInputPin * iface)
1139 {
1140     BaseInputPin *This = impl_from_IMemInputPin(iface);
1141 
1142     TRACE("(%p/%p)->()\n", This, iface);
1143 
1144     return S_OK;
1145 }
1146 
1147 static const IMemInputPinVtbl MemInputPin_Vtbl =
1148 {
1149     MemInputPin_QueryInterface,
1150     MemInputPin_AddRef,
1151     MemInputPin_Release,
1152     MemInputPin_GetAllocator,
1153     MemInputPin_NotifyAllocator,
1154     MemInputPin_GetAllocatorRequirements,
1155     MemInputPin_Receive,
1156     MemInputPin_ReceiveMultiple,
1157     MemInputPin_ReceiveCanBlock
1158 };
1159 
1160 static HRESULT InputPin_Init(const IPinVtbl *InputPin_Vtbl, const PIN_INFO * pPinInfo,
1161                              const BaseInputPinFuncTable* vtbl,
1162                              LPCRITICAL_SECTION pCritSec, IMemAllocator *allocator, BaseInputPin * pPinImpl)
1163 {
1164     TRACE("\n");
1165 
1166     /* Common attributes */
1167     pPinImpl->pin.refCount = 1;
1168     pPinImpl->pin.pConnectedTo = NULL;
1169     pPinImpl->pin.pCritSec = pCritSec;
1170     pPinImpl->pin.tStart = 0;
1171     pPinImpl->pin.tStop = 0;
1172     pPinImpl->pin.dRate = 1.0;
1173     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
1174     ZeroMemory(&pPinImpl->pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
1175     pPinImpl->pin.pFuncsTable = &vtbl->base;
1176 
1177     /* Input pin attributes */
1178     pPinImpl->pFuncsTable = vtbl;
1179     pPinImpl->pAllocator = pPinImpl->preferred_allocator = allocator;
1180     if (pPinImpl->preferred_allocator)
1181         IMemAllocator_AddRef(pPinImpl->preferred_allocator);
1182     pPinImpl->pin.IPin_iface.lpVtbl = InputPin_Vtbl;
1183     pPinImpl->IMemInputPin_iface.lpVtbl = &MemInputPin_Vtbl;
1184     pPinImpl->flushing = pPinImpl->end_of_stream = FALSE;
1185 
1186     return S_OK;
1187 }
1188 
1189 HRESULT BaseInputPin_Construct(const IPinVtbl *InputPin_Vtbl, LONG inputpin_size, const PIN_INFO * pPinInfo,
1190                                const BaseInputPinFuncTable* vtbl,
1191                                LPCRITICAL_SECTION pCritSec, IMemAllocator *allocator, IPin ** ppPin)
1192 {
1193     BaseInputPin * pPinImpl;
1194 
1195     *ppPin = NULL;
1196 
1197     assert(inputpin_size >= sizeof(BaseInputPin));
1198     assert(vtbl->base.pfnCheckMediaType);
1199 
1200     if (pPinInfo->dir != PINDIR_INPUT)
1201     {
1202         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
1203         return E_INVALIDARG;
1204     }
1205 
1206     pPinImpl = CoTaskMemAlloc(inputpin_size);
1207 
1208     if (!pPinImpl)
1209         return E_OUTOFMEMORY;
1210 
1211     if (SUCCEEDED(InputPin_Init(InputPin_Vtbl, pPinInfo, vtbl, pCritSec, allocator, pPinImpl)))
1212     {
1213         *ppPin = &pPinImpl->pin.IPin_iface;
1214         return S_OK;
1215     }
1216 
1217     CoTaskMemFree(pPinImpl);
1218     return E_FAIL;
1219 }
1220 
1221 HRESULT WINAPI BaseInputPin_Destroy(BaseInputPin *This)
1222 {
1223     FreeMediaType(&This->pin.mtCurrent);
1224     if (This->pAllocator)
1225         IMemAllocator_Release(This->pAllocator);
1226     This->pAllocator = NULL;
1227     This->pin.IPin_iface.lpVtbl = NULL;
1228     CoTaskMemFree(This);
1229     return S_OK;
1230 }
1231