[PATCH 3/4] mf: Implement transition to closed state together with sink finalization.

Nikolay Sivov nsivov at codeweavers.com
Tue Mar 3 03:25:00 CST 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mf/session.c | 236 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 203 insertions(+), 33 deletions(-)

diff --git a/dlls/mf/session.c b/dlls/mf/session.c
index 779a10d08d..4639831578 100644
--- a/dlls/mf/session.c
+++ b/dlls/mf/session.c
@@ -81,6 +81,7 @@ enum session_state
     SESSION_STATE_PAUSED,
     SESSION_STATE_STOPPING_SINKS,
     SESSION_STATE_STOPPING_SOURCES,
+    SESSION_STATE_FINALIZING_SINKS,
     SESSION_STATE_CLOSED,
     SESSION_STATE_SHUT_DOWN,
 };
@@ -115,6 +116,7 @@ struct media_sink
     struct list entry;
     IMFMediaSink *sink;
     IMFMediaEventGenerator *event_generator;
+    BOOL finalized;
 };
 
 struct output_node
@@ -128,6 +130,7 @@ enum presentation_flags
 {
     SESSION_FLAG_SOURCES_SUBSCRIBED = 0x1,
     SESSION_FLAG_SINKS_SUBSCRIBED = 0x2,
+    SESSION_FLAG_FINALIZE_SINKS = 0x4,
 };
 
 struct media_session
@@ -138,6 +141,7 @@ struct media_session
     IMFRateControl IMFRateControl_iface;
     IMFAsyncCallback commands_callback;
     IMFAsyncCallback events_callback;
+    IMFAsyncCallback sink_finalizer_callback;
     LONG refcount;
     IMFMediaEventQueue *event_queue;
     IMFPresentationClock *clock;
@@ -258,6 +262,11 @@ static struct media_session *impl_from_events_callback_IMFAsyncCallback(IMFAsync
     return CONTAINING_RECORD(iface, struct media_session, events_callback);
 }
 
+static struct media_session *impl_from_sink_finalizer_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct media_session, sink_finalizer_callback);
+}
+
 static struct media_session *impl_from_IMFGetService(IMFGetService *iface)
 {
     return CONTAINING_RECORD(iface, struct media_session, IMFGetService_iface);
@@ -570,6 +579,28 @@ static HRESULT session_bind_output_nodes(IMFTopology *topology)
     return hr;
 }
 
+static void session_set_caps(struct media_session *session, DWORD caps)
+{
+    DWORD delta = session->caps ^ caps;
+    IMFMediaEvent *event;
+
+    /* Delta is documented to reflect rate value changes as well, but it's not clear what to compare
+       them to, since session always queries for current object rates. */
+    if (!delta)
+        return;
+
+    session->caps = caps;
+
+    if (FAILED(MFCreateMediaEvent(MESessionCapabilitiesChanged, &GUID_NULL, S_OK, NULL, &event)))
+        return;
+
+    IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS, caps);
+    IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS_DELTA, delta);
+
+    IMFMediaEventQueue_QueueEvent(session->event_queue, event);
+    IMFMediaEvent_Release(event);
+}
+
 static void session_clear_presentation(struct media_session *session)
 {
     struct source_node *src_node, *src_node2;
@@ -714,6 +745,74 @@ static void session_stop(struct media_session *session)
     LeaveCriticalSection(&session->cs);
 }
 
