2 * AVI Decompressor (VFW decompressors wrapper)
4 * Copyright 2004-2005 Christian Costa
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "quartz_private.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
39 struct avi_decompressor
41 struct strmbase_filter filter
;
43 struct strmbase_source source
;
44 IQualityControl source_IQualityControl_iface
;
45 struct strmbase_passthrough passthrough
;
47 struct strmbase_sink sink
;
50 BITMAPINFOHEADER
* pBihIn
;
54 static struct avi_decompressor
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
56 return CONTAINING_RECORD(iface
, struct avi_decompressor
, filter
);
59 static HRESULT
avi_decompressor_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
61 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->filter
);
63 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
64 *out
= &filter
->sink
.IMemInputPin_iface
;
68 IUnknown_AddRef((IUnknown
*)*out
);
72 static HRESULT
avi_decompressor_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
77 static HRESULT
avi_decompressor_sink_end_flush(struct strmbase_sink
*iface
)
79 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->pin
.filter
);
81 if (filter
->source
.pin
.peer
)
82 return IPin_EndFlush(filter
->source
.pin
.peer
);
86 static int AVIDec_DropSample(struct avi_decompressor
*This
, REFERENCE_TIME tStart
)
91 if (tStart
< This
->late
) {
92 TRACE("Dropping sample\n");
99 static HRESULT WINAPI
avi_decompressor_sink_Receive(struct strmbase_sink
*iface
, IMediaSample
*pSample
)
101 struct avi_decompressor
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
102 VIDEOINFOHEADER
*source_format
;
105 IMediaSample
* pOutSample
= NULL
;
110 LONGLONG tStart
, tStop
;
113 /* We do not expect pin connection state to change while the filter is
114 * running. This guarantee is necessary, since otherwise we would have to
115 * take the filter lock, and we can't take the filter lock from a streaming
117 if (!This
->source
.pMemInputPin
)
119 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
120 return VFW_E_NOT_CONNECTED
;
123 source_format
= (VIDEOINFOHEADER
*)This
->source
.pin
.mt
.pbFormat
;
125 if (This
->filter
.state
== State_Stopped
)
126 return VFW_E_WRONG_STATE
;
128 if (This
->sink
.flushing
)
131 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
134 ERR("Cannot get pointer to sample data (%x)\n", hr
);
138 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
140 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream
, cbSrcStream
);
142 /* Update input size to match sample size */
143 This
->pBihIn
->biSizeImage
= cbSrcStream
;
145 hr
= BaseOutputPinImpl_GetDeliveryBuffer(&This
->source
, &pOutSample
, NULL
, NULL
, 0);
147 ERR("Unable to get delivery buffer (%x)\n", hr
);
151 hr
= IMediaSample_SetActualDataLength(pOutSample
, 0);
154 hr
= IMediaSample_GetPointer(pOutSample
, &pbDstStream
);
156 ERR("Unable to get pointer to buffer (%x)\n", hr
);
157 IMediaSample_Release(pOutSample
);
160 cbDstStream
= IMediaSample_GetSize(pOutSample
);
161 if (cbDstStream
< source_format
->bmiHeader
.biSizeImage
)
163 ERR("Sample size is too small (%u < %u).\n", cbDstStream
, source_format
->bmiHeader
.biSizeImage
);
164 IMediaSample_Release(pOutSample
);
168 if (IMediaSample_IsPreroll(pSample
) == S_OK
)
169 flags
|= ICDECOMPRESS_PREROLL
;
170 if (IMediaSample_IsSyncPoint(pSample
) != S_OK
)
171 flags
|= ICDECOMPRESS_NOTKEYFRAME
;
172 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
173 if (hr
== S_OK
&& AVIDec_DropSample(This
, tStart
))
174 flags
|= ICDECOMPRESS_HURRYUP
;
176 res
= ICDecompress(This
->hvid
, flags
, This
->pBihIn
, pbSrcStream
, &source_format
->bmiHeader
, pbDstStream
);
178 ERR("Error occurred during the decompression (%x)\n", res
);
180 /* Drop sample if it's intended to be dropped */
181 if (flags
& ICDECOMPRESS_HURRYUP
) {
182 IMediaSample_Release(pOutSample
);
186 IMediaSample_SetActualDataLength(pOutSample
, source_format
->bmiHeader
.biSizeImage
);
188 IMediaSample_SetPreroll(pOutSample
, (IMediaSample_IsPreroll(pSample
) == S_OK
));
189 IMediaSample_SetDiscontinuity(pOutSample
, (IMediaSample_IsDiscontinuity(pSample
) == S_OK
));
190 IMediaSample_SetSyncPoint(pOutSample
, (IMediaSample_IsSyncPoint(pSample
) == S_OK
));
193 IMediaSample_SetTime(pOutSample
, &tStart
, &tStop
);
194 else if (hr
== VFW_S_NO_STOP_TIME
)
195 IMediaSample_SetTime(pOutSample
, &tStart
, NULL
);
197 IMediaSample_SetTime(pOutSample
, NULL
, NULL
);
199 hr
= IMemInputPin_Receive(This
->source
.pMemInputPin
, pOutSample
);
200 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
201 ERR("Error sending sample (%x)\n", hr
);
203 IMediaSample_Release(pOutSample
);
207 static HRESULT
avi_decompressor_sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*pmt
)
209 struct avi_decompressor
*This
= impl_from_strmbase_filter(iface
->pin
.filter
);
210 HRESULT hr
= VFW_E_TYPE_NOT_ACCEPTED
;
212 /* Check root (GUID w/o FOURCC) */
213 if ((IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Video
)) &&
214 (!memcmp(((const char *)&pmt
->subtype
)+4, ((const char *)&MEDIATYPE_Video
)+4, sizeof(GUID
)-4)))
216 VIDEOINFOHEADER
*format1
= (VIDEOINFOHEADER
*)pmt
->pbFormat
;
217 VIDEOINFOHEADER2
*format2
= (VIDEOINFOHEADER2
*)pmt
->pbFormat
;
218 BITMAPINFOHEADER
*bmi
;
220 if (IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo
))
221 bmi
= &format1
->bmiHeader
;
222 else if (IsEqualIID(&pmt
->formattype
, &FORMAT_VideoInfo2
))
223 bmi
= &format2
->bmiHeader
;
227 This
->hvid
= ICLocate(pmt
->majortype
.Data1
, pmt
->subtype
.Data1
, bmi
, NULL
, ICMODE_DECOMPRESS
);
233 /* Copy bitmap header from media type to 1 for input and 1 for output */
234 bih_size
= bmi
->biSize
+ bmi
->biClrUsed
* 4;
235 This
->pBihIn
= CoTaskMemAlloc(bih_size
);
241 memcpy(This
->pBihIn
, bmi
, bih_size
);
243 if ((result
= ICDecompressQuery(This
->hvid
, This
->pBihIn
, NULL
)))
245 WARN("No decompressor found, error %d.\n", result
);
246 return VFW_E_TYPE_NOT_ACCEPTED
;
249 TRACE("Connection accepted\n");
252 TRACE("Unable to find a suitable VFW decompressor\n");
257 TRACE("Connection refused\n");
261 static void avi_decompressor_sink_disconnect(struct strmbase_sink
*iface
)
263 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->pin
.filter
);
266 ICClose(filter
->hvid
);
267 CoTaskMemFree(filter
->pBihIn
);
269 filter
->pBihIn
= NULL
;
272 static const struct strmbase_sink_ops sink_ops
=
274 .base
.pin_query_interface
= avi_decompressor_sink_query_interface
,
275 .base
.pin_query_accept
= avi_decompressor_sink_query_accept
,
276 .pfnReceive
= avi_decompressor_sink_Receive
,
277 .sink_connect
= avi_decompressor_sink_connect
,
278 .sink_disconnect
= avi_decompressor_sink_disconnect
,
279 .sink_end_flush
= avi_decompressor_sink_end_flush
,
282 static HRESULT
avi_decompressor_source_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
284 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->filter
);
286 if (IsEqualGUID(iid
, &IID_IQualityControl
))
287 *out
= &filter
->source_IQualityControl_iface
;
288 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
289 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
291 return E_NOINTERFACE
;
293 IUnknown_AddRef((IUnknown
*)*out
);
297 static HRESULT
avi_decompressor_source_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
299 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->filter
);
300 VIDEOINFOHEADER
*sink_format
, *format
;
302 if (!filter
->sink
.pin
.peer
|| !IsEqualGUID(&mt
->formattype
, &FORMAT_VideoInfo
))
305 sink_format
= (VIDEOINFOHEADER
*)filter
->sink
.pin
.mt
.pbFormat
;
306 format
= (VIDEOINFOHEADER
*)mt
->pbFormat
;
308 if (ICDecompressQuery(filter
->hvid
, &sink_format
->bmiHeader
, &format
->bmiHeader
))
314 static HRESULT
avi_decompressor_source_get_media_type(struct strmbase_pin
*iface
,
315 unsigned int index
, AM_MEDIA_TYPE
*mt
)
325 {&MEDIASUBTYPE_CLJR
, mmioFOURCC('C','L','J','R'), 8},
326 {&MEDIASUBTYPE_UYVY
, mmioFOURCC('U','Y','V','Y'), 16},
327 {&MEDIASUBTYPE_YUY2
, mmioFOURCC('Y','U','Y','2'), 16},
328 {&MEDIASUBTYPE_RGB32
, BI_RGB
, 32},
329 {&MEDIASUBTYPE_RGB24
, BI_RGB
, 24},
330 {&MEDIASUBTYPE_RGB565
, BI_BITFIELDS
, 16},
331 {&MEDIASUBTYPE_RGB555
, BI_RGB
, 16},
332 {&MEDIASUBTYPE_RGB8
, BI_RGB
, 8},
335 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
->filter
);
336 const VIDEOINFOHEADER
*sink_format
;
339 if (!filter
->sink
.pin
.peer
)
340 return VFW_S_NO_MORE_ITEMS
;
342 sink_format
= (VIDEOINFOHEADER
*)filter
->sink
.pin
.mt
.pbFormat
;
344 memset(mt
, 0, sizeof(AM_MEDIA_TYPE
));
346 if (index
< ARRAY_SIZE(formats
))
348 if (!(format
= CoTaskMemAlloc(offsetof(VIDEOINFO
, dwBitMasks
[3]))))
349 return E_OUTOFMEMORY
;
350 memset(format
, 0, offsetof(VIDEOINFO
, dwBitMasks
[3]));
352 format
->rcSource
= sink_format
->rcSource
;
353 format
->rcTarget
= sink_format
->rcTarget
;
354 format
->dwBitRate
= sink_format
->dwBitRate
;
355 format
->dwBitErrorRate
= sink_format
->dwBitErrorRate
;
356 format
->AvgTimePerFrame
= sink_format
->AvgTimePerFrame
;
358 format
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
359 format
->bmiHeader
.biWidth
= sink_format
->bmiHeader
.biWidth
;
360 format
->bmiHeader
.biHeight
= sink_format
->bmiHeader
.biHeight
;
361 format
->bmiHeader
.biPlanes
= sink_format
->bmiHeader
.biPlanes
;
362 format
->bmiHeader
.biBitCount
= formats
[index
].bpp
;
363 format
->bmiHeader
.biCompression
= formats
[index
].compression
;
364 format
->bmiHeader
.biSizeImage
= format
->bmiHeader
.biWidth
365 * format
->bmiHeader
.biHeight
* formats
[index
].bpp
/ 8;
367 if (IsEqualGUID(formats
[index
].subtype
, &MEDIASUBTYPE_RGB565
))
369 format
->dwBitMasks
[iRED
] = 0xf800;
370 format
->dwBitMasks
[iGREEN
] = 0x07e0;
371 format
->dwBitMasks
[iBLUE
] = 0x001f;
372 mt
->cbFormat
= offsetof(VIDEOINFO
, dwBitMasks
[3]);
375 mt
->cbFormat
= sizeof(VIDEOINFOHEADER
);
377 mt
->majortype
= MEDIATYPE_Video
;
378 mt
->subtype
= *formats
[index
].subtype
;
379 mt
->bFixedSizeSamples
= TRUE
;
380 mt
->lSampleSize
= format
->bmiHeader
.biSizeImage
;
381 mt
->formattype
= FORMAT_VideoInfo
;
382 mt
->pbFormat
= (BYTE
*)format
;
387 if (index
== ARRAY_SIZE(formats
))
389 size_t size
= ICDecompressGetFormatSize(filter
->hvid
, &sink_format
->bmiHeader
);
392 return VFW_S_NO_MORE_ITEMS
;
394 mt
->cbFormat
= offsetof(VIDEOINFOHEADER
, bmiHeader
) + size
;
395 if (!(format
= CoTaskMemAlloc(mt
->cbFormat
)))
396 return E_OUTOFMEMORY
;
397 memset(format
, 0, mt
->cbFormat
);
399 format
->rcSource
= sink_format
->rcSource
;
400 format
->rcTarget
= sink_format
->rcTarget
;
401 format
->dwBitRate
= sink_format
->dwBitRate
;
402 format
->dwBitErrorRate
= sink_format
->dwBitErrorRate
;
403 format
->AvgTimePerFrame
= sink_format
->AvgTimePerFrame
;
405 if (ICDecompressGetFormat(filter
->hvid
, &sink_format
->bmiHeader
, &format
->bmiHeader
))
407 CoTaskMemFree(format
);
408 return VFW_S_NO_MORE_ITEMS
;
411 mt
->majortype
= MEDIATYPE_Video
;
412 mt
->subtype
= MEDIATYPE_Video
;
413 mt
->subtype
.Data1
= format
->bmiHeader
.biCompression
;
414 mt
->bFixedSizeSamples
= TRUE
;
415 mt
->lSampleSize
= format
->bmiHeader
.biSizeImage
;
416 mt
->formattype
= FORMAT_VideoInfo
;
417 mt
->pbFormat
= (BYTE
*)format
;
422 return VFW_S_NO_MORE_ITEMS
;
425 static HRESULT WINAPI
avi_decompressor_source_DecideBufferSize(struct strmbase_source
*iface
,
426 IMemAllocator
*pAlloc
, ALLOCATOR_PROPERTIES
*ppropInputRequest
)
428 const VIDEOINFOHEADER
*source_format
= (VIDEOINFOHEADER
*)iface
->pin
.mt
.pbFormat
;
429 ALLOCATOR_PROPERTIES actual
;
431 if (!ppropInputRequest
->cbAlign
)
432 ppropInputRequest
->cbAlign
= 1;
434 if (ppropInputRequest
->cbBuffer
< source_format
->bmiHeader
.biSizeImage
)
435 ppropInputRequest
->cbBuffer
= source_format
->bmiHeader
.biSizeImage
;
437 if (!ppropInputRequest
->cBuffers
)
438 ppropInputRequest
->cBuffers
= 1;
440 return IMemAllocator_SetProperties(pAlloc
, ppropInputRequest
, &actual
);
443 static const struct strmbase_source_ops source_ops
=
445 .base
.pin_query_interface
= avi_decompressor_source_query_interface
,
446 .base
.pin_query_accept
= avi_decompressor_source_query_accept
,
447 .base
.pin_get_media_type
= avi_decompressor_source_get_media_type
,
448 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
449 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
450 .pfnDecideBufferSize
= avi_decompressor_source_DecideBufferSize
,
453 static struct avi_decompressor
*impl_from_source_IQualityControl(IQualityControl
*iface
)
455 return CONTAINING_RECORD(iface
, struct avi_decompressor
, source_IQualityControl_iface
);
458 static HRESULT WINAPI
avi_decompressor_source_qc_QueryInterface(IQualityControl
*iface
,
459 REFIID iid
, void **out
)
461 struct avi_decompressor
*filter
= impl_from_source_IQualityControl(iface
);
462 return IPin_QueryInterface(&filter
->source
.pin
.IPin_iface
, iid
, out
);
465 static ULONG WINAPI
avi_decompressor_source_qc_AddRef(IQualityControl
*iface
)
467 struct avi_decompressor
*filter
= impl_from_source_IQualityControl(iface
);
468 return IPin_AddRef(&filter
->source
.pin
.IPin_iface
);
471 static ULONG WINAPI
avi_decompressor_source_qc_Release(IQualityControl
*iface
)
473 struct avi_decompressor
*filter
= impl_from_source_IQualityControl(iface
);
474 return IPin_Release(&filter
->source
.pin
.IPin_iface
);
477 static HRESULT WINAPI
avi_decompressor_source_qc_Notify(IQualityControl
*iface
,
478 IBaseFilter
*sender
, Quality q
)
480 struct avi_decompressor
*filter
= impl_from_source_IQualityControl(iface
);
482 TRACE("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s.\n",
483 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
485 EnterCriticalSection(&filter
->filter
.stream_cs
);
487 filter
->late
= q
.Late
+ q
.TimeStamp
;
490 LeaveCriticalSection(&filter
->filter
.stream_cs
);
494 static HRESULT WINAPI
avi_decompressor_source_qc_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
496 struct avi_decompressor
*filter
= impl_from_source_IQualityControl(iface
);
498 TRACE("filter %p, sink %p.\n", filter
, sink
);
503 static const IQualityControlVtbl source_qc_vtbl
=
505 avi_decompressor_source_qc_QueryInterface
,
506 avi_decompressor_source_qc_AddRef
,
507 avi_decompressor_source_qc_Release
,
508 avi_decompressor_source_qc_Notify
,
509 avi_decompressor_source_qc_SetSink
,
512 static struct strmbase_pin
*avi_decompressor_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
514 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
);
517 return &filter
->sink
.pin
;
519 return &filter
->source
.pin
;
523 static void avi_decompressor_destroy(struct strmbase_filter
*iface
)
525 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
);
527 if (filter
->sink
.pin
.peer
)
528 IPin_Disconnect(filter
->sink
.pin
.peer
);
529 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
531 if (filter
->source
.pin
.peer
)
532 IPin_Disconnect(filter
->source
.pin
.peer
);
533 IPin_Disconnect(&filter
->source
.pin
.IPin_iface
);
535 strmbase_sink_cleanup(&filter
->sink
);
536 strmbase_source_cleanup(&filter
->source
);
537 strmbase_passthrough_cleanup(&filter
->passthrough
);
539 strmbase_filter_cleanup(&filter
->filter
);
542 InterlockedDecrement(&object_locks
);
545 static HRESULT
avi_decompressor_init_stream(struct strmbase_filter
*iface
)
547 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
);
548 VIDEOINFOHEADER
*source_format
;
552 if (!filter
->source
.pin
.peer
)
557 source_format
= (VIDEOINFOHEADER
*)filter
->sink
.pin
.mt
.pbFormat
;
558 if ((res
= ICDecompressBegin(filter
->hvid
, filter
->pBihIn
, &source_format
->bmiHeader
)))
560 ERR("ICDecompressBegin() failed, error %ld.\n", res
);
564 if (FAILED(hr
= IMemAllocator_Commit(filter
->source
.pAllocator
)))
565 ERR("Failed to commit allocator, hr %#x.\n", hr
);
570 static HRESULT
avi_decompressor_cleanup_stream(struct strmbase_filter
*iface
)
572 struct avi_decompressor
*filter
= impl_from_strmbase_filter(iface
);
575 if (!filter
->source
.pin
.peer
)
578 if (filter
->hvid
&& (res
= ICDecompressEnd(filter
->hvid
)))
580 ERR("ICDecompressEnd() failed, error %ld.\n", res
);
584 IMemAllocator_Decommit(filter
->source
.pAllocator
);
589 static const struct strmbase_filter_ops filter_ops
=
591 .filter_get_pin
= avi_decompressor_get_pin
,
592 .filter_destroy
= avi_decompressor_destroy
,
593 .filter_init_stream
= avi_decompressor_init_stream
,
594 .filter_cleanup_stream
= avi_decompressor_cleanup_stream
,
597 HRESULT
avi_dec_create(IUnknown
*outer
, IUnknown
**out
)
599 struct avi_decompressor
*object
;
601 if (!(object
= calloc(1, sizeof(*object
))))
602 return E_OUTOFMEMORY
;
604 strmbase_filter_init(&object
->filter
, outer
, &CLSID_AVIDec
, &filter_ops
);
606 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
608 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
609 object
->source_IQualityControl_iface
.lpVtbl
= &source_qc_vtbl
;
610 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->source
.pin
.IPin_iface
);
611 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, FALSE
,
612 &object
->sink
.pin
.IPin_iface
);
614 TRACE("Created AVI decompressor %p.\n", object
);
615 *out
= &object
->filter
.IUnknown_inner
;