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