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 "qcap_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
26 struct strmbase_filter filter
;
27 IPersistPropertyBag IPersistPropertyBag_iface
;
29 struct strmbase_sink sink
;
30 struct strmbase_source source
;
35 VIDEOINFOHEADER
*videoinfo
;
36 size_t videoinfo_size
;
43 static inline AVICompressor
*impl_from_strmbase_filter(struct strmbase_filter
*filter
)
45 return CONTAINING_RECORD(filter
, AVICompressor
, filter
);
48 static inline AVICompressor
*impl_from_strmbase_pin(struct strmbase_pin
*pin
)
50 return impl_from_strmbase_filter(pin
->filter
);
53 static HRESULT
ensure_driver(AVICompressor
*This
)
58 This
->hic
= ICOpen(FCC('v','i','d','c'), This
->fcc_handler
, ICMODE_COMPRESS
);
60 FIXME("ICOpen failed\n");
67 static HRESULT
fill_format_info(AVICompressor
*This
, VIDEOINFOHEADER
*src_videoinfo
)
73 hres
= ensure_driver(This
);
77 size
= ICGetInfo(This
->hic
, &icinfo
, sizeof(icinfo
));
78 if(size
!= sizeof(icinfo
))
81 size
= ICCompressGetFormatSize(This
->hic
, &src_videoinfo
->bmiHeader
);
83 FIXME("ICCompressGetFormatSize failed\n");
87 size
+= FIELD_OFFSET(VIDEOINFOHEADER
, bmiHeader
);
88 if (!(This
->videoinfo
= calloc(1, size
)))
91 This
->videoinfo_size
= size
;
92 This
->driver_flags
= icinfo
.dwFlags
;
93 ICCompressGetFormat(This
->hic
, &src_videoinfo
->bmiHeader
, &This
->videoinfo
->bmiHeader
);
95 This
->videoinfo
->dwBitRate
= 10000000/src_videoinfo
->AvgTimePerFrame
* This
->videoinfo
->bmiHeader
.biSizeImage
* 8;
96 This
->videoinfo
->AvgTimePerFrame
= src_videoinfo
->AvgTimePerFrame
;
97 This
->max_frame_size
= This
->videoinfo
->bmiHeader
.biSizeImage
;
101 static struct strmbase_pin
*avi_compressor_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
103 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
106 return &filter
->sink
.pin
;
108 return &filter
->source
.pin
;
112 static void avi_compressor_destroy(struct strmbase_filter
*iface
)
114 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
117 ICClose(filter
->hic
);
118 free(filter
->videoinfo
);
119 strmbase_sink_cleanup(&filter
->sink
);
120 strmbase_source_cleanup(&filter
->source
);
121 strmbase_filter_cleanup(&filter
->filter
);
125 static HRESULT
avi_compressor_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
127 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
129 if (IsEqualGUID(iid
, &IID_IPersistPropertyBag
))
130 *out
= &filter
->IPersistPropertyBag_iface
;
132 return E_NOINTERFACE
;
134 IUnknown_AddRef((IUnknown
*)*out
);
138 static HRESULT
avi_compressor_init_stream(struct strmbase_filter
*iface
)
140 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
143 if (filter
->source
.pAllocator
&& FAILED(hr
= IMemAllocator_Commit(filter
->source
.pAllocator
)))
145 ERR("Failed to commit allocator, hr %#x.\n", hr
);
149 filter
->frame_cnt
= 0;
154 static HRESULT
avi_compressor_cleanup_stream(struct strmbase_filter
*iface
)
156 AVICompressor
*filter
= impl_from_strmbase_filter(iface
);
158 ICCompressEnd(filter
->hic
);
162 static const struct strmbase_filter_ops filter_ops
=
164 .filter_get_pin
= avi_compressor_get_pin
,
165 .filter_destroy
= avi_compressor_destroy
,
166 .filter_query_interface
= avi_compressor_query_interface
,
167 .filter_init_stream
= avi_compressor_init_stream
,
168 .filter_cleanup_stream
= avi_compressor_cleanup_stream
,
171 static AVICompressor
*impl_from_IPersistPropertyBag(IPersistPropertyBag
*iface
)
173 return CONTAINING_RECORD(iface
, AVICompressor
, IPersistPropertyBag_iface
);
176 static HRESULT WINAPI
AVICompressorPropertyBag_QueryInterface(IPersistPropertyBag
*iface
, REFIID riid
, void **ppv
)
178 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
179 return IBaseFilter_QueryInterface(&This
->filter
.IBaseFilter_iface
, riid
, ppv
);
182 static ULONG WINAPI
AVICompressorPropertyBag_AddRef(IPersistPropertyBag
*iface
)
184 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
185 return IBaseFilter_AddRef(&This
->filter
.IBaseFilter_iface
);
188 static ULONG WINAPI
AVICompressorPropertyBag_Release(IPersistPropertyBag
*iface
)
190 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
191 return IBaseFilter_Release(&This
->filter
.IBaseFilter_iface
);
194 static HRESULT WINAPI
AVICompressorPropertyBag_GetClassID(IPersistPropertyBag
*iface
, CLSID
*pClassID
)
196 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
197 return IBaseFilter_GetClassID(&This
->filter
.IBaseFilter_iface
, pClassID
);
200 static HRESULT WINAPI
AVICompressorPropertyBag_InitNew(IPersistPropertyBag
*iface
)
202 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
203 FIXME("(%p)->()\n", This
);
207 static HRESULT WINAPI
AVICompressorPropertyBag_Load(IPersistPropertyBag
*iface
, IPropertyBag
*pPropBag
, IErrorLog
*pErrorLog
)
209 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
214 TRACE("(%p)->(%p %p)\n", This
, pPropBag
, pErrorLog
);
217 hres
= IPropertyBag_Read(pPropBag
, L
"FccHandler", &v
, NULL
);
219 WARN("Could not read FccHandler: %08x\n", hres
);
223 if(V_VT(&v
) != VT_BSTR
) {
224 FIXME("Got vt %d\n", V_VT(&v
));
230 TRACE("FccHandler = %s\n", debugstr_w(str
));
231 if(SysStringLen(str
) != 4) {
232 FIXME("Invalid FccHandler len\n");
237 This
->fcc_handler
= FCC(str
[0], str
[1], str
[2], str
[3]);
242 static HRESULT WINAPI
AVICompressorPropertyBag_Save(IPersistPropertyBag
*iface
, IPropertyBag
*pPropBag
,
243 BOOL fClearDirty
, BOOL fSaveAllProperties
)
245 AVICompressor
*This
= impl_from_IPersistPropertyBag(iface
);
246 FIXME("(%p)->(%p %x %x)\n", This
, pPropBag
, fClearDirty
, fSaveAllProperties
);
250 static const IPersistPropertyBagVtbl PersistPropertyBagVtbl
= {
251 AVICompressorPropertyBag_QueryInterface
,
252 AVICompressorPropertyBag_AddRef
,
253 AVICompressorPropertyBag_Release
,
254 AVICompressorPropertyBag_GetClassID
,
255 AVICompressorPropertyBag_InitNew
,
256 AVICompressorPropertyBag_Load
,
257 AVICompressorPropertyBag_Save
260 static HRESULT
sink_query_accept(struct strmbase_pin
*base
, const AM_MEDIA_TYPE
*pmt
)
262 AVICompressor
*This
= impl_from_strmbase_pin(base
);
263 VIDEOINFOHEADER
*videoinfo
;
267 TRACE("(%p)->(AM_MEDIA_TYPE(%p))\n", base
, pmt
);
269 if(!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Video
))
272 if(!IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo
)) {
273 FIXME("formattype %s unsupported\n", debugstr_guid(&pmt
->formattype
));
277 hres
= ensure_driver(This
);
281 videoinfo
= (VIDEOINFOHEADER
*)pmt
->pbFormat
;
282 res
= ICCompressQuery(This
->hic
, &videoinfo
->bmiHeader
, NULL
);
283 return res
== ICERR_OK
? S_OK
: S_FALSE
;
286 static HRESULT
sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
288 AVICompressor
*filter
= impl_from_strmbase_pin(iface
);
290 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
291 *out
= &filter
->sink
.IMemInputPin_iface
;
293 return E_NOINTERFACE
;
295 IUnknown_AddRef((IUnknown
*)*out
);
299 static HRESULT WINAPI
AVICompressorIn_Receive(struct strmbase_sink
*base
, IMediaSample
*pSample
)
301 AVICompressor
*This
= impl_from_strmbase_pin(&base
->pin
);
302 IMemInputPin
*meminput
= This
->source
.pMemInputPin
;
303 VIDEOINFOHEADER
*src_videoinfo
;
304 REFERENCE_TIME start
, stop
;
305 IMediaSample
*out_sample
;
307 IMediaSample2
*sample2
;
308 DWORD comp_flags
= 0;
315 TRACE("(%p)->(%p)\n", base
, pSample
);
319 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
320 return VFW_E_NOT_CONNECTED
;
324 FIXME("Driver not loaded\n");
328 hres
= IMediaSample_QueryInterface(pSample
, &IID_IMediaSample2
, (void**)&sample2
);
329 if(SUCCEEDED(hres
)) {
330 FIXME("Use IMediaSample2\n");
331 IMediaSample2_Release(sample2
);
334 is_preroll
= IMediaSample_IsPreroll(pSample
) == S_OK
;
335 sync_point
= IMediaSample_IsSyncPoint(pSample
) == S_OK
;
337 hres
= IMediaSample_GetTime(pSample
, &start
, &stop
);
339 WARN("GetTime failed: %08x\n", hres
);
343 hres
= IMediaSample_GetMediaType(pSample
, &mt
);
347 hres
= IMediaSample_GetPointer(pSample
, &ptr
);
349 WARN("GetPointer failed: %08x\n", hres
);
353 hres
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->source
, &out_sample
, &start
, &stop
, 0);
357 hres
= IMediaSample_GetPointer(out_sample
, &buf
);
361 if((This
->driver_flags
& VIDCF_TEMPORAL
) && !(This
->driver_flags
& VIDCF_FASTTEMPORALC
))
362 FIXME("Unsupported temporal compression\n");
364 src_videoinfo
= (VIDEOINFOHEADER
*)This
->sink
.pin
.mt
.pbFormat
;
365 This
->videoinfo
->bmiHeader
.biSizeImage
= This
->max_frame_size
;
366 res
= ICCompress(This
->hic
, sync_point
? ICCOMPRESS_KEYFRAME
: 0, &This
->videoinfo
->bmiHeader
, buf
,
367 &src_videoinfo
->bmiHeader
, ptr
, 0, &comp_flags
, This
->frame_cnt
, 0, 0, NULL
, NULL
);
368 if(res
!= ICERR_OK
) {
369 WARN("ICCompress failed: %d\n", res
);
370 IMediaSample_Release(out_sample
);
374 IMediaSample_SetActualDataLength(out_sample
, This
->videoinfo
->bmiHeader
.biSizeImage
);
375 IMediaSample_SetPreroll(out_sample
, is_preroll
);
376 IMediaSample_SetSyncPoint(out_sample
, (comp_flags
&AVIIF_KEYFRAME
) != 0);
377 IMediaSample_SetDiscontinuity(out_sample
, (IMediaSample_IsDiscontinuity(pSample
) == S_OK
));
379 if (IMediaSample_GetMediaTime(pSample
, &start
, &stop
) == S_OK
)
380 IMediaSample_SetMediaTime(out_sample
, &start
, &stop
);
382 IMediaSample_SetMediaTime(out_sample
, NULL
, NULL
);
384 hres
= IMemInputPin_Receive(meminput
, out_sample
);
386 WARN("Deliver failed: %08x\n", hres
);
388 IMediaSample_Release(out_sample
);
393 static HRESULT
sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*mt
)
395 AVICompressor
*filter
= impl_from_strmbase_pin(&iface
->pin
);
396 return fill_format_info(filter
, (VIDEOINFOHEADER
*)mt
->pbFormat
);
399 static void sink_disconnect(struct strmbase_sink
*iface
)
401 AVICompressor
*filter
= impl_from_strmbase_pin(&iface
->pin
);
402 free(filter
->videoinfo
);
403 filter
->videoinfo
= NULL
;
406 static const struct strmbase_sink_ops sink_ops
=
408 .base
.pin_query_accept
= sink_query_accept
,
409 .base
.pin_query_interface
= sink_query_interface
,
410 .pfnReceive
= AVICompressorIn_Receive
,
411 .sink_connect
= sink_connect
,
412 .sink_disconnect
= sink_disconnect
,
415 static HRESULT
source_get_media_type(struct strmbase_pin
*base
, unsigned int iPosition
, AM_MEDIA_TYPE
*amt
)
417 AVICompressor
*This
= impl_from_strmbase_filter(base
->filter
);
419 if(iPosition
|| !This
->videoinfo
)
422 amt
->majortype
= MEDIATYPE_Video
;
423 amt
->subtype
= MEDIASUBTYPE_PCM
;
424 amt
->bFixedSizeSamples
= FALSE
;
425 amt
->bTemporalCompression
= (This
->driver_flags
& VIDCF_TEMPORAL
) != 0;
426 amt
->lSampleSize
= This
->sink
.pin
.mt
.lSampleSize
;
427 amt
->formattype
= FORMAT_VideoInfo
;
429 amt
->cbFormat
= This
->videoinfo_size
;
430 amt
->pbFormat
= (BYTE
*)This
->videoinfo
;
434 static HRESULT WINAPI
AVICompressorOut_DecideBufferSize(struct strmbase_source
*base
,
435 IMemAllocator
*alloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
437 AVICompressor
*This
= impl_from_strmbase_pin(&base
->pin
);
438 ALLOCATOR_PROPERTIES actual
;
440 TRACE("(%p)\n", This
);
442 if (!ppropInputRequest
->cBuffers
)
443 ppropInputRequest
->cBuffers
= 1;
444 if (ppropInputRequest
->cbBuffer
< This
->max_frame_size
)
445 ppropInputRequest
->cbBuffer
= This
->max_frame_size
;
446 if (!ppropInputRequest
->cbAlign
)
447 ppropInputRequest
->cbAlign
= 1;
449 return IMemAllocator_SetProperties(alloc
, ppropInputRequest
, &actual
);
452 static HRESULT WINAPI
AVICompressorOut_DecideAllocator(struct strmbase_source
*base
,
453 IMemInputPin
*pPin
, IMemAllocator
**pAlloc
)
455 TRACE("(%p)->(%p %p)\n", base
, pPin
, pAlloc
);
456 return BaseOutputPinImpl_DecideAllocator(base
, pPin
, pAlloc
);
459 static const struct strmbase_source_ops source_ops
=
461 .base
.pin_get_media_type
= source_get_media_type
,
462 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
463 .pfnDecideBufferSize
= AVICompressorOut_DecideBufferSize
,
464 .pfnDecideAllocator
= AVICompressorOut_DecideAllocator
,
467 HRESULT
avi_compressor_create(IUnknown
*outer
, IUnknown
**out
)
469 AVICompressor
*object
;
471 if (!(object
= calloc(1, sizeof(*object
))))
472 return E_OUTOFMEMORY
;
474 strmbase_filter_init(&object
->filter
, outer
, &CLSID_AVICo
, &filter_ops
);
475 object
->IPersistPropertyBag_iface
.lpVtbl
= &PersistPropertyBagVtbl
;
477 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
478 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
480 TRACE("Created AVI compressor %p.\n", object
);
481 *out
= &object
->filter
.IUnknown_inner
;