mf: Avoid unnecessary prerolling calls in SAR.
[wine.git] / dlls / qcap / avico.c
blobfc5826b16fe64dd9d411f1902f0684a8d3c4788e
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_strmbase_pin(struct strmbase_pin *pin)
61 return impl_from_strmbase_filter(pin->filter);
64 static HRESULT ensure_driver(AVICompressor *This)
66 if(This->hic)
67 return S_OK;
69 This->hic = ICOpen(FCC('v','i','d','c'), This->fcc_handler, ICMODE_COMPRESS);
70 if(!This->hic) {
71 FIXME("ICOpen failed\n");
72 return E_FAIL;
75 return S_OK;
78 static HRESULT fill_format_info(AVICompressor *This, VIDEOINFOHEADER *src_videoinfo)
80 DWORD size;
81 ICINFO icinfo;
82 HRESULT hres;
84 hres = ensure_driver(This);
85 if(hres != S_OK)
86 return hres;
88 size = ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
89 if(size != sizeof(icinfo))
90 return E_FAIL;
92 size = ICCompressGetFormatSize(This->hic, &src_videoinfo->bmiHeader);
93 if(!size) {
94 FIXME("ICCompressGetFormatSize failed\n");
95 return E_FAIL;
98 size += FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader);
99 This->videoinfo = heap_alloc(size);
100 if(!This->videoinfo)
101 return E_OUTOFMEMORY;
103 This->videoinfo_size = size;
104 This->driver_flags = icinfo.dwFlags;
105 memset(This->videoinfo, 0, sizeof(*This->videoinfo));
106 ICCompressGetFormat(This->hic, &src_videoinfo->bmiHeader, &This->videoinfo->bmiHeader);
108 This->videoinfo->dwBitRate = 10000000/src_videoinfo->AvgTimePerFrame * This->videoinfo->bmiHeader.biSizeImage * 8;
109 This->videoinfo->AvgTimePerFrame = src_videoinfo->AvgTimePerFrame;
110 This->max_frame_size = This->videoinfo->bmiHeader.biSizeImage;
111 return S_OK;
114 static struct strmbase_pin *avi_compressor_get_pin(struct strmbase_filter *iface, unsigned int index)
116 AVICompressor *filter = impl_from_strmbase_filter(iface);
118 if (index == 0)
119 return &filter->sink.pin;
120 else if (index == 1)
121 return &filter->source.pin;
122 return NULL;
125 static void avi_compressor_destroy(struct strmbase_filter *iface)
127 AVICompressor *filter = impl_from_strmbase_filter(iface);
129 if (filter->hic)
130 ICClose(filter->hic);
131 heap_free(filter->videoinfo);
132 strmbase_sink_cleanup(&filter->sink);
133 strmbase_source_cleanup(&filter->source);
134 strmbase_filter_cleanup(&filter->filter);
135 heap_free(filter);
138 static HRESULT avi_compressor_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
140 AVICompressor *filter = impl_from_strmbase_filter(iface);
142 if (IsEqualGUID(iid, &IID_IPersistPropertyBag))
143 *out = &filter->IPersistPropertyBag_iface;
144 else
145 return E_NOINTERFACE;
147 IUnknown_AddRef((IUnknown *)*out);
148 return S_OK;
151 static HRESULT avi_compressor_init_stream(struct strmbase_filter *iface)
153 AVICompressor *filter = impl_from_strmbase_filter(iface);
154 HRESULT hr;
156 if (filter->source.pAllocator && FAILED(hr = IMemAllocator_Commit(filter->source.pAllocator)))
158 ERR("Failed to commit allocator, hr %#x.\n", hr);
159 return hr;
162 filter->frame_cnt = 0;
164 return S_OK;
167 static HRESULT avi_compressor_cleanup_stream(struct strmbase_filter *iface)
169 AVICompressor *filter = impl_from_strmbase_filter(iface);
171 ICCompressEnd(filter->hic);
172 return S_OK;
175 static const struct strmbase_filter_ops filter_ops =
177 .filter_get_pin = avi_compressor_get_pin,
178 .filter_destroy = avi_compressor_destroy,
179 .filter_query_interface = avi_compressor_query_interface,
180 .filter_init_stream = avi_compressor_init_stream,
181 .filter_cleanup_stream = avi_compressor_cleanup_stream,
184 static AVICompressor *impl_from_IPersistPropertyBag(IPersistPropertyBag *iface)
186 return CONTAINING_RECORD(iface, AVICompressor, IPersistPropertyBag_iface);
189 static HRESULT WINAPI AVICompressorPropertyBag_QueryInterface(IPersistPropertyBag *iface, REFIID riid, void **ppv)
191 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
192 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
195 static ULONG WINAPI AVICompressorPropertyBag_AddRef(IPersistPropertyBag *iface)
197 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
198 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
201 static ULONG WINAPI AVICompressorPropertyBag_Release(IPersistPropertyBag *iface)
203 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
204 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
207 static HRESULT WINAPI AVICompressorPropertyBag_GetClassID(IPersistPropertyBag *iface, CLSID *pClassID)
209 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
210 return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID);
213 static HRESULT WINAPI AVICompressorPropertyBag_InitNew(IPersistPropertyBag *iface)
215 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
216 FIXME("(%p)->()\n", This);
217 return E_NOTIMPL;
220 static HRESULT WINAPI AVICompressorPropertyBag_Load(IPersistPropertyBag *iface, IPropertyBag *pPropBag, IErrorLog *pErrorLog)
222 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
223 BSTR str;
224 VARIANT v;
225 HRESULT hres;
227 static const WCHAR fcc_handlerW[] = {'F','c','c','H','a','n','d','l','e','r',0};
229 TRACE("(%p)->(%p %p)\n", This, pPropBag, pErrorLog);
231 V_VT(&v) = VT_BSTR;
232 hres = IPropertyBag_Read(pPropBag, fcc_handlerW, &v, NULL);
233 if(FAILED(hres)) {
234 WARN("Could not read FccHandler: %08x\n", hres);
235 return hres;
238 if(V_VT(&v) != VT_BSTR) {
239 FIXME("Got vt %d\n", V_VT(&v));
240 VariantClear(&v);
241 return E_FAIL;
244 str = V_BSTR(&v);
245 TRACE("FccHandler = %s\n", debugstr_w(str));
246 if(SysStringLen(str) != 4) {
247 FIXME("Invalid FccHandler len\n");
248 SysFreeString(str);
249 return E_FAIL;
252 This->fcc_handler = FCC(str[0], str[1], str[2], str[3]);
253 SysFreeString(str);
254 return S_OK;
257 static HRESULT WINAPI AVICompressorPropertyBag_Save(IPersistPropertyBag *iface, IPropertyBag *pPropBag,
258 BOOL fClearDirty, BOOL fSaveAllProperties)
260 AVICompressor *This = impl_from_IPersistPropertyBag(iface);
261 FIXME("(%p)->(%p %x %x)\n", This, pPropBag, fClearDirty, fSaveAllProperties);
262 return E_NOTIMPL;
265 static const IPersistPropertyBagVtbl PersistPropertyBagVtbl = {
266 AVICompressorPropertyBag_QueryInterface,
267 AVICompressorPropertyBag_AddRef,
268 AVICompressorPropertyBag_Release,
269 AVICompressorPropertyBag_GetClassID,
270 AVICompressorPropertyBag_InitNew,
271 AVICompressorPropertyBag_Load,
272 AVICompressorPropertyBag_Save
275 static HRESULT sink_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *pmt)
277 AVICompressor *This = impl_from_strmbase_pin(base);
278 VIDEOINFOHEADER *videoinfo;
279 HRESULT hres;
280 DWORD res;
282 TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base, pmt);
284 if(!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video))
285 return S_FALSE;
287 if(!IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) {
288 FIXME("formattype %s unsupported\n", debugstr_guid(&pmt->formattype));
289 return S_FALSE;
292 hres = ensure_driver(This);
293 if(hres != S_OK)
294 return hres;
296 videoinfo = (VIDEOINFOHEADER*)pmt->pbFormat;
297 res = ICCompressQuery(This->hic, &videoinfo->bmiHeader, NULL);
298 return res == ICERR_OK ? S_OK : S_FALSE;
301 static HRESULT sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
303 AVICompressor *filter = impl_from_strmbase_pin(iface);
305 if (IsEqualGUID(iid, &IID_IMemInputPin))
306 *out = &filter->sink.IMemInputPin_iface;
307 else
308 return E_NOINTERFACE;
310 IUnknown_AddRef((IUnknown *)*out);
311 return S_OK;
314 static HRESULT WINAPI AVICompressorIn_Receive(struct strmbase_sink *base, IMediaSample *pSample)
316 AVICompressor *This = impl_from_strmbase_pin(&base->pin);
317 IMemInputPin *meminput = This->source.pMemInputPin;
318 VIDEOINFOHEADER *src_videoinfo;
319 REFERENCE_TIME start, stop;
320 IMediaSample *out_sample;
321 AM_MEDIA_TYPE *mt;
322 IMediaSample2 *sample2;
323 DWORD comp_flags = 0;
324 BOOL is_preroll;
325 BOOL sync_point;
326 BYTE *ptr, *buf;
327 DWORD res;
328 HRESULT hres;
330 TRACE("(%p)->(%p)\n", base, pSample);
332 if (!meminput)
334 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
335 return VFW_E_NOT_CONNECTED;
338 if(!This->hic) {
339 FIXME("Driver not loaded\n");
340 return E_UNEXPECTED;
343 hres = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&sample2);
344 if(SUCCEEDED(hres)) {
345 FIXME("Use IMediaSample2\n");
346 IMediaSample2_Release(sample2);
349 is_preroll = IMediaSample_IsPreroll(pSample) == S_OK;
350 sync_point = IMediaSample_IsSyncPoint(pSample) == S_OK;
352 hres = IMediaSample_GetTime(pSample, &start, &stop);
353 if(FAILED(hres)) {
354 WARN("GetTime failed: %08x\n", hres);
355 return hres;
358 hres = IMediaSample_GetMediaType(pSample, &mt);
359 if(FAILED(hres))
360 return hres;
362 hres = IMediaSample_GetPointer(pSample, &ptr);
363 if(FAILED(hres)) {
364 WARN("GetPointer failed: %08x\n", hres);
365 return hres;
368 hres = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &out_sample, &start, &stop, 0);
369 if(FAILED(hres))
370 return hres;
372 hres = IMediaSample_GetPointer(out_sample, &buf);
373 if(FAILED(hres))
374 return hres;
376 if((This->driver_flags & VIDCF_TEMPORAL) && !(This->driver_flags & VIDCF_FASTTEMPORALC))
377 FIXME("Unsupported temporal compression\n");
379 src_videoinfo = (VIDEOINFOHEADER *)This->sink.pin.mt.pbFormat;
380 This->videoinfo->bmiHeader.biSizeImage = This->max_frame_size;
381 res = ICCompress(This->hic, sync_point ? ICCOMPRESS_KEYFRAME : 0, &This->videoinfo->bmiHeader, buf,
382 &src_videoinfo->bmiHeader, ptr, 0, &comp_flags, This->frame_cnt, 0, 0, NULL, NULL);
383 if(res != ICERR_OK) {
384 WARN("ICCompress failed: %d\n", res);
385 IMediaSample_Release(out_sample);
386 return E_FAIL;
389 IMediaSample_SetActualDataLength(out_sample, This->videoinfo->bmiHeader.biSizeImage);
390 IMediaSample_SetPreroll(out_sample, is_preroll);
391 IMediaSample_SetSyncPoint(out_sample, (comp_flags&AVIIF_KEYFRAME) != 0);
392 IMediaSample_SetDiscontinuity(out_sample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
394 if (IMediaSample_GetMediaTime(pSample, &start, &stop) == S_OK)
395 IMediaSample_SetMediaTime(out_sample, &start, &stop);
396 else
397 IMediaSample_SetMediaTime(out_sample, NULL, NULL);
399 hres = IMemInputPin_Receive(meminput, out_sample);
400 if(FAILED(hres))
401 WARN("Deliver failed: %08x\n", hres);
403 IMediaSample_Release(out_sample);
404 This->frame_cnt++;
405 return hres;
408 static HRESULT sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
410 AVICompressor *filter = impl_from_strmbase_pin(&iface->pin);
411 return fill_format_info(filter, (VIDEOINFOHEADER *)mt->pbFormat);
414 static void sink_disconnect(struct strmbase_sink *iface)
416 AVICompressor *filter = impl_from_strmbase_pin(&iface->pin);
417 heap_free(filter->videoinfo);
418 filter->videoinfo = NULL;
421 static const struct strmbase_sink_ops sink_ops =
423 .base.pin_query_accept = sink_query_accept,
424 .base.pin_get_media_type = strmbase_pin_get_media_type,
425 .base.pin_query_interface = sink_query_interface,
426 .pfnReceive = AVICompressorIn_Receive,
427 .sink_connect = sink_connect,
428 .sink_disconnect = sink_disconnect,
431 static HRESULT source_get_media_type(struct strmbase_pin *base, unsigned int iPosition, AM_MEDIA_TYPE *amt)
433 AVICompressor *This = impl_from_strmbase_filter(base->filter);
435 if(iPosition || !This->videoinfo)
436 return S_FALSE;
438 amt->majortype = MEDIATYPE_Video;
439 amt->subtype = MEDIASUBTYPE_PCM;
440 amt->bFixedSizeSamples = FALSE;
441 amt->bTemporalCompression = (This->driver_flags & VIDCF_TEMPORAL) != 0;
442 amt->lSampleSize = This->sink.pin.mt.lSampleSize;
443 amt->formattype = FORMAT_VideoInfo;
444 amt->pUnk = NULL;
445 amt->cbFormat = This->videoinfo_size;
446 amt->pbFormat = (BYTE*)This->videoinfo;
447 return S_OK;
450 static HRESULT WINAPI AVICompressorOut_DecideBufferSize(struct strmbase_source *base,
451 IMemAllocator *alloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
453 AVICompressor *This = impl_from_strmbase_pin(&base->pin);
454 ALLOCATOR_PROPERTIES actual;
456 TRACE("(%p)\n", This);
458 if (!ppropInputRequest->cBuffers)
459 ppropInputRequest->cBuffers = 1;
460 if (ppropInputRequest->cbBuffer < This->max_frame_size)
461 ppropInputRequest->cbBuffer = This->max_frame_size;
462 if (!ppropInputRequest->cbAlign)
463 ppropInputRequest->cbAlign = 1;
465 return IMemAllocator_SetProperties(alloc, ppropInputRequest, &actual);
468 static HRESULT WINAPI AVICompressorOut_DecideAllocator(struct strmbase_source *base,
469 IMemInputPin *pPin, IMemAllocator **pAlloc)
471 TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc);
472 return BaseOutputPinImpl_DecideAllocator(base, pPin, pAlloc);
475 static const struct strmbase_source_ops source_ops =
477 .base.pin_get_media_type = source_get_media_type,
478 .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
479 .pfnDecideBufferSize = AVICompressorOut_DecideBufferSize,
480 .pfnDecideAllocator = AVICompressorOut_DecideAllocator,
483 HRESULT avi_compressor_create(IUnknown *outer, IUnknown **out)
485 static const WCHAR source_name[] = {'O','u','t',0};
486 static const WCHAR sink_name[] = {'I','n',0};
487 AVICompressor *object;
489 if (!(object = heap_alloc_zero(sizeof(*object))))
490 return E_OUTOFMEMORY;
492 strmbase_filter_init(&object->filter, outer, &CLSID_AVICo, &filter_ops);
493 object->IPersistPropertyBag_iface.lpVtbl = &PersistPropertyBagVtbl;
495 strmbase_sink_init(&object->sink, &object->filter, sink_name, &sink_ops, NULL);
496 strmbase_source_init(&object->source, &object->filter, source_name, &source_ops);
498 TRACE("Created AVI compressor %p.\n", object);
499 *out = &object->filter.IUnknown_inner;
500 return S_OK;