Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 237 ++++++++++++++++++++++++++++++++++++++++------ include/mfapi.h | 10 ++ 2 files changed, 219 insertions(+), 28 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 6fdf773702..c1daef3a91 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -36,6 +36,7 @@ enum session_command { SESSION_CMD_CLEAR_TOPOLOGIES, SESSION_CMD_CLOSE, + SESSION_CMD_SET_TOPOLOGY, };
struct session_op @@ -43,6 +44,14 @@ struct session_op IUnknown IUnknown_iface; LONG refcount; enum session_command command; + union + { + struct + { + DWORD flags; + IMFTopology *topology; + } set_topology; + } u; };
struct queued_topology @@ -71,6 +80,7 @@ struct media_session IMFRateControl *clock_rate_control; IMFTopoLoader *topo_loader; IMFQualityManager *quality_manager; + IMFTopology *current_topology; struct list topologies; enum session_state state; CRITICAL_SECTION cs; @@ -238,6 +248,11 @@ static ULONG WINAPI session_op_Release(IUnknown *iface)
if (!refcount) { + if (op->command == SESSION_CMD_SET_TOPOLOGY) + { + if (op->u.set_topology.topology) + IMFTopology_Release(op->u.set_topology.topology); + } heap_free(op); }
@@ -251,7 +266,7 @@ static IUnknownVtbl session_op_vtbl = session_op_Release, };
-static HRESULT create_session_op(enum session_command command, IUnknown **ret) +static HRESULT create_session_op(enum session_command command, struct session_op **ret) { struct session_op *op;
@@ -262,11 +277,25 @@ static HRESULT create_session_op(enum session_command command, IUnknown **ret) op->refcount = 1; op->command = command;
- *ret = &op->IUnknown_iface; + *ret = op;
return S_OK; }
+static HRESULT session_submit_command(struct media_session *session, struct session_op *op) +{ + HRESULT hr; + + EnterCriticalSection(&session->cs); + if (session->state == SESSION_STATE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, &op->IUnknown_iface); + LeaveCriticalSection(&session->cs); + + return hr; +} + static void session_clear_topologies(struct media_session *session) { struct queued_topology *ptr, *next; @@ -279,6 +308,68 @@ static void session_clear_topologies(struct media_session *session) } }
+static HRESULT session_bind_output_nodes(IMFTopology *topology) +{ + MF_TOPOLOGY_TYPE node_type; + IMFStreamSink *stream_sink; + IMFMediaSink *media_sink; + WORD node_count = 0, i; + IMFTopologyNode *node; + IMFActivate *activate; + UINT32 stream_id; + IUnknown *object; + HRESULT hr; + + hr = IMFTopology_GetNodeCount(topology, &node_count); + + for (i = 0; i < node_count; ++i) + { + if (FAILED(hr = IMFTopology_GetNode(topology, i, &node))) + break; + + if (FAILED(hr = IMFTopologyNode_GetNodeType(node, &node_type)) || node_type != MF_TOPOLOGY_OUTPUT_NODE) + { + IMFTopologyNode_Release(node); + break; + } + + if (SUCCEEDED(hr = IMFTopologyNode_GetObject(node, &object))) + { + stream_sink = NULL; + if (FAILED(IUnknown_QueryInterface(object, &IID_IMFStreamSink, (void **)&stream_sink))) + { + if (SUCCEEDED(hr = IUnknown_QueryInterface(object, &IID_IMFActivate, (void **)&activate))) + { + if (SUCCEEDED(hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&media_sink))) + { + if (FAILED(IMFTopologyNode_GetUINT32(node, &MF_TOPONODE_STREAMID, &stream_id))) + stream_id = 0; + + stream_sink = NULL; + if (FAILED(IMFMediaSink_GetStreamSinkById(media_sink, stream_id, &stream_sink))) + hr = IMFMediaSink_AddStreamSink(media_sink, stream_id, NULL, &stream_sink); + + if (stream_sink) + hr = IMFTopologyNode_SetObject(node, (IUnknown *)stream_sink); + + IMFMediaSink_Release(media_sink); + } + + IMFActivate_Release(activate); + } + } + + if (stream_sink) + IMFStreamSink_Release(stream_sink); + IUnknown_Release(object); + } + + IMFTopologyNode_Release(node); + } + + return hr; +} + static HRESULT WINAPI mfsession_QueryInterface(IMFMediaSession *iface, REFIID riid, void **out) { struct media_session *session = impl_from_IMFMediaSession(iface); @@ -325,6 +416,8 @@ static ULONG WINAPI mfsession_Release(IMFMediaSession *iface) if (!refcount) { session_clear_topologies(session); + if (session->current_topology) + IMFTopology_Release(session->current_topology); if (session->event_queue) IMFMediaEventQueue_Release(session->event_queue); if (session->clock) @@ -382,33 +475,28 @@ static HRESULT WINAPI mfsession_QueueEvent(IMFMediaSession *iface, MediaEventTyp static HRESULT WINAPI mfsession_SetTopology(IMFMediaSession *iface, DWORD flags, IMFTopology *topology) { struct media_session *session = impl_from_IMFMediaSession(iface); - struct queued_topology *queued_topology; - - FIXME("%p, %#x, %p.\n", iface, flags, topology); - - if (!(queued_topology = heap_alloc(sizeof(*queued_topology)))) - return E_OUTOFMEMORY; + struct session_op *op; + WORD node_count = 0; + HRESULT hr;
- queued_topology->topology = topology; - IMFTopology_AddRef(queued_topology->topology); + TRACE("%p, %#x, %p.\n", iface, flags, topology);
- EnterCriticalSection(&session->cs); - list_add_tail(&session->topologies, &queued_topology->entry); - LeaveCriticalSection(&session->cs); + if (topology) + { + if (FAILED(IMFTopology_GetNodeCount(topology, &node_count)) || node_count == 0) + return E_INVALIDARG; + }
- return S_OK; -} + if (FAILED(hr = create_session_op(SESSION_CMD_SET_TOPOLOGY, &op))) + return hr;
-static HRESULT session_submit_command(struct media_session *session, IUnknown *op) -{ - HRESULT hr; + op->u.set_topology.flags = flags; + op->u.set_topology.topology = topology; + if (op->u.set_topology.topology) + IMFTopology_AddRef(op->u.set_topology.topology);
- EnterCriticalSection(&session->cs); - if (session->state == SESSION_STATE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &session->commands_callback, op); - LeaveCriticalSection(&session->cs); + hr = session_submit_command(session, op); + IUnknown_Release(&op->IUnknown_iface);
return hr; } @@ -416,7 +504,7 @@ static HRESULT session_submit_command(struct media_session *session, IUnknown *o static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface) { struct media_session *session = impl_from_IMFMediaSession(iface); - IUnknown *op; + struct session_op *op; HRESULT hr;
TRACE("%p.\n", iface); @@ -425,7 +513,7 @@ static HRESULT WINAPI mfsession_ClearTopologies(IMFMediaSession *iface) return hr;
hr = session_submit_command(session, op); - IUnknown_Release(op); + IUnknown_Release(&op->IUnknown_iface);
return hr; } @@ -454,7 +542,7 @@ static HRESULT WINAPI mfsession_Stop(IMFMediaSession *iface) static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface) { struct media_session *session = impl_from_IMFMediaSession(iface); - IUnknown *op; + struct session_op *op; HRESULT hr;
TRACE("(%p)\n", iface); @@ -463,7 +551,7 @@ static HRESULT WINAPI mfsession_Close(IMFMediaSession *iface) return hr;
hr = session_submit_command(session, op); - IUnknown_Release(op); + IUnknown_Release(&op->IUnknown_iface);
return hr; } @@ -627,6 +715,7 @@ 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); + HRESULT status = S_OK;
switch (op->command) { @@ -638,6 +727,98 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface, IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologiesCleared, &GUID_NULL, S_OK, NULL); break; + case SESSION_CMD_SET_TOPOLOGY: + { + IMFTopology *topology = op->u.set_topology.topology; + MF_TOPOSTATUS topo_status = MF_TOPOSTATUS_INVALID; + struct queued_topology *queued_topology; + DWORD flags = op->u.set_topology.flags; + PROPVARIANT param; + + if (flags & MFSESSION_SETTOPOLOGY_CLEAR_CURRENT) + { + EnterCriticalSection(&session->cs); + if ((topology && topology == session->current_topology) || !topology) + { + /* FIXME: stop current topology, queue next one. */ + if (session->current_topology) + { + IMFTopology_Release(session->current_topology); + session->current_topology = NULL; + } + } + else + status = S_FALSE; + topo_status = MF_TOPOSTATUS_READY; + LeaveCriticalSection(&session->cs); + } + else if (topology) + { + /* Resolve unless claimed to be full. */ + if (!(flags & MFSESSION_SETTOPOLOGY_NORESOLUTION)) + { + IMFTopology *resolved_topology = NULL; + + status = session_bind_output_nodes(topology); + + if (SUCCEEDED(status)) + status = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); + + if (SUCCEEDED(status)) + topology = resolved_topology; + } + + if (SUCCEEDED(status)) + { + if (!(queued_topology = heap_alloc_zero(sizeof(*queued_topology)))) + status = E_OUTOFMEMORY; + else + { + EnterCriticalSection(&session->cs); + + if (flags & MFSESSION_SETTOPOLOGY_IMMEDIATE) + { + session_clear_topologies(session); + if (session->current_topology) + { + IMFTopology_Release(session->current_topology); + session->current_topology = NULL; + } + } + + queued_topology->topology = topology; + IMFTopology_AddRef(queued_topology->topology); + list_add_tail(&session->topologies, &queued_topology->entry); + + LeaveCriticalSection(&session->cs); + } + } + } + + if (topology) + { + param.vt = VT_UNKNOWN; + param.punkVal = (IUnknown *)topology; + } + else + param.vt = VT_EMPTY; + + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionTopologySet, &GUID_NULL, + status, ¶m); + if (topo_status != MF_TOPOSTATUS_INVALID) + { + IMFMediaEvent *event; + + if (SUCCEEDED(MFCreateMediaEvent(MESessionTopologyStatus, &GUID_NULL, status, ¶m, &event))) + { + IMFMediaEvent_SetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, topo_status); + IMFMediaEventQueue_QueueEvent(session->event_queue, event); + IMFMediaEvent_Release(event); + } + } + + break; + } case SESSION_CMD_CLOSE: EnterCriticalSection(&session->cs); if (session->state != SESSION_STATE_CLOSED) diff --git a/include/mfapi.h b/include/mfapi.h index 1a8fa25d00..346bd38d92 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -370,6 +370,16 @@ enum _MFT_ENUM_FLAG MFT_ENUM_FLAG_UNTRUSTED_STOREMFT = 0x00000400, };
+typedef enum +{ + MF_TOPOSTATUS_INVALID = 0, + MF_TOPOSTATUS_READY = 100, + MF_TOPOSTATUS_STARTED_SOURCE = 200, + MF_TOPOSTATUS_DYNAMIC_CHANGED = 210, + MF_TOPOSTATUS_SINK_SWITCHED = 300, + MF_TOPOSTATUS_ENDED = 400, +} MF_TOPOSTATUS; + HRESULT WINAPI MFAddPeriodicCallback(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key); HRESULT WINAPI MFAllocateWorkQueue(DWORD *queue); HRESULT WINAPI MFAllocateWorkQueueEx(MFASYNC_WORKQUEUE_TYPE queue_type, DWORD *queue);