qcap/avico: Call IMemInputPin::Receive() directly.
[wine.git] / dlls / qcap / avico.c
blobf0f60ae4394b42cf4535ff24f85f4dc71da68cd3
1 /*
2 * Copyright 2013 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
21 #define COBJMACROS
23 #include "windef.h"
24 #include "winbase.h"
25 #include "dshow.h"
26 #include "vfw.h"
27 #include "aviriff.h"
29 #include "qcap_main.h"
31 #include "wine/debug.h"
32 #include "wine/heap.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
36 typedef struct {
37 struct strmbase_filter filter;
38 IPersistPropertyBag IPersistPropertyBag_iface;
40 struct strmbase_sink sink;
41 struct strmbase_source source;
43 DWORD fcc_handler;
44 HIC hic;
46 VIDEOINFOHEADER *videoinfo;
47 size_t videoinfo_size;
48 DWORD driver_flags;
49 DWORD max_frame_size;
51 DWORD frame_cnt;
52 } AVICompressor;
54 static inline AVICompressor *impl_from_strmbase_filter(struct strmbase_filter *filter)
56 return CONTAINING_RECORD(filter, AVICompressor, filter);
59 static inline AVICompressor *impl_from_IBaseFilter(IBaseFilter *iface)
61 struct strmbase_filter *filter = CONTAINING_RECORD(iface, struct strmbase_filter, IBaseFilter_iface);
62 return impl_from_strmbase_filter(filter);
65 static inline AVICompressor *impl_from_strmbase_pin(struct strmbase_pin *pin)
67 return impl_from_strmbase_filter(pin->filter);
70 static HRESULT ensure_driver(AVICompressor *This)
72 if(This->hic)
73 return S_OK;
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;
81 return S_OK;
84 static HRESULT fill_format_info(AVICompressor *This, VIDEOINFOHEADER *src_videoinfo)
86 DWORD size;
87 ICINFO icinfo;
88 HRESULT hres;
90 hres = ensure_driver(This);
91 if(hres != S_OK)
92 return hres;
94 size = ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
95 if(size != sizeof(icinfo))
96 return E_FAIL;
98 size = ICCompressGetFormatSize(This->hic, &src_videoinfo->bmiHeader);
99 if(!size) {
100 FIXME("ICCompressGetFormatSize failed\n");
101 return E_FAIL;
104 size += FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader);
105 This->videoinfo = heap_alloc(size);
106 if(!This->videoinfo)
107 return E_OUTOFMEMORY;
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);
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;
120 static HRESULT WINAPI AVICompressor_Stop(IBaseFilter *iface)
122 AVICompressor *This = impl_from_IBaseFilter(iface);
124 TRACE("(%p)\n", This);
126 if(This->filter.state == State_Stopped)
127 return S_OK;
129 ICCompressEnd(This->hic);
130 This->filter.state = State_Stopped;
131 return S_OK;
134 static HRESULT WINAPI AVICompressor_Pause(IBaseFilter *iface)
136 AVICompressor *This = impl_from_IBaseFilter(iface);
137 FIXME("(%p)\n", This);
138 return E_NOTIMPL;
141 static HRESULT WINAPI AVICompressor_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
143 AVICompressor *This = impl_from_IBaseFilter(iface);
144 HRESULT hres;
146 TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart));
148 if(This->filter.state == State_Running)
149 return S_OK;
151 if (This->source.pAllocator && FAILED(hres = IMemAllocator_Commit(This->source.pAllocator)))
153 FIXME("Commit failed: %08x\n", hres);
154 return hres;
157 This->frame_cnt = 0;
159 This->filter.state = State_Running;
160 return S_OK;
163 static const IBaseFilterVtbl AVICompressorVtbl = {
164 BaseFilterImpl_QueryInterface,
165 BaseFilterImpl_AddRef,
166 BaseFilterImpl_Release,
167 BaseFilterImpl_GetClassID,
168 AVICompressor_Stop,
169 AVICompressor_Pause,
170 AVICompressor_Run,
171 BaseFilterImpl_GetState,
172 BaseFilterImpl_SetSyncSource,
173 BaseFilterImpl_GetSyncSource,
174 BaseFilterImpl_EnumPins,
175 BaseFilterImpl_FindPin,
176 BaseFilterImpl_QueryFilterInfo,
177 BaseFilterImpl_JoinFilterGraph,
178 BaseFilterImpl_QueryVendorInfo,
181 static struct strmbase_pin *avi_compressor_get_pin(struct strmbase_filter *iface, unsigned int index)
183 AVICompressor *filter = impl_from_strmbase_filter(iface);
185 if (index == 0)
186 return &filter->sink.pin;
187 else if (index == 1)
188 return &filter->source.pin;
189 return NULL;
192 static void avi_compressor_destroy(struct strmbase_filter *iface)
194 AVICompressor *filter = impl_from_strmbase_filter(iface);
196 if (filter->hic)
197 ICClose(filter->hic);
198 heap_free(filter->videoinfo);
199 strmbase_sink_cleanup(&filter->sink);
200 strmbase_source_cleanup(&filter->source);
201 strmbase_filter_cleanup(&filter->filter);
202 heap_free(filter);
205 static HRESULT avi_compressor_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
207 AVICompressor *filter = impl_from_strmbase_filter(iface);
209 if (IsEqualGUID(iid, &IID_IPersistPropertyBag))
210 *out = &filter->IPersistPropertyBag_iface;
211 else
212 return E_NOINTERFACE;
214 IUnknown_AddRef((IUnknown *)*out);
215 return S_OK;
218 static const struct strmbase_filter_ops filter_ops =
220 .filter_get_pin = avi_compressor_get_pin,
221 .filter_destroy = avi_compressor_destroy,
222 .filter_query_interface = avi_compressor_query_interface,
225 static AVICompressor *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
227 return CONTAINING_RECORD(iface, AVICompressor, IPersistPropertyBag_iface);
230 static HRESULT WINAPI AVICompressorPropertyBag_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ppv)
232 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
233 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
236 static ULONG WINAPI AVICompressorPropertyBag_AddRef(IPersistPropertyBag *iface)
238 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
239 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
242 static ULONG WINAPI AVICompressorPropertyBag_Release(IPersistPropertyBag *iface)
244 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
245 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
248 static HRESULT WINAPI AVICompressorPropertyBag_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID)
250 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
251 return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID);
254 static HRESULT WINAPI AVICompressorPropertyBag_InitNew(IPersistPropertyBag *iface)
256 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
257 FIXME("(%p)->()\n", This);
258 return E_NOTIMPL;
261 static HRESULT WINAPI AVICompressorPropertyBag_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag, IErrorLog *pErrorLog)
263 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
264 BSTR str;
265 VARIANT v;
266 HRESULT hres;
268 static const WCHAR fcc_handlerW[] = {'F','c','c','H','a','n','d','l','e','r',0};
270 TRACE("(%p)->(%p %p)\n", This, pPropBag, pErrorLog);
272 V_VT(&v) = VT_BSTR;
273 hres = IPropertyBag_Read(pPropBag, fcc_handlerW, &v, NULL);
274 if(FAILED(hres)) {
275 WARN("Could not read FccHandler: %08x\n", hres);
276 return hres;
279 if(V_VT(&v) != VT_BSTR) {
280 FIXME("Got vt %d\n", V_VT(&v));
281 VariantClear(&v);
282 return E_FAIL;
285 str = V_BSTR(&v);
286 TRACE("FccHandler = %s\n", debugstr_w(str));
287 if(SysStringLen(str) != 4) {
288 FIXME("Invalid FccHandler len\n");
289 SysFreeString(str);
290 return E_FAIL;
293 This->fcc_handler = FCC(str[0], str[1], str[2], str[3]);
294 SysFreeString(str);
295 return S_OK;
298 static HRESULT WINAPI AVICompressorPropertyBag_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag,
299 BOOL fClearDirty, BOOL fSaveAllProperties)
301 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
302 FIXME("(%p)->(%p %x %x)\n", This, pPropBag, fClearDirty, fSaveAllProperties);
303 return E_NOTIMPL;
306 static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = {
307 AVICompressorPropertyBag_QueryInterface,
308 AVICompressorPropertyBag_AddRef,
309 AVICompressorPropertyBag_Release,
310 AVICompressorPropertyBag_GetClassID,
311 AVICompressorPropertyBag_InitNew,
312 AVICompressorPropertyBag_Load,
313 AVICompressorPropertyBag_Save
316 static inline AVICompressor *impl_from_IPin(IPin *iface)
318 return impl_from_strmbase_filter(CONTAINING_RECORD(iface, struct strmbase_pin, IPin_iface)->filter);
321 static HRESULT WINAPI AVICompressorIn_ReceiveConnection(IPin *iface,
322 IPin *pConnector, const AM_MEDIA_TYPE *pmt)
324 AVICompressor *This = impl_from_IPin(iface);
325 HRESULT hres;
327 TRACE("(%p)->(%p AM_MEDIA_TYPE(%p))\n", This, pConnector, pmt);
329 hres = BaseInputPinImpl_ReceiveConnection(iface, pConnector, pmt);
330 if(FAILED(hres))
331 return hres;
333 hres = fill_format_info(This, (VIDEOINFOHEADER*)pmt->pbFormat);
334 if(FAILED(hres))
335 BasePinImpl_Disconnect(iface);
336 return hres;
339 static HRESULT WINAPI AVICompressorIn_Disconnect(IPin *iface)
341 AVICompressor *This = impl_from_IPin(iface);
342 HRESULT hres;
344 TRACE("(%p)\n", This);
346 hres = BasePinImpl_Disconnect(iface);
347 if(FAILED(hres))
348 return hres;
350 heap_free(This->videoinfo);
351 This->videoinfo = NULL;
352 return S_OK;
355 static const IPinVtbl AVICompressorInputPinVtbl = {
356 BasePinImpl_QueryInterface,
357 BasePinImpl_AddRef,
358 BasePinImpl_Release,
359 BaseInputPinImpl_Connect,
360 AVICompressorIn_ReceiveConnection,
361 AVICompressorIn_Disconnect,
362 BasePinImpl_ConnectedTo,
363 BasePinImpl_ConnectionMediaType,
364 BasePinImpl_QueryPinInfo,
365 BasePinImpl_QueryDirection,
366 BasePinImpl_QueryId,
367 BasePinImpl_QueryAccept,
368 BasePinImpl_EnumMediaTypes,
369 BasePinImpl_QueryInternalConnections,
370 BaseInputPinImpl_EndOfStream,
371 BaseInputPinImpl_BeginFlush,
372 BaseInputPinImpl_EndFlush,
373 BaseInputPinImpl_NewSegment
376 static HRESULT sink_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *pmt)
378 AVICompressor *This = impl_from_strmbase_pin(base);
379 VIDEOINFOHEADER *videoinfo;
380 HRESULT hres;
381 DWORD res;
383 TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base, pmt);
385 if(!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video))
386 return S_FALSE;
388 if(!IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) {
389 FIXME("formattype %s unsupported\n", debugstr_guid(&pmt->formattype));
390 return S_FALSE;
393 hres = ensure_driver(This);
394 if(hres != S_OK)
395 return hres;
397 videoinfo = (VIDEOINFOHEADER*)pmt->pbFormat;
398 res = ICCompressQuery(This->hic, &videoinfo->bmiHeader, NULL);
399 return res == ICERR_OK ? S_OK : S_FALSE;
402 static HRESULT sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
404 AVICompressor *filter = impl_from_strmbase_pin(iface);
406 if (IsEqualGUID(iid, &IID_IMemInputPin))
407 *out = &filter->sink.IMemInputPin_iface;
408 else
409 return E_NOINTERFACE;
411 IUnknown_AddRef((IUnknown *)*out);
412 return S_OK;
415 static HRESULT WINAPI AVICompressorIn_Receive(struct strmbase_sink *base, IMediaSample *pSample)
417 AVICompressor *This = impl_from_strmbase_pin(&base->pin);
418 IMemInputPin *meminput = This->source.pMemInputPin;
419 VIDEOINFOHEADER *src_videoinfo;
420 REFERENCE_TIME start, stop;
421 IMediaSample *out_sample;
422 AM_MEDIA_TYPE *mt;
423 IMediaSample2 *sample2;
424 DWORD comp_flags = 0;
425 BOOL is_preroll;
426 BOOL sync_point;
427 BYTE *ptr, *buf;
428 DWORD res;
429 HRESULT hres;
431 TRACE("(%p)->(%p)\n", base, pSample);
433 if (!meminput)
435 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
436 return VFW_E_NOT_CONNECTED;
439 if(!This->hic) {
440 FIXME("Driver not loaded\n");
441 return E_UNEXPECTED;
444 hres = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&sample2);
445 if(SUCCEEDED(hres)) {
446 FIXME("Use IMediaSample2\n");
447 IMediaSample2_Release(sample2);
450 is_preroll = IMediaSample_IsPreroll(pSample) == S_OK;
451 sync_point = IMediaSample_IsSyncPoint(pSample) == S_OK;
453 hres = IMediaSample_GetTime(pSample, &start, &stop);
454 if(FAILED(hres)) {
455 WARN("GetTime failed: %08x\n", hres);
456 return hres;
459 hres = IMediaSample_GetMediaType(pSample, &mt);
460 if(FAILED(hres))
461 return hres;
463 hres = IMediaSample_GetPointer(pSample, &ptr);
464 if(FAILED(hres)) {
465 WARN("GetPointer failed: %08x\n", hres);
466 return hres;
469 hres = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &out_sample, &start, &stop, 0);
470 if(FAILED(hres))
471 return hres;
473 hres = IMediaSample_GetPointer(out_sample, &buf);
474 if(FAILED(hres))
475 return hres;
477 if((This->driver_flags & VIDCF_TEMPORAL) && !(This->driver_flags & VIDCF_FASTTEMPORALC))
478 FIXME("Unsupported temporal compression\n");
480 src_videoinfo = (VIDEOINFOHEADER *)This->sink.pin.mt.pbFormat;
481 This->videoinfo->bmiHeader.biSizeImage = This->max_frame_size;
482 res = ICCompress(This->hic, sync_point ? ICCOMPRESS_KEYFRAME : 0, &This->videoinfo->bmiHeader, buf,
483 &src_videoinfo->bmiHeader, ptr, 0, &comp_flags, This->frame_cnt, 0, 0, NULL, NULL);
484 if(res != ICERR_OK) {
485 WARN("ICCompress failed: %d\n", res);
486 IMediaSample_Release(out_sample);
487 return E_FAIL;
490 IMediaSample_SetActualDataLength(out_sample, This->videoinfo->bmiHeader.biSizeImage);
491 IMediaSample_SetPreroll(out_sample, is_preroll);
492 IMediaSample_SetSyncPoint(out_sample, (comp_flags&AVIIF_KEYFRAME) != 0);
493 IMediaSample_SetDiscontinuity(out_sample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
495 if (IMediaSample_GetMediaTime(pSample, &start, &stop) == S_OK)
496 IMediaSample_SetMediaTime(out_sample, &start, &stop);
497 else
498 IMediaSample_SetMediaTime(out_sample, NULL, NULL);
500 hres = IMemInputPin_Receive(meminput, out_sample);
501 if(FAILED(hres))
502 WARN("Deliver failed: %08x\n", hres);
504 IMediaSample_Release(out_sample);
505 This->frame_cnt++;
506 return hres;
509 static const struct strmbase_sink_ops sink_ops =
511 .base.pin_query_accept = sink_query_accept,
512 .base.pin_get_media_type = strmbase_pin_get_media_type,
513 .base.pin_query_interface = sink_query_interface,
514 .pfnReceive = AVICompressorIn_Receive,
517 static const IPinVtbl AVICompressorOutputPinVtbl = {
518 BasePinImpl_QueryInterface,
519 BasePinImpl_AddRef,
520 BasePinImpl_Release,
521 BaseOutputPinImpl_Connect,
522 BaseOutputPinImpl_ReceiveConnection,
523 BaseOutputPinImpl_Disconnect,
524 BasePinImpl_ConnectedTo,
525 BasePinImpl_ConnectionMediaType,
526 BasePinImpl_QueryPinInfo,
527 BasePinImpl_QueryDirection,
528 BasePinImpl_QueryId,
529 BasePinImpl_QueryAccept,
530 BasePinImpl_EnumMediaTypes,
531 BasePinImpl_QueryInternalConnections,
532 BaseOutputPinImpl_EndOfStream,
533 BaseOutputPinImpl_BeginFlush,
534 BaseOutputPinImpl_EndFlush,
535 BasePinImpl_NewSegment
538 static HRESULT source_get_media_type(struct strmbase_pin *base, unsigned int iPosition, AM_MEDIA_TYPE *amt)
540 AVICompressor *This = impl_from_strmbase_filter(base->filter);
542 if(iPosition || !This->videoinfo)
543 return S_FALSE;
545 amt->majortype = MEDIATYPE_Video;
546 amt->subtype = MEDIASUBTYPE_PCM;
547 amt->bFixedSizeSamples = FALSE;
548 amt->bTemporalCompression = (This->driver_flags & VIDCF_TEMPORAL) != 0;
549 amt->lSampleSize = This->sink.pin.mt.lSampleSize;
550 amt->formattype = FORMAT_VideoInfo;
551 amt->pUnk = NULL;
552 amt->cbFormat = This->videoinfo_size;
553 amt->pbFormat = (BYTE*)This->videoinfo;
554 return S_OK;
557 static HRESULT WINAPI AVICompressorOut_DecideBufferSize(struct strmbase_source *base,
558 IMemAllocator *alloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
560 AVICompressor *This = impl_from_strmbase_pin(&base->pin);
561 ALLOCATOR_PROPERTIES actual;
563 TRACE("(%p)\n", This);
565 if (!ppropInputRequest->cBuffers)
566 ppropInputRequest->cBuffers = 1;
567 if (ppropInputRequest->cbBuffer < This->max_frame_size)
568 ppropInputRequest->cbBuffer = This->max_frame_size;
569 if (!ppropInputRequest->cbAlign)
570 ppropInputRequest->cbAlign = 1;
572 return IMemAllocator_SetProperties(alloc, ppropInputRequest, &actual);
575 static HRESULT WINAPI AVICompressorOut_DecideAllocator(struct strmbase_source *base,
576 IMemInputPin *pPin, IMemAllocator **pAlloc)
578 TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc);
579 return BaseOutputPinImpl_DecideAllocator(base, pPin, pAlloc);
582 static const struct strmbase_source_ops source_ops =
584 .base.pin_get_media_type = source_get_media_type,
585 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
586 .pfnDecideBufferSize = AVICompressorOut_DecideBufferSize,
587 .pfnDecideAllocator = AVICompressorOut_DecideAllocator,
590 IUnknown* WINAPI QCAP_createAVICompressor(IUnknown *outer, HRESULT *phr)
592 static const WCHAR source_name[] = {'O','u','t',0};
593 static const WCHAR sink_name[] = {'I','n',0};
594 AVICompressor *compressor;
596 compressor = heap_alloc_zero(sizeof(*compressor));
597 if(!compressor) {
598 *phr = E_NOINTERFACE;
599 return NULL;
602 strmbase_filter_init(&compressor->filter, &AVICompressorVtbl, outer, &CLSID_AVICo, &filter_ops);
604 compressor->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl;
606 strmbase_sink_init(&compressor->sink, &AVICompressorInputPinVtbl,
607 &compressor->filter, sink_name, &sink_ops, NULL);
608 strmbase_source_init(&compressor->source, &AVICompressorOutputPinVtbl,
609 &compressor->filter, source_name, &source_ops);
611 *phr = S_OK;
612 return &compressor->filter.IUnknown_inner;