From: Conor McCarthy cmccarthy@codeweavers.com
Streams can persist after the media source is destroyed. In that case, they cannot access the source object and it should be released and set to null. Windows behaviour is to release references to the source in Shutdown(). --- dlls/mfplat/tests/mfplat.c | 3 -- dlls/winegstreamer/media_source.c | 51 ++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 801236e329c..40015ab20c7 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -1707,7 +1707,6 @@ static void test_source_resolver(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* During shutdown, circular references such as source <-> stream should be released. */ - todo_wine EXPECT_REF(mediasource, 3);
ok(bytestream_closed, "Missing IMFByteStream::Close call\n"); @@ -1717,14 +1716,12 @@ static void test_source_resolver(void)
IMFRateSupport_Release(rate_support);
- todo_wine EXPECT_REF(mediasource, 2);
IMFGetService_Release(get_service);
/* Holding a reference to the video stream does not prevent release of the media source. */ refcount = IMFMediaSource_Release(mediasource); - todo_wine ok(!refcount, "Unexpected refcount %ld\n", refcount);
IMFByteStream_Release(stream); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 96f671b0104..7d4444c4b24 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -124,6 +124,8 @@ struct media_stream IMFMediaStream IMFMediaStream_iface; LONG ref;
+ SRWLOCK source_lock; + IMFMediaSource *media_source; IMFMediaEventQueue *event_queue; IMFStreamDescriptor *descriptor; @@ -551,6 +553,8 @@ static void flush_token_queue(struct media_stream *stream, BOOL send) IUnknown *op; HRESULT hr;
+ assert(stream->media_source); + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) { struct source_async_command *command = impl_from_async_command_IUnknown(op); @@ -994,7 +998,11 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
if (!ref) { - IMFMediaSource_Release(stream->media_source); + if (stream->media_source) + { + IMFMediaSource_Release(stream->media_source); + stream->media_source = NULL; + } IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->event_queue); flush_token_queue(stream, FALSE); @@ -1041,21 +1049,38 @@ static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventT return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); }
+static struct media_source *media_stream_get_media_source(struct media_stream *stream) +{ + IMFMediaSource *source_iface; + + AcquireSRWLockShared(&stream->source_lock); + if ((source_iface = stream->media_source)) + IMFMediaSource_AddRef(source_iface); + ReleaseSRWLockShared(&stream->source_lock); + + return source_iface ? impl_from_IMFMediaSource(source_iface) : NULL; +} + static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **out) { struct media_stream *stream = impl_from_IMFMediaStream(iface); - struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct media_source *source = media_stream_get_media_source(stream); HRESULT hr = S_OK;
TRACE("%p, %p.\n", iface, out);
+ if (!source) + return MF_E_SHUTDOWN; + EnterCriticalSection(&source->cs);
if (source->state == SOURCE_SHUTDOWN) + { + IMFMediaSource_Release(&source->IMFMediaSource_iface); hr = MF_E_SHUTDOWN; + } else { - IMFMediaSource_AddRef(&source->IMFMediaSource_iface); *out = &source->IMFMediaSource_iface; }
@@ -1067,11 +1092,14 @@ static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMedi static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) { struct media_stream *stream = impl_from_IMFMediaStream(iface); - struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct media_source *source = media_stream_get_media_source(stream); HRESULT hr = S_OK;
TRACE("%p, %p.\n", iface, descriptor);
+ if (!source) + return MF_E_SHUTDOWN; + EnterCriticalSection(&source->cs);
if (source->state == SOURCE_SHUTDOWN) @@ -1084,18 +1112,22 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
LeaveCriticalSection(&source->cs);
+ IMFMediaSource_Release(&source->IMFMediaSource_iface); return hr; }
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct media_stream *stream = impl_from_IMFMediaStream(iface); - struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + struct media_source *source = media_stream_get_media_source(stream); IUnknown *op; HRESULT hr;
TRACE("%p, %p.\n", iface, token);
+ if (!source) + return MF_E_SHUTDOWN; + EnterCriticalSection(&source->cs);
if (source->state == SOURCE_SHUTDOWN) @@ -1118,6 +1150,7 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown
LeaveCriticalSection(&source->cs);
+ IMFMediaSource_Release(&source->IMFMediaSource_iface); return hr; }
@@ -1149,6 +1182,8 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; object->ref = 1;
+ InitializeSRWLock(&object->source_lock); + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) { free(object); @@ -1683,6 +1718,12 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) if (stream) { IMFMediaEventQueue_Shutdown(stream->event_queue); + /* Media Foundation documentation says circular references such as + * those between the source and its streams should be released here. */ + IMFMediaSource_Release(stream->media_source); + AcquireSRWLockExclusive(&stream->source_lock); + stream->media_source = NULL; + ReleaseSRWLockExclusive(&stream->source_lock); IMFMediaStream_Release(&stream->IMFMediaStream_iface); } }