From: Conor McCarthy cmccarthy@codeweavers.com
A race condition exists if the source is shut down in another thread between completion of a method and execution of its async command. Reasons for emitting the event are detailed in media_source_start(). --- dlls/winegstreamer/media_source.c | 92 ++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 8dc8145b60f..d10850c7aa1 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -586,6 +586,7 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe GUID *format, PROPVARIANT *position) { BOOL starting = source->state == SOURCE_STOPPED, seek_message = !starting && position->vt != VT_EMPTY; + MediaEventType event_type = seek_message ? MESourceSeeked : MESourceStarted; IMFStreamDescriptor **descriptors; DWORD i, count; HRESULT hr; @@ -594,7 +595,19 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe debugstr_guid(format), wine_dbgstr_variant((VARIANT *)position));
if (source->state == SOURCE_SHUTDOWN) + { + /* We land here if the source is shut down before the async start command + * executes. The media session implementation still needs some form of + * notification in this case. The below call results in callback invocation, + * but EndGetEvent() will fail. That can happen without doing this, so we + * are not creating a new behaviour here. It's not possible for the app + * to determine if the event was enqueued before or after source shutdown, + * which implies this cannot cause issues. */ + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + event_type, &GUID_NULL, MF_E_SHUTDOWN, position))) + WARN("Failed to queue event for shutdown, hr %#lx\n", hr); return MF_E_SHUTDOWN; + }
/* seek to beginning on stop->play */ if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) @@ -661,7 +674,7 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe flush_token_queue(source->streams[i], position->vt == VT_EMPTY);
return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, - seek_message ? MESourceSeeked : MESourceStarted, &GUID_NULL, S_OK, position); + event_type, &GUID_NULL, S_OK, position); }
static HRESULT media_source_pause(struct media_source *source) @@ -672,7 +685,12 @@ static HRESULT media_source_pause(struct media_source *source) TRACE("source %p\n", source);
if (source->state == SOURCE_SHUTDOWN) + { + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + MESourcePaused, &GUID_NULL, MF_E_SHUTDOWN, NULL))) + WARN("Failed to queue shutdown MESourcePaused event, hr %#lx\n", hr); return MF_E_SHUTDOWN; + }
for (i = 0; i < source->stream_count; i++) { @@ -694,7 +712,12 @@ static HRESULT media_source_stop(struct media_source *source) TRACE("source %p\n", source);
if (source->state == SOURCE_SHUTDOWN) + { + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + MESourceStopped, &GUID_NULL, MF_E_SHUTDOWN, NULL))) + WARN("Failed to queue shutdown MESourceStopped event, hr %#lx\n", hr); return MF_E_SHUTDOWN; + }
for (i = 0; i < source->stream_count; i++) { @@ -1362,6 +1385,9 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) if (!ref) { IMFMediaSource_Shutdown(iface); + /* The event queue is kept alive after shutdown for reasons described + * in media_source_start(). This is not externally visible. */ + IMFMediaEventQueue_Shutdown(source->event_queue); IMFMediaEventQueue_Release(source->event_queue); IMFByteStream_Release(source->byte_stream); wg_parser_destroy(source->wg_parser); @@ -1376,38 +1402,89 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface) static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) { struct media_source *source = impl_from_IMFMediaSource(iface); + IMFMediaEventQueue *event_queue = NULL; + HRESULT hr = S_OK;
TRACE("%p, %#lx, %p.\n", iface, flags, event);
- return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); + EnterCriticalSection(&source->cs); + + /* GetEvent() is blocking and must not share a CS with QueueEvent(). */ + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + { + event_queue = source->event_queue; + IMFMediaEventQueue_AddRef(event_queue); + } + + LeaveCriticalSection(&source->cs); + + if (SUCCEEDED(hr)) + { + hr = IMFMediaEventQueue_GetEvent(event_queue, flags, event); + IMFMediaEventQueue_Release(event_queue); + } + + return hr; }
static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) { struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr;
TRACE("%p, %p, %p.\n", iface, callback, state);
- return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + hr = IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) { struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr;
TRACE("%p, %p, %p.\n", iface, result, event);
- return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + hr = IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, - HRESULT hr, const PROPVARIANT *value) + HRESULT status, const PROPVARIANT *value) { struct media_source *source = impl_from_IMFMediaSource(iface); + HRESULT hr;
- TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), status, value);
- return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else + hr = IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, status, value); + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) @@ -1562,7 +1639,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) WaitForSingleObject(source->read_thread, INFINITE); CloseHandle(source->read_thread);
- IMFMediaEventQueue_Shutdown(source->event_queue); IMFByteStream_Close(source->byte_stream);
while (source->stream_count--)