+static HRESULT session_finalize_sinks(struct media_session *session)
+{
+    IMFFinalizableMediaSink *fin_sink;
+    BOOL sinks_finalized = TRUE;
+    struct media_sink *sink;
+    HRESULT hr = S_OK;
+
+    session->presentation.flags &= ~SESSION_FLAG_FINALIZE_SINKS;
+    session->state = SESSION_STATE_FINALIZING_SINKS;
+
+    LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
+    {
+        if (SUCCEEDED(IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
+        {
+            hr = IMFFinalizableMediaSink_BeginFinalize(fin_sink, &session->sink_finalizer_callback,
+                    (IUnknown *)fin_sink);
+            IMFFinalizableMediaSink_Release(fin_sink);
+            if (FAILED(hr))
+                break;
+            sinks_finalized = FALSE;
+        }
+        else
+            sink->finalized = TRUE;
+    }
+
+    if (sinks_finalized)
+    {
+        session->state = SESSION_STATE_CLOSED;
+        session_set_caps(session, session->caps & ~(MFSESSIONCAP_START | MFSESSIONCAP_SEEK));
+        IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionClosed, &GUID_NULL, hr, NULL);
+    }
+
+    return hr;
+}
+
+static void session_close(struct media_session *session)
+{
+    HRESULT hr = S_OK;
+
+    EnterCriticalSection(&session->cs);
+
+    switch (session->state)
+    {
+        case SESSION_STATE_STOPPED:
+            hr = session_finalize_sinks(session);
+            break;
+        case SESSION_STATE_RUNNING:
+        case SESSION_STATE_PAUSED:
+            session->presentation.flags |= SESSION_FLAG_FINALIZE_SINKS;
+            if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock)))
+                session->state = SESSION_STATE_STOPPING_SINKS;
+            break;
+        case SESSION_STATE_CLOSED:
+            hr = MF_E_INVALIDREQUEST;
+            break;
+        default:
+            ;
+    }
+
+    if (FAILED(hr))
+    {
+        session->state = SESSION_STATE_CLOSED;
+        IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionClosed, &GUID_NULL, hr, NULL);
+    }
+
+    LeaveCriticalSection(&session->cs);
+}
+
 static struct media_source *session_get_media_source(struct media_session *session, IMFMediaSource *source)
 {
     struct media_source *cur;
@@ -870,28 +969,6 @@ static DWORD session_get_object_rate_caps(IUnknown *object)
     return caps;
 }
 
-static void session_set_caps(struct media_session *session, DWORD caps)
-{
-    DWORD delta = session->caps ^ caps;
-    IMFMediaEvent *event;
-
-    /* Delta is documented to reflect rate value changes as well, but it's not clear what to compare
-       them to, since session always queries for current object rates. */
-    if (!delta)
-        return;
-
-    session->caps = caps;
-
-    if (FAILED(MFCreateMediaEvent(MESessionCapabilitiesChanged, &GUID_NULL, S_OK, NULL, &event)))
-        return;
-
-    IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS, caps);
-    IMFMediaEvent_SetUINT32(event, &MF_EVENT_SESSIONCAPS_DELTA, delta);
-
-    IMFMediaEventQueue_QueueEvent(session->event_queue, event);
-    IMFMediaEvent_Release(event);
-}
-
 static HRESULT session_add_media_sink(struct media_session *session, IMFTopologyNode *node, IMFMediaSink *sink)
 {
     struct media_sink *media_sink;
@@ -1583,14 +1660,7 @@ static HRESULT WINAPI session_commands_callback_Invoke(IMFAsyncCallback *iface,
             session_stop(session);
             break;
         case SESSION_CMD_CLOSE:
-            EnterCriticalSection(&session->cs);
-            if (session->state != SESSION_STATE_CLOSED)
-            {
-                /* FIXME: actually do something to presentation objects */
-                session->state = SESSION_STATE_CLOSED;
-                IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionClosed, &GUID_NULL, S_OK, NULL);
-            }
-            LeaveCriticalSection(&session->cs);
+            session_close(session);
             break;
         default:
             ;
@@ -1873,8 +1943,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
             if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED))
                 break;
 
-            session->state = SESSION_STATE_STOPPED;
-
             LIST_FOR_EACH_ENTRY(out_node, &session->presentation.output_nodes, struct output_node, entry)
             {
                 IMFStreamSink_Flush(out_node->stream);
@@ -1882,7 +1950,15 @@ static void session_set_source_object_state(struct media_session *session, IUnkn
 
             session_set_caps(session, session->caps & ~MFSESSIONCAP_PAUSE);
 
-            IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStopped, &GUID_NULL, S_OK, NULL);
+            if (session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS)
+            {
+                session_finalize_sinks(session);
+            }
+            else
+            {
+                session->state = SESSION_STATE_STOPPED;
+                IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStopped, &GUID_NULL, S_OK, NULL);
+            }
 
             break;
         default:
@@ -2094,6 +2170,99 @@ static const IMFAsyncCallbackVtbl session_events_callback_vtbl =
     session_events_callback_Invoke,
 };
 
+static HRESULT WINAPI session_sink_finalizer_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+{
+    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = iface;
+        IMFAsyncCallback_AddRef(iface);
+        return S_OK;
+    }
+
+    WARN("Unsupported %s.\n", debugstr_guid(riid));
+    *obj = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI session_sink_finalizer_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
+    return IMFMediaSession_AddRef(&session->IMFMediaSession_iface);
+}
+
+static ULONG WINAPI session_sink_finalizer_callback_Release(IMFAsyncCallback *iface)
+{
+    struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
+    return IMFMediaSession_Release(&session->IMFMediaSession_iface);
+}
+
+static HRESULT WINAPI session_sink_finalizer_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI session_sink_finalizer_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct media_session *session = impl_from_sink_finalizer_callback_IMFAsyncCallback(iface);
+    IMFFinalizableMediaSink *fin_sink = NULL;
+    BOOL sinks_finalized = TRUE;
+    struct media_sink *sink;
+    IUnknown *state;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
+        return hr;
+
+    EnterCriticalSection(&session->cs);
+
+    LIST_FOR_EACH_ENTRY(sink, &session->presentation.sinks, struct media_sink, entry)
+    {
+        if (state == (IUnknown *)sink->sink)
+        {
+            if (FAILED(hr = IMFMediaSink_QueryInterface(sink->sink, &IID_IMFFinalizableMediaSink, (void **)&fin_sink)))
+                WARN("Unexpected, missing IMFFinalizableSink, hr %#x.\n", hr);
+        }
+        else
+        {
+            sinks_finalized &= sink->finalized;
+            if (!sinks_finalized)
+                break;
+        }
+    }
+
+    IUnknown_Release(state);
+
+    if (fin_sink)
+    {
+        /* Complete session transition, or close prematurely on error. */
+        if (SUCCEEDED(hr = IMFFinalizableMediaSink_EndFinalize(fin_sink, result)))
+        {
+            sink->finalized = TRUE;
+            if (sinks_finalized)
+            {
+                session->state = SESSION_STATE_CLOSED;
+                session_set_caps(session, session->caps & ~(MFSESSIONCAP_START | MFSESSIONCAP_SEEK));
+                IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionClosed, &GUID_NULL, hr, NULL);
+            }
+        }
+        IMFFinalizableMediaSink_Release(fin_sink);
+    }
+
+    LeaveCriticalSection(&session->cs);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl session_sink_finalizer_callback_vtbl =
+{
+    session_sink_finalizer_callback_QueryInterface,
+    session_sink_finalizer_callback_AddRef,
+    session_sink_finalizer_callback_Release,
+    session_sink_finalizer_callback_GetParameters,
+    session_sink_finalizer_callback_Invoke,
+};
+
 static HRESULT WINAPI session_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj)
 {
     struct media_session *session = impl_session_from_IMFRateSupport(iface);
@@ -2263,6 +2432,7 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses
     object->IMFRateControl_iface.lpVtbl = &session_rate_control_vtbl;
     object->commands_callback.lpVtbl = &session_commands_callback_vtbl;
     object->events_callback.lpVtbl = &session_events_callback_vtbl;
+    object->sink_finalizer_callback.lpVtbl = &session_sink_finalizer_callback_vtbl;
     object->refcount = 1;
     list_init(&object->topologies);
     list_init(&object->presentation.sources);
-- 
2.25.1




More information about the wine-devel mailing list