Consolidate secondary buffer list processing and locking.
[wine] / dlls / quartz / avidec.c
1 /*
2  * AVI Decompressor (VFW decompressors wrapper)
3  *
4  * Copyright 2004 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "uuids.h"
28 #include "aviriff.h"
29 #include "mmreg.h"
30 #include "vfwmsgs.h"
31 #include "amvideo.h"
32 #include "windef.h"
33 #include "winbase.h"
34 #include "dshow.h"
35 #include "strmif.h"
36 #include "vfwmsgs.h"
37 #include "evcode.h"
38 #include "vfw.h"
39 /* #include "fourcc.h" */
40 /* #include "avcodec.h" */
41
42 #include <assert.h>
43
44 #include "wine/unicode.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
48
49 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
50 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
51
52 static const IBaseFilterVtbl AVIDec_Vtbl;
53 static const IPinVtbl AVIDec_InputPin_Vtbl;
54 static const IMemInputPinVtbl MemInputPin_Vtbl; 
55 static const IPinVtbl AVIDec_OutputPin_Vtbl;
56
57 typedef struct AVIDecImpl
58 {
59     const IBaseFilterVtbl * lpVtbl;
60
61     ULONG refCount;
62     CRITICAL_SECTION csFilter;
63     FILTER_STATE state;
64     REFERENCE_TIME rtStreamStart;
65     IReferenceClock * pClock;
66     FILTER_INFO filterInfo;
67
68     IPin ** ppPins;
69
70     HIC hvid;
71     int init;
72 } AVIDecImpl;
73
74 static DWORD AVIDec_SendSampleData(AVIDecImpl* This, LPBYTE data, DWORD size)
75 {
76     VIDEOINFOHEADER* format;
77     AM_MEDIA_TYPE amt;
78     BITMAPINFOHEADER bi;
79     HRESULT hr;
80     DWORD res;
81     IMediaSample* pSample = NULL;
82     DWORD cbDstStream;
83     LPBYTE pbDstStream;
84
85     hr = IPin_ConnectionMediaType(This->ppPins[0], &amt);
86     if (FAILED(hr)) {
87         ERR("Unable to retrieve media type\n");
88         goto error;
89     }
90     format = (VIDEOINFOHEADER*)amt.pbFormat;
91
92     /* Fill a bitmap header for output */
93     bi.biSize = sizeof(bi);
94     bi.biWidth = format->bmiHeader.biWidth;
95     bi.biHeight = format->bmiHeader.biHeight;
96     bi.biPlanes = 1;
97     bi.biBitCount = format->bmiHeader.biBitCount;
98     bi.biCompression = 0;
99     bi.biSizeImage = bi.biWidth * bi.biHeight * bi.biBitCount / 8;
100
101     hr = OutputPin_GetDeliveryBuffer((OutputPin*)This->ppPins[1], &pSample, NULL, NULL, 0);
102     if (FAILED(hr)) {
103         ERR("Unable to get delivery buffer (%lx)\n", hr);
104         goto error;
105     }
106     
107     hr = IMediaSample_SetActualDataLength(pSample, 0);
108     assert(hr == S_OK);
109
110     hr = IMediaSample_GetPointer(pSample, &pbDstStream);
111     if (FAILED(hr)) {
112         ERR("Unable to get pointer to buffer (%lx)\n", hr);
113         goto error;
114     }
115     cbDstStream = IMediaSample_GetSize(pSample);
116     if (cbDstStream < bi.biSizeImage) {
117         ERR("Sample size is too small %ld < %ld\n", cbDstStream, bi.biSizeImage);
118         hr = E_FAIL;
119         goto error;
120     }
121
122     res = ICDecompress(This->hvid, 0, &format->bmiHeader, data, &bi, pbDstStream);
123     if (res != ICERR_OK)
124         ERR("Error occurred during the decompression (%lx)\n", res);
125     
126     hr = OutputPin_SendSample((OutputPin*)This->ppPins[1], pSample);
127     if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) {
128         ERR("Error sending sample (%lx)\n", hr);
129         goto error;
130     }
131
132     return S_OK;
133
134 error:
135     /* If we have a sample that has not been delivered, release it */
136     if (pSample)
137         IMediaSample_Release(pSample);
138
139     return hr;
140 }
141
142 static HRESULT AVIDec_Sample(LPVOID iface, IMediaSample * pSample)
143 {
144     ICOM_THIS(AVIDecImpl, iface);
145     LPBYTE pbSrcStream = NULL;
146     long cbSrcStream = 0;
147     REFERENCE_TIME tStart, tStop;
148     HRESULT hr;
149
150     TRACE("%p %p\n", iface, pSample);
151     
152     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
153     if (FAILED(hr))
154     {
155         ERR("Cannot get pointer to sample data (%lx)\n", hr);
156         return hr;
157     }
158
159     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
160     if (FAILED(hr))
161         ERR("Cannot get sample time (%lx)\n", hr);
162     
163     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
164
165     TRACE("Sample data ptr = %p, size = %ld\n", pbSrcStream, cbSrcStream);
166
167 #if 0 /* For debugging purpose */
168     {
169         int i;
170         for(i = 0; i < cbSrcStream; i++)
171         {
172             if ((i!=0) && !(i%16))
173                 DPRINTF("\n");
174             DPRINTF("%02x ", pbSrcStream[i]);
175         }
176         DPRINTF("\n");
177     }
178 #endif
179     
180     AVIDec_SendSampleData(This, pbSrcStream, cbSrcStream);
181
182     /* We have finished with the incoming sample, we must release it now */
183     IMediaSample_Release(pSample);
184         
185     return S_OK;
186 }
187
188 static HRESULT AVIDec_Input_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
189 {
190     AVIDecImpl* pAVIDec = (AVIDecImpl*)iface;
191     TRACE("%p\n", iface);
192     dump_AM_MEDIA_TYPE(pmt);
193
194     if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
195         (!memcmp(((char*)&pmt->subtype)+4, ((char*)&MEDIATYPE_Video)+4, sizeof(GUID)-4)) && /* Check root (GUID w/o FOURCC) */
196         (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)))
197     {
198         HIC drv;
199         VIDEOINFOHEADER* format = (VIDEOINFOHEADER*)pmt->pbFormat;
200         drv = ICLocate(pmt->majortype.Data1, pmt->subtype.Data1, &format->bmiHeader, NULL, ICMODE_DECOMPRESS);
201         if (drv)
202         {
203             AM_MEDIA_TYPE* outpmt = &((OutputPin*)pAVIDec->ppPins[1])->pin.mtCurrent;
204             const CLSID* outsubtype;
205             switch(format->bmiHeader.biBitCount)
206             {
207                 case 32: outsubtype = &MEDIATYPE_Video; break;
208                 case 24: outsubtype = &MEDIASUBTYPE_RGB24; break;
209                 case 16: outsubtype = &MEDIASUBTYPE_RGB565; break;
210                 case 8:  outsubtype = &MEDIASUBTYPE_RGB8; break;
211                 default:
212                     FIXME("Depth %d not supported\n", format->bmiHeader.biBitCount);
213                     ICClose(drv);
214                     return S_FALSE;
215             }
216             CopyMediaType( outpmt, pmt);
217             outpmt->subtype = *outsubtype;
218             pAVIDec->hvid = drv;
219             pAVIDec->init = 1;
220             TRACE("Connection accepted\n");
221             return S_OK;
222         }
223         TRACE("Unable to find a suitable VFW decompressor\n");
224     }
225     
226     TRACE("Connection refused\n");
227     return S_FALSE;
228 }
229
230
231 static HRESULT AVIDec_Output_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
232 {
233     AVIDecImpl* pAVIDec = (AVIDecImpl*)iface;
234     AM_MEDIA_TYPE* outpmt = &((OutputPin*)pAVIDec->ppPins[1])->pin.mtCurrent;
235     TRACE("%p\n", iface);
236
237     if (IsEqualIID(&pmt->majortype, &outpmt->majortype) && IsEqualIID(&pmt->subtype, &outpmt->subtype))
238         return S_OK;
239     return S_FALSE;
240 }
241
242 static HRESULT AVIDec_InputPin_Construct(const PIN_INFO * pPinInfo, SAMPLEPROC pSampleProc, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
243 {
244     InputPin * pPinImpl;
245
246     *ppPin = NULL;
247
248     if (pPinInfo->dir != PINDIR_INPUT)
249     {
250         ERR("Pin direction(%x) != PINDIR_INPUT\n", pPinInfo->dir);
251         return E_INVALIDARG;
252     }
253
254     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
255
256     if (!pPinImpl)
257         return E_OUTOFMEMORY;
258     TRACE("QA : %p %p\n", pQueryAccept, AVIDec_Input_QueryAccept);
259     if (SUCCEEDED(InputPin_Init(pPinInfo, pSampleProc, pUserData, pQueryAccept, pCritSec, pPinImpl)))
260     {
261         pPinImpl->pin.lpVtbl = &AVIDec_InputPin_Vtbl;
262         pPinImpl->lpVtblMemInput = &MemInputPin_Vtbl;
263         
264         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
265         return S_OK;
266     }
267     return E_FAIL;
268 }
269
270 HRESULT AVIDec_OutputPin_Construct(const PIN_INFO * pPinInfo, ALLOCATOR_PROPERTIES *props, LPVOID pUserData, QUERYACCEPTPROC pQueryAccept, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
271 {
272     OutputPin * pPinImpl;
273
274     *ppPin = NULL;
275
276     if (pPinInfo->dir != PINDIR_OUTPUT)
277     {
278         ERR("Pin direction(%x) != PINDIR_OUTPUT\n", pPinInfo->dir);
279         return E_INVALIDARG;
280     }
281
282     pPinImpl = CoTaskMemAlloc(sizeof(*pPinImpl));
283
284     if (!pPinImpl)
285         return E_OUTOFMEMORY;
286
287     if (SUCCEEDED(OutputPin_Init(pPinInfo, props, pUserData, pQueryAccept, pCritSec, pPinImpl)))
288     {
289         pPinImpl->pin.lpVtbl = &AVIDec_OutputPin_Vtbl;
290         
291         *ppPin = (IPin *)(&pPinImpl->pin.lpVtbl);
292         return S_OK;
293     }
294     return E_FAIL;
295 }
296
297 HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv)
298 {
299     HRESULT hr;
300     PIN_INFO piInput;
301     PIN_INFO piOutput;
302     AVIDecImpl * pAVIDec;
303
304     TRACE("(%p, %p)\n", pUnkOuter, ppv);
305
306     *ppv = NULL;
307
308     if (pUnkOuter)
309         return CLASS_E_NOAGGREGATION;
310     
311     pAVIDec = CoTaskMemAlloc(sizeof(AVIDecImpl));
312
313     pAVIDec->lpVtbl = &AVIDec_Vtbl;
314     
315     pAVIDec->refCount = 1;
316     InitializeCriticalSection(&pAVIDec->csFilter);
317     pAVIDec->state = State_Stopped;
318     pAVIDec->pClock = NULL;
319     pAVIDec->init = 0;
320     ZeroMemory(&pAVIDec->filterInfo, sizeof(FILTER_INFO));
321
322     pAVIDec->ppPins = CoTaskMemAlloc(2 * sizeof(IPin *));
323
324     /* construct input pin */
325     piInput.dir = PINDIR_INPUT;
326     piInput.pFilter = (IBaseFilter *)pAVIDec;
327     strncpyW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
328     piOutput.dir = PINDIR_OUTPUT;
329     piOutput.pFilter = (IBaseFilter *)pAVIDec;
330     strncpyW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
331
332     hr = AVIDec_InputPin_Construct(&piInput, AVIDec_Sample, (LPVOID)pAVIDec, AVIDec_Input_QueryAccept, &pAVIDec->csFilter, &pAVIDec->ppPins[0]);
333
334     if (SUCCEEDED(hr))
335     {
336         hr = AVIDec_OutputPin_Construct(&piOutput, NULL, NULL, AVIDec_Output_QueryAccept, &pAVIDec->csFilter, &pAVIDec->ppPins[1]);
337
338         if (FAILED(hr))
339             ERR("Cannot create output pin (%lx)\n", hr);
340         
341         *ppv = (LPVOID)pAVIDec;
342     }
343     else
344     {
345         CoTaskMemFree(pAVIDec->ppPins);
346         DeleteCriticalSection(&pAVIDec->csFilter);
347         CoTaskMemFree(pAVIDec);
348     }
349
350     return hr;
351 }
352
353 static HRESULT WINAPI AVIDec_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
354 {
355     ICOM_THIS(AVIDecImpl, iface);
356     TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
357
358     *ppv = NULL;
359
360     if (IsEqualIID(riid, &IID_IUnknown))
361         *ppv = (LPVOID)This;
362     else if (IsEqualIID(riid, &IID_IPersist))
363         *ppv = (LPVOID)This;
364     else if (IsEqualIID(riid, &IID_IMediaFilter))
365         *ppv = (LPVOID)This;
366     else if (IsEqualIID(riid, &IID_IBaseFilter))
367         *ppv = (LPVOID)This;
368
369     if (*ppv)
370     {
371         IUnknown_AddRef((IUnknown *)(*ppv));
372         return S_OK;
373     }
374
375     FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
376
377     return E_NOINTERFACE;
378 }
379
380 static ULONG WINAPI AVIDec_AddRef(IBaseFilter * iface)
381 {
382     ICOM_THIS(AVIDecImpl, iface);
383     TRACE("(%p/%p)->()\n", This, iface);
384     return InterlockedIncrement(&This->refCount);
385 }
386
387 static ULONG WINAPI AVIDec_Release(IBaseFilter * iface)
388 {
389     ICOM_THIS(AVIDecImpl, iface);
390     TRACE("(%p/%p)->()\n", This, iface);
391     if (!InterlockedDecrement(&This->refCount))
392     {
393         ULONG i;
394
395         DeleteCriticalSection(&This->csFilter);
396         IReferenceClock_Release(This->pClock);
397         
398         for (i = 0; i < 2; i++)
399             IPin_Release(This->ppPins[i]);
400         
401         HeapFree(GetProcessHeap(), 0, This->ppPins);
402         This->lpVtbl = NULL;
403
404         if (This->hvid)
405             ICClose(This->hvid);
406         
407         TRACE("Destroying AVI Decompressor\n");
408         CoTaskMemFree(This);
409         
410         return 0;
411     }
412     else
413         return This->refCount;
414 }
415
416 /** IPersist methods **/
417
418 static HRESULT WINAPI AVIDec_GetClassID(IBaseFilter * iface, CLSID * pClsid)
419 {
420     ICOM_THIS(AVIDecImpl, iface);
421
422     TRACE("(%p/%p)->(%p)\n", This, iface, pClsid);
423
424     *pClsid = CLSID_AVIDec;
425
426     return S_OK;
427 }
428
429 /** IMediaFilter methods **/
430
431 static HRESULT WINAPI AVIDec_Stop(IBaseFilter * iface)
432 {
433     ICOM_THIS(AVIDecImpl, iface);
434
435     TRACE("(%p/%p)\n", This, iface);
436
437     EnterCriticalSection(&This->csFilter);
438     {
439         This->state = State_Stopped;
440     }
441     LeaveCriticalSection(&This->csFilter);
442     
443     return S_OK;
444 }
445
446 static HRESULT WINAPI AVIDec_Pause(IBaseFilter * iface)
447 {
448     ICOM_THIS(AVIDecImpl, iface);
449     
450     TRACE("(%p/%p)->()\n", This, iface);
451
452     EnterCriticalSection(&This->csFilter);
453     {
454         This->state = State_Paused;
455     }
456     LeaveCriticalSection(&This->csFilter);
457
458     return S_OK;
459 }
460
461 static HRESULT WINAPI AVIDec_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
462 {
463     HRESULT hr = S_OK;
464     ICOM_THIS(AVIDecImpl, iface);
465
466     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
467
468     EnterCriticalSection(&This->csFilter);
469     {
470         This->rtStreamStart = tStart;
471
472         This->state = State_Running;
473     }
474     LeaveCriticalSection(&This->csFilter);
475
476     return hr;
477 }
478
479 static HRESULT WINAPI AVIDec_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
480 {
481     ICOM_THIS(AVIDecImpl, iface);
482
483     TRACE("(%p/%p)->(%ld, %p)\n", This, iface, dwMilliSecsTimeout, pState);
484
485     EnterCriticalSection(&This->csFilter);
486     {
487         *pState = This->state;
488     }
489     LeaveCriticalSection(&This->csFilter);
490
491     return S_OK;
492 }
493
494 static HRESULT WINAPI AVIDec_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
495 {
496     ICOM_THIS(AVIDecImpl, iface);
497
498     TRACE("(%p/%p)->(%p)\n", This, iface, pClock);
499
500     EnterCriticalSection(&This->csFilter);
501     {
502         if (This->pClock)
503             IReferenceClock_Release(This->pClock);
504         This->pClock = pClock;
505         if (This->pClock)
506             IReferenceClock_AddRef(This->pClock);
507     }
508     LeaveCriticalSection(&This->csFilter);
509
510     return S_OK;
511 }
512
513 static HRESULT WINAPI AVIDec_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
514 {
515     ICOM_THIS(AVIDecImpl, iface);
516
517     TRACE("(%p/%p)->(%p)\n", This, iface, ppClock);
518
519     EnterCriticalSection(&This->csFilter);
520     {
521         *ppClock = This->pClock;
522         IReferenceClock_AddRef(This->pClock);
523     }
524     LeaveCriticalSection(&This->csFilter);
525     
526     return S_OK;
527 }
528
529 /** IBaseFilter implementation **/
530
531 static HRESULT WINAPI AVIDec_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
532 {
533     ENUMPINDETAILS epd;
534     ICOM_THIS(AVIDecImpl, iface);
535
536     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
537
538     epd.cPins = 2; /* input and output pins */
539     epd.ppPins = This->ppPins;
540     return IEnumPinsImpl_Construct(&epd, ppEnum);
541 }
542
543 static HRESULT WINAPI AVIDec_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
544 {
545     ICOM_THIS(AVIDecImpl, iface);
546
547     TRACE("(%p/%p)->(%p,%p)\n", This, iface, debugstr_w(Id), ppPin);
548
549     FIXME("AVISplitter::FindPin(...)\n");
550
551     /* FIXME: critical section */
552
553     return E_NOTIMPL;
554 }
555
556 static HRESULT WINAPI AVIDec_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
557 {
558     ICOM_THIS(AVIDecImpl, iface);
559
560     TRACE("(%p/%p)->(%p)\n", This, iface, pInfo);
561
562     strcpyW(pInfo->achName, This->filterInfo.achName);
563     pInfo->pGraph = This->filterInfo.pGraph;
564
565     if (pInfo->pGraph)
566         IFilterGraph_AddRef(pInfo->pGraph);
567     
568     return S_OK;
569 }
570
571 static HRESULT WINAPI AVIDec_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
572 {
573     HRESULT hr = S_OK;
574     ICOM_THIS(AVIDecImpl, iface);
575
576     TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName));
577
578     EnterCriticalSection(&This->csFilter);
579     {
580         if (pName)
581             strcpyW(This->filterInfo.achName, pName);
582         else
583             *This->filterInfo.achName = '\0';
584         This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
585     }
586     LeaveCriticalSection(&This->csFilter);
587
588     return hr;
589 }
590
591 static HRESULT WINAPI AVIDec_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
592 {
593     ICOM_THIS(AVIDecImpl, iface);
594     TRACE("(%p/%p)->(%p)\n", This, iface, pVendorInfo);
595     return E_NOTIMPL;
596 }
597
598 static const IBaseFilterVtbl AVIDec_Vtbl =
599 {
600     AVIDec_QueryInterface,
601     AVIDec_AddRef,
602     AVIDec_Release,
603     AVIDec_GetClassID,
604     AVIDec_Stop,
605     AVIDec_Pause,
606     AVIDec_Run,
607     AVIDec_GetState,
608     AVIDec_SetSyncSource,
609     AVIDec_GetSyncSource,
610     AVIDec_EnumPins,
611     AVIDec_FindPin,
612     AVIDec_QueryFilterInfo,
613     AVIDec_JoinFilterGraph,
614     AVIDec_QueryVendorInfo
615 };
616
617 static const IPinVtbl AVIDec_InputPin_Vtbl = 
618 {
619     InputPin_QueryInterface,
620     IPinImpl_AddRef,
621     InputPin_Release,
622     InputPin_Connect,
623     InputPin_ReceiveConnection,
624     IPinImpl_Disconnect,
625     IPinImpl_ConnectedTo,
626     IPinImpl_ConnectionMediaType,
627     IPinImpl_QueryPinInfo,
628     IPinImpl_QueryDirection,
629     IPinImpl_QueryId,
630     IPinImpl_QueryAccept,
631     IPinImpl_EnumMediaTypes,
632     IPinImpl_QueryInternalConnections,
633     InputPin_EndOfStream,
634     InputPin_BeginFlush,
635     InputPin_EndFlush,
636     InputPin_NewSegment
637 };
638
639 HRESULT WINAPI AVIDec_Output_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
640 {
641     ICOM_THIS(IPinImpl, iface);
642     ENUMMEDIADETAILS emd;
643
644     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
645
646     emd.cMediaTypes = 1;
647     emd.pMediaTypes = &This->mtCurrent;
648
649     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
650 }
651
652 HRESULT WINAPI AVIDec_Output_Disconnect(IPin * iface)
653 {
654     OutputPin* This = (OutputPin*)iface;
655     HRESULT hr;
656     AVIDecImpl* pAVIDec = (AVIDecImpl*)This->pin.pinInfo.pFilter;
657
658     TRACE("(%p/%p)->()\n", This, iface);
659     
660     hr = OutputPin_Disconnect(iface);
661
662     if (hr == S_OK)
663     {
664         ICClose(pAVIDec->hvid);
665         pAVIDec->hvid = 0;
666     }
667     
668     return hr;
669 }
670
671 static const IPinVtbl AVIDec_OutputPin_Vtbl = 
672 {
673     OutputPin_QueryInterface,
674     IPinImpl_AddRef,
675     OutputPin_Release,
676     OutputPin_Connect,
677     OutputPin_ReceiveConnection,
678     AVIDec_Output_Disconnect,
679     IPinImpl_ConnectedTo,
680     IPinImpl_ConnectionMediaType,
681     IPinImpl_QueryPinInfo,
682     IPinImpl_QueryDirection,
683     IPinImpl_QueryId,
684     IPinImpl_QueryAccept,
685     AVIDec_Output_EnumMediaTypes,
686     IPinImpl_QueryInternalConnections,
687     OutputPin_EndOfStream,
688     OutputPin_BeginFlush,
689     OutputPin_EndFlush,
690     OutputPin_NewSegment
691 };
692
693 static const IMemInputPinVtbl MemInputPin_Vtbl = 
694 {
695     MemInputPin_QueryInterface,
696     MemInputPin_AddRef,
697     MemInputPin_Release,
698     MemInputPin_GetAllocator,
699     MemInputPin_NotifyAllocator,
700     MemInputPin_GetAllocatorRequirements,
701     MemInputPin_Receive,
702     MemInputPin_ReceiveMultiple,
703     MemInputPin_ReceiveCanBlock
704 };