xref: /reactos/dll/directx/wine/qcap/avico.c (revision 139a3d66)
1 /*
2  * Copyright 2013 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdarg.h>
20 
21 #define COBJMACROS
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "dshow.h"
26 #include "vfw.h"
27 #include "aviriff.h"
28 
29 #include "qcap_main.h"
30 
31 #include "wine/debug.h"
32 #include "wine/heap.h"
33 
34 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
35 
36 typedef struct {
37     BaseFilter filter;
38     IPersistPropertyBag IPersistPropertyBag_iface;
39 
40     BaseInputPin *in;
41     BaseOutputPin *out;
42 
43     DWORD fcc_handler;
44     HIC hic;
45 
46     VIDEOINFOHEADER *videoinfo;
47     size_t videoinfo_size;
48     DWORD driver_flags;
49     DWORD max_frame_size;
50 
51     DWORD frame_cnt;
52 } AVICompressor;
53 
54 static inline AVICompressor *impl_from_BaseFilter(BaseFilter *filter)
55 {
56     return CONTAINING_RECORD(filter, AVICompressor, filter);
57 }
58 
59 static inline AVICompressor *impl_from_IBaseFilter(IBaseFilter *iface)
60 {
61     BaseFilter *filter = CONTAINING_RECORD(iface, BaseFilter, IBaseFilter_iface);
62     return impl_from_BaseFilter(filter);
63 }
64 
65 static inline AVICompressor *impl_from_BasePin(BasePin *pin)
66 {
67     return impl_from_IBaseFilter(pin->pinInfo.pFilter);
68 }
69 
70 static HRESULT ensure_driver(AVICompressor *This)
71 {
72     if(This->hic)
73         return S_OK;
74 
75     This->hic = ICOpen(FCC('v','i','d','c'), This->fcc_handler, ICMODE_COMPRESS);
76     if(!This->hic) {
77         FIXME("ICOpen failed\n");
78         return E_FAIL;
79     }
80 
81     return S_OK;
82 }
83 
84 static HRESULT fill_format_info(AVICompressor *This, VIDEOINFOHEADER *src_videoinfo)
85 {
86     DWORD size;
87     ICINFO icinfo;
88     HRESULT hres;
89 
90     hres = ensure_driver(This);
91     if(hres != S_OK)
92         return hres;
93 
94     size = ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
95     if(size != sizeof(icinfo))
96         return E_FAIL;
97 
98     size = ICCompressGetFormatSize(This->hic, &src_videoinfo->bmiHeader);
99     if(!size) {
100         FIXME("ICCompressGetFormatSize failed\n");
101         return E_FAIL;
102     }
103 
104     size += FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader);
105     This->videoinfo = heap_alloc(size);
106     if(!This->videoinfo)
107         return E_OUTOFMEMORY;
108 
109     This->videoinfo_size = size;
110     This->driver_flags = icinfo.dwFlags;
111     memset(This->videoinfo, 0, sizeof(*This->videoinfo));
112     ICCompressGetFormat(This->hic, &src_videoinfo->bmiHeader, &This->videoinfo->bmiHeader);
113 
114     This->videoinfo->dwBitRate = 10000000/src_videoinfo->AvgTimePerFrame * This->videoinfo->bmiHeader.biSizeImage * 8;
115     This->videoinfo->AvgTimePerFrame = src_videoinfo->AvgTimePerFrame;
116     This->max_frame_size = This->videoinfo->bmiHeader.biSizeImage;
117     return S_OK;
118 }
119 
120 static HRESULT WINAPI AVICompressor_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv)
121 {
122     AVICompressor *This = impl_from_IBaseFilter(iface);
123 
124     if(IsEqualIID(riid, &IID_IUnknown)) {
125         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
126         *ppv = &This->filter.IBaseFilter_iface;
127     }else if(IsEqualIID(riid, &IID_IPersist)) {
128         TRACE("(%p)->(IID_IPersist %p)\n", This, ppv);
129         *ppv = &This->filter.IBaseFilter_iface;
130     }else if(IsEqualIID(riid, &IID_IMediaFilter)) {
131         TRACE("(%p)->(IID_IMediaFilter %p)\n", This, ppv);
132         *ppv = &This->filter.IBaseFilter_iface;
133     }else if(IsEqualIID(riid, &IID_IBaseFilter)) {
134         TRACE("(%p)->(IID_IBaseFilter %p)\n", This, ppv);
135         *ppv = &This->filter.IBaseFilter_iface;
136     }else if(IsEqualIID(riid, &IID_IPersistPropertyBag)) {
137         TRACE("(%p)->(IID_IPersistPropertyBag %p)\n", This, ppv);
138         *ppv = &This->IPersistPropertyBag_iface;
139     }else {
140         FIXME("no interface for %s\n", debugstr_guid(riid));
141         *ppv = NULL;
142         return E_NOINTERFACE;
143     }
144 
145     IUnknown_AddRef((IUnknown*)*ppv);
146     return S_OK;
147 
148 }
149 
150 static ULONG WINAPI AVICompressor_Release(IBaseFilter *iface)
151 {
152     AVICompressor *This = impl_from_IBaseFilter(iface);
153     ULONG ref = BaseFilterImpl_Release(&This->filter.IBaseFilter_iface);
154 
155     TRACE("(%p) ref=%d\n", This, ref);
156 
157     if(!ref) {
158         if(This->hic)
159             ICClose(This->hic);
160         heap_free(This->videoinfo);
161         if(This->in)
162             BaseInputPinImpl_Release(&This->in->pin.IPin_iface);
163         if(This->out)
164             BaseOutputPinImpl_Release(&This->out->pin.IPin_iface);
165         heap_free(This);
166     }
167 
168     return ref;
169 }
170 
171 static HRESULT WINAPI AVICompressor_Stop(IBaseFilter *iface)
172 {
173     AVICompressor *This = impl_from_IBaseFilter(iface);
174 
175     TRACE("(%p)\n", This);
176 
177     if(This->filter.state == State_Stopped)
178         return S_OK;
179 
180     ICCompressEnd(This->hic);
181     This->filter.state = State_Stopped;
182     return S_OK;
183 }
184 
185 static HRESULT WINAPI AVICompressor_Pause(IBaseFilter *iface)
186 {
187     AVICompressor *This = impl_from_IBaseFilter(iface);
188     FIXME("(%p)\n", This);
189     return E_NOTIMPL;
190 }
191 
192 static HRESULT WINAPI AVICompressor_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
193 {
194     AVICompressor *This = impl_from_IBaseFilter(iface);
195     HRESULT hres;
196 
197     TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart));
198 
199     if(This->filter.state == State_Running)
200         return S_OK;
201 
202     hres = IMemAllocator_Commit(This->out->pAllocator);
203     if(FAILED(hres)) {
204         FIXME("Commit failed: %08x\n", hres);
205         return hres;
206     }
207 
208     This->frame_cnt = 0;
209 
210     This->filter.state = State_Running;
211     return S_OK;
212 }
213 
214 static HRESULT WINAPI AVICompressor_FindPin(IBaseFilter *iface, LPCWSTR Id, IPin **ppPin)
215 {
216     AVICompressor *This = impl_from_IBaseFilter(iface);
217     FIXME("(%p)->(%s %p)\n", This, debugstr_w(Id), ppPin);
218     return VFW_E_NOT_FOUND;
219 }
220 
221 static HRESULT WINAPI AVICompressor_QueryFilterInfo(IBaseFilter *iface, FILTER_INFO *pInfo)
222 {
223     AVICompressor *This = impl_from_IBaseFilter(iface);
224     FIXME("(%p)->(%p)\n", This, pInfo);
225     return E_NOTIMPL;
226 }
227 
228 static HRESULT WINAPI AVICompressor_QueryVendorInfo(IBaseFilter *iface, LPWSTR *pVendorInfo)
229 {
230     AVICompressor *This = impl_from_IBaseFilter(iface);
231     FIXME("(%p)->(%p)\n", This, pVendorInfo);
232     return E_NOTIMPL;
233 }
234 
235 static const IBaseFilterVtbl AVICompressorVtbl = {
236     AVICompressor_QueryInterface,
237     BaseFilterImpl_AddRef,
238     AVICompressor_Release,
239     BaseFilterImpl_GetClassID,
240     AVICompressor_Stop,
241     AVICompressor_Pause,
242     AVICompressor_Run,
243     BaseFilterImpl_GetState,
244     BaseFilterImpl_SetSyncSource,
245     BaseFilterImpl_GetSyncSource,
246     BaseFilterImpl_EnumPins,
247     AVICompressor_FindPin,
248     AVICompressor_QueryFilterInfo,
249     BaseFilterImpl_JoinFilterGraph,
250     AVICompressor_QueryVendorInfo
251 };
252 
253 static IPin* WINAPI AVICompressor_GetPin(BaseFilter *iface, int pos)
254 {
255     AVICompressor *This = impl_from_BaseFilter(iface);
256     IPin *ret;
257 
258     TRACE("(%p)->(%d)\n", This, pos);
259 
260     switch(pos) {
261     case 0:
262         ret = &This->in->pin.IPin_iface;
263         break;
264     case 1:
265         ret = &This->out->pin.IPin_iface;
266         break;
267     default:
268         TRACE("No pin %d\n", pos);
269         return NULL;
270     };
271 
272     IPin_AddRef(ret);
273     return ret;
274 }
275 
276 static LONG WINAPI AVICompressor_GetPinCount(BaseFilter *iface)
277 {
278     return 2;
279 }
280 
281 static const BaseFilterFuncTable filter_func_table = {
282     AVICompressor_GetPin,
283     AVICompressor_GetPinCount
284 };
285 
286 static AVICompressor *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
287 {
288     return CONTAINING_RECORD(iface, AVICompressor, IPersistPropertyBag_iface);
289 }
290 
291 static HRESULT WINAPI AVICompressorPropertyBag_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ppv)
292 {
293     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
294     return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
295 }
296 
297 static ULONG WINAPI AVICompressorPropertyBag_AddRef(IPersistPropertyBag *iface)
298 {
299     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
300     return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
301 }
302 
303 static ULONG WINAPI AVICompressorPropertyBag_Release(IPersistPropertyBag *iface)
304 {
305     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
306     return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
307 }
308 
309 static HRESULT WINAPI AVICompressorPropertyBag_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID)
310 {
311     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
312     return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID);
313 }
314 
315 static HRESULT WINAPI AVICompressorPropertyBag_InitNew(IPersistPropertyBag *iface)
316 {
317     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
318     FIXME("(%p)->()\n", This);
319     return E_NOTIMPL;
320 }
321 
322 static HRESULT WINAPI AVICompressorPropertyBag_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag, IErrorLog *pErrorLog)
323 {
324     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
325     BSTR str;
326     VARIANT v;
327     HRESULT hres;
328 
329     static const WCHAR fcc_handlerW[] = {'F','c','c','H','a','n','d','l','e','r',0};
330 
331     TRACE("(%p)->(%p %p)\n", This, pPropBag, pErrorLog);
332 
333     V_VT(&v) = VT_EMPTY;
334     hres = IPropertyBag_Read(pPropBag, fcc_handlerW, &v, NULL);
335     if(FAILED(hres)) {
336         WARN("Could not read FccHandler: %08x\n", hres);
337         return hres;
338     }
339 
340     if(V_VT(&v) != VT_BSTR) {
341         FIXME("Got vt %d\n", V_VT(&v));
342         VariantClear(&v);
343         return E_FAIL;
344     }
345 
346     str = V_BSTR(&v);
347     TRACE("FccHandler = %s\n", debugstr_w(str));
348     if(SysStringLen(str) != 4) {
349         FIXME("Invalid FccHandler len\n");
350         SysFreeString(str);
351         return E_FAIL;
352     }
353 
354     This->fcc_handler = FCC(str[0], str[1], str[2], str[3]);
355     SysFreeString(str);
356     return S_OK;
357 }
358 
359 static HRESULT WINAPI AVICompressorPropertyBag_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag,
360         BOOL fClearDirty, BOOL fSaveAllProperties)
361 {
362     AVICompressor *This = impl_from_IPersistPropertyBag(iface);
363     FIXME("(%p)->(%p %x %x)\n", This, pPropBag, fClearDirty, fSaveAllProperties);
364     return E_NOTIMPL;
365 }
366 
367 static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = {
368     AVICompressorPropertyBag_QueryInterface,
369     AVICompressorPropertyBag_AddRef,
370     AVICompressorPropertyBag_Release,
371     AVICompressorPropertyBag_GetClassID,
372     AVICompressorPropertyBag_InitNew,
373     AVICompressorPropertyBag_Load,
374     AVICompressorPropertyBag_Save
375 };
376 
377 static inline AVICompressor *impl_from_IPin(IPin *iface)
378 {
379     BasePin *bp = CONTAINING_RECORD(iface, BasePin, IPin_iface);
380     return impl_from_IBaseFilter(bp->pinInfo.pFilter);
381 }
382 
383 static HRESULT WINAPI AVICompressorIn_QueryInterface(IPin *iface, REFIID riid, void **ppv)
384 {
385     return BaseInputPinImpl_QueryInterface(iface, riid, ppv);
386 }
387 
388 static ULONG WINAPI AVICompressorIn_AddRef(IPin *iface)
389 {
390     AVICompressor *This = impl_from_IPin(iface);
391     return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
392 }
393 
394 static ULONG WINAPI AVICompressorIn_Release(IPin *iface)
395 {
396     AVICompressor *This = impl_from_IPin(iface);
397     return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
398 }
399 
400 static HRESULT WINAPI AVICompressorIn_ReceiveConnection(IPin *iface,
401         IPin *pConnector, const AM_MEDIA_TYPE *pmt)
402 {
403     AVICompressor *This = impl_from_IPin(iface);
404     HRESULT hres;
405 
406     TRACE("(%p)->(%p AM_MEDIA_TYPE(%p))\n", This, pConnector, pmt);
407     dump_AM_MEDIA_TYPE(pmt);
408 
409     hres = BaseInputPinImpl_ReceiveConnection(iface, pConnector, pmt);
410     if(FAILED(hres))
411         return hres;
412 
413     hres = fill_format_info(This, (VIDEOINFOHEADER*)pmt->pbFormat);
414     if(FAILED(hres))
415         BasePinImpl_Disconnect(iface);
416     return hres;
417 }
418 
419 static HRESULT WINAPI AVICompressorIn_Disconnect(IPin *iface)
420 {
421     AVICompressor *This = impl_from_IPin(iface);
422     HRESULT hres;
423 
424     TRACE("(%p)\n", This);
425 
426     hres = BasePinImpl_Disconnect(iface);
427     if(FAILED(hres))
428         return hres;
429 
430     heap_free(This->videoinfo);
431     This->videoinfo = NULL;
432     return S_OK;
433 }
434 
435 static const IPinVtbl AVICompressorInputPinVtbl = {
436     AVICompressorIn_QueryInterface,
437     AVICompressorIn_AddRef,
438     AVICompressorIn_Release,
439     BaseInputPinImpl_Connect,
440     AVICompressorIn_ReceiveConnection,
441     AVICompressorIn_Disconnect,
442     BasePinImpl_ConnectedTo,
443     BasePinImpl_ConnectionMediaType,
444     BasePinImpl_QueryPinInfo,
445     BasePinImpl_QueryDirection,
446     BasePinImpl_QueryId,
447     BasePinImpl_QueryAccept,
448     BasePinImpl_EnumMediaTypes,
449     BasePinImpl_QueryInternalConnections,
450     BaseInputPinImpl_EndOfStream,
451     BaseInputPinImpl_BeginFlush,
452     BaseInputPinImpl_EndFlush,
453     BaseInputPinImpl_NewSegment
454 };
455 
456 static HRESULT WINAPI AVICompressorIn_CheckMediaType(BasePin *base, const AM_MEDIA_TYPE *pmt)
457 {
458     AVICompressor *This = impl_from_BasePin(base);
459     VIDEOINFOHEADER *videoinfo;
460     HRESULT hres;
461     DWORD res;
462 
463     TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base, pmt);
464     dump_AM_MEDIA_TYPE(pmt);
465 
466     if(!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video))
467         return S_FALSE;
468 
469     if(!IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) {
470         FIXME("formattype %s unsupported\n", debugstr_guid(&pmt->formattype));
471         return S_FALSE;
472     }
473 
474     hres = ensure_driver(This);
475     if(hres != S_OK)
476         return hres;
477 
478     videoinfo = (VIDEOINFOHEADER*)pmt->pbFormat;
479     res = ICCompressQuery(This->hic, &videoinfo->bmiHeader, NULL);
480     return res == ICERR_OK ? S_OK : S_FALSE;
481 }
482 
483 static LONG WINAPI AVICompressorIn_GetMediaTypeVersion(BasePin *base)
484 {
485     return 0;
486 }
487 
488 static HRESULT WINAPI AVICompressorIn_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt)
489 {
490     TRACE("(%p)->(%d %p)\n", base, iPosition, amt);
491     return S_FALSE;
492 }
493 
494 static HRESULT WINAPI AVICompressorIn_Receive(BaseInputPin *base, IMediaSample *pSample)
495 {
496     AVICompressor *This = impl_from_BasePin(&base->pin);
497     VIDEOINFOHEADER *src_videoinfo;
498     REFERENCE_TIME start, stop;
499     IMediaSample *out_sample;
500     AM_MEDIA_TYPE *mt;
501     IMediaSample2 *sample2;
502     DWORD comp_flags = 0;
503     BOOL is_preroll;
504     BOOL sync_point;
505     BYTE *ptr, *buf;
506     DWORD res;
507     HRESULT hres;
508 
509     TRACE("(%p)->(%p)\n", base, pSample);
510 
511     if(!This->hic) {
512         FIXME("Driver not loaded\n");
513         return E_UNEXPECTED;
514     }
515 
516     hres = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&sample2);
517     if(SUCCEEDED(hres)) {
518         FIXME("Use IMediaSample2\n");
519         IMediaSample2_Release(sample2);
520     }
521 
522     is_preroll = IMediaSample_IsPreroll(pSample) == S_OK;
523     sync_point = IMediaSample_IsSyncPoint(pSample) == S_OK;
524 
525     hres = IMediaSample_GetTime(pSample, &start, &stop);
526     if(FAILED(hres)) {
527         WARN("GetTime failed: %08x\n", hres);
528         return hres;
529     }
530 
531     hres = IMediaSample_GetMediaType(pSample, &mt);
532     if(FAILED(hres))
533         return hres;
534 
535     hres = IMediaSample_GetPointer(pSample, &ptr);
536     if(FAILED(hres)) {
537         WARN("GetPointer failed: %08x\n", hres);
538         return hres;
539     }
540 
541     hres = BaseOutputPinImpl_GetDeliveryBuffer(This->out, &out_sample, &start, &stop, 0);
542     if(FAILED(hres))
543         return hres;
544 
545     hres = IMediaSample_GetPointer(out_sample, &buf);
546     if(FAILED(hres))
547         return hres;
548 
549     if((This->driver_flags & VIDCF_TEMPORAL) && !(This->driver_flags & VIDCF_FASTTEMPORALC))
550         FIXME("Unsupported temporal compression\n");
551 
552     src_videoinfo = (VIDEOINFOHEADER*)This->in->pin.mtCurrent.pbFormat;
553     This->videoinfo->bmiHeader.biSizeImage = This->max_frame_size;
554     res = ICCompress(This->hic, sync_point ? ICCOMPRESS_KEYFRAME : 0, &This->videoinfo->bmiHeader, buf,
555             &src_videoinfo->bmiHeader, ptr, 0, &comp_flags, This->frame_cnt, 0, 0, NULL, NULL);
556     if(res != ICERR_OK) {
557         WARN("ICCompress failed: %d\n", res);
558         IMediaSample_Release(out_sample);
559         return E_FAIL;
560     }
561 
562     IMediaSample_SetActualDataLength(out_sample, This->videoinfo->bmiHeader.biSizeImage);
563     IMediaSample_SetPreroll(out_sample, is_preroll);
564     IMediaSample_SetSyncPoint(out_sample, (comp_flags&AVIIF_KEYFRAME) != 0);
565     IMediaSample_SetDiscontinuity(out_sample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
566 
567     if (IMediaSample_GetMediaTime(pSample, &start, &stop) == S_OK)
568         IMediaSample_SetMediaTime(out_sample, &start, &stop);
569     else
570         IMediaSample_SetMediaTime(out_sample, NULL, NULL);
571 
572     hres = BaseOutputPinImpl_Deliver(This->out, out_sample);
573     if(FAILED(hres))
574         WARN("Deliver failed: %08x\n", hres);
575 
576     IMediaSample_Release(out_sample);
577     This->frame_cnt++;
578     return hres;
579 }
580 
581 static const BaseInputPinFuncTable AVICompressorBaseInputPinVtbl = {
582     {
583         AVICompressorIn_CheckMediaType,
584         NULL,
585         AVICompressorIn_GetMediaTypeVersion,
586         AVICompressorIn_GetMediaType
587     },
588     AVICompressorIn_Receive
589 };
590 
591 static HRESULT WINAPI AVICompressorOut_QueryInterface(IPin *iface, REFIID riid, void **ppv)
592 {
593     return BaseInputPinImpl_QueryInterface(iface, riid, ppv);
594 }
595 
596 static ULONG WINAPI AVICompressorOut_AddRef(IPin *iface)
597 {
598     AVICompressor *This = impl_from_IPin(iface);
599     return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
600 }
601 
602 static ULONG WINAPI AVICompressorOut_Release(IPin *iface)
603 {
604     AVICompressor *This = impl_from_IPin(iface);
605     return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
606 }
607 
608 static const IPinVtbl AVICompressorOutputPinVtbl = {
609     AVICompressorOut_QueryInterface,
610     AVICompressorOut_AddRef,
611     AVICompressorOut_Release,
612     BaseOutputPinImpl_Connect,
613     BaseOutputPinImpl_ReceiveConnection,
614     BaseOutputPinImpl_Disconnect,
615     BasePinImpl_ConnectedTo,
616     BasePinImpl_ConnectionMediaType,
617     BasePinImpl_QueryPinInfo,
618     BasePinImpl_QueryDirection,
619     BasePinImpl_QueryId,
620     BasePinImpl_QueryAccept,
621     BasePinImpl_EnumMediaTypes,
622     BasePinImpl_QueryInternalConnections,
623     BaseOutputPinImpl_EndOfStream,
624     BaseOutputPinImpl_BeginFlush,
625     BaseOutputPinImpl_EndFlush,
626     BasePinImpl_NewSegment
627 };
628 
629 static LONG WINAPI AVICompressorOut_GetMediaTypeVersion(BasePin *base)
630 {
631     FIXME("(%p)\n", base);
632     return 0;
633 }
634 
635 static HRESULT WINAPI AVICompressorOut_GetMediaType(BasePin *base, int iPosition, AM_MEDIA_TYPE *amt)
636 {
637     AVICompressor *This = impl_from_IBaseFilter(base->pinInfo.pFilter);
638 
639     TRACE("(%p)->(%d %p)\n", base, iPosition, amt);
640 
641     if(iPosition || !This->videoinfo)
642         return S_FALSE;
643 
644     amt->majortype = MEDIATYPE_Video;
645     amt->subtype = MEDIASUBTYPE_PCM;
646     amt->bFixedSizeSamples = FALSE;
647     amt->bTemporalCompression = (This->driver_flags & VIDCF_TEMPORAL) != 0;
648     amt->lSampleSize = This->in->pin.mtCurrent.lSampleSize;
649     amt->formattype = FORMAT_VideoInfo;
650     amt->pUnk = NULL;
651     amt->cbFormat = This->videoinfo_size;
652     amt->pbFormat = (BYTE*)This->videoinfo;
653     return S_OK;
654 }
655 
656 static HRESULT WINAPI AVICompressorOut_DecideBufferSize(BaseOutputPin *base, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
657 {
658     AVICompressor *This = impl_from_BasePin(&base->pin);
659     ALLOCATOR_PROPERTIES actual;
660 
661     TRACE("(%p)\n", This);
662 
663     if (!ppropInputRequest->cBuffers)
664         ppropInputRequest->cBuffers = 1;
665     if (ppropInputRequest->cbBuffer < This->max_frame_size)
666         ppropInputRequest->cbBuffer = This->max_frame_size;
667     if (!ppropInputRequest->cbAlign)
668         ppropInputRequest->cbAlign = 1;
669 
670     return IMemAllocator_SetProperties(alloc, ppropInputRequest, &actual);
671 }
672 
673 static HRESULT WINAPI AVICompressorOut_DecideAllocator(BaseOutputPin *base,
674         IMemInputPin *pPin, IMemAllocator **pAlloc)
675 {
676     TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc);
677     return BaseOutputPinImpl_DecideAllocator(base, pPin, pAlloc);
678 }
679 
680 static HRESULT WINAPI AVICompressorOut_BreakConnect(BaseOutputPin *base)
681 {
682     FIXME("(%p)\n", base);
683     return E_NOTIMPL;
684 }
685 
686 static const BaseOutputPinFuncTable AVICompressorBaseOutputPinVtbl = {
687     {
688         NULL,
689         BaseOutputPinImpl_AttemptConnection,
690         AVICompressorOut_GetMediaTypeVersion,
691         AVICompressorOut_GetMediaType
692     },
693     AVICompressorOut_DecideBufferSize,
694     AVICompressorOut_DecideAllocator,
695     AVICompressorOut_BreakConnect
696 };
697 
698 IUnknown* WINAPI QCAP_createAVICompressor(IUnknown *outer, HRESULT *phr)
699 {
700     PIN_INFO in_pin_info  = {NULL, PINDIR_INPUT,  {'I','n','p','u','t',0}};
701     PIN_INFO out_pin_info = {NULL, PINDIR_OUTPUT, {'O','u','t','p','u','t',0}};
702     AVICompressor *compressor;
703     HRESULT hres;
704 
705     TRACE("\n");
706 
707     compressor = heap_alloc_zero(sizeof(*compressor));
708     if(!compressor) {
709         *phr = E_NOINTERFACE;
710         return NULL;
711     }
712 
713     BaseFilter_Init(&compressor->filter, &AVICompressorVtbl, &CLSID_AVICo,
714             (DWORD_PTR)(__FILE__ ": AVICompressor.csFilter"), &filter_func_table);
715 
716     compressor->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl;
717 
718     in_pin_info.pFilter = &compressor->filter.IBaseFilter_iface;
719     hres = BaseInputPin_Construct(&AVICompressorInputPinVtbl, sizeof(BaseInputPin), &in_pin_info,
720             &AVICompressorBaseInputPinVtbl, &compressor->filter.csFilter, NULL, (IPin**)&compressor->in);
721     if(FAILED(hres)) {
722         IBaseFilter_Release(&compressor->filter.IBaseFilter_iface);
723         *phr = hres;
724         return NULL;
725     }
726 
727     out_pin_info.pFilter = &compressor->filter.IBaseFilter_iface;
728     hres = BaseOutputPin_Construct(&AVICompressorOutputPinVtbl, sizeof(BaseOutputPin), &out_pin_info,
729             &AVICompressorBaseOutputPinVtbl, &compressor->filter.csFilter, (IPin**)&compressor->out);
730     if(FAILED(hres)) {
731         IBaseFilter_Release(&compressor->filter.IBaseFilter_iface);
732         *phr = hres;
733         return NULL;
734     }
735 
736     *phr = S_OK;
737     return (IUnknown*)&compressor->filter.IBaseFilter_iface;
738 }
739