Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/evr/mixer.c b/dlls/evr/mixer.c index d9f86a7fc91..7e08beae353 100644 --- a/dlls/evr/mixer.c +++ b/dlls/evr/mixer.c @@ -284,7 +284,7 @@ static HRESULT WINAPI video_mixer_transform_GetStreamLimits(IMFTransform *iface, TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum);
*input_minimum = 1; - *input_maximum = 16; + *input_maximum = MAX_MIXER_INPUT_STREAMS; *output_minimum = 1; *output_maximum = 1;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/evr.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/dlls/mf/evr.c b/dlls/mf/evr.c index 8a4aa3c350d..fe73f6a4a0c 100644 --- a/dlls/mf/evr.c +++ b/dlls/mf/evr.c @@ -734,7 +734,7 @@ static HRESULT video_renderer_create_mixer(struct video_renderer *renderer, IMFA CLSID clsid; HRESULT hr;
- if (SUCCEEDED(IMFAttributes_GetUnknown(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_MIXER_ACTIVATE, + if (SUCCEEDED(hr = IMFAttributes_GetUnknown(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_MIXER_ACTIVATE, &IID_IMFActivate, (void **)&activate))) { IMFAttributes_GetUINT32(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_MIXER_FLAGS, &flags); @@ -744,23 +744,31 @@ static HRESULT video_renderer_create_mixer(struct video_renderer *renderer, IMFA return hr; }
- if (FAILED(IMFAttributes_GetGUID(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_MIXER_CLSID, &clsid))) - memcpy(&clsid, &CLSID_MFVideoMixer9, sizeof(clsid)); + /* Activation object failed, use class activation. */ + if (FAILED(hr)) + { + if (FAILED(IMFAttributes_GetGUID(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_MIXER_CLSID, &clsid))) + memcpy(&clsid, &CLSID_MFVideoMixer9, sizeof(clsid)); + hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)out); + }
- if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)out))) + if (FAILED(hr)) { - if (SUCCEEDED(hr = IMFTransform_QueryInterface(*out, &IID_IMFTopologyServiceLookupClient, - (void **)&lookup_client))) + WARN("Failed to create a mixer object, hr %#x.\n", hr); + return hr; + } + + if (SUCCEEDED(hr = IMFTransform_QueryInterface(*out, &IID_IMFTopologyServiceLookupClient, + (void **)&lookup_client))) + { + renderer->flags |= EVR_INIT_SERVICES; + if (SUCCEEDED(hr = IMFTopologyServiceLookupClient_InitServicePointers(lookup_client, + &renderer->IMFTopologyServiceLookup_iface))) { - renderer->flags |= EVR_INIT_SERVICES; - if (SUCCEEDED(hr = IMFTopologyServiceLookupClient_InitServicePointers(lookup_client, - &renderer->IMFTopologyServiceLookup_iface))) - { - renderer->flags |= EVR_MIXER_INITED_SERVICES; - } - renderer->flags &= ~EVR_INIT_SERVICES; - IMFTopologyServiceLookupClient_Release(lookup_client); + renderer->flags |= EVR_MIXER_INITED_SERVICES; } + renderer->flags &= ~EVR_INIT_SERVICES; + IMFTopologyServiceLookupClient_Release(lookup_client); }
return hr;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/evr.c | 347 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 334 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/evr.c b/dlls/mf/evr.c index fe73f6a4a0c..86242611a47 100644 --- a/dlls/mf/evr.c +++ b/dlls/mf/evr.c @@ -32,6 +32,16 @@ enum video_renderer_flags EVR_PRESENTER_INITED_SERVICES = 0x8, };
+struct video_renderer; + +struct video_stream +{ + IMFStreamSink IMFStreamSink_iface; + LONG refcount; + unsigned int id; + struct video_renderer *parent; +}; + struct video_renderer { IMFMediaSink IMFMediaSink_iface; @@ -50,6 +60,11 @@ struct video_renderer IMFTransform *mixer; IMFVideoPresenter *presenter; unsigned int flags; + + struct video_stream **streams; + size_t stream_size; + size_t stream_count; + CRITICAL_SECTION cs; };
@@ -93,6 +108,11 @@ static struct video_renderer *impl_from_IMediaEventSink(IMediaEventSink *iface) return CONTAINING_RECORD(iface, struct video_renderer, IMediaEventSink_iface); }
+static struct video_stream *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct video_stream, IMFStreamSink_iface); +} + static void video_renderer_release_services(struct video_renderer *renderer) { IMFTopologyServiceLookupClient *lookup_client; @@ -114,6 +134,176 @@ static void video_renderer_release_services(struct video_renderer *renderer) } }
+static HRESULT WINAPI video_stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFStreamSink_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI video_stream_sink_AddRef(IMFStreamSink *iface) +{ + struct video_stream *stream = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedIncrement(&stream->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI video_stream_sink_Release(IMFStreamSink *iface) +{ + struct video_stream *stream = impl_from_IMFStreamSink(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + + if (!refcount) + { + heap_free(stream); + } + + return refcount; +} + +static HRESULT WINAPI video_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + FIXME("%p, %#x, %p.\n", iface, flags, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + FIXME("%p, %p, %p.\n", iface, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + FIXME("%p, %p, %p.\n", iface, result, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID exttype, HRESULT hr_status, const PROPVARIANT *value) +{ + FIXME("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(exttype), hr_status, value); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) +{ + struct video_stream *stream = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, sink); + + if (!stream->parent) + return MF_E_STREAMSINK_REMOVED; + + if (!sink) + return E_POINTER; + + /* FIXME: not entirely safe if sink is being shut down. */ + *sink = &stream->parent->IMFMediaSink_iface; + IMFMediaSink_AddRef(*sink); + + return S_OK; +} + +static HRESULT WINAPI video_stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *id) +{ + struct video_stream *stream = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, id); + + if (!stream->parent) + return MF_E_STREAMSINK_REMOVED; + + if (!id) + return E_INVALIDARG; + + *id = stream->id; + + return S_OK; +} + +static HRESULT WINAPI video_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + FIXME("%p, %p.\n", iface, handler); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + FIXME("%p, %p.\n", iface, sample); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, + const PROPVARIANT *marker_value, const PROPVARIANT *context_value) +{ + FIXME("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_stream_sink_Flush(IMFStreamSink *iface) +{ + FIXME("%p.\n", iface); + + return E_NOTIMPL; +} + +static const IMFStreamSinkVtbl video_stream_sink_vtbl = +{ + video_stream_sink_QueryInterface, + video_stream_sink_AddRef, + video_stream_sink_Release, + video_stream_sink_GetEvent, + video_stream_sink_BeginGetEvent, + video_stream_sink_EndGetEvent, + video_stream_sink_QueueEvent, + video_stream_sink_GetMediaSink, + video_stream_sink_GetIdentifier, + video_stream_sink_GetMediaTypeHandler, + video_stream_sink_ProcessSample, + video_stream_sink_PlaceMarker, + video_stream_sink_Flush, +}; + +static HRESULT video_renderer_stream_create(struct video_renderer *renderer, unsigned int id, + struct video_stream **ret) +{ + struct video_stream *stream; + + if (!(stream = heap_alloc_zero(sizeof(*stream)))) + return E_OUTOFMEMORY; + + stream->IMFStreamSink_iface.lpVtbl = &video_stream_sink_vtbl; + stream->refcount = 1; + stream->parent = renderer; + IMFMediaSink_AddRef(&stream->parent->IMFMediaSink_iface); + stream->id = id; + + *ret = stream; + + return S_OK; +} + static HRESULT WINAPI video_renderer_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj) { struct video_renderer *renderer = impl_from_IMFMediaSink(iface); @@ -203,42 +393,161 @@ static HRESULT WINAPI video_renderer_sink_GetCharacteristics(IMFMediaSink *iface return S_OK; }
-static HRESULT WINAPI video_renderer_sink_AddStreamSink(IMFMediaSink *iface, DWORD stream_sink_id, +static HRESULT WINAPI video_renderer_sink_AddStreamSink(IMFMediaSink *iface, DWORD id, IMFMediaType *media_type, IMFStreamSink **stream_sink) { - FIXME("%p, %#x, %p, %p.\n", iface, stream_sink_id, media_type, stream_sink); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + struct video_stream *stream; + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %#x, %p, %p.\n", iface, id, media_type, stream_sink); + + /* Rely on mixer for stream id validation. */ + + EnterCriticalSection(&renderer->cs); + if (renderer->flags & EVR_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = IMFTransform_AddInputStreams(renderer->mixer, 1, &id))) + { + if (mf_array_reserve((void **)&renderer->streams, &renderer->stream_size, renderer->stream_count + 1, + sizeof(*renderer->streams))) + { + if (SUCCEEDED(hr = video_renderer_stream_create(renderer, id, &stream))) + { + *stream_sink = &stream->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream_sink); + renderer->streams[renderer->stream_count++] = stream; + } + } + else + hr = E_OUTOFMEMORY; + + if (FAILED(hr)) + IMFTransform_DeleteInputStream(renderer->mixer, id); + + } + LeaveCriticalSection(&renderer->cs); + + return hr; }
-static HRESULT WINAPI video_renderer_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id) +static HRESULT WINAPI video_renderer_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD id) { - FIXME("%p, %#x.\n", iface, stream_sink_id); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr; + size_t i;
- return E_NOTIMPL; + TRACE("%p, %#x.\n", iface, id); + + /* Rely on mixer for stream id validation. */ + + EnterCriticalSection(&renderer->cs); + if (renderer->flags & EVR_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = IMFTransform_DeleteInputStream(renderer->mixer, id))) + { + for (i = 0; i < renderer->stream_count; ++i) + { + if (renderer->streams[i]->id == id) + { + IMFStreamSink_Release(&renderer->streams[i]->IMFStreamSink_iface); + renderer->streams[i] = NULL; + if (i < renderer->stream_count - 1) + { + memmove(&renderer->streams[i], &renderer->streams[i+1], + (renderer->stream_count - i - 1) * sizeof(*renderer->streams)); + } + renderer->stream_count--; + break; + } + } + } + LeaveCriticalSection(&renderer->cs); + + return hr; }
static HRESULT WINAPI video_renderer_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count) { - FIXME("%p, %p.\n", iface, count); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, count); + + if (!count) + return E_POINTER; + + EnterCriticalSection(&renderer->cs); + if (renderer->flags & EVR_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!count) + hr = E_POINTER; + else + *count = renderer->stream_count; + LeaveCriticalSection(&renderer->cs); + + return hr; }
static HRESULT WINAPI video_renderer_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index, IMFStreamSink **stream) { - FIXME("%p, %u, %p.\n", iface, index, stream); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %u, %p.\n", iface, index, stream); + + EnterCriticalSection(&renderer->cs); + if (renderer->flags & EVR_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!stream) + hr = E_POINTER; + else if (index >= renderer->stream_count) + hr = E_INVALIDARG; + else + { + *stream = &renderer->streams[index]->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + LeaveCriticalSection(&renderer->cs); + + return hr; }
-static HRESULT WINAPI video_renderer_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id, +static HRESULT WINAPI video_renderer_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD id, IMFStreamSink **stream) { - FIXME("%p, %#x, %p.\n", iface, stream_sink_id, stream); + struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + HRESULT hr = S_OK; + size_t i;
- return E_NOTIMPL; + TRACE("%p, %#x, %p.\n", iface, id, stream); + + EnterCriticalSection(&renderer->cs); + if (renderer->flags & EVR_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (!stream) + hr = E_POINTER; + else + { + for (i = 0; i < renderer->stream_count; ++i) + { + if (renderer->streams[i]->id == id) + break; + } + + if (i == renderer->stream_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + *stream = &renderer->streams[i]->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + } + LeaveCriticalSection(&renderer->cs); + + return hr; }
static void video_renderer_set_presentation_clock(struct video_renderer *renderer, IMFPresentationClock *clock) @@ -305,6 +614,7 @@ static HRESULT WINAPI video_renderer_sink_GetPresentationClock(IMFMediaSink *ifa static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface) { struct video_renderer *renderer = impl_from_IMFMediaSink(iface); + size_t i;
TRACE("%p.\n", iface);
@@ -313,6 +623,17 @@ static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface)
EnterCriticalSection(&renderer->cs); renderer->flags |= EVR_SHUT_DOWN; + /* Detach streams from the sink. */ + for (i = 0; i < renderer->stream_count; ++i) + { + IMFMediaSink_Release(&renderer->streams[i]->parent->IMFMediaSink_iface); + renderer->streams[i]->parent = NULL; + IMFStreamSink_Release(&renderer->streams[i]->IMFStreamSink_iface); + renderer->streams[i] = NULL; + } + heap_free(renderer->streams); + renderer->stream_count = 0; + renderer->stream_size = 0; IMFMediaEventQueue_Shutdown(renderer->event_queue); video_renderer_set_presentation_clock(renderer, NULL); video_renderer_release_services(renderer);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/evr.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/dlls/mf/evr.c b/dlls/mf/evr.c index 86242611a47..93ad06b9493 100644 --- a/dlls/mf/evr.c +++ b/dlls/mf/evr.c @@ -40,6 +40,7 @@ struct video_stream LONG refcount; unsigned int id; struct video_renderer *parent; + IMFMediaEventQueue *event_queue; };
struct video_renderer @@ -167,6 +168,8 @@ static ULONG WINAPI video_stream_sink_Release(IMFStreamSink *iface)
if (!refcount) { + if (stream->event_queue) + IMFMediaEventQueue_Release(stream->event_queue); heap_free(stream); }
@@ -175,32 +178,40 @@ static ULONG WINAPI video_stream_sink_Release(IMFStreamSink *iface)
static HRESULT WINAPI video_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) { - FIXME("%p, %#x, %p.\n", iface, flags, event); + struct video_stream *stream = impl_from_IMFStreamSink(iface);
- return E_NOTIMPL; + TRACE("%p, %#x, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); }
static HRESULT WINAPI video_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, IUnknown *state) { - FIXME("%p, %p, %p.\n", iface, callback, state); + struct video_stream *stream = impl_from_IMFStreamSink(iface);
- return E_NOTIMPL; + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); }
static HRESULT WINAPI video_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, IMFMediaEvent **event) { - FIXME("%p, %p, %p.\n", iface, result, event); + struct video_stream *stream = impl_from_IMFStreamSink(iface);
- return E_NOTIMPL; + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); }
static HRESULT WINAPI video_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, - REFGUID exttype, HRESULT hr_status, const PROPVARIANT *value) + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) { - FIXME("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(exttype), hr_status, value); + struct video_stream *stream = impl_from_IMFStreamSink(iface);
- return E_NOTIMPL; + TRACE("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); }
static HRESULT WINAPI video_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink) @@ -289,12 +300,17 @@ static HRESULT video_renderer_stream_create(struct video_renderer *renderer, uns struct video_stream **ret) { struct video_stream *stream; + HRESULT hr;
if (!(stream = heap_alloc_zero(sizeof(*stream)))) return E_OUTOFMEMORY;
stream->IMFStreamSink_iface.lpVtbl = &video_stream_sink_vtbl; stream->refcount = 1; + + if (FAILED(hr = MFCreateEventQueue(&stream->event_queue))) + return hr; + stream->parent = renderer; IMFMediaSink_AddRef(&stream->parent->IMFMediaSink_iface); stream->id = id; @@ -628,6 +644,7 @@ static HRESULT WINAPI video_renderer_sink_Shutdown(IMFMediaSink *iface) { IMFMediaSink_Release(&renderer->streams[i]->parent->IMFMediaSink_iface); renderer->streams[i]->parent = NULL; + IMFMediaEventQueue_Shutdown(renderer->streams[i]->event_queue); IMFStreamSink_Release(&renderer->streams[i]->IMFStreamSink_iface); renderer->streams[i] = NULL; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index e2a6b868caf..a1b54b7080e 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -24,6 +24,7 @@ #include "windef.h" #include "winbase.h" #include "mfidl.h" +#include "evr.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -1794,6 +1795,57 @@ static HRESULT WINAPI session_get_service_GetService(IMFGetService *iface, REFGU { return IMFLocalMFTRegistration_QueryInterface(&local_mft_registration, riid, obj); } + else if (IsEqualGUID(service, &MR_VIDEO_RENDER_SERVICE)) + { + IMFStreamSink *stream_sink; + IMFTopologyNode *node; + IUnknown *vr, *object; + IMFCollection *nodes; + IMFMediaSink *sink; + unsigned int i = 0; + HRESULT hr; + + EnterCriticalSection(&session->cs); + + /* Use first sink to support IMFVideoRenderer. */ + if (session->presentation.current_topology) + { + if (SUCCEEDED(IMFTopology_GetOutputNodeCollection(session->presentation.current_topology, + &nodes))) + { + while (IMFCollection_GetElement(nodes, i++, (IUnknown **)&node) == S_OK) + { + if (SUCCEEDED(IMFTopologyNode_GetObject(node, &object))) + { + if (SUCCEEDED(IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&stream_sink))) + { + if (SUCCEEDED(IMFStreamSink_GetMediaSink(stream_sink, &sink))) + { + if (SUCCEEDED(IMFMediaSink_QueryInterface(sink, &IID_IMFVideoRenderer, (void **)&vr))) + { + if (FAILED(hr = MFGetService(vr, service, riid, obj))) + WARN("Failed to get service from video renderer %#x.\n", hr); + IUnknown_Release(vr); + } + } + IMFStreamSink_Release(stream_sink); + } + + IUnknown_Release(object); + } + + IMFTopologyNode_Release(node); + + if (*obj) + break; + } + + IMFCollection_Release(nodes); + } + } + + LeaveCriticalSection(&session->cs); + } else FIXME("Unsupported service %s.\n", debugstr_guid(service));