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
29 #include "qcap_main.h"
31 #include "wine/debug.h"
32 #include "wine/heap.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
37 struct strmbase_filter filter
;
38 IPersistPropertyBag IPersistPropertyBag_iface
;
40 struct strmbase_sink sink
;
41 struct strmbase_source source
;
46 VIDEOINFOHEADER
*videoinfo
;
47 size_t videoinfo_size
;
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
)
69 This
->hic
= ICOpen(FCC('v','i','d','c'), This
->fcc_handler
, ICMODE_COMPRESS
);
71 FIXME("ICOpen failed\n");
78 static HRESULT
fill_format_info(AVICompressor
*This
, VIDEOINFOHEADER
*src_videoinfo
)
84 hres
= ensure_driver(This
);
88 size
= ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
89 if(size
!= sizeof(icinfo
))
92 size
= ICCompressGetFormatSize(This
->hic
, &src_videoinfo
->bmiHeader
);
94 FIXME("ICCompressGetFormatSize failed\n");
98 size
+= FIELD_OFFSET(VIDEOINFOHEADER
, bmiHeader
);
99 This
->videoinfo
= heap_alloc(size
);
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
;
114 static struct strmbase_pin
*avi_compressor_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
116 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
119 return &filter
->sink
.pin
;
121 return &filter
->source
.pin
;
125 static void avi_compressor_destroy(struct strmbase_filter
*iface
)
127 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
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
);
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
;
145 return E_NOINTERFACE
;
147 IUnknown_AddRef((IUnknown
*)*out
);
151 static HRESULT
avi_compressor_init_stream(struct strmbase_filter
*iface
)
153 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
156 if (filter
->source
.pAllocator
&& FAILED(hr
= IMemAllocator_Commit(filter
->source
.pAllocator
)))
158 ERR("Failed to commit allocator, hr %#x.\n", hr
);
162 filter
->frame_cnt
= 0;
167 static HRESULT
avi_compressor_cleanup_stream(struct strmbase_filter
*iface
)
169 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
171 ICCompressEnd(filter
->hic
);
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
);
220 static HRESULT WINAPI
AVICompressorPropertyBag_Load(IPersistPropertyBag
*iface
, IPropertyBag
*pPropBag
, IErrorLog
*pErrorLog
)
222 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
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
);
232 hres
= IPropertyBag_Read(pPropBag
, fcc_handlerW
, &v
, NULL
);
234 WARN("Could not read FccHandler: %08x\n", hres
);
238 if(V_VT(&v
) != VT_BSTR
) {
239 FIXME("Got vt %d\n", V_VT(&v
));
245 TRACE("FccHandler = %s\n", debugstr_w(str
));
246 if(SysStringLen(str
) != 4) {
247 FIXME("Invalid FccHandler len\n");
252 This
->fcc_handler
= FCC(str
[0], str
[1], str
[2], str
[3]);
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
);
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
;
282 TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base
, pmt
);
284 if(!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Video
))
287 if(!IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo
)) {
288 FIXME("formattype %s unsupported\n", debugstr_guid(&pmt
->formattype
));
292 hres
= ensure_driver(This
);
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
;
308 return E_NOINTERFACE
;
310 IUnknown_AddRef((IUnknown
*)*out
);
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
;
322 IMediaSample2
*sample2
;
323 DWORD comp_flags
= 0;
330 TRACE("(%p)->(%p)\n", base
, pSample
);
334 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
335 return VFW_E_NOT_CONNECTED
;
339 FIXME("Driver not loaded\n");
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
);
354 WARN("GetTime failed: %08x\n", hres
);
358 hres
= IMediaSample_GetMediaType(pSample
, &mt
);
362 hres
= IMediaSample_GetPointer(pSample
, &ptr
);
364 WARN("GetPointer failed: %08x\n", hres
);
368 hres
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->source
, &out_sample
, &start
, &stop
, 0);
372 hres
= IMediaSample_GetPointer(out_sample
, &buf
);
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
);
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
);
397 IMediaSample_SetMediaTime(out_sample
, NULL
, NULL
);
399 hres
= IMemInputPin_Receive(meminput
, out_sample
);
401 WARN("Deliver failed: %08x\n", hres
);
403 IMediaSample_Release(out_sample
);
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
)
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
;
445 amt
->cbFormat
= This
->videoinfo_size
;
446 amt
->pbFormat
= (BYTE
*)This
->videoinfo
;
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
;