From e272e1a1c321de601a4b175bdb657f8f40baa34e Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Thu, 12 Nov 2020 13:36:38 +0300 Subject: [PATCH] mf/session: Implement support for sinks that provide sample allocators. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- 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; } }