Merge branch 'media_source_ref' into 'master'

winegstreamer: Fix memory/resource leaks in media sources.

See merge request wine/wine!6783
This commit is contained in:
Conor McCarthy 2024-11-19 17:03:14 +00:00
commit a1757b7ad6
2 changed files with 116 additions and 25 deletions

View file

@ -986,12 +986,14 @@ static BOOL get_event(IMFMediaEventGenerator *generator, MediaEventType expected
ok(hr == S_OK, "Failed to get value of event, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to get value of event, hr %#lx.\n", hr);
} }
IMFMediaEvent_Release(callback->media_event);
break; break;
} }
if (callback->media_event)
IMFMediaEvent_Release(callback->media_event);
} }
if (callback->media_event)
IMFMediaEvent_Release(callback->media_event);
IMFAsyncCallback_Release(&callback->IMFAsyncCallback_iface); IMFAsyncCallback_Release(&callback->IMFAsyncCallback_iface);
return ret; return ret;
@ -1319,8 +1321,10 @@ static void test_source_resolver(void)
ok(mediasource != NULL, "got %p\n", mediasource); ok(mediasource != NULL, "got %p\n", mediasource);
ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type); ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
/* Test that no extra refs to mediasource are held if Start() was not called */
EXPECT_REF(mediasource, 1);
refcount = IMFMediaSource_Release(mediasource); refcount = IMFMediaSource_Release(mediasource);
todo_wine
ok(!refcount, "Unexpected refcount %ld\n", refcount); ok(!refcount, "Unexpected refcount %ld\n", refcount);
IMFByteStream_Release(stream); IMFByteStream_Release(stream);
@ -1407,15 +1411,21 @@ static void test_source_resolver(void)
ok(mediasource != NULL, "got %p\n", mediasource); ok(mediasource != NULL, "got %p\n", mediasource);
ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type); ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
EXPECT_REF(mediasource, 1);
check_interface(mediasource, &IID_IMFGetService, TRUE); check_interface(mediasource, &IID_IMFGetService, TRUE);
check_service_interface(mediasource, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, TRUE); check_service_interface(mediasource, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, TRUE);
hr = IMFMediaSource_QueryInterface(mediasource, &IID_IMFGetService, (void**)&get_service); hr = IMFMediaSource_QueryInterface(mediasource, &IID_IMFGetService, (void**)&get_service);
ok(hr == S_OK, "Failed to get service interface, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to get service interface, hr %#lx.\n", hr);
EXPECT_REF(mediasource, 2);
hr = IMFGetService_GetService(get_service, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void**)&rate_support); hr = IMFGetService_GetService(get_service, &MF_RATE_CONTROL_SERVICE, &IID_IMFRateSupport, (void**)&rate_support);
ok(hr == S_OK, "Failed to get rate support interface, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to get rate support interface, hr %#lx.\n", hr);
EXPECT_REF(mediasource, 3);
hr = IMFRateSupport_GetFastestRate(rate_support, MFRATE_FORWARD, FALSE, &rate); hr = IMFRateSupport_GetFastestRate(rate_support, MFRATE_FORWARD, FALSE, &rate);
ok(hr == S_OK, "Failed to query fastest rate, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to query fastest rate, hr %#lx.\n", hr);
ok(rate == 1e6f, "Unexpected fastest rate %f.\n", rate); ok(rate == 1e6f, "Unexpected fastest rate %f.\n", rate);
@ -1504,6 +1514,11 @@ static void test_source_resolver(void)
hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var);
ok(hr == S_OK, "Failed to start media source, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to start media source, hr %#lx.\n", hr);
/* The stream holds a reference. It is unclear which object holds the fifth
* reference in Windows, but it's released after MENewStream is retrieved. */
todo_wine
EXPECT_REF(mediasource, 5);
video_stream = NULL; video_stream = NULL;
if (get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var)) if (get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var))
{ {
@ -1511,6 +1526,8 @@ static void test_source_resolver(void)
video_stream = (IMFMediaStream *)var.punkVal; video_stream = (IMFMediaStream *)var.punkVal;
} }
EXPECT_REF(mediasource, 4);
hr = IMFMediaSource_Pause(mediasource); hr = IMFMediaSource_Pause(mediasource);
ok(hr == S_OK, "Failed to pause media source, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to pause media source, hr %#lx.\n", hr);
if (get_event((IMFMediaEventGenerator *)mediasource, MESourcePaused, &var)) if (get_event((IMFMediaEventGenerator *)mediasource, MESourcePaused, &var))
@ -1610,7 +1627,6 @@ static void test_source_resolver(void)
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL); get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
IMFMediaStream_Release(video_stream);
IMFMediaTypeHandler_Release(handler); IMFMediaTypeHandler_Release(handler);
IMFPresentationDescriptor_Release(descriptor); IMFPresentationDescriptor_Release(descriptor);
@ -1619,14 +1635,24 @@ static void test_source_resolver(void)
hr = IMFMediaSource_Shutdown(mediasource); hr = IMFMediaSource_Shutdown(mediasource);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
/* During shutdown, circular references such as source <-> stream should be released. */
EXPECT_REF(mediasource, 3);
ok(bytestream_closed, "Missing IMFByteStream::Close call\n"); ok(bytestream_closed, "Missing IMFByteStream::Close call\n");
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL);
ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
IMFRateSupport_Release(rate_support); IMFRateSupport_Release(rate_support);
EXPECT_REF(mediasource, 2);
IMFGetService_Release(get_service); IMFGetService_Release(get_service);
IMFMediaSource_Release(mediasource);
/* Holding a reference to the video stream does not prevent release of the media source. */
refcount = IMFMediaSource_Release(mediasource);
ok(!refcount, "Unexpected refcount %ld\n", refcount);
IMFByteStream_Release(stream); IMFByteStream_Release(stream);
/* Create directly through scheme handler. */ /* Create directly through scheme handler. */
@ -1659,6 +1685,12 @@ static void test_source_resolver(void)
IMFSourceResolver_Release(resolver); IMFSourceResolver_Release(resolver);
hr = IMFMediaStream_GetMediaSource(video_stream, &mediasource);
ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr);
refcount = IMFMediaStream_Release(video_stream);
ok(!refcount, "Unexpected refcount %ld\n", refcount);
hr = MFShutdown(); hr = MFShutdown();
ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);

