Signed-off-by: Anton Baskanov baskanov@gmail.com --- v2: Added init_pcm_mt() helper. --- dlls/quartz/tests/mpegaudio.c | 238 ++++++++++++++++++++++++++++++++-- 1 file changed, 227 insertions(+), 11 deletions(-)
diff --git a/dlls/quartz/tests/mpegaudio.c b/dlls/quartz/tests/mpegaudio.c index 84297884a6d..68e49afe576 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,17 +130,22 @@ static const AM_MEDIA_TYPE mp3_mt1 = .pbFormat = (BYTE *)&mp3_format1, };
-static const WAVEFORMATEX pcm16_format = +static const WAVEFORMATEXTENSIBLE pcm16ex_format = { - .wFormatTag = WAVE_FORMAT_PCM, - .nChannels = 1, - .nSamplesPerSec = 32000, - .wBitsPerSample = 16, - .nBlockAlign = 2, - .nAvgBytesPerSec = 64000, + .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 pcm16_mt = +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}}, @@ -145,8 +153,8 @@ static const AM_MEDIA_TYPE pcm16_mt = .bFixedSizeSamples = TRUE, .lSampleSize = 2, .formattype = {0x05589f81, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, - .cbFormat = sizeof(WAVEFORMATEX), - .pbFormat = (BYTE *)&pcm16_format, + .cbFormat = sizeof(WAVEFORMATEXTENSIBLE), + .pbFormat = (BYTE *)&pcm16ex_format, };
static IBaseFilter *create_mpeg_audio_codec(void) @@ -158,6 +166,31 @@ 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 void init_pcm_mt(AM_MEDIA_TYPE *mt, WAVEFORMATEX *format, + WORD channels, DWORD sample_rate, WORD depth) +{ + format->wFormatTag = WAVE_FORMAT_PCM; + format->nChannels = channels; + format->nSamplesPerSec = sample_rate; + format->wBitsPerSample = depth; + format->nBlockAlign = channels * depth / 8; + format->nAvgBytesPerSec = format->nBlockAlign * format->nSamplesPerSec; + format->cbSize = 0; + mt->majortype = MEDIATYPE_Audio; + mt->subtype = MEDIASUBTYPE_PCM; + mt->bFixedSizeSamples = TRUE; + mt->lSampleSize = format->nBlockAlign; + mt->formattype = FORMAT_WaveFormatEx; + mt->cbFormat = sizeof(WAVEFORMATEX); + mt->pbFormat = (BYTE *)format; +} + static ULONG get_refcount(void *iface) { IUnknown *unknown = iface; @@ -730,6 +763,7 @@ static void test_enum_media_types(void) static void test_media_types(void) { IBaseFilter *filter = create_mpeg_audio_codec(); + WAVEFORMATEX format; AM_MEDIA_TYPE mt; HRESULT hr; ULONG ref; @@ -783,7 +817,8 @@ static void test_media_types(void)
IBaseFilter_FindPin(filter, L"Out", &pin);
- hr = IPin_QueryAccept(pin, &pcm16_mt); + init_pcm_mt(&mt, &format, 1, 32000, 16); + hr = IPin_QueryAccept(pin, &mt); todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr);
IPin_Release(pin); @@ -792,6 +827,186 @@ 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); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + hr = IPin_QueryAccept(source, &req_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 8); + hr = IPin_QueryAccept(source, &req_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + req_mt.majortype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + req_mt.subtype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + req_mt.formattype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 2, 32000, 16); + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 16000, 16); + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 24); + hr = IPin_QueryAccept(source, &req_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); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + req_format.nBlockAlign = 333; + hr = IPin_QueryAccept(source, &req_mt); + todo_wine ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + init_pcm_mt(&req_mt, &req_format, 1, 32000, 16); + req_format.nAvgBytesPerSec = 333; + 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 +1019,7 @@ START_TEST(mpegaudio) test_pin_info(); test_enum_media_types(); test_media_types(); + test_connect_pin();
CoUninitialize(); }