[PATCH 2/2] mf: Add initial async implementation of SetTopology().

Nikolay Sivov nsivov at codeweavers.com
Fri Sep 27 06:28:31 CDT 2019


Signed-off-by: Nikolay Sivov <nsivov at 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, &param);
+            if (topo_status != MF_TOPOSTATUS_INVALID)
+            {
+                IMFMediaEvent *event;
+
+                if (SUCCEEDED(MFCreateMediaEvent(MESessionTopologyStatus, &GUID_NULL, status, &param, &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);
-- 
2.23.0




More information about the wine-devel mailing list