View file

@ -187,6 +187,7 @@ struct media_source
UINT64 duration; UINT64 duration;
IMFStreamDescriptor **descriptors; IMFStreamDescriptor **descriptors;
wg_parser_stream_t *wg_streams;
struct media_stream **streams; struct media_stream **streams;
ULONG stream_count; ULONG stream_count;
@ -542,6 +543,8 @@ static void flush_token_queue(struct media_stream *stream, BOOL send)
IUnknown *op; IUnknown *op;
HRESULT hr; HRESULT hr;
assert(stream->media_source);
if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op))) if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &op)))
{ {
struct source_async_command *command = impl_from_async_command_IUnknown(op); struct source_async_command *command = impl_from_async_command_IUnknown(op);
@ -582,6 +585,9 @@ static HRESULT media_stream_start(struct media_stream *stream, BOOL active, BOOL
&GUID_NULL, S_OK, position); &GUID_NULL, S_OK, position);
} }
static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor *descriptor,
wg_parser_stream_t wg_stream, struct media_stream **out);
static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor,
GUID *format, PROPVARIANT *position) GUID *format, PROPVARIANT *position)
{ {
@ -596,6 +602,26 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe
if (source->state == SOURCE_SHUTDOWN) if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN; return MF_E_SHUTDOWN;
/* if starting for the first time, create the streams */
if (source->stream_count && !source->streams[0])
{
assert(source->state == SOURCE_STOPPED);
for (i = 0; i < source->stream_count; ++i)
{
wg_parser_stream_t wg_stream = source->wg_streams[i];
struct media_stream *stream;
if (FAILED(hr = media_stream_create(&source->IMFMediaSource_iface,
source->descriptors[i], wg_stream, &stream)))
return hr;
source->streams[i] = stream;
}
free(source->wg_streams);
source->wg_streams = NULL;
}
/* seek to beginning on stop->play */ /* seek to beginning on stop->play */
if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY)
{ {
@ -962,7 +988,11 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
if (!ref) if (!ref)
{ {
IMFMediaSource_Release(stream->media_source); if (stream->media_source)
{
IMFMediaSource_Release(stream->media_source);
stream->media_source = NULL;
}
IMFStreamDescriptor_Release(stream->descriptor); IMFStreamDescriptor_Release(stream->descriptor);
IMFMediaEventQueue_Release(stream->event_queue); IMFMediaEventQueue_Release(stream->event_queue);
flush_token_queue(stream, FALSE); flush_token_queue(stream, FALSE);
@ -1012,13 +1042,24 @@ static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventT
static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **out) static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **out)
{ {
struct media_stream *stream = impl_from_IMFMediaStream(iface); struct media_stream *stream = impl_from_IMFMediaStream(iface);
struct media_source *source = impl_from_IMFMediaSource(stream->media_source); IMFMediaSource *source_iface = stream->media_source;
struct media_source *source;
HRESULT hr = S_OK; HRESULT hr = S_OK;
TRACE("%p, %p.\n", iface, out); TRACE("%p, %p.\n", iface, out);
if (!source_iface)
return MF_E_SHUTDOWN;
source = impl_from_IMFMediaSource(source_iface);
EnterCriticalSection(&source->cs); EnterCriticalSection(&source->cs);
/* A shutdown state can occur here if shutdown was in progress in another
* thread when we got the source pointer above. The source object must
* still exist and we cannot reasonably handle a case where the source has
* been destroyed at this point in a get/request method without introducing
* a critical section into the stream object. */
if (source->state == SOURCE_SHUTDOWN) if (source->state == SOURCE_SHUTDOWN)
hr = MF_E_SHUTDOWN; hr = MF_E_SHUTDOWN;
else else
@ -1035,11 +1076,17 @@ static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMedi
static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor)
{ {
struct media_stream *stream = impl_from_IMFMediaStream(iface); struct media_stream *stream = impl_from_IMFMediaStream(iface);
struct media_source *source = impl_from_IMFMediaSource(stream->media_source); IMFMediaSource *source_iface = stream->media_source;
struct media_source *source;
HRESULT hr = S_OK; HRESULT hr = S_OK;
TRACE("%p, %p.\n", iface, descriptor); TRACE("%p, %p.\n", iface, descriptor);
if (!source_iface)
return MF_E_SHUTDOWN;
source = impl_from_IMFMediaSource(source_iface);
EnterCriticalSection(&source->cs); EnterCriticalSection(&source->cs);
if (source->state == SOURCE_SHUTDOWN) if (source->state == SOURCE_SHUTDOWN)
@ -1058,12 +1105,18 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
{ {
struct media_stream *stream = impl_from_IMFMediaStream(iface); struct media_stream *stream = impl_from_IMFMediaStream(iface);
struct media_source *source = impl_from_IMFMediaSource(stream->media_source); IMFMediaSource *source_iface = stream->media_source;
struct media_source *source;
IUnknown *op; IUnknown *op;
HRESULT hr; HRESULT hr;
TRACE("%p, %p.\n", iface, token); TRACE("%p, %p.\n", iface, token);
if (!source_iface)
return MF_E_SHUTDOWN;
source = impl_from_IMFMediaSource(source_iface);
EnterCriticalSection(&source->cs); EnterCriticalSection(&source->cs);
if (source->state == SOURCE_SHUTDOWN) if (source->state == SOURCE_SHUTDOWN)
@ -1569,10 +1622,18 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
{ {
struct media_stream *stream = source->streams[source->stream_count]; struct media_stream *stream = source->streams[source->stream_count];
IMFStreamDescriptor_Release(source->descriptors[source->stream_count]); IMFStreamDescriptor_Release(source->descriptors[source->stream_count]);
IMFMediaEventQueue_Shutdown(stream->event_queue); if (stream)
IMFMediaStream_Release(&stream->IMFMediaStream_iface); {
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);
stream->media_source = NULL;
IMFMediaStream_Release(&stream->IMFMediaStream_iface);
}
} }
free(source->descriptors); free(source->descriptors);
free(source->wg_streams);
free(source->streams); free(source->streams);
LeaveCriticalSection(&source->cs); LeaveCriticalSection(&source->cs);
@ -1606,13 +1667,12 @@ static void media_source_init_descriptors(struct media_source *source)
for (i = 0; i < source->stream_count; i++) for (i = 0; i < source->stream_count; i++)
{ {
struct media_stream *stream = source->streams[i]; IMFStreamDescriptor *descriptor = source->descriptors[i];
IMFStreamDescriptor *descriptor = stream->descriptor;
if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_streams[i],
&MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE))) &MF_SD_LANGUAGE, WG_PARSER_TAG_LANGUAGE)))
WARN("Failed to set stream descriptor language, hr %#lx\n", hr); WARN("Failed to set stream descriptor language, hr %#lx\n", hr);
if (FAILED(hr = stream_descriptor_set_tag(descriptor, stream->wg_stream, if (FAILED(hr = stream_descriptor_set_tag(descriptor, source->wg_streams[i],
&MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME))) &MF_SD_STREAM_NAME, WG_PARSER_TAG_NAME)))
WARN("Failed to set stream descriptor name, hr %#lx\n", hr); WARN("Failed to set stream descriptor name, hr %#lx\n", hr);
} }
@ -1665,6 +1725,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc
stream_count = wg_parser_get_stream_count(parser); stream_count = wg_parser_get_stream_count(parser);
if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors))) if (!(object->descriptors = calloc(stream_count, sizeof(*object->descriptors)))
|| !(object->wg_streams = calloc(stream_count, sizeof(*object->wg_streams)))
|| !(object->streams = calloc(stream_count, sizeof(*object->streams)))) || !(object->streams = calloc(stream_count, sizeof(*object->streams))))
{ {
hr = E_OUTOFMEMORY; hr = E_OUTOFMEMORY;
@ -1673,24 +1734,23 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc
for (i = 0; i < stream_count; ++i) for (i = 0; i < stream_count; ++i)
{ {
/* It is valid to create and release a MF source without ever calling Start() and
* Shutdown(). Each MF stream holds a reference to the source, and that ref should
* be released in Shutdown(), so streams are not created here.
* The wg streams are needed now to get the format and duration. Their buffer is
* freed in Start(). */
wg_parser_stream_t wg_stream = wg_parser_get_stream(object->wg_parser, i); wg_parser_stream_t wg_stream = wg_parser_get_stream(object->wg_parser, i);
IMFStreamDescriptor *descriptor; IMFStreamDescriptor *descriptor;
struct media_stream *stream;
struct wg_format format; struct wg_format format;
wg_parser_stream_get_current_format(wg_stream, &format); wg_parser_stream_get_current_format(wg_stream, &format);
if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor))) if (FAILED(hr = stream_descriptor_create(i, &format, &descriptor)))
goto fail; goto fail;
if (FAILED(hr = media_stream_create(&object->IMFMediaSource_iface, descriptor, wg_stream, &stream)))
{
IMFStreamDescriptor_Release(descriptor);
goto fail;
}
object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream)); object->duration = max(object->duration, wg_parser_stream_get_duration(wg_stream));
IMFStreamDescriptor_AddRef(descriptor); IMFStreamDescriptor_AddRef(descriptor);
object->descriptors[i] = descriptor; object->descriptors[i] = descriptor;
object->streams[i] = stream; object->wg_streams[i] = wg_stream;
object->stream_count++; object->stream_count++;
} }
@ -1704,13 +1764,12 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc
fail: fail:
WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr); WARN("Failed to construct MFMediaSource, hr %#lx.\n", hr);
while (object->streams && object->stream_count--) while (object->descriptors && object->stream_count--)
{ {
struct media_stream *stream = object->streams[object->stream_count];
IMFStreamDescriptor_Release(object->descriptors[object->stream_count]); IMFStreamDescriptor_Release(object->descriptors[object->stream_count]);
IMFMediaStream_Release(&stream->IMFMediaStream_iface);
} }
free(object->descriptors); free(object->descriptors);
free(object->wg_streams);
free(object->streams); free(object->streams);
if (stream_count != UINT_MAX) if (stream_count != UINT_MAX)