This MR adds the IMFTransform interface to the mp3dmod module so that it can be used as an MFT.
It also calls `MFTRegister` (so the MFT can be found via `MFTEnum`) and also registers a byte stream handler for the mp3 format.
-- v2: mfsrcsnk: Register the MP3 Byte Stream Handler class. mp3dmod: Implement an IMFTransform interface.
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mf/tests/mf_test.h | 2 + dlls/mf/tests/topology.c | 5 +- dlls/mf/tests/transform.c | 14 +- dlls/mp3dmod/Makefile.in | 2 +- dlls/mp3dmod/mp3dmod.c | 545 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 558 insertions(+), 10 deletions(-)
diff --git a/dlls/mf/tests/mf_test.h b/dlls/mf/tests/mf_test.h index a4f330efbcb..f6f281939f7 100644 --- a/dlls/mf/tests/mf_test.h +++ b/dlls/mf/tests/mf_test.h @@ -101,6 +101,7 @@ struct buffer_desc RECT compare_rect; dump_cb dump; SIZE size; + BOOL todo_data; };
struct sample_desc @@ -115,6 +116,7 @@ struct sample_desc BOOL todo_length; BOOL todo_duration; BOOL todo_time; + BOOL todo_data; };
#define check_mf_sample_collection(a, b, c) check_mf_sample_collection_(__FILE__, __LINE__, a, b, c, FALSE) diff --git a/dlls/mf/tests/topology.c b/dlls/mf/tests/topology.c index 4b2e83d7ee4..9070098a60a 100644 --- a/dlls/mf/tests/topology.c +++ b/dlls/mf/tests/topology.c @@ -2268,7 +2268,7 @@ static void test_topology_loader(void) ATTR_UINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100), ATTR_UINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 2 * 4 * 44100), ATTR_UINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 4 * 8), - ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3), + ATTR_UINT32(MF_MT_AUDIO_CHANNEL_MASK, 3, .todo = TRUE), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), }; static const media_type_desc video_i420_1280 = @@ -2457,14 +2457,12 @@ static void test_topology_loader(void) .input_type = &audio_mp3_44100, .output_type = &audio_pcm_44100, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, - .flags = LOADER_TODO, }, { /* MP3 -> PCM, need both decoder and converter */ .input_type = &audio_mp3_44100, .output_type = &audio_float_48000, .sink_method = MF_CONNECT_ALLOW_DECODER, .source_method = -1, .current_input = &audio_mp3_44100, .decoded_type = &audio_float_44100_stereo, .expected_result = S_OK, .decoder_class = CLSID_CMP3DecMediaObject, .converter_class = CLSID_CResamplerMediaObject, - .flags = LOADER_TODO, },
{ @@ -2776,7 +2774,6 @@ todo_wine {
hr = IMFTopology_GetNodeCount(full_topology, &node_count); ok(hr == S_OK, "Failed to get node count, hr %#lx.\n", hr); - todo_wine_if(IsEqualGUID(&test->decoder_class, &CLSID_CMP3DecMediaObject)) ok(node_count == count, "Unexpected node count %u.\n", node_count);
hr = IMFTopologyNode_GetTopoNodeID(src_node, &node_id); diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index 507c58906d3..439d8ee9d54 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -1345,7 +1345,7 @@ static DWORD check_mf_media_buffer_(const char *file, int line, IMFMediaBuffer * if (*expect_data) { if (*expect_data_len < length) - todo_wine_if(expect->todo_length) + todo_wine_if(expect->todo_length || expect->todo_data) ok_(file, line)(0, "missing %#lx bytes\n", length - *expect_data_len); else if (!expect->compare) diff = compare_bytes(data, &length, NULL, NULL, *expect_data); @@ -1430,6 +1430,7 @@ static DWORD check_mf_sample_(const char *file, int line, IMFSample *sample, con todo_wine_if(expect->todo_length) ok_(file, line)(total_length == expect_length, "got total length %#lx\n", total_length); + todo_wine_if(expect->todo_data) ok_(file, line)(!*expect_data || *expect_data_len >= expect_length, "missing %#lx data\n", expect_length - *expect_data_len);
@@ -9120,8 +9121,9 @@ static void test_mp3_decoder(void)
const struct buffer_desc output_buffer_desc[] = { - {.length = 0x9c0, .compare = compare_pcm16}, + {.length = 0x9c0, .compare = compare_pcm16, .todo_length = TRUE}, {.length = mp3dec_block_size, .compare = compare_pcm16}, + {.length = mp3dec_block_size, .compare = compare_pcm16, .todo_data = TRUE}, }; const struct attribute_desc output_sample_attributes[] = { @@ -9135,16 +9137,19 @@ static void test_mp3_decoder(void) .attributes = output_sample_attributes + 0, .sample_time = 0, .sample_duration = 282993, .buffer_count = 1, .buffers = output_buffer_desc + 0, + .todo_length = TRUE, .todo_duration = TRUE, }, { .attributes = output_sample_attributes + 0, .sample_time = 282993, .sample_duration = 522449, .buffer_count = 1, .buffers = output_buffer_desc + 1, .repeat_count = 18, + .todo_time = TRUE, }, { .attributes = output_sample_attributes + 1, /* not MFT_OUTPUT_DATA_BUFFER_INCOMPLETE */ .sample_time = 10209524, .sample_duration = 522449, - .buffer_count = 1, .buffers = output_buffer_desc + 1, + .buffer_count = 1, .buffers = output_buffer_desc + 2, + .todo_time = TRUE, .todo_data = TRUE, }, };
@@ -9198,9 +9203,7 @@ static void test_mp3_decoder(void) ok(ret == 0, "Release returned %lu\n", ret); winetest_pop_context(); } - todo_wine ok(hr == MF_E_NO_MORE_TYPES, "GetInputAvailableType returned %#lx\n", hr); - todo_wine ok(i == ARRAY_SIZE(expect_available_inputs), "%lu input media types\n", i);
/* setting output media type first doesn't work */ @@ -9295,6 +9298,7 @@ static void test_mp3_decoder(void) else { ret = check_mf_sample_collection(output_samples, output_sample_desc, L"mp3decdata.bin"); + todo_wine ok(ret == 0, "got %lu%% diff\n", ret); } IMFCollection_Release(output_samples); diff --git a/dlls/mp3dmod/Makefile.in b/dlls/mp3dmod/Makefile.in index ff2f2f24116..254174d4d31 100644 --- a/dlls/mp3dmod/Makefile.in +++ b/dlls/mp3dmod/Makefile.in @@ -1,5 +1,5 @@ MODULE = mp3dmod.dll -IMPORTS = $(MPG123_PE_LIBS) dmoguids msdmo ole32 uuid wmcodecdspuuid kernelbase +IMPORTS = $(MPG123_PE_LIBS) dmoguids mfplat mfuuid msdmo ole32 uuid wmcodecdspuuid kernelbase EXTRAINCL = $(MPG123_PE_CFLAGS) EXTRADEFS = -DMPG123_NO_LARGENAME
diff --git a/dlls/mp3dmod/mp3dmod.c b/dlls/mp3dmod/mp3dmod.c index 211d4ac63a9..82da02bfaee 100644 --- a/dlls/mp3dmod/mp3dmod.c +++ b/dlls/mp3dmod/mp3dmod.c @@ -30,6 +30,9 @@ #include "dmo.h" #include "rpcproxy.h" #include "wmcodecdsp.h" +#include "mfapi.h" +#include "mferror.h" +#include "mftransform.h" #include "wine/debug.h"
#include "initguid.h" @@ -44,6 +47,7 @@ struct mp3_decoder { IUnknown IUnknown_inner; IMediaObject IMediaObject_iface; + IMFTransform IMFTransform_iface; IUnknown *outer; LONG ref; mpg123_handle *mh; @@ -53,6 +57,8 @@ struct mp3_decoder
IMediaBuffer *buffer; REFERENCE_TIME timestamp; + + BOOL draining; };
static inline struct mp3_decoder *impl_from_IUnknown(IUnknown *iface) @@ -70,6 +76,8 @@ static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID iid, void * *obj = &This->IUnknown_inner; else if (IsEqualGUID(iid, &IID_IMediaObject)) *obj = &This->IMediaObject_iface; + else if (IsEqualGUID(iid, &IID_IMFTransform)) + *obj = &This->IMFTransform_iface; else { FIXME("no interface for %s\n", debugstr_guid(iid)); @@ -634,6 +642,532 @@ static const IMediaObjectVtbl MediaObject_vtbl = { MediaObject_Lock, };
+static HRESULT convert_dmo_to_mf_error(HRESULT dmo_error) +{ + switch (dmo_error) + { + case DMO_E_INVALIDSTREAMINDEX: + return MF_E_INVALIDSTREAMNUMBER; + + case DMO_E_INVALIDTYPE: + case DMO_E_TYPE_NOT_ACCEPTED: + return MF_E_INVALIDMEDIATYPE; + + case DMO_E_TYPE_NOT_SET: + return MF_E_TRANSFORM_TYPE_NOT_SET; + + case DMO_E_NOTACCEPTING: + return MF_E_NOTACCEPTING; + + case DMO_E_NO_MORE_ITEMS: + return MF_E_NO_MORE_TYPES; + } + + return dmo_error; +} + +static inline struct mp3_decoder *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct mp3_decoder, IMFTransform_iface); +} + +static HRESULT WINAPI MFTransform_QueryInterface(IMFTransform *iface, REFIID iid, void **obj) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + return IUnknown_QueryInterface(decoder->outer, iid, obj); +} + +static ULONG WINAPI MFTransform_AddRef(IMFTransform *iface) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + return IUnknown_AddRef(decoder->outer); +} + +static ULONG WINAPI MFTransform_Release(IMFTransform *iface) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + return IUnknown_Release(decoder->outer); +} + +static HRESULT WINAPI MFTransform_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, + DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n", + iface, input_minimum, input_maximum, output_minimum, output_maximum); + IMediaObject_GetStreamCount(&decoder->IMediaObject_iface, input_minimum, output_minimum); + *input_maximum = *input_minimum; + *output_maximum = *output_minimum; + return S_OK; +} + +static HRESULT WINAPI MFTransform_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs); + IMediaObject_GetStreamCount(&decoder->IMediaObject_iface, inputs, outputs); + return S_OK; +} + +static HRESULT WINAPI MFTransform_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + FIXME("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p stub!\n", iface, + input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + HRESULT hr; + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + memset(info, 0, sizeof(*info)); + + /* Doc says: If not implemented, assume zero latency. */ + if (FAILED(hr = IMediaObject_GetInputMaxLatency(&decoder->IMediaObject_iface, id, &info->hnsMaxLatency)) && hr != E_NOTIMPL) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = IMediaObject_GetInputStreamInfo(&decoder->IMediaObject_iface, id, &info->dwFlags))) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = IMediaObject_GetInputSizeInfo(&decoder->IMediaObject_iface, id, &info->cbSize, &info->cbMaxLookahead, &info->cbAlignment))) + return convert_dmo_to_mf_error(hr); + + return S_OK; +} + +static HRESULT WINAPI MFTransform_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + HRESULT hr; + + TRACE("iface %p, id %#lx, info %p.\n", iface, id, info); + + memset(info, 0, sizeof(*info)); + + if (FAILED(hr = IMediaObject_GetOutputStreamInfo(&decoder->IMediaObject_iface, id, &info->dwFlags))) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = IMediaObject_GetOutputSizeInfo(&decoder->IMediaObject_iface, id, &info->cbSize, &info->cbAlignment))) + return convert_dmo_to_mf_error(hr); + + return S_OK; +} + +static HRESULT WINAPI MFTransform_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + TRACE("iface %p, attributes %p.\n", iface, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes) +{ + TRACE("iface %p, id %#lx, attributes %p.\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + TRACE("iface %p, id %#lx.\n", iface, id); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + TRACE("iface %p, streams %lu, ids %p.\n", iface, streams, ids); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_MEDIA_TYPE pt; + HRESULT hr; + + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + + if (FAILED(hr = IMediaObject_GetInputType(&decoder->IMediaObject_iface, id, index, &pt))) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = MFCreateMediaType(type))) + return hr; + + return MFInitMediaTypeFromAMMediaType(*type, (AM_MEDIA_TYPE*)&pt); +} + +static HRESULT WINAPI MFTransform_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 bps, block_align, num_channels; + const WAVEFORMATEX *input_format; + HRESULT hr; + + TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type); + + if (id) + return MF_E_INVALIDSTREAMNUMBER; + + if (!decoder->intype_set) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + input_format = (WAVEFORMATEX *)decoder->intype.pbFormat; + + if (index >= 6 || (input_format->nChannels != 2 && index >= 3)) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(*type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetGUID(*type, &MF_MT_SUBTYPE, index % 3 ? &MFAudioFormat_PCM : &MFAudioFormat_Float))) + goto fail; + + if (index % 3 == 0) + bps = 32; + else if (index % 3 == 1) + bps = 16; + else + bps = 8; + + num_channels = index / 3 ? 1 : input_format->nChannels; + block_align = bps/8 * num_channels; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bps))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_AUDIO_NUM_CHANNELS, num_channels))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, input_format->nSamplesPerSec))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, input_format->nSamplesPerSec * block_align))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, block_align))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(*type, &MF_MT_ALL_SAMPLES_INDEPENDENT, 1))) + goto fail; + + return S_OK; + +fail: + IMFMediaType_Release(*type); + return hr; +} +static HRESULT WINAPI MFTransform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_MEDIA_TYPE mt; + HRESULT hr; + + TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); + + if (!type) + flags |= DMO_SET_TYPEF_CLEAR; + else if (FAILED(hr = MFInitAMMediaTypeFromMFMediaType(type, GUID_NULL, (AM_MEDIA_TYPE*)&mt))) + return hr; + + hr = IMediaObject_SetInputType(&decoder->IMediaObject_iface, id, &mt, flags); + + if (hr == S_FALSE) + return MF_E_INVALIDMEDIATYPE; + else if (FAILED(hr)) + return convert_dmo_to_mf_error(hr); + + return S_OK; +} + +static HRESULT WINAPI MFTransform_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_MEDIA_TYPE mt; + HRESULT hr; + + TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags); + + if (!type) + flags |= DMO_SET_TYPEF_CLEAR; + else if (FAILED(hr = MFInitAMMediaTypeFromMFMediaType(type, GUID_NULL, (AM_MEDIA_TYPE*)&mt))) + return hr; + + hr = IMediaObject_SetOutputType(&decoder->IMediaObject_iface, id, &mt, flags); + + if (hr == S_FALSE) + return MF_E_INVALIDMEDIATYPE; + else if (FAILED(hr)) + return convert_dmo_to_mf_error(hr); + + return S_OK; +} + +static HRESULT WINAPI MFTransform_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **out) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_MEDIA_TYPE mt; + HRESULT hr; + + TRACE("iface %p, id %#lx, out %p.\n", iface, id, out); + + if (FAILED(hr = IMediaObject_GetInputCurrentType(&decoder->IMediaObject_iface, id, &mt))) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = MFCreateMediaType(out))) + return hr; + + return MFInitMediaTypeFromAMMediaType(*out, (AM_MEDIA_TYPE*)&mt); +} + +static HRESULT WINAPI MFTransform_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **out) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_MEDIA_TYPE mt; + HRESULT hr; + + TRACE("iface %p, id %#lx, out %p.\n", iface, id, out); + + if (FAILED(hr = IMediaObject_GetOutputCurrentType(&decoder->IMediaObject_iface, id, &mt))) + return convert_dmo_to_mf_error(hr); + + if (FAILED(hr = MFCreateMediaType(out))) + return hr; + + return MFInitMediaTypeFromAMMediaType(*out, (AM_MEDIA_TYPE*)&mt); +} + +static HRESULT WINAPI MFTransform_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + + FIXME("iface %p, id %#lx, flags %p stub!\n", iface, id, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("iface %p, flags %p stub!\n", iface, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("iface %p, lower %I64d, upper %I64d stub!\n", iface, lower, upper); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("iface %p, id %#lx, event %p stub!\n", iface, id, event); + return E_NOTIMPL; +} + +static HRESULT WINAPI MFTransform_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + HRESULT hr; + + TRACE("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + + switch(message) + { + case MFT_MESSAGE_COMMAND_FLUSH: + if (FAILED(hr = IMediaObject_Flush(&decoder->IMediaObject_iface))) + return convert_dmo_to_mf_error(hr); + break; + case MFT_MESSAGE_COMMAND_DRAIN: + decoder->draining = TRUE; + break; + + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + if (FAILED(hr = IMediaObject_AllocateStreamingResources(&decoder->IMediaObject_iface))) + return convert_dmo_to_mf_error(hr); + break; + + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + case MFT_MESSAGE_NOTIFY_END_STREAMING: + if (FAILED(hr = IMediaObject_FreeStreamingResources(&decoder->IMediaObject_iface))) + return convert_dmo_to_mf_error(hr); + break; + + default: + FIXME("message %#x stub!\n", message); + break; + } + + return S_OK; +} + +static HRESULT WINAPI MFTransform_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + LONGLONG sample_time, duration; + IMediaBuffer *dmo_buffer; + IMFMediaBuffer *buffer; + UINT32 discontinuity; + HRESULT hr; + + TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags); + + if (decoder->draining) + return MF_E_NOTACCEPTING; + + if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_Discontinuity, &discontinuity)) && discontinuity) + { + if (FAILED(hr = IMediaObject_Discontinuity(&decoder->IMediaObject_iface, id))) + return convert_dmo_to_mf_error(hr); + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer))) + return hr; + + hr = MFCreateLegacyMediaBufferOnMFMediaBuffer(sample, buffer, 0, &dmo_buffer); + IMFMediaBuffer_Release(buffer); + if (FAILED(hr)) + return hr; + + flags = 0; + + if (SUCCEEDED(IMFSample_GetSampleTime(sample, &sample_time))) + flags |= DMO_INPUT_DATA_BUFFERF_TIME; + + if (SUCCEEDED(IMFSample_GetSampleDuration(sample, &duration))) + flags |= DMO_INPUT_DATA_BUFFERF_TIMELENGTH; + + hr = IMediaObject_ProcessInput(&decoder->IMediaObject_iface, id, dmo_buffer, flags, sample_time, duration); + IMediaBuffer_Release(dmo_buffer); + if (FAILED(hr)) + return convert_dmo_to_mf_error(hr); + + return S_OK; +} + +static HRESULT WINAPI MFTransform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct mp3_decoder *decoder = impl_from_IMFTransform(iface); + DMO_OUTPUT_DATA_BUFFER *dmo_output; + IMFMediaBuffer *buffer; + HRESULT hr; + int i; + + TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status); + + if (count > 1) + { + FIXME("Multiple buffers not handled.\n"); + return E_INVALIDARG; + } + + dmo_output = calloc(count, sizeof(*dmo_output)); + if (!dmo_output) + return E_OUTOFMEMORY; + + for (i = 0; i < count; i++) + { + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[i].pSample, &buffer))) + goto fail; + + hr = MFCreateLegacyMediaBufferOnMFMediaBuffer(samples[i].pSample, buffer, 0, &dmo_output[i].pBuffer); + IMFMediaBuffer_Release(buffer); + if (FAILED(hr)) + goto fail; + } + + hr = IMediaObject_ProcessOutput(&decoder->IMediaObject_iface, flags, count, dmo_output, status); + + for (i = 0; i < count; i++) + IMediaBuffer_Release(dmo_output[i].pBuffer); + + if (FAILED(hr)) + { + free(dmo_output); + return convert_dmo_to_mf_error(hr); + } + + for (i = 0; i < count; i++) + { + if (dmo_output[i].dwStatus & DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT) + IMFSample_SetUINT32(samples[i].pSample, &MFSampleExtension_CleanPoint, 1); + + if (dmo_output[i].dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIME) + IMFSample_SetSampleTime(samples[i].pSample, dmo_output[i].rtTimestamp); + + if (dmo_output[i].dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH) + IMFSample_SetSampleDuration(samples[i].pSample, dmo_output[i].rtTimelength); + + if (dmo_output[i].dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE) + samples[i].dwStatus |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE; + } + + free(dmo_output); + + if (hr == S_FALSE) + { + decoder->draining = FALSE; + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + return S_OK; + +fail: + for (i = 0; i < count; i++) + { + if (dmo_output[i].pBuffer) + IMediaBuffer_Release(dmo_output[i].pBuffer); + } + free(dmo_output); + + return hr; +} + +static const IMFTransformVtbl IMFTransform_vtbl = +{ + MFTransform_QueryInterface, + MFTransform_AddRef, + MFTransform_Release, + MFTransform_GetStreamLimits, + MFTransform_GetStreamCount, + MFTransform_GetStreamIDs, + MFTransform_GetInputStreamInfo, + MFTransform_GetOutputStreamInfo, + MFTransform_GetAttributes, + MFTransform_GetInputStreamAttributes, + MFTransform_GetOutputStreamAttributes, + MFTransform_DeleteInputStream, + MFTransform_AddInputStreams, + MFTransform_GetInputAvailableType, + MFTransform_GetOutputAvailableType, + MFTransform_SetInputType, + MFTransform_SetOutputType, + MFTransform_GetInputCurrentType, + MFTransform_GetOutputCurrentType, + MFTransform_GetInputStatus, + MFTransform_GetOutputStatus, + MFTransform_SetOutputBounds, + MFTransform_ProcessEvent, + MFTransform_ProcessMessage, + MFTransform_ProcessInput, + MFTransform_ProcessOutput, +}; + static HRESULT create_mp3_decoder(IUnknown *outer, REFIID iid, void **obj) { struct mp3_decoder *This; @@ -645,6 +1179,7 @@ static HRESULT create_mp3_decoder(IUnknown *outer, REFIID iid, void **obj)
This->IUnknown_inner.lpVtbl = &Unknown_vtbl; This->IMediaObject_iface.lpVtbl = &MediaObject_vtbl; + This->IMFTransform_iface.lpVtbl = &IMFTransform_vtbl; This->ref = 1; This->outer = outer ? outer : &This->IUnknown_inner;
@@ -733,6 +1268,7 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **obj) */ HRESULT WINAPI DllRegisterServer(void) { + MFT_REGISTER_TYPE_INFO mf_in, mf_out; DMO_PARTIAL_MEDIATYPE in, out; HRESULT hr;
@@ -740,9 +1276,16 @@ HRESULT WINAPI DllRegisterServer(void) in.subtype = WMMEDIASUBTYPE_MP3; out.type = WMMEDIATYPE_Audio; out.subtype = WMMEDIASUBTYPE_PCM; + mf_in.guidMajorType = MFMediaType_Audio; + mf_in.guidSubtype = MFAudioFormat_MP3; + mf_out.guidMajorType = MFMediaType_Audio; + mf_out.guidSubtype = MFAudioFormat_PCM; hr = DMORegister(L"MP3 Decoder DMO", &CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER, 0, 1, &in, 1, &out); if (FAILED(hr)) return hr; + hr = MFTRegister(CLSID_CMP3DecMediaObject, MFT_CATEGORY_AUDIO_DECODER, (LPWSTR) L"MP3 Decoder MFT", + 0, 1, &mf_in, 1, &mf_out, NULL); + if (FAILED(hr)) return hr;
return __wine_register_resources(); } @@ -756,6 +1299,8 @@ HRESULT WINAPI DllUnregisterServer(void)
hr = DMOUnregister(&CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER); if (FAILED(hr)) return hr; + hr = MFTUnregister(CLSID_CMP3DecMediaObject); + if (FAILED(hr)) return hr;
return __wine_unregister_resources(); }
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfsrcsnk/factory.c | 2 ++ dlls/mfsrcsnk/media_source.c | 26 ++++++++++++++++++++++++++ dlls/mfsrcsnk/media_source.h | 1 + dlls/mfsrcsnk/mfsrcsnk.idl | 7 +++++++ dlls/mfsrcsnk/mfsrcsnk.rgs | 13 +++++++++++++ dlls/mp3dmod/mp3dmod.c | 2 ++ include/wine/mfinternal.idl | 1 + 7 files changed, 52 insertions(+)
diff --git a/dlls/mfsrcsnk/factory.c b/dlls/mfsrcsnk/factory.c index c0a02d5fa9d..d8fe4268feb 100644 --- a/dlls/mfsrcsnk/factory.c +++ b/dlls/mfsrcsnk/factory.c @@ -33,6 +33,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) return IClassFactory_QueryInterface(&avi_byte_stream_plugin_factory, riid, out); if (IsEqualGUID(clsid, &CLSID_WAVByteStreamPlugin)) return IClassFactory_QueryInterface(&wav_byte_stream_plugin_factory, riid, out); + if (IsEqualGUID(clsid, &CLSID_MP3ByteStreamPlugin)) + return IClassFactory_QueryInterface(&mp3_byte_stream_plugin_factory, riid, out);
if (IsEqualGUID(clsid, &CLSID_MFWAVESinkClassFactory)) return IClassFactory_QueryInterface(wave_sink_class_factory, riid, out); diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 5fe5a4af6e7..1e7918562fd 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -2008,3 +2008,29 @@ static const IClassFactoryVtbl wav_byte_stream_plugin_factory_vtbl = };
IClassFactory wav_byte_stream_plugin_factory = {&wav_byte_stream_plugin_factory_vtbl}; + +static HRESULT WINAPI mp3_byte_stream_plugin_factory_CreateInstance(IClassFactory *iface, + IUnknown *outer, REFIID riid, void **out) +{ + NTSTATUS status; + + if ((status = winedmo_demuxer_check("audio/mp3")) || use_gst_byte_stream_handler()) + { + static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618,0x5e5a,0x468a,{0x9f,0x15,0xd8,0x27,0xa9,0xa0,0x81,0x62}}; + if (status) WARN("Unsupported demuxer, status %#lx.\n", status); + return CoCreateInstance(&CLSID_GStreamerByteStreamHandler, outer, CLSCTX_INPROC_SERVER, riid, out); + } + + return byte_stream_plugin_create(outer, riid, out); +} + +static const IClassFactoryVtbl mp3_byte_stream_plugin_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + mp3_byte_stream_plugin_factory_CreateInstance, + class_factory_LockServer, +}; + +IClassFactory mp3_byte_stream_plugin_factory = {&mp3_byte_stream_plugin_factory_vtbl}; diff --git a/dlls/mfsrcsnk/media_source.h b/dlls/mfsrcsnk/media_source.h index 1e6c90ea7d5..7703e7634ec 100644 --- a/dlls/mfsrcsnk/media_source.h +++ b/dlls/mfsrcsnk/media_source.h @@ -24,3 +24,4 @@ extern IClassFactory asf_byte_stream_plugin_factory; extern IClassFactory avi_byte_stream_plugin_factory; extern IClassFactory mpeg4_byte_stream_plugin_factory; extern IClassFactory wav_byte_stream_plugin_factory; +extern IClassFactory mp3_byte_stream_plugin_factory; diff --git a/dlls/mfsrcsnk/mfsrcsnk.idl b/dlls/mfsrcsnk/mfsrcsnk.idl index 10b41769060..e6473b355e9 100644 --- a/dlls/mfsrcsnk/mfsrcsnk.idl +++ b/dlls/mfsrcsnk/mfsrcsnk.idl @@ -38,3 +38,10 @@ coclass MFWAVESinkClassFactory { } uuid(42c9b9f5-16fc-47ef-af22-da05f7c842e3) ] coclass WAVByteStreamPlugin {} + +[ + helpstring("MP3 Byte Stream Handler"), + threading(both), + uuid(a82e50ba-8e92-41eb-9df2-433f50ec2993) +] +coclass MP3ByteStreamPlugin {} diff --git a/dlls/mfsrcsnk/mfsrcsnk.rgs b/dlls/mfsrcsnk/mfsrcsnk.rgs index c8272189273..dcdcb165db1 100644 --- a/dlls/mfsrcsnk/mfsrcsnk.rgs +++ b/dlls/mfsrcsnk/mfsrcsnk.rgs @@ -37,6 +37,19 @@ HKLM { val '{42c9b9f5-16fc-47ef-af22-da05f7c842e3}' = s 'WAV Byte Stream Handler' } + + '.mp3' + { + val '{a82e50ba-8e92-41eb-9df2-433f50ec2993}' = s 'MP3 Byte Stream Handler' + } + 'audio/mp3' + { + val '{a82e50ba-8e92-41eb-9df2-433f50ec2993}' = s 'MP3 Byte Stream Handler' + } + 'audio/x-mp3' + { + val '{a82e50ba-8e92-41eb-9df2-433f50ec2993}' = s 'MP3 Byte Stream Handler' + } } } } diff --git a/dlls/mp3dmod/mp3dmod.c b/dlls/mp3dmod/mp3dmod.c index 82da02bfaee..bf518f38f39 100644 --- a/dlls/mp3dmod/mp3dmod.c +++ b/dlls/mp3dmod/mp3dmod.c @@ -872,6 +872,7 @@ fail: IMFMediaType_Release(*type); return hr; } + static HRESULT WINAPI MFTransform_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) { struct mp3_decoder *decoder = impl_from_IMFTransform(iface); @@ -991,6 +992,7 @@ static HRESULT WINAPI MFTransform_ProcessMessage(IMFTransform *iface, MFT_MESSAG if (FAILED(hr = IMediaObject_Flush(&decoder->IMediaObject_iface))) return convert_dmo_to_mf_error(hr); break; + case MFT_MESSAGE_COMMAND_DRAIN: decoder->draining = TRUE; break; diff --git a/include/wine/mfinternal.idl b/include/wine/mfinternal.idl index 3fc915c0437..0b77eaefab1 100644 --- a/include/wine/mfinternal.idl +++ b/include/wine/mfinternal.idl @@ -71,3 +71,4 @@ cpp_quote("DEFINE_GUID(CLSID_AsfByteStreamPlugin, 0x41457294, 0x644c, 0x4298, 0x cpp_quote("DEFINE_GUID(CLSID_AVIByteStreamPlugin, 0x7afa253e, 0xf823, 0x42f6, 0xa5, 0xd9, 0x71, 0x4b, 0xde, 0x46, 0x74, 0x12);") cpp_quote("DEFINE_GUID(CLSID_MPEG4ByteStreamHandlerPlugin, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") cpp_quote("DEFINE_GUID(CLSID_WAVByteStreamPlugin, 0x42c9b9f5, 0x16fc, 0x47ef, 0xaf, 0x22, 0xda, 0x05, 0xf7, 0xc8, 0x42, 0xe3);") +cpp_quote("DEFINE_GUID(CLSID_MP3ByteStreamPlugin, 0xa82e50ba, 0x8e92, 0x41eb, 0x9d, 0xf2, 0x43, 0x3f, 0x50, 0xec, 0x29, 0x93);")
Nikolay Sivov (@nsivov) commented about dlls/mfsrcsnk/media_source.c:
IClassFactory wav_byte_stream_plugin_factory = {&wav_byte_stream_plugin_factory_vtbl};
+static HRESULT WINAPI mp3_byte_stream_plugin_factory_CreateInstance(IClassFactory *iface,
IUnknown *outer, REFIID riid, void **out)
+{
- NTSTATUS status;
- if ((status = winedmo_demuxer_check("audio/mp3")) || use_gst_byte_stream_handler())
- {
static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618,0x5e5a,0x468a,{0x9f,0x15,0xd8,0x27,0xa9,0xa0,0x81,0x62}};
if (status) WARN("Unsupported demuxer, status %#lx.\n", status);
return CoCreateInstance(&CLSID_GStreamerByteStreamHandler, outer, CLSCTX_INPROC_SERVER, riid, out);
- }
- return byte_stream_plugin_create(outer, riid, out);
+}
We might want to consider making this mime string a property of the factory instance, to reduce this duplication by having a single copy of this method.
On Thu Mar 6 07:22:31 2025 +0000, Nikolay Sivov wrote:
We might want to consider making this mime string a property of the factory instance, to reduce this duplication by having a single copy of this method.
Fwiw I considered this but I don't really see how it is better, you would have to define a custom struct for the factory, and have duplicated declarations of this struct with the mime and gstreamer guid still. Duplicating the method reduces the dependencies between the components as they don't need to share a custom struct.
On Thu Mar 6 08:23:35 2025 +0000, Rémi Bernon wrote:
Fwiw I considered this but I don't really see how it is better, you would have to define a custom struct for the factory, and have duplicated declarations of this struct with the mime and gstreamer guid still. Duplicating the method reduces the dependencies between the components as they don't need to share a custom struct.
To me it's better because it would be using some static data and common code instead of same code blocks, but I'm not asking anyone to make this change. Maybe we could have a helper for this if() block still?
This merge request was approved by Elizabeth Figura.