strmbase: Move BasePin implementation to strmbase.
[wine] / dlls / quartz / transform.c
1 /*
2  * Transform Filter (Base for decoders, etc...)
3  *
4  * Copyright 2005 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include "quartz_private.h"
24 #include "control_private.h"
25 #include "pin.h"
26
27 #include "amvideo.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "dshow.h"
31 #include "strmif.h"
32 #include "vfw.h"
33
34 #include <assert.h>
35
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 #include "transform.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
42
43 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
44 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
45
46 static const IBaseFilterVtbl TransformFilter_Vtbl;
47 static const IPinVtbl TransformFilter_InputPin_Vtbl;
48 static const IPinVtbl TransformFilter_OutputPin_Vtbl;
49
50 static HRESULT TransformFilter_Input_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
51 {
52     TransformFilterImpl* This = (TransformFilterImpl *)((BasePin *)iface)->pinInfo.pFilter;
53     TRACE("%p\n", iface);
54     dump_AM_MEDIA_TYPE(pmt);
55
56     if (This->pFuncsTable->pfnQueryConnect)
57         return This->pFuncsTable->pfnQueryConnect(This, pmt);
58     /* Assume OK if there's no query method (the connection will fail if
59        needed) */
60     return S_OK;
61 }
62
63
64 static HRESULT WINAPI TransformFilter_Output_QueryAccept(IPin *iface, const AM_MEDIA_TYPE * pmt)
65 {
66     BasePin *This = (BasePin *)iface;
67     TransformFilterImpl *pTransformFilter = (TransformFilterImpl *)This->pinInfo.pFilter;
68     AM_MEDIA_TYPE* outpmt = &pTransformFilter->pmt;
69     TRACE("%p\n", iface);
70
71     if (IsEqualIID(&pmt->majortype, &outpmt->majortype)
72         && (IsEqualIID(&pmt->subtype, &outpmt->subtype) || IsEqualIID(&outpmt->subtype, &GUID_NULL)))
73         return S_OK;
74     return S_FALSE;
75 }
76
77 HRESULT TransformFilter_Create(TransformFilterImpl* pTransformFilter, const CLSID* pClsid, const TransformFuncsTable* pFuncsTable)
78 {
79     HRESULT hr;
80     PIN_INFO piInput;
81     PIN_INFO piOutput;
82
83     /* pTransformFilter is already allocated */
84     pTransformFilter->clsid = *pClsid;
85     pTransformFilter->pFuncsTable = pFuncsTable;
86
87     pTransformFilter->lpVtbl = &TransformFilter_Vtbl;
88
89     pTransformFilter->refCount = 1;
90     InitializeCriticalSection(&pTransformFilter->csFilter);
91     pTransformFilter->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TransformFilterImpl.csFilter");
92     pTransformFilter->state = State_Stopped;
93     pTransformFilter->pClock = NULL;
94     ZeroMemory(&pTransformFilter->filterInfo, sizeof(FILTER_INFO));
95     ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
96     pTransformFilter->npins = 2;
97
98     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
99
100     /* construct input pin */
101     piInput.dir = PINDIR_INPUT;
102     piInput.pFilter = (IBaseFilter *)pTransformFilter;
103     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
104     piOutput.dir = PINDIR_OUTPUT;
105     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
106     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
107
108     hr = InputPin_Construct(&TransformFilter_InputPin_Vtbl, &piInput, (SAMPLEPROC_PUSH)pFuncsTable->pfnProcessSampleData, NULL, TransformFilter_Input_QueryAccept, NULL, &pTransformFilter->csFilter, NULL, &pTransformFilter->ppPins[0]);
109
110     if (SUCCEEDED(hr))
111     {
112         ALLOCATOR_PROPERTIES props;
113         props.cbAlign = 1;
114         props.cbPrefix = 0;
115         props.cbBuffer = 0; /* Will be updated at connection time */
116         props.cBuffers = 1;
117
118        ((InputPin *)pTransformFilter->ppPins[0])->pUserData = pTransformFilter->ppPins[0];
119
120         hr = OutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(OutputPin), &piOutput, &props, &pTransformFilter->csFilter, &pTransformFilter->ppPins[1]);
121
122         if (FAILED(hr))
123             ERR("Cannot create output pin (%x)\n", hr);
124         else
125         {
126             ISeekingPassThru *passthru;
127             hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)pTransformFilter, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&pTransformFilter->seekthru_unk);
128             IUnknown_QueryInterface(pTransformFilter->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
129             ISeekingPassThru_Init(passthru, FALSE, (IPin*)pTransformFilter->ppPins[0]);
130             ISeekingPassThru_Release(passthru);
131         }
132     }
133     if (FAILED(hr))
134     {
135         CoTaskMemFree(pTransformFilter->ppPins);
136         pTransformFilter->csFilter.DebugInfo->Spare[0] = 0;
137         DeleteCriticalSection(&pTransformFilter->csFilter);
138         CoTaskMemFree(pTransformFilter);
139     }
140
141     return hr;
142 }
143
144 static HRESULT WINAPI TransformFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
145 {
146     TransformFilterImpl *This = (TransformFilterImpl *)iface;
147     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
148
149     *ppv = NULL;
150
151     if (IsEqualIID(riid, &IID_IUnknown))
152         *ppv = This;
153     else if (IsEqualIID(riid, &IID_IPersist))
154         *ppv = This;
155     else if (IsEqualIID(riid, &IID_IMediaFilter))
156         *ppv = This;
157     else if (IsEqualIID(riid, &IID_IBaseFilter))
158         *ppv = This;
159     else if (IsEqualIID(riid, &IID_IMediaSeeking))
160         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
161
162     if (*ppv)
163     {
164         IUnknown_AddRef((IUnknown *)(*ppv));
165         return S_OK;
166     }
167
168     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
169         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
170
171     return E_NOINTERFACE;
172 }
173
174 static ULONG WINAPI TransformFilter_AddRef(IBaseFilter * iface)
175 {
176     TransformFilterImpl *This = (TransformFilterImpl *)iface;
177     ULONG refCount = InterlockedIncrement(&This->refCount);
178
179     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
180
181     return refCount;
182 }
183
184 static ULONG WINAPI TransformFilter_Release(IBaseFilter * iface)
185 {
186     TransformFilterImpl *This = (TransformFilterImpl *)iface;
187     ULONG refCount = InterlockedDecrement(&This->refCount);
188
189     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
190
191     if (!refCount)
192     {
193         ULONG i;
194
195         if (This->pClock)
196             IReferenceClock_Release(This->pClock);
197
198         for (i = 0; i < This->npins; i++)
199         {
200             IPin *pConnectedTo;
201
202             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
203             {
204                 IPin_Disconnect(pConnectedTo);
205                 IPin_Release(pConnectedTo);
206             }
207             IPin_Disconnect(This->ppPins[i]);
208
209             IPin_Release(This->ppPins[i]);
210         }
211
212         CoTaskMemFree(This->ppPins);
213         This->lpVtbl = NULL;
214
215         This->csFilter.DebugInfo->Spare[0] = 0;
216         DeleteCriticalSection(&This->csFilter);
217
218         TRACE("Destroying transform filter\n");
219         FreeMediaType(&This->pmt);
220         CoTaskMemFree(This);
221
222         return 0;
223     }
224     else
225         return refCount;
226 }
227
228 /** IPersist methods **/
229
230 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
231 {
232     TransformFilterImpl *This = (TransformFilterImpl *)iface;
233
234     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
235
236     *pClsid = This->clsid;
237
238     return S_OK;
239 }
240
241 /** IMediaFilter methods **/
242
243 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
244 {
245     TransformFilterImpl *This = (TransformFilterImpl *)iface;
246     HRESULT hr = S_OK;
247
248     TRACE("(%p/%p)\n", This, iface);
249
250     EnterCriticalSection(&This->csFilter);
251     {
252         This->state = State_Stopped;
253         if (This->pFuncsTable->pfnProcessEnd)
254             hr = This->pFuncsTable->pfnProcessEnd(This);
255     }
256     LeaveCriticalSection(&This->csFilter);
257
258     return hr;
259 }
260
261 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
262 {
263     TransformFilterImpl *This = (TransformFilterImpl *)iface;
264     HRESULT hr;
265
266     TRACE("(%p/%p)->()\n", This, iface);
267
268     EnterCriticalSection(&This->csFilter);
269     {
270         if (This->state == State_Stopped)
271             hr = IBaseFilter_Run(iface, -1);
272         else
273             hr = S_OK;
274
275         if (SUCCEEDED(hr))
276             This->state = State_Paused;
277     }
278     LeaveCriticalSection(&This->csFilter);
279
280     return hr;
281 }
282
283 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
284 {
285     HRESULT hr = S_OK;
286     TransformFilterImpl *This = (TransformFilterImpl *)iface;
287
288     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
289
290     EnterCriticalSection(&This->csFilter);
291     {
292         if (This->state == State_Stopped)
293         {
294             ((InputPin *)This->ppPins[0])->end_of_stream = 0;
295             if (This->pFuncsTable->pfnProcessBegin)
296                 hr = This->pFuncsTable->pfnProcessBegin(This);
297             if (SUCCEEDED(hr))
298                 hr = OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
299         }
300
301         if (SUCCEEDED(hr))
302         {
303             This->rtStreamStart = tStart;
304             This->state = State_Running;
305         }
306     }
307     LeaveCriticalSection(&This->csFilter);
308
309     return hr;
310 }
311
312 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
313 {
314     TransformFilterImpl *This = (TransformFilterImpl *)iface;
315
316     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
317
318     EnterCriticalSection(&This->csFilter);
319     {
320         *pState = This->state;
321     }
322     LeaveCriticalSection(&This->csFilter);
323
324     return S_OK;
325 }
326
327 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
328 {
329     TransformFilterImpl *This = (TransformFilterImpl *)iface;
330
331     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
332
333     EnterCriticalSection(&This->csFilter);
334     {
335         if (This->pClock)
336             IReferenceClock_Release(This->pClock);
337         This->pClock = pClock;
338         if (This->pClock)
339             IReferenceClock_AddRef(This->pClock);
340     }
341     LeaveCriticalSection(&This->csFilter);
342
343     return S_OK;
344 }
345
346 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
347 {
348     TransformFilterImpl *This = (TransformFilterImpl *)iface;
349
350     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
351
352     EnterCriticalSection(&This->csFilter);
353     {
354         *ppClock = This->pClock;
355         if (This->pClock)
356             IReferenceClock_AddRef(This->pClock);
357     }
358     LeaveCriticalSection(&This->csFilter);
359
360     return S_OK;
361 }
362
363 /** IBaseFilter implementation **/
364
365 static IPin* WINAPI TransformFilter_GetPin(IBaseFilter *iface, int pos)
366 {
367     TransformFilterImpl *This = (TransformFilterImpl *)iface;
368
369     if (pos >= This->npins || pos < 0)
370         return NULL;
371
372     IPin_AddRef(This->ppPins[pos]);
373     return This->ppPins[pos];
374 }
375
376 static LONG WINAPI TransformFilter_GetPinCount(IBaseFilter *iface)
377 {
378     TransformFilterImpl *This = (TransformFilterImpl *)iface;
379
380     return (This->npins+1);
381 }
382
383 static LONG WINAPI TransformFilter_GetPinVersion(IBaseFilter *iface)
384 {
385     /* Our pins are static, not changing so setting static tick count is ok */
386     return 0;
387 }
388
389 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
390 {
391     TransformFilterImpl *This = (TransformFilterImpl *)iface;
392
393     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
394
395     return EnumPins_Construct(iface, TransformFilter_GetPin, TransformFilter_GetPinCount, TransformFilter_GetPinVersion, ppEnum);
396 }
397
398 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
399 {
400     TransformFilterImpl *This = (TransformFilterImpl *)iface;
401
402     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
403
404     return E_NOTIMPL;
405 }
406
407 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
408 {
409     TransformFilterImpl *This = (TransformFilterImpl *)iface;
410
411     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
412
413     strcpyW(pInfo->achName, This->filterInfo.achName);
414     pInfo->pGraph = This->filterInfo.pGraph;
415
416     if (pInfo->pGraph)
417         IFilterGraph_AddRef(pInfo->pGraph);
418
419     return S_OK;
420 }
421
422 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
423 {
424     HRESULT hr = S_OK;
425     TransformFilterImpl *This = (TransformFilterImpl *)iface;
426
427     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
428
429     EnterCriticalSection(&This->csFilter);
430     {
431         if (pName)
432             strcpyW(This->filterInfo.achName, pName);
433         else
434             *This->filterInfo.achName = '\0';
435         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
436     }
437     LeaveCriticalSection(&This->csFilter);
438
439     return hr;
440 }
441
442 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
443 {
444     TransformFilterImpl *This = (TransformFilterImpl *)iface;
445     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
446     return E_NOTIMPL;
447 }
448
449 static const IBaseFilterVtbl TransformFilter_Vtbl =
450 {
451     TransformFilter_QueryInterface,
452     TransformFilter_AddRef,
453     TransformFilter_Release,
454     TransformFilter_GetClassID,
455     TransformFilter_Stop,
456     TransformFilter_Pause,
457     TransformFilter_Run,
458     TransformFilter_GetState,
459     TransformFilter_SetSyncSource,
460     TransformFilter_GetSyncSource,
461     TransformFilter_EnumPins,
462     TransformFilter_FindPin,
463     TransformFilter_QueryFilterInfo,
464     TransformFilter_JoinFilterGraph,
465     TransformFilter_QueryVendorInfo
466 };
467
468 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
469 {
470     InputPin* This = (InputPin*) iface;
471     TransformFilterImpl* pTransform;
472     IPin* ppin;
473     HRESULT hr;
474     
475     TRACE("(%p)->()\n", iface);
476
477     /* Since we process samples synchronously, just forward notification downstream */
478     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
479     if (!pTransform)
480         hr = E_FAIL;
481     else
482         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
483     if (SUCCEEDED(hr))
484     {
485         hr = IPin_EndOfStream(ppin);
486         IPin_Release(ppin);
487     }
488
489     if (FAILED(hr))
490         ERR("%x\n", hr);
491     return hr;
492 }
493
494 static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
495 {
496     InputPin* This = (InputPin*) iface;
497     TransformFilterImpl* pTransform;
498     HRESULT hr;
499
500     TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);
501
502     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
503
504     hr = pTransform->pFuncsTable->pfnConnectInput(This, pmt);
505     if (SUCCEEDED(hr))
506     {
507         hr = InputPin_ReceiveConnection(iface, pReceivePin, pmt);
508         if (FAILED(hr))
509             pTransform->pFuncsTable->pfnCleanup(This);
510     }
511
512     return hr;
513 }
514
515 static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
516 {
517     InputPin* This = (InputPin*) iface;
518     TransformFilterImpl* pTransform;
519
520     TRACE("(%p)->()\n", iface);
521
522     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
523     pTransform->pFuncsTable->pfnCleanup(This);
524
525     return BasePinImpl_Disconnect(iface);
526 }
527
528 static HRESULT WINAPI TransformFilter_InputPin_BeginFlush(IPin * iface)
529 {
530     InputPin* This = (InputPin*) iface;
531     TransformFilterImpl* pTransform;
532     HRESULT hr = S_OK;
533
534     TRACE("(%p)->()\n", iface);
535
536     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
537     EnterCriticalSection(&pTransform->csFilter);
538     if (pTransform->pFuncsTable->pfnBeginFlush)
539         hr = pTransform->pFuncsTable->pfnBeginFlush(This);
540     if (SUCCEEDED(hr))
541         hr = InputPin_BeginFlush(iface);
542     LeaveCriticalSection(&pTransform->csFilter);
543     return hr;
544 }
545
546 static HRESULT WINAPI TransformFilter_InputPin_EndFlush(IPin * iface)
547 {
548     InputPin* This = (InputPin*) iface;
549     TransformFilterImpl* pTransform;
550     HRESULT hr = S_OK;
551
552     TRACE("(%p)->()\n", iface);
553
554     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
555     EnterCriticalSection(&pTransform->csFilter);
556     if (pTransform->pFuncsTable->pfnEndFlush)
557         hr = pTransform->pFuncsTable->pfnEndFlush(This);
558     if (SUCCEEDED(hr))
559         hr = InputPin_EndFlush(iface);
560     LeaveCriticalSection(&pTransform->csFilter);
561     return hr;
562 }
563
564 static HRESULT WINAPI TransformFilter_InputPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
565 {
566     InputPin* This = (InputPin*) iface;
567     TransformFilterImpl* pTransform;
568     HRESULT hr = S_OK;
569
570     TRACE("(%p)->()\n", iface);
571
572     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
573     EnterCriticalSection(&pTransform->csFilter);
574     if (pTransform->pFuncsTable->pfnNewSegment)
575         hr = pTransform->pFuncsTable->pfnNewSegment(This, tStart, tStop, dRate);
576     if (SUCCEEDED(hr))
577         hr = InputPin_NewSegment(iface, tStart, tStop, dRate);
578     LeaveCriticalSection(&pTransform->csFilter);
579     return hr;
580 }
581
582 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
583 {
584     InputPin_QueryInterface,
585     BasePinImpl_AddRef,
586     InputPin_Release,
587     InputPin_Connect,
588     TransformFilter_InputPin_ReceiveConnection,
589     TransformFilter_InputPin_Disconnect,
590     BasePinImpl_ConnectedTo,
591     BasePinImpl_ConnectionMediaType,
592     BasePinImpl_QueryPinInfo,
593     BasePinImpl_QueryDirection,
594     BasePinImpl_QueryId,
595     InputPin_QueryAccept,
596     BasePinImpl_EnumMediaTypes,
597     BasePinImpl_QueryInternalConnections,
598     TransformFilter_InputPin_EndOfStream,
599     TransformFilter_InputPin_BeginFlush,
600     TransformFilter_InputPin_EndFlush,
601     TransformFilter_InputPin_NewSegment
602 };
603
604 static HRESULT WINAPI TransformFilter_Output_GetMediaType(IPin *iface, int iPosition, AM_MEDIA_TYPE *pmt)
605 {
606     BasePin *This = (BasePin *)iface;
607     TransformFilterImpl *pTransform = (TransformFilterImpl *)This->pinInfo.pFilter;
608
609     if (iPosition < 0)
610         return E_INVALIDARG;
611     if (iPosition > 0)
612         return VFW_S_NO_MORE_ITEMS;
613     CopyMediaType(pmt, &pTransform->pmt);
614     return S_OK;
615 }
616
617 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
618 {
619     BasePin *This = (BasePin *)iface;
620     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
621
622     return EnumMediaTypes_Construct(iface, TransformFilter_Output_GetMediaType, BasePinImpl_GetMediaTypeVersion, ppEnum);
623 }
624
625 static const IPinVtbl TransformFilter_OutputPin_Vtbl =
626 {
627     OutputPin_QueryInterface,
628     BasePinImpl_AddRef,
629     OutputPin_Release,
630     OutputPin_Connect,
631     OutputPin_ReceiveConnection,
632     OutputPin_Disconnect,
633     BasePinImpl_ConnectedTo,
634     BasePinImpl_ConnectionMediaType,
635     BasePinImpl_QueryPinInfo,
636     BasePinImpl_QueryDirection,
637     BasePinImpl_QueryId,
638     TransformFilter_Output_QueryAccept,
639     TransformFilter_Output_EnumMediaTypes,
640     BasePinImpl_QueryInternalConnections,
641     OutputPin_EndOfStream,
642     OutputPin_BeginFlush,
643     OutputPin_EndFlush,
644     OutputPin_NewSegment
645 };