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
);
57 static inline struct transform
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
59 return CONTAINING_RECORD(iface
, struct transform
, filter
);
62 static struct strmbase_pin
*transform_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
64 struct transform
*filter
= impl_from_strmbase_filter(iface
);
66 return &filter
->sink
.pin
;
68 return &filter
->source
.pin
;
72 static void transform_destroy(struct strmbase_filter
*iface
)
74 struct transform
*filter
= impl_from_strmbase_filter(iface
);
76 strmbase_passthrough_cleanup(&filter
->passthrough
);
77 strmbase_source_cleanup(&filter
->source
);
78 strmbase_sink_cleanup(&filter
->sink
);
79 strmbase_filter_cleanup(&filter
->filter
);
84 static HRESULT
transform_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
86 struct transform
*filter
= impl_from_strmbase_filter(iface
);
88 if (IsEqualGUID(iid
, &IID_IMpegAudioDecoder
) && filter
->IMpegAudioDecoder_iface
.lpVtbl
)
89 *out
= &filter
->IMpegAudioDecoder_iface
;
93 IUnknown_AddRef((IUnknown
*)*out
);
97 static HRESULT
transform_init_stream(struct strmbase_filter
*iface
)
99 struct transform
*filter
= impl_from_strmbase_filter(iface
);
100 struct wg_format input_format
, output_format
;
101 struct wg_transform_attrs attrs
= {0};
104 if (filter
->source
.pin
.peer
)
106 if (!amt_to_wg_format(&filter
->sink
.pin
.mt
, &input_format
))
109 if (!amt_to_wg_format(&filter
->source
.pin
.mt
, &output_format
))
112 if (FAILED(hr
= wg_sample_queue_create(&filter
->sample_queue
)))
115 filter
->transform
= wg_transform_create(&input_format
, &output_format
, &attrs
);
116 if (!filter
->transform
)
118 wg_sample_queue_destroy(filter
->sample_queue
);
122 hr
= IMemAllocator_Commit(filter
->source
.pAllocator
);
124 ERR("Failed to commit allocator, hr %#lx.\n", hr
);
130 static HRESULT
transform_cleanup_stream(struct strmbase_filter
*iface
)
132 struct transform
*filter
= impl_from_strmbase_filter(iface
);
134 if (filter
->source
.pin
.peer
)
136 IMemAllocator_Decommit(filter
->source
.pAllocator
);
138 EnterCriticalSection(&filter
->filter
.stream_cs
);
139 wg_transform_destroy(filter
->transform
);
140 wg_sample_queue_destroy(filter
->sample_queue
);
141 LeaveCriticalSection(&filter
->filter
.stream_cs
);
147 static const struct strmbase_filter_ops filter_ops
=
149 .filter_get_pin
= transform_get_pin
,
150 .filter_destroy
= transform_destroy
,
151 .filter_query_interface
= transform_query_interface
,
152 .filter_init_stream
= transform_init_stream
,
153 .filter_cleanup_stream
= transform_cleanup_stream
,
156 static struct transform
*impl_from_IMpegAudioDecoder(IMpegAudioDecoder
*iface
)
158 return CONTAINING_RECORD(iface
, struct transform
, IMpegAudioDecoder_iface
);
161 static HRESULT WINAPI
mpeg_audio_decoder_QueryInterface(IMpegAudioDecoder
*iface
,
162 REFIID iid
, void **out
)
164 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
165 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
168 static ULONG WINAPI
mpeg_audio_decoder_AddRef(IMpegAudioDecoder
*iface
)
170 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
171 return IUnknown_AddRef(filter
->filter
.outer_unk
);
174 static ULONG WINAPI
mpeg_audio_decoder_Release(IMpegAudioDecoder
*iface
)
176 struct transform
*filter
= impl_from_IMpegAudioDecoder(iface
);
177 return IUnknown_Release(filter
->filter
.outer_unk
);
180 static HRESULT WINAPI
mpeg_audio_decoder_get_FrequencyDivider(IMpegAudioDecoder
*iface
, ULONG
*divider
)
182 FIXME("iface %p, divider %p, stub!\n", iface
, divider
);
186 static HRESULT WINAPI
mpeg_audio_decoder_put_FrequencyDivider(IMpegAudioDecoder
*iface
, ULONG divider
)
188 FIXME("iface %p, divider %lu, stub!\n", iface
, divider
);
192 static HRESULT WINAPI
mpeg_audio_decoder_get_DecoderAccuracy(IMpegAudioDecoder
*iface
, ULONG
*accuracy
)
194 FIXME("iface %p, accuracy %p, stub!\n", iface
, accuracy
);
198 static HRESULT WINAPI
mpeg_audio_decoder_put_DecoderAccuracy(IMpegAudioDecoder
*iface
, ULONG accuracy
)
200 FIXME("iface %p, accuracy %lu, stub!\n", iface
, accuracy
);
204 static HRESULT WINAPI
mpeg_audio_decoder_get_Stereo(IMpegAudioDecoder
*iface
, ULONG
*stereo
)
206 FIXME("iface %p, stereo %p, stub!\n", iface
, stereo
);
210 static HRESULT WINAPI
mpeg_audio_decoder_put_Stereo(IMpegAudioDecoder
*iface
, ULONG stereo
)
212 FIXME("iface %p, stereo %lu, stub!\n", iface
, stereo
);
216 static HRESULT WINAPI
mpeg_audio_decoder_get_DecoderWordSize(IMpegAudioDecoder
*iface
, ULONG
*word_size
)
218 FIXME("iface %p, word_size %p, stub!\n", iface
, word_size
);
222 static HRESULT WINAPI
mpeg_audio_decoder_put_DecoderWordSize(IMpegAudioDecoder
*iface
, ULONG word_size
)
224 FIXME("iface %p, word_size %lu, stub!\n", iface
, word_size
);
228 static HRESULT WINAPI
mpeg_audio_decoder_get_IntegerDecode(IMpegAudioDecoder
*iface
, ULONG
*integer_decode
)
230 FIXME("iface %p, integer_decode %p, stub!\n", iface
, integer_decode
);
234 static HRESULT WINAPI
mpeg_audio_decoder_put_IntegerDecode(IMpegAudioDecoder
*iface
, ULONG integer_decode
)
236 FIXME("iface %p, integer_decode %lu, stub!\n", iface
, integer_decode
);
240 static HRESULT WINAPI
mpeg_audio_decoder_get_DualMode(IMpegAudioDecoder
*iface
, ULONG
*dual_mode
)
242 FIXME("iface %p, dual_mode %p, stub!\n", iface
, dual_mode
);
246 static HRESULT WINAPI
mpeg_audio_decoder_put_DualMode(IMpegAudioDecoder
*iface
, ULONG dual_mode
)
248 FIXME("iface %p, dual_mode %lu, stub!\n", iface
, dual_mode
);
252 static HRESULT WINAPI
mpeg_audio_decoder_get_AudioFormat(IMpegAudioDecoder
*iface
, MPEG1WAVEFORMAT
*format
)
254 FIXME("iface %p, format %p, stub!\n", iface
, format
);
258 static const IMpegAudioDecoderVtbl mpeg_audio_decoder_vtbl
=
260 mpeg_audio_decoder_QueryInterface
,
261 mpeg_audio_decoder_AddRef
,
262 mpeg_audio_decoder_Release
,
263 mpeg_audio_decoder_get_FrequencyDivider
,
264 mpeg_audio_decoder_put_FrequencyDivider
,
265 mpeg_audio_decoder_get_DecoderAccuracy
,
266 mpeg_audio_decoder_put_DecoderAccuracy
,
267 mpeg_audio_decoder_get_Stereo
,
268 mpeg_audio_decoder_put_Stereo
,
269 mpeg_audio_decoder_get_DecoderWordSize
,
270 mpeg_audio_decoder_put_DecoderWordSize
,
271 mpeg_audio_decoder_get_IntegerDecode
,
272 mpeg_audio_decoder_put_IntegerDecode
,
273 mpeg_audio_decoder_get_DualMode
,
274 mpeg_audio_decoder_put_DualMode
,
275 mpeg_audio_decoder_get_AudioFormat
,
278 static HRESULT
transform_sink_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
280 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
282 return filter
->ops
->sink_query_accept(filter
, mt
);
285 static HRESULT
transform_sink_query_interface(struct strmbase_pin
*pin
, REFIID iid
, void **out
)
287 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
289 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
290 *out
= &filter
->sink
.IMemInputPin_iface
;
291 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
292 *out
= &filter
->sink_IQualityControl_iface
;
294 return E_NOINTERFACE
;
296 IUnknown_AddRef((IUnknown
*)*out
);
300 static HRESULT WINAPI
transform_sink_receive(struct strmbase_sink
*pin
, IMediaSample
*sample
)
302 struct transform
*filter
= impl_from_strmbase_filter(pin
->pin
.filter
);
303 struct wg_sample
*wg_sample
;
306 /* We do not expect pin connection state to change while the filter is
307 * running. This guarantee is necessary, since otherwise we would have to
308 * take the filter lock, and we can't take the filter lock from a streaming
310 if (!filter
->source
.pMemInputPin
)
312 WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
313 return VFW_E_NOT_CONNECTED
;
316 if (filter
->filter
.state
== State_Stopped
)
317 return VFW_E_WRONG_STATE
;
319 if (filter
->sink
.flushing
)
322 hr
= wg_sample_create_quartz(sample
, &wg_sample
);
326 hr
= wg_transform_push_quartz(filter
->transform
, wg_sample
, filter
->sample_queue
);
332 IMediaSample
*output_sample
;
334 hr
= IMemAllocator_GetBuffer(filter
->source
.pAllocator
, &output_sample
, NULL
, NULL
, 0);
338 hr
= wg_sample_create_quartz(output_sample
, &wg_sample
);
341 IMediaSample_Release(output_sample
);
345 hr
= wg_transform_read_quartz(filter
->transform
, wg_sample
);
346 wg_sample_release(wg_sample
);
348 if (hr
== MF_E_TRANSFORM_NEED_MORE_INPUT
)
350 IMediaSample_Release(output_sample
);
355 IMediaSample_Release(output_sample
);
359 wg_sample_queue_flush(filter
->sample_queue
, false);
361 hr
= IMemInputPin_Receive(filter
->source
.pMemInputPin
, output_sample
);
364 IMediaSample_Release(output_sample
);
368 IMediaSample_Release(output_sample
);
374 static const struct strmbase_sink_ops sink_ops
=
376 .base
.pin_query_accept
= transform_sink_query_accept
,
377 .base
.pin_query_interface
= transform_sink_query_interface
,
378 .pfnReceive
= transform_sink_receive
,
381 static HRESULT
transform_source_query_accept(struct strmbase_pin
*pin
, const AM_MEDIA_TYPE
*mt
)
383 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
385 return filter
->ops
->source_query_accept(filter
, mt
);
388 static HRESULT
transform_source_get_media_type(struct strmbase_pin
*pin
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
390 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
392 return filter
->ops
->source_get_media_type(filter
, index
, mt
);
395 static HRESULT
transform_source_query_interface(struct strmbase_pin
*pin
, REFIID iid
, void **out
)
397 struct transform
*filter
= impl_from_strmbase_filter(pin
->filter
);
399 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
400 *out
= &filter
->passthrough
.IMediaPosition_iface
;
401 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
402 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
403 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
404 *out
= &filter
->source_IQualityControl_iface
;
406 return E_NOINTERFACE
;
408 IUnknown_AddRef((IUnknown
*)*out
);
412 static HRESULT WINAPI
transform_source_DecideBufferSize(struct strmbase_source
*pin
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
414 struct transform
*filter
= impl_from_strmbase_filter(pin
->pin
.filter
);
416 return filter
->ops
->source_decide_buffer_size(filter
, allocator
, props
);
419 static const struct strmbase_source_ops source_ops
=
421 .base
.pin_query_accept
= transform_source_query_accept
,
422 .base
.pin_get_media_type
= transform_source_get_media_type
,
423 .base
.pin_query_interface
= transform_source_query_interface
,
424 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
425 .pfnDecideAllocator
= BaseOutputPinImpl_DecideAllocator
,
426 .pfnDecideBufferSize
= transform_source_DecideBufferSize
,
429 static struct transform
*impl_from_sink_IQualityControl(IQualityControl
*iface
)
431 return CONTAINING_RECORD(iface
, struct transform
, sink_IQualityControl_iface
);
434 static HRESULT WINAPI
sink_quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
436 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
437 return IPin_QueryInterface(&filter
->source
.pin
.IPin_iface
, iid
, out
);
440 static ULONG WINAPI
sink_quality_control_AddRef(IQualityControl
*iface
)
442 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
443 return IPin_AddRef(&filter
->source
.pin
.IPin_iface
);
446 static ULONG WINAPI
sink_quality_control_Release(IQualityControl
*iface
)
448 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
449 return IPin_Release(&filter
->source
.pin
.IPin_iface
);
452 static HRESULT WINAPI
sink_quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
454 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
456 TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
457 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
462 static HRESULT WINAPI
sink_quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
464 struct transform
*filter
= impl_from_sink_IQualityControl(iface
);
466 TRACE("filter %p, sink %p.\n", filter
, sink
);
468 filter
->qc_sink
= sink
;
473 static const IQualityControlVtbl sink_quality_control_vtbl
=
475 sink_quality_control_QueryInterface
,
476 sink_quality_control_AddRef
,
477 sink_quality_control_Release
,
478 sink_quality_control_Notify
,
479 sink_quality_control_SetSink
,
482 static struct transform
*impl_from_source_IQualityControl(IQualityControl
*iface
)
484 return CONTAINING_RECORD(iface
, struct transform
, source_IQualityControl_iface
);
487 static HRESULT WINAPI
source_quality_control_QueryInterface(IQualityControl
*iface
, REFIID iid
, void **out
)
489 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
490 return IPin_QueryInterface(&filter
->source
.pin
.IPin_iface
, iid
, out
);
493 static ULONG WINAPI
source_quality_control_AddRef(IQualityControl
*iface
)
495 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
496 return IPin_AddRef(&filter
->source
.pin
.IPin_iface
);
499 static ULONG WINAPI
source_quality_control_Release(IQualityControl
*iface
)
501 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
502 return IPin_Release(&filter
->source
.pin
.IPin_iface
);
505 static HRESULT WINAPI
source_quality_control_Notify(IQualityControl
*iface
, IBaseFilter
*sender
, Quality q
)
507 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
508 IQualityControl
*peer
;
509 HRESULT hr
= VFW_E_NOT_FOUND
;
511 TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
512 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
515 return IQualityControl_Notify(filter
->qc_sink
, &filter
->filter
.IBaseFilter_iface
, q
);
517 if (filter
->sink
.pin
.peer
518 && SUCCEEDED(IPin_QueryInterface(filter
->sink
.pin
.peer
, &IID_IQualityControl
, (void **)&peer
)))
520 hr
= IQualityControl_Notify(peer
, &filter
->filter
.IBaseFilter_iface
, q
);
521 IQualityControl_Release(peer
);
527 static HRESULT WINAPI
source_quality_control_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
529 struct transform
*filter
= impl_from_source_IQualityControl(iface
);
531 TRACE("filter %p, sink %p.\n", filter
, sink
);
536 static const IQualityControlVtbl source_quality_control_vtbl
=
538 source_quality_control_QueryInterface
,
539 source_quality_control_AddRef
,
540 source_quality_control_Release
,
541 source_quality_control_Notify
,
542 source_quality_control_SetSink
,
545 static HRESULT
transform_create(IUnknown
*outer
, const CLSID
*clsid
, const struct transform_ops
*ops
, struct transform
**out
)
547 struct transform
*object
;
549 object
= calloc(1, sizeof(*object
));
551 return E_OUTOFMEMORY
;
553 strmbase_filter_init(&object
->filter
, outer
, clsid
, &filter_ops
);
554 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
555 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
557 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->source
.pin
.IPin_iface
);
558 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, FALSE
,
559 &object
->sink
.pin
.IPin_iface
);
561 object
->sink_IQualityControl_iface
.lpVtbl
= &sink_quality_control_vtbl
;
562 object
->source_IQualityControl_iface
.lpVtbl
= &source_quality_control_vtbl
;
570 static HRESULT
mpeg_audio_codec_sink_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
572 const MPEG1WAVEFORMAT
*format
;
574 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
))
577 if (!IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Packet
)
578 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1Payload
)
579 && !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_MPEG1AudioPayload
)
580 && !IsEqualGUID(&mt
->subtype
, &GUID_NULL
))
583 if (!IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
584 || mt
->cbFormat
< sizeof(MPEG1WAVEFORMAT
))
587 format
= (const MPEG1WAVEFORMAT
*)mt
->pbFormat
;
589 if (format
->wfx
.wFormatTag
!= WAVE_FORMAT_MPEG
590 || format
->fwHeadLayer
== ACM_MPEG_LAYER3
)
596 static HRESULT
mpeg_audio_codec_source_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
598 const MPEG1WAVEFORMAT
*input_format
;
599 const WAVEFORMATEX
*output_format
;
600 DWORD expected_avg_bytes_per_sec
;
601 WORD expected_block_align
;
603 if (!filter
->sink
.pin
.peer
)
606 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
607 || !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_PCM
)
608 || !IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
609 || mt
->cbFormat
< sizeof(WAVEFORMATEX
))
612 input_format
= (const MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
613 output_format
= (const WAVEFORMATEX
*)mt
->pbFormat
;
615 if (output_format
->wFormatTag
!= WAVE_FORMAT_PCM
616 || input_format
->wfx
.nSamplesPerSec
!= output_format
->nSamplesPerSec
617 || input_format
->wfx
.nChannels
!= output_format
->nChannels
618 || (output_format
->wBitsPerSample
!= 8 && output_format
->wBitsPerSample
!= 16))
621 expected_block_align
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
622 expected_avg_bytes_per_sec
= expected_block_align
* output_format
->nSamplesPerSec
;
624 if (output_format
->nBlockAlign
!= expected_block_align
625 || output_format
->nAvgBytesPerSec
!= expected_avg_bytes_per_sec
)
631 static HRESULT
mpeg_audio_codec_source_get_media_type(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
633 const MPEG1WAVEFORMAT
*input_format
;
634 WAVEFORMATEX
*output_format
;
636 if (!filter
->sink
.pin
.peer
)
637 return VFW_S_NO_MORE_ITEMS
;
640 return VFW_S_NO_MORE_ITEMS
;
642 input_format
= (const MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
644 output_format
= CoTaskMemAlloc(sizeof(*output_format
));
646 return E_OUTOFMEMORY
;
648 memset(output_format
, 0, sizeof(*output_format
));
649 output_format
->wFormatTag
= WAVE_FORMAT_PCM
;
650 output_format
->nSamplesPerSec
= input_format
->wfx
.nSamplesPerSec
;
651 output_format
->nChannels
= input_format
->wfx
.nChannels
;
652 output_format
->wBitsPerSample
= index
? 8 : 16;
653 output_format
->nBlockAlign
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
654 output_format
->nAvgBytesPerSec
= output_format
->nBlockAlign
* output_format
->nSamplesPerSec
;
656 memset(mt
, 0, sizeof(*mt
));
657 mt
->majortype
= MEDIATYPE_Audio
;
658 mt
->subtype
= MEDIASUBTYPE_PCM
;
659 mt
->bFixedSizeSamples
= TRUE
;
660 mt
->lSampleSize
= output_format
->nBlockAlign
;
661 mt
->formattype
= FORMAT_WaveFormatEx
;
662 mt
->cbFormat
= sizeof(*output_format
);
663 mt
->pbFormat
= (BYTE
*)output_format
;
668 static HRESULT
mpeg_audio_codec_source_decide_buffer_size(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
670 MPEG1WAVEFORMAT
*input_format
= (MPEG1WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
671 WAVEFORMATEX
*output_format
= (WAVEFORMATEX
*)filter
->source
.pin
.mt
.pbFormat
;
672 LONG frame_samples
= (input_format
->fwHeadLayer
& ACM_MPEG_LAYER2
) ? 1152 : 384;
673 LONG frame_size
= frame_samples
* output_format
->nBlockAlign
;
674 ALLOCATOR_PROPERTIES ret_props
;
676 props
->cBuffers
= max(props
->cBuffers
, 8);
677 props
->cbBuffer
= max(props
->cbBuffer
, frame_size
* 4);
678 props
->cbAlign
= max(props
->cbAlign
, 1);
680 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
683 static const struct transform_ops mpeg_audio_codec_transform_ops
=
685 mpeg_audio_codec_sink_query_accept
,
686 mpeg_audio_codec_source_query_accept
,
687 mpeg_audio_codec_source_get_media_type
,
688 mpeg_audio_codec_source_decide_buffer_size
,
691 HRESULT
mpeg_audio_codec_create(IUnknown
*outer
, IUnknown
**out
)
693 static const struct wg_format output_format
=
695 .major_type
= WG_MAJOR_TYPE_AUDIO
,
698 .format
= WG_AUDIO_FORMAT_S16LE
,
704 static const struct wg_format input_format
=
706 .major_type
= WG_MAJOR_TYPE_AUDIO_MPEG1
,
714 struct wg_transform_attrs attrs
= {0};
715 wg_transform_t transform
;
716 struct transform
*object
;
719 transform
= wg_transform_create(&input_format
, &output_format
, &attrs
);
722 ERR_(winediag
)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n");
725 wg_transform_destroy(transform
);
727 hr
= transform_create(outer
, &CLSID_CMpegAudioCodec
, &mpeg_audio_codec_transform_ops
, &object
);
731 wcscpy(object
->sink
.pin
.name
, L
"XForm In");
732 wcscpy(object
->source
.pin
.name
, L
"XForm Out");
734 object
->IMpegAudioDecoder_iface
.lpVtbl
= &mpeg_audio_decoder_vtbl
;
736 TRACE("Created MPEG audio decoder %p.\n", object
);
737 *out
= &object
->filter
.IUnknown_inner
;
741 static HRESULT
mpeg_layer3_decoder_sink_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
743 const MPEGLAYER3WAVEFORMAT
*format
;
745 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
746 || !IsEqualGUID(&mt
->formattype
, &FORMAT_WaveFormatEx
)
747 || mt
->cbFormat
< sizeof(MPEGLAYER3WAVEFORMAT
))
750 format
= (const MPEGLAYER3WAVEFORMAT
*)mt
->pbFormat
;
752 if (format
->wfx
.wFormatTag
!= WAVE_FORMAT_MPEGLAYER3
)
758 static HRESULT
mpeg_layer3_decoder_source_query_accept(struct transform
*filter
, const AM_MEDIA_TYPE
*mt
)
760 if (!filter
->sink
.pin
.peer
)
763 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Audio
)
764 || !IsEqualGUID(&mt
->subtype
, &MEDIASUBTYPE_PCM
))
770 static HRESULT
mpeg_layer3_decoder_source_get_media_type(struct transform
*filter
, unsigned int index
, AM_MEDIA_TYPE
*mt
)
772 const MPEGLAYER3WAVEFORMAT
*input_format
;
773 WAVEFORMATEX
*output_format
;
775 if (!filter
->sink
.pin
.peer
)
776 return VFW_S_NO_MORE_ITEMS
;
779 return VFW_S_NO_MORE_ITEMS
;
781 input_format
= (const MPEGLAYER3WAVEFORMAT
*)filter
->sink
.pin
.mt
.pbFormat
;
783 output_format
= CoTaskMemAlloc(sizeof(*output_format
));
785 return E_OUTOFMEMORY
;
787 memset(output_format
, 0, sizeof(*output_format
));
788 output_format
->wFormatTag
= WAVE_FORMAT_PCM
;
789 output_format
->nSamplesPerSec
= input_format
->wfx
.nSamplesPerSec
;
790 output_format
->nChannels
= input_format
->wfx
.nChannels
;
791 output_format
->wBitsPerSample
= 16;
792 output_format
->nBlockAlign
= output_format
->nChannels
* output_format
->wBitsPerSample
/ 8;
793 output_format
->nAvgBytesPerSec
= output_format
->nBlockAlign
* output_format
->nSamplesPerSec
;
795 memset(mt
, 0, sizeof(*mt
));
796 mt
->majortype
= MEDIATYPE_Audio
;
797 mt
->subtype
= MEDIASUBTYPE_PCM
;
798 mt
->bFixedSizeSamples
= TRUE
;
799 mt
->lSampleSize
= 1152 * output_format
->nBlockAlign
;
800 mt
->formattype
= FORMAT_WaveFormatEx
;
801 mt
->cbFormat
= sizeof(*output_format
);
802 mt
->pbFormat
= (BYTE
*)output_format
;
807 static HRESULT
mpeg_layer3_decoder_source_decide_buffer_size(struct transform
*filter
, IMemAllocator
*allocator
, ALLOCATOR_PROPERTIES
*props
)
809 ALLOCATOR_PROPERTIES ret_props
;
811 props
->cBuffers
= max(props
->cBuffers
, 8);
812 props
->cbBuffer
= max(props
->cbBuffer
, filter
->source
.pin
.mt
.lSampleSize
* 4);
813 props
->cbAlign
= max(props
->cbAlign
, 1);
815 return IMemAllocator_SetProperties(allocator
, props
, &ret_props
);
818 static const struct transform_ops mpeg_layer3_decoder_transform_ops
=
820 mpeg_layer3_decoder_sink_query_accept
,
821 mpeg_layer3_decoder_source_query_accept
,
822 mpeg_layer3_decoder_source_get_media_type
,
823 mpeg_layer3_decoder_source_decide_buffer_size
,
826 HRESULT
mpeg_layer3_decoder_create(IUnknown
*outer
, IUnknown
**out
)
828 static const struct wg_format output_format
=
830 .major_type
= WG_MAJOR_TYPE_AUDIO
,
833 .format
= WG_AUDIO_FORMAT_S16LE
,
839 static const struct wg_format input_format
=
841 .major_type
= WG_MAJOR_TYPE_AUDIO_MPEG1
,
849 struct wg_transform_attrs attrs
= {0};
850 wg_transform_t transform
;
851 struct transform
*object
;
854 transform
= wg_transform_create(&input_format
, &output_format
, &attrs
);
857 ERR_(winediag
)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n");
860 wg_transform_destroy(transform
);
862 hr
= transform_create(outer
, &CLSID_mpeg_layer3_decoder
, &mpeg_layer3_decoder_transform_ops
, &object
);
866 wcscpy(object
->sink
.pin
.name
, L
"XForm In");
867 wcscpy(object
->source
.pin
.name
, L
"XForm Out");
869 TRACE("Created MPEG layer-3 decoder %p.\n", object
);
870 *out
= &object
->filter
.IUnknown_inner
;