From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/mf.c | 67 ++--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 3 + dlls/winegstreamer/mpeg4_muxer.c | 243 +++++++++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 7 + 6 files changed, 292 insertions(+), 31 deletions(-) create mode 100644 dlls/winegstreamer/mpeg4_muxer.c
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 18488649523..d880a4eb600 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6802,61 +6802,42 @@ static void test_mpeg4_media_sink(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = MFCreateMPEG4MediaSink(NULL, NULL, NULL, NULL); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
sink = (void *)0xdeadbeef; hr = MFCreateMPEG4MediaSink(NULL, NULL, NULL, &sink); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); ok(sink == (void *)0xdeadbeef, "Unexpected pointer %p.\n", sink); sink = NULL;
hr = MFCreateMPEG4MediaSink(bytestream_empty, NULL, NULL, &sink_empty); - todo_wine ok(hr == S_OK || broken(hr == E_INVALIDARG), "Unexpected hr %#lx.\n", hr); - hr = MFCreateMPEG4MediaSink(bytestream_audio, NULL, audio_type, &sink_audio); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateMPEG4MediaSink(bytestream_video, video_type, NULL, &sink_video); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = MFCreateMPEG4MediaSink(bytestream, video_type, audio_type, &sink); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (!sink) - { - if (sink_video) - IMFMediaSink_Release(sink_video); - if (sink_audio) - IMFMediaSink_Release(sink_audio); - if (sink_empty) - IMFMediaSink_Release(sink_empty); - IMFByteStream_Release(bytestream); - IMFByteStream_Release(bytestream_empty); - IMFByteStream_Release(bytestream_video); - IMFByteStream_Release(bytestream_audio); - IMFMediaType_Release(video_type); - IMFMediaType_Release(audio_type); - return; - } - /* Test sink. */ hr = IMFMediaSink_GetCharacteristics(sink, &flags); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(flags == MEDIASINK_RATELESS || broken(flags == (MEDIASINK_RATELESS | MEDIASINK_FIXED_STREAMS)), "Unexpected flags %#lx.\n", flags);
+ todo_wine check_interface(sink, &IID_IMFMediaEventGenerator, TRUE); check_interface(sink, &IID_IMFFinalizableMediaSink, TRUE); + todo_wine check_interface(sink, &IID_IMFClockStateSink, TRUE); + todo_wine check_interface(sink, &IID_IMFGetService, TRUE);
/* Test sink stream count. */ + todo_wine + { hr = IMFMediaSink_GetStreamSinkCount(sink, NULL); ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
@@ -6878,53 +6859,72 @@ static void test_mpeg4_media_sink(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 0, "Unexpected count %lu.\n", count); } + }
/* Test GetStreamSinkByIndex. */ hr = IMFMediaSink_GetStreamSinkByIndex(sink_video, 0, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFStreamSink_GetIdentifier(stream_sink, &id); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(id == 1, "Unexpected id %lu.\n", id); IMFStreamSink_Release(stream_sink);
hr = IMFMediaSink_GetStreamSinkByIndex(sink_audio, 0, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFStreamSink_GetIdentifier(stream_sink, &id); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(id == 2, "Unexpected id %lu.\n", id); IMFStreamSink_Release(stream_sink);
stream_sink = (void *)0xdeadbeef; hr = IMFMediaSink_GetStreamSinkByIndex(sink_audio, 1, &stream_sink); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); ok(stream_sink == (void *)0xdeadbeef, "Unexpected pointer %p.\n", stream_sink);
stream_sink = (void *)0xdeadbeef; hr = IMFMediaSink_GetStreamSinkByIndex(sink_video, 1, &stream_sink); + todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); ok(stream_sink == (void *)0xdeadbeef, "Unexpected pointer %p.\n", stream_sink);
/* Test GetStreamSinkById. */ hr = IMFMediaSink_GetStreamSinkById(sink, 1, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFStreamSink_Release(stream_sink); + if (hr == S_OK) + IMFStreamSink_Release(stream_sink); hr = IMFMediaSink_GetStreamSinkById(sink, 2, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFStreamSink_Release(stream_sink); + if (hr == S_OK) + IMFStreamSink_Release(stream_sink); hr = IMFMediaSink_GetStreamSinkById(sink_video, 1, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFStreamSink_Release(stream_sink); + if (hr == S_OK) + IMFStreamSink_Release(stream_sink); hr = IMFMediaSink_GetStreamSinkById(sink_audio, 2, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - IMFStreamSink_Release(stream_sink); + if (hr == S_OK) + IMFStreamSink_Release(stream_sink);
stream_sink = (void *)0xdeadbeef; hr = IMFMediaSink_GetStreamSinkById(sink_video, 2, &stream_sink); + todo_wine ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); ok(stream_sink == (void *)0xdeadbeef, "Unexpected pointer %p.\n", stream_sink);
stream_sink = (void *)0xdeadbeef; hr = IMFMediaSink_GetStreamSinkById(sink_audio, 1, &stream_sink); + todo_wine ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); ok(stream_sink == (void *)0xdeadbeef, "Unexpected pointer %p.\n", stream_sink);
@@ -6932,7 +6932,10 @@ static void test_mpeg4_media_sink(void) if (!(flags & MEDIASINK_FIXED_STREAMS)) { hr = IMFMediaSink_AddStreamSink(sink, 123, video_type, &stream_sink); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (hr != S_OK) + goto done; IMFStreamSink_Release(stream_sink); hr = IMFMediaSink_GetStreamSinkByIndex(sink, 2, &stream_sink); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -7039,7 +7042,9 @@ static void test_mpeg4_media_sink(void) hr = IMFMediaSink_GetCharacteristics(sink, &flags); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
- IMFMediaTypeHandler_Release(type_handler); +done: + if (type_handler) + IMFMediaTypeHandler_Release(type_handler); IMFMediaSink_Release(sink); IMFMediaSink_Release(sink_video); IMFMediaSink_Release(sink_audio); diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 1c701bfa9f6..c61fc3f77dc 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -13,6 +13,7 @@ C_SRCS = \ main.c \ media_source.c \ mfplat.c \ + mpeg4_muxer.c \ quartz_parser.c \ quartz_transform.c \ resampler.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 54f59aa708a..1e3461cb14f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -35,6 +35,7 @@ #include "mfidl.h" #include "wine/debug.h" #include "wine/strmbase.h" +#include "wine/mfinternal.h"
#include "unixlib.h"
@@ -119,6 +120,7 @@ HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out); HRESULT wmv_decoder_create(IUnknown *outer, IUnknown **out); HRESULT resampler_create(IUnknown *outer, IUnknown **out); HRESULT color_convert_create(IUnknown *outer, IUnknown **out); +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out);
bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 2e7763872d0..4e091536b74 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -582,6 +582,7 @@ static struct class_factory wma_decoder_cf = {{&class_factory_vtbl}, wma_decoder static struct class_factory wmv_decoder_cf = {{&class_factory_vtbl}, wmv_decoder_create}; static struct class_factory resampler_cf = {{&class_factory_vtbl}, resampler_create}; static struct class_factory color_convert_cf = {{&class_factory_vtbl}, color_convert_create}; +static struct class_factory mpeg4_sink_class_factory_cf = {{&class_factory_vtbl}, mpeg4_sink_class_factory_create};
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) { @@ -616,6 +617,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) factory = &resampler_cf; else if (IsEqualGUID(clsid, &CLSID_CColorConvertDMO)) factory = &color_convert_cf; + else if (IsEqualGUID(clsid, &CLSID_MFMPEG4SinkClassFactory)) + factory = &mpeg4_sink_class_factory_cf; else { FIXME("%s not implemented, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); diff --git a/dlls/winegstreamer/mpeg4_muxer.c b/dlls/winegstreamer/mpeg4_muxer.c new file mode 100644 index 00000000000..0d3406dfbc3 --- /dev/null +++ b/dlls/winegstreamer/mpeg4_muxer.c @@ -0,0 +1,243 @@ +/* MPEG4 Muxer + * + * Copyright 2023 Ziqing Hui for CodeWeavers + * + * 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 + */ + +#include "gst_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct mpeg4_media_sink +{ + IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + LONG refcount; + IMFByteStream *bytestream; +}; + +static struct mpeg4_media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct mpeg4_media_sink, IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI mpeg4_media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) || + IsEqualIID(riid, &IID_IMFMediaSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else + { + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI mpeg4_media_sink_AddRef(IMFFinalizableMediaSink *iface) +{ + struct mpeg4_media_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedIncrement(&sink->refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI mpeg4_media_sink_Release(IMFFinalizableMediaSink *iface) +{ + struct mpeg4_media_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedDecrement(&sink->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFByteStream_Release(sink->bytestream); + free(sink); + } + + return refcount; +} + +static HRESULT WINAPI mpeg4_media_sink_GetCharacteristics(IMFFinalizableMediaSink *iface, DWORD *flags) +{ + FIXME("%p, %p stub!\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + FIXME("%p, %#lx, %p, %p stub!\n", iface, stream_sink_id, media_type, stream_sink); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) +{ + FIXME("%p, %#lx stub!\n", iface, stream_sink_id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_GetStreamSinkCount(IMFFinalizableMediaSink *iface, DWORD *count) +{ + FIXME("%p, %p stub!\n", iface, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + FIXME("%p, %lu, %p stub!\n", iface, index, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + FIXME("%p, %#lx, %p stub!\n", iface, stream_sink_id, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) +{ + + FIXME("%p, %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) +{ + FIXME("%p, %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_Shutdown(IMFFinalizableMediaSink *iface) +{ + FIXME("%p stub!\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + FIXME("%p, %p, %p stub!\n", iface, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mpeg4_media_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) +{ + FIXME("%p, %p stub!\n", iface, result); + + return E_NOTIMPL; +} + +static const IMFFinalizableMediaSinkVtbl mpeg4_media_sink_vtbl = +{ + mpeg4_media_sink_QueryInterface, + mpeg4_media_sink_AddRef, + mpeg4_media_sink_Release, + mpeg4_media_sink_GetCharacteristics, + mpeg4_media_sink_AddStreamSink, + mpeg4_media_sink_RemoveStreamSink, + mpeg4_media_sink_GetStreamSinkCount, + mpeg4_media_sink_GetStreamSinkByIndex, + mpeg4_media_sink_GetStreamSinkById, + mpeg4_media_sink_SetPresentationClock, + mpeg4_media_sink_GetPresentationClock, + mpeg4_media_sink_Shutdown, + mpeg4_media_sink_BeginFinalize, + mpeg4_media_sink_EndFinalize, +}; + +static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFSinkClassFactory) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFSinkClassFactory_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI sink_class_factory_AddRef(IMFSinkClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + struct mpeg4_media_sink *sink; + + TRACE("%p, %p, %p, %p.\n", bytestream, video_type, audio_type, out); + + if (!out || ! bytestream) + return E_POINTER; + + if (!(sink = calloc(1, sizeof(*sink)))) + return E_OUTOFMEMORY; + + sink->IMFFinalizableMediaSink_iface.lpVtbl = &mpeg4_media_sink_vtbl; + sink->refcount = 1; + IMFByteStream_AddRef(sink->bytestream = bytestream); + + *out = (IMFMediaSink *)&sink->IMFFinalizableMediaSink_iface; + + return S_OK; +} + +static const IMFSinkClassFactoryVtbl mpeg4_sink_class_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + sink_class_factory_CreateMediaSink, +}; + +static IMFSinkClassFactory mpeg4_sink_class_factory = { &mpeg4_sink_class_factory_vtbl }; + +HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out) +{ + *out = (IUnknown *)&mpeg4_sink_class_factory; + return S_OK; +} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 30a99c9acfb..db36fc368c0 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -111,3 +111,10 @@ coclass CResamplerMediaObject {} uuid(98230571-0087-4204-b020-3282538e57d3) ] coclass CColorConvertDMO {} + +[ + helpstring("MF MPEG4 Sink Class Factory"), + threading(both), + uuid(a22c4fc7-6e91-4e1d-89e9-53b2667b72ba) +] +coclass MFMPEG4SinkClassFactory {}