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