Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 44e1a9ad2e6..5f851cb5973 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -526,6 +526,81 @@ static void test_pin_info(void) ok(!ref, "Got outstanding refcount %ld.\n", ref); }
+static void test_enum_media_types(void) +{ + IBaseFilter *filter = create_mpeg_audio_codec(); + IEnumMediaTypes *enum1, *enum2; + AM_MEDIA_TYPE *mts[1]; + ULONG ref, count; + HRESULT hr; + IPin *pin; + + IBaseFilter_FindPin(filter, L"In", &pin); + + hr = IPin_EnumMediaTypes(pin, &enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumMediaTypes_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Clone(enum1, &enum2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Skip(enum1, 1); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum2, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IEnumMediaTypes_Release(enum1); + IEnumMediaTypes_Release(enum2); + IPin_Release(pin); + + IBaseFilter_FindPin(filter, L"Out", &pin); + + hr = IPin_EnumMediaTypes(pin, &enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumMediaTypes_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Clone(enum1, &enum2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Skip(enum1, 1); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum2, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IEnumMediaTypes_Release(enum1); + IEnumMediaTypes_Release(enum2); + IPin_Release(pin); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + START_TEST(mpegaudio) { CoInitialize(NULL); @@ -536,6 +611,7 @@ START_TEST(mpegaudio) test_enum_pins(); test_find_pin(); test_pin_info(); + test_enum_media_types();
CoUninitialize(); }
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 192 ++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 5f851cb5973..cbf8ef393d2 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -21,8 +21,134 @@
#define COBJMACROS #include "dshow.h" +#include "mmreg.h" #include "wine/test.h"
+static const MPEG1WAVEFORMAT mp1_format = +{ + .wfx.wFormatTag = WAVE_FORMAT_MPEG, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 32000, + .wfx.nBlockAlign = 48, + .wfx.nAvgBytesPerSec = 4000, + .wfx.cbSize = sizeof(MPEG1WAVEFORMAT) - sizeof(WAVEFORMATEX), + .fwHeadLayer = ACM_MPEG_LAYER1, + .dwHeadBitrate = 32000, + .fwHeadMode = ACM_MPEG_SINGLECHANNEL, + .fwHeadFlags = ACM_MPEG_ID_MPEG1, +}; + +static const AM_MEDIA_TYPE mp1_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MPEG1AudioPayload, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000050, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MPEG1WAVEFORMAT), + .pbFormat = (BYTE *)&mp1_format, +}; + +static const MPEG1WAVEFORMAT mp2_format = +{ + .wfx.wFormatTag = WAVE_FORMAT_MPEG, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 32000, + .wfx.nBlockAlign = 144, + .wfx.nAvgBytesPerSec = 4000, + .wfx.cbSize = sizeof(MPEG1WAVEFORMAT) - sizeof(WAVEFORMATEX), + .fwHeadLayer = ACM_MPEG_LAYER2, + .dwHeadBitrate = 32000, + .fwHeadMode = ACM_MPEG_SINGLECHANNEL, + .fwHeadFlags = ACM_MPEG_ID_MPEG1, +}; + +static const AM_MEDIA_TYPE mp2_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MPEG1AudioPayload, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000050, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MPEG1WAVEFORMAT), + .pbFormat = (BYTE *)&mp2_format, +}; + +static const MPEG1WAVEFORMAT mp3_format0 = +{ + .wfx.wFormatTag = WAVE_FORMAT_MPEG, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 32000, + .wfx.nBlockAlign = 144, + .wfx.nAvgBytesPerSec = 4000, + .wfx.cbSize = sizeof(MPEG1WAVEFORMAT) - sizeof(WAVEFORMATEX), + .fwHeadLayer = ACM_MPEG_LAYER3, + .dwHeadBitrate = 32000, + .fwHeadMode = ACM_MPEG_SINGLECHANNEL, + .fwHeadFlags = ACM_MPEG_ID_MPEG1, +}; + +static const AM_MEDIA_TYPE mp3_mt0 = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MPEG1AudioPayload, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000050, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MPEG1WAVEFORMAT), + .pbFormat = (BYTE *)&mp3_format0, +}; + +static const MPEGLAYER3WAVEFORMAT mp3_format1 = +{ + .wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3, + .wfx.nChannels = 1, + .wfx.nSamplesPerSec = 32000, + .wfx.nBlockAlign = 144, + .wfx.nAvgBytesPerSec = 4000, + .wfx.cbSize = sizeof(MPEGLAYER3WAVEFORMAT) - sizeof(WAVEFORMATEX), + .wID = MPEGLAYER3_ID_MPEG, + .nBlockSize = 144, + .nFramesPerBlock = 1, +}; + +static const AM_MEDIA_TYPE mp3_mt1 = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_MPEG1AudioPayload, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000050, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MPEGLAYER3WAVEFORMAT), + .pbFormat = (BYTE *)&mp3_format1, +}; + +static const WAVEFORMATEX pcm16_format = +{ + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 32000, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 64000, +}; + +static const AM_MEDIA_TYPE pcm16_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 2, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&pcm16_format, +}; + static IBaseFilter *create_mpeg_audio_codec(void) { IBaseFilter *filter = NULL; @@ -601,6 +727,71 @@ static void test_enum_media_types(void) ok(!ref, "Got outstanding refcount %ld.\n", ref); }
+static void test_media_types(void) +{ + IBaseFilter *filter = create_mpeg_audio_codec(); + AM_MEDIA_TYPE mt; + HRESULT hr; + ULONG ref; + IPin *pin; + + IBaseFilter_FindPin(filter, L"In", &pin); + + hr = IPin_QueryAccept(pin, &mp1_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(pin, &mp2_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.subtype = MEDIASUBTYPE_MPEG1Packet; + hr = IPin_QueryAccept(pin, &mp2_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.subtype = MEDIASUBTYPE_MPEG1Payload; + hr = IPin_QueryAccept(pin, &mp2_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.subtype = GUID_NULL; + hr = IPin_QueryAccept(pin, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(pin, &mp3_mt0); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(pin, &mp3_mt1); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.majortype = GUID_NULL; + hr = IPin_QueryAccept(pin, &mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.subtype = MEDIASUBTYPE_PCM; + hr = IPin_QueryAccept(pin, &mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + mt = mp2_mt; + mt.formattype = GUID_NULL; + hr = IPin_QueryAccept(pin, &mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + IBaseFilter_FindPin(filter, L"Out", &pin); + + hr = IPin_QueryAccept(pin, &pcm16_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + START_TEST(mpegaudio) { CoInitialize(NULL); @@ -612,6 +803,7 @@ START_TEST(mpegaudio) test_find_pin(); test_pin_info(); test_enum_media_types(); + test_media_types();
CoUninitialize(); }
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 10 ++--- dlls/winegstreamer/quartz_transform.c | 55 ++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index cbf8ef393d2..84297884a6d 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -759,25 +759,25 @@ static void test_media_types(void) ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(pin, &mp3_mt0); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(pin, &mp3_mt1); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
mt = mp2_mt; mt.majortype = GUID_NULL; hr = IPin_QueryAccept(pin, &mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
mt = mp2_mt; mt.subtype = MEDIASUBTYPE_PCM; hr = IPin_QueryAccept(pin, &mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
mt = mp2_mt; mt.formattype = GUID_NULL; hr = IPin_QueryAccept(pin, &mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
IPin_Release(pin);
diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 9665c56d848..698d08a0d2a 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -28,6 +28,13 @@ struct transform
struct strmbase_sink sink; struct strmbase_source source; + + const struct transform_ops *ops; +}; + +struct transform_ops +{ + HRESULT (*sink_query_accept)(struct transform *filter, const AM_MEDIA_TYPE *mt); };
static inline struct transform *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -62,6 +69,13 @@ static const struct strmbase_filter_ops filter_ops = .filter_destroy = transform_destroy, };
+static HRESULT transform_sink_query_accept(struct strmbase_pin *pin, const AM_MEDIA_TYPE *mt) +{ + struct transform *filter = impl_from_strmbase_filter(pin->filter); + + return filter->ops->sink_query_accept(filter, mt); +} + static HRESULT transform_sink_query_interface(struct strmbase_pin *pin, REFIID iid, void **out) { struct transform *filter = impl_from_strmbase_filter(pin->filter); @@ -77,6 +91,7 @@ static HRESULT transform_sink_query_interface(struct strmbase_pin *pin, REFIID i
static const struct strmbase_sink_ops sink_ops = { + .base.pin_query_accept = transform_sink_query_accept, .base.pin_query_interface = transform_sink_query_interface, };
@@ -86,7 +101,7 @@ static const struct strmbase_source_ops source_ops = .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, };
-static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, struct transform **out) +static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struct transform_ops *ops, struct transform **out) { struct transform *object;
@@ -98,16 +113,52 @@ static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, struct tran strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, NULL); strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops);
+ object->ops = ops; + *out = object; return S_OK; }
+static HRESULT mpeg_audio_codec_sink_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt) +{ + const MPEG1WAVEFORMAT *format; + + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)) + return S_FALSE; + + if (!IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Packet) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Payload) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1AudioPayload) + && !IsEqualGUID(&mt->subtype, &GUID_NULL)) + return S_FALSE; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx) + || mt->cbFormat < sizeof(MPEG1WAVEFORMAT)) + return S_FALSE; + + format = (const MPEG1WAVEFORMAT *)mt->pbFormat; + + if (format->wfx.wFormatTag != WAVE_FORMAT_MPEG + || sizeof(WAVEFORMATEX) + format->wfx.cbSize < sizeof(MPEG1WAVEFORMAT)) + return S_FALSE; + + if ((format->fwHeadLayer & (ACM_MPEG_LAYER1 | ACM_MPEG_LAYER2)) != format->fwHeadLayer) + return S_FALSE; + + return S_OK; +} + +static const struct transform_ops mpeg_audio_codec_transform_ops = +{ + mpeg_audio_codec_sink_query_accept, +}; + HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out) { struct transform *object; HRESULT hr;
- hr = transform_create(outer, &CLSID_CMpegAudioCodec, &object); + hr = transform_create(outer, &CLSID_CMpegAudioCodec, &mpeg_audio_codec_transform_ops, &object); if (FAILED(hr)) return hr;
On 4/27/22 01:42, Anton Baskanov wrote:
- if (format->wfx.wFormatTag != WAVE_FORMAT_MPEG
|| sizeof(WAVEFORMATEX) + format->wfx.cbSize < sizeof(MPEG1WAVEFORMAT))
return S_FALSE;
Not a big deal, but some manual testing implies that native doesn't actually check cbSize.
- if ((format->fwHeadLayer & (ACM_MPEG_LAYER1 | ACM_MPEG_LAYER2)) != format->fwHeadLayer)
return S_FALSE;
This doesn't read the most intuitively to me, and some manual testing implies that the condition actually checked here is
if (format->fwHeadLayer == ACM_MPEG_LAYER3)
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 304 ++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 84297884a6d..defd65eaa62 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -22,6 +22,9 @@ #define COBJMACROS #include "dshow.h" #include "mmreg.h" +#include "ks.h" +#include "ksmedia.h" +#include "wine/strmbase.h" #include "wine/test.h"
static const MPEG1WAVEFORMAT mp1_format = @@ -127,6 +130,28 @@ static const AM_MEDIA_TYPE mp3_mt1 = .pbFormat = (BYTE *)&mp3_format1, };
+static const WAVEFORMATEX pcm8_format = +{ + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 32000, + .wBitsPerSample = 8, + .nBlockAlign = 1, + .nAvgBytesPerSec = 32000, +}; + +static const AM_MEDIA_TYPE pcm8_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&pcm8_format, +}; + static const WAVEFORMATEX pcm16_format = { .wFormatTag = WAVE_FORMAT_PCM, @@ -149,6 +174,99 @@ static const AM_MEDIA_TYPE pcm16_mt = .pbFormat = (BYTE *)&pcm16_format, };
+static const WAVEFORMATEX pcm16_stereo_format = +{ + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 2, + .nSamplesPerSec = 32000, + .wBitsPerSample = 16, + .nBlockAlign = 4, + .nAvgBytesPerSec = 128000, +}; + +static const AM_MEDIA_TYPE pcm16_stereo_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 4, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&pcm16_stereo_format, +}; + +static const WAVEFORMATEX pcm16_16khz_format = +{ + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 16000, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 32000, +}; + +static const AM_MEDIA_TYPE pcm16_16khz_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 2, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&pcm16_16khz_format, +}; + +static const WAVEFORMATEX pcm24_format = +{ + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 32000, + .wBitsPerSample = 24, + .nBlockAlign = 3, + .nAvgBytesPerSec = 96000, +}; + +static const AM_MEDIA_TYPE pcm24_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 3, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&pcm24_format, +}; + +static const WAVEFORMATEXTENSIBLE pcm16ex_format = +{ + .Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE, + .Format.nChannels = 1, + .Format.nSamplesPerSec = 32000, + .Format.wBitsPerSample = 16, + .Format.nBlockAlign = 2, + .Format.nAvgBytesPerSec = 64000, + .Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + .Samples.wValidBitsPerSample = 16, + .dwChannelMask = KSAUDIO_SPEAKER_STEREO, + /* KSDATAFORMAT_SUBTYPE_PCM */ + .SubFormat = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, +}; + +static const AM_MEDIA_TYPE pcm16ex_mt = +{ + /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */ + .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 2, + .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(WAVEFORMATEXTENSIBLE), + .pbFormat = (BYTE *)&pcm16ex_format, +}; + static IBaseFilter *create_mpeg_audio_codec(void) { IBaseFilter *filter = NULL; @@ -158,6 +276,12 @@ static IBaseFilter *create_mpeg_audio_codec(void) return filter; }
+static inline BOOL compare_media_types(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b) +{ + return !memcmp(a, b, offsetof(AM_MEDIA_TYPE, pbFormat)) + && !memcmp(a->pbFormat, b->pbFormat, a->cbFormat); +} + static ULONG get_refcount(void *iface) { IUnknown *unknown = iface; @@ -792,6 +916,185 @@ static void test_media_types(void) ok(!ref, "Got outstanding refcount %ld.\n", ref); }
+struct testfilter +{ + struct strmbase_filter filter; + struct strmbase_source source; + const AM_MEDIA_TYPE *mt; +}; + +static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, filter); +} + +static struct strmbase_pin *testfilter_get_pin(struct strmbase_filter *iface, unsigned int index) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + if (!index) + return &filter->source.pin; + return NULL; +} + +static void testfilter_destroy(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + strmbase_source_cleanup(&filter->source); + strmbase_filter_cleanup(&filter->filter); +} + +static const struct strmbase_filter_ops testfilter_ops = +{ + .filter_get_pin = testfilter_get_pin, + .filter_destroy = testfilter_destroy, +}; + +static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, + IMemInputPin *peer, IMemAllocator **allocator) +{ + return S_OK; +} + +static const struct strmbase_source_ops testsource_ops = +{ + .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideAllocator = testsource_DecideAllocator, +}; + +static void testfilter_init(struct testfilter *filter) +{ + static const GUID clsid = {0xabacab}; + memset(filter, 0, sizeof(*filter)); + strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); + strmbase_source_init(&filter->source, &filter->filter, L"source", &testsource_ops); +} + +static void test_connect_pin(void) +{ + IBaseFilter *filter = create_mpeg_audio_codec(); + struct testfilter testsource; + IPin *sink, *source, *peer; + WAVEFORMATEX req_format; + IMediaControl *control; + IMemInputPin *meminput; + AM_MEDIA_TYPE req_mt; + IFilterGraph2 *graph; + AM_MEDIA_TYPE mt; + HRESULT hr; + ULONG ref; + + CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IFilterGraph2, (void **)&graph); + testfilter_init(&testsource); + IFilterGraph2_AddFilter(graph, &testsource.filter.IBaseFilter_iface, L"source"); + IFilterGraph2_AddFilter(graph, filter, L"MPEG audio decoder"); + IBaseFilter_FindPin(filter, L"In", &sink); + IBaseFilter_FindPin(filter, L"Out", &source); + IPin_QueryInterface(sink, &IID_IMemInputPin, (void **)&meminput); + IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); + + /* Test sink connection. */ + + peer = (IPin *)0xdeadbeef; + hr = IPin_ConnectedTo(sink, &peer); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + ok(!peer, "Got peer %p.\n", peer); + + hr = IPin_ConnectionMediaType(sink, &mt); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &mp1_mt); + ok(hr == VFW_E_NOT_STOPPED, "Got hr %#lx.\n", hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + req_mt = mp1_mt; + req_mt.subtype = MEDIASUBTYPE_PCM; + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &req_mt); + ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#lx.\n", hr); + + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &mp1_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IPin_ConnectedTo(sink, &peer); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(peer == &testsource.source.pin.IPin_iface, "Got peer %p.\n", peer); + IPin_Release(peer); + + hr = IPin_ConnectionMediaType(sink, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(compare_media_types(&mt, &mp1_mt), "Media types didn't match.\n"); + ok(compare_media_types(&testsource.source.pin.mt, &mp1_mt), "Media types didn't match.\n"); + FreeMediaType(&mt); + + hr = IPin_QueryAccept(source, &pcm16_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(source, &pcm8_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + req_mt = pcm16_mt; + req_mt.majortype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + req_mt = pcm16_mt; + req_mt.subtype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + req_mt = pcm16_mt; + req_mt.formattype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(source, &pcm16_stereo_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(source, &pcm16_16khz_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(source, &pcm24_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_QueryAccept(source, &pcm16ex_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + req_format = pcm16_format; + req_format.nBlockAlign = 333; + req_mt = pcm16_mt; + req_mt.pbFormat = (BYTE *)&req_format; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + req_format = pcm16_format; + req_format.nAvgBytesPerSec = 333; + req_mt = pcm16_mt; + req_mt.pbFormat = (BYTE *)&req_format; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_Disconnect(graph, sink); + ok(hr == VFW_E_NOT_STOPPED, "Got hr %#lx.\n", hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + IMemInputPin_Release(meminput); + IPin_Release(sink); + IPin_Release(source); + IMediaControl_Release(control); + ref = IFilterGraph2_Release(graph); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(&testsource.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + START_TEST(mpegaudio) { CoInitialize(NULL); @@ -804,6 +1107,7 @@ START_TEST(mpegaudio) test_pin_info(); test_enum_media_types(); test_media_types(); + test_connect_pin();
CoUninitialize(); }
On 4/27/22 01:42, Anton Baskanov wrote:
+static const WAVEFORMATEX pcm8_format = +{
- .wFormatTag = WAVE_FORMAT_PCM,
- .nChannels = 1,
- .nSamplesPerSec = 32000,
- .wBitsPerSample = 8,
- .nBlockAlign = 1,
- .nAvgBytesPerSec = 32000,
+};
+static const AM_MEDIA_TYPE pcm8_mt = +{
- /* MEDIATYPE_Audio, MEDIASUBTYPE_PCM, FORMAT_WaveFormatEx */
- .majortype = {0x73647561, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}},
- .subtype = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}},
- .bFixedSizeSamples = TRUE,
- .lSampleSize = 1,
- .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}},
- .cbFormat = sizeof(WAVEFORMATEX),
- .pbFormat = (BYTE *)&pcm8_format,
+};
This works, but it occurs to me that a helper like
static void init_pcm_mt(AM_MEDIA_TYPE *mt, WAVEFORMATEX *format, unsigned int channels, unsigned int sample_rate, unsigned int depth)
would save some space.
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/quartz/tests/mpegaudio.c | 20 ++++++------ dlls/winegstreamer/quartz_transform.c | 45 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index defd65eaa62..70809d2d218 100644 --- a/dlls/quartz/tests/mpegaudio.c +++ b/dlls/quartz/tests/mpegaudio.c @@ -908,7 +908,7 @@ static void test_media_types(void) IBaseFilter_FindPin(filter, L"Out", &pin);
hr = IPin_QueryAccept(pin, &pcm16_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
IPin_Release(pin);
@@ -1038,43 +1038,43 @@ static void test_connect_pin(void) req_mt = pcm16_mt; req_mt.majortype = GUID_NULL; hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
req_mt = pcm16_mt; req_mt.subtype = GUID_NULL; hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
req_mt = pcm16_mt; req_mt.formattype = GUID_NULL; hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(source, &pcm16_stereo_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(source, &pcm16_16khz_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(source, &pcm24_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IPin_QueryAccept(source, &pcm16ex_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
req_format = pcm16_format; req_format.nBlockAlign = 333; req_mt = pcm16_mt; req_mt.pbFormat = (BYTE *)&req_format; hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
req_format = pcm16_format; req_format.nAvgBytesPerSec = 333; req_mt = pcm16_mt; req_mt.pbFormat = (BYTE *)&req_format; hr = IPin_QueryAccept(source, &req_mt); - todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
hr = IMediaControl_Pause(control); ok(hr == S_OK, "Got hr %#lx.\n", hr); diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 698d08a0d2a..a8f6806346e 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -35,6 +35,7 @@ struct transform struct transform_ops { HRESULT (*sink_query_accept)(struct transform *filter, const AM_MEDIA_TYPE *mt); + HRESULT (*source_query_accept)(struct transform *filter, const AM_MEDIA_TYPE *mt); };
static inline struct transform *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -95,8 +96,16 @@ static const struct strmbase_sink_ops sink_ops = .base.pin_query_interface = transform_sink_query_interface, };
+static HRESULT transform_source_query_accept(struct strmbase_pin *pin, const AM_MEDIA_TYPE *mt) +{ + struct transform *filter = impl_from_strmbase_filter(pin->filter); + + return filter->ops->source_query_accept(filter, mt); +} + static const struct strmbase_source_ops source_ops = { + .base.pin_query_accept = transform_source_query_accept, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, }; @@ -148,9 +157,45 @@ static HRESULT mpeg_audio_codec_sink_query_accept(struct transform *filter, cons return S_OK; }
+static HRESULT mpeg_audio_codec_source_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt) +{ + const MPEG1WAVEFORMAT *input_format; + const WAVEFORMATEX *output_format; + DWORD expected_avg_bytes_per_sec; + WORD expected_block_align; + + if (!filter->sink.pin.peer) + return S_FALSE; + + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio) + || !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_PCM) + || !IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx) + || mt->cbFormat < sizeof(WAVEFORMATEX)) + return S_FALSE; + + input_format = (const MPEG1WAVEFORMAT *)filter->sink.pin.mt.pbFormat; + output_format = (const WAVEFORMATEX *)mt->pbFormat; + + if (output_format->wFormatTag != WAVE_FORMAT_PCM + || input_format->wfx.nSamplesPerSec != output_format->nSamplesPerSec + || input_format->wfx.nChannels != output_format->nChannels + || (output_format->wBitsPerSample != 8 && output_format->wBitsPerSample != 16)) + return S_FALSE; + + expected_block_align = output_format->nChannels * output_format->wBitsPerSample / 8; + expected_avg_bytes_per_sec = expected_block_align * output_format->nSamplesPerSec; + + if (output_format->nBlockAlign != expected_block_align + || output_format->nAvgBytesPerSec != expected_avg_bytes_per_sec) + return S_FALSE; + + return S_OK; +} + static const struct transform_ops mpeg_audio_codec_transform_ops = { mpeg_audio_codec_sink_query_accept, + mpeg_audio_codec_source_query_accept, };
HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out)