There is a working in progress winegstreamer muxer backend: !3303 This patch set is the windows api side(frontend) of the muxer stuff.
It will make the patch set too large to submit the backend and frontend together, so I decide to submit the frontend first.
-- v2: winegstreamer: Add async command handling to media sink. winegstreamer: Add IMFClockStateSink stubs to media sink. winegstreamer: Implement IMFMediaEventGenerator for media sink. winegstreamer: Add IMFMediaTypeHandler stubs to stream sink. winegstreamer: Add stubs for stream sink. winegstreamer: Add stubs for media sink.
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 3 + dlls/winegstreamer/media_sink.c | 283 +++++++++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 6 + 5 files changed, 295 insertions(+) create mode 100644 dlls/winegstreamer/media_sink.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 1c701bfa9f6..78bdd6c0ef1 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -11,6 +11,7 @@ C_SRCS = \ color_convert.c \ h264_decoder.c \ main.c \ + media_sink.c \ media_source.c \ mfplat.c \ quartz_parser.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 920ffc9d252..ed867f741d9 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"
@@ -121,6 +122,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 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 a92d440c42f..9624c469314 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -612,6 +612,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 sink_class_factory_cf = {{&class_factory_vtbl}, sink_class_factory_create};
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) { @@ -646,6 +647,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_MFMP3SinkClassFactory)) + factory = &sink_class_factory_cf; else { FIXME("%s not implemented, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c new file mode 100644 index 00000000000..1a09c839642 --- /dev/null +++ b/dlls/winegstreamer/media_sink.c @@ -0,0 +1,283 @@ +/* Gstreamer Media Sink + * + * 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 "mferror.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct media_sink +{ + IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + LONG refcount; + CRITICAL_SECTION cs; + bool shutdown; + + IMFByteStream *bytestream; +}; + +static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) +{ + TRACE("iface %p, riid %s, obj %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 media_sink_AddRef(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedIncrement(&media_sink->refcount); + TRACE("iface %p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedDecrement(&media_sink->refcount); + + TRACE("iface %p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFFinalizableMediaSink_Shutdown(iface); + IMFByteStream_Release(media_sink->bytestream); + DeleteCriticalSection(&media_sink->cs); + free(media_sink); + } + + return refcount; +} + +static HRESULT WINAPI media_sink_GetCharacteristics(IMFFinalizableMediaSink *iface, DWORD *flags) +{ + FIXME("iface %p, flags %p stub!\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + FIXME("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p stub!\n", + iface, stream_sink_id, media_type, stream_sink); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) +{ + FIXME("iface %p, stream_sink_id %#lx stub!\n", iface, stream_sink_id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkCount(IMFFinalizableMediaSink *iface, DWORD *count) +{ + FIXME("iface %p, count %p stub!\n", iface, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + FIXME("iface %p, index %lu, stream %p stub!\n", iface, index, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + FIXME("iface %p, stream_sink_id %#lx, stream %p stub!\n", iface, stream_sink_id, stream); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) +{ + FIXME("iface %p, clock %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) +{ + FIXME("iface %p, clock %p stub!\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("iface %p.\n", iface); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->shutdown) + { + LeaveCriticalSection(&media_sink->cs); + return MF_E_SHUTDOWN; + } + + IMFByteStream_Close(media_sink->bytestream); + + media_sink->shutdown = TRUE; + + LeaveCriticalSection(&media_sink->cs); + + return S_OK; +} + +static HRESULT WINAPI media_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + FIXME("iface %p, callback %p, state %p stub!\n", iface, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) +{ + FIXME("iface %p, result %p stub!\n", iface, result); + + return E_NOTIMPL; +} + +static const IMFFinalizableMediaSinkVtbl media_sink_vtbl = +{ + media_sink_QueryInterface, + media_sink_AddRef, + media_sink_Release, + media_sink_GetCharacteristics, + media_sink_AddStreamSink, + media_sink_RemoveStreamSink, + media_sink_GetStreamSinkCount, + media_sink_GetStreamSinkByIndex, + media_sink_GetStreamSinkById, + media_sink_SetPresentationClock, + media_sink_GetPresentationClock, + media_sink_Shutdown, + media_sink_BeginFinalize, + media_sink_EndFinalize, +}; + +static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) +{ + struct media_sink *media_sink; + + TRACE("bytestream %p, out %p.\n", bytestream, out); + + if (!bytestream) + return E_POINTER; + + if (!(media_sink = calloc(1, sizeof(*media_sink)))) + return E_OUTOFMEMORY; + + media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; + media_sink->refcount = 1; + InitializeCriticalSection(&media_sink->cs); + media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + IMFByteStream_AddRef((media_sink->bytestream = bytestream)); + + *out = media_sink; + TRACE("Created media sink %p.\n", media_sink); + + return S_OK; +} + +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 media_sink *media_sink; + HRESULT hr; + + TRACE("iface %p, bytestream %p, video_type %p, audio_type %p, out %p.\n", + iface, bytestream, video_type, audio_type, out); + + if (FAILED(hr = media_sink_create(bytestream, &media_sink))) + return hr; + + *out = (IMFMediaSink *)&media_sink->IMFFinalizableMediaSink_iface; + return S_OK; +} + +static const IMFSinkClassFactoryVtbl sink_class_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + sink_class_factory_CreateMediaSink, +}; + +static IMFSinkClassFactory sink_class_factory = { &sink_class_factory_vtbl }; + +HRESULT sink_class_factory_create(IUnknown *outer, IUnknown **out) +{ + *out = (IUnknown *)&sink_class_factory; + return S_OK; +} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 30a99c9acfb..2bc85e70b3a 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -111,3 +111,9 @@ coclass CResamplerMediaObject {} uuid(98230571-0087-4204-b020-3282538e57d3) ] coclass CColorConvertDMO {} + +[ + threading(both), + uuid(11275a82-5e5a-47fd-a01c-3683c12fb196) +] +coclass MFMP3SinkClassFactory {}
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 280 +++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 1a09c839642..99246fce2ac 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -19,11 +19,26 @@
#include "gst_private.h" #include "mferror.h" +#include "mfapi.h"
#include "wine/debug.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+struct stream_sink +{ + IMFStreamSink IMFStreamSink_iface; + LONG refcount; + DWORD id; + + IMFMediaType *type; + IMFMediaEventQueue *event_queue; + IMFFinalizableMediaSink *media_sink; + + struct list entry; +}; + struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; @@ -32,13 +47,224 @@ struct media_sink bool shutdown;
IMFByteStream *bytestream; + + struct list stream_sinks; };
+static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct stream_sink, IMFStreamSink_iface); +} + static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) { return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); }
+static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualGUID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &stream_sink->IMFStreamSink_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 stream_sink_AddRef(IMFStreamSink *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedIncrement(&stream_sink->refcount); + TRACE("iface %p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI stream_sink_Release(IMFStreamSink *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedDecrement(&stream_sink->refcount); + + TRACE("iface %p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + IMFMediaEventQueue_Release(stream_sink->event_queue); + IMFFinalizableMediaSink_Release(stream_sink->media_sink); + if (stream_sink->type) + IMFMediaType_Release(stream_sink->type); + free(stream_sink); + } + + return refcount; +} + +static HRESULT WINAPI stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream_sink->event_queue, flags, event); +} + +static HRESULT WINAPI stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, callback %p, state %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream_sink->event_queue, callback, state); +} + +static HRESULT WINAPI stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, result %p, event %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream_sink->event_queue, result, event); +} + +static HRESULT WINAPI stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n", + iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, ret %p.\n", iface, ret); + + IMFMediaSink_AddRef((*ret = (IMFMediaSink *)&stream_sink->media_sink)); + + return S_OK; +} + +static HRESULT WINAPI stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *identifier) +{ + struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); + + TRACE("iface %p, identifier %p.\n", iface, identifier); + + *identifier = stream_sink->id; + + return S_OK; +} + +static HRESULT WINAPI stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + FIXME("iface %p, handler %p stub!\n", iface, handler); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + FIXME("iface %p, sample %p stub!\n", iface, sample); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context_value) +{ + FIXME("iface %p, marker_type %d, marker_value %p, context_value %p stub!\n", + iface, marker_type, marker_value, context_value); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_Flush(IMFStreamSink *iface) +{ + FIXME("iface %p stub!\n", iface); + + return E_NOTIMPL; +} + +static const IMFStreamSinkVtbl stream_sink_vtbl = +{ + stream_sink_QueryInterface, + stream_sink_AddRef, + stream_sink_Release, + stream_sink_GetEvent, + stream_sink_BeginGetEvent, + stream_sink_EndGetEvent, + stream_sink_QueueEvent, + stream_sink_GetMediaSink, + stream_sink_GetIdentifier, + stream_sink_GetMediaTypeHandler, + stream_sink_ProcessSample, + stream_sink_PlaceMarker, + stream_sink_Flush, +}; + +static HRESULT stream_sink_create(DWORD stream_sink_id, IMFMediaType *media_type, struct media_sink *media_sink, + struct stream_sink **out) +{ + struct stream_sink *stream_sink; + HRESULT hr; + + TRACE("stream_sink_id %#lx, media_type %p, media_sink %p, out %p.\n", + stream_sink_id, media_type, media_sink, out); + + if (!(stream_sink = calloc(1, sizeof(*stream_sink)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = MFCreateEventQueue(&stream_sink->event_queue))) + { + free(stream_sink); + return hr; + } + + stream_sink->IMFStreamSink_iface.lpVtbl = &stream_sink_vtbl; + stream_sink->refcount = 1; + stream_sink->id = stream_sink_id; + if (media_type) + IMFMediaType_AddRef((stream_sink->type = media_type)); + IMFFinalizableMediaSink_AddRef((stream_sink->media_sink = &media_sink->IMFFinalizableMediaSink_iface)); + + TRACE("Created stream sink %p.\n", stream_sink); + *out = stream_sink; + + return S_OK; +} + +static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *media_sink, DWORD id) +{ + struct stream_sink *stream_sink; + + LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry) + { + if (stream_sink->id == id) + return stream_sink; + } + + return NULL; +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); @@ -97,10 +323,30 @@ static HRESULT WINAPI media_sink_GetCharacteristics(IMFFinalizableMediaSink *ifa static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, IMFMediaType *media_type, IMFStreamSink **stream_sink) { - FIXME("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p stub!\n", + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + struct stream_sink *object; + HRESULT hr; + + TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", iface, stream_sink_id, media_type, stream_sink);
- return E_NOTIMPL; + if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id)) + return MF_E_STREAMSINK_EXISTS; + + if (FAILED(hr = stream_sink_create(stream_sink_id, media_type, media_sink, &object))) + { + WARN("Failed to create stream sink, hr %#lx.\n", hr); + return hr; + } + + EnterCriticalSection(&media_sink->cs); + list_add_tail(&media_sink->stream_sinks, &object->entry); + LeaveCriticalSection(&media_sink->cs); + + if (stream_sink) + IMFStreamSink_AddRef(*stream_sink = &object->IMFStreamSink_iface); + + return S_OK; }
static HRESULT WINAPI media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) @@ -150,6 +396,7 @@ static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *i static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + struct stream_sink *stream_sink;
TRACE("iface %p.\n", iface);
@@ -161,6 +408,12 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) return MF_E_SHUTDOWN; }
+ LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry) + { + IMFMediaEventQueue_Shutdown(stream_sink->event_queue); + IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface); + } + IMFByteStream_Close(media_sink->bytestream);
media_sink->shutdown = TRUE; @@ -253,6 +506,7 @@ static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) { + IMFFinalizableMediaSink *media_sink_iface; struct media_sink *media_sink; HRESULT hr;
@@ -261,8 +515,28 @@ static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *if
if (FAILED(hr = media_sink_create(bytestream, &media_sink))) return hr; + media_sink_iface = &media_sink->IMFFinalizableMediaSink_iface; + + if (video_type) + { + if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 1, video_type, NULL))) + { + IMFFinalizableMediaSink_Shutdown(media_sink_iface); + IMFFinalizableMediaSink_Release(media_sink_iface); + return hr; + } + } + if (audio_type) + { + if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 2, audio_type, NULL))) + { + IMFFinalizableMediaSink_Shutdown(media_sink_iface); + IMFFinalizableMediaSink_Release(media_sink_iface); + return hr; + } + }
- *out = (IMFMediaSink *)&media_sink->IMFFinalizableMediaSink_iface; + *out = (IMFMediaSink *)media_sink_iface; return S_OK; }
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 95 +++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 99246fce2ac..a73606e78f5 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -29,6 +29,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); struct stream_sink { IMFStreamSink IMFStreamSink_iface; + IMFMediaTypeHandler IMFMediaTypeHandler_iface; LONG refcount; DWORD id;
@@ -56,6 +57,11 @@ static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) return CONTAINING_RECORD(iface, struct stream_sink, IMFStreamSink_iface); }
+static struct stream_sink *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct stream_sink, IMFMediaTypeHandler_iface); +} + static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) { return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); @@ -73,6 +79,10 @@ static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID ri { *obj = &stream_sink->IMFStreamSink_iface; } + else if (IsEqualIID(riid, &IID_IMFMediaTypeHandler)) + { + *obj = &stream_sink->IMFMediaTypeHandler_iface; + } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); @@ -221,6 +231,90 @@ static const IMFStreamSinkVtbl stream_sink_vtbl = stream_sink_Flush, };
+static HRESULT WINAPI stream_sink_type_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, void **obj) +{ + struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_QueryInterface(&stream_sink->IMFStreamSink_iface, riid, obj); +} + +static ULONG WINAPI stream_sink_type_handler_AddRef(IMFMediaTypeHandler *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_AddRef(&stream_sink->IMFStreamSink_iface); +} + +static ULONG WINAPI stream_sink_type_handler_Release(IMFMediaTypeHandler *iface) +{ + struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface); +} + +static HRESULT WINAPI stream_sink_type_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, + IMFMediaType *in_type, IMFMediaType **out_type) +{ + FIXME("iface %p, in_type %p, out_type %p.\n", iface, in_type, out_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_type_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) +{ + FIXME("iface %p, count %p.\n", iface, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_type_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, + IMFMediaType **type) +{ + FIXME("iface %p, index %lu, type %p.\n", iface, index, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_type_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType *type) +{ + FIXME("iface %p, type %p.\n", iface, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI stream_sink_type_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **type) +{ + struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface); + + TRACE("iface %p, type %p.\n", iface, type); + + if (!type) + return E_POINTER; + if (!stream_sink->type) + return MF_E_NOT_INITIALIZED; + + IMFMediaType_AddRef((*type = stream_sink->type)); + + return S_OK; +} + +static HRESULT WINAPI stream_sink_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) +{ + FIXME("iface %p, type %p.\n", iface, type); + + return E_NOTIMPL; +} + +static const IMFMediaTypeHandlerVtbl stream_sink_type_handler_vtbl = +{ + stream_sink_type_handler_QueryInterface, + stream_sink_type_handler_AddRef, + stream_sink_type_handler_Release, + stream_sink_type_handler_IsMediaTypeSupported, + stream_sink_type_handler_GetMediaTypeCount, + stream_sink_type_handler_GetMediaTypeByIndex, + stream_sink_type_handler_SetCurrentMediaType, + stream_sink_type_handler_GetCurrentMediaType, + stream_sink_type_handler_GetMajorType, +}; + static HRESULT stream_sink_create(DWORD stream_sink_id, IMFMediaType *media_type, struct media_sink *media_sink, struct stream_sink **out) { @@ -240,6 +334,7 @@ static HRESULT stream_sink_create(DWORD stream_sink_id, IMFMediaType *media_type }
stream_sink->IMFStreamSink_iface.lpVtbl = &stream_sink_vtbl; + stream_sink->IMFMediaTypeHandler_iface.lpVtbl = &stream_sink_type_handler_vtbl; stream_sink->refcount = 1; stream_sink->id = stream_sink_id; if (media_type)
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index a73606e78f5..5ccb88141fa 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -43,11 +43,13 @@ struct stream_sink struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; LONG refcount; CRITICAL_SECTION cs; bool shutdown;
IMFByteStream *bytestream; + IMFMediaEventQueue *event_queue;
struct list stream_sinks; }; @@ -67,6 +69,11 @@ static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaS return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface); }
+static struct media_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFMediaEventGenerator_iface); +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -362,6 +369,8 @@ static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *m
static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj);
if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) || @@ -370,6 +379,10 @@ static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, { *obj = iface; } + else if (IsEqualGUID(riid, &IID_IMFMediaEventGenerator)) + { + *obj = &media_sink->IMFMediaEventGenerator_iface; + } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); @@ -400,6 +413,7 @@ static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface) if (!refcount) { IMFFinalizableMediaSink_Shutdown(iface); + IMFMediaEventQueue_Release(media_sink->event_queue); IMFByteStream_Release(media_sink->bytestream); DeleteCriticalSection(&media_sink->cs); free(media_sink); @@ -509,6 +523,7 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface); }
+ IMFMediaEventQueue_Shutdown(media_sink->event_queue); IMFByteStream_Close(media_sink->bytestream);
media_sink->shutdown = TRUE; @@ -550,9 +565,79 @@ static const IMFFinalizableMediaSinkVtbl media_sink_vtbl = media_sink_EndFinalize, };
+static HRESULT WINAPI media_sink_event_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, void **obj) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI media_sink_event_AddRef(IMFMediaEventGenerator *iface) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_event_Release(IMFMediaEventGenerator *iface) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_event_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(media_sink->event_queue, flags, event); +} + +static HRESULT WINAPI media_sink_event_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, callback %p, state %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(media_sink->event_queue, callback, state); +} + +static HRESULT WINAPI media_sink_event_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, result %p, event %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(media_sink->event_queue, result, event); +} + +static HRESULT WINAPI media_sink_event_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n", + iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(media_sink->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl media_sink_event_vtbl = +{ + media_sink_event_QueryInterface, + media_sink_event_AddRef, + media_sink_event_Release, + media_sink_event_GetEvent, + media_sink_event_BeginGetEvent, + media_sink_event_EndGetEvent, + media_sink_event_QueueEvent, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; + HRESULT hr;
TRACE("bytestream %p, out %p.\n", bytestream, out);
@@ -562,7 +647,14 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** if (!(media_sink = calloc(1, sizeof(*media_sink)))) return E_OUTOFMEMORY;
+ if (FAILED(hr = MFCreateEventQueue(&media_sink->event_queue))) + { + free(media_sink); + return hr; + } + media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; + media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; media_sink->refcount = 1; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 5ccb88141fa..5beaac949d1 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -44,6 +44,7 @@ struct media_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFClockStateSink IMFClockStateSink_iface; LONG refcount; CRITICAL_SECTION cs; bool shutdown; @@ -74,6 +75,11 @@ static struct media_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerato return CONTAINING_RECORD(iface, struct media_sink, IMFMediaEventGenerator_iface); }
+static struct media_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, IMFClockStateSink_iface); +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -383,6 +389,10 @@ static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, { *obj = &media_sink->IMFMediaEventGenerator_iface; } + else if (IsEqualIID(riid, &IID_IMFClockStateSink)) + { + *obj = &media_sink->IMFClockStateSink_iface; + } else { WARN("Unsupported interface %s.\n", debugstr_guid(riid)); @@ -634,6 +644,71 @@ static const IMFMediaEventGeneratorVtbl media_sink_event_vtbl = media_sink_event_QueueEvent, };
+static HRESULT WINAPI media_sink_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI media_sink_clock_sink_AddRef(IMFClockStateSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_clock_sink_Release(IMFClockStateSink *iface) +{ + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) +{ + FIXME("iface %p, systime %s, offset %s stub!\n", iface, debugstr_time(systime), debugstr_time(offset)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("%p, %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) +{ + FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) +{ + FIXME("iface %p, systime %s, rate %f stub!\n", iface, debugstr_time(systime), rate); + + return E_NOTIMPL; +} + +static const IMFClockStateSinkVtbl media_sink_clock_sink_vtbl = +{ + media_sink_clock_sink_QueryInterface, + media_sink_clock_sink_AddRef, + media_sink_clock_sink_Release, + media_sink_clock_sink_OnClockStart, + media_sink_clock_sink_OnClockStop, + media_sink_clock_sink_OnClockPause, + media_sink_clock_sink_OnClockRestart, + media_sink_clock_sink_OnClockSetRate, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; @@ -655,6 +730,7 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **
media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; + media_sink->IMFClockStateSink_iface.lpVtbl = &media_sink_clock_sink_vtbl; media_sink->refcount = 1; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 296 ++++++++++++++++++++++++++++++-- 1 file changed, 285 insertions(+), 11 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 5beaac949d1..86bbc524e13 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -26,6 +26,21 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+enum async_op +{ + ASYNC_START, + ASYNC_STOP, + ASYNC_PAUSE, +}; + +struct async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + + enum async_op op; +}; + struct stream_sink { IMFStreamSink IMFStreamSink_iface; @@ -45,9 +60,18 @@ struct media_sink IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; IMFClockStateSink IMFClockStateSink_iface; + IMFAsyncCallback async_callback; LONG refcount; CRITICAL_SECTION cs; - bool shutdown; + + enum + { + STATE_OPENED, + STATE_STARTED, + STATE_STOPPED, + STATE_PAUSED, + STATE_SHUTDOWN, + } state;
IMFByteStream *bytestream; IMFMediaEventQueue *event_queue; @@ -80,6 +104,71 @@ static struct media_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) return CONTAINING_RECORD(iface, struct media_sink, IMFClockStateSink_iface); }
+static struct media_sink *impl_from_async_callback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_sink, async_callback); +} + +static struct async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct async_command, IUnknown_iface); +} + +static HRESULT WINAPI async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_command_AddRef(IUnknown *iface) +{ + struct async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI async_command_Release(IUnknown *iface) +{ + struct async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + free(command); + + return refcount; +} + +static const IUnknownVtbl async_command_vtbl = +{ + async_command_QueryInterface, + async_command_AddRef, + async_command_Release, +}; + +static HRESULT async_command_create(enum async_op op, struct async_command **out) +{ + struct async_command *command; + + if (!(command = calloc(1, sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &async_command_vtbl; + command->refcount = 1; + command->op = op; + + TRACE("Created async command %p.\n", command); + *out = command; + + return S_OK; +} + static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) { struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface); @@ -373,6 +462,73 @@ static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *m return NULL; }
+static HRESULT media_sink_queue_command(struct media_sink *media_sink, enum async_op op) +{ + struct async_command *command; + HRESULT hr; + + if (media_sink->state == STATE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (FAILED(hr = async_command_create(op, &command))) + return hr; + + return MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &media_sink->async_callback, &command->IUnknown_iface); +} + +static HRESULT media_sink_queue_stream_event(struct media_sink *media_sink, MediaEventType type) +{ + struct stream_sink *stream_sink; + HRESULT hr; + + LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry) + { + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, type, &GUID_NULL, S_OK, NULL))) + return hr; + } + + return S_OK; +} + +static HRESULT media_sink_start(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_STARTED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkStarted); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + +static HRESULT media_sink_stop(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_STOPPED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkStopped); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + +static HRESULT media_sink_pause(struct media_sink *media_sink) +{ + HRESULT hr; + + EnterCriticalSection(&media_sink->cs); + + media_sink->state = STATE_PAUSED; + hr = media_sink_queue_stream_event(media_sink, MEStreamSinkPaused); + + LeaveCriticalSection(&media_sink->cs); + return hr; +} + static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); @@ -521,7 +677,7 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
EnterCriticalSection(&media_sink->cs);
- if (media_sink->shutdown) + if (media_sink->state == STATE_SHUTDOWN) { LeaveCriticalSection(&media_sink->cs); return MF_E_SHUTDOWN; @@ -536,7 +692,7 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface) IMFMediaEventQueue_Shutdown(media_sink->event_queue); IMFByteStream_Close(media_sink->bytestream);
- media_sink->shutdown = TRUE; + media_sink->state = STATE_SHUTDOWN;
LeaveCriticalSection(&media_sink->cs);
@@ -664,30 +820,62 @@ static ULONG WINAPI media_sink_clock_sink_Release(IMFClockStateSink *iface)
static HRESULT WINAPI media_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) { - FIXME("iface %p, systime %s, offset %s stub!\n", iface, debugstr_time(systime), debugstr_time(offset)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s, offset %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_START); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) { - FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_STOP); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) { - FIXME("%p, %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_PAUSE); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) { - FIXME("iface %p, systime %s stub!\n", iface, debugstr_time(systime)); + struct media_sink *media_sink = impl_from_IMFClockStateSink(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&media_sink->cs); + + hr = media_sink_queue_command(media_sink, ASYNC_START); + + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) @@ -709,6 +897,90 @@ static const IMFClockStateSinkVtbl media_sink_clock_sink_vtbl = media_sink_clock_sink_OnClockSetRate, };
+static HRESULT WINAPI media_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI media_sink_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_sink *sink = impl_from_async_callback(iface); + return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI media_sink_callback_Release(IMFAsyncCallback *iface) +{ + struct media_sink *sink = impl_from_async_callback(iface); + return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI media_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + TRACE("iface %p, flags %p, queue %p.\n", iface, flags, queue); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *async_result) +{ + struct media_sink *media_sink = impl_from_async_callback(iface); + struct async_command *command; + HRESULT hr = E_FAIL; + IUnknown *state; + + TRACE("iface %p, async_result %p.\n", iface, async_result); + + EnterCriticalSection(&media_sink->cs); + + if (!(state = IMFAsyncResult_GetStateNoAddRef(async_result))) + { + LeaveCriticalSection(&media_sink->cs); + return hr; + } + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case ASYNC_START: + hr = media_sink_start(media_sink); + break; + case ASYNC_STOP: + hr = media_sink_stop(media_sink); + break; + case ASYNC_PAUSE: + hr = media_sink_pause(media_sink); + break; + default: + WARN("Unsupported op %u.\n", command->op); + break; + } + + LeaveCriticalSection(&media_sink->cs); + + return hr; +} + +static const IMFAsyncCallbackVtbl media_sink_callback_vtbl = +{ + media_sink_callback_QueryInterface, + media_sink_callback_AddRef, + media_sink_callback_Release, + media_sink_callback_GetParameters, + media_sink_callback_Invoke, +}; + static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink **out) { struct media_sink *media_sink; @@ -731,7 +1003,9 @@ static HRESULT media_sink_create(IMFByteStream *bytestream, struct media_sink ** media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl; media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl; media_sink->IMFClockStateSink_iface.lpVtbl = &media_sink_clock_sink_vtbl; + media_sink->async_callback.lpVtbl = &media_sink_callback_vtbl; media_sink->refcount = 1; + media_sink->state = STATE_OPENED; InitializeCriticalSection(&media_sink->cs); media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); IMFByteStream_AddRef((media_sink->bytestream = bytestream));
On Tue Aug 8 07:46:49 2023 +0000, Rémi Bernon wrote:
You need to destroy the CS here.
You'll also need to reset `media_sink->cs.DebugInfo->Spare[0] =` to 0.
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, IMFMediaType *media_type, IMFStreamSink **stream_sink) {
- FIXME("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p stub!\n",
- struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
- struct stream_sink *object;
- HRESULT hr;
- TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", iface, stream_sink_id, media_type, stream_sink);
- return E_NOTIMPL;
- if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id))
return MF_E_STREAMSINK_EXISTS;
You'll need to guard `media_sink_get_stream_sink_by_id` calls with the CS as well. And for correctness it has to include the `list_add_tail(&media_sink->stream_sinks, &object->entry);` below (so that no other thread can insert the stream in the meantime).
This means you will have to create the stream sink inside the CS (the simplest, but you can also create it outside and eventually destroy it if it was already present).
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
- return E_NOTIMPL;
- if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id))
return MF_E_STREAMSINK_EXISTS;
- if (FAILED(hr = stream_sink_create(stream_sink_id, media_type, media_sink, &object)))
- {
WARN("Failed to create stream sink, hr %#lx.\n", hr);
return hr;
- }
- EnterCriticalSection(&media_sink->cs);
- list_add_tail(&media_sink->stream_sinks, &object->entry);
- LeaveCriticalSection(&media_sink->cs);
- if (stream_sink)
IMFStreamSink_AddRef(*stream_sink = &object->IMFStreamSink_iface);
```suggestion:-0+0 IMFStreamSink_AddRef((*stream_sink = &object->IMFStreamSink_iface)); ```
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
return MF_E_SHUTDOWN; }
- LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry)
- {
IMFMediaEventQueue_Shutdown(stream_sink->event_queue);
IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface);
- }
```suggestion:-4+0 LIST_FOR_EACH_ENTRY_SAFE(stream_sink, next, &media_sink->stream_sinks, struct stream_sink, entry) { list_remove(&stream_sink->entry); IMFMediaEventQueue_Shutdown(stream_sink->event_queue); IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface); } ```
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
#include "mferror.h" +#include "mfapi.h"
#include "wine/debug.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+struct stream_sink +{
- IMFStreamSink IMFStreamSink_iface;
- LONG refcount;
- DWORD id;
- IMFMediaType *type;
- IMFMediaEventQueue *event_queue;
I understand the streams are also supposed to implement IMFMediaEventGenerator interface, maybe you can introduce the event_queue then instead of now (and leave the few event methods return E_NOTIMPL until then).
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
+{
- struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
- TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n",
iface, event_type, debugstr_guid(ext_type), hr, value);
- return IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, event_type, ext_type, hr, value);
+}
+static HRESULT WINAPI stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret) +{
- struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
- TRACE("iface %p, ret %p.\n", iface, ret);
- IMFMediaSink_AddRef((*ret = (IMFMediaSink *)&stream_sink->media_sink));
```suggestion:-0+0 IMFMediaSink_AddRef((*ret = (IMFMediaSink *)stream_sink->media_sink)); ```
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/media_sink.c:
{ FIXME("iface %p, handler %p stub!\n", iface, handler);
return E_NOTIMPL;
You could probably implement `GetMediaTypeHandler` too, afaics it does the same as QueryInterface.
On Tue Aug 8 07:46:47 2023 +0000, Rémi Bernon wrote:
You're not using it anywhere, so the code is actually dead code, please add a use.
Is there any reason you didn't want to use MPEG4 media sink, which is tested? It's no big deal but I thought it could be useful to remove the todo_wine there progressively (though I understand you'd need to change the tests a bit to avoid crashing while things are getting implemented).
On Wed Aug 9 09:12:25 2023 +0000, Rémi Bernon wrote:
Is there any reason you didn't want to use MPEG4 media sink, which is tested? It's no big deal but I thought it could be useful to remove the todo_wine there progressively (though I understand you'd need to change the tests a bit to avoid crashing while things are getting implemented).
The reason is that, as you said, it would be annoying to change the tests in each patch in this patch set to avoid crashing. Imo, it's kind of wasting time to do so. My plan is to add mpeg4 media sink after the media sink and stream sink class has a better implementation.