From: Conor McCarthy cmccarthy@codeweavers.com
Native Windows robustly handles unexpected shutdown of a media source used in a session, and always sends MESessionStarted with an error status, but if the native source is contained within a wrapper source object, MESessionStarted is never sent even if BeginGetEvent() fails with MF_E_SHUTDOWN, which should be easily handled. This implies that Windows uses a private interface for shutdown notification, and its proper functioning is disrupted when the source is wrapped. --- dlls/mf/session.c | 104 +++++++++++++++++++++++++++++++++--- include/wine/mfinternal.idl | 14 +++++ 2 files changed, 112 insertions(+), 6 deletions(-)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 95952cb0bdd..cb45fceb714 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -29,6 +29,7 @@
#include "wine/debug.h" #include "wine/list.h" +#include "wine/mfinternal.h"
#include "mf_private.h"
@@ -127,6 +128,7 @@ struct media_source IMFMediaSource *source; IUnknown *object; }; + IMFMediaShutdownNotify *shutdown_notify; IMFPresentationDescriptor *pd; enum object_state state; unsigned int flags; @@ -230,9 +232,11 @@ struct media_session IMFTopologyNodeAttributeEditor IMFTopologyNodeAttributeEditor_iface; IMFAsyncCallback commands_callback; IMFAsyncCallback sa_ready_callback; + IMFAsyncCallback shutdown_callback; IMFAsyncCallback events_callback; IMFAsyncCallback sink_finalizer_callback; LONG refcount; + BOOL source_shutdown_handled; IMFMediaEventQueue *event_queue; IMFPresentationClock *clock; IMFPresentationTimeSource *system_time_source; @@ -278,6 +282,11 @@ static struct media_session *impl_from_sa_ready_callback_IMFAsyncCallback(IMFAsy return CONTAINING_RECORD(iface, struct media_session, sa_ready_callback); }
+static struct media_session *impl_from_shutdown_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_session, shutdown_callback); +} + static struct media_session *impl_from_events_callback_IMFAsyncCallback(IMFAsyncCallback *iface) { return CONTAINING_RECORD(iface, struct media_session, events_callback); @@ -855,6 +864,8 @@ static void session_clear_command_list(struct media_session *session) } }
+static void session_release_media_source(struct media_source *source); + static void session_clear_presentation(struct media_session *session) { struct media_source *source, *source2; @@ -870,11 +881,7 @@ static void session_clear_presentation(struct media_session *session) LIST_FOR_EACH_ENTRY_SAFE(source, source2, &session->presentation.sources, struct media_source, entry) { list_remove(&source->entry); - if (source->source) - IMFMediaSource_Release(source->source); - if (source->pd) - IMFPresentationDescriptor_Release(source->pd); - free(source); + session_release_media_source(source); }
LIST_FOR_EACH_ENTRY_SAFE(node, node2, &session->presentation.nodes, struct topo_node, entry) @@ -1576,7 +1583,13 @@ static struct media_source *session_get_media_source(struct media_session *sessi
static void session_release_media_source(struct media_source *source) { - IMFMediaSource_Release(source->source); + if (source->shutdown_notify) + { + IMFMediaShutdownNotify_set_notification_callback(source->shutdown_notify, NULL, NULL); + IMFMediaShutdownNotify_Release(source->shutdown_notify); + } + if (source->source) + IMFMediaSource_Release(source->source); if (source->pd) IMFPresentationDescriptor_Release(source->pd); free(source); @@ -1596,6 +1609,21 @@ static HRESULT session_add_media_source(struct media_session *session, IMFTopolo media_source->source = source; IMFMediaSource_AddRef(media_source->source);
+ if (SUCCEEDED(hr = IMFMediaSource_QueryInterface(media_source->source, + &IID_IMFMediaShutdownNotify, (void **)&media_source->shutdown_notify))) + { + hr = IMFMediaShutdownNotify_set_notification_callback(media_source->shutdown_notify, + &session->shutdown_callback, (IUnknown *)media_source->source); + if (hr == MF_E_SHUTDOWN) + { + session_release_media_source(media_source); + return hr; + } + } + + if (FAILED(hr)) + TRACE("Session is not robust to unexpected shutdown, hr %#lx.\n", hr); + hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, (void **)&media_source->pd);
@@ -1962,6 +1990,8 @@ static HRESULT session_set_current_topology(struct media_session *session, IMFTo return hr; }
+ session->source_shutdown_handled = FALSE; + session_collect_nodes(session);
LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) @@ -2873,6 +2903,14 @@ static void session_handle_source_shutdown(struct media_session *session)
EnterCriticalSection(&session->cs);
+ /* Shutdown may be notified via a dedicated callback or by Begin/EndGetEvent() failure. */ + if (session->source_shutdown_handled) + { + LeaveCriticalSection(&session->cs); + return; + } + session->source_shutdown_handled = TRUE; + finalize_sinks = session->presentation.flags & SESSION_FLAG_FINALIZE_SINKS;
/* When stopping the session, MESessionStopped is sent without waiting @@ -2907,6 +2945,59 @@ static void session_handle_source_shutdown(struct media_session *session) LeaveCriticalSection(&session->cs); }
+static HRESULT WINAPI session_shutdown_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_shutdown_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_session *session = impl_from_shutdown_callback_IMFAsyncCallback(iface); + return IMFMediaSession_AddRef(&session->IMFMediaSession_iface); +} + +static ULONG WINAPI session_shutdown_callback_Release(IMFAsyncCallback *iface) +{ + struct media_session *session = impl_from_shutdown_callback_IMFAsyncCallback(iface); + return IMFMediaSession_Release(&session->IMFMediaSession_iface); +} + +static HRESULT WINAPI session_shutdown_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI session_shutdown_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_session *session = impl_from_shutdown_callback_IMFAsyncCallback(iface); + IMFMediaSource *source = (IMFMediaSource *)IMFAsyncResult_GetStateNoAddRef(result); + + TRACE("Source %p was shut down.\n", source); + + session_handle_source_shutdown(session); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl session_shutdown_callback_vtbl = +{ + session_shutdown_callback_QueryInterface, + session_shutdown_callback_AddRef, + session_shutdown_callback_Release, + session_shutdown_callback_GetParameters, + session_shutdown_callback_Invoke, +}; + static HRESULT WINAPI session_events_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IMFAsyncCallback) || @@ -4710,6 +4801,7 @@ HRESULT WINAPI MFCreateMediaSession(IMFAttributes *config, IMFMediaSession **ses object->IMFTopologyNodeAttributeEditor_iface.lpVtbl = &node_attribute_editor_vtbl; object->commands_callback.lpVtbl = &session_commands_callback_vtbl; object->sa_ready_callback.lpVtbl = &session_sa_ready_callback_vtbl; + object->shutdown_callback.lpVtbl = &session_shutdown_callback_vtbl; object->events_callback.lpVtbl = &session_events_callback_vtbl; object->sink_finalizer_callback.lpVtbl = &session_sink_finalizer_callback_vtbl; object->refcount = 1; diff --git a/include/wine/mfinternal.idl b/include/wine/mfinternal.idl index 0106c2b50d5..3fc915c0437 100644 --- a/include/wine/mfinternal.idl +++ b/include/wine/mfinternal.idl @@ -25,6 +25,7 @@ import "unknwn.idl"; interface IMFByteStream; interface IMFMediaSink; interface IMFMediaType; +interface IMFAsyncCallback;
/* Internal interface used to instantiate registered sinks. It should be compatible, except for used method names. */ @@ -44,6 +45,19 @@ interface IMFSinkClassFactory : IUnknown ); }
+[ + uuid(0aab315c-33a4-43db-af6b-c9c86921034e), + object, + local +] +interface IMFMediaShutdownNotify : IUnknown +{ + HRESULT set_notification_callback( + [in] IMFAsyncCallback *callback, + [in] IUnknown *state + ); +} + cpp_quote("DEFINE_GUID(CLSID_MF3GPSinkClassFactory, 0xe54cdfaf, 0x2381, 0x4cad, 0xab, 0x99, 0xf3, 0x85, 0x17, 0x12, 0x7d, 0x5c);") cpp_quote("DEFINE_GUID(CLSID_MFAC3SinkClassFactory, 0x255a6fda, 0x6f93, 0x4e8a, 0x96, 0x11, 0xde, 0xd1, 0x16, 0x9e, 0xef, 0xb4);") cpp_quote("DEFINE_GUID(CLSID_MFADTSSinkClassFactory, 0xd7ca55ab, 0x5022, 0x4db3, 0xa5, 0x99, 0xab, 0xaf, 0xa3, 0x58, 0xe6, 0xf3);")