Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 184 ++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 3 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index d0e05650c2..43ca186236 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -350,6 +350,67 @@ static const IMFAsyncCallbackVtbl test_create_from_file_handler_callback_vtbl = test_create_from_file_handler_callback_Invoke, };
+struct expected_event +{ + HRESULT status_code; + MediaEventType type; + GUID *extended_type; +}; + +static BOOL expect_event(IMFMediaEventGenerator *gen, struct expected_event *expected, PROPVARIANT *out) +{ + HRESULT hr; + IMFMediaEvent *event; + BOOL ret = TRUE; + + hr = IMFMediaEventGenerator_GetEvent(gen, 0, &event); + ok(hr == S_OK, "Failed to get event from media source, hr %#x.\n", hr); + { + HRESULT status_code; + hr = IMFMediaEvent_GetStatus(event, &status_code); + ok (hr == S_OK, "Failed to get status code, hr %#x.\n", hr); + ok (status_code == expected->status_code, "Unexpected status code %#x, expected %#x.\n", + status_code, expected->status_code); + + if (hr != S_OK || status_code != expected->status_code) + ret = FALSE; + } + { + MediaEventType event_type; + hr = IMFMediaEvent_GetType(event, &event_type); + ok(hr == S_OK, "Failed to event type, hr %#x.\n", hr); + ok(event_type == expected->type, "Unexpected event type %u, expected %u.\n", + event_type, expected->type); + + if (hr != S_OK || event_type != expected->type) + ret = FALSE; + } + if (expected->extended_type) + { + GUID extended_type; + BOOL is_equal; + hr = IMFMediaEvent_GetExtendedType(event, &extended_type); + ok(hr == S_OK, "Failed to get extended type, hr %#x.\n", hr); + is_equal = IsEqualGUID(&extended_type, expected->extended_type); + ok(is_equal, "Unexpected extended type %s, expected %s.\n", + debugstr_guid(&extended_type), debugstr_guid(expected->extended_type)); + + if (hr != S_OK || !is_equal) + ret = FALSE; + } + if (out) + { + hr = IMFMediaEvent_GetValue(event, out); + ok (hr == S_OK, "Failed to get value of event, hr %#x.\n", hr); + + if (hr != S_OK) + ret = FALSE; + } + IMFMediaEvent_Release(event); + + return ret; +} + static void test_source_resolver(void) { static const WCHAR file_type[] = {'v','i','d','e','o','/','m','p','4',0}; @@ -361,7 +422,7 @@ static void test_source_resolver(void) IMFMediaSource *mediasource; IMFPresentationDescriptor *descriptor; IMFMediaTypeHandler *handler; - BOOL selected, do_uninit; + IMFMediaType *type; MF_OBJECT_TYPE obj_type; IMFStreamDescriptor *sd; IUnknown *cancel_cookie; @@ -370,6 +431,9 @@ static void test_source_resolver(void) HRESULT hr; WCHAR *filename; GUID guid; + PROPVARIANT empty_var; + BOOL selected, do_uninit, skip_source_tests; + ULONG refcount;
if (!pMFCreateSourceResolver) { @@ -451,13 +515,127 @@ static void test_source_resolver(void) hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); todo_wine ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); + if (hr == S_OK) + { + ok (IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); + + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &type); + ok (hr == S_OK, "Failed to get current media type, hr %#x.\n", hr); + + hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &guid); + ok(hr == S_OK, "Failed to get media sub type, hr %#x.\n", hr); + ok(IsEqualGUID(&guid, &MFVideoFormat_M4S2), "Unexpected sub type %s.\n", debugstr_guid(&guid)); + + hr = IMFPresentationDescriptor_SelectStream(descriptor, 0); + ok(hr == S_OK, "Failed to select video stream, hr %#x.\n", hr); + + IMFMediaType_Release(type); + + skip_source_tests = FALSE; + } + else + { + /* skip media source tests to avoid crashing, as the media source is fake */ + skip_source_tests = TRUE; + }
IMFMediaTypeHandler_Release(handler); IMFStreamDescriptor_Release(sd);
+ if (skip_source_tests) + goto source_tests_end; + + empty_var.vt = VT_EMPTY; + hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &empty_var); + ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + + if (hr == S_OK) + { + static struct expected_event new_stream_event = { S_OK, MENewStream, NULL }; + static struct expected_event source_started_event = { S_OK, MESourceStarted, NULL }; + static struct expected_event end_of_presentation_event = {S_OK, MEEndOfPresentation, NULL }; + PROPVARIANT new_stream_value; + IMFMediaStream *video_stream; + + expect_event((IMFMediaEventGenerator*)mediasource, &new_stream_event, &new_stream_value); + ok(new_stream_value.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", + new_stream_value.vt); + video_stream = (IMFMediaStream *)new_stream_value.punkVal; + expect_event((IMFMediaEventGenerator*)mediasource, &source_started_event, NULL); + + /* TODO: verify stream information */ + + /* Request samples, our file is 10 frames at 25fps */ + { + static struct expected_event stream_started_event = { S_OK, MEStreamStarted, NULL }; + static struct expected_event new_sample_event = {S_OK, MEMediaSample, NULL}; + static struct expected_event end_of_stream_event = {S_OK, MEEndOfStream, NULL}; + unsigned int i, sample_count = 10; + + expect_event((IMFMediaEventGenerator*)video_stream, &stream_started_event, NULL); + + /* request one beyond EOS, otherwise EndOfStream isn't queued */ + for (i = 0; i <= sample_count; i++) + { + hr = IMFMediaStream_RequestSample(video_stream, NULL); + if (i == sample_count) + break; + ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr); + if (hr != S_OK) + break; + } + + for (i = 0; i < sample_count; i++) + { + PROPVARIANT new_sample_value; + IMFSample *sample; + DWORD buffer_count; + LONGLONG duration, time; + static const LONGLONG MILLI_TO_100_NANO = 10000; + + if (!(expect_event((IMFMediaEventGenerator*)video_stream, &new_sample_event, &new_sample_value))) + { + ok(FALSE, "Sample %u not recieved.\n", i + 1); + break; + } + ok(new_sample_value.vt == VT_UNKNOWN, "Unexpected value type %u from MEMediaSample event.\n", + new_sample_value.vt); + sample = (IMFSample *)new_sample_value.punkVal; + + hr = IMFSample_GetBufferCount(sample, &buffer_count); + ok(hr == S_OK, "Failed to get buffer count, hr %#x.\n", hr); + ok(buffer_count == 1, "Unexpected buffer count %u.\n", buffer_count); + + hr = IMFSample_GetSampleDuration(sample, &duration); + ok(hr == S_OK, "Failed to get sample duration, hr %#x.\n", hr); + ok(duration == 40 * MILLI_TO_100_NANO, "Unexpected duration %s.\n", wine_dbgstr_longlong(duration)); + + hr = IMFSample_GetSampleTime(sample, &time); + ok(hr == S_OK, "Failed to get sample time, hr %#x.\n", hr); + ok(time == i * 40 * MILLI_TO_100_NANO, "Unexpected time %s.\n", wine_dbgstr_longlong(time)); + } + + if (i == sample_count) + expect_event((IMFMediaEventGenerator*)video_stream, &end_of_stream_event, NULL); + + hr = IMFMediaStream_RequestSample(video_stream, NULL); + ok (hr == MF_E_END_OF_STREAM, "Unexpected hr %#x, expected MF_E_END_OF_STREAM.\n", hr); + } + + expect_event((IMFMediaEventGenerator*)mediasource, &end_of_presentation_event, NULL); + } + IMFPresentationDescriptor_Release(descriptor); - IMFMediaSource_Release(mediasource); - IMFByteStream_Release(stream); + IMFMediaSource_Shutdown(mediasource); + + hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); + ok (hr == MF_E_SHUTDOWN, "Got 0x%08x\n", hr); + + source_tests_end: + refcount = IMFMediaSource_Release(mediasource); + ok(!refcount, "Unexpected refcount %u.\n", refcount); + refcount = IMFByteStream_Release(stream); + ok(!refcount, "Unexpected refcount %u.\n", refcount);
/* Create from URL. */ callback.event = CreateEventA(NULL, FALSE, FALSE, NULL);
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: - Fix premature removal of code. - Add tests. --- dlls/mfplat/main.c | 22 ++++++++++++++-------- dlls/mfplat/tests/mfplat.c | 5 +++++ 2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index e96a972909..85d676a0d6 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -3278,13 +3278,6 @@ static HRESULT WINAPI bytestream_GetCapabilities(IMFByteStream *iface, DWORD *ca return S_OK; }
-static HRESULT WINAPI mfbytestream_GetLength(IMFByteStream *iface, QWORD *length) -{ - FIXME("%p, %p.\n", iface, length); - - return E_NOTIMPL; -} - static HRESULT WINAPI mfbytestream_SetLength(IMFByteStream *iface, QWORD length) { mfbytestream *This = impl_from_IMFByteStream(iface); @@ -3312,6 +3305,19 @@ static HRESULT WINAPI mfbytestream_SetCurrentPosition(IMFByteStream *iface, QWOR return E_NOTIMPL; }
+static HRESULT WINAPI bytestream_file_GetLength(IMFByteStream *iface, QWORD *length) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + LARGE_INTEGER li; + + if (GetFileSizeEx(stream->hfile, &li)) + *length = li.QuadPart; + else + return HRESULT_FROM_WIN32(GetLastError()); + + return S_OK; +} + static HRESULT WINAPI bytestream_file_IsEndOfStream(IMFByteStream *iface, BOOL *ret) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -3440,7 +3446,7 @@ static const IMFByteStreamVtbl bytestream_file_vtbl = bytestream_AddRef, bytestream_Release, bytestream_GetCapabilities, - mfbytestream_GetLength, + bytestream_file_GetLength, mfbytestream_SetLength, mfbytestream_GetCurrentPosition, mfbytestream_SetCurrentPosition, diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 43ca186236..81219adff4 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -1524,6 +1524,7 @@ static void test_file_stream(void) DWORD caps, count; WCHAR *filename; IUnknown *unk; + QWORD bytestream_length; HRESULT hr; WCHAR *str;
@@ -1583,6 +1584,10 @@ static void test_file_stream(void)
IMFAttributes_Release(attributes);
+ hr = IMFByteStream_GetLength(bytestream, &bytestream_length); + ok(hr == S_OK, "Failed to get bytestream length, hr %#x.\n", hr); + ok(bytestream_length == 1554, "Unexpected bytestream length %s.\n", wine_dbgstr_longlong(bytestream_length)); + hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &bytestream2); ok(hr == S_OK, "got 0x%08x\n", hr);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64834
Your paranoid android.
=== w1064v1809_he (32 bit report) ===
mfplat: mfplat.c:2592: Test failed: Unexpected return value 0x102. mfplat.c:1943: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1946: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1949: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1952: Test failed: Unexpected result, hr 0xc00d3e85. 1804:mfplat: unhandled exception c0000005 at 00404EFB
=== debian10 (32 bit report) ===
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: Add tests. --- dlls/mfplat/main.c | 26 ++++++++++++++++---------- dlls/mfplat/tests/mfplat.c | 8 ++++++++ 2 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 85d676a0d6..853b5ab370 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -3296,15 +3296,6 @@ static HRESULT WINAPI mfbytestream_GetCurrentPosition(IMFByteStream *iface, QWOR return E_NOTIMPL; }
-static HRESULT WINAPI mfbytestream_SetCurrentPosition(IMFByteStream *iface, QWORD position) -{ - mfbytestream *This = impl_from_IMFByteStream(iface); - - FIXME("%p, %s\n", This, wine_dbgstr_longlong(position)); - - return E_NOTIMPL; -} - static HRESULT WINAPI bytestream_file_GetLength(IMFByteStream *iface, QWORD *length) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -3318,6 +3309,21 @@ static HRESULT WINAPI bytestream_file_GetLength(IMFByteStream *iface, QWORD *len return S_OK; }
+static HRESULT WINAPI bytestream_file_SetCurrentPosition(IMFByteStream *iface, QWORD position) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p, %s\n", iface, wine_dbgstr_longlong(position)); + + EnterCriticalSection(&stream->cs); + + stream->position = position; + + LeaveCriticalSection(&stream->cs); + + return S_OK; +} + static HRESULT WINAPI bytestream_file_IsEndOfStream(IMFByteStream *iface, BOOL *ret) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -3449,7 +3455,7 @@ static const IMFByteStreamVtbl bytestream_file_vtbl = bytestream_file_GetLength, mfbytestream_SetLength, mfbytestream_GetCurrentPosition, - mfbytestream_SetCurrentPosition, + bytestream_file_SetCurrentPosition, bytestream_file_IsEndOfStream, bytestream_file_Read, bytestream_BeginRead, diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 81219adff4..c468286362 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -1525,6 +1525,7 @@ static void test_file_stream(void) WCHAR *filename; IUnknown *unk; QWORD bytestream_length; + BOOL eos; HRESULT hr; WCHAR *str;
@@ -1588,6 +1589,13 @@ static void test_file_stream(void) ok(hr == S_OK, "Failed to get bytestream length, hr %#x.\n", hr); ok(bytestream_length == 1554, "Unexpected bytestream length %s.\n", wine_dbgstr_longlong(bytestream_length));
+ hr = IMFByteStream_SetCurrentPosition(bytestream, bytestream_length); + ok (hr == S_OK, "Failed to set bytestream position, hr %#x.\n", hr); + + hr = IMFByteStream_IsEndOfStream(bytestream, &eos); + ok (hr == S_OK, "Failed query end of stream, hr %#x.\n", hr); + ok (eos == TRUE, "Unexpected IsEndOfStream result, %u.\n", eos); + hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, filename, &bytestream2); ok(hr == S_OK, "got 0x%08x\n", hr);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64835
Your paranoid android.
=== w1064v1809_ja (32 bit report) ===
mfplat: mfplat.c:2600: Test failed: Unexpected return value 0x102. mfplat.c:1951: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1954: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1957: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1960: Test failed: Unexpected result, hr 0xc00d3e85. 191c:mfplat: unhandled exception c0000005 at 00404EFB
=== debian10 (64 bit WoW report) ===
mfplat: mfplat.c:3082: Test failed: Unexpected refcount 1. Unhandled exception: page fault on read access to 0x00000009 in 32-bit code (0x65881bc7).
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: - Split into two patches. --- dlls/winegstreamer/gst_cbs.c | 28 ++++++++++++++-------------- dlls/winegstreamer/gst_cbs.h | 18 +++++++++--------- dlls/winegstreamer/gstdemux.c | 14 +++++++------- 3 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index e7a4b41d55..9fc172e292 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -89,9 +89,9 @@ void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) { struct cb_data cbdata = { EXISTING_NEW_PAD };
- cbdata.u.existing_new_pad_data.bin = bin; - cbdata.u.existing_new_pad_data.pad = pad; - cbdata.u.existing_new_pad_data.user = user; + cbdata.u.pad_added_data.element = bin; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user;
call_cb(&cbdata); } @@ -123,11 +123,11 @@ gboolean activate_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, return cbdata.u.activate_mode_data.ret; }
-void no_more_pads_wrapper(GstElement *decodebin, gpointer user) +void no_more_pads_wrapper(GstElement *element, gpointer user) { struct cb_data cbdata = { NO_MORE_PADS };
- cbdata.u.no_more_pads_data.decodebin = decodebin; + cbdata.u.no_more_pads_data.element = element; cbdata.u.no_more_pads_data.user = user;
call_cb(&cbdata); @@ -138,15 +138,15 @@ GstFlowReturn request_buffer_src_wrapper(GstPad *pad, GstObject *parent, guint64 { struct cb_data cbdata = { REQUEST_BUFFER_SRC };
- cbdata.u.request_buffer_src_data.pad = pad; - cbdata.u.request_buffer_src_data.parent = parent; - cbdata.u.request_buffer_src_data.ofs = ofs; - cbdata.u.request_buffer_src_data.len = len; - cbdata.u.request_buffer_src_data.buf = buf; + cbdata.u.getrange_data.pad = pad; + cbdata.u.getrange_data.parent = parent; + cbdata.u.getrange_data.ofs = ofs; + cbdata.u.getrange_data.len = len; + cbdata.u.getrange_data.buf = buf;
call_cb(&cbdata);
- return cbdata.u.request_buffer_src_data.ret; + return cbdata.u.getrange_data.ret; }
gboolean event_src_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) @@ -192,9 +192,9 @@ void removed_decoded_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) { struct cb_data cbdata = { REMOVED_DECODED_PAD };
- cbdata.u.removed_decoded_pad_data.bin = bin; - cbdata.u.removed_decoded_pad_data.pad = pad; - cbdata.u.removed_decoded_pad_data.user = user; + cbdata.u.pad_removed_data.element = bin; + cbdata.u.pad_removed_data.pad = pad; + cbdata.u.pad_removed_data.user = user;
call_cb(&cbdata); } diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 46f8add57c..ff83da5ac3 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -55,11 +55,11 @@ struct cb_data { gpointer user; GstBusSyncReply ret; } watch_bus_data; - struct existing_new_pad_data { - GstElement *bin; + struct pad_added_data { + GstElement *element; GstPad *pad; gpointer user; - } existing_new_pad_data; + } pad_added_data; struct query_function_data { GstPad *pad; GstObject *parent; @@ -74,17 +74,17 @@ struct cb_data { gboolean ret; } activate_mode_data; struct no_more_pads_data { - GstElement *decodebin; + GstElement *element; gpointer user; } no_more_pads_data; - struct request_buffer_src_data { + struct getrange_data { GstPad *pad; GstObject *parent; guint64 ofs; guint len; GstBuffer **buf; GstFlowReturn ret; - } request_buffer_src_data; + } getrange_data; struct event_src_data { GstPad *pad; GstObject *parent; @@ -103,11 +103,11 @@ struct cb_data { GstBuffer *buf; GstFlowReturn ret; } got_data_sink_data; - struct removed_decoded_pad_data { - GstElement *bin; + struct pad_removed_data { + GstElement *element; GstPad *pad; gpointer user; - } removed_decoded_pad_data; + } pad_removed_data; struct autoplug_blacklist_data { GstElement *bin; GstPad *pad; diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 0b5fee3027..a11af29f2a 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -2266,8 +2266,8 @@ void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) } case EXISTING_NEW_PAD: { - struct existing_new_pad_data *data = &cbdata->u.existing_new_pad_data; - existing_new_pad(data->bin, data->pad, data->user); + struct pad_added_data *data = &cbdata->u.pad_added_data; + existing_new_pad(data->element, data->pad, data->user); break; } case QUERY_FUNCTION: @@ -2285,13 +2285,13 @@ void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) case NO_MORE_PADS: { struct no_more_pads_data *data = &cbdata->u.no_more_pads_data; - no_more_pads(data->decodebin, data->user); + no_more_pads(data->element, data->user); break; } case REQUEST_BUFFER_SRC: { - struct request_buffer_src_data *data = &cbdata->u.request_buffer_src_data; - cbdata->u.request_buffer_src_data.ret = request_buffer_src(data->pad, data->parent, + struct getrange_data *data = &cbdata->u.getrange_data; + cbdata->u.getrange_data.ret = request_buffer_src(data->pad, data->parent, data->ofs, data->len, data->buf); break; } @@ -2315,8 +2315,8 @@ void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) } case REMOVED_DECODED_PAD: { - struct removed_decoded_pad_data *data = &cbdata->u.removed_decoded_pad_data; - removed_decoded_pad(data->bin, data->pad, data->user); + struct pad_removed_data *data = &cbdata->u.pad_removed_data; + removed_decoded_pad(data->element, data->pad, data->user); break; } case AUTOPLUG_BLACKLIST:
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: - Zebediah's suggestions. --- dlls/winegstreamer/gst_cbs.c | 66 ++++++++++++++++++++++++++++++++++- dlls/winegstreamer/gst_cbs.h | 9 ++--- dlls/winegstreamer/gstdemux.c | 61 +++----------------------------- 3 files changed, 73 insertions(+), 63 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 9fc172e292..bb110f47b9 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -20,10 +20,74 @@
#include <gst/gst.h>
+#include "objbase.h" + #include "wine/list.h"
#include "gst_cbs.h"
+static pthread_key_t wine_gst_key; + +void mark_wine_thread(void) +{ + /* set it to non-NULL to indicate that this is a Wine thread */ + pthread_setspecific(wine_gst_key, &wine_gst_key); +} + +BOOL is_wine_thread(void) +{ + return pthread_getspecific(wine_gst_key) != NULL; +} + +pthread_mutex_t cb_list_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cb_list_cond = PTHREAD_COND_INITIALIZER; +struct list cb_list = LIST_INIT(cb_list); + +void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) +{ + struct cb_data *cbdata = user; + + if (cbdata->type < GSTDEMUX_MAX) + perform_cb_gstdemux(cbdata); + + pthread_mutex_lock(&cbdata->lock); + cbdata->finished = 1; + pthread_cond_broadcast(&cbdata->cond); + pthread_mutex_unlock(&cbdata->lock); +} + +static DWORD WINAPI dispatch_thread(void *user) +{ + struct cb_data *cbdata; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + pthread_mutex_lock(&cb_list_lock); + + while(1){ + pthread_cond_wait(&cb_list_cond, &cb_list_lock); + + while(!list_empty(&cb_list)){ + cbdata = LIST_ENTRY(list_head(&cb_list), struct cb_data, entry); + list_remove(&cbdata->entry); + + TrySubmitThreadpoolCallback(&perform_cb, cbdata, NULL); + } + } + + pthread_mutex_unlock(&cb_list_lock); + + CoUninitialize(); + + return 0; +} + +void start_dispatch_thread(void) +{ + pthread_key_create(&wine_gst_key, NULL); + CloseHandle(CreateThread(NULL, 0, &dispatch_thread, NULL, 0, NULL)); +} + /* gstreamer calls our callbacks from threads that Wine did not create. Some * callbacks execute code which requires Wine to have created the thread * (critical sections, debug logging, dshow client code). Since gstreamer can't @@ -31,7 +95,7 @@ * callbacks in code which avoids the Wine thread requirement, and then * dispatch those callbacks on a thread that is known to be created by Wine. * - * This file must not contain any code that depends on the Wine TEB! + * This thread must not run any code that depends on the Wine TEB! */
static void call_cb(struct cb_data *cbdata) diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index ff83da5ac3..542b5e933e 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -43,7 +43,8 @@ enum CB_TYPE { AUTOPLUG_BLACKLIST, UNKNOWN_TYPE, RELEASE_SAMPLE, - QUERY_SINK + QUERY_SINK, + GSTDEMUX_MAX };
struct cb_data { @@ -139,12 +140,8 @@ struct cb_data { struct list entry; };
-extern pthread_mutex_t cb_list_lock DECLSPEC_HIDDEN; -extern pthread_cond_t cb_list_cond DECLSPEC_HIDDEN; -extern struct list cb_list DECLSPEC_HIDDEN; -void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) DECLSPEC_HIDDEN; -BOOL is_wine_thread(void) DECLSPEC_HIDDEN; void mark_wine_thread(void) DECLSPEC_HIDDEN; +void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index a11af29f2a..6915328e05 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -48,8 +48,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(gstreamer);
static const GUID MEDIASUBTYPE_CVID = {mmioFOURCC('c','v','i','d'), 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
-static pthread_key_t wine_gst_key; - struct gstdemux { struct strmbase_filter filter; @@ -109,17 +107,6 @@ static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface); static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface); static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface);
-void mark_wine_thread(void) -{ - /* set it to non-NULL to indicate that this is a Wine thread */ - pthread_setspecific(wine_gst_key, &wine_gst_key); -} - -BOOL is_wine_thread(void) -{ - return pthread_getspecific(wine_gst_key) != NULL; -} - static gboolean amt_from_gst_caps_audio_raw(const GstCaps *caps, AM_MEDIA_TYPE *amt) { WAVEFORMATEXTENSIBLE *wfe; @@ -2248,14 +2235,8 @@ static HRESULT GST_RemoveOutputPins(struct gstdemux *This) return S_OK; }
-pthread_mutex_t cb_list_lock = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t cb_list_cond = PTHREAD_COND_INITIALIZER; -struct list cb_list = LIST_INIT(cb_list); - -void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) +void perform_cb_gstdemux(struct cb_data *cbdata) { - struct cb_data *cbdata = user; - switch(cbdata->type) { case WATCH_BUS: @@ -2345,44 +2326,12 @@ void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) data->query); break; } - } - - pthread_mutex_lock(&cbdata->lock); - cbdata->finished = 1; - pthread_cond_broadcast(&cbdata->cond); - pthread_mutex_unlock(&cbdata->lock); -} - -static DWORD WINAPI dispatch_thread(void *user) -{ - struct cb_data *cbdata; - - CoInitializeEx(NULL, COINIT_MULTITHREADED); - - pthread_mutex_lock(&cb_list_lock); - - while(1){ - pthread_cond_wait(&cb_list_cond, &cb_list_lock); - - while(!list_empty(&cb_list)){ - cbdata = LIST_ENTRY(list_head(&cb_list), struct cb_data, entry); - list_remove(&cbdata->entry); - - TrySubmitThreadpoolCallback(&perform_cb, cbdata, NULL); + default: + { + ERR("Wrong callback forwarder called\n"); + assert(0); } } - - pthread_mutex_unlock(&cb_list_lock); - - CoUninitialize(); - - return 0; -} - -void start_dispatch_thread(void) -{ - pthread_key_create(&wine_gst_key, NULL); - CloseHandle(CreateThread(NULL, 0, &dispatch_thread, NULL, 0, NULL)); }
static BOOL compare_media_types(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64837
Your paranoid android.
=== debian10 (32 bit WoW report) ===
mfplat: mfplat.c:3082: Test failed: Unexpected refcount 1. Unhandled exception: page fault on execute access to 0x19625000 in 32-bit code (0x19625000).
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: Use CoInitialize and CoUninitialize instead of TLS. --- dlls/mfplat/tests/mfplat.c | 86 ++++++++++++++++++++++++++++++++++++++ dlls/rtworkq/Makefile.in | 1 + dlls/rtworkq/queue.c | 4 ++ 3 files changed, 91 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index c468286362..f056bbda2a 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -2220,6 +2220,91 @@ static void test_allocate_queue(void) ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); }
+static HRESULT WINAPI test_get_com_state_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + APTTYPE com_type; + APTTYPEQUALIFIER qualifier; + HRESULT hr; + + hr = CoGetApartmentType(&com_type, &qualifier); + ok (SUCCEEDED(hr), "Failed to get apartment type, hr %#x.\n", hr); + if (SUCCEEDED(hr)) + { + ok (com_type == APTTYPE_MTA && qualifier == APTTYPEQUALIFIER_NONE, + "Unexpected type %u, qualifier %u.\n", com_type, qualifier); + } + + SetEvent(callback->event); + return S_OK; +} + +static const IMFAsyncCallbackVtbl test_get_com_state_callback_vtbl = +{ + testcallback_QueryInterface, + testcallback_AddRef, + testcallback_Release, + testcallback_GetParameters, + test_get_com_state_callback_Invoke, +}; + +static void test_queue_com_state(void) +{ + DWORD standard, multithreaded, window; + struct test_callback callback = { { &test_get_com_state_callback_vtbl } }; + HRESULT hr; + + callback.event = CreateEventA(NULL, TRUE, FALSE, NULL); + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#x.\n", hr); + + for (DWORD i = MFASYNC_CALLBACK_QUEUE_STANDARD; i <= MFASYNC_CALLBACK_QUEUE_MULTITHREADED; i++) + { + hr = MFPutWorkItem(i, &callback.IMFAsyncCallback_iface, NULL); + ok (SUCCEEDED(hr), "Failed to queue work item, hr %#x.\n", hr); + WaitForSingleObject(callback.event, INFINITE); + ResetEvent(callback.event); + } + + hr = MFAllocateWorkQueueEx(MF_STANDARD_WORKQUEUE, &standard); + ok(hr == S_OK, "Failed to allocate a queue, hr %#x.\n", hr); + + hr = MFAllocateWorkQueueEx(MF_MULTITHREADED_WORKQUEUE, &multithreaded); + ok(hr == S_OK, "Failed to allocate a queue, hr %#x.\n", hr); + + hr = MFAllocateWorkQueueEx(MF_WINDOW_WORKQUEUE, &window); + ok(hr == S_OK, "Failed to allocate a queue, hr %#x.\n", hr); + + hr = MFPutWorkItem(standard, &callback.IMFAsyncCallback_iface, NULL); + ok (SUCCEEDED(hr), "Failed to queue work item, hr %#x.\n", hr); + WaitForSingleObject(callback.event, INFINITE); + ResetEvent(callback.event); + + hr = MFPutWorkItem(multithreaded, &callback.IMFAsyncCallback_iface, NULL); + ok (SUCCEEDED(hr), "Failed to queue work item, hr %#x.\n", hr); + WaitForSingleObject(callback.event, INFINITE); + ResetEvent(callback.event); + + hr = MFPutWorkItem(window, &callback.IMFAsyncCallback_iface, NULL); + ok (SUCCEEDED(hr), "Failed to queue work item, hr %#x.\n", hr); + WaitForSingleObject(callback.event, INFINITE); + + CloseHandle(callback.event); + + hr = MFUnlockWorkQueue(standard); + ok(hr == S_OK, "Failed to unlock the queue, hr %#x.\n", hr); + + hr = MFUnlockWorkQueue(multithreaded); + ok(hr == S_OK, "Failed to unlock the queue, hr %#x.\n", hr); + + hr = MFUnlockWorkQueue(window); + ok(hr == S_OK, "Failed to unlock the queue, hr %#x.\n", hr); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); +} + static void test_MFCopyImage(void) { BYTE dest[16], src[16]; @@ -4135,6 +4220,7 @@ START_TEST(mfplat) test_source_resolver(); test_MFCreateAsyncResult(); test_allocate_queue(); + test_queue_com_state(); test_MFCopyImage(); test_MFCreateCollection(); test_MFHeapAlloc(); diff --git a/dlls/rtworkq/Makefile.in b/dlls/rtworkq/Makefile.in index a224198986..b356b3cb24 100644 --- a/dlls/rtworkq/Makefile.in +++ b/dlls/rtworkq/Makefile.in @@ -1,5 +1,6 @@ MODULE = rtworkq.dll IMPORTLIB = rtworkq +IMPORTS = ole32
EXTRADLLFLAGS = -mno-cygwin
diff --git a/dlls/rtworkq/queue.c b/dlls/rtworkq/queue.c index a2fb3824c3..a65167789f 100644 --- a/dlls/rtworkq/queue.c +++ b/dlls/rtworkq/queue.c @@ -333,8 +333,12 @@ static void CALLBACK standard_queue_worker(TP_CALLBACK_INSTANCE *instance, void
TRACE("result object %p.\n", result);
+ CoInitializeEx(NULL, COINIT_MULTITHREADED); + IRtwqAsyncCallback_Invoke(result->pCallback, item->result);
+ CoUninitialize(); + release_work_item(item); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64838
Your paranoid android.
=== wxppro (32 bit report) ===
mfplat: mfplat: Timeout
=== w1064v1809_he (32 bit report) ===
mfplat: mfplat.c:2685: Test failed: Unexpected return value 0x102.
On 2/11/20 01:32, Derek Lesho wrote:
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IRtwqAsyncCallback_Invoke(result->pCallback, item->result);
CoUninitialize();
release_work_item(item);
}
Can it happen that the apartment is already initialized as apartment threaded earlier? If yes, this can leave the caller with uninitialized apartment.
On 2/11/20 11:13 AM, Paul Gofman wrote:
On 2/11/20 01:32, Derek Lesho wrote:
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IRtwqAsyncCallback_Invoke(result->pCallback, item->result);
CoUninitialize();
release_work_item(item);
}
Can it happen that the apartment is already initialized as apartment threaded earlier? If yes, this can leave the caller with uninitialized apartment.
Yes, I think it's a valid concern. There is newer API to essentially pin MTA for any time period you want by explicitly incrementing/decrementing refcount, but we don't have it available in wine yet. It seems more fitting in this case, because it won't be affected by refcount manipulations user code might do.
Anyway, such initialization tests have to run in separate process, once per test attempt, to exclude unwanted interaction with earlier tests.
Another case to consider - if user code relies on using STA, how does that align with already initialized MTA.
And to add more to that heap, explicit initialization calls like MFStartup()/RtwqStartup() could be doing COM initialization too potentially, that would be unrelated to async queue execution.
I will add more tests for this.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v2: Remove dependency on handler helper ): --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/media_source.c | 718 ++++++++++++++++++++++++++++++ 3 files changed, 721 insertions(+) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 2050a44b13..84916fe649 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \ + media_source.c \ mediatype.c \ mfplat.c \ pin.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 353ff0bbd5..8b11760740 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -47,4 +47,6 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; extern HRESULT mfplat_can_unload_now(void) DECLSPEC_HIDDEN;
+HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char *demuxer_name); + #endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c new file mode 100644 index 0000000000..ae5bd4c121 --- /dev/null +++ b/dlls/winegstreamer/media_source.c @@ -0,0 +1,718 @@ +#include "gst_private.h" + +#include <stdarg.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "mfapi.h" +#include "mferror.h" +#include "mfidl.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + LONG ref; + IMFMediaEventQueue *event_queue; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; +}; + +/* source */ + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->IMFMediaSource_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + if (This->state != SOURCE_SHUTDOWN) + ERR("Application has freed media source without calling ::Shutdown\n"); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, characteristics); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, descriptor); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *start_position) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT media_source_teardown(struct media_source *This) +{ + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)\n", This); + + This->state = SOURCE_SHUTDOWN; + return media_source_teardown(This); +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +static HRESULT media_source_constructor(IMFByteStream *bytestream, const char *demuxer_name, struct media_source **out_media_source) +{ + struct media_source *This = heap_alloc_zero(sizeof(*This)); + HRESULT hr; + + if (!This) + return E_OUTOFMEMORY; + + This->state = SOURCE_OPENING; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + goto fail; + + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + This->ref = 1; + + *out_media_source = This; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#x.\n", hr); + + media_source_teardown(This); + heap_free(This); + return hr; +} + +/* IMFByteStreamHandler */ + +struct container_stream_handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +struct container_stream_handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + const char *demuxer_name; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface); +} + +static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %u.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct container_stream_handler_result *result, *next; + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); + heap_free(handler); + } + + return refcount; +} + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + if (context->url) + heap_free(context->url); + heap_free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (lstrlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + context = heap_alloc(sizeof(*context)); + if (!context) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = heap_strdupW(url); + if (!context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + IMFAsyncResult_Release(caller); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + IMFAsyncResult_GetState(item, cancel_cookie); + } + + IMFAsyncResult_Release(item); + } + + return hr; +} + +static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + heap_free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + + TRACE("%p, %p.\n", iface, cancel_cookie); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = +{ + container_stream_handler_QueryInterface, + container_stream_handler_AddRef, + container_stream_handler_Release, + container_stream_handler_BeginCreateObject, + container_stream_handler_EndCreateObject, + container_stream_handler_CancelObjectCreation, + container_stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +static HRESULT WINAPI container_stream_handler_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 container_stream_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) +{ + TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + + if (flags & MF_RESOLUTION_MEDIASOURCE) + { + HRESULT hr; + struct media_source *new_source; + + if (FAILED(hr = media_source_constructor(stream, This->demuxer_name, &new_source))) + return hr; + + TRACE("->(%p)\n", new_source); + + *out_object = (IUnknown*)&new_source->IMFMediaSource_iface; + *out_obj_type = MF_OBJECT_MEDIASOURCE; + + return S_OK; + } + else + { + FIXME("flags = %08x\n", flags); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + struct container_stream_handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + handler_result = heap_alloc(sizeof(*handler_result)); + if (handler_result) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl = +{ + container_stream_handler_callback_QueryInterface, + container_stream_handler_callback_AddRef, + container_stream_handler_callback_Release, + container_stream_handler_callback_GetParameters, + container_stream_handler_callback_Invoke, +}; + +HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char *demuxer_name) +{ + struct container_stream_handler *this; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + this = heap_alloc_zero(sizeof(*this)); + if (!this) + return E_OUTOFMEMORY; + + list_init(&this->results); + InitializeCriticalSection(&this->cs); + + this->demuxer_name = demuxer_name; + this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl; + this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl; + this->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface); + + return hr; +} \ No newline at end of file
On 2/11/20 1:32 AM, Derek Lesho wrote:
--- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \
- media_source.c \ mediatype.c \ mfplat.c \ pin.c \
Can we have everything in single mfplat.c?
+static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{
- struct media_source *This = impl_from_IMFMediaSource(iface);
- ULONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p) ref=%u\n", This, ref);
- if (!ref)
- {
if (This->state != SOURCE_SHUTDOWN)
ERR("Application has freed media source without calling ::Shutdown\n");
heap_free(This);
- }
- return ref;
+}
I don't think it's necessarily an error. You should shutdown and release event queue here by the way.
+static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{
- struct media_source *This = impl_from_IMFMediaSource(iface);
- TRACE("(%p)->(%#x, %p)\n", This, flags, event);
- if (This->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
- return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event);
+}
+static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{
- struct media_source *This = impl_from_IMFMediaSource(iface);
- TRACE("(%p)->(%p, %p)\n", This, callback, state);
- if (This->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
- return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state);
+}
State checks here are unnecessary, on source Shutdown() event queue should be shut down too, to unblock potential waiting threads.
P.S. please use names and traces formats similar to current code in other media foundation module - This -> source, etc.
+static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{
- struct media_source *This = impl_from_IMFMediaSource(iface);
- TRACE("(%p)\n", This);
- This->state = SOURCE_SHUTDOWN;
- return media_source_teardown(This);
+}
I think this and everything testing/setting state should be protected for multiple threads access.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_cbs.c | 112 ++++ dlls/winegstreamer/gst_cbs.h | 31 +- dlls/winegstreamer/gst_private.h | 5 + dlls/winegstreamer/media_source.c | 983 +++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 104 ++++ 5 files changed, 1227 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index bb110f47b9..be1b565eb7 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -49,6 +49,8 @@ void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
if (cbdata->type < GSTDEMUX_MAX) perform_cb_gstdemux(cbdata); + else if (cbdata->type < MEDIA_SOURCE_MAX) + perform_cb_media_source(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -312,3 +314,113 @@ gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
return cbdata.u.query_sink_data.ret; } + +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct cb_data cbdata = { PULL_FROM_BYTESTREAM }; + + cbdata.u.getrange_data.pad = pad; + cbdata.u.getrange_data.parent = parent; + cbdata.u.getrange_data.ofs = ofs; + cbdata.u.getrange_data.len = len; + cbdata.u.getrange_data.buf = buf; + + call_cb(&cbdata); + + return cbdata.u.getrange_data.ret; +} + +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_BYTESTREAM }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_BYTESTREAM_PAD_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct cb_data cbdata = { PROCESS_BYTESTREAM_PAD_EVENT }; + + cbdata.u.event_src_data.pad = pad; + cbdata.u.event_src_data.parent = parent; + cbdata.u.event_src_data.event = event; + + call_cb(&cbdata); + + return cbdata.u.event_src_data.ret; +} + +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_ADDED }; + + cbdata.u.pad_added_data.element = bin; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_REMOVED }; + + cbdata.u.pad_removed_data.element = element; + cbdata.u.pad_removed_data.pad = pad; + cbdata.u.pad_removed_data.user = user; + + call_cb(&cbdata); +} + +void source_all_streams_wrapper(GstElement *element, gpointer user) +{ + struct cb_data cbdata = { SOURCE_ALL_STREAMS }; + + cbdata.u.no_more_pads_data.element = element; + cbdata.u.no_more_pads_data.user = user; + + call_cb(&cbdata); +} + +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_NEW_SAMPLE }; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; +} + +void stream_eos_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_EOS }; + + cbdata.u.eos_data.appsink = appsink; + cbdata.u.eos_data.user = user; + + call_cb(&cbdata); +} \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 542b5e933e..9b1045f3a8 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -44,7 +44,17 @@ enum CB_TYPE { UNKNOWN_TYPE, RELEASE_SAMPLE, QUERY_SINK, - GSTDEMUX_MAX + GSTDEMUX_MAX, + PULL_FROM_BYTESTREAM, + QUERY_BYTESTREAM, + ACTIVATE_BYTESTREAM_PAD_MODE, + PROCESS_BYTESTREAM_PAD_EVENT, + SOURCE_STREAM_ADDED, + SOURCE_STREAM_REMOVED, + SOURCE_ALL_STREAMS, + STREAM_NEW_SAMPLE, + STREAM_EOS, + MEDIA_SOURCE_MAX };
struct cb_data { @@ -132,6 +142,15 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data; + struct new_sample_data { + GstElement *appsink; + gpointer user; + GstFlowReturn ret; + } new_sample_data; + struct eos_data { + GstElement *appsink; + gpointer user; + } eos_data; } u;
int finished; @@ -142,6 +161,7 @@ struct cb_data {
void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -159,5 +179,14 @@ void unknown_type_wrapper(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer void release_sample_wrapper(gpointer data) DECLSPEC_HIDDEN; void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN; +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN; +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN; +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 8b11760740..13f4fde4f2 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -30,6 +30,8 @@ #include "winuser.h" #include "dshow.h" #include "strmif.h" +#include "mfidl.h" +#include "mfobjects.h" #include "wine/heap.h" #include "wine/strmbase.h"
@@ -47,6 +49,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; extern HRESULT mfplat_can_unload_now(void) DECLSPEC_HIDDEN;
+IMFMediaType* mfplat_media_type_from_caps(GstCaps *caps); +IMFSample* mf_sample_from_gst_sample(GstSample *in); + HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char *demuxer_name);
#endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ae5bd4c121..4e48c100b0 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1,4 +1,9 @@ +#include "config.h" + +#include <gst/gst.h> + #include "gst_private.h" +#include "gst_cbs.h"
#include <stdarg.h>
@@ -8,27 +13,473 @@ #include "mfapi.h" #include "mferror.h" #include "mfidl.h" +#include "mfobjects.h"
#include "wine/debug.h" #include "wine/heap.h" #include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct sample_request +{ + struct list entry; + IUnknown *token; +}; + +struct media_source; + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + GstElement *appsink; + GstPad *their_src, *appsink_sink; + /* usually reflects state of source */ + enum + { + STREAM_INACTIVE, + STREAM_ENABLED, + STREAM_PAUSED, + STREAM_RUNNING, + STREAM_SHUTDOWN, + } state; + BOOL eos; + CRITICAL_SECTION dispatch_samples_cs; + struct list sample_requests; + unsigned int pending_samples; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; LONG ref; IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + struct media_stream **streams; + ULONG stream_count; + IMFPresentationDescriptor *pres_desc; + GstElement *demuxer; + GstPad *my_src, *their_sink; enum { SOURCE_OPENING, - SOURCE_STOPPED, + SOURCE_STOPPED, /* (READY) */ SOURCE_PAUSED, SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; + CRITICAL_SECTION streams_cs; + HANDLE init_complete_event; +}; + +/* stream */ + +static void media_source_notify_stream_ended(struct media_source *source); +static void stream_dispatch_samples(struct media_stream *This) +{ + struct sample_request *req, *cursor2; + + if (This->state != STREAM_RUNNING && This->state != STREAM_SHUTDOWN) + return; + + EnterCriticalSection(&This->dispatch_samples_cs); + + LIST_FOR_EACH_ENTRY_SAFE(req, cursor2, &This->sample_requests, struct sample_request, entry) + { + IMFSample *sample; + + if (This->state == STREAM_SHUTDOWN + /* Not sure if this is correct: */ + || (!(This->pending_samples) && This->eos)) + { + if (req->token) + { + IUnknown_Release(req->token); + } + list_remove(&req->entry); + continue; + } + + if (!(This->pending_samples)) + { + break; + } + + /* Get the sample from the appsink, then construct an IMFSample */ + /* We do this in the dispatch function so we can have appsink buffer for us */ + { + GstSample *gst_sample; + + TRACE("Trying to pull sample\n"); + + g_signal_emit_by_name (This->appsink, "pull-sample", &gst_sample); + if (!gst_sample) + { + ERR("Appsink has no samples and pending_samples != 0\n"); + break; + } + + sample = mf_sample_from_gst_sample(gst_sample); + } + + if (req->token) + { + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token); + } + + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + + if (req->token) + { + IUnknown_Release(req->token); + } + + list_remove(&req->entry); + + This->pending_samples--; + } + + if (This->eos && !This->pending_samples && This->state == STREAM_RUNNING) + { + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty); + media_source_notify_stream_ended(This->parent_source); + } + LeaveCriticalSection(&This->dispatch_samples_cs); +} + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + ERR("incomplete cleanup\n"); + IMFMediaEventQueue_Release(This->event_queue); + IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + FIXME("stub (%p)->(%p)\n", This, source); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", This, descriptor); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + IMFStreamDescriptor_AddRef(This->descriptor); + *descriptor = This->descriptor; + + return S_OK; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + struct sample_request *req; + + TRACE("(%p)->(%p)\n", iface, token); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (This->eos && !This->pending_samples) + return MF_E_END_OF_STREAM; + + req = heap_alloc(sizeof(*req)); + if (token) + IUnknown_AddRef(token); + req->token = token; + list_add_tail(&This->sample_requests, &req->entry); + + stream_dispatch_samples(This); + + return S_OK; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample };
+static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) got sample\n", This); + + if (This->state == STREAM_INACTIVE) + { + ERR("got sample on inactive stream\n"); + } + + This->pending_samples++; + stream_dispatch_samples(This); + return GST_FLOW_OK; +} + +void stream_eos(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) EOS\n", This); + + This->eos = TRUE; + + stream_dispatch_samples(This); +} + +static void media_stream_teardown(struct media_stream *This) +{ + TRACE("(%p)\n", This); + + This->state = STREAM_SHUTDOWN; + + if (This->their_src) + gst_object_unref(GST_OBJECT(This->their_src)); + if (This->appsink_sink) + gst_object_unref(GST_OBJECT(This->appsink_sink)); + if (This->appsink) + { + gst_element_set_state(This->appsink, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(This->appsink)); + } + + /* Frees pending requests and samples when state == STREAM_SHUTDOWN */ + stream_dispatch_samples(This); + + if (This->descriptor) + IMFStreamDescriptor_Release(This->descriptor); + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + if (This->parent_source) + IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface); + + DeleteCriticalSection(&This->dispatch_samples_cs); +} + +static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) +{ + HRESULT hr; + GstCaps *caps = NULL; + IMFMediaType *media_type; + IMFMediaTypeHandler *type_handler; + struct media_stream *This = heap_alloc_zero(sizeof(*This)); + + TRACE("(%p %p)->(%p)\n", source, pad, out_stream); + + This->state = STREAM_INACTIVE; + This->pending_samples = 0; + list_init(&This->sample_requests); + This->eos = FALSE; + InitializeCriticalSection(&This->dispatch_samples_cs); + + if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface))) + { + goto fail; + } + This->parent_source = source; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + { + goto fail; + } + + caps = gst_pad_query_caps(pad, NULL); + + if (!(caps)) + { + goto fail; + } + + if (FAILED(hr = MFCreateMediaType(&media_type))) + { + goto fail; + } + + caps = gst_caps_make_writable(caps); + media_type = mfplat_media_type_from_caps(caps); + gst_caps_unref(caps); + caps = NULL; + + MFCreateStreamDescriptor(stream_id, 1, &media_type, &This->descriptor); + + IMFStreamDescriptor_GetMediaTypeHandler(This->descriptor, &type_handler); + IMFMediaTypeHandler_SetCurrentMediaType(type_handler, media_type); + IMFMediaTypeHandler_Release(type_handler); + IMFMediaType_Release(media_type); + media_type = NULL; + + /* Setup appsink element, but don't link it to the demuxer (it isn't selected by default) */ + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This); + g_signal_connect(This->appsink, "eos", G_CALLBACK(stream_eos_wrapper), This); + + /* always in playing state */ + gst_element_set_state(This->appsink, GST_STATE_PLAYING); + + This->appsink_sink = gst_element_get_static_pad(This->appsink, "sink"); + + This->their_src = pad; + gst_pad_set_element_private(pad, This); + + This->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + This->ref = 1; + + TRACE("->(%p)\n", This); + + *out_stream = This; + return S_OK; + + fail: + WARN("Failed to construct media stream, hr %#x.\n", hr); + + /* Destroy temporary objects */ + if (caps) + gst_caps_unref(caps); + if (media_type) + IMFMediaType_Release(media_type); + + media_stream_teardown(This); + heap_free(This); + return hr; +} + /* source */
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) @@ -151,25 +602,80 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, descriptor); + TRACE("(%p)->(%p)\n", This, descriptor);
if (This->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + if (!(This->pres_desc)) + { + return MF_E_NOT_INITIALIZED; + } + + IMFPresentationDescriptor_Clone(This->pres_desc, descriptor); + + return S_OK; }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *This = impl_from_IMFMediaSource(iface); + PROPVARIANT empty_var; + empty_var.vt = VT_EMPTY;
- FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + TRACE("(%p)->(%p, %p, %p)\n", This, descriptor, time_format, start_position);
if (This->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + /* Find out which streams are active */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + IMFStreamDescriptor *stream_desc; + DWORD in_stream_id; + BOOL selected; + + IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, &selected, &stream_desc); + IMFStreamDescriptor_GetStreamIdentifier(stream_desc, &in_stream_id); + + for (unsigned int k = 0; k < This->stream_count; k++) + { + DWORD cur_stream_id; + + IMFStreamDescriptor_GetStreamIdentifier(This->streams[k]->descriptor, &cur_stream_id); + + if (in_stream_id == cur_stream_id) + { + BOOL was_active = This->streams[k]->state != STREAM_INACTIVE; + This->streams[k]->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + if (selected) + { + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &This->streams[k]->IMFMediaStream_iface); + IMFMediaEventQueue_QueueEventParamVar(This->streams[k]->event_queue, + MEStreamStarted, &GUID_NULL, S_OK, &empty_var); + stream_dispatch_samples(This->streams[k]); + } + } + } + + IMFStreamDescriptor_Release(stream_desc); + } + + if (!IsEqualIID(time_format, &GUID_NULL) || start_position->vt != VT_EMPTY) + { + WARN("ignoring start time\n"); + return MF_E_UNSUPPORTED_TIME_FORMAT; + } + + This->state = SOURCE_RUNNING; + gst_element_set_state(This->demuxer, GST_STATE_PLAYING); + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MESourceStarted, &GUID_NULL, S_OK, &empty_var); + + return S_OK; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -198,8 +704,34 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
static HRESULT media_source_teardown(struct media_source *This) { + if (This->my_src) + gst_object_unref(G_OBJECT(This->my_src)); + if (This->their_sink) + gst_object_unref(G_OBJECT(This->their_sink)); + if (This->demuxer) + { + gst_element_set_state(This->demuxer, GST_STATE_NULL); + gst_object_unref(G_OBJECT(This->demuxer)); + } + if (This->pres_desc) + IMFPresentationDescriptor_Release(This->pres_desc); if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); + if (This->byte_stream) + IMFByteStream_Release(This->byte_stream); + + for (unsigned int i = 0; i < This->stream_count; i++) + { + media_stream_teardown(This->streams[i]); + IMFMediaStream_Release(&This->streams[i]->IMFMediaStream_iface); + } + + if (This->stream_count) + heap_free(This->streams); + + if (This->init_complete_event) + CloseHandle(This->init_complete_event); + DeleteCriticalSection(&This->streams_cs);
return S_OK; } @@ -231,21 +763,387 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct media_source *This = gst_pad_get_element_private(pad); + IMFByteStream *byte_stream = This->byte_stream; + BOOL is_eof; + GstMapInfo info; + ULONG bytes_read; + HRESULT hr; + + TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), This, buf); + + if (ofs != GST_BUFFER_OFFSET_NONE) + { + if (FAILED(IMFByteStream_SetCurrentPosition(byte_stream, ofs))) + return GST_FLOW_ERROR; + } + + if (FAILED(IMFByteStream_IsEndOfStream(byte_stream, &is_eof))) + return GST_FLOW_ERROR; + if (is_eof) + return GST_FLOW_EOS; + + *buf = gst_buffer_new_and_alloc(len); + gst_buffer_map(*buf, &info, GST_MAP_WRITE); + hr = IMFByteStream_Read(byte_stream, info.data, len, &bytes_read); + gst_buffer_unmap(*buf, &info); + + gst_buffer_set_size(*buf, bytes_read); + + if (FAILED(hr)) + { + return GST_FLOW_ERROR; + } + GST_BUFFER_OFFSET(*buf) = ofs; + return GST_FLOW_OK; +} + +static gboolean query_bytestream(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct media_source *This = gst_pad_get_element_private(pad); + GstFormat format; + QWORD bytestream_len; + gboolean ret; + + TRACE("GStreamer queries source %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + if (FAILED(IMFByteStream_GetLength(This->byte_stream, &bytestream_len))) + return FALSE; + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_DURATION: + { + LONGLONG duration; + + gst_query_parse_duration (query, &format, NULL); + if (format == GST_FORMAT_PERCENT) { + gst_query_set_duration (query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX); + return TRUE; + } + ret = gst_pad_query_convert (pad, GST_FORMAT_BYTES, bytestream_len, format, &duration); + gst_query_set_duration(query, format, duration); + return ret; + } + case GST_QUERY_SEEKING: + { + gst_query_parse_seeking (query, &format, NULL, NULL, NULL); + if (format != GST_FORMAT_BYTES) + { + WARN("Cannot seek using format "%s".\n", gst_format_get_name(format)); + return FALSE; + } + gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len); + return TRUE; + } + case GST_QUERY_SCHEDULING: + { + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + return TRUE; + } + case GST_QUERY_CAPS: + { + GstCaps *caps, *filter; + + gst_query_parse_caps(query, &filter); + + caps = gst_caps_new_any(); + + if (filter) { + GstCaps* filtered; + filtered = gst_caps_intersect_full( + filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(caps); + caps = filtered; + } + gst_query_set_caps_result(query, caps); + gst_caps_unref(caps); + return TRUE; + } + default: + { + WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query)); + return FALSE; + } + } +} + +static gboolean activate_bytestream_pad_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct media_source *source = gst_pad_get_element_private(pad); + + TRACE("%s source pad for mediasource %p in %s mode.\n", + activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode)); + + /* There is no push mode in mfplat */ + + switch (mode) { + case GST_PAD_MODE_PULL: + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct media_source *This = gst_pad_get_element_private(pad); + + TRACE("filter %p, type "%s".\n", This, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) { + default: + WARN("Ignoring "%s" event.\n", GST_EVENT_TYPE_NAME(event)); + case GST_EVENT_TAG: + case GST_EVENT_QOS: + case GST_EVENT_RECONFIGURE: + return gst_pad_event_default(pad, parent, event); + } + return TRUE; +} + +static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + struct media_source *source = (struct media_source *) user; + struct media_stream **new_stream_array; + gchar *g_stream_id; + const char *stream_id_string; + DWORD stream_id; + + EnterCriticalSection(&source->streams_cs); + + g_stream_id = gst_pad_get_stream_id(pad); + stream_id_string = strstr(g_stream_id, "/"); + sscanf(stream_id_string, "/%03u", &stream_id); + TRACE("stream-id: %u\n", stream_id); + g_free(g_stream_id); + + /* find existing stream */ + for (unsigned int i = 0; i < source->stream_count; i++) + { + DWORD existing_stream_id; + IMFStreamDescriptor *descriptor = source->streams[i]->descriptor; + + if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id))) + goto leave; + + if (existing_stream_id == stream_id) + { + struct media_stream *existing_stream = source->streams[i]; + + TRACE("Found existing stream %p\n", existing_stream); + + if (!existing_stream->appsink_sink) + { + ERR("Couldn't find our appsink sink\n"); + goto leave; + } + + existing_stream->their_src = pad; + gst_pad_set_element_private(pad, existing_stream); + + if (existing_stream->state != STREAM_INACTIVE) + { + GstPadLinkReturn err = gst_pad_link(existing_stream->their_src, existing_stream->appsink_sink); + if (err != GST_PAD_LINK_OK) + { + ERR("Error linking demuxer to appsink %u\n", err); + } + } + goto leave; + } + } + + if (FAILED(media_stream_constructor(source, pad, stream_id, &stream))) + { + goto leave; + } + + if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array))))) + { + ERR("Failed to add stream to source\n"); + goto leave; + } + + source->streams = new_stream_array; + source->streams[source->stream_count++] = stream; + + leave: + LeaveCriticalSection(&source->streams_cs); + return; +} + +static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + + if (gst_pad_get_direction(pad) != GST_PAD_SRC) + { + return; + } + + stream = (struct media_stream *) gst_pad_get_element_private(pad); + + if (stream) + { + if (stream->their_src != pad) + { + ERR("assert: unexpected pad/user combination!!!"); + return; + } + if (stream->state != STREAM_INACTIVE) + { + gst_pad_unlink(stream->their_src, stream->appsink_sink); + } + + stream->their_src = NULL; + gst_pad_set_element_private(pad, NULL); + } +} + +static void source_all_streams(GstElement *element, gpointer user) +{ + IMFStreamDescriptor **descriptors; + struct media_source *source = (struct media_source *) user; + + EnterCriticalSection(&source->streams_cs); + if (source->state != SOURCE_OPENING) + goto leave; + + /* Init presentation descriptor */ + + descriptors = heap_alloc(source->stream_count * sizeof(IMFStreamDescriptor*)); + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFMediaStream_GetStreamDescriptor(&source->streams[i]->IMFMediaStream_iface, &descriptors[i]); + } + + if (FAILED(MFCreatePresentationDescriptor(source->stream_count, descriptors, &source->pres_desc))) + goto leave; + + /*if (SUCCEEDED(IMFByteStream_GetItem(source->byte_stream, &MF_BYTESTREAM_CONTENT_TYPE, &mime_type))) + { + IMFPresentationDescriptor_SetItem(source->pres_desc, &MF_PD_MIME_TYPE, mime_type); + PropVariantClear(&mime_type); + }*/ + + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFStreamDescriptor_Release(descriptors[i]); + } + heap_free(descriptors); + + SetEvent(source->init_complete_event); + + leave: + LeaveCriticalSection(&source->streams_cs); +} + +static void media_source_notify_stream_ended(struct media_source *This) +{ + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + /* A stream has ended, check whether all have */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + struct media_stream *stream = This->streams[i]; + + if (!stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, const char *demuxer_name, struct media_source **out_media_source) { + GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + struct media_source *This = heap_alloc_zero(sizeof(*This)); + int ret; HRESULT hr;
if (!This) return E_OUTOFMEMORY;
This->state = SOURCE_OPENING; + InitializeCriticalSection(&This->streams_cs); + This->init_complete_event = CreateEventA(NULL, TRUE, FALSE, NULL); + + /* Setup interface early as the streams interact with us during initialization */ + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + This->ref = 1; + + if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) + { + goto fail; + }
if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) goto fail;
- This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; - This->ref = 1; + /* create demuxer */ + + This->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); + gst_pad_set_element_private(This->my_src, This); + gst_pad_set_getrange_function(This->my_src, pull_from_bytestream_wrapper); + gst_pad_set_query_function(This->my_src, query_bytestream_wrapper); + gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); + gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper); + + This->demuxer = gst_element_factory_make(demuxer_name, NULL); + if (!(This->demuxer)) + { + WARN("Failed to create demuxer for source\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + This->their_sink = gst_element_get_static_pad(This->demuxer, "sink"); + + if ((ret = gst_pad_link(This->my_src, This->their_sink)) < 0) + { + WARN("Failed to link our bytestream pad to the demuxer input\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + g_signal_connect(This->demuxer, "pad-added", G_CALLBACK(source_stream_added_wrapper), This); + g_signal_connect(This->demuxer, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), This); + g_signal_connect(This->demuxer, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), This); + + gst_element_set_state(This->demuxer, GST_STATE_PLAYING); + ret = gst_element_get_state(This->demuxer, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + WaitForSingleObject(This->init_complete_event, INFINITE); + CloseHandle(This->init_complete_event); + This->init_complete_event = NULL; + + gst_element_set_state(This->demuxer, GST_STATE_READY); + if (!(This->pres_desc)) + { + hr = E_FAIL; + goto fail; + } + + This->state = SOURCE_STOPPED;
*out_media_source = This; return S_OK; @@ -612,6 +1510,9 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha { TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
+ if (!(init_gstreamer())) + return E_FAIL; + if (flags & MF_RESOLUTION_MEDIASOURCE) { HRESULT hr; @@ -715,4 +1616,72 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char * IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr; +} + +/* helper for callback forwarding */ +void perform_cb_media_source(struct cb_data *cbdata) +{ + switch(cbdata->type) + { + case PULL_FROM_BYTESTREAM: + { + struct getrange_data *data = &cbdata->u.getrange_data; + cbdata->u.getrange_data.ret = pull_from_bytestream(data->pad, data->parent, + data->ofs, data->len, data->buf); + break; + } + case QUERY_BYTESTREAM: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_bytestream(data->pad, data->parent, data->query); + break; + } + case ACTIVATE_BYTESTREAM_PAD_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_bytestream_pad_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case PROCESS_BYTESTREAM_PAD_EVENT: + { + struct event_src_data *data = &cbdata->u.event_src_data; + cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event); + break; + } + case SOURCE_STREAM_ADDED: + { + struct pad_added_data *data = &cbdata->u.pad_added_data; + source_stream_added(data->element, data->pad, data->user); + break; + } + case SOURCE_STREAM_REMOVED: + { + struct pad_removed_data *data = &cbdata->u.pad_removed_data; + source_stream_removed(data->element, data->pad, data->user); + break; + } + case SOURCE_ALL_STREAMS: + { + struct no_more_pads_data *data = &cbdata->u.no_more_pads_data; + source_all_streams(data->element, data->user); + break; + } + case STREAM_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user); + break; + } + case STREAM_EOS: + { + struct eos_data *data = &cbdata->u.eos_data; + stream_eos(data->appsink, data->user); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } } \ No newline at end of file diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 2233bbe159..0a25cad48f 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -16,6 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "config.h" +#include <gst/gst.h> + +#include "gst_private.h" + #include <stdarg.h>
#define COBJMACROS @@ -442,3 +447,102 @@ HRESULT mfplat_can_unload_now(void) { return !object_locks ? S_OK : S_FALSE; } + +/* caps must me writable*/ +IMFMediaType* mfplat_media_type_from_caps(GstCaps *caps) +{ + IMFMediaType *media_type; + GstStructure *info; + const char *media_type_name; + gchar *human_readable; + + if (FAILED(MFCreateMediaType(&media_type))) + { + return NULL; + } + + info = gst_caps_get_structure(caps, 0); + media_type_name = gst_structure_get_name(info); + + human_readable = gst_structure_to_string(info); + TRACE("caps = %s\n", human_readable); + g_free(human_readable); + + if (!(strncmp(media_type_name, "video", 5))) + { + const char *video_format = media_type_name + 6; + gint width, height, framerate_num, framerate_dem; + + if (gst_structure_get_int(info, "width", &width) && gst_structure_get_int(info, "height", &height)) + { + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)width << 32) | height); + } + if (gst_structure_get_fraction(info, "framerate", &framerate_num, &framerate_dem)) + { + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)framerate_num << 32) | framerate_dem); + } + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + if (!(strcmp(video_format, "x-h264"))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + } + else if (!(strcmp(video_format, "mpeg"))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_M4S2); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + } + else + ERR("Unrecognized video format %s\n", video_format); + } + else if (!(strncmp(media_type_name, "audio", 5))) + { + const char *audio_format = media_type_name + 6; + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + if (!(strcmp(audio_format, "mpeg"))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_MPEG); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + } + else + ERR("Unrecognized audio format %s\n", audio_format); + } + else + { + return NULL; + } + + return media_type; +} + +IMFSample* mf_sample_from_gst_sample(GstSample *in) +{ + GstBuffer *gst_buffer; + gsize buf_size; + BYTE *buf_data; + LONGLONG duration, time; + IMFMediaBuffer *mf_buffer; + IMFSample *out = NULL; + + gst_buffer = gst_sample_get_buffer(in); + + buf_size = gst_buffer_get_size(gst_buffer); + + duration = GST_BUFFER_DURATION(gst_buffer); + time = GST_BUFFER_DTS(gst_buffer); + + MFCreateMemoryBuffer(buf_size, &mf_buffer); + IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL); + gst_buffer_extract(gst_buffer, 0, buf_data, buf_size); + IMFMediaBuffer_Unlock(mf_buffer); + + MFCreateSample(&out); + IMFSample_AddBuffer(out, mf_buffer); + IMFMediaBuffer_Release(mf_buffer); + IMFSample_SetSampleDuration(out, duration / 100); + IMFSample_SetSampleTime(out, time / 100); + + return out; +}
On 2/11/20 1:32 AM, Derek Lesho wrote:
+static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type,
HRESULT hr, const PROPVARIANT *value)
+{
- struct media_stream *This = impl_from_IMFMediaStream(iface);
- TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value);
- if (This->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
- return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value);
+}
Same pattern as with sources, this should simply forward to event queue method.
+static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{
- struct media_stream *This = impl_from_IMFMediaStream(iface);
- struct sample_request *req;
- TRACE("(%p)->(%p)\n", iface, token);
- if (This->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
- if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED)
- {
WARN("Stream isn't active\n");
return MF_E_MEDIA_SOURCE_WRONGSTATE;
- }
- if (This->eos && !This->pending_samples)
return MF_E_END_OF_STREAM;
- req = heap_alloc(sizeof(*req));
- if (token)
IUnknown_AddRef(token);
- req->token = token;
- list_add_tail(&This->sample_requests, &req->entry);
- stream_dispatch_samples(This);
- return S_OK;
+}
This should be protected with critical section as well.
+static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user) +{
- struct media_stream *This = (struct media_stream *) user;
- TRACE("(%p) got sample\n", This);
- if (This->state == STREAM_INACTIVE)
- {
ERR("got sample on inactive stream\n");
- }
- This->pending_samples++;
- stream_dispatch_samples(This);
- return GST_FLOW_OK;
+}
Does it matter how many samples sink had accumulated so far? As I understand it's meant to hold fixed number of them and ask to decode more when you start pull them out. So basically when iterating through requests list, sink will simply fail to return any new samples, and that's how you could stop iterating it.
/* Get the sample from the appsink, then construct an IMFSample */
/* We do this in the dispatch function so we can have appsink buffer for us */
{
GstSample *gst_sample;
TRACE("Trying to pull sample\n");
g_signal_emit_by_name (This->appsink, "pull-sample", &gst_sample);
if (!gst_sample)
{
ERR("Appsink has no samples and pending_samples != 0\n");
break;
}
sample = mf_sample_from_gst_sample(gst_sample);
}
if (req->token)
{
IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token);
}
IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
if (req->token)
{
IUnknown_Release(req->token);
}
list_remove(&req->entry);
This->pending_samples--;
It looks like this leaks samples, event will hold a reference. It also leaks 'req' structure.
+IMFSample* mf_sample_from_gst_sample(GstSample *in) +{
- GstBuffer *gst_buffer;
- gsize buf_size;
- BYTE *buf_data;
- LONGLONG duration, time;
- IMFMediaBuffer *mf_buffer;
- IMFSample *out = NULL;
- gst_buffer = gst_sample_get_buffer(in);
- buf_size = gst_buffer_get_size(gst_buffer);
- duration = GST_BUFFER_DURATION(gst_buffer);
- time = GST_BUFFER_DTS(gst_buffer);
- MFCreateMemoryBuffer(buf_size, &mf_buffer);
- IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL);
- gst_buffer_extract(gst_buffer, 0, buf_data, buf_size);
- IMFMediaBuffer_Unlock(mf_buffer);
- MFCreateSample(&out);
- IMFSample_AddBuffer(out, mf_buffer);
- IMFMediaBuffer_Release(mf_buffer);
- IMFSample_SetSampleDuration(out, duration / 100);
- IMFSample_SetSampleTime(out, time / 100);
- return out;
+}
I think we should consider having a wrapper reusing gstreamer memory, so we don't have to copy.
Another important thing that I forgot to mention. IMFMediaStream and IMFMediaSource relationship creates refcount circular dependency - source will have to hold stream reference, and stream has to hold source reference. At the same time they have to separate enough, and not to share critical sections for example when they don't have to, because there is not reason to block streams with one another, or with the source. It's not a structural flaw in your patch, it's how it should work, however I suspect current implementation will not allow to ever release those objects.
The way to solve this is explicit Shutdown() call for source, which dissociates those objects. Calling it will essentially detach streams from source, making GetMediaSource() fail for example (I don't know if we have tests for that yet), it will also shutdown event queues for streams and the source, which is important for waking event handlers that might wait in GetEvent() for example. Source reader should be calling this method correctly already I think, respecting additional configurable option not to shut it down, in case application will want to read from the same source again.
On 2/20/20 2:12 AM, Nikolay Sivov wrote:
Another important thing that I forgot to mention. IMFMediaStream and IMFMediaSource relationship creates refcount circular dependency - source will have to hold stream reference, and stream has to hold source reference. At the same time they have to separate enough, and not to share critical sections for example when they don't have to, because there is not reason to block streams with one another, or with the source. It's not a structural flaw in your patch, it's how it should work, however I suspect current implementation will not allow to ever release those objects.
The way to solve this is explicit Shutdown() call for source, which dissociates those objects. Calling it will essentially detach streams from source, making GetMediaSource() fail for example (I don't know if we have tests for that yet), it will also shutdown event queues for streams and the source, which is important for waking event handlers that might wait in GetEvent() for example. Source reader should be calling this method correctly already I think, respecting additional configurable option not to shut it down, in case application will want to read from the same source again.
I'm aware of this, I already implement Shutdown() by calling media_source_teardown, which performs the actions you describe: https://github.com/Guy1524/wine/blob/mfplat/dlls/winegstreamer/media_source....
Although looking at it again I should probably reorder the code to release the streams first.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mf/mf.rgs | 11 +++++++++++ dlls/mfplat/tests/mfplat.c | 36 ++++++++++------------------------- dlls/winegstreamer/mfplat.c | 9 ++++++++- dlls/winegstreamer/mfplat.idl | 7 +++++++ include/mfidl.idl | 1 + 5 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/dlls/mf/mf.rgs b/dlls/mf/mf.rgs index f127df7632..11fb106b4f 100644 --- a/dlls/mf/mf.rgs +++ b/dlls/mf/mf.rgs @@ -13,6 +13,17 @@ HKLM val '{477ec299-1421-4bdd-971f-7ccb933f21ad}' = s 'File Scheme Handler' } } + NoRemove 'ByteStreamHandlers' + { + '.mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + 'video/mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + } } } } diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index f056bbda2a..e08486bce1 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -432,7 +432,7 @@ static void test_source_resolver(void) WCHAR *filename; GUID guid; PROPVARIANT empty_var; - BOOL selected, do_uninit, skip_source_tests; + BOOL selected, do_uninit; ULONG refcount;
if (!pMFCreateSourceResolver) @@ -513,38 +513,23 @@ static void test_source_resolver(void) ok(hr == S_OK, "Failed to get type handler, hr %#x.\n", hr);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); -todo_wine ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); - if (hr == S_OK) - { - ok (IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); - - hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &type); - ok (hr == S_OK, "Failed to get current media type, hr %#x.\n", hr); - - hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &guid); - ok(hr == S_OK, "Failed to get media sub type, hr %#x.\n", hr); - ok(IsEqualGUID(&guid, &MFVideoFormat_M4S2), "Unexpected sub type %s.\n", debugstr_guid(&guid)); + ok (IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid));
- hr = IMFPresentationDescriptor_SelectStream(descriptor, 0); - ok(hr == S_OK, "Failed to select video stream, hr %#x.\n", hr); + hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &type); + ok (hr == S_OK, "Failed to get current media type, hr %#x.\n", hr);
- IMFMediaType_Release(type); + hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &guid); + ok(hr == S_OK, "Failed to get media sub type, hr %#x.\n", hr); + ok(IsEqualGUID(&guid, &MFVideoFormat_M4S2), "Unexpected sub type %s.\n", debugstr_guid(&guid));
- skip_source_tests = FALSE; - } - else - { - /* skip media source tests to avoid crashing, as the media source is fake */ - skip_source_tests = TRUE; - } + hr = IMFPresentationDescriptor_SelectStream(descriptor, 0); + ok(hr == S_OK, "Failed to select video stream, hr %#x.\n", hr);
+ IMFMediaType_Release(type); IMFMediaTypeHandler_Release(handler); IMFStreamDescriptor_Release(sd);
- if (skip_source_tests) - goto source_tests_end; - empty_var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &empty_var); ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); @@ -631,7 +616,6 @@ todo_wine hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); ok (hr == MF_E_SHUTDOWN, "Got 0x%08x\n", hr);
- source_tests_end: refcount = IMFMediaSource_Release(mediasource); ok(!refcount, "Unexpected refcount %u.\n", refcount); refcount = IMFByteStream_Release(stream); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 0a25cad48f..53aa0bc8f1 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -21,13 +21,14 @@
#include "gst_private.h"
+#include "gst_private.h" + #include <stdarg.h>
#define COBJMACROS #define NONAMELESSUNION
#include "mfapi.h" -#include "mfidl.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -407,6 +408,11 @@ failed: return hr; }
+static HRESULT mp4_stream_handler_create(REFIID riid, void **ret) +{ + return container_stream_handler_construct(riid, ret, "qtdemux"); +} + static const struct class_object { const GUID *clsid; @@ -415,6 +421,7 @@ static const struct class_object class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create }, };
HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) diff --git a/dlls/winegstreamer/mfplat.idl b/dlls/winegstreamer/mfplat.idl index 05a75bdb8e..947b47f435 100644 --- a/dlls/winegstreamer/mfplat.idl +++ b/dlls/winegstreamer/mfplat.idl @@ -24,3 +24,10 @@
] coclass VideoProcessorMFT { } + +[ + helpstring("MP4 Byte Stream Handler"), + threading(both), + uuid(271c3902-6095-4c45-a22f-20091816ee9e) +] +coclass MPEG4ByteStreamHandler { } \ No newline at end of file diff --git a/include/mfidl.idl b/include/mfidl.idl index da43399828..4c94c5d03e 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -910,3 +910,4 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") \ No newline at end of file
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64841
Your paranoid android.
=== wxppro (32 bit report) ===
mfplat: mfplat: Timeout
=== w1064v1809 (32 bit report) ===
mfplat: mfplat.c:2669: Test failed: Unexpected return value 0x102. mfplat.c:1935: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1938: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1941: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1944: Test failed: Unexpected result, hr 0xc00d3e85. 18ec:mfplat: unhandled exception c0000005 at 00404EFB
=== w1064v1809_he (32 bit report) ===
mfplat: mfplat.c:2669: Test failed: Unexpected return value 0x102. mfplat.c:1935: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1938: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1941: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1944: Test failed: Unexpected result, hr 0xc00d3e85. 115c:mfplat: unhandled exception c0000005 at 00404EFB
=== w1064v1809 (64 bit report) ===
mfplat: mfplat.c:2669: Test failed: Unexpected return value 0x102. mfplat.c:1935: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1938: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1941: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1944: Test failed: Unexpected result, hr 0xc00d3e85. 18f4:mfplat: unhandled exception c0000005 at 00000000004045C3
=== debian10 (32 bit report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit French report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit Chinese:China report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (64 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- tools/make_makefiles | 45 +++++++++++++++++++++++++++----------------- tools/makedep.c | 30 ++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 26 deletions(-)
diff --git a/tools/make_makefiles b/tools/make_makefiles index 7c376aca92..b52a12e954 100755 --- a/tools/make_makefiles +++ b/tools/make_makefiles @@ -229,14 +229,14 @@ sub parse_makefile($) { die "Configure substitution is not allowed in $file" unless $file eq "Makefile"; } - if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) + if (/^\s*(MODULE|IMPORTLIB|TESTDLL|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) { my $var = $1; $make{$var} = $2; next; } my $source_vars_regexp = join "|", @source_vars; - if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV)\s*=\s*(.*)/) + if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV|PARENTSRC)\s*=\s*(.*)/) { my $var = $1; my @list = split(/\s+/, $2); @@ -291,19 +291,27 @@ sub get_makedep_flags($) return %flags; }
-sub get_parent_makefile($) +sub get_parent_makefiles($) { my $file = shift; my %make = %{$makefiles{$file}}; - my $reldir = $make{"PARENTSRC"} || ""; - return "" unless $reldir; - (my $path = $file) =~ s//Makefile$///; - while ($reldir =~ /^..//) + my $pointer = $make{"PARENTSRC"} || (); + return () unless $pointer; + my @reldirs = @{$pointer}; + my @makefiles = (); + foreach my $reldir (@reldirs) { - $reldir =~ s/^..///; - $path =~ s/[^/]+/$//; + my $length = @reldirs; + (my $path = $file) =~ s//Makefile$///; + while ($reldir =~ /^..//) + { + $reldir =~ s/^..///; + $path =~ s/[^/]+/$//; + } + push @makefiles, "$path$reldir/Makefile"; } - return "$path$reldir/Makefile"; + + return @makefiles }
# preserve shared source files that are listed in the existing makefile @@ -400,13 +408,16 @@ sub assign_sources_to_makefiles(@) foreach my $file (@makefiles) { my $make = $makefiles{$file}; - my $parent = get_parent_makefile( $file ); - next unless $parent; - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + my @parents = get_parent_makefiles( $file ); + next unless @parents; + foreach my $parent (@parents) + { + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + } } }
diff --git a/tools/makedep.c b/tools/makedep.c index f7261795fc..9037618280 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -182,6 +182,7 @@ struct makefile struct strarray install_lib; struct strarray install_dev; struct strarray extra_targets; + struct strarray parent_dirs; struct list sources; struct list includes; const char *base_dir; @@ -189,7 +190,6 @@ struct makefile const char *obj_dir; const char *top_src_dir; const char *top_obj_dir; - const char *parent_dir; const char *module; const char *testdll; const char *sharedlib; @@ -1373,15 +1373,24 @@ static struct file *open_local_file( const struct makefile *make, const char *pa { char *src_path = root_dir_path( base_dir_path( make, path )); struct file *ret = load_file( src_path ); + unsigned int i;
- /* if not found, try parent dir */ - if (!ret && make->parent_dir) + /* if not found, try parent dirs */ + for (i = 0; !ret && i < make->parent_dirs.count; i++) { + char *new_path; + free( src_path ); - path = strmake( "%s/%s", make->parent_dir, path ); - src_path = root_dir_path( base_dir_path( make, path )); + new_path = strmake( "%s/%s", make->parent_dirs.str[i], path ); + src_path = root_dir_path( base_dir_path( make, new_path )); ret = load_file( src_path ); - if (ret) ret->flags |= FLAG_PARENTDIR; + if (ret) + { + ret->flags |= FLAG_PARENTDIR; + path = new_path; + } + else + free(new_path); }
if (ret) *filename = src_dir_path( make, path ); @@ -4185,13 +4194,13 @@ static void load_sources( struct makefile *make ) strarray_set_value( &make->vars, "top_srcdir", top_src_dir_path( make, "" )); strarray_set_value( &make->vars, "srcdir", src_dir_path( make, "" ));
- make->parent_dir = get_expanded_make_variable( make, "PARENTSRC" ); make->module = get_expanded_make_variable( make, "MODULE" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); make->sharedlib = get_expanded_make_variable( make, "SHAREDLIB" ); make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); make->importlib = get_expanded_make_variable( make, "IMPORTLIB" );
+ make->parent_dirs = get_expanded_make_var_array( make, "PARENTSRC" ); make->programs = get_expanded_make_var_array( make, "PROGRAMS" ); make->scripts = get_expanded_make_var_array( make, "SCRIPTS" ); make->imports = get_expanded_make_var_array( make, "IMPORTS" ); @@ -4236,8 +4245,11 @@ static void load_sources( struct makefile *make ) strarray_add( &make->include_args, strmake( "-I%s", obj_dir_path( make, "" ))); if (make->src_dir) strarray_add( &make->include_args, strmake( "-I%s", make->src_dir )); - if (make->parent_dir) - strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dir ))); + if (make->parent_dirs.count) + { + for (i = 0; i < make->parent_dirs.count; i++) + strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dirs.str[i] ))); + } strarray_add( &make->include_args, strmake( "-I%s", top_obj_dir_path( make, "include" ))); if (make->top_src_dir) strarray_add( &make->include_args, strmake( "-I%s", top_src_dir_path( make, "include" )));
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64842
Your paranoid android.
=== debian10 (32 bit report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit Chinese:China report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (64 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mf/Makefile.in | 1 + dlls/mf/handler.c | 357 ++++++++++++++++++++++++++++ dlls/mf/handler.h | 29 +++ dlls/mf/main.c | 370 ++++-------------------------- dlls/winegstreamer/Makefile.in | 3 +- dlls/winegstreamer/media_source.c | 332 ++------------------------- 6 files changed, 443 insertions(+), 649 deletions(-) create mode 100644 dlls/mf/handler.c create mode 100644 dlls/mf/handler.h
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index d23349c9cf..81872b17a5 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -5,6 +5,7 @@ IMPORTS = mfplat ole32 uuid mfuuid EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ + handler.c \ main.c \ samplegrabber.c \ sar.c \ diff --git a/dlls/mf/handler.c b/dlls/mf/handler.c new file mode 100644 index 0000000000..db8783a7cd --- /dev/null +++ b/dlls/mf/handler.c @@ -0,0 +1,357 @@ +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "mfidl.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "handler.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +struct handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + if (context->url) + heap_free(context->url); + heap_free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +/* Start async methods */ +static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI handler_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; +} + +/* lifetime not managed with COM */ +static ULONG WINAPI handler_callback_AddRef(IMFAsyncCallback *iface) +{ + return 2; +} + +static ULONG WINAPI handler_callback_Release(IMFAsyncCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + struct handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = handler->create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + handler_result = heap_alloc(sizeof(*handler_result)); + if (handler_result) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl handler_callback_vtbl = +{ + handler_callback_QueryInterface, + handler_callback_AddRef, + handler_callback_Release, + handler_callback_GetParameters, + handler_callback_Invoke, +}; + +/* Start handler helpers */ + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (lstrlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state) +{ + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + context = heap_alloc(sizeof(*context)); + if (!context) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = heap_strdupW(url); + if (!context->url && !context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + IMFAsyncResult_Release(caller); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + IMFAsyncResult_GetState(item, cancel_cookie); + } + + IMFAsyncResult_Release(item); + } + + return hr; +} + +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct handler_result *found = NULL, *cur; + HRESULT hr; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + heap_free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie) +{ + struct handler_result *found = NULL, *cur; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback) +{ + handler->IMFAsyncCallback_iface.lpVtbl = &handler_callback_vtbl; + handler->create_object = create_object_callback; + + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); +} + +void handler_destruct(struct handler *handler) +{ + struct handler_result *result, *next; + + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); +} \ No newline at end of file diff --git a/dlls/mf/handler.h b/dlls/mf/handler.h new file mode 100644 index 0000000000..14b110e1a7 --- /dev/null +++ b/dlls/mf/handler.h @@ -0,0 +1,29 @@ +#include "windef.h" + +#include "mfobjects.h" +#include "mfapi.h" + +#include "wine/list.h" + +/* helper sub-object that handles ansyncronous nature of handlers */ + +struct handler; +typedef HRESULT (*p_create_object_callback)(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props, + IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type); + +struct handler +{ + IMFAsyncCallback IMFAsyncCallback_iface; + struct list results; + CRITICAL_SECTION cs; + p_create_object_callback create_object; +}; + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback); +void handler_destruct(struct handler *handler); +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state); +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object); +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie); \ No newline at end of file diff --git a/dlls/mf/main.c b/dlls/mf/main.c index 251d59e1c0..5861560b60 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -35,6 +35,7 @@ #include "mferror.h"
#include "mf_private.h" +#include "handler.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -547,22 +548,12 @@ static const IClassFactoryVtbl class_factory_vtbl = class_factory_LockServer, };
-struct file_scheme_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - struct file_scheme_handler { IMFSchemeHandler IMFSchemeHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; IMFSourceResolver *resolver; - struct list results; - CRITICAL_SECTION cs; + struct handler handler; };
static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) @@ -570,11 +561,6 @@ static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler * return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface); }
-static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface); -} - static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); @@ -604,239 +590,45 @@ static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct file_scheme_handler_result *result, *next; - - TRACE("%p, refcount %u.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - heap_free(result); - } - DeleteCriticalSection(&handler->cs); - if (handler->resolver) - IMFSourceResolver_Release(handler->resolver); - heap_free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %u.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface); + ULONG refcount = InterlockedDecrement(&this->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) { - if (context->props) - IPropertyStore_Release(context->props); - heap_free(context->url); - heap_free(context); + if (this->resolver) + IMFSourceResolver_Release(this->resolver); + handler_destruct(&this->handler); }
return refcount; }
-static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static WCHAR *heap_strdupW(const WCHAR *str) -{ - WCHAR *ret = NULL; - - if (str) - { - unsigned int size; - - size = (lstrlenW(str) + 1) * sizeof(WCHAR); - ret = heap_alloc(size); - if (ret) - memcpy(ret, str, size); - } - - return ret; -} - static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - context = heap_alloc(sizeof(*context)); - if (!context) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->url = heap_strdupW(url); - if (!context->url) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - IMFAsyncResult_Release(caller); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - IMFAsyncResult_GetState(item, cancel_cookie); - } - - IMFAsyncResult_Release(item); - } - - return hr; + return handler_begin_create_object(&this->handler, NULL, url, flags, props, cancel_cookie, callback, state); }
static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, MF_OBJECT_TYPE *obj_type, IUnknown **object) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - *obj_type = found->obj_type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - heap_free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; + return handler_end_create_object(&this->handler, result, obj_type, object); }
static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - heap_free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = @@ -849,38 +641,6 @@ static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = file_scheme_handler_CancelObjectCreation, };
-static HRESULT WINAPI file_scheme_handler_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 file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface); -} - -static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); -} - -static HRESULT WINAPI file_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *handler, IMFSourceResolver **resolver) { HRESULT hr; @@ -902,111 +662,63 @@ static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *hand return S_OK; }
-static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +static HRESULT file_scheme_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) { static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'}; - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - struct file_scheme_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; + HRESULT hr = S_OK; + WCHAR *path; + IMFByteStream *file_byte_stream; + struct file_scheme_handler *this = CONTAINING_RECORD(handler, struct file_scheme_handler, handler); IMFSourceResolver *resolver; - IMFAsyncResult *caller; - IMFByteStream *stream; - const WCHAR *url; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object);
/* Strip from scheme, MFCreateFile() won't be expecting it. */ - url = context->url; - if (!wcsnicmp(context->url, schemeW, ARRAY_SIZE(schemeW))) - url += ARRAY_SIZE(schemeW); + path = url; + if (!wcsnicmp(url, schemeW, ARRAY_SIZE(schemeW))) + path += ARRAY_SIZE(schemeW);
- hr = MFCreateFile(context->flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, - MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, &stream); + hr = MFCreateFile(flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, + MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, path, &file_byte_stream); if (SUCCEEDED(hr)) { - if (context->flags & MF_RESOLUTION_MEDIASOURCE) + if (flags & MF_RESOLUTION_MEDIASOURCE) { - if (SUCCEEDED(hr = file_scheme_handler_get_resolver(handler, &resolver))) + if (SUCCEEDED(hr = file_scheme_handler_get_resolver(this, &resolver))) { - hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, context->url, context->flags, - context->props, &obj_type, &object); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, file_byte_stream, url, flags, + props, out_obj_type, out_object); IMFSourceResolver_Release(resolver); - IMFByteStream_Release(stream); + IMFByteStream_Release(file_byte_stream); } } else { - object = (IUnknown *)stream; - obj_type = MF_OBJECT_BYTESTREAM; + *out_object = (IUnknown *)file_byte_stream; + *out_obj_type = MF_OBJECT_BYTESTREAM; } }
- handler_result = heap_alloc(sizeof(*handler_result)); - if (handler_result) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - handler_result->obj_type = obj_type; - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&context->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; + return hr; }
-static const IMFAsyncCallbackVtbl file_scheme_handler_callback_vtbl = -{ - file_scheme_handler_callback_QueryInterface, - file_scheme_handler_callback_AddRef, - file_scheme_handler_callback_Release, - file_scheme_handler_callback_GetParameters, - file_scheme_handler_callback_Invoke, -}; - static HRESULT file_scheme_handler_construct(REFIID riid, void **obj) { - struct file_scheme_handler *handler; + struct file_scheme_handler *this; HRESULT hr;
TRACE("%s, %p.\n", debugstr_guid(riid), obj);
- handler = heap_alloc_zero(sizeof(*handler)); - if (!handler) + this = heap_alloc_zero(sizeof(*this)); + if (!this) return E_OUTOFMEMORY;
- handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; - handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl; - handler->refcount = 1; - list_init(&handler->results); - InitializeCriticalSection(&handler->cs); + handler_construct(&this->handler, file_scheme_handler_create_object); + + this->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; + this->refcount = 1;
- hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); - IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + hr = IMFSchemeHandler_QueryInterface(&this->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&this->IMFSchemeHandler_iface);
return hr; } diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 84916fe649..b00ce2586e 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -2,13 +2,14 @@ MODULE = winegstreamer.dll IMPORTS = strmiids uuid winmm msacm32 msvfw32 ole32 oleaut32 user32 gdi32 advapi32 mfplat mfuuid EXTRAINCL = $(GSTREAMER_CFLAGS) EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) -PARENTSRC = ../strmbase +PARENTSRC = ../strmbase ../mf
C_SRCS = \ dllfunc.c \ filter.c \ gst_cbs.c \ gstdemux.c \ + handler.c \ main.c \ media_source.c \ mediatype.c \ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 4e48c100b0..fce267ed3b 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -4,6 +4,7 @@
#include "gst_private.h" #include "gst_cbs.h" +#include "handler.h"
#include <stdarg.h>
@@ -1158,22 +1159,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, const char *d
/* IMFByteStreamHandler */
-struct container_stream_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - struct container_stream_handler { IMFByteStreamHandler IMFByteStreamHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; const char *demuxer_name; - struct list results; - CRITICAL_SECTION cs; + struct handler handler; };
static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) @@ -1181,11 +1172,6 @@ static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteSt return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface); }
-static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface); -} - static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); @@ -1215,245 +1201,44 @@ static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface)
static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface) { - struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct container_stream_handler_result *result, *next; - - TRACE("%p, refcount %u.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - heap_free(result); - } - DeleteCriticalSection(&handler->cs); - heap_free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - IMFByteStream *stream; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %u.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&this->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) { - if (context->props) - IPropertyStore_Release(context->props); - if (context->stream) - IMFByteStream_Release(context->stream); - if (context->url) - heap_free(context->url); - heap_free(context); + handler_destruct(&this->handler); + heap_free(this); }
return refcount; }
-static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static WCHAR *heap_strdupW(const WCHAR *str) -{ - WCHAR *ret = NULL; - - if (str) - { - unsigned int size; - - size = (lstrlenW(str) + 1) * sizeof(WCHAR); - ret = heap_alloc(size); - if (ret) - memcpy(ret, str, size); - } - - return ret; -} - static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr;
TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - context = heap_alloc(sizeof(*context)); - if (!context) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->stream = stream; - if (context->stream) - IMFByteStream_AddRef(context->stream); - if (url) - context->url = heap_strdupW(url); - if (!context->stream) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - IMFAsyncResult_Release(caller); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - IMFAsyncResult_GetState(item, cancel_cookie); - } - - IMFAsyncResult_Release(item); - } - - return hr; + return handler_begin_create_object(&this->handler, stream, url, flags, props, cancel_cookie, callback, state); }
static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, MF_OBJECT_TYPE *obj_type, IUnknown **object) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct container_stream_handler_result *found = NULL, *cur; - HRESULT hr;
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - *obj_type = found->obj_type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - heap_free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; + return handler_end_create_object(&this->handler, result, obj_type, object); }
static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct container_stream_handler_result *found = NULL, *cur;
TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - heap_free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) @@ -1473,42 +1258,10 @@ static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = container_stream_handler_GetMaxNumberOfBytesRequiredForResolution, };
-static HRESULT WINAPI container_stream_handler_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 container_stream_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); -} - -static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); -} - -static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - -static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, +static HRESULT container_stream_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) { - TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + TRACE("(%p %s %p %u %p %p %p)\n", handler, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
if (!(init_gstreamer())) return E_FAIL; @@ -1517,6 +1270,7 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha { HRESULT hr; struct media_source *new_source; + struct container_stream_handler *This = CONTAINING_RECORD(handler, struct container_stream_handler, handler);
if (FAILED(hr = media_source_constructor(stream, This->demuxer_name, &new_source))) return hr; @@ -1535,64 +1289,6 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha } }
-static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - struct container_stream_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; - IMFAsyncResult *caller; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object); - - hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); - - handler_result = heap_alloc(sizeof(*handler_result)); - if (handler_result) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - handler_result->obj_type = obj_type; - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&context->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; -} - -static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl = -{ - container_stream_handler_callback_QueryInterface, - container_stream_handler_callback_AddRef, - container_stream_handler_callback_Release, - container_stream_handler_callback_GetParameters, - container_stream_handler_callback_Invoke, -}; - HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char *demuxer_name) { struct container_stream_handler *this; @@ -1604,12 +1300,10 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, const char * if (!this) return E_OUTOFMEMORY;
- list_init(&this->results); - InitializeCriticalSection(&this->cs); + handler_construct(&this->handler, container_stream_handler_create_object);
this->demuxer_name = demuxer_name; this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl; - this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl; this->refcount = 1;
hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64843
Your paranoid android.
=== debian10 (32 bit report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit Chinese:China report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (32 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debian10 (64 bit WoW report) ===
mfplat: mfplat.c:516: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:517: Test failed: Unexpected major type {0062fb38-fb30-0062-0400-000030fb6200}. mfplat.c:520: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on execute access to 0x3f3f3f3f in 32-bit code (0x3f3f3f3f).
Report validation errors: mfplat:mfplat crashed (c0000005)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=64833
Your paranoid android.
=== w1064v1809_ja (32 bit report) ===
mfplat: mfplat.c:2587: Test failed: Unexpected return value 0x102. mfplat.c:1938: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1941: Test failed: Failed to get event, hr 0xc00d3e85. mfplat.c:1944: Test failed: Failed to finalize GetEvent, hr 0xc00d3e85. mfplat.c:1947: Test failed: Unexpected result, hr 0xc00d3e85. 14e4:mfplat: unhandled exception c0000005 at 00404EFB