user32/tests: Run tests on win95 again.
[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 IMemInputPinVtbl MemInputPin_Vtbl; 
49 static const IPinVtbl TransformFilter_OutputPin_Vtbl;
50
51 static HRESULT TransformFilter_Input_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
52 {
53     TransformFilterImpl* This = (TransformFilterImpl *)((IPinImpl *)iface)->pinInfo.pFilter;
54     TRACE("%p\n", iface);
55     dump_AM_MEDIA_TYPE(pmt);
56
57     if (This->pFuncsTable->pfnQueryConnect)
58         return This->pFuncsTable->pfnQueryConnect(This, pmt);
59     /* Assume OK if there's no query method (the connection will fail if
60        needed) */
61     return S_OK;
62 }
63
64
65 static HRESULT TransformFilter_Output_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
66 {
67     TransformFilterImpl* pTransformFilter = (TransformFilterImpl*)iface;
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
78 static inline TransformFilterImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
79 {
80     return (TransformFilterImpl *)((char*)iface - FIELD_OFFSET(TransformFilterImpl, mediaSeeking.lpVtbl));
81 }
82
83 static HRESULT WINAPI TransformFilter_Seeking_QueryInterface(IMediaSeeking * iface, REFIID riid, LPVOID * ppv)
84 {
85     TransformFilterImpl *This = impl_from_IMediaSeeking(iface);
86
87     return IUnknown_QueryInterface((IUnknown *)This, riid, ppv);
88 }
89
90 static ULONG WINAPI TransformFilter_Seeking_AddRef(IMediaSeeking * iface)
91 {
92     TransformFilterImpl *This = impl_from_IMediaSeeking(iface);
93
94     return IUnknown_AddRef((IUnknown *)This);
95 }
96
97 static ULONG WINAPI TransformFilter_Seeking_Release(IMediaSeeking * iface)
98 {
99     TransformFilterImpl *This = impl_from_IMediaSeeking(iface);
100
101     return IUnknown_Release((IUnknown *)This);
102 }
103
104 static const IMediaSeekingVtbl TransformFilter_Seeking_Vtbl =
105 {
106     TransformFilter_Seeking_QueryInterface,
107     TransformFilter_Seeking_AddRef,
108     TransformFilter_Seeking_Release,
109     MediaSeekingImpl_GetCapabilities,
110     MediaSeekingImpl_CheckCapabilities,
111     MediaSeekingImpl_IsFormatSupported,
112     MediaSeekingImpl_QueryPreferredFormat,
113     MediaSeekingImpl_GetTimeFormat,
114     MediaSeekingImpl_IsUsingTimeFormat,
115     MediaSeekingImpl_SetTimeFormat,
116     MediaSeekingImpl_GetDuration,
117     MediaSeekingImpl_GetStopPosition,
118     MediaSeekingImpl_GetCurrentPosition,
119     MediaSeekingImpl_ConvertTimeFormat,
120     MediaSeekingImpl_SetPositions,
121     MediaSeekingImpl_GetPositions,
122     MediaSeekingImpl_GetAvailable,
123     MediaSeekingImpl_SetRate,
124     MediaSeekingImpl_GetRate,
125     MediaSeekingImpl_GetPreroll
126 };
127
128 /* These shouldn't be implemented by default.
129  * Usually only source filters should implement these
130  * and even it's not needed all of the time
131  */
132 static HRESULT TransformFilter_ChangeCurrent(IBaseFilter *iface)
133 {
134     TRACE("(%p) filter hasn't implemented current position change!\n", iface);
135     return S_OK;
136 }
137
138 static HRESULT TransformFilter_ChangeStop(IBaseFilter *iface)
139 {
140     TRACE("(%p) filter hasn't implemented stop position change!\n", iface);
141     return S_OK;
142 }
143
144 static HRESULT TransformFilter_ChangeRate(IBaseFilter *iface)
145 {
146     TRACE("(%p) filter hasn't implemented rate change!\n", iface);
147     return S_OK;
148 }
149
150 HRESULT TransformFilter_Create(TransformFilterImpl* pTransformFilter, const CLSID* pClsid, const TransformFuncsTable* pFuncsTable, CHANGEPROC stop, CHANGEPROC current, CHANGEPROC rate)
151 {
152     HRESULT hr;
153     PIN_INFO piInput;
154     PIN_INFO piOutput;
155
156     /* pTransformFilter is already allocated */
157     pTransformFilter->clsid = *pClsid;
158     pTransformFilter->pFuncsTable = pFuncsTable;
159
160     pTransformFilter->lpVtbl = &TransformFilter_Vtbl;
161
162     pTransformFilter->refCount = 1;
163     InitializeCriticalSection(&pTransformFilter->csFilter);
164     pTransformFilter->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TransformFilterImpl.csFilter");
165     pTransformFilter->state = State_Stopped;
166     pTransformFilter->pClock = NULL;
167     ZeroMemory(&pTransformFilter->filterInfo, sizeof(FILTER_INFO));
168     ZeroMemory(&pTransformFilter->pmt, sizeof(pTransformFilter->pmt));
169     pTransformFilter->npins = 2;
170
171     pTransformFilter->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
172
173     /* construct input pin */
174     piInput.dir = PINDIR_INPUT;
175     piInput.pFilter = (IBaseFilter *)pTransformFilter;
176     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
177     piOutput.dir = PINDIR_OUTPUT;
178     piOutput.pFilter = (IBaseFilter *)pTransformFilter;
179     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
180
181     hr = InputPin_Construct(&TransformFilter_InputPin_Vtbl, &piInput, (SAMPLEPROC_PUSH)pFuncsTable->pfnProcessSampleData, NULL, TransformFilter_Input_QueryAccept, NULL, &pTransformFilter->csFilter, NULL, &pTransformFilter->ppPins[0]);
182
183     if (SUCCEEDED(hr))
184     {
185         ALLOCATOR_PROPERTIES props;
186         props.cbAlign = 1;
187         props.cbPrefix = 0;
188         props.cbBuffer = 0; /* Will be updated at connection time */
189         props.cBuffers = 1;
190
191        ((InputPin *)pTransformFilter->ppPins[0])->pin.pUserData = pTransformFilter->ppPins[0];
192
193         hr = OutputPin_Construct(&TransformFilter_OutputPin_Vtbl, sizeof(OutputPin), &piOutput, &props, pTransformFilter, TransformFilter_Output_QueryAccept, &pTransformFilter->csFilter, &pTransformFilter->ppPins[1]);
194
195         if (FAILED(hr))
196             ERR("Cannot create output pin (%x)\n", hr);
197         else
198         {
199             if (!stop)
200                 stop = TransformFilter_ChangeStop;
201             if (!current)
202                 current = TransformFilter_ChangeCurrent;
203             if (!rate)
204                 rate = TransformFilter_ChangeRate;
205
206             MediaSeekingImpl_Init((IBaseFilter*)pTransformFilter, stop, current, rate, &pTransformFilter->mediaSeeking, &pTransformFilter->csFilter);
207             pTransformFilter->mediaSeeking.lpVtbl = &TransformFilter_Seeking_Vtbl;
208         }
209     }
210     else
211     {
212         CoTaskMemFree(pTransformFilter->ppPins);
213         pTransformFilter->csFilter.DebugInfo->Spare[0] = 0;
214         DeleteCriticalSection(&pTransformFilter->csFilter);
215         CoTaskMemFree(pTransformFilter);
216     }
217
218     return hr;
219 }
220
221 static HRESULT WINAPI TransformFilter_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
222 {
223     TransformFilterImpl *This = (TransformFilterImpl *)iface;
224     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
225
226     *ppv = NULL;
227
228     if (IsEqualIID(riid, &IID_IUnknown))
229         *ppv = (LPVOID)This;
230     else if (IsEqualIID(riid, &IID_IPersist))
231         *ppv = (LPVOID)This;
232     else if (IsEqualIID(riid, &IID_IMediaFilter))
233         *ppv = (LPVOID)This;
234     else if (IsEqualIID(riid, &IID_IBaseFilter))
235         *ppv = (LPVOID)This;
236     else if (IsEqualIID(riid, &IID_IMediaSeeking))
237         *ppv = &This->mediaSeeking;
238
239     if (*ppv)
240     {
241         IUnknown_AddRef((IUnknown *)(*ppv));
242         return S_OK;
243     }
244
245     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
246         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
247
248     return E_NOINTERFACE;
249 }
250
251 static ULONG WINAPI TransformFilter_AddRef(IBaseFilter * iface)
252 {
253     TransformFilterImpl *This = (TransformFilterImpl *)iface;
254     ULONG refCount = InterlockedIncrement(&This->refCount);
255
256     TRACE("(%p/%p)->() AddRef from %d\n", This, iface, refCount - 1);
257
258     return refCount;
259 }
260
261 static ULONG WINAPI TransformFilter_Release(IBaseFilter * iface)
262 {
263     TransformFilterImpl *This = (TransformFilterImpl *)iface;
264     ULONG refCount = InterlockedDecrement(&This->refCount);
265
266     TRACE("(%p/%p)->() Release from %d\n", This, iface, refCount + 1);
267
268     if (!refCount)
269     {
270         ULONG i;
271
272         if (This->pClock)
273             IReferenceClock_Release(This->pClock);
274
275         for (i = 0; i < This->npins; i++)
276         {
277             IPin *pConnectedTo;
278
279             if (SUCCEEDED(IPin_ConnectedTo(This->ppPins[i], &pConnectedTo)))
280             {
281                 IPin_Disconnect(pConnectedTo);
282                 IPin_Release(pConnectedTo);
283             }
284             IPin_Disconnect(This->ppPins[i]);
285
286             IPin_Release(This->ppPins[i]);
287         }
288
289         CoTaskMemFree(This->ppPins);
290         This->lpVtbl = NULL;
291
292         This->csFilter.DebugInfo->Spare[0] = 0;
293         DeleteCriticalSection(&This->csFilter);
294
295         TRACE("Destroying transform filter\n");
296         FreeMediaType(&This->pmt);
297         CoTaskMemFree(This);
298
299         return 0;
300     }
301     else
302         return refCount;
303 }
304
305 /** IPersist methods **/
306
307 static HRESULT WINAPI TransformFilter_GetClassID(IBaseFilter * iface, CLSID * pClsid)
308 {
309     TransformFilterImpl *This = (TransformFilterImpl *)iface;
310
311     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
312
313     *pClsid = This->clsid;
314
315     return S_OK;
316 }
317
318 /** IMediaFilter methods **/
319
320 static HRESULT WINAPI TransformFilter_Stop(IBaseFilter * iface)
321 {
322     TransformFilterImpl *This = (TransformFilterImpl *)iface;
323     HRESULT hr = S_OK;
324
325     TRACE("(%p/%p)\n", This, iface);
326
327     EnterCriticalSection(&This->csFilter);
328     {
329         This->state = State_Stopped;
330         if (This->pFuncsTable->pfnProcessEnd)
331             hr = This->pFuncsTable->pfnProcessEnd(This);
332     }
333     LeaveCriticalSection(&This->csFilter);
334
335     return hr;
336 }
337
338 static HRESULT WINAPI TransformFilter_Pause(IBaseFilter * iface)
339 {
340     TransformFilterImpl *This = (TransformFilterImpl *)iface;
341     HRESULT hr;
342
343     TRACE("(%p/%p)->()\n", This, iface);
344
345     EnterCriticalSection(&This->csFilter);
346     {
347         if (This->state == State_Stopped)
348             hr = IBaseFilter_Run(iface, -1);
349         else
350             hr = S_OK;
351
352         if (SUCCEEDED(hr))
353             This->state = State_Paused;
354     }
355     LeaveCriticalSection(&This->csFilter);
356
357     return hr;
358 }
359
360 static HRESULT WINAPI TransformFilter_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
361 {
362     HRESULT hr = S_OK;
363     TransformFilterImpl *This = (TransformFilterImpl *)iface;
364
365     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
366
367     EnterCriticalSection(&This->csFilter);
368     {
369         if (This->state == State_Stopped)
370         {
371             ((InputPin *)This->ppPins[0])->end_of_stream = 0;
372             if (This->pFuncsTable->pfnProcessBegin)
373                 hr = This->pFuncsTable->pfnProcessBegin(This);
374             if (SUCCEEDED(hr))
375                 hr = OutputPin_CommitAllocator((OutputPin *)This->ppPins[1]);
376         }
377
378         if (SUCCEEDED(hr))
379         {
380             This->rtStreamStart = tStart;
381             This->state = State_Running;
382         }
383     }
384     LeaveCriticalSection(&This->csFilter);
385
386     return hr;
387 }
388
389 static HRESULT WINAPI TransformFilter_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
390 {
391     TransformFilterImpl *This = (TransformFilterImpl *)iface;
392
393     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
394
395     EnterCriticalSection(&This->csFilter);
396     {
397         *pState = This->state;
398     }
399     LeaveCriticalSection(&This->csFilter);
400
401     return S_OK;
402 }
403
404 static HRESULT WINAPI TransformFilter_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
405 {
406     TransformFilterImpl *This = (TransformFilterImpl *)iface;
407
408     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
409
410     EnterCriticalSection(&This->csFilter);
411     {
412         if (This->pClock)
413             IReferenceClock_Release(This->pClock);
414         This->pClock = pClock;
415         if (This->pClock)
416             IReferenceClock_AddRef(This->pClock);
417     }
418     LeaveCriticalSection(&This->csFilter);
419
420     return S_OK;
421 }
422
423 static HRESULT WINAPI TransformFilter_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
424 {
425     TransformFilterImpl *This = (TransformFilterImpl *)iface;
426
427     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
428
429     EnterCriticalSection(&This->csFilter);
430     {
431         *ppClock = This->pClock;
432         if (This->pClock)
433             IReferenceClock_AddRef(This->pClock);
434     }
435     LeaveCriticalSection(&This->csFilter);
436
437     return S_OK;
438 }
439
440 /** IBaseFilter implementation **/
441
442 static HRESULT TransformFilter_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
443 {
444     TransformFilterImpl *This = (TransformFilterImpl *)iface;
445
446     /* Our pins are static, not changing so setting static tick count is ok */
447     *lastsynctick = 0;
448
449     if (pos >= This->npins)
450         return S_FALSE;
451
452     *pin = This->ppPins[pos];
453     IPin_AddRef(*pin);
454     return S_OK;
455 }
456
457 static HRESULT WINAPI TransformFilter_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
458 {
459     TransformFilterImpl *This = (TransformFilterImpl *)iface;
460
461     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
462
463     return IEnumPinsImpl_Construct(ppEnum, TransformFilter_GetPin, iface);
464 }
465
466 static HRESULT WINAPI TransformFilter_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
467 {
468     TransformFilterImpl *This = (TransformFilterImpl *)iface;
469
470     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
471
472     return E_NOTIMPL;
473 }
474
475 static HRESULT WINAPI TransformFilter_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
476 {
477     TransformFilterImpl *This = (TransformFilterImpl *)iface;
478
479     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
480
481     strcpyW(pInfo->achName, This->filterInfo.achName);
482     pInfo->pGraph = This->filterInfo.pGraph;
483
484     if (pInfo->pGraph)
485         IFilterGraph_AddRef(pInfo->pGraph);
486
487     return S_OK;
488 }
489
490 static HRESULT WINAPI TransformFilter_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
491 {
492     HRESULT hr = S_OK;
493     TransformFilterImpl *This = (TransformFilterImpl *)iface;
494
495     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
496
497     EnterCriticalSection(&This->csFilter);
498     {
499         if (pName)
500             strcpyW(This->filterInfo.achName, pName);
501         else
502             *This->filterInfo.achName = '\0';
503         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
504     }
505     LeaveCriticalSection(&This->csFilter);
506
507     return hr;
508 }
509
510 static HRESULT WINAPI TransformFilter_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
511 {
512     TransformFilterImpl *This = (TransformFilterImpl *)iface;
513     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
514     return E_NOTIMPL;
515 }
516
517 static const IBaseFilterVtbl TransformFilter_Vtbl =
518 {
519     TransformFilter_QueryInterface,
520     TransformFilter_AddRef,
521     TransformFilter_Release,
522     TransformFilter_GetClassID,
523     TransformFilter_Stop,
524     TransformFilter_Pause,
525     TransformFilter_Run,
526     TransformFilter_GetState,
527     TransformFilter_SetSyncSource,
528     TransformFilter_GetSyncSource,
529     TransformFilter_EnumPins,
530     TransformFilter_FindPin,
531     TransformFilter_QueryFilterInfo,
532     TransformFilter_JoinFilterGraph,
533     TransformFilter_QueryVendorInfo
534 };
535
536 static HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface)
537 {
538     InputPin* This = (InputPin*) iface;
539     TransformFilterImpl* pTransform;
540     IPin* ppin;
541     HRESULT hr;
542     
543     TRACE("(%p)->()\n", iface);
544
545     /* Since we process samples synchronously, just forward notification downstream */
546     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
547     if (!pTransform)
548         hr = E_FAIL;
549     else
550         hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin);
551     if (SUCCEEDED(hr))
552     {
553         hr = IPin_EndOfStream(ppin);
554         IPin_Release(ppin);
555     }
556
557     if (FAILED(hr))
558         ERR("%x\n", hr);
559     return hr;
560 }
561
562 static HRESULT WINAPI TransformFilter_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
563 {
564     InputPin* This = (InputPin*) iface;
565     TransformFilterImpl* pTransform;
566     HRESULT hr;
567
568     TRACE("(%p)->(%p, %p)\n", iface, pReceivePin, pmt);
569
570     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
571
572     hr = pTransform->pFuncsTable->pfnConnectInput(This, pmt);
573     if (SUCCEEDED(hr))
574     {
575         hr = InputPin_ReceiveConnection(iface, pReceivePin, pmt);
576         if (FAILED(hr))
577             pTransform->pFuncsTable->pfnCleanup(This);
578     }
579
580     return hr;
581 }
582
583 static HRESULT WINAPI TransformFilter_InputPin_Disconnect(IPin * iface)
584 {
585     InputPin* This = (InputPin*) iface;
586     TransformFilterImpl* pTransform;
587
588     TRACE("(%p)->()\n", iface);
589
590     pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter;
591     pTransform->pFuncsTable->pfnCleanup(This);
592
593     return IPinImpl_Disconnect(iface);
594 }
595
596 static const IPinVtbl TransformFilter_InputPin_Vtbl = 
597 {
598     InputPin_QueryInterface,
599     IPinImpl_AddRef,
600     InputPin_Release,
601     InputPin_Connect,
602     TransformFilter_InputPin_ReceiveConnection,
603     TransformFilter_InputPin_Disconnect,
604     IPinImpl_ConnectedTo,
605     IPinImpl_ConnectionMediaType,
606     IPinImpl_QueryPinInfo,
607     IPinImpl_QueryDirection,
608     IPinImpl_QueryId,
609     IPinImpl_QueryAccept,
610     IPinImpl_EnumMediaTypes,
611     IPinImpl_QueryInternalConnections,
612     TransformFilter_InputPin_EndOfStream,
613     InputPin_BeginFlush,
614     InputPin_EndFlush,
615     InputPin_NewSegment
616 };
617
618 static HRESULT WINAPI TransformFilter_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
619 {
620     IPinImpl *This = (IPinImpl *)iface;
621     TransformFilterImpl *pTransform = (TransformFilterImpl *)This->pinInfo.pFilter;
622     ENUMMEDIADETAILS emd;
623
624     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
625
626     emd.cMediaTypes = 1;
627     emd.pMediaTypes = &pTransform->pmt;
628
629     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
630 }
631
632 static const IPinVtbl TransformFilter_OutputPin_Vtbl =
633 {
634     OutputPin_QueryInterface,
635     IPinImpl_AddRef,
636     OutputPin_Release,
637     OutputPin_Connect,
638     OutputPin_ReceiveConnection,
639     OutputPin_Disconnect,
640     IPinImpl_ConnectedTo,
641     IPinImpl_ConnectionMediaType,
642     IPinImpl_QueryPinInfo,
643     IPinImpl_QueryDirection,
644     IPinImpl_QueryId,
645     IPinImpl_QueryAccept,
646     TransformFilter_Output_EnumMediaTypes,
647     IPinImpl_QueryInternalConnections,
648     OutputPin_EndOfStream,
649     OutputPin_BeginFlush,
650     OutputPin_EndFlush,
651     OutputPin_NewSegment
652 };
653
654 static const IMemInputPinVtbl MemInputPin_Vtbl = 
655 {
656     MemInputPin_QueryInterface,
657     MemInputPin_AddRef,
658     MemInputPin_Release,
659     MemInputPin_GetAllocator,
660     MemInputPin_NotifyAllocator,
661     MemInputPin_GetAllocatorRequirements,
662     MemInputPin_Receive,
663     MemInputPin_ReceiveMultiple,
664     MemInputPin_ReceiveCanBlock
665 };