/* * Copyright 2018 Zebediah Figura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include "windef.h" #include "wingdi.h" #include "mmreg.h" #include "mmsystem.h" #include "dmo.h" #include "wmcodecdsp.h" #include "uuids.h" #include "wine/test.h" #include "initguid.h" DEFINE_GUID(WMMEDIASUBTYPE_MP3, 0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); /* shorter aliases for flags */ #define O_SYNCPOINT DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT #define O_TIME DMO_OUTPUT_DATA_BUFFERF_TIME #define O_TIMELENGTH DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH #define O_INCOMPLETE DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE static REFERENCE_TIME samplelen(DWORD samples, int rate) { return (REFERENCE_TIME) 10000000 * samples / rate; } struct test_buffer { IMediaBuffer IMediaBuffer_iface; LONG refcount; BYTE data[5000]; DWORD len; DWORD maxlen; }; static inline struct test_buffer *impl_from_IMediaBuffer(IMediaBuffer *iface) { return CONTAINING_RECORD(iface, struct test_buffer, IMediaBuffer_iface); } static HRESULT WINAPI Buffer_QueryInterface(IMediaBuffer *iface, REFIID iid, void **obj) { if (IsEqualIID(iid, &IID_IMediaBuffer) || IsEqualIID(iid, &IID_IUnknown)) { *obj = iface; return S_OK; } ok(0, "Unexpected IID %s\n", wine_dbgstr_guid(iid)); return E_NOINTERFACE; } static ULONG WINAPI Buffer_AddRef(IMediaBuffer *iface) { struct test_buffer *buffer = impl_from_IMediaBuffer(iface); return InterlockedIncrement(&buffer->refcount); } static ULONG WINAPI Buffer_Release(IMediaBuffer *iface) { struct test_buffer *buffer = impl_from_IMediaBuffer(iface); return InterlockedDecrement(&buffer->refcount); } static HRESULT WINAPI Buffer_SetLength(IMediaBuffer *iface, DWORD len) { struct test_buffer *This = impl_from_IMediaBuffer(iface); This->len = len; return S_OK; } static HRESULT WINAPI Buffer_GetMaxLength(IMediaBuffer *iface, DWORD *len) { struct test_buffer *This = impl_from_IMediaBuffer(iface); *len = This->maxlen; return S_OK; } static HRESULT WINAPI Buffer_GetBufferAndLength(IMediaBuffer *iface, BYTE **buf, DWORD *len) { struct test_buffer *This = impl_from_IMediaBuffer(iface); if (buf) *buf = This->data; if (len) *len = This->len; return S_OK; } static IMediaBufferVtbl Buffer_vtbl = { Buffer_QueryInterface, Buffer_AddRef, Buffer_Release, Buffer_SetLength, Buffer_GetMaxLength, Buffer_GetBufferAndLength, }; static void test_convert(void) { static const BYTE mp3hdr[] = {0xff,0xfb,0x14,0xc4}; struct test_buffer outbuf = {.IMediaBuffer_iface = {&Buffer_vtbl}, .refcount = 1}; struct test_buffer inbuf = {.IMediaBuffer_iface = {&Buffer_vtbl}, .refcount = 1}; DMO_MEDIA_TYPE in = {{0}}, out = {{0}}; MPEGLAYER3WAVEFORMAT mp3fmt = {{0}}; DMO_OUTPUT_DATA_BUFFER output; WAVEFORMATEX wavfmt = {0}; IMediaObject *dmo; DWORD written; DWORD status; HRESULT hr; int i; hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo); ok(hr == S_OK, "got %#x\n", hr); mp3fmt.wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3; mp3fmt.wfx.nChannels = 2; mp3fmt.wfx.nSamplesPerSec = 48000; in.majortype = MEDIATYPE_Audio; in.subtype = WMMEDIASUBTYPE_MP3; in.formattype = FORMAT_WaveFormatEx; in.cbFormat = sizeof(mp3fmt); in.pbFormat = (BYTE *)&mp3fmt; hr = IMediaObject_SetInputType(dmo, 0, &in, 0); ok(hr == S_OK, "got %#x\n", hr); wavfmt.wFormatTag = WAVE_FORMAT_PCM; wavfmt.nChannels = 1; wavfmt.nSamplesPerSec = 48000; wavfmt.nAvgBytesPerSec = 48000 * ((1 * 8) / 8); wavfmt.nBlockAlign = (1 * 8) / 8; wavfmt.wBitsPerSample = 8; out.majortype = MEDIATYPE_Audio; out.subtype = MEDIASUBTYPE_PCM; out.formattype = FORMAT_WaveFormatEx; out.cbFormat = sizeof(wavfmt); out.pbFormat = (BYTE *)&wavfmt; hr = IMediaObject_SetOutputType(dmo, 0, &out, 0); ok(hr == S_OK, "got %#x\n", hr); outbuf.len = 0; outbuf.maxlen = sizeof(outbuf.data); output.pBuffer = &outbuf.IMediaBuffer_iface; output.dwStatus = 0xdeadbeef; output.rtTimestamp = 0xdeadbeef; output.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_FALSE, "got %#x\n", hr); ok(outbuf.len == 0, "got %u\n", outbuf.len); ok(output.dwStatus == 0, "got %#x\n", output.dwStatus); ok(output.rtTimestamp == 0xdeadbeef, "got %s\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == 0xdeadbeef, "got %s\n", wine_dbgstr_longlong(output.rtTimelength)); /* write several frames of mp3 data */ for (i = 0; i < 5; i++) memcpy(inbuf.data + 96 * i, mp3hdr, 4); inbuf.len = 96 * 5; hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == S_OK, "got %#x\n", hr); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == DMO_E_NOTACCEPTING, "got %#x\n", hr); outbuf.len = 0; outbuf.maxlen = 0; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_FALSE, "got %#x\n", hr); ok(outbuf.len == 0, "got %u\n", outbuf.len); ok(output.dwStatus == (O_SYNCPOINT | O_INCOMPLETE), "got %#x\n", output.dwStatus); ok(output.rtTimestamp == 0xdeadbeef, "got %s\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == 0xdeadbeef, "got %s\n", wine_dbgstr_longlong(output.rtTimelength)); /* implementations are inconsistent, but should write at least one frame of data */ outbuf.len = 0; outbuf.maxlen = 5000; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_OK, "got %#x\n", hr); written = outbuf.len; ok(written > 1152 && written <= 5000, "got %u\n", written); ok(output.dwStatus == (O_SYNCPOINT | O_TIME | O_TIMELENGTH | O_INCOMPLETE), "got %#x\n", output.dwStatus); ok(output.rtTimestamp == 0, "got %s\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == samplelen(written, 48000), "got %s\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_FALSE, "got %#x\n", hr); ok(outbuf.len == written, "expected %u, got %u\n", written, outbuf.len); ok(output.dwStatus == (O_SYNCPOINT | O_INCOMPLETE), "got %#x\n", output.dwStatus); ok(output.rtTimestamp == 0, "got %s\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == samplelen(written, 48000), "got %s\n", wine_dbgstr_longlong(output.rtTimelength)); hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == DMO_E_NOTACCEPTING, "got %#x\n", hr); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); /* write the rest */ outbuf.len = 0; outbuf.maxlen = 5000; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_OK, "got %#x\n", hr); ok(written + outbuf.len == (1152 * 5) || broken(written + outbuf.len == (1152 * 5) - 528), /* Win10 */ "got %u, total %u\n", outbuf.len, written + outbuf.len); ok(output.dwStatus == (O_SYNCPOINT | O_TIME | O_TIMELENGTH), "got %#x\n", output.dwStatus); ok(output.rtTimestamp == samplelen(written, 48000), "got %s\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == samplelen(outbuf.len, 48000), "got %s\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); written += outbuf.len; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_FALSE, "got %#x\n", hr); ok(output.dwStatus == 0, "got %#x\n", output.dwStatus); output.pBuffer = NULL; output.dwStatus = 0xdeadbeef; output.rtTimestamp = 0xdeadbeef; output.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(dmo, DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, 1, &output, &status); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!output.pBuffer, "Got buffer %p.\n", output.pBuffer); ok(!output.dwStatus, "Got status %#x.\n", output.dwStatus); ok(output.rtTimestamp == 0xdeadbeef, "Got timestamp %s.\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == 0xdeadbeef, "Got length %s.\n", wine_dbgstr_longlong(output.rtTimelength)); hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == S_OK, "got %#x\n", hr); hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == DMO_E_NOTACCEPTING, "Got hr %#x.\n", hr); output.pBuffer = NULL; output.dwStatus = 0xdeadbeef; output.rtTimestamp = 0xdeadbeef; output.rtTimelength = 0xdeadbeef; hr = IMediaObject_ProcessOutput(dmo, DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, 1, &output, &status); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!output.pBuffer, "Got buffer %p.\n", output.pBuffer); ok(output.dwStatus == O_INCOMPLETE, "Got status %#x.\n", output.dwStatus); ok(output.rtTimestamp == 0xdeadbeef, "Got timestamp %s.\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == 0xdeadbeef, "Got length %s.\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!output.pBuffer, "Got buffer %p.\n", output.pBuffer); ok(output.dwStatus == O_INCOMPLETE, "Got status %#x.\n", output.dwStatus); ok(output.rtTimestamp == 0xdeadbeef, "Got timestamp %s.\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == 0xdeadbeef, "Got length %s.\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); output.pBuffer = &outbuf.IMediaBuffer_iface; outbuf.len = 0; outbuf.maxlen = 5000; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(outbuf.len > 1152 && outbuf.len <= 5000, "Got length %u.\n", outbuf.len); ok(output.dwStatus == (O_SYNCPOINT | O_TIME | O_TIMELENGTH | O_INCOMPLETE), "Got status %#x.\n", output.dwStatus); ok(output.rtTimestamp == samplelen(written, 48000), "Got timestamp %s.\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == samplelen(outbuf.len, 48000), "Got length %s.\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); hr = IMediaObject_Flush(dmo); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(inbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); outbuf.len = 0; outbuf.maxlen = 5000; hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_FALSE, "Got hr %#x.\n", hr); hr = IMediaObject_Flush(dmo); ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaObject_ProcessInput(dmo, 0, &inbuf.IMediaBuffer_iface, 0, 0, 0); ok(hr == S_OK, "got %#x\n", hr); hr = IMediaObject_ProcessOutput(dmo, 0, 1, &output, &status); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(outbuf.len > 1152 && outbuf.len <= 5000, "Got length %u.\n", outbuf.len); ok(output.dwStatus == (O_SYNCPOINT | O_TIME | O_TIMELENGTH | O_INCOMPLETE), "Got status %#x.\n", output.dwStatus); ok(!output.rtTimestamp, "Got timestamp %s.\n", wine_dbgstr_longlong(output.rtTimestamp)); ok(output.rtTimelength == samplelen(outbuf.len, 48000), "Got length %s.\n", wine_dbgstr_longlong(output.rtTimelength)); ok(inbuf.refcount == 2, "Got refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got refcount %d.\n", inbuf.refcount); IMediaObject_Release(dmo); ok(inbuf.refcount == 1, "Got outstanding refcount %d.\n", inbuf.refcount); ok(outbuf.refcount == 1, "Got outstanding refcount %d.\n", outbuf.refcount); } static const GUID IID_test_outer = {0xdeadbeef,0,0,{0,0,0,0,0,0,0,0x66}}; static HRESULT WINAPI Outer_QueryInterface(IUnknown *iface, REFIID iid, void **obj) { if (IsEqualGUID(iid, &IID_test_outer)) { *obj = (IUnknown *)0xdeadbeef; return S_OK; } ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid)); return E_NOINTERFACE; } static ULONG WINAPI Outer_AddRef(IUnknown *iface) { return 2; } static ULONG WINAPI Outer_Release(IUnknown *iface) { return 1; } static IUnknownVtbl Outer_vtbl = { Outer_QueryInterface, Outer_AddRef, Outer_Release, }; static IUnknown Outer = { &Outer_vtbl }; static void test_aggregation(void) { IUnknown *unk, *unk2; IMediaObject *dmo; HRESULT hr; hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, &Outer, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk); ok(hr == S_OK, "got %#x\n", hr); hr = IUnknown_QueryInterface(unk, &IID_IMediaObject, (void **)&dmo); ok(hr == S_OK, "got %#x\n", hr); hr = IMediaObject_QueryInterface(dmo, &IID_test_outer, (void **)&unk2); ok(hr == S_OK, "got %#x\n", hr); ok(unk2 == (IUnknown *)0xdeadbeef, "got unk %p\n", unk2); IUnknown_Release(dmo); IUnknown_Release(unk); hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, &Outer, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&unk); ok(hr == E_NOINTERFACE, "got %#x\n", hr); } static void test_stream_info(void) { MPEGLAYER3WAVEFORMAT input_format = { .wfx.nSamplesPerSec = 48000, }; DMO_MEDIA_TYPE input_mt = { .majortype = MEDIATYPE_Audio, .subtype = WMMEDIASUBTYPE_MP3, .formattype = FORMAT_WaveFormatEx, .cbFormat = sizeof(input_format), .pbFormat = (BYTE *)&input_format, }; WAVEFORMATEX output_format = { .nSamplesPerSec = 48000, }; DMO_MEDIA_TYPE output_mt = { .formattype = FORMAT_WaveFormatEx, .cbFormat = sizeof(output_format), .pbFormat = (BYTE *)&output_format, }; DWORD input_count, output_count, flags, size, lookahead, alignment; WORD channels, depth; IMediaObject *dmo; HRESULT hr; hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo); ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaObject_GetStreamCount(dmo, &input_count, &output_count); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(input_count == 1, "Got input count %u.\n", input_count); ok(output_count == 1, "Got output count %u.\n", output_count); flags = 0xdeadbeef; hr = IMediaObject_GetInputStreamInfo(dmo, 0, &flags); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!flags, "Got flags %#x.\n", flags); flags = 0xdeadbeef; hr = IMediaObject_GetOutputStreamInfo(dmo, 0, &flags); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!flags, "Got flags %#x.\n", flags); hr = IMediaObject_GetInputSizeInfo(dmo, 0, &size, &lookahead, &alignment); ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#x.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#x.\n", hr); for (channels = 1; channels <= 2; ++channels) { input_format.wfx.nChannels = channels; output_format.nChannels = channels; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, 0); ok(hr == S_OK, "Got hr %#x.\n", hr); hr = IMediaObject_GetInputSizeInfo(dmo, 0, &size, &lookahead, &alignment); ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#x.\n", hr); hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#x.\n", hr); for (depth = 8; depth <= 16; depth += 8) { output_format.wBitsPerSample = depth; output_format.nBlockAlign = channels * depth / 8; output_format.nAvgBytesPerSec = 48000 * output_format.nBlockAlign; hr = IMediaObject_SetOutputType(dmo, 0, &output_mt, 0); ok(hr == S_OK, "Got hr %#x.\n", hr); size = lookahead = alignment = 0xdeadbeef; hr = IMediaObject_GetInputSizeInfo(dmo, 0, &size, &lookahead, &alignment); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(!size || broken(size == output_format.nBlockAlign) /* Vista */, "Got size %u for %u channels, depth %u.\n", size, channels, depth); ok(lookahead == 0xdeadbeef, "Got lookahead %u.\n", lookahead); ok(alignment == 1, "Got alignment %u.\n", alignment); size = alignment = 0xdeadbeef; hr = IMediaObject_GetOutputSizeInfo(dmo, 0, &size, &alignment); ok(hr == S_OK, "Got hr %#x.\n", hr); /* Vista returns the expected size; all later versions act as if * channels == 2 for some reason. */ ok(size >= channels * 1152 * depth / 8, "Got size %u for %u channels, depth %u.\n", size, channels, depth); ok(alignment == 1, "Got alignment %u.\n", alignment); } hr = IMediaObject_SetOutputType(dmo, 0, &output_mt, DMO_SET_TYPEF_CLEAR); ok(hr == S_OK, "Got hr %#x.\n", hr); } IMediaObject_Release(dmo); } static void test_media_types(void) { MPEGLAYER3WAVEFORMAT mp3fmt = { .wfx.nChannels = 2, .wfx.nSamplesPerSec = 48000, }; DMO_MEDIA_TYPE input_mt = { .majortype = MEDIATYPE_Audio, .subtype = WMMEDIASUBTYPE_MP3, .formattype = FORMAT_WaveFormatEx, .cbFormat = sizeof(mp3fmt), .pbFormat = (BYTE *)&mp3fmt, }; WAVEFORMATEX expect_wfx = { .wFormatTag = WAVE_FORMAT_PCM, .nSamplesPerSec = 48000, }; WAVEFORMATEX output_format = { .nChannels = 1, .nSamplesPerSec = 48000, .nAvgBytesPerSec = 48000, .nBlockAlign = 1, .wBitsPerSample = 8, }; DMO_MEDIA_TYPE output_mt = { .formattype = FORMAT_WaveFormatEx, .cbFormat = sizeof(output_format), .pbFormat = (BYTE *)&output_format, }; DMO_MEDIA_TYPE mt; IMediaObject *dmo; HRESULT hr; DWORD i; hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo); ok(hr == S_OK, "Got hr %#x.\n", hr); memset(&mt, 0xcc, sizeof(DMO_MEDIA_TYPE)); hr = IMediaObject_GetInputType(dmo, 0, 0, &mt); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Audio), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype)); ok(IsEqualGUID(&mt.subtype, &WMMEDIASUBTYPE_MP3), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); ok(mt.bFixedSizeSamples == 0xcccccccc, "Got fixed size %d.\n", mt.bFixedSizeSamples); ok(mt.bTemporalCompression == 0xcccccccc, "Got temporal compression %d.\n", mt.bTemporalCompression); ok(mt.lSampleSize == 0xcccccccc, "Got sample size %u.\n", mt.lSampleSize); ok(IsEqualGUID(&mt.formattype, &GUID_NULL), "Got format type %s.\n", wine_dbgstr_guid(&mt.formattype)); ok(!mt.pUnk, "Got pUnk %p.\n", mt.pUnk); ok(!mt.cbFormat, "Got format size %u.\n", mt.cbFormat); ok(!mt.pbFormat, "Got format block %p.\n", mt.pbFormat); hr = IMediaObject_GetInputType(dmo, 0, 1, &mt); ok(hr == DMO_E_NO_MORE_ITEMS, "Got hr %#x.\n", hr); memset(&mt, 0xcc, sizeof(DMO_MEDIA_TYPE)); hr = IMediaObject_GetOutputType(dmo, 0, 0, &mt); ok(hr == DMO_E_TYPE_NOT_SET, "Got hr %#x.\n", hr); hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "Got hr %#x.\n", hr); input_mt.majortype = GUID_NULL; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.majortype = MEDIATYPE_Stream; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.majortype = MEDIATYPE_Audio; input_mt.subtype = GUID_NULL; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.subtype = MEDIASUBTYPE_PCM; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.subtype = WMMEDIASUBTYPE_MP3; input_mt.formattype = GUID_NULL; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.formattype = FORMAT_None; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); input_mt.formattype = FORMAT_WaveFormatEx; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, 0); ok(hr == S_OK, "Got hr %#x.\n", hr); for (i = 0; i < 4; ++i) { memset(&mt, 0xcc, sizeof(DMO_MEDIA_TYPE)); hr = IMediaObject_GetOutputType(dmo, 0, i, &mt); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Audio), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype)); ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_PCM), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); ok(mt.bFixedSizeSamples == 0xcccccccc, "Got fixed size %d.\n", mt.bFixedSizeSamples); ok(mt.bTemporalCompression == 0xcccccccc, "Got temporal compression %d.\n", mt.bTemporalCompression); ok(mt.lSampleSize == 0xcccccccc, "Got sample size %u.\n", mt.lSampleSize); ok(IsEqualGUID(&mt.formattype, &FORMAT_WaveFormatEx), "Got format type %s.\n", wine_dbgstr_guid(&mt.formattype)); ok(!mt.pUnk, "Got pUnk %p.\n", mt.pUnk); ok(mt.cbFormat >= sizeof(WAVEFORMATEX), "Got format size %u.\n", mt.cbFormat); ok(!!mt.pbFormat, "Got format block %p.\n", mt.pbFormat); expect_wfx.nChannels = (i / 2) ? 1 : 2; expect_wfx.wBitsPerSample = (i % 2) ? 8 : 16; expect_wfx.nBlockAlign = expect_wfx.nChannels * expect_wfx.wBitsPerSample / 8; expect_wfx.nAvgBytesPerSec = 48000 * expect_wfx.nBlockAlign; ok(!memcmp(mt.pbFormat, &expect_wfx, sizeof(WAVEFORMATEX)), "Format blocks didn't match.\n"); MoFreeMediaType(&mt); } hr = IMediaObject_GetOutputType(dmo, 0, 4, &mt); ok(hr == DMO_E_NO_MORE_ITEMS, "Got hr %#x.\n", hr); mp3fmt.wfx.nChannels = 1; hr = IMediaObject_SetInputType(dmo, 0, &input_mt, 0); ok(hr == S_OK, "Got hr %#x.\n", hr); for (i = 0; i < 2; ++i) { memset(&mt, 0xcc, sizeof(DMO_MEDIA_TYPE)); hr = IMediaObject_GetOutputType(dmo, 0, i, &mt); ok(hr == S_OK, "Got hr %#x.\n", hr); ok(IsEqualGUID(&mt.majortype, &MEDIATYPE_Audio), "Got major type %s.\n", wine_dbgstr_guid(&mt.majortype)); ok(IsEqualGUID(&mt.subtype, &MEDIASUBTYPE_PCM), "Got subtype %s.\n", wine_dbgstr_guid(&mt.subtype)); ok(mt.bFixedSizeSamples == 0xcccccccc, "Got fixed size %d.\n", mt.bFixedSizeSamples); ok(mt.bTemporalCompression == 0xcccccccc, "Got temporal compression %d.\n", mt.bTemporalCompression); ok(mt.lSampleSize == 0xcccccccc, "Got sample size %u.\n", mt.lSampleSize); ok(IsEqualGUID(&mt.formattype, &FORMAT_WaveFormatEx), "Got format type %s.\n", wine_dbgstr_guid(&mt.formattype)); ok(!mt.pUnk, "Got pUnk %p.\n", mt.pUnk); ok(mt.cbFormat >= sizeof(WAVEFORMATEX), "Got format size %u.\n", mt.cbFormat); ok(!!mt.pbFormat, "Got format block %p.\n", mt.pbFormat); expect_wfx.nChannels = 1; expect_wfx.wBitsPerSample = (i % 2) ? 8 : 16; expect_wfx.nBlockAlign = expect_wfx.nChannels * expect_wfx.wBitsPerSample / 8; expect_wfx.nAvgBytesPerSec = 48000 * expect_wfx.nBlockAlign; ok(!memcmp(mt.pbFormat, &expect_wfx, sizeof(WAVEFORMATEX)), "Format blocks didn't match.\n"); MoFreeMediaType(&mt); } hr = IMediaObject_GetOutputType(dmo, 0, 2, &mt); ok(hr == DMO_E_NO_MORE_ITEMS, "Got hr %#x.\n", hr); hr = IMediaObject_SetOutputType(dmo, 0, &output_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == S_OK, "Got hr %#x.\n", hr); output_mt.formattype = GUID_NULL; hr = IMediaObject_SetOutputType(dmo, 0, &output_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); output_mt.formattype = FORMAT_None; hr = IMediaObject_SetOutputType(dmo, 0, &output_mt, DMO_SET_TYPEF_TEST_ONLY); ok(hr == DMO_E_TYPE_NOT_ACCEPTED, "Got hr %#x.\n", hr); output_mt.formattype = FORMAT_WaveFormatEx; IMediaObject_Release(dmo); } START_TEST(mp3dmod) { IMediaObject *dmo; HRESULT hr; CoInitializeEx(NULL, COINIT_MULTITHREADED); hr = CoCreateInstance(&CLSID_CMP3DecMediaObject, NULL, CLSCTX_INPROC_SERVER, &IID_IMediaObject, (void **)&dmo); if (hr == REGDB_E_CLASSNOTREG) { win_skip("CLSID_CMP3DecMediaObject not available\n"); return; } IMediaObject_Release(dmo); test_convert(); test_aggregation(); test_stream_info(); test_media_types(); CoUninitialize(); }