strmbase: Move OutputPin implementation to strmbase.
[wine] / dlls / strmbase / pin.c
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 IPinVtbl OutputPin_Vtbl;
35
36 static void Copy_PinInfo(PIN_INFO * pDest, const PIN_INFO * pSrc)
37 {
38     /* Tempting to just do a memcpy, but the name field is
39        128 characters long! We will probably never exceed 10
40        most of the time, so we are better off copying
41        each field manually */
42     strcpyW(pDest->achName, pSrc->achName);
43     pDest->dir = pSrc->dir;
44     pDest->pFilter = pSrc->pFilter;
45 }
46
47 static void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt)
48 {
49     if (!pmt)
50         return;
51     TRACE("\t%s\n\t%s\n\t...\n\t%s\n", debugstr_guid(&pmt->majortype), debugstr_guid(&pmt->subtype), debugstr_guid(&pmt->formattype));
52 }
53
54 static BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards)
55 {
56     TRACE("pmt1: ");
57     dump_AM_MEDIA_TYPE(pmt1);
58     TRACE("pmt2: ");
59     dump_AM_MEDIA_TYPE(pmt2);
60     return (((bWildcards && (IsEqualGUID(&pmt1->majortype, &GUID_NULL) || IsEqualGUID(&pmt2->majortype, &GUID_NULL))) || IsEqualGUID(&pmt1->majortype, &pmt2->majortype)) &&
61             ((bWildcards && (IsEqualGUID(&pmt1->subtype, &GUID_NULL)   || IsEqualGUID(&pmt2->subtype, &GUID_NULL)))   || IsEqualGUID(&pmt1->subtype, &pmt2->subtype)));
62 }
63
64 /*** Common Base Pin function */
65 HRESULT WINAPI BasePinImpl_GetMediaType(IPin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
66 {
67     if (iPosition < 0)
68         return E_INVALIDARG;
69     return VFW_S_NO_MORE_ITEMS;
70 }
71
72 LONG WINAPI BasePinImpl_GetMediaTypeVersion(IPin *iface)
73 {
74     return 1;
75 }
76
77 ULONG WINAPI BasePinImpl_AddRef(IPin * iface)
78 {
79     BasePin *This = (BasePin *)iface;
80     ULONG refCount = InterlockedIncrement(&This->refCount);
81
82     TRACE("(%p)->() AddRef from %d\n", iface, refCount - 1);
83
84     return refCount;
85 }
86
87 HRESULT WINAPI BasePinImpl_Disconnect(IPin * iface)
88 {
89     HRESULT hr;
90     BasePin *This = (BasePin *)iface;
91
92     TRACE("()\n");
93
94     EnterCriticalSection(This->pCritSec);
95     {
96         if (This->pConnectedTo)
97         {
98             IPin_Release(This->pConnectedTo);
99             This->pConnectedTo = NULL;
100             FreeMediaType(&This->mtCurrent);
101             ZeroMemory(&This->mtCurrent, sizeof(This->mtCurrent));
102             hr = S_OK;
103         }
104         else
105             hr = S_FALSE;
106     }
107     LeaveCriticalSection(This->pCritSec);
108
109     return hr;
110 }
111
112 HRESULT WINAPI BasePinImpl_ConnectedTo(IPin * iface, IPin ** ppPin)
113 {
114     HRESULT hr;
115     BasePin *This = (BasePin *)iface;
116
117     TRACE("(%p)\n", ppPin);
118
119     EnterCriticalSection(This->pCritSec);
120     {
121         if (This->pConnectedTo)
122         {
123             *ppPin = This->pConnectedTo;
124             IPin_AddRef(*ppPin);
125             hr = S_OK;
126         }
127         else
128         {
129             hr = VFW_E_NOT_CONNECTED;
130             *ppPin = NULL;
131         }
132     }
133     LeaveCriticalSection(This->pCritSec);
134
135     return hr;
136 }
137
138 HRESULT WINAPI BasePinImpl_ConnectionMediaType(IPin * iface, AM_MEDIA_TYPE * pmt)
139 {
140     HRESULT hr;
141     BasePin *This = (BasePin *)iface;
142
143     TRACE("(%p/%p)->(%p)\n", This, iface, pmt);
144
145     EnterCriticalSection(This->pCritSec);
146     {
147         if (This->pConnectedTo)
148         {
149             CopyMediaType(pmt, &This->mtCurrent);
150             hr = S_OK;
151         }
152         else
153         {
154             ZeroMemory(pmt, sizeof(*pmt));
155             hr = VFW_E_NOT_CONNECTED;
156         }
157     }
158     LeaveCriticalSection(This->pCritSec);
159
160     return hr;
161 }
162
163 HRESULT WINAPI BasePinImpl_QueryPinInfo(IPin * iface, PIN_INFO * pInfo)
164 {
165     BasePin *This = (BasePin *)iface;
166
167     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
168
169     Copy_PinInfo(pInfo, &This->pinInfo);
170     IBaseFilter_AddRef(pInfo->pFilter);
171
172     return S_OK;
173 }
174
175 HRESULT WINAPI BasePinImpl_QueryDirection(IPin * iface, PIN_DIRECTION * pPinDir)
176 {
177     BasePin *This = (BasePin *)iface;
178
179     TRACE("(%p/%p)->(%p)\n", This, iface, pPinDir);
180
181     *pPinDir = This->pinInfo.dir;
182
183     return S_OK;
184 }
185
186 HRESULT WINAPI BasePinImpl_QueryId(IPin * iface, LPWSTR * Id)
187 {
188     BasePin *This = (BasePin *)iface;
189
190     TRACE("(%p/%p)->(%p)\n", This, iface, Id);
191
192     *Id = CoTaskMemAlloc((strlenW(This->pinInfo.achName) + 1) * sizeof(WCHAR));
193     if (!*Id)
194         return E_OUTOFMEMORY;
195
196     strcpyW(*Id, This->pinInfo.achName);
197
198     return S_OK;
199 }
200
201 HRESULT WINAPI BasePinImpl_QueryAccept(IPin * iface, const AM_MEDIA_TYPE * pmt)
202 {
203     TRACE("(%p)->(%p)\n", iface, pmt);
204
205     return S_OK;
206 }
207
208 HRESULT WINAPI BasePinImpl_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
209 {
210     BasePin *This = (BasePin *)iface;
211
212     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
213
214     /* override this method to allow enumeration of your types */
215
216     return EnumMediaTypes_Construct(iface, BasePinImpl_GetMediaType, BasePinImpl_GetMediaTypeVersion , ppEnum);
217 }
218
219 HRESULT WINAPI BasePinImpl_QueryInternalConnections(IPin * iface, IPin ** apPin, ULONG * cPin)
220 {
221     BasePin *This = (BasePin *)iface;
222
223     TRACE("(%p/%p)->(%p, %p)\n", This, iface, apPin, cPin);
224
225     return E_NOTIMPL; /* to tell caller that all input pins connected to all output pins */
226 }
227
228 /*** OutputPin implementation ***/
229
230 HRESULT WINAPI BaseOutputPinImpl_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
231 {
232     BaseOutputPin *This = (BaseOutputPin *)iface;
233
234     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
235
236     *ppv = NULL;
237
238     if (IsEqualIID(riid, &IID_IUnknown))
239         *ppv = iface;
240     else if (IsEqualIID(riid, &IID_IPin))
241         *ppv = iface;
242     else if (IsEqualIID(riid, &IID_IMediaSeeking))
243     {
244         return IBaseFilter_QueryInterface(This->pin.pinInfo.pFilter, &IID_IMediaSeeking, ppv);
245     }
246
247     if (*ppv)
248     {
249         IUnknown_AddRef((IUnknown *)(*ppv));
250         return S_OK;
251     }
252
253     FIXME("No interface for %s!\n", debugstr_guid(riid));
254
255     return E_NOINTERFACE;
256 }
257
258 ULONG WINAPI BaseOutputPinImpl_Release(IPin * iface)
259 {
260     BaseOutputPin *This = (BaseOutputPin *)iface;
261     ULONG refCount = InterlockedDecrement(&This->pin.refCount);
262
263     TRACE("(%p)->() Release from %d\n", iface, refCount + 1);
264
265     if (!refCount)
266     {
267         FreeMediaType(&This->pin.mtCurrent);
268         CoTaskMemFree(This);
269         return 0;
270     }
271     return refCount;
272 }
273
274 HRESULT WINAPI BaseOutputPinImpl_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
275 {
276     HRESULT hr;
277     BaseOutputPin *This = (BaseOutputPin *)iface;
278
279     TRACE("(%p/%p)->(%p, %p)\n", This, iface, pReceivePin, pmt);
280     dump_AM_MEDIA_TYPE(pmt);
281
282     /* If we try to connect to ourself, we will definitely deadlock.
283      * There are other cases where we could deadlock too, but this
284      * catches the obvious case */
285     assert(pReceivePin != iface);
286
287     EnterCriticalSection(This->pin.pCritSec);
288     {
289         /* if we have been a specific type to connect with, then we can either connect
290          * with that or fail. We cannot choose different AM_MEDIA_TYPE */
291         if (pmt && !IsEqualGUID(&pmt->majortype, &GUID_NULL) && !IsEqualGUID(&pmt->subtype, &GUID_NULL))
292             hr = This->pAttemptConnection(iface, pReceivePin, pmt);
293         else
294         {
295             /* negotiate media type */
296
297             IEnumMediaTypes * pEnumCandidates;
298             AM_MEDIA_TYPE * pmtCandidate = NULL; /* Candidate media type */
299
300             if (SUCCEEDED(hr = IPin_EnumMediaTypes(iface, &pEnumCandidates)))
301             {
302                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
303
304                 /* try this filter's media types first */
305                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
306                 {
307                     assert(pmtCandidate);
308                     dump_AM_MEDIA_TYPE(pmtCandidate);
309                     if (!IsEqualGUID(&FORMAT_None, &pmtCandidate->formattype)
310                         && !IsEqualGUID(&GUID_NULL, &pmtCandidate->formattype))
311                         assert(pmtCandidate->pbFormat);
312                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
313                         (This->pAttemptConnection(iface, pReceivePin, pmtCandidate) == S_OK))
314                     {
315                         hr = S_OK;
316                         DeleteMediaType(pmtCandidate);
317                         break;
318                     }
319                     DeleteMediaType(pmtCandidate);
320                     pmtCandidate = NULL;
321                 }
322                 IEnumMediaTypes_Release(pEnumCandidates);
323             }
324
325             /* then try receiver filter's media types */
326             if (hr != S_OK && SUCCEEDED(hr = IPin_EnumMediaTypes(pReceivePin, &pEnumCandidates))) /* if we haven't already connected successfully */
327             {
328                 hr = VFW_E_NO_ACCEPTABLE_TYPES; /* Assume the worst, but set to S_OK if connected successfully */
329
330                 while (S_OK == IEnumMediaTypes_Next(pEnumCandidates, 1, &pmtCandidate, NULL))
331                 {
332                     assert(pmtCandidate);
333                     dump_AM_MEDIA_TYPE(pmtCandidate);
334                     if (( !pmt || CompareMediaTypes(pmt, pmtCandidate, TRUE) ) &&
335                         (This->pAttemptConnection(iface, pReceivePin, pmtCandidate) == S_OK))
336                     {
337                         hr = S_OK;
338                         DeleteMediaType(pmtCandidate);
339                         break;
340                     }
341                     DeleteMediaType(pmtCandidate);
342                     pmtCandidate = NULL;
343                 } /* while */
344                 IEnumMediaTypes_Release(pEnumCandidates);
345             } /* if not found */
346         } /* if negotiate media type */
347     } /* if succeeded */
348     LeaveCriticalSection(This->pin.pCritSec);
349
350     TRACE(" -- %x\n", hr);
351     return hr;
352 }
353
354 HRESULT WINAPI BaseOutputPinImpl_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
355 {
356     ERR("Incoming connection on an output pin! (%p, %p)\n", pReceivePin, pmt);
357
358     return E_UNEXPECTED;
359 }
360
361 HRESULT WINAPI BaseOutputPinImpl_Disconnect(IPin * iface)
362 {
363     HRESULT hr;
364     BaseOutputPin *This = (BaseOutputPin *)iface;
365
366     TRACE("()\n");
367
368     EnterCriticalSection(This->pin.pCritSec);
369     {
370         if (This->pMemInputPin)
371         {
372             IMemInputPin_Release(This->pMemInputPin);
373             This->pMemInputPin = NULL;
374         }
375         if (This->pin.pConnectedTo)
376         {
377             IPin_Release(This->pin.pConnectedTo);
378             This->pin.pConnectedTo = NULL;
379             FreeMediaType(&This->pin.mtCurrent);
380             ZeroMemory(&This->pin.mtCurrent, sizeof(This->pin.mtCurrent));
381             hr = S_OK;
382         }
383         else
384             hr = S_FALSE;
385     }
386     LeaveCriticalSection(This->pin.pCritSec);
387
388     return hr;
389 }
390
391 HRESULT WINAPI BaseOutputPinImpl_EndOfStream(IPin * iface)
392 {
393     TRACE("()\n");
394
395     /* not supposed to do anything in an output pin */
396
397     return E_UNEXPECTED;
398 }
399
400 HRESULT WINAPI BaseOutputPinImpl_BeginFlush(IPin * iface)
401 {
402     TRACE("(%p)->()\n", iface);
403
404     /* not supposed to do anything in an output pin */
405
406     return E_UNEXPECTED;
407 }
408
409 HRESULT WINAPI BaseOutputPinImpl_EndFlush(IPin * iface)
410 {
411     TRACE("(%p)->()\n", iface);
412
413     /* not supposed to do anything in an output pin */
414
415     return E_UNEXPECTED;
416 }
417
418 HRESULT WINAPI BaseOutputPinImpl_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
419 {
420     TRACE("(%p)->(%x%08x, %x%08x, %e)\n", iface, (ULONG)(tStart >> 32), (ULONG)tStart, (ULONG)(tStop >> 32), (ULONG)tStop, dRate);
421
422     /* not supposed to do anything in an output pin */
423
424     return E_UNEXPECTED;
425 }
426
427 static const IPinVtbl OutputPin_Vtbl =
428 {
429     BaseOutputPinImpl_QueryInterface,
430     BasePinImpl_AddRef,
431     BaseOutputPinImpl_Release,
432     BaseOutputPinImpl_Connect,
433     BaseOutputPinImpl_ReceiveConnection,
434     BaseOutputPinImpl_Disconnect,
435     BasePinImpl_ConnectedTo,
436     BasePinImpl_ConnectionMediaType,
437     BasePinImpl_QueryPinInfo,
438     BasePinImpl_QueryDirection,
439     BasePinImpl_QueryId,
440     BasePinImpl_QueryAccept,
441     BasePinImpl_EnumMediaTypes,
442     BasePinImpl_QueryInternalConnections,
443     BaseOutputPinImpl_EndOfStream,
444     BaseOutputPinImpl_BeginFlush,
445     BaseOutputPinImpl_EndFlush,
446     BaseOutputPinImpl_NewSegment
447 };
448
449 HRESULT WINAPI BaseOutputPinImpl_GetDeliveryBuffer(BaseOutputPin *This, IMediaSample ** ppSample, REFERENCE_TIME * tStart, REFERENCE_TIME * tStop, DWORD dwFlags)
450 {
451     HRESULT hr;
452
453     TRACE("(%p, %p, %p, %x)\n", ppSample, tStart, tStop, dwFlags);
454
455     EnterCriticalSection(This->pin.pCritSec);
456     {
457         if (!This->pin.pConnectedTo)
458             hr = VFW_E_NOT_CONNECTED;
459         else
460         {
461             IMemAllocator * pAlloc = NULL;
462
463             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
464
465             if (SUCCEEDED(hr))
466                 hr = IMemAllocator_GetBuffer(pAlloc, ppSample, tStart, tStop, dwFlags);
467
468             if (SUCCEEDED(hr))
469                 hr = IMediaSample_SetTime(*ppSample, tStart, tStop);
470
471             if (pAlloc)
472                 IMemAllocator_Release(pAlloc);
473         }
474     }
475     LeaveCriticalSection(This->pin.pCritSec);
476
477     return hr;
478 }
479
480 /* replaces OutputPin_SendSample */
481 HRESULT WINAPI BaseOutputPinImpl_Deliver(BaseOutputPin *This, IMediaSample * pSample)
482 {
483     HRESULT hr = S_OK;
484     IMemInputPin * pMemConnected = NULL;
485     PIN_INFO pinInfo;
486
487     EnterCriticalSection(This->pin.pCritSec);
488     {
489         if (!This->pin.pConnectedTo || !This->pMemInputPin)
490             hr = VFW_E_NOT_CONNECTED;
491         else
492         {
493             /* we don't have the lock held when using This->pMemInputPin,
494              * so we need to AddRef it to stop it being deleted while we are
495              * using it. Same with its filter. */
496             pMemConnected = This->pMemInputPin;
497             IMemInputPin_AddRef(pMemConnected);
498             hr = IPin_QueryPinInfo(This->pin.pConnectedTo, &pinInfo);
499         }
500     }
501     LeaveCriticalSection(This->pin.pCritSec);
502
503     if (SUCCEEDED(hr))
504     {
505         /* NOTE: if we are in a critical section when Receive is called
506          * then it causes some problems (most notably with the native Video
507          * Renderer) if we are re-entered for whatever reason */
508         hr = IMemInputPin_Receive(pMemConnected, pSample);
509
510         /* If the filter's destroyed, tell upstream to stop sending data */
511         if(IBaseFilter_Release(pinInfo.pFilter) == 0 && SUCCEEDED(hr))
512             hr = S_FALSE;
513     }
514     if (pMemConnected)
515         IMemInputPin_Release(pMemConnected);
516
517     return hr;
518 }
519
520 /* replaces OutputPin_CommitAllocator */
521 HRESULT WINAPI BaseOutputPinImpl_Active(BaseOutputPin *This)
522 {
523     HRESULT hr = S_OK;
524
525     TRACE("(%p)->()\n", This);
526
527     EnterCriticalSection(This->pin.pCritSec);
528     {
529         if (!This->pin.pConnectedTo || !This->pMemInputPin)
530             hr = VFW_E_NOT_CONNECTED;
531         else
532         {
533             IMemAllocator * pAlloc = NULL;
534
535             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
536
537             if (SUCCEEDED(hr))
538                 hr = IMemAllocator_Commit(pAlloc);
539
540             if (pAlloc)
541                 IMemAllocator_Release(pAlloc);
542         }
543     }
544     LeaveCriticalSection(This->pin.pCritSec);
545
546     TRACE("--> %08x\n", hr);
547     return hr;
548 }
549
550 /* replaces OutputPin_DecommitAllocator */
551 HRESULT WINAPI BaseOutputPinImpl_Inactive(BaseOutputPin *This)
552 {
553     HRESULT hr = S_OK;
554
555     TRACE("(%p)->()\n", This);
556
557     EnterCriticalSection(This->pin.pCritSec);
558     {
559         if (!This->pin.pConnectedTo || !This->pMemInputPin)
560             hr = VFW_E_NOT_CONNECTED;
561         else
562         {
563             IMemAllocator * pAlloc = NULL;
564
565             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
566
567             if (SUCCEEDED(hr))
568                 hr = IMemAllocator_Decommit(pAlloc);
569
570             if (pAlloc)
571                 IMemAllocator_Release(pAlloc);
572         }
573     }
574     LeaveCriticalSection(This->pin.pCritSec);
575
576     TRACE("--> %08x\n", hr);
577     return hr;
578 }
579
580 /* replaces OutputPin_DeliverDisconnect */
581 HRESULT WINAPI BaseOutputPinImpl_BreakConnect(BaseOutputPin *This)
582 {
583     HRESULT hr;
584
585     TRACE("(%p)->()\n", This);
586
587     EnterCriticalSection(This->pin.pCritSec);
588     {
589         if (!This->pin.pConnectedTo || !This->pMemInputPin)
590             hr = VFW_E_NOT_CONNECTED;
591         else if (!This->custom_allocator)
592         {
593             IMemAllocator * pAlloc = NULL;
594
595             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pAlloc);
596
597             if (SUCCEEDED(hr))
598                 hr = IMemAllocator_Decommit(pAlloc);
599
600             if (pAlloc)
601                 IMemAllocator_Release(pAlloc);
602
603             if (SUCCEEDED(hr))
604                 hr = IPin_Disconnect(This->pin.pConnectedTo);
605         }
606         else /* Kill the allocator! */
607         {
608             hr = IPin_Disconnect(This->pin.pConnectedTo);
609         }
610         IPin_Disconnect((IPin *)This);
611     }
612     LeaveCriticalSection(This->pin.pCritSec);
613
614     return hr;
615 }
616
617 /*** The Construct functions ***/
618
619 /* Function called as a helper to IPin_Connect */
620 /* specific AM_MEDIA_TYPE - it cannot be NULL */
621 static HRESULT WINAPI OutputPin_AttemptConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
622 {
623     BaseOutputPin *This = (BaseOutputPin *)iface;
624     HRESULT hr;
625     IMemAllocator * pMemAlloc = NULL;
626     ALLOCATOR_PROPERTIES actual; /* FIXME: should we put the actual props back in to This? */
627
628     TRACE("(%p, %p)\n", pReceivePin, pmt);
629     dump_AM_MEDIA_TYPE(pmt);
630
631     /* FIXME: call queryacceptproc */
632
633     This->pin.pConnectedTo = pReceivePin;
634     IPin_AddRef(pReceivePin);
635     CopyMediaType(&This->pin.mtCurrent, pmt);
636
637     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
638
639     /* get the IMemInputPin interface we will use to deliver samples to the
640      * connected pin */
641     if (SUCCEEDED(hr))
642     {
643         This->pMemInputPin = NULL;
644         hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin);
645
646         if (SUCCEEDED(hr) && !This->custom_allocator)
647         {
648             hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pMemAlloc);
649
650             if (hr == VFW_E_NO_ALLOCATOR)
651                 /* Input pin provides no allocator, use standard memory allocator */
652                 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (LPVOID*)&pMemAlloc);
653
654             if (SUCCEEDED(hr))
655                 hr = IMemAllocator_SetProperties(pMemAlloc, &This->allocProps, &actual);
656
657             if (SUCCEEDED(hr))
658                 hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, pMemAlloc, This->readonly);
659
660             if (pMemAlloc)
661                 IMemAllocator_Release(pMemAlloc);
662         }
663         else if (SUCCEEDED(hr))
664         {
665             if (This->alloc)
666             {
667                 hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, This->alloc, This->readonly);
668             }
669             else
670                 hr = VFW_E_NO_ALLOCATOR;
671         }
672
673         /* break connection if we couldn't get the allocator */
674         if (FAILED(hr))
675         {
676             if (This->pMemInputPin)
677                 IMemInputPin_Release(This->pMemInputPin);
678             This->pMemInputPin = NULL;
679
680             IPin_Disconnect(pReceivePin);
681         }
682     }
683
684     if (FAILED(hr))
685     {
686         IPin_Release(This->pin.pConnectedTo);
687         This->pin.pConnectedTo = NULL;
688         FreeMediaType(&This->pin.mtCurrent);
689     }
690
691     TRACE(" -- %x\n", hr);
692     return hr;
693 }
694
695 static HRESULT OutputPin_Init(const IPinVtbl *OutputPin_Vtbl, const PIN_INFO *
696 pPinInfo, const ALLOCATOR_PROPERTIES * props, BasePin_AttemptConnection pConnectProc, LPCRITICAL_SECTION pCritSec,
697 BaseOutputPin * pPinImpl)
698 {
699     TRACE("\n");
700
701     /* Common attributes */
702     pPinImpl->pin.lpVtbl = OutputPin_Vtbl;
703     pPinImpl->pin.refCount = 1;
704     pPinImpl->pin.pConnectedTo = NULL;
705     pPinImpl->pin.pCritSec = pCritSec;
706     Copy_PinInfo(&pPinImpl->pin.pinInfo, pPinInfo);
707     ZeroMemory(&pPinImpl->pin.mtCurrent, sizeof(AM_MEDIA_TYPE));
708
709     /* Output pin attributes */
710     pPinImpl->pMemInputPin = NULL;
711     if(pConnectProc)
712         pPinImpl->pAttemptConnection = pConnectProc;
713     else
714         pPinImpl->pAttemptConnection = OutputPin_AttemptConnection;
715     /* If custom_allocator is set, you will need to specify an allocator
716      * in the alloc member of the struct before an output pin can connect
717      */
718     pPinImpl->custom_allocator = 0;
719     pPinImpl->alloc = NULL;
720     pPinImpl->readonly = FALSE;
721     if (props)
722     {
723         pPinImpl->allocProps = *props;
724         if (pPinImpl->allocProps.cbAlign == 0)
725             pPinImpl->allocProps.cbAlign = 1;
726     }
727     else
728         ZeroMemory(&pPinImpl->allocProps, sizeof(pPinImpl->allocProps));
729
730     return S_OK;
731 }
732
733 HRESULT WINAPI BaseOutputPin_Construct(const IPinVtbl *OutputPin_Vtbl, LONG outputpin_size, const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, BasePin_AttemptConnection pConnectProc, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
734 {
735     BaseOutputPin * pPinImpl;
736
737     *ppPin = NULL;
738
739     if (pPinInfo->dir != PINDIR_OUTPUT)
740     {
741         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
742         return E_INVALIDARG;
743     }
744
745     assert(outputpin_size >= sizeof(BaseOutputPin));
746
747     pPinImpl = CoTaskMemAlloc(outputpin_size);
748
749     if (!pPinImpl)
750         return E_OUTOFMEMORY;
751
752     if (SUCCEEDED(OutputPin_Init(OutputPin_Vtbl, pPinInfo, props, pConnectProc, pCritSec, pPinImpl)))
753     {
754         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
755         return S_OK;
756     }
757
758     CoTaskMemFree(pPinImpl);
759     return E_FAIL;
760 }