From 186b6a6fa7f87f950ecb6d5e272caa339e97489e Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 3 Mar 2020 12:25:00 +0300 Subject: [PATCH] mf: Implement transition to closed state together with sink finalization. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- 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 779a10d08d2..46398315785 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);