Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 33 ++++++++++++++++++--------------- dlls/mf/tests/mf.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index a2a2ba6fe5..c5235e42a8 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1401,8 +1401,8 @@ static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD fl { struct media_session *session = impl_from_IMFMediaSession(iface); struct queued_topology *queued; - HRESULT hr = S_OK; TOPOID topo_id; + HRESULT hr;
TRACE("%p, %#x, %s, %p.\n", iface, flags, wine_dbgstr_longlong(id), topology);
@@ -1410,27 +1410,30 @@ static HRESULT WINAPI mfsession_GetFullTopology(IMFMediaSession *iface, DWORD fl
EnterCriticalSection(&session->cs);
- if (flags & MFSESSION_GETFULLTOPOLOGY_CURRENT) + if (SUCCEEDED(hr = session_is_shut_down(session))) { - if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID) - *topology = session->presentation.current_topology; + if (flags & MFSESSION_GETFULLTOPOLOGY_CURRENT) + { + if (session->presentation.topo_status != MF_TOPOSTATUS_INVALID) + *topology = session->presentation.current_topology; + else + hr = MF_E_INVALIDREQUEST; + } else - hr = MF_E_INVALIDREQUEST; - } - else - { - LIST_FOR_EACH_ENTRY(queued, &session->topologies, struct queued_topology, entry) { - if (SUCCEEDED(IMFTopology_GetTopologyID(queued->topology, &topo_id)) && topo_id == id) + LIST_FOR_EACH_ENTRY(queued, &session->topologies, struct queued_topology, entry) { - *topology = queued->topology; - break; + if (SUCCEEDED(IMFTopology_GetTopologyID(queued->topology, &topo_id)) && topo_id == id) + { + *topology = queued->topology; + break; + } } } - }
- if (*topology) - IMFTopology_AddRef(*topology); + if (*topology) + IMFTopology_AddRef(*topology); + }
LeaveCriticalSection(&session->cs);
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 5f3114da32..5a690880d1 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1027,11 +1027,14 @@ static void test_media_session(void) IMFRateSupport *rate_support; IMFAttributes *attributes; IMFMediaSession *session; + IMFTopology *topology; + PROPVARIANT propvar; IMFGetService *gs; IMFClock *clock; IUnknown *unk; HRESULT hr; float rate; + DWORD caps; BOOL thin;
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); @@ -1115,12 +1118,40 @@ todo_wine hr = IMFMediaSession_Shutdown(session); ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+ hr = IMFMediaSession_ClearTopologies(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_Start(session, &GUID_NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + propvar.vt = VT_EMPTY; + hr = IMFMediaSession_Start(session, &GUID_NULL, &propvar); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_Pause(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_Stop(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + hr = IMFMediaSession_Close(session); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
hr = IMFMediaSession_GetClock(session, &clock); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+ hr = IMFMediaSession_GetSessionCapabilities(session, &caps); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_GetSessionCapabilities(session, NULL); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_GetFullTopology(session, MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + + hr = IMFMediaSession_Shutdown(session); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); + IMFMediaSession_Release(session);
/* Custom topology loader, GUID is not registered. */
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index c5235e42a8..779a10d08d 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -646,6 +646,10 @@ static void session_start(struct media_session *session, const GUID *time_format case SESSION_STATE_RUNNING: FIXME("Seeking is not implemented.\n"); break; + case SESSION_STATE_CLOSED: + IMFMediaEventQueue_QueueEventParamVar(session->event_queue, MESessionStarted, &GUID_NULL, + MF_E_INVALIDREQUEST, NULL); + break; default: ; }
Signed-off-by: Nikolay Sivov nsivov@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);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mf/session.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 4639831578..29f366a34e 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -75,7 +75,7 @@ enum session_state SESSION_STATE_STOPPED = 0, SESSION_STATE_STARTING_SOURCES, SESSION_STATE_STARTING_SINKS, - SESSION_STATE_RUNNING, + SESSION_STATE_STARTED, SESSION_STATE_PAUSING_SINKS, SESSION_STATE_PAUSING_SOURCES, SESSION_STATE_PAUSED, @@ -674,7 +674,7 @@ static void session_start(struct media_session *session, const GUID *time_format session->presentation.flags |= SESSION_FLAG_SOURCES_SUBSCRIBED; session->state = SESSION_STATE_STARTING_SOURCES; break; - case SESSION_STATE_RUNNING: + case SESSION_STATE_STARTED: FIXME("Seeking is not implemented.\n"); break; case SESSION_STATE_CLOSED: @@ -696,7 +696,7 @@ static void session_pause(struct media_session *session)
switch (session->state) { - case SESSION_STATE_RUNNING: + case SESSION_STATE_STARTED:
/* Transition in two steps - pause clock, wait for sinks and pause sources. */ if (SUCCEEDED(hr = IMFPresentationClock_Pause(session->clock))) @@ -724,7 +724,7 @@ static void session_stop(struct media_session *session)
switch (session->state) { - case SESSION_STATE_RUNNING: + case SESSION_STATE_STARTED: case SESSION_STATE_PAUSED:
/* Transition in two steps - pause clock, wait for sinks and pause sources. */ @@ -791,7 +791,7 @@ static void session_close(struct media_session *session) case SESSION_STATE_STOPPED: hr = session_finalize_sinks(session); break; - case SESSION_STATE_RUNNING: + case SESSION_STATE_STARTED: case SESSION_STATE_PAUSED: session->presentation.flags |= SESSION_FLAG_FINALIZE_SINKS; if (SUCCEEDED(hr = IMFPresentationClock_Stop(session->clock))) @@ -1999,7 +1999,7 @@ static void session_set_sink_stream_state(struct media_session *session, IMFStre if (!session_is_output_nodes_state(session, OBJ_STATE_STARTED)) break;
- session->state = SESSION_STATE_RUNNING; + session->state = SESSION_STATE_STARTED;
caps = session->caps | MFSESSIONCAP_PAUSE;