Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/sample.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c index a44e3ef45c6..e84d7450c7f 100644 --- a/dlls/evr/sample.c +++ b/dlls/evr/sample.c @@ -796,15 +796,23 @@ static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback { struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface); struct queued_sample *iter; - IUnknown *sample = NULL; + IUnknown *object = NULL; + IMFSample *sample = NULL; + HRESULT hr;
- EnterCriticalSection(&allocator->cs); + if (FAILED(IMFAsyncResult_GetObject(result, &object))) + return E_UNEXPECTED;
- IMFAsyncResult_GetObject(result, (IUnknown **)&sample); + hr = IUnknown_QueryInterface(object, &IID_IMFSample, (void **)&sample); + IUnknown_Release(object); + if (FAILED(hr)) + return E_UNEXPECTED; + + EnterCriticalSection(&allocator->cs);
LIST_FOR_EACH_ENTRY(iter, &allocator->used_samples, struct queued_sample, entry) { - if (sample == (IUnknown *)iter->sample) + if (sample == iter->sample) { list_remove(&iter->entry); list_add_tail(&allocator->free_samples, &iter->entry); @@ -813,7 +821,7 @@ static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback } }
- IUnknown_Release(sample); + IMFSample_Release(sample);
if (allocator->callback) IMFVideoSampleAllocatorNotify_NotifyRelease(allocator->callback);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/sample.c | 6 ++++-- dlls/evr/tests/evr.c | 7 +++---- 2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c index e84d7450c7f..7764ec945d2 100644 --- a/dlls/evr/sample.c +++ b/dlls/evr/sample.c @@ -459,7 +459,6 @@ static void sample_allocator_release_samples(struct sample_allocator *allocator) LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &allocator->used_samples, struct queued_sample, entry) { list_remove(&iter->entry); - IMFSample_Release(iter->sample); heap_free(iter); } } @@ -676,8 +675,10 @@ static HRESULT WINAPI sample_allocator_AllocateSample(IMFVideoSampleAllocator *i list_add_tail(&allocator->used_samples, head); allocator->free_sample_count--;
+ /* Reference counter is not increased when sample is returned, so next release could trigger + tracking condition. This is balanced by incremented reference counter when sample is returned + back to the free list. */ *out = sample; - IMFSample_AddRef(*out); } }
@@ -816,6 +817,7 @@ static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback { list_remove(&iter->entry); list_add_tail(&allocator->free_samples, &iter->entry); + IMFSample_AddRef(iter->sample); allocator->free_sample_count++; break; } diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index a1d7b7109d9..b8a16fc7cc9 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1326,13 +1326,13 @@ static void test_MFCreateVideoSampleAllocator(void) IDirect3DSurface9 *surface; IDirect3DDevice9 *device; IMFMediaBuffer *buffer; + LONG refcount, count; unsigned int token; IMFGetService *gs; IDirect3D9 *d3d; IUnknown *unk; HWND window; HRESULT hr; - LONG count; BYTE *data;
hr = MFCreateVideoSampleAllocator(&IID_IUnknown, (void **)&unk); @@ -1397,7 +1397,7 @@ todo_wine sample = NULL; hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); - ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample)); + refcount = get_refcount(sample);
hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample2); ok(hr == MF_E_SAMPLEALLOCATOR_EMPTY, "Unexpected hr %#x.\n", hr); @@ -1405,8 +1405,7 @@ todo_wine /* Reinitialize with active sample. */ hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 4, video_type); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); -todo_wine - ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample)); + ok(refcount == get_refcount(sample), "Unexpected refcount %u.\n", get_refcount(sample));
hr = IMFVideoSampleAllocatorCallback_GetFreeSampleCount(allocator_cb, &count); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index d586f1c6be9..af8500cb7d6 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -171,6 +171,7 @@ struct topo_node struct { unsigned int requests; + IMFVideoSampleAllocator *allocator; } sink; struct { @@ -742,6 +743,10 @@ static void release_topo_node(struct topo_node *node) heap_free(node->u.transform.input_map); heap_free(node->u.transform.output_map); break; + case MF_TOPOLOGY_OUTPUT_NODE: + if (node->u.sink.allocator) + IMFVideoSampleAllocator_Release(node->u.sink.allocator); + break; default: ; } @@ -1185,10 +1190,25 @@ static HRESULT session_set_transform_stream_info(struct topo_node *node) return hr; }
+static HRESULT session_get_stream_sink_type(IMFStreamSink *sink, IMFMediaType **media_type) +{ + IMFMediaTypeHandler *handler; + HRESULT hr; + + if (SUCCEEDED(hr = IMFStreamSink_GetMediaTypeHandler(sink, &handler))) + { + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, media_type); + IMFMediaTypeHandler_Release(handler); + } + + return hr; +} + static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node) { struct topo_node *topo_node; IMFMediaSink *media_sink; + IMFMediaType *media_type; IMFStreamDescriptor *sd; HRESULT hr = S_OK; IUnknown *object; @@ -1217,7 +1237,18 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod if (FAILED(hr = IMFStreamSink_GetMediaSink(topo_node->object.sink_stream, &media_sink))) break;
- hr = session_add_media_sink(session, node, media_sink); + if (SUCCEEDED(hr = session_add_media_sink(session, node, media_sink))) + { + if (SUCCEEDED(session_get_stream_sink_type(topo_node->object.sink_stream, &media_type))) + { + if (SUCCEEDED(MFGetService(topo_node->object.object, &MR_VIDEO_ACCELERATION_SERVICE, + &IID_IMFVideoSampleAllocator, (void **)&topo_node->u.sink.allocator))) + { + IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 2, media_type); + } + IMFMediaType_Release(media_type); + } + } IMFMediaSink_Release(media_sink);
break;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 173 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 27 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index af8500cb7d6..cf351c003e7 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -45,6 +45,7 @@ enum session_command /* Internally used commands. */ SESSION_CMD_END, SESSION_CMD_QM_NOTIFY_TOPOLOGY, + SESSION_CMD_SA_READY, };
struct session_op @@ -68,6 +69,10 @@ struct session_op { IMFTopology *topology; } notify_topology; + struct + { + TOPOID node_id; + } sa_ready; } u; struct list entry; }; @@ -148,6 +153,7 @@ enum topo_node_flags struct topo_node { struct list entry; + struct media_session *session; MF_TOPOLOGY_TYPE type; TOPOID node_id; IMFTopologyNode *node; @@ -171,7 +177,9 @@ struct topo_node struct { unsigned int requests; + IMFVideoSampleAllocatorNotify notify_cb; IMFVideoSampleAllocator *allocator; + IMFVideoSampleAllocatorCallback *allocator_cb; } sink; struct { @@ -400,6 +408,11 @@ static struct quality_manager *impl_from_IMFQualityManager(IMFQualityManager *if return CONTAINING_RECORD(iface, struct quality_manager, IMFQualityManager_iface); }
+static struct topo_node *impl_node_from_IMFVideoSampleAllocatorNotify(IMFVideoSampleAllocatorNotify *iface) +{ + return CONTAINING_RECORD(iface, struct topo_node, u.sink.notify_cb); +} + /* IMFLocalMFTRegistration */ static HRESULT WINAPI local_mft_registration_QueryInterface(IMFLocalMFTRegistration *iface, REFIID riid, void **obj) { @@ -746,6 +759,11 @@ static void release_topo_node(struct topo_node *node) case MF_TOPOLOGY_OUTPUT_NODE: if (node->u.sink.allocator) IMFVideoSampleAllocator_Release(node->u.sink.allocator); + if (node->u.sink.allocator_cb) + { + IMFVideoSampleAllocatorCallback_SetCallback(node->u.sink.allocator_cb, NULL); + IMFVideoSampleAllocatorCallback_Release(node->u.sink.allocator_cb); + } break; default: ; @@ -804,6 +822,19 @@ static void session_clear_presentation(struct media_session *session) } }
+static struct topo_node *session_get_node_by_id(const struct media_session *session, TOPOID id) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->node_id == id) + return node; + } + + return NULL; +} + static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; @@ -1204,6 +1235,56 @@ static HRESULT session_get_stream_sink_type(IMFStreamSink *sink, IMFMediaType ** return hr; }
+static HRESULT WINAPI node_sample_allocator_cb_QueryInterface(IMFVideoSampleAllocatorNotify *iface, + REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFVideoSampleAllocatorNotify) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFVideoSampleAllocatorNotify_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI node_sample_allocator_cb_AddRef(IMFVideoSampleAllocatorNotify *iface) +{ + return 2; +} + +static ULONG WINAPI node_sample_allocator_cb_Release(IMFVideoSampleAllocatorNotify *iface) +{ + return 1; +} + +static HRESULT session_request_sample_from_node(struct media_session *session, IMFTopologyNode *node, DWORD output); + +static HRESULT WINAPI node_sample_allocator_cb_NotifyRelease(IMFVideoSampleAllocatorNotify *iface) +{ + struct topo_node *topo_node = impl_node_from_IMFVideoSampleAllocatorNotify(iface); + struct session_op *op; + + if (SUCCEEDED(create_session_op(SESSION_CMD_SA_READY, &op))) + { + op->u.sa_ready.node_id = topo_node->node_id; + MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &topo_node->session->commands_callback, &op->IUnknown_iface); + IUnknown_Release(&op->IUnknown_iface); + } + + return S_OK; +} + +static const IMFVideoSampleAllocatorNotifyVtbl node_sample_allocator_cb_vtbl = +{ + node_sample_allocator_cb_QueryInterface, + node_sample_allocator_cb_AddRef, + node_sample_allocator_cb_Release, + node_sample_allocator_cb_NotifyRelease, +}; + static HRESULT session_append_node(struct media_session *session, IMFTopologyNode *node) { struct topo_node *topo_node; @@ -1220,10 +1301,13 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod IMFTopologyNode_GetTopoNodeID(node, &topo_node->node_id); topo_node->node = node; IMFTopologyNode_AddRef(topo_node->node); + topo_node->session = session;
switch (topo_node->type) { case MF_TOPOLOGY_OUTPUT_NODE: + topo_node->u.sink.notify_cb.lpVtbl = &node_sample_allocator_cb_vtbl; + if (FAILED(hr = IMFTopologyNode_GetObject(node, &object))) { WARN("Node %s does not have associated object.\n", wine_dbgstr_longlong(topo_node->node_id)); @@ -1245,6 +1329,10 @@ static HRESULT session_append_node(struct media_session *session, IMFTopologyNod &IID_IMFVideoSampleAllocator, (void **)&topo_node->u.sink.allocator))) { IMFVideoSampleAllocator_InitializeSampleAllocator(topo_node->u.sink.allocator, 2, media_type); + IMFVideoSampleAllocator_QueryInterface(topo_node->u.sink.allocator, + &IID_IMFVideoSampleAllocatorCallback, (void **)&topo_node->u.sink.allocator_cb); + IMFVideoSampleAllocatorCallback_SetCallback(topo_node->u.sink.allocator_cb, + &topo_node->u.sink.notify_cb); } IMFMediaType_Release(media_type); } @@ -1955,6 +2043,9 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, { struct session_op *op = impl_op_from_IUnknown(IMFAsyncResult_GetStateNoAddRef(result)); struct media_session *session = impl_from_commands_callback_IMFAsyncCallback(iface); + struct topo_node *topo_node; + IMFTopologyNode *upstream_node; + unsigned int upstream_output;
EnterCriticalSection(&session->cs);
@@ -1985,6 +2076,18 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, case SESSION_CMD_QM_NOTIFY_TOPOLOGY: IMFQualityManager_NotifyTopology(session->quality_manager, op->u.notify_topology.topology); break; + case SESSION_CMD_SA_READY: + topo_node = session_get_node_by_id(session, op->u.sa_ready.node_id); + + if (topo_node->u.sink.requests) + { + if (SUCCEEDED(IMFTopologyNode_GetInput(topo_node->node, 0, &upstream_node, &upstream_output))) + { + session_request_sample_from_node(session, upstream_node, upstream_output); + IMFTopologyNode_Release(upstream_node); + } + } + break; default: ; } @@ -2463,19 +2566,6 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre } }
-static struct topo_node *session_get_node_by_id(struct media_session *session, TOPOID id) -{ - struct topo_node *node; - - LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) - { - if (node->node_id == id) - return node; - } - - return NULL; -} - static DWORD transform_node_get_stream_id(struct topo_node *node, BOOL output, DWORD index) { unsigned int *map = output ? node->u.transform.output_map : node->u.transform.input_map; @@ -2496,6 +2586,47 @@ static struct sample *transform_create_sample(IMFSample *sample) return sample_entry; }
+static HRESULT transform_get_external_output_sample(const struct media_session *session, struct topo_node *transform, + unsigned int output_index, const MFT_OUTPUT_STREAM_INFO *stream_info, IMFSample **sample) +{ + IMFTopologyNode *downstream_node; + unsigned int downstream_input; + IMFMediaBuffer *buffer = NULL; + struct topo_node *topo_node; + TOPOID node_id; + HRESULT hr; + + if (FAILED(IMFTopologyNode_GetOutput(transform->node, output_index, &downstream_node, &downstream_input))) + { + WARN("Failed to get connected node for output %u.\n", output_index); + return MF_E_UNEXPECTED; + } + + IMFTopologyNode_GetTopoNodeID(downstream_node, &node_id); + IMFTopologyNode_Release(downstream_node); + + topo_node = session_get_node_by_id(session, node_id); + + if (topo_node->type == MF_TOPOLOGY_OUTPUT_NODE && topo_node->u.sink.allocator) + { + hr = IMFVideoSampleAllocator_AllocateSample(topo_node->u.sink.allocator, sample); + } + else + { + hr = MFCreateAlignedMemoryBuffer(stream_info->cbSize, stream_info->cbAlignment, &buffer); + if (SUCCEEDED(hr)) + hr = MFCreateSample(sample); + + if (SUCCEEDED(hr)) + hr = IMFSample_AddBuffer(*sample, buffer); + + if (buffer) + IMFMediaBuffer_Release(buffer); + } + + return hr; +} + static HRESULT transform_node_pull_samples(const struct media_session *session, struct topo_node *node) { MFT_OUTPUT_STREAM_INFO stream_info; @@ -2503,7 +2634,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session, struct sample *queued_sample; DWORD status = 0; unsigned int i; - HRESULT hr; + HRESULT hr = E_UNEXPECTED;
if (!(buffers = heap_calloc(node->u.transform.output_count, sizeof(*buffers)))) return E_OUTOFMEMORY; @@ -2521,19 +2652,7 @@ static HRESULT transform_node_pull_samples(const struct media_session *session,
if (!(stream_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) { - IMFMediaBuffer *buffer = NULL; - - hr = MFCreateAlignedMemoryBuffer(stream_info.cbSize, stream_info.cbAlignment, &buffer); - if (SUCCEEDED(hr)) - hr = MFCreateSample(&buffers[i].pSample); - - if (SUCCEEDED(hr)) - hr = IMFSample_AddBuffer(buffers[i].pSample, buffer); - - if (buffer) - IMFMediaBuffer_Release(buffer); - - if (FAILED(hr)) + if (FAILED(hr = transform_get_external_output_sample(session, node, i, &stream_info, &buffers[i].pSample))) break; } }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/evr.c | 19 ++++++++++++++++--- dlls/mf/tests/Makefile.in | 2 +- dlls/mf/tests/mf.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/evr.c b/dlls/mf/evr.c index d01bb0f06f1..f14f2f46612 100644 --- a/dlls/mf/evr.c +++ b/dlls/mf/evr.c @@ -84,6 +84,7 @@ struct video_renderer
IMFTransform *mixer; IMFVideoPresenter *presenter; + HWND window; IUnknown *device_manager; unsigned int flags; unsigned int state; @@ -1098,13 +1099,18 @@ static HRESULT video_renderer_create_mixer(IMFAttributes *attributes, IMFTransfo return CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)out); }
-static HRESULT video_renderer_create_presenter(IMFAttributes *attributes, IMFVideoPresenter **out) +static HRESULT video_renderer_create_presenter(struct video_renderer *renderer, IMFAttributes *attributes, + IMFVideoPresenter **out) { unsigned int flags = 0; IMFActivate *activate; + UINT64 value; CLSID clsid; HRESULT hr;
+ if (attributes && SUCCEEDED(IMFAttributes_GetUINT64(attributes, &MF_ACTIVATE_VIDEO_WINDOW, &value))) + renderer->window = UlongToHandle(value); + if (attributes && SUCCEEDED(IMFAttributes_GetUnknown(attributes, &MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, &IID_IMFActivate, (void **)&activate))) { @@ -1193,8 +1199,15 @@ static HRESULT video_renderer_configure_mixer(struct video_renderer *renderer) static HRESULT video_renderer_configure_presenter(struct video_renderer *renderer) { IMFTopologyServiceLookupClient *lookup_client; + IMFVideoDisplayControl *control; HRESULT hr;
+ if (SUCCEEDED(IMFVideoPresenter_QueryInterface(renderer->presenter, &IID_IMFVideoDisplayControl, (void **)&control))) + { + IMFVideoDisplayControl_SetVideoWindow(control, renderer->window); + IMFVideoDisplayControl_Release(control); + } + if (SUCCEEDED(hr = IMFVideoPresenter_QueryInterface(renderer->presenter, &IID_IMFTopologyServiceLookupClient, (void **)&lookup_client))) { @@ -1270,7 +1283,7 @@ static HRESULT WINAPI video_renderer_InitializeRenderer(IMFVideoRenderer *iface,
if (presenter) IMFVideoPresenter_AddRef(presenter); - else if (FAILED(hr = video_renderer_create_presenter(NULL, &presenter))) + else if (FAILED(hr = video_renderer_create_presenter(renderer, NULL, &presenter))) { WARN("Failed to create default presenter, hr %#x.\n", hr); IMFTransform_Release(mixer); @@ -2141,7 +2154,7 @@ static HRESULT evr_create_object(IMFAttributes *attributes, void *user_context, if (FAILED(hr = video_renderer_create_mixer(attributes, &mixer))) goto failed;
- if (FAILED(hr = video_renderer_create_presenter(attributes, &presenter))) + if (FAILED(hr = video_renderer_create_presenter(object, attributes, &presenter))) goto failed;
if (FAILED(hr = video_renderer_initialize(object, mixer, presenter))) diff --git a/dlls/mf/tests/Makefile.in b/dlls/mf/tests/Makefile.in index b26c8630063..5eb9ee4d4e3 100644 --- a/dlls/mf/tests/Makefile.in +++ b/dlls/mf/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mf.dll -IMPORTS = mf mfplat mfuuid ole32 +IMPORTS = mf mfplat mfuuid ole32 user32
C_SRCS = \ mf.c diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 7780bf4ea58..9eccafee633 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -56,6 +56,16 @@ static void _expect_ref(IUnknown* obj, ULONG expected_refcount, int line) expected_refcount); }
+static HWND create_window(void) +{ + RECT r = {0, 0, 640, 480}; + + AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE); + + return CreateWindowA("static", "mf_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, r.right - r.left, r.bottom - r.top, NULL, NULL, NULL, NULL); +} + static WCHAR *load_resource(const WCHAR *name) { static WCHAR pathW[MAX_PATH]; @@ -3235,6 +3245,7 @@ static void test_evr(void) IMFMediaSink *sink, *sink2; IMFAttributes *attributes; IMFActivate *activate; + HWND window, window2; DWORD flags, count; LONG sample_count; IMFGetService *gs; @@ -3258,6 +3269,32 @@ static void test_evr(void) hr = MFCreateVideoRendererActivate(NULL, NULL); ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+ /* Window */ + window = create_window(); + hr = MFCreateVideoRendererActivate(window, &activate); + ok(hr == S_OK, "Failed to create activate object, hr %#x.\n", hr); + + hr = IMFActivate_GetUINT64(activate, &MF_ACTIVATE_VIDEO_WINDOW, &value); + ok(hr == S_OK, "Failed to get attribute, hr %#x.\n", hr); + ok(UlongToHandle(value) == window, "Unexpected value.\n"); + + hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = MFGetService((IUnknown *)sink, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDisplayControl, + (void **)&display_control); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + window2 = NULL; + hr = IMFVideoDisplayControl_GetVideoWindow(display_control, &window2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(window2 == window, "Unexpected window %p.\n", window2); + + IMFVideoDisplayControl_Release(display_control); + IMFMediaSink_Release(sink); + IMFActivate_Release(activate); + DestroyWindow(window); + hr = MFCreateVideoRendererActivate(NULL, &activate); ok(hr == S_OK, "Failed to create activate object, hr %#x.\n", hr);