Signed-off-by: Anton Baskanov baskanov@gmail.com --- v2: * Added "parsed" field to MPEG-1 audio caps. * Allow mpg123audiodec to be used. * Use WG_MAJOR_TYPE_AUDIO instead of WG_MAJOR_TYPE_MPEG1_AUDIO. --- dlls/winegstreamer/wg_format.c | 29 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_transform.c | 13 +++++++++++++ 2 files changed, 42 insertions(+)
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index ff9238a6a69..038ec956a42 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -338,12 +338,41 @@ static void wg_channel_mask_to_gst(GstAudioChannelPosition *positions, uint32_t } }
+static GstCaps *wg_format_to_caps_mpeg1_audio(const struct wg_format *format) +{ + GstCaps *caps; + int layer; + + if (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER1) + layer = 1; + else if (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER2) + layer = 2; + else if (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER3) + layer = 3; + + if (!(caps = gst_caps_new_empty_simple("audio/mpeg"))) + return NULL; + + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "layer", G_TYPE_INT, layer, NULL); + gst_caps_set_simple(caps, "rate", G_TYPE_INT, format->u.audio.rate, NULL); + gst_caps_set_simple(caps, "channels", G_TYPE_INT, format->u.audio.channels, NULL); + gst_caps_set_simple(caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); + + return caps; +} + static GstCaps *wg_format_to_caps_audio(const struct wg_format *format) { GstAudioChannelPosition positions[32]; GstAudioFormat audio_format; GstAudioInfo info;
+ if (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER1 + || format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER2 + || format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER3) + return wg_format_to_caps_mpeg1_audio(format); + if ((audio_format = wg_audio_format_to_gst(format->u.audio.format)) == GST_AUDIO_FORMAT_UNKNOWN) return NULL;
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 49c7bfaa927..d8fe896f31e 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -234,6 +234,19 @@ NTSTATUS wg_transform_create(void *args) break;
case WG_MAJOR_TYPE_AUDIO: + if (input_format.u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER1 + || input_format.u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER2 + || input_format.u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER3) + { + if (!(element = transform_find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, src_caps, raw_caps)) + || !transform_append_element(transform, element, &first, &last)) + { + gst_caps_unref(raw_caps); + goto out; + } + break; + } + /* fallthrough */ case WG_MAJOR_TYPE_VIDEO: case WG_MAJOR_TYPE_UNKNOWN: GST_FIXME("Format %u not implemented!", input_format.major_type);
Signed-off-by: Anton Baskanov baskanov@gmail.com --- v2: Check whether decoder can be created once at the beginning. --- dlls/quartz/tests/mpegaudio.c | 60 +++++++++-------------------------- 1 file changed, 15 insertions(+), 45 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 296f5f479a0..a43adfbbd6d 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -216,16 +216,9 @@ static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOO
static void test_interfaces(void) { - IBaseFilter *filter; + IBaseFilter *filter = create_mpeg_audio_codec(); IPin *pin;
- filter = create_mpeg_audio_codec(); - if (!filter) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } - check_interface(filter, &IID_IBaseFilter, TRUE); check_interface(filter, &IID_IMediaFilter, TRUE); check_interface(filter, &IID_IPersist, TRUE); @@ -322,11 +315,6 @@ static void test_aggregation(void) hr = CoCreateInstance(&CLSID_CMpegAudioCodec, &test_outer, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk); ok(hr == S_OK, "Got hr %#lx.\n", hr); - if (FAILED(hr)) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n"); ref = get_refcount(unk); @@ -372,18 +360,11 @@ static void test_aggregation(void)
static void test_unconnected_filter_state(void) { - IBaseFilter *filter; + IBaseFilter *filter = create_mpeg_audio_codec(); FILTER_STATE state; HRESULT hr; ULONG ref;
- filter = create_mpeg_audio_codec(); - if (!filter) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } - hr = IBaseFilter_GetState(filter, 0, &state); ok(hr == S_OK, "Got hr %#lx.\n", hr); ok(state == State_Stopped, "Got state %u.\n", state); @@ -436,19 +417,12 @@ static void test_unconnected_filter_state(void)
static void test_enum_pins(void) { - IBaseFilter *filter; + IBaseFilter *filter = create_mpeg_audio_codec(); IEnumPins *enum1, *enum2; ULONG count, ref; IPin *pins[3]; HRESULT hr;
- filter = create_mpeg_audio_codec(); - if (!filter) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } - ref = get_refcount(filter); ok(ref == 1, "Got unexpected refcount %ld.\n", ref);
@@ -564,19 +538,12 @@ static void test_enum_pins(void)
static void test_find_pin(void) { - IBaseFilter *filter; + IBaseFilter *filter = create_mpeg_audio_codec(); IEnumPins *enum_pins; IPin *pin, *pin2; HRESULT hr; ULONG ref;
- filter = create_mpeg_audio_codec(); - if (!filter) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } - hr = IBaseFilter_EnumPins(filter, &enum_pins); ok(hr == S_OK, "Got hr %#lx.\n", hr);
@@ -612,7 +579,7 @@ static void test_find_pin(void)
static void test_pin_info(void) { - IBaseFilter *filter; + IBaseFilter *filter = create_mpeg_audio_codec(); PIN_DIRECTION dir; PIN_INFO info; HRESULT hr; @@ -620,13 +587,6 @@ static void test_pin_info(void) ULONG ref; IPin *pin;
- filter = create_mpeg_audio_codec(); - if (!filter) - { - skip("Failed to create MPEG audio decoder instance, skipping tests.\n"); - return; - } - hr = IBaseFilter_FindPin(filter, L"In", &pin); ok(hr == S_OK, "Got hr %#lx.\n", hr); ref = get_refcount(filter); @@ -1393,8 +1353,18 @@ static void test_connect_pin(void)
START_TEST(mpegaudio) { + IBaseFilter *filter; + CoInitialize(NULL);
+ if (FAILED(CoCreateInstance(&CLSID_CMpegAudioCodec, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter))) + { + skip("Failed to create MPEG audio decoder instance.\n"); + return; + } + IBaseFilter_Release(filter); + test_interfaces(); test_aggregation(); test_unconnected_filter_state();
Signed-off-by: Anton Baskanov baskanov@gmail.com --- v2: * Replace FIXME with winediag error. * Use WG_MAJOR_TYPE_AUDIO instead of WG_MAJOR_TYPE_MPEG1_AUDIO. --- dlls/winegstreamer/quartz_transform.c | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 375c549aad5..84f0a6a2456 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -21,6 +21,7 @@ #include "gst_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz); +WINE_DECLARE_DEBUG_CHANNEL(winediag);
struct transform { @@ -296,9 +297,38 @@ static const struct transform_ops mpeg_audio_codec_transform_ops =
HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out) { + struct wg_format output_format = + { + .major_type = WG_MAJOR_TYPE_AUDIO, + .u.audio = { + .format = WG_AUDIO_FORMAT_S16LE, + .channel_mask = 1, + .channels = 1, + .rate = 44100, + }, + }; + struct wg_format input_format = + { + .major_type = WG_MAJOR_TYPE_AUDIO, + .u.audio = { + .format = WG_AUDIO_FORMAT_MPEG1_LAYER2, + .channel_mask = 1, + .channels = 1, + .rate = 44100, + }, + }; + struct wg_transform *transform; struct transform *object; HRESULT hr;
+ transform = wg_transform_create(&input_format, &output_format); + if (!transform) + { + ERR_(winediag)("GStreamer doesn't support MPEG-1 audio decoding, please install appropriate plugins.\n"); + return E_FAIL; + } + wg_transform_destroy(transform); + hr = transform_create(outer, &CLSID_CMpegAudioCodec, &mpeg_audio_codec_transform_ops, &object); if (FAILED(hr)) return hr;
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/winegstreamer/quartz_transform.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 84f0a6a2456..1eba1b781d2 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -30,6 +30,8 @@ struct transform struct strmbase_sink sink; struct strmbase_source source;
+ struct wg_transform *transform; + const struct transform_ops *ops; };
@@ -70,10 +72,22 @@ static void transform_destroy(struct strmbase_filter *iface) static HRESULT transform_init_stream(struct strmbase_filter *iface) { struct transform *filter = impl_from_strmbase_filter(iface); + struct wg_format input_format; + struct wg_format output_format; HRESULT hr;
if (filter->source.pin.peer) { + if (!amt_to_wg_format(&filter->sink.pin.mt, &input_format)) + return E_FAIL; + + if (!amt_to_wg_format(&filter->source.pin.mt, &output_format)) + return E_FAIL; + + filter->transform = wg_transform_create(&input_format, &output_format); + if (!filter->transform) + return E_FAIL; + hr = IMemAllocator_Commit(filter->source.pAllocator); if (FAILED(hr)) ERR("Failed to commit allocator, hr %#lx.\n", hr); @@ -87,8 +101,12 @@ static HRESULT transform_cleanup_stream(struct strmbase_filter *iface) struct transform *filter = impl_from_strmbase_filter(iface);
if (filter->source.pin.peer) + { IMemAllocator_Decommit(filter->source.pAllocator);
+ wg_transform_destroy(filter->transform); + } + return S_OK; }
Signed-off-by: Anton Baskanov baskanov@gmail.com --- v2: * Send 3 frames instead of 1 to compensate for mpg123audiodec internal queuing. * Removed the output data check. --- dlls/quartz/tests/mpegaudio.c | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index a43adfbbd6d..b34f09e137f 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -794,6 +794,7 @@ struct testfilter struct strmbase_source source; struct strmbase_sink sink; const AM_MEDIA_TYPE *mt; + unsigned int got_sample; };
static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -867,11 +868,27 @@ static HRESULT testsink_connect(struct strmbase_sink *iface, IPin *peer, const A return S_OK; }
+static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample *sample) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + LONG size; + + ++filter->got_sample; + + size = IMediaSample_GetSize(sample); + ok(size == 3072, "Got size %lu.\n", size); + size = IMediaSample_GetActualDataLength(sample); + ok(size == 768, "Got valid size %lu.\n", size); + + return S_OK; +} + static const struct strmbase_sink_ops testsink_ops = { .base.pin_query_interface = testsink_query_interface, .base.pin_get_media_type = testsink_get_media_type, .sink_connect = testsink_connect, + .pfnReceive = testsink_Receive, };
static void testfilter_init(struct testfilter *filter) @@ -1040,6 +1057,62 @@ static void test_source_allocator(IFilterGraph2 *graph, IMediaControl *control, IFilterGraph2_Disconnect(graph, &testsource->source.pin.IPin_iface); }
+static void test_sample_processing(IMediaControl *control, IMemInputPin *input, struct testfilter *sink) +{ + IMemAllocator *allocator; + IMediaSample *sample; + HRESULT hr; + BYTE *data; + LONG size; + + hr = IMemInputPin_ReceiveCanBlock(input); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == VFW_E_NOT_COMMITTED, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaSample_GetPointer(sample, &data); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + size = IMediaSample_GetSize(sample); + ok(size == 256, "Got size %ld.\n", size); + memset(data, 0, 48); + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0x18; + data[3] = 0xc4; + hr = IMediaSample_SetActualDataLength(sample, 48); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_Receive(input, sample); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemInputPin_Receive(input, sample); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); + sink->got_sample = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_Receive(input, sample); + todo_wine ok(hr == VFW_E_WRONG_STATE, "Got hr %#lx.\n", hr); + + IMediaSample_Release(sample); + IMemAllocator_Release(allocator); +} + static void test_connect_pin(void) { IBaseFilter *filter = create_mpeg_audio_codec(); @@ -1233,6 +1306,8 @@ static void test_connect_pin(void) hr = IMediaControl_Stop(control); ok(hr == S_OK, "Got hr %#lx.\n", hr);
+ test_sample_processing(control, meminput, &testsink); + hr = IFilterGraph2_Disconnect(graph, source); ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IFilterGraph2_Disconnect(graph, source);
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 10 ++-- dlls/winegstreamer/quartz_transform.c | 86 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index b34f09e137f..105469edaf1 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -1095,19 +1095,19 @@ static void test_sample_processing(IMediaControl *control, IMemInputPin *input, ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); sink->got_sample = 0;
hr = IMediaControl_Stop(control); ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IMemInputPin_Receive(input, sample); - todo_wine ok(hr == VFW_E_WRONG_STATE, "Got hr %#lx.\n", hr); + ok(hr == VFW_E_WRONG_STATE, "Got hr %#lx.\n", hr);
IMediaSample_Release(sample); IMemAllocator_Release(allocator); diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 1eba1b781d2..c5b22649c13 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -20,6 +20,8 @@
#include "gst_private.h"
+#include "mferror.h" + WINE_DEFAULT_DEBUG_CHANNEL(quartz); WINE_DECLARE_DEBUG_CHANNEL(winediag);
@@ -138,10 +140,94 @@ static HRESULT transform_sink_query_interface(struct strmbase_pin *pin, REFIID i return S_OK; }
+static HRESULT WINAPI transform_sink_receive(struct strmbase_sink *pin, IMediaSample *sample) +{ + struct transform *filter = impl_from_strmbase_filter(pin->pin.filter); + struct wg_sample input_wg_sample = {0}; + HRESULT hr; + + /* We do not expect pin connection state to change while the filter is + * running. This guarantee is necessary, since otherwise we would have to + * take the filter lock, and we can't take the filter lock from a streaming + * thread. */ + if (!filter->source.pMemInputPin) + { + WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n"); + return VFW_E_NOT_CONNECTED; + } + + if (filter->filter.state == State_Stopped) + return VFW_E_WRONG_STATE; + + if (filter->sink.flushing) + return S_FALSE; + + input_wg_sample.max_size = IMediaSample_GetSize(sample); + input_wg_sample.size = IMediaSample_GetActualDataLength(sample); + + hr = IMediaSample_GetPointer(sample, &input_wg_sample.data); + if (FAILED(hr)) + return hr; + + hr = wg_transform_push_data(filter->transform, &input_wg_sample); + if (FAILED(hr)) + return hr; + + for (;;) + { + struct wg_sample output_wg_sample = {0}; + IMediaSample *output_sample; + + hr = IMemAllocator_GetBuffer(filter->source.pAllocator, &output_sample, NULL, NULL, 0); + if (FAILED(hr)) + return hr; + + output_wg_sample.max_size = IMediaSample_GetSize(output_sample); + + hr = IMediaSample_GetPointer(output_sample, &output_wg_sample.data); + if (FAILED(hr)) + { + IMediaSample_Release(output_sample); + return hr; + } + + hr = wg_transform_read_data(filter->transform, &output_wg_sample); + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) + { + IMediaSample_Release(output_sample); + break; + } + if (FAILED(hr)) + { + IMediaSample_Release(output_sample); + return hr; + } + + hr = IMediaSample_SetActualDataLength(output_sample, output_wg_sample.size); + if (FAILED(hr)) + { + IMediaSample_Release(output_sample); + return hr; + } + + hr = IMemInputPin_Receive(filter->source.pMemInputPin, output_sample); + if (FAILED(hr)) + { + IMediaSample_Release(output_sample); + return hr; + } + + IMediaSample_Release(output_sample); + } + + return S_OK; +} + static const struct strmbase_sink_ops sink_ops = { .base.pin_query_accept = transform_sink_query_accept, .base.pin_query_interface = transform_sink_query_interface, + .pfnReceive = transform_sink_receive, };
static HRESULT transform_source_query_accept(struct strmbase_pin *pin, const AM_MEDIA_TYPE *mt)
On 5/4/22 02:58, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
v2:
- Added "parsed" field to MPEG-1 audio caps.
- Allow mpg123audiodec to be used.
- Use WG_MAJOR_TYPE_AUDIO instead of WG_MAJOR_TYPE_MPEG1_AUDIO.
dlls/winegstreamer/wg_format.c | 29 +++++++++++++++++++++++++++++ dlls/winegstreamer/wg_transform.c | 13 +++++++++++++ 2 files changed, 42 insertions(+)
Okay, I'm convinced, making it a new major type is better after all :-)
I was kind of on the fence anyway; conceptually it probably is better that way, given that we've been making other compressed formats into separate major types, and if you take this patch into account the code actually does get simpler.
I'll send a new revision that reverts to your initial 1/7, combined with the other changes from v2.