From: Conor McCarthy cmccarthy@codeweavers.com
Native Windows robustly handles unexpected shutdown of a media source used in a session, but if the native source is contained within a wrapper source object, it hangs 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 | 84 +++++++++++++++++++++++++++++++++++++ include/wine/mfinternal.idl | 14 +++++++ 2 files changed, 98 insertions(+)
diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 66ec634ea59..4c0e1f8819a 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"
@@ -230,9 +231,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 +281,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); @@ -1583,6 +1591,7 @@ static void session_release_media_source(struct media_source *source) static HRESULT session_add_media_source(struct media_session *session, IMFTopologyNode *node, IMFMediaSource *source) { struct media_source *media_source; + IMFMediaShutdownNotify *notify; HRESULT hr;
if (session_get_media_source(session, source)) @@ -1594,6 +1603,17 @@ 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 **)¬ify))) + { + IMFMediaShutdownNotify_set_notification_callback(notify, &session->shutdown_callback, + (IUnknown *)media_source->source); + } + else + { + TRACE("IMFMediaShutdownNotify unsupported, hr %#lx; session not robust to unexpected shutdown.\n", hr); + } + hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, &IID_IMFPresentationDescriptor, (void **)&media_source->pd);
@@ -1960,6 +1980,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) @@ -2871,6 +2893,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 @@ -2902,6 +2932,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) || @@ -4705,6 +4788,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);")