2 * DirectShow transform filters
4 * Copyright 2022 Anton Baskanov
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 "gst_private.h"
22 #include "gst_guids.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
28 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
32 struct strmbase_filter filter
;
33 IMpegAudioDecoder IMpegAudioDecoder_iface
;
35 struct strmbase_sink sink
;
36 struct strmbase_source source
;
37 struct strmbase_passthrough passthrough
;
39 IQualityControl sink_IQualityControl_iface
;
40 IQualityControl source_IQualityControl_iface
;
41 IQualityControl
*qc_sink
;
43 wg_transform_t transform
;
44 struct wg_sample_queue
*sample_queue
;
46 const struct transform_ops
*ops
;
51 HRESULT (*sink_query_accept
)(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
);
52 HRESULT (*source_query_accept
)(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
);
53 HRESULT (*source_get_media_type
)(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
);
54 HRESULT (*source_decide_buffer_size
)(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
);
55 HRESULT (*source_qc_notify
)(struct transform
*filter
, IBaseFilter
*sender
, Quality q
);
58 static inline struct transform
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
60 return CONTAINING_RECORD(iface
, struct transform
, filter
);
63 static struct strmbase_pin
*transform_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
65 struct transform
*filter
= impl_from_strmbase_filter(iface
);
67 return &filter
->sink
.pin
;
69 return &filter
->source
.pin
;
73 static void transform_destroy(struct strmbase_filter
*iface
)
75 struct transform
*filter
= impl_from_strmbase_filter(iface
);
77 strmbase_passthrough_cleanup(&filter
->passthrough
);
78 strmbase_source_cleanup(&filter
->source
);
79 strmbase_sink_cleanup(&filter
->sink
);
80 strmbase_filter_cleanup(&filter
->filter
);
85 static HRESULT
transform_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
87 struct transform
*filter
= impl_from_strmbase_filter(iface
);
89 if (IsEqualGUID(iid
, &IID_IMpegAudioDecoder
) && filter
->IMpegAudioDecoder_iface
.lpVtbl
)
90 *out
= &filter
->IMpegAudioDecoder_iface
;
94 IUnknown_AddRef((IUnknown
*)*out
);
98 static HRESULT
transform_init_stream(struct strmbase_filter
*iface
)
100 struct transform
*filter
= impl_from_strmbase_filter(iface
);
101 struct wg_transform_attrs attrs
= {0};
104 if (filter
->source
.pin
.peer
)
106 if (FAILED(hr
= wg_sample_queue_create(&filter
->sample_queue
)))
109 if (FAILED(hr
= wg_transform_create_quartz(&filter
->sink
.pin
.mt
, &filter
->source
.pin
.mt
,
110 &attrs
, &filter
->transform
)))
112 wg_sample_queue_destroy(filter
->sample_queue
);
116 hr
= IMemAllocator_Commit(filter
->source
.pAllocator
);
118 ERR("Failed to commit allocator, hr %#lx.\n", hr
);
124 static HRESULT
transform_cleanup_stream(struct strmbase_filter
*iface
)
126 struct transform
*filter
= impl_from_strmbase_filter(iface
);
128 if (filter
->source
.pin
.peer
)
130 IMemAllocator_Decommit(filter
->source
.pAllocator
);
132 EnterCriticalSection(&filter
->filter
.stream_cs
);
133 wg_transform_destroy(filter
->transform
);
134 wg_sample_queue_destroy(filter
->sample_queue
);
135 LeaveCriticalSection(&filter
->filter
.stream_cs
);
141 static const struct strmbase_filter_ops filter_ops
=
143 .filter_get_pin
= transform_get_pin
,
144 .filter_destroy
= transform_destroy
,
145 .filter_query_interface
= transform_query_interface
,
146 .filter_init_stream
= transform_init_stream
,
147 .filter_cleanup_stream
= transform_cleanup_stream
,
150 static struct transform
*impl_from_IMpegAudioDecoder(IMpegAudioDecoder
*iface
)
152 return CONTAINING_RECORD(iface
, struct transform
, IMpegAudioDecoder_iface
);
155 static HRESULT WINAPI
mpeg_audio_decoder_QueryInterface(IMpegAudioDecoder
*iface
,
156 REFIID iid
, void **out
)
158 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
159 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
162 static ULONG WINAPI
mpeg_audio_decoder_AddRef(IMpegAudioDecoder
*iface
)
164 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
165 return IUnknown_AddRef(filter
->filter
.outer_unk
);
168 static ULONG WINAPI
mpeg_audio_decoder_Release(IMpegAudioDecoder
*iface
)
170 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
171 return IUnknown_Release(filter
->filter
.outer_unk
);
174 static HRESULT WINAPI
mpeg_audio_decoder_get_FrequencyDivider(IMpegAudioDecoder
*iface
, ULONG
*divider
)
176 FIXME("iface %p, divider %p, stub!\n", iface
, divider
);
180 static HRESULT WINAPI
mpeg_audio_decoder_put_FrequencyDivider(IMpegAudioDecoder
*iface
, ULONG divider
)
182 FIXME("iface %p, divider %lu, stub!\n", iface
, divider
);
186 static HRESULT WINAPI
mpeg_audio_decoder_get_DecoderAccuracy(IMpegAudioDecoder
*iface
, ULONG
*accuracy
)
188 FIXME("iface %p, accuracy %p, stub!\n", iface
, accuracy
);
192 static HRESULT WINAPI
mpeg_audio_decoder_put_DecoderAccuracy(IMpegAudioDecoder
*iface
, ULONG accuracy
)
194 FIXME("iface %p, accuracy %lu, stub!\n", iface
, accuracy
);
198 static HRESULT WINAPI
mpeg_audio_decoder_get_Stereo(IMpegAudioDecoder
*iface
, ULONG
*stereo
)
200 FIXME("iface %p, stereo %p, stub!\n", iface
, stereo
);
204 static HRESULT WINAPI
mpeg_audio_decoder_put_Stereo(IMpegAudioDecoder
*iface
, ULONG stereo
)
206 FIXME("iface %p, stereo %lu, stub!\n", iface
, stereo
);
210 static HRESULT WINAPI
mpeg_audio_decoder_get_DecoderWordSize(IMpegAudioDecoder
*iface
, ULONG
*word_size
)
212 FIXME("iface %p, word_size %p, stub!\n", iface
, word_size
);
216 static HRESULT WINAPI
mpeg_audio_decoder_put_DecoderWordSize(IMpegAudioDecoder
*iface
, ULONG word_size
)
218 FIXME("iface %p, word_size %lu, stub!\n", iface
, word_size
);
222 static HRESULT WINAPI
mpeg_audio_decoder_get_IntegerDecode(IMpegAudioDecoder
*iface
, ULONG
*integer_decode
)
224 FIXME("iface %p, integer_decode %p, stub!\n", iface
, integer_decode
);
228 static HRESULT WINAPI
mpeg_audio_decoder_put_IntegerDecode(IMpegAudioDecoder
*iface
, ULONG integer_decode
)
230 FIXME("iface %p, integer_decode %lu, stub!\n", iface
, integer_decode
);
234 static HRESULT WINAPI
mpeg_audio_decoder_get_DualMode(IMpegAudioDecoder
*iface
, ULONG
*dual_mode
)
236 FIXME("iface %p, dual_mode %p, stub!\n", iface
, dual_mode
);
240 static HRESULT WINAPI
mpeg_audio_decoder_put_DualMode(IMpegAudioDecoder
*iface
, ULONG dual_mode
)
242 FIXME("iface %p, dual_mode %lu, stub!\n", iface
, dual_mode
);
246 static HRESULT WINAPI
mpeg_audio_decoder_get_AudioFormat(IMpegAudioDecoder
*iface
, MPEG1WAVEFORMAT
*format
)
248 FIXME("iface %p, format %p, stub!\n", iface
, format
);
252 static const IMpegAudioDecoderVtbl mpeg_audio_decoder_vtbl
=
254 mpeg_audio_decoder_QueryInterface
,
255 mpeg_audio_decoder_AddRef
,
256 mpeg_audio_decoder_Release
,
257 mpeg_audio_decoder_get_FrequencyDivider
,
258 mpeg_audio_decoder_put_FrequencyDivider
,
259 mpeg_audio_decoder_get_DecoderAccuracy
,
260 mpeg_audio_decoder_put_DecoderAccuracy
,
261 mpeg_audio_decoder_get_Stereo
,
262 mpeg_audio_decoder_put_Stereo
,
263 mpeg_audio_decoder_get_DecoderWordSize
,
264 mpeg_audio_decoder_put_DecoderWordSize
,
265 mpeg_audio_decoder_get_IntegerDecode
,
266 mpeg_audio_decoder_put_IntegerDecode
,
267 mpeg_audio_decoder_get_DualMode
,
268 mpeg_audio_decoder_put_DualMode
,
269 mpeg_audio_decoder_get_AudioFormat
,
272 static HRESULT
transform_sink_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
274 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
276 return filter
->ops
->sink_query_accept(filter
, mt
);
279 static HRESULT
transform_sink_query_interface(struct strmbase_pin
*pin
, REFIID iid
, void **out
)
281 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
283 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
284 *out
= &filter
->sink
.IMemInputPin_iface
;
285 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
286 *out
= &filter
->sink_IQualityControl_iface
;
288 return E_NOINTERFACE
;
290 IUnknown_AddRef((IUnknown
*)*out
);
294 static HRESULT WINAPI
transform_sink_receive(struct strmbase_sink
*pin
, IMediaSample
*sample
)
296 struct transform
*filter
= impl_from_strmbase_filter(pin
->pin
.filter
);
297 struct wg_sample
*wg_sample
;
300 /* We do not expect pin connection state to change while the filter is
301 * running. This guarantee is necessary, since otherwise we would have to
302 * take the filter lock, and we can't take the filter lock from a streaming
304 if (!filter
->source
.pMemInputPin
)
306 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
307 return VFW_E_NOT_CONNECTED
;
310 if (filter
->filter
.state
== State_Stopped
)
311 return VFW_E_WRONG_STATE
;
313 if (filter
->sink
.flushing
)
316 hr
= wg_sample_create_quartz(sample
, &wg_sample
);
320 hr
= wg_transform_push_quartz(filter
->transform
, wg_sample
, filter
->sample_queue
);
326 IMediaSample
*output_sample
;
328 hr
= IMemAllocator_GetBuffer(filter
->source
.pAllocator
, &output_sample
, NULL
, NULL
, 0);
332 hr
= wg_sample_create_quartz(output_sample
, &wg_sample
);
335 IMediaSample_Release(output_sample
);
339 hr
= wg_transform_read_quartz(filter
->transform
, wg_sample
);
340 wg_sample_release(wg_sample
);
342 if (hr
== MF_E_TRANSFORM_NEED_MORE_INPUT
)
344 IMediaSample_Release(output_sample
);
349 IMediaSample_Release(output_sample
);
353 wg_sample_queue_flush(filter
->sample_queue
, false);
355 hr
= IMemInputPin_Receive(filter
->source
.pMemInputPin
, output_sample
);
358 IMediaSample_Release(output_sample
);
362 IMediaSample_Release(output_sample
);
368 static const struct strmbase_sink_ops sink_ops
=
370 .base
.pin_query_accept
= transform_sink_query_accept
,
371 .base
.pin_query_interface
= transform_sink_query_interface
,
372 .pfnReceive
= transform_sink_receive
,
375 static HRESULT
transform_source_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
377 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
379 return filter
->ops
->source_query_accept(filter
, mt
);
382 static HRESULT
transform_source_get_media_type(struct strmbase_pin
*pin
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
384 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
386 return filter
->ops
->source_get_media_type(filter
, index
, mt
);
389 static HRESULT
transform_source_query_interface(struct strmbase_pin
*pin
, REFIID iid
, void **out
)
391 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
393 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
394 *out
= &filter
->passthrough
.IMediaPosition_iface
;
395 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
396 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
397 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
398 *out
= &filter
->source_IQualityControl_iface
;
400 return E_NOINTERFACE
;
402 IUnknown_AddRef((IUnknown
*)*out
);
406 static HRESULT WINAPI
transform_source_DecideBufferSize(struct strmbase_source
*pin
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
408 struct transform
*filter
= impl_from_strmbase_filter(pin
->pin
.filter
);
410 return filter
->ops
->source_decide_buffer_size(filter
, allocator
, props
);
413 static const struct strmbase_source_ops source_ops
=
415 .base
.pin_query_accept
= transform_source_query_accept
,
416 .base
.pin_get_media_type
= transform_source_get_media_type
,
417 .base
.pin_query_interface
= transform_source_query_interface
,
418 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
419 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
420 .pfnDecideBufferSize
= transform_source_DecideBufferSize
,
423 static struct transform
*impl_from_sink_IQualityControl(IQualityControl
*iface
)
425 return CONTAINING_RECORD(iface
, struct transform
, sink_IQualityControl_iface
);
428 static HRESULT WINAPI
sink_quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
430 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
431 return IPin_QueryInterface(&filter
->source
.pin
.IPin_iface
, iid
, out
);
434 static ULONG WINAPI
sink_quality_control_AddRef(IQualityControl
*iface
)
436 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
437 return IPin_AddRef(&filter
->source
.pin
.IPin_iface
);
440 static ULONG WINAPI
sink_quality_control_Release(IQualityControl
*iface
)
442 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
443 return IPin_Release(&filter
->source
.pin
.IPin_iface
);
446 static HRESULT WINAPI
sink_quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
448 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
450 TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
451 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
456 static HRESULT WINAPI
sink_quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
458 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
460 TRACE("filter %p, sink %p.\n", filter
, sink
);
462 filter
->qc_sink
= sink
;
467 static const IQualityControlVtbl sink_quality_control_vtbl
=
469 sink_quality_control_QueryInterface
,
470 sink_quality_control_AddRef
,
471 sink_quality_control_Release
,
472 sink_quality_control_Notify
,
473 sink_quality_control_SetSink
,
476 static struct transform
*impl_from_source_IQualityControl(IQualityControl
*iface
)
478 return CONTAINING_RECORD(iface
, struct transform
, source_IQualityControl_iface
);
481 static HRESULT WINAPI
source_quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
483 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
484 return IPin_QueryInterface(&filter
->source
.pin
.IPin_iface
, iid
, out
);
487 static ULONG WINAPI
source_quality_control_AddRef(IQualityControl
*iface
)
489 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
490 return IPin_AddRef(&filter
->source
.pin
.IPin_iface
);
493 static ULONG WINAPI
source_quality_control_Release(IQualityControl
*iface
)
495 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
496 return IPin_Release(&filter
->source
.pin
.IPin_iface
);
499 static HRESULT WINAPI
source_quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
501 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
503 return filter
->ops
->source_qc_notify(filter
, sender
, q
);
506 static HRESULT WINAPI
source_quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
508 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
510 TRACE("filter %p, sink %p.\n", filter
, sink
);
515 static const IQualityControlVtbl source_quality_control_vtbl
=
517 source_quality_control_QueryInterface
,
518 source_quality_control_AddRef
,
519 source_quality_control_Release
,
520 source_quality_control_Notify
,
521 source_quality_control_SetSink
,
524 static HRESULT
transform_create(IUnknown
*outer
, const CLSID
*clsid
, const struct transform_ops
*ops
, struct transform
**out
)
526 struct transform
*object
;
528 object
= calloc(1, sizeof(*object
));
530 return E_OUTOFMEMORY
;
532 strmbase_filter_init(&object
->filter
, outer
, clsid
, &filter_ops
);
533 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
534 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
536 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->source
.pin
.IPin_iface
);
537 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, FALSE
,
538 &object
->sink
.pin
.IPin_iface
);
540 object
->sink_IQualityControl_iface
.lpVtbl
= &sink_quality_control_vtbl
;
541 object
->source_IQualityControl_iface
.lpVtbl
= &source_quality_control_vtbl
;
549 static HRESULT
passthrough_source_qc_notify(struct transform
*filter
, IBaseFilter
*sender
, Quality q
)
551 IQualityControl
*peer
;
552 HRESULT hr
= VFW_E_NOT_FOUND
;
554 TRACE("filter %p, sender %p, type %s, proportion %ld, late %s, timestamp %s.\n",
555 filter
, sender
, q
.Type
== Famine
? "Famine" : "Flood", q
.Proportion
,
556 debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
559 return IQualityControl_Notify(filter
->qc_sink
, &filter
->filter
.IBaseFilter_iface
, q
);
561 if (filter
->sink
.pin
.peer
562 && SUCCEEDED(IPin_QueryInterface(filter
->sink
.pin
.peer
, &IID_IQualityControl
, (void **)&peer
)))
564 hr
= IQualityControl_Notify(peer
, &filter
->filter
.IBaseFilter_iface
, q
);
565 IQualityControl_Release(peer
);
571 static HRESULT
handle_source_qc_notify(struct transform
*filter
, IBaseFilter
*sender
, Quality q
)
576 TRACE("filter %p, sender %p, type %s, proportion %ld, late %s, timestamp %s.\n",
577 filter
, sender
, q
.Type
== Famine
? "Famine" : "Flood", q
.Proportion
,
578 debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
580 /* DirectShow filters sometimes pass negative timestamps (Audiosurf uses the
581 * current time instead of the time of the last buffer). GstClockTime is
582 * unsigned, so clamp it to 0. */
583 timestamp
= max(q
.TimeStamp
, 0);
585 /* The documentation specifies that timestamp + diff must be nonnegative. */
587 if (diff
< 0 && timestamp
< (uint64_t)-diff
)
590 /* DirectShow "Proportion" describes what percentage of buffers the upstream
591 * filter should keep (i.e. dropping the rest). If frames are late, the
592 * proportion will be less than 1. For example, a proportion of 500 means
593 * that the element should drop half of its frames, essentially because
594 * frames are taking twice as long as they should to arrive.
596 * GStreamer "proportion" is the inverse of this; it describes how much
597 * faster the upstream element should produce frames. I.e. if frames are
598 * taking twice as long as they should to arrive, we want the frames to be
599 * decoded twice as fast, and so we pass 2.0 to GStreamer. */
603 WARN("Ignoring quality message with zero proportion.\n");
607 /* GST_QOS_TYPE_OVERFLOW is also used for buffers that arrive on time, but
608 * DirectShow filters might use Famine, so check that there actually is an
610 wg_transform_notify_qos(filter
->transform
, q
.Type
== Famine
&& q
.Proportion
< 1000,
611 1000.0 / q
.Proportion
, diff
, timestamp
);
616 static HRESULT
mpeg_audio_codec_sink_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
618 const MPEG1WAVEFORMAT
*format
;
620 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
))
623 if (!IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Packet
)
624 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Payload
)
625 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1AudioPayload
)
626 && !IsEqualGUID(&mt
->subtype
, &GUID_NULL
))
629 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
630 || mt
->cbFormat
< sizeof(MPEG1WAVEFORMAT
))
633 format
= (const MPEG1WAVEFORMAT
*)mt
->pbFormat
;
635 if (format
->wfx
.wFormatTag
!= WAVE_FORMAT_MPEG
636 || format
->fwHeadLayer
== ACM_MPEG_LAYER3
)
642 static HRESULT
mpeg_audio_codec_source_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
644 const MPEG1WAVEFORMAT
*input_format
;
645 const WAVEFORMATEX
*output_format
;
646 DWORD expected_avg_bytes_per_sec
;
647 WORD expected_block_align
;
649 if (!filter
->sink
.pin
.peer
)
652 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
653 || !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_PCM
)
654 || !IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
655 || mt
->cbFormat
< sizeof(WAVEFORMATEX
))
658 input_format
= (const MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
659 output_format
= (const WAVEFORMATEX
*)mt
->pbFormat
;
661 if (output_format
->wFormatTag
!= WAVE_FORMAT_PCM
662 || input_format
->wfx
.nSamplesPerSec
!= output_format
->nSamplesPerSec
663 || input_format
->wfx
.nChannels
!= output_format
->nChannels
664 || (output_format
->wBitsPerSample
!= 8 && output_format
->wBitsPerSample
!= 16))
667 expected_block_align
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
668 expected_avg_bytes_per_sec
= expected_block_align
* output_format
->nSamplesPerSec
;
670 if (output_format
->nBlockAlign
!= expected_block_align
671 || output_format
->nAvgBytesPerSec
!= expected_avg_bytes_per_sec
)
677 static HRESULT
mpeg_audio_codec_source_get_media_type(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
679 const MPEG1WAVEFORMAT
*input_format
;
680 WAVEFORMATEX
*output_format
;
682 if (!filter
->sink
.pin
.peer
)
683 return VFW_S_NO_MORE_ITEMS
;
686 return VFW_S_NO_MORE_ITEMS
;
688 input_format
= (const MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
690 output_format
= CoTaskMemAlloc(sizeof(*output_format
));
692 return E_OUTOFMEMORY
;
694 memset(output_format
, 0, sizeof(*output_format
));
695 output_format
->wFormatTag
= WAVE_FORMAT_PCM
;
696 output_format
->nSamplesPerSec
= input_format
->wfx
.nSamplesPerSec
;
697 output_format
->nChannels
= input_format
->wfx
.nChannels
;
698 output_format
->wBitsPerSample
= index
? 8 : 16;
699 output_format
->nBlockAlign
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
700 output_format
->nAvgBytesPerSec
= output_format
->nBlockAlign
* output_format
->nSamplesPerSec
;
702 memset(mt
, 0, sizeof(*mt
));
703 mt
->majortype
= MEDIATYPE_Audio
;
704 mt
->subtype
= MEDIASUBTYPE_PCM
;
705 mt
->bFixedSizeSamples
= TRUE
;
706 mt
->lSampleSize
= output_format
->nBlockAlign
;
707 mt
->formattype
= FORMAT_WaveFormatEx
;
708 mt
->cbFormat
= sizeof(*output_format
);
709 mt
->pbFormat
= (BYTE
*)output_format
;
714 static HRESULT
mpeg_audio_codec_source_decide_buffer_size(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
716 MPEG1WAVEFORMAT
*input_format
= (MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
717 WAVEFORMATEX
*output_format
= (WAVEFORMATEX
*)filter
->source
.pin
.mt
.pbFormat
;
718 LONG frame_samples
= (input_format
->fwHeadLayer
& ACM_MPEG_LAYER2
) ? 1152 : 384;
719 LONG frame_size
= frame_samples
* output_format
->nBlockAlign
;
720 ALLOCATOR_PROPERTIES ret_props
;
722 props
->cBuffers
= max(props
->cBuffers
, 8);
723 props
->cbBuffer
= max(props
->cbBuffer
, frame_size
* 4);
724 props
->cbAlign
= max(props
->cbAlign
, 1);
726 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
729 static const struct transform_ops mpeg_audio_codec_transform_ops
=
731 mpeg_audio_codec_sink_query_accept
,
732 mpeg_audio_codec_source_query_accept
,
733 mpeg_audio_codec_source_get_media_type
,
734 mpeg_audio_codec_source_decide_buffer_size
,
735 passthrough_source_qc_notify
,
738 HRESULT
mpeg_audio_codec_create(IUnknown
*outer
, IUnknown
**out
)
740 static const WAVEFORMATEX output_format
=
742 .wFormatTag
= WAVE_FORMAT_PCM
, .wBitsPerSample
= 16, .nSamplesPerSec
= 44100, .nChannels
= 1,
744 static const MPEG1WAVEFORMAT input_format
=
746 .wfx
= {.wFormatTag
= WAVE_FORMAT_MPEG
, .nSamplesPerSec
= 44100, .nChannels
= 1,
747 .cbSize
= sizeof(input_format
) - sizeof(WAVEFORMATEX
)},
750 struct transform
*object
;
753 if (FAILED(hr
= check_audio_transform_support(&input_format
.wfx
, &output_format
)))
755 ERR_(winediag
)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n");
759 hr
= transform_create(outer
, &CLSID_CMpegAudioCodec
, &mpeg_audio_codec_transform_ops
, &object
);
763 wcscpy(object
->sink
.pin
.name
, L
"XForm In");
764 wcscpy(object
->source
.pin
.name
, L
"XForm Out");
766 object
->IMpegAudioDecoder_iface
.lpVtbl
= &mpeg_audio_decoder_vtbl
;
768 TRACE("Created MPEG audio decoder %p.\n", object
);
769 *out
= &object
->filter
.IUnknown_inner
;
773 static HRESULT
mpeg_video_codec_sink_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
775 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
)
776 || !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Payload
)
777 || !IsEqualGUID(&mt
->formattype
, &FORMAT_MPEGVideo
)
778 || mt
->cbFormat
< sizeof(MPEG1VIDEOINFO
))
784 static HRESULT
mpeg_video_codec_source_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
786 if (!filter
->sink
.pin
.peer
)
789 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
)
790 || !IsEqualGUID(&mt
->formattype
, &FORMAT_VideoInfo
)
791 || mt
->cbFormat
< sizeof(VIDEOINFOHEADER
))
794 if (!IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_YV12
)
795 /* missing: MEDIASUBTYPE_Y41P, not supported by GStreamer */
796 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_YUY2
)
797 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_UYVY
)
798 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_RGB24
)
799 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_RGB32
)
800 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_RGB565
)
801 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_RGB555
)
802 /* missing: MEDIASUBTYPE_RGB8, not supported by GStreamer */)
808 static HRESULT
mpeg_video_codec_source_get_media_type(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
810 static const enum wg_video_format formats
[] = {
811 WG_VIDEO_FORMAT_YV12
,
812 WG_VIDEO_FORMAT_YUY2
,
813 WG_VIDEO_FORMAT_UYVY
,
815 WG_VIDEO_FORMAT_BGRx
,
816 WG_VIDEO_FORMAT_RGB16
,
817 WG_VIDEO_FORMAT_RGB15
,
820 const MPEG1VIDEOINFO
*input_format
= (MPEG1VIDEOINFO
*)filter
->sink
.pin
.mt
.pbFormat
;
821 struct wg_format wg_format
= {};
822 VIDEOINFO
*video_format
;
824 if (!filter
->sink
.pin
.peer
)
825 return VFW_S_NO_MORE_ITEMS
;
827 if (index
>= ARRAY_SIZE(formats
))
828 return VFW_S_NO_MORE_ITEMS
;
830 input_format
= (MPEG1VIDEOINFO
*)filter
->sink
.pin
.mt
.pbFormat
;
831 wg_format
.major_type
= WG_MAJOR_TYPE_VIDEO
;
832 wg_format
.u
.video
.format
= formats
[index
];
833 wg_format
.u
.video
.width
= input_format
->hdr
.bmiHeader
.biWidth
;
834 wg_format
.u
.video
.height
= input_format
->hdr
.bmiHeader
.biHeight
;
835 wg_format
.u
.video
.fps_n
= 10000000;
836 wg_format
.u
.video
.fps_d
= input_format
->hdr
.AvgTimePerFrame
;
837 if (!amt_from_wg_format(mt
, &wg_format
, false))
838 return E_OUTOFMEMORY
;
840 video_format
= (VIDEOINFO
*)mt
->pbFormat
;
841 video_format
->bmiHeader
.biHeight
= abs(video_format
->bmiHeader
.biHeight
);
842 SetRect(&video_format
->rcSource
, 0, 0, video_format
->bmiHeader
.biWidth
, video_format
->bmiHeader
.biHeight
);
844 video_format
->bmiHeader
.biXPelsPerMeter
= 2000;
845 video_format
->bmiHeader
.biYPelsPerMeter
= 2000;
846 video_format
->dwBitRate
= MulDiv(video_format
->bmiHeader
.biSizeImage
* 8, 10000000, video_format
->AvgTimePerFrame
);
847 mt
->lSampleSize
= video_format
->bmiHeader
.biSizeImage
;
848 mt
->bTemporalCompression
= FALSE
;
849 mt
->bFixedSizeSamples
= TRUE
;
854 static HRESULT
mpeg_video_codec_source_decide_buffer_size(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
856 VIDEOINFOHEADER
*output_format
= (VIDEOINFOHEADER
*)filter
->source
.pin
.mt
.pbFormat
;
857 ALLOCATOR_PROPERTIES ret_props
;
859 props
->cBuffers
= max(props
->cBuffers
, 1);
860 props
->cbBuffer
= max(props
->cbBuffer
, output_format
->bmiHeader
.biSizeImage
);
861 props
->cbAlign
= max(props
->cbAlign
, 1);
863 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
866 static const struct transform_ops mpeg_video_codec_transform_ops
=
868 mpeg_video_codec_sink_query_accept
,
869 mpeg_video_codec_source_query_accept
,
870 mpeg_video_codec_source_get_media_type
,
871 mpeg_video_codec_source_decide_buffer_size
,
872 handle_source_qc_notify
,
875 HRESULT
mpeg_video_codec_create(IUnknown
*outer
, IUnknown
**out
)
877 const MFVIDEOFORMAT output_format
=
879 .dwSize
= sizeof(MFVIDEOFORMAT
),
880 .videoInfo
= {.dwWidth
= 1920, .dwHeight
= 1080},
881 .guidFormat
= MEDIASUBTYPE_NV12
,
883 const MFVIDEOFORMAT input_format
=
885 .dwSize
= sizeof(MFVIDEOFORMAT
),
886 .videoInfo
= {.dwWidth
= 1920, .dwHeight
= 1080},
887 .guidFormat
= MEDIASUBTYPE_MPEG1Payload
,
889 struct transform
*object
;
892 if (FAILED(hr
= check_video_transform_support(&input_format
, &output_format
)))
894 ERR_(winediag
)("GStreamer doesn't support MPEG-1 video decoding, please install appropriate plugins.\n");
898 hr
= transform_create(outer
, &CLSID_CMpegVideoCodec
, &mpeg_video_codec_transform_ops
, &object
);
902 wcscpy(object
->sink
.pin
.name
, L
"Input");
903 wcscpy(object
->source
.pin
.name
, L
"Output");
905 TRACE("Created MPEG video decoder %p.\n", object
);
906 *out
= &object
->filter
.IUnknown_inner
;
910 static HRESULT
mpeg_layer3_decoder_sink_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
912 const MPEGLAYER3WAVEFORMAT
*format
;
914 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
915 || !IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
916 || mt
->cbFormat
< sizeof(MPEGLAYER3WAVEFORMAT
))
919 format
= (const MPEGLAYER3WAVEFORMAT
*)mt
->pbFormat
;
921 if (format
->wfx
.wFormatTag
!= WAVE_FORMAT_MPEGLAYER3
)
927 static HRESULT
mpeg_layer3_decoder_source_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
929 if (!filter
->sink
.pin
.peer
)
932 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
933 || !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_PCM
))
939 static HRESULT
mpeg_layer3_decoder_source_get_media_type(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
941 const MPEGLAYER3WAVEFORMAT
*input_format
;
942 WAVEFORMATEX
*output_format
;
944 if (!filter
->sink
.pin
.peer
)
945 return VFW_S_NO_MORE_ITEMS
;
948 return VFW_S_NO_MORE_ITEMS
;
950 input_format
= (const MPEGLAYER3WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
952 output_format
= CoTaskMemAlloc(sizeof(*output_format
));
954 return E_OUTOFMEMORY
;
956 memset(output_format
, 0, sizeof(*output_format
));
957 output_format
->wFormatTag
= WAVE_FORMAT_PCM
;
958 output_format
->nSamplesPerSec
= input_format
->wfx
.nSamplesPerSec
;
959 output_format
->nChannels
= input_format
->wfx
.nChannels
;
960 output_format
->wBitsPerSample
= 16;
961 output_format
->nBlockAlign
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
962 output_format
->nAvgBytesPerSec
= output_format
->nBlockAlign
* output_format
->nSamplesPerSec
;
964 memset(mt
, 0, sizeof(*mt
));
965 mt
->majortype
= MEDIATYPE_Audio
;
966 mt
->subtype
= MEDIASUBTYPE_PCM
;
967 mt
->bFixedSizeSamples
= TRUE
;
968 mt
->lSampleSize
= 1152 * output_format
->nBlockAlign
;
969 mt
->formattype
= FORMAT_WaveFormatEx
;
970 mt
->cbFormat
= sizeof(*output_format
);
971 mt
->pbFormat
= (BYTE
*)output_format
;
976 static HRESULT
mpeg_layer3_decoder_source_decide_buffer_size(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
978 ALLOCATOR_PROPERTIES ret_props
;
980 props
->cBuffers
= max(props
->cBuffers
, 8);
981 props
->cbBuffer
= max(props
->cbBuffer
, filter
->source
.pin
.mt
.lSampleSize
* 4);
982 props
->cbAlign
= max(props
->cbAlign
, 1);
984 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
987 static const struct transform_ops mpeg_layer3_decoder_transform_ops
=
989 mpeg_layer3_decoder_sink_query_accept
,
990 mpeg_layer3_decoder_source_query_accept
,
991 mpeg_layer3_decoder_source_get_media_type
,
992 mpeg_layer3_decoder_source_decide_buffer_size
,
993 passthrough_source_qc_notify
,
996 HRESULT
mpeg_layer3_decoder_create(IUnknown
*outer
, IUnknown
**out
)
998 static const WAVEFORMATEX output_format
=
1000 .wFormatTag
= WAVE_FORMAT_PCM
, .wBitsPerSample
= 16, .nSamplesPerSec
= 44100, .nChannels
= 1,
1002 static const MPEGLAYER3WAVEFORMAT input_format
=
1004 .wfx
= {.wFormatTag
= WAVE_FORMAT_MPEGLAYER3
, .nSamplesPerSec
= 44100, .nChannels
= 1,
1005 .cbSize
= sizeof(input_format
) - sizeof(WAVEFORMATEX
)},
1007 struct transform
*object
;
1010 if (FAILED(hr
= check_audio_transform_support(&input_format
.wfx
, &output_format
)))
1012 ERR_(winediag
)("GStreamer doesn't support MP3 audio decoding, please install appropriate plugins.\n");
1016 hr
= transform_create(outer
, &CLSID_mpeg_layer3_decoder
, &mpeg_layer3_decoder_transform_ops
, &object
);
1020 wcscpy(object
->sink
.pin
.name
, L
"XForm In");
1021 wcscpy(object
->source
.pin
.name
, L
"XForm Out");
1023 TRACE("Created MPEG layer-3 decoder %p.\n", object
);
1024 *out
= &object
->filter
.IUnknown_inner
;