Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfmediaengine/main.c | 22 ++++++++++++++++++---- dlls/mfmediaengine/tests/mfmediaengine.c | 9 ++++++--- 2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index d93e2b2f38c..64f0ae6d2a1 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -87,6 +87,7 @@ struct media_engine MF_MEDIA_ENGINE_ERR error_code; HRESULT extended_code; MF_MEDIA_ENGINE_READY ready_state; + MF_MEDIA_ENGINE_PRELOAD preload; IMFMediaSession *session; IMFSourceResolver *resolver; BSTR current_source; @@ -717,16 +718,29 @@ static USHORT WINAPI media_engine_GetNetworkState(IMFMediaEngine *iface)
static MF_MEDIA_ENGINE_PRELOAD WINAPI media_engine_GetPreload(IMFMediaEngine *iface) { - FIXME("(%p): stub.\n", iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); + MF_MEDIA_ENGINE_PRELOAD preload; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&engine->cs); + preload = engine->preload; + LeaveCriticalSection(&engine->cs);
- return MF_MEDIA_ENGINE_PRELOAD_NONE; + return preload; }
static HRESULT WINAPI media_engine_SetPreload(IMFMediaEngine *iface, MF_MEDIA_ENGINE_PRELOAD preload) { - FIXME("(%p, %d): stub.\n", iface, preload); + struct media_engine *engine = impl_from_IMFMediaEngine(iface);
- return E_NOTIMPL; + TRACE("%p, %d.\n", iface, preload); + + EnterCriticalSection(&engine->cs); + engine->preload = preload; + LeaveCriticalSection(&engine->cs); + + return S_OK; }
static HRESULT WINAPI media_engine_GetBuffered(IMFMediaEngine *iface, IMFMediaTimeRange **buffered) diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 660e5b95331..0394bcf6a0e 100644 --- a/dlls/mfmediaengine/tests/mfmediaengine.c +++ b/dlls/mfmediaengine/tests/mfmediaengine.c @@ -265,17 +265,20 @@ todo_wine
/* Preload mode is still accessible. */ state = IMFMediaEngine_GetPreload(media_engine); -todo_wine ok(!state, "Unexpected state %d.\n", state);
hr = IMFMediaEngine_SetPreload(media_engine, MF_MEDIA_ENGINE_PRELOAD_AUTOMATIC); -todo_wine ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
state = IMFMediaEngine_GetPreload(media_engine); -todo_wine ok(state == MF_MEDIA_ENGINE_PRELOAD_AUTOMATIC, "Unexpected state %d.\n", state);
+ hr = IMFMediaEngine_SetPreload(media_engine, 100); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + state = IMFMediaEngine_GetPreload(media_engine); + ok(state == 100, "Unexpected state %d.\n", state); + hr = IMFMediaEngine_GetBuffered(media_engine, &time_range); todo_wine ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfmediaengine/Makefile.in | 3 +- dlls/mfmediaengine/main.c | 51 +- dlls/mfmediaengine/mfmediaengine_private.h | 19 + dlls/mfmediaengine/renderer.c | 661 +++++++++++++++++++++ 4 files changed, 720 insertions(+), 14 deletions(-) create mode 100644 dlls/mfmediaengine/mfmediaengine_private.h create mode 100644 dlls/mfmediaengine/renderer.c
diff --git a/dlls/mfmediaengine/Makefile.in b/dlls/mfmediaengine/Makefile.in index b97e9eec331..a405b29f92f 100644 --- a/dlls/mfmediaengine/Makefile.in +++ b/dlls/mfmediaengine/Makefile.in @@ -5,7 +5,8 @@ IMPORTS = oleaut32 mfplat mf mfuuid uuid EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - main.c + main.c \ + renderer.c
IDL_SRCS = \ mediaengine.idl \ diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 64f0ae6d2a1..4b08b3614f4 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -29,6 +29,8 @@ #include "mferror.h" #include "dxgi.h"
+#include "mfmediaengine_private.h" + #include "wine/debug.h" #include "wine/heap.h"
@@ -455,31 +457,54 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi
IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_LOADEDDATA, 0, 0);
- /* TODO: set up video stream nodes */ - if (SUCCEEDED(hr = MFCreateTopology(&topology))) { - IMFTopologyNode *sar_node = NULL, *audio_src = NULL; + IMFTopologyNode *output_node, *source_node;
if (sd_audio) { - if (FAILED(hr = media_engine_create_source_node(source, pd, sd_audio, &audio_src))) + output_node = source_node = NULL; + + if (FAILED(hr = media_engine_create_source_node(source, pd, sd_audio, &source_node))) WARN("Failed to create audio source node, hr %#x.\n", hr);
- if (FAILED(hr = media_engine_create_audio_renderer(engine, &sar_node))) + if (FAILED(hr = media_engine_create_audio_renderer(engine, &output_node))) WARN("Failed to create audio renderer node, hr %#x.\n", hr);
- if (sar_node && audio_src) + if (output_node && source_node) + { + IMFTopology_AddNode(topology, source_node); + IMFTopology_AddNode(topology, output_node); + IMFTopologyNode_ConnectOutput(source_node, 0, output_node, 0); + } + + if (output_node) + IMFTopologyNode_Release(output_node); + if (source_node) + IMFTopologyNode_Release(source_node); + } + + if (SUCCEEDED(hr) && sd_video) + { + output_node = source_node = NULL; + + if (FAILED(hr = media_engine_create_source_node(source, pd, sd_video, &source_node))) + WARN("Failed to create video source node, hr %#x.\n", hr); + + if (FAILED(hr = media_engine_create_video_renderer(&output_node))) + WARN("Failed to create video renderer node, hr %#x.\n", hr); + + if (output_node && source_node) { - IMFTopology_AddNode(topology, audio_src); - IMFTopology_AddNode(topology, sar_node); - IMFTopologyNode_ConnectOutput(audio_src, 0, sar_node, 0); + IMFTopology_AddNode(topology, source_node); + IMFTopology_AddNode(topology, output_node); + IMFTopologyNode_ConnectOutput(source_node, 0, output_node, 0); }
- if (sar_node) - IMFTopologyNode_Release(sar_node); - if (audio_src) - IMFTopologyNode_Release(audio_src); + if (output_node) + IMFTopologyNode_Release(output_node); + if (source_node) + IMFTopologyNode_Release(source_node); } }
diff --git a/dlls/mfmediaengine/mfmediaengine_private.h b/dlls/mfmediaengine/mfmediaengine_private.h new file mode 100644 index 00000000000..ab157f3da40 --- /dev/null +++ b/dlls/mfmediaengine/mfmediaengine_private.h @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Nikolay Sivov 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 + */ + +HRESULT media_engine_create_video_renderer(IMFTopologyNode **node) DECLSPEC_HIDDEN; diff --git a/dlls/mfmediaengine/renderer.c b/dlls/mfmediaengine/renderer.c new file mode 100644 index 00000000000..98807f23d0f --- /dev/null +++ b/dlls/mfmediaengine/renderer.c @@ -0,0 +1,661 @@ +/* + * Copyright 2020 Nikolay Sivov 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 + */ + +#define COBJMACROS + +#include "mfapi.h" +#include "mfidl.h" +#include "mferror.h" + +#include "mfmediaengine_private.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +enum video_renderer_flags +{ + FLAG_SHUT_DOWN = 0x1, +}; + +struct video_renderer +{ + IMFMediaSink IMFMediaSink_iface; + IMFStreamSink IMFStreamSink_iface; + IMFMediaTypeHandler IMFMediaTypeHandler_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; + LONG refcount; + IMFMediaEventQueue *event_queue; + IMFMediaEventQueue *stream_event_queue; + unsigned int flags; + CRITICAL_SECTION cs; +}; + +static struct video_renderer *impl_from_IMFMediaSink(IMFMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_renderer, IMFMediaSink_iface); +} + +static struct video_renderer *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_renderer, IMFStreamSink_iface); +} + +static struct video_renderer *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface) +{ + return CONTAINING_RECORD(iface, struct video_renderer, IMFMediaEventGenerator_iface); +} + +static struct video_renderer *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct video_renderer, IMFMediaEventGenerator_iface); +} + +static HRESULT WINAPI video_renderer_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFMediaSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaEventGenerator)) + { + *obj = &renderer->IMFMediaEventGenerator_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI video_renderer_sink_AddRef(IMFMediaSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + ULONG refcount = InterlockedIncrement(&renderer->refcount); + TRACE("%p, refcount %u.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI video_renderer_sink_Release(IMFMediaSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + ULONG refcount = InterlockedDecrement(&renderer->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (renderer->event_queue) + IMFMediaEventQueue_Release(renderer->event_queue); + if (renderer->stream_event_queue) + IMFMediaEventQueue_Release(renderer->stream_event_queue); + DeleteCriticalSection(&renderer->cs); + heap_free(renderer); + } + + return refcount; +} + +static HRESULT WINAPI video_renderer_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *flags) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p, %p.\n", iface, flags); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_SHUTDOWN; + + *flags = MEDIASINK_FIXED_STREAMS; + + return S_OK; +} + +static HRESULT WINAPI video_renderer_sink_AddStreamSink(IMFMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p, %#x, %p, %p.\n", iface, stream_sink_id, media_type, stream_sink); + + return renderer->flags & FLAG_SHUT_DOWN ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI video_renderer_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p, %#x.\n", iface, stream_sink_id); + + return renderer->flags & FLAG_SHUT_DOWN ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI video_renderer_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p, %p.\n", iface, count); + + if (!count) + return E_POINTER; + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_SHUTDOWN; + + *count = 1; + + return S_OK; +} + +static HRESULT WINAPI video_renderer_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %p.\n", iface, index, stream); + + EnterCriticalSection(&renderer->cs); + + if (renderer->flags & FLAG_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (index > 0) + hr = MF_E_INVALIDINDEX; + else + { + *stream = &renderer->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&renderer->cs); + + return hr; +} + +static HRESULT WINAPI video_renderer_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %#x, %p.\n", iface, stream_sink_id, stream); + + EnterCriticalSection(&renderer->cs); + + if (renderer->flags & FLAG_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (stream_sink_id > 0) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + *stream = &renderer->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&renderer->cs); + + return hr; +} + +static HRESULT WINAPI video_renderer_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) +{ + FIXME("%p, %p.\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_sink_GetPresentationClock(IMFMediaSink *iface, + IMFPresentationClock **clock) +{ + FIXME("%p, %p.\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + + TRACE("%p.\n", iface); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_SHUTDOWN; + + EnterCriticalSection(&renderer->cs); + renderer->flags |= FLAG_SHUT_DOWN; + IMFMediaEventQueue_Shutdown(renderer->event_queue); + IMFMediaEventQueue_Shutdown(renderer->stream_event_queue); + LeaveCriticalSection(&renderer->cs); + + return S_OK; +} + +static const IMFMediaSinkVtbl video_renderer_sink_vtbl = +{ + video_renderer_sink_QueryInterface, + video_renderer_sink_AddRef, + video_renderer_sink_Release, + video_renderer_sink_GetCharacteristics, + video_renderer_sink_AddStreamSink, + video_renderer_sink_RemoveStreamSink, + video_renderer_sink_GetStreamSinkCount, + video_renderer_sink_GetStreamSinkByIndex, + video_renderer_sink_GetStreamSinkById, + video_renderer_sink_SetPresentationClock, + video_renderer_sink_GetPresentationClock, + video_renderer_sink_Shutdown, +}; + +static HRESULT WINAPI video_renderer_stream_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &renderer->IMFStreamSink_iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaTypeHandler)) + { + *obj = &renderer->IMFMediaTypeHandler_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI video_renderer_stream_AddRef(IMFStreamSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + return IMFMediaSink_AddRef(&renderer->IMFMediaSink_iface); +} + +static ULONG WINAPI video_renderer_stream_Release(IMFStreamSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + return IMFMediaSink_Release(&renderer->IMFMediaSink_iface); +} + +static HRESULT WINAPI video_renderer_stream_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %#x, %p.\n", iface, flags, event); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_GetEvent(renderer->stream_event_queue, flags, event); +} + +static HRESULT WINAPI video_renderer_stream_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_BeginGetEvent(renderer->stream_event_queue, callback, state); +} + +static HRESULT WINAPI video_renderer_stream_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_EndGetEvent(renderer->stream_event_queue, result, event); +} + +static HRESULT WINAPI video_renderer_stream_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %u, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI video_renderer_stream_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, sink); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + *sink = &renderer->IMFMediaSink_iface; + IMFMediaSink_AddRef(*sink); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_GetIdentifier(IMFStreamSink *iface, DWORD *identifier) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, identifier); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + *identifier = 0; + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, handler); + + if (!handler) + return E_POINTER; + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + *handler = &renderer->IMFMediaTypeHandler_iface; + IMFMediaTypeHandler_AddRef(*handler); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, sample); + + if (!sample) + return E_POINTER; + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + /* FIXME: samples are dropped */ + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context_value) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_Flush(IMFStreamSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFStreamSink(iface); + + TRACE("%p.\n", iface); + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return S_OK; +} + +static const IMFStreamSinkVtbl video_renderer_stream_vtbl = +{ + video_renderer_stream_QueryInterface, + video_renderer_stream_AddRef, + video_renderer_stream_Release, + video_renderer_stream_GetEvent, + video_renderer_stream_BeginGetEvent, + video_renderer_stream_EndGetEvent, + video_renderer_stream_QueueEvent, + video_renderer_stream_GetMediaSink, + video_renderer_stream_GetIdentifier, + video_renderer_stream_GetMediaTypeHandler, + video_renderer_stream_ProcessSample, + video_renderer_stream_PlaceMarker, + video_renderer_stream_Flush, +}; + +static HRESULT WINAPI video_renderer_stream_type_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, + void **obj) +{ + struct video_renderer *renderer = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_QueryInterface(&renderer->IMFStreamSink_iface, riid, obj); +} + +static ULONG WINAPI video_renderer_stream_type_handler_AddRef(IMFMediaTypeHandler *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_AddRef(&renderer->IMFStreamSink_iface); +} + +static ULONG WINAPI video_renderer_stream_type_handler_Release(IMFMediaTypeHandler *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaTypeHandler(iface); + return IMFStreamSink_Release(&renderer->IMFStreamSink_iface); +} + +static HRESULT WINAPI video_renderer_stream_type_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface, + IMFMediaType *in_type, IMFMediaType **out_type) +{ + FIXME("%p, %p, %p.\n", iface, in_type, out_type); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_stream_type_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count) +{ + FIXME("%p, %p.\n", iface, count); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_stream_type_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index, + IMFMediaType **media_type) +{ + FIXME("%p, %u, %p.\n", iface, index, media_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_stream_type_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, + IMFMediaType *media_type) +{ + FIXME("%p, %p.\n", iface, media_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_stream_type_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, + IMFMediaType **media_type) +{ + FIXME("%p, %p.\n", iface, media_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_renderer_stream_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) +{ + struct video_renderer *renderer = impl_from_IMFMediaTypeHandler(iface); + + TRACE("%p, %p.\n", iface, type); + + if (!type) + return E_POINTER; + + if (renderer->flags & FLAG_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + memcpy(type, &MFMediaType_Video, sizeof(*type)); + return S_OK; +} + +static const IMFMediaTypeHandlerVtbl video_renderer_stream_type_handler_vtbl = +{ + video_renderer_stream_type_handler_QueryInterface, + video_renderer_stream_type_handler_AddRef, + video_renderer_stream_type_handler_Release, + video_renderer_stream_type_handler_IsMediaTypeSupported, + video_renderer_stream_type_handler_GetMediaTypeCount, + video_renderer_stream_type_handler_GetMediaTypeByIndex, + video_renderer_stream_type_handler_SetCurrentMediaType, + video_renderer_stream_type_handler_GetCurrentMediaType, + video_renderer_stream_type_handler_GetMajorType, +}; + +static HRESULT WINAPI video_renderer_events_QueryInterface(IMFMediaEventGenerator *iface, + REFIID riid, void **obj) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_QueryInterface(&renderer->IMFMediaSink_iface, riid, obj); +} + +static ULONG WINAPI video_renderer_events_AddRef(IMFMediaEventGenerator *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_AddRef(&renderer->IMFMediaSink_iface); +} + +static ULONG WINAPI video_renderer_events_Release(IMFMediaEventGenerator *iface) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + return IMFMediaSink_Release(&renderer->IMFMediaSink_iface); +} + +static HRESULT WINAPI video_renderer_events_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %#x, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(renderer->event_queue, flags, event); +} + +static HRESULT WINAPI video_renderer_events_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(renderer->event_queue, callback, state); +} + +static HRESULT WINAPI video_renderer_events_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(renderer->event_queue, result, event); +} + +static HRESULT WINAPI video_renderer_events_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct video_renderer *renderer = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %u, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(renderer->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl video_renderer_events_vtbl = +{ + video_renderer_events_QueryInterface, + video_renderer_events_AddRef, + video_renderer_events_Release, + video_renderer_events_GetEvent, + video_renderer_events_BeginGetEvent, + video_renderer_events_EndGetEvent, + video_renderer_events_QueueEvent, +}; + +HRESULT media_engine_create_video_renderer(IMFTopologyNode **node) +{ + struct video_renderer *renderer; + HRESULT hr; + + *node = NULL; + + if (!(renderer = heap_alloc_zero(sizeof(*renderer)))) + return E_OUTOFMEMORY; + + renderer->IMFMediaSink_iface.lpVtbl = &video_renderer_sink_vtbl; + renderer->IMFStreamSink_iface.lpVtbl = &video_renderer_stream_vtbl; + renderer->IMFMediaTypeHandler_iface.lpVtbl = &video_renderer_stream_type_handler_vtbl; + renderer->IMFMediaEventGenerator_iface.lpVtbl = &video_renderer_events_vtbl; + renderer->refcount = 1; + InitializeCriticalSection(&renderer->cs); + + if (FAILED(hr = MFCreateEventQueue(&renderer->event_queue))) + goto failed; + + if (FAILED(hr = MFCreateEventQueue(&renderer->stream_event_queue))) + goto failed; + + if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, node))) + goto failed; + + IMFTopologyNode_SetObject(*node, (IUnknown *)&renderer->IMFStreamSink_iface); + IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); + +failed: + + IMFMediaSink_Release(&renderer->IMFMediaSink_iface); + + return hr; +}
Please ignore patches 2,3, and 4. There might be a better way with less additional code.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfmediaengine/renderer.c | 207 +++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 4 deletions(-)
diff --git a/dlls/mfmediaengine/renderer.c b/dlls/mfmediaengine/renderer.c index 98807f23d0f..d747f42774a 100644 --- a/dlls/mfmediaengine/renderer.c +++ b/dlls/mfmediaengine/renderer.c @@ -29,6 +29,34 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+static const char *debugstr_time(LONGLONG time) +{ + ULONGLONG abstime = time >= 0 ? time : -time; + unsigned int i = 0, j = 0; + char buffer[23], rev[23]; + + while (abstime || i <= 8) + { + buffer[i++] = '0' + (abstime % 10); + abstime /= 10; + if (i == 7) buffer[i++] = '.'; + } + if (time < 0) buffer[i++] = '-'; + + while (i--) rev[j++] = buffer[i]; + while (rev[j-1] == '0' && rev[j-2] != '.') --j; + rev[j] = 0; + + return wine_dbg_sprintf("%s", rev); +} + +enum stream_state +{ + STREAM_STATE_STOPPED = 0, + STREAM_STATE_RUNNING, + STREAM_STATE_PAUSED, +}; + enum video_renderer_flags { FLAG_SHUT_DOWN = 0x1, @@ -39,11 +67,14 @@ struct video_renderer IMFMediaSink IMFMediaSink_iface; IMFStreamSink IMFStreamSink_iface; IMFMediaTypeHandler IMFMediaTypeHandler_iface; + IMFClockStateSink IMFClockStateSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; LONG refcount; IMFMediaEventQueue *event_queue; IMFMediaEventQueue *stream_event_queue; + IMFPresentationClock *clock; unsigned int flags; + unsigned int state; CRITICAL_SECTION cs; };
@@ -52,6 +83,11 @@ static struct video_renderer *impl_from_IMFMediaSink(IMFMediaSink *iface) return CONTAINING_RECORD(iface, struct video_renderer, IMFMediaSink_iface); }
+static struct video_renderer *impl_from_IMFClockStateSink(IMFClockStateSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_renderer, IMFClockStateSink_iface); +} + static struct video_renderer *impl_from_IMFStreamSink(IMFStreamSink *iface) { return CONTAINING_RECORD(iface, struct video_renderer, IMFStreamSink_iface); @@ -78,6 +114,10 @@ static HRESULT WINAPI video_renderer_sink_QueryInterface(IMFMediaSink *iface, RE { *obj = iface; } + else if (IsEqualIID(riid, &IID_IMFClockStateSink)) + { + *obj = &renderer->IMFClockStateSink_iface; + } else if (IsEqualIID(riid, &IID_IMFMediaEventGenerator)) { *obj = &renderer->IMFMediaEventGenerator_iface; @@ -115,6 +155,8 @@ static ULONG WINAPI video_renderer_sink_Release(IMFMediaSink *iface) IMFMediaEventQueue_Release(renderer->event_queue); if (renderer->stream_event_queue) IMFMediaEventQueue_Release(renderer->stream_event_queue); + if (renderer->clock) + IMFPresentationClock_Release(renderer->clock); DeleteCriticalSection(&renderer->cs); heap_free(renderer); } @@ -222,19 +264,66 @@ static HRESULT WINAPI video_renderer_sink_GetStreamSinkById(IMFMediaSink *iface, return hr; }
+static void video_renderer_set_presentation_clock(struct video_renderer *renderer, IMFPresentationClock *clock) +{ + if (renderer->clock) + { + IMFPresentationClock_RemoveClockStateSink(renderer->clock, &renderer->IMFClockStateSink_iface); + IMFPresentationClock_Release(renderer->clock); + } + renderer->clock = clock; + if (renderer->clock) + { + IMFPresentationClock_AddRef(renderer->clock); + IMFPresentationClock_AddClockStateSink(renderer->clock, &renderer->IMFClockStateSink_iface); + } +} + static HRESULT WINAPI video_renderer_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock) { - FIXME("%p, %p.\n", iface, clock); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, clock); + + EnterCriticalSection(&renderer->cs); + + if (renderer->flags & FLAG_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + video_renderer_set_presentation_clock(renderer, clock); + + LeaveCriticalSection(&renderer->cs); + + return hr; }
static HRESULT WINAPI video_renderer_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock) { - FIXME("%p, %p.\n", iface, clock); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, clock); + + if (!clock) + return E_POINTER; + + EnterCriticalSection(&renderer->cs); + + if (renderer->flags & FLAG_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (renderer->clock) + { + *clock = renderer->clock; + IMFPresentationClock_AddRef(*clock); + } + else + hr = MF_E_NO_CLOCK; + + LeaveCriticalSection(&renderer->cs); + + return hr; }
static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface) @@ -250,6 +339,7 @@ static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface) renderer->flags |= FLAG_SHUT_DOWN; IMFMediaEventQueue_Shutdown(renderer->event_queue); IMFMediaEventQueue_Shutdown(renderer->stream_event_queue); + video_renderer_set_presentation_clock(renderer, NULL); LeaveCriticalSection(&renderer->cs);
return S_OK; @@ -624,6 +714,114 @@ static const IMFMediaEventGeneratorVtbl video_renderer_events_vtbl = video_renderer_events_QueueEvent, };
+static HRESULT WINAPI video_renderer_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_QueryInterface(&renderer->IMFMediaSink_iface, riid, obj); +} + +static ULONG WINAPI video_renderer_clock_sink_AddRef(IMFClockStateSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_AddRef(&renderer->IMFMediaSink_iface); +} + +static ULONG WINAPI video_renderer_clock_sink_Release(IMFClockStateSink *iface) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + return IMFMediaSink_Release(&renderer->IMFMediaSink_iface); +} + +static HRESULT WINAPI video_renderer_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s, %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); + + EnterCriticalSection(&renderer->cs); + + if (renderer->state == STREAM_STATE_STOPPED) + renderer->state = STREAM_STATE_RUNNING; + + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + + LeaveCriticalSection(&renderer->cs); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&renderer->cs); + + renderer->state = STREAM_STATE_STOPPED; + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStopped, &GUID_NULL, S_OK, NULL); + + LeaveCriticalSection(&renderer->cs); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&renderer->cs); + + if (renderer->state == STREAM_STATE_RUNNING) + { + renderer->state = STREAM_STATE_PAUSED; + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkPaused, &GUID_NULL, S_OK, NULL); + } + else + hr = MF_E_INVALID_STATE_TRANSITION; + + LeaveCriticalSection(&renderer->cs); + + return hr; +} + +static HRESULT WINAPI video_renderer_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) +{ + struct video_renderer *renderer = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + EnterCriticalSection(&renderer->cs); + + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + + LeaveCriticalSection(&renderer->cs); + + return S_OK; +} + +static HRESULT WINAPI video_renderer_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) +{ + FIXME("%p, %s, %f.\n", iface, debugstr_time(systime), rate); + + return E_NOTIMPL; +} + +static const IMFClockStateSinkVtbl video_renderer_clock_sink_vtbl = +{ + video_renderer_clock_sink_QueryInterface, + video_renderer_clock_sink_AddRef, + video_renderer_clock_sink_Release, + video_renderer_clock_sink_OnClockStart, + video_renderer_clock_sink_OnClockStop, + video_renderer_clock_sink_OnClockPause, + video_renderer_clock_sink_OnClockRestart, + video_renderer_clock_sink_OnClockSetRate, +}; + HRESULT media_engine_create_video_renderer(IMFTopologyNode **node) { struct video_renderer *renderer; @@ -638,6 +836,7 @@ HRESULT media_engine_create_video_renderer(IMFTopologyNode **node) renderer->IMFStreamSink_iface.lpVtbl = &video_renderer_stream_vtbl; renderer->IMFMediaTypeHandler_iface.lpVtbl = &video_renderer_stream_type_handler_vtbl; renderer->IMFMediaEventGenerator_iface.lpVtbl = &video_renderer_events_vtbl; + renderer->IMFClockStateSink_iface.lpVtbl = &video_renderer_clock_sink_vtbl; renderer->refcount = 1; InitializeCriticalSection(&renderer->cs);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfmediaengine/renderer.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/dlls/mfmediaengine/renderer.c b/dlls/mfmediaengine/renderer.c index d747f42774a..0d558e16963 100644 --- a/dlls/mfmediaengine/renderer.c +++ b/dlls/mfmediaengine/renderer.c @@ -513,6 +513,13 @@ static HRESULT WINAPI video_renderer_stream_ProcessSample(IMFStreamSink *iface,
/* FIXME: samples are dropped */
+ EnterCriticalSection(&renderer->cs); + + if (renderer->state == STREAM_STATE_RUNNING) + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL); + + LeaveCriticalSection(&renderer->cs); + return S_OK; }
@@ -741,9 +748,12 @@ static HRESULT WINAPI video_renderer_clock_sink_OnClockStart(IMFClockStateSink * EnterCriticalSection(&renderer->cs);
if (renderer->state == STREAM_STATE_STOPPED) - renderer->state = STREAM_STATE_RUNNING; + { + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL);
- IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + renderer->state = STREAM_STATE_RUNNING; + }
LeaveCriticalSection(&renderer->cs);
@@ -796,7 +806,13 @@ static HRESULT WINAPI video_renderer_clock_sink_OnClockRestart(IMFClockStateSink
EnterCriticalSection(&renderer->cs);
- IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + if (renderer->state != STREAM_STATE_RUNNING) + { + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); + IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL); + + renderer->state = STREAM_STATE_RUNNING; + }
LeaveCriticalSection(&renderer->cs);