include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winegstreamer / quartz_transform.c
blobff57ea02ed0d8f625828eeb4e9e7651d26602650
1 /*
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"
24 #include "mferror.h"
25 #include "mpegtype.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
28 WINE_DECLARE_DEBUG_CHANNEL(winediag);
30 struct transform
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;
49 struct transform_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);
66 if (index == 0)
67 return &filter->sink.pin;
68 if (index == 1)
69 return &filter->source.pin;
70 return NULL;
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);
82 free(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;
91 else
92 return E_NOINTERFACE;
94 IUnknown_AddRef((IUnknown *)*out);
95 return S_OK;
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};
102 HRESULT hr;
104 if (filter->source.pin.peer)
106 if (FAILED(hr = wg_sample_queue_create(&filter->sample_queue)))
107 return hr;
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);
113 return hr;
116 hr = IMemAllocator_Commit(filter->source.pAllocator);
117 if (FAILED(hr))
118 ERR("Failed to commit allocator, hr %#lx.\n", hr);
121 return S_OK;
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);
138 return S_OK;
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);
177 return E_NOTIMPL;
180 static HRESULT WINAPI mpeg_audio_decoder_put_FrequencyDivider(IMpegAudioDecoder *iface, ULONG divider)
182 FIXME("iface %p, divider %lu, stub!\n", iface, divider);
183 return E_NOTIMPL;
186 static HRESULT WINAPI mpeg_audio_decoder_get_DecoderAccuracy(IMpegAudioDecoder *iface, ULONG *accuracy)
188 FIXME("iface %p, accuracy %p, stub!\n", iface, accuracy);
189 return E_NOTIMPL;
192 static HRESULT WINAPI mpeg_audio_decoder_put_DecoderAccuracy(IMpegAudioDecoder *iface, ULONG accuracy)
194 FIXME("iface %p, accuracy %lu, stub!\n", iface, accuracy);
195 return E_NOTIMPL;
198 static HRESULT WINAPI mpeg_audio_decoder_get_Stereo(IMpegAudioDecoder *iface, ULONG *stereo)
200 FIXME("iface %p, stereo %p, stub!\n", iface, stereo);
201 return E_NOTIMPL;
204 static HRESULT WINAPI mpeg_audio_decoder_put_Stereo(IMpegAudioDecoder *iface, ULONG stereo)
206 FIXME("iface %p, stereo %lu, stub!\n", iface, stereo);
207 return E_NOTIMPL;
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);
213 return E_NOTIMPL;
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);
219 return E_NOTIMPL;
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);
225 return E_NOTIMPL;
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);
231 return E_NOTIMPL;
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);
237 return E_NOTIMPL;
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);
243 return E_NOTIMPL;
246 static HRESULT WINAPI mpeg_audio_decoder_get_AudioFormat(IMpegAudioDecoder *iface, MPEG1WAVEFORMAT *format)
248 FIXME("iface %p, format %p, stub!\n", iface, format);
249 return E_NOTIMPL;
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;
287 else
288 return E_NOINTERFACE;
290 IUnknown_AddRef((IUnknown *)*out);
291 return S_OK;
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;
298 HRESULT hr;
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
303 * thread. */
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)
314 return S_FALSE;
316 hr = wg_sample_create_quartz(sample, &wg_sample);
317 if (FAILED(hr))
318 return hr;
320 hr = wg_transform_push_quartz(filter->transform, wg_sample, filter->sample_queue);
321 if (FAILED(hr))
322 return hr;
324 for (;;)
326 IMediaSample *output_sample;
328 hr = IMemAllocator_GetBuffer(filter->source.pAllocator, &output_sample, NULL, NULL, 0);
329 if (FAILED(hr))
330 return hr;
332 hr = wg_sample_create_quartz(output_sample, &wg_sample);
333 if (FAILED(hr))
335 IMediaSample_Release(output_sample);
336 return hr;
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);
345 break;
347 if (FAILED(hr))
349 IMediaSample_Release(output_sample);
350 return hr;
353 wg_sample_queue_flush(filter->sample_queue, false);
355 hr = IMemInputPin_Receive(filter->source.pMemInputPin, output_sample);
356 if (FAILED(hr))
358 IMediaSample_Release(output_sample);
359 return hr;
362 IMediaSample_Release(output_sample);
365 return S_OK;
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;
399 else
400 return E_NOINTERFACE;
402 IUnknown_AddRef((IUnknown *)*out);
403 return S_OK;
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));
453 return S_OK;
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;
464 return S_OK;
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);
512 return S_OK;
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));
529 if (!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;
543 object->ops = ops;
545 *out = object;
546 return S_OK;
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));
558 if (filter->qc_sink)
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);
568 return hr;
571 static HRESULT handle_source_qc_notify(struct transform *filter, IBaseFilter *sender, Quality q)
573 uint64_t timestamp;
574 int64_t diff;
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. */
586 diff = q.Late;
587 if (diff < 0 && timestamp < (uint64_t)-diff)
588 diff = -timestamp;
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. */
601 if (!q.Proportion)
603 WARN("Ignoring quality message with zero proportion.\n");
604 return S_OK;
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
609 * underrun. */
610 wg_transform_notify_qos(filter->transform, q.Type == Famine && q.Proportion < 1000,
611 1000.0 / q.Proportion, diff, timestamp);
613 return S_OK;
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))
621 return S_FALSE;
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))
627 return S_FALSE;
629 if (!IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)
630 || mt->cbFormat < sizeof(MPEG1WAVEFORMAT))
631 return S_FALSE;
633 format = (const MPEG1WAVEFORMAT *)mt->pbFormat;
635 if (format->wfx.wFormatTag != WAVE_FORMAT_MPEG
636 || format->fwHeadLayer == ACM_MPEG_LAYER3)
637 return S_FALSE;
639 return S_OK;
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)
650 return S_FALSE;
652 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)
653 || !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_PCM)
654 || !IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)
655 || mt->cbFormat < sizeof(WAVEFORMATEX))
656 return S_FALSE;
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))
665 return S_FALSE;
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)
672 return S_FALSE;
674 return S_OK;
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;
685 if (index > 1)
686 return VFW_S_NO_MORE_ITEMS;
688 input_format = (const MPEG1WAVEFORMAT *)filter->sink.pin.mt.pbFormat;
690 output_format = CoTaskMemAlloc(sizeof(*output_format));
691 if (!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;
711 return S_OK;
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)},
748 .fwHeadLayer = 2,
750 struct transform *object;
751 HRESULT hr;
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");
756 return hr;
759 hr = transform_create(outer, &CLSID_CMpegAudioCodec, &mpeg_audio_codec_transform_ops, &object);
760 if (FAILED(hr))
761 return hr;
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;
770 return hr;
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))
779 return S_FALSE;
781 return S_OK;
784 static HRESULT mpeg_video_codec_source_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt)
786 if (!filter->sink.pin.peer)
787 return S_FALSE;
789 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video)
790 || !IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo)
791 || mt->cbFormat < sizeof(VIDEOINFOHEADER))
792 return S_FALSE;
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 */)
803 return S_FALSE;
805 return S_OK;
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,
814 WG_VIDEO_FORMAT_BGR,
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;
851 return S_OK;
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;
890 HRESULT hr;
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");
895 return hr;
898 hr = transform_create(outer, &CLSID_CMpegVideoCodec, &mpeg_video_codec_transform_ops, &object);
899 if (FAILED(hr))
900 return hr;
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;
907 return hr;
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))
917 return S_FALSE;
919 format = (const MPEGLAYER3WAVEFORMAT *)mt->pbFormat;
921 if (format->wfx.wFormatTag != WAVE_FORMAT_MPEGLAYER3)
922 return S_FALSE;
924 return S_OK;
927 static HRESULT mpeg_layer3_decoder_source_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt)
929 if (!filter->sink.pin.peer)
930 return S_FALSE;
932 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)
933 || !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_PCM))
934 return S_FALSE;
936 return S_OK;
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;
947 if (index > 0)
948 return VFW_S_NO_MORE_ITEMS;
950 input_format = (const MPEGLAYER3WAVEFORMAT *)filter->sink.pin.mt.pbFormat;
952 output_format = CoTaskMemAlloc(sizeof(*output_format));
953 if (!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;
973 return S_OK;
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;
1008 HRESULT hr;
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");
1013 return hr;
1016 hr = transform_create(outer, &CLSID_mpeg_layer3_decoder, &mpeg_layer3_decoder_transform_ops, &object);
1017 if (FAILED(hr))
1018 return hr;
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;
1025 return hr;