Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/media_source.c | 124 ++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 14 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5f3c43a0204..cbee412030d 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -384,6 +384,43 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample };
+/* Setup a chain of elements which should hopefully allow transformations to any IMFMediaType + the user throws at us through gstreamer's caps negotiation. */ +static HRESULT media_stream_connect_to_sink(struct media_stream *stream) +{ + GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL); + const gchar *stream_type; + + if (!source_caps) + return E_FAIL; + + stream_type = gst_structure_get_name(gst_caps_get_structure(source_caps, 0)); + gst_caps_unref(source_caps); + + if (!strcmp(stream_type, "video/x-raw")) + { + GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL); + + gst_bin_add(GST_BIN(stream->parent_source->container), videoconvert); + + stream->my_sink = gst_element_get_static_pad(videoconvert, "sink"); + + if (!gst_element_link(videoconvert, stream->appsink)) + return E_FAIL; + + gst_element_sync_state_with_parent(videoconvert); + } + else + { + stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink"); + } + + if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK) + return E_FAIL; + + return S_OK; +} + static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) { struct media_stream *object = heap_alloc_zero(sizeof(*object)); @@ -414,8 +451,8 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD g_object_set(object->appsink, "sync", FALSE, NULL); g_object_set(object->appsink, "max-buffers", 5, NULL);
- object->my_sink = gst_element_get_static_pad(object->appsink, "sink"); - gst_pad_link(object->their_src, object->my_sink); + if (FAILED(hr = media_stream_connect_to_sink(object))) + goto fail;
gst_element_sync_state_with_parent(object->appsink);
@@ -435,28 +472,87 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) { GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src); IMFMediaTypeHandler *type_handler; + IMFMediaType **stream_types = NULL; IMFMediaType *stream_type = NULL; + DWORD type_count = 0; + const gchar *major_type; + unsigned int i; HRESULT hr;
- stream_type = mf_media_type_from_caps(current_caps); - gst_caps_unref(current_caps); - if (!stream_type) - return E_FAIL; + major_type = gst_structure_get_name(gst_caps_get_structure(current_caps, 0)); + + if (!strcmp(major_type, "video/x-raw")) + { + /* These are the most common native output types of decoders: https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-o... */ + static const GUID * video_types[] = + { + &MFVideoFormat_NV12, + &MFVideoFormat_YV12, + &MFVideoFormat_YUY2, + &MFVideoFormat_IYUV, + &MFVideoFormat_I420, + };
- hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor); + IMFMediaType *base_type = mf_media_type_from_caps(current_caps); + GUID base_subtype;
- IMFMediaType_Release(stream_type); + IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype);
- if (FAILED(hr)) - return hr; + stream_types = heap_alloc( sizeof(IMFMediaType *) * ARRAY_SIZE(video_types) + 1);
- if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) - return hr; + stream_types[0] = base_type; + type_count = 1;
- hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type); + for (i = 0; i < ARRAY_SIZE(video_types); i++) + { + IMFMediaType *new_type;
- IMFMediaTypeHandler_Release(type_handler); + if (IsEqualGUID(&base_subtype, video_types[i])) + continue;
+ if (FAILED(hr = MFCreateMediaType(&new_type))) + goto done; + stream_types[type_count++] = new_type; + + if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i]))) + goto done; + } + } + else + { + stream_type = mf_media_type_from_caps(current_caps); + if (stream_type) + { + stream_types = &stream_type; + type_count = 1; + } + } + + if (!type_count) + { + ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); + return E_FAIL; + } + + if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) + goto done; + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0]))) + goto done; + +done: + gst_caps_unref(current_caps); + if (type_handler) + IMFMediaTypeHandler_Release(type_handler); + for (i = 0; i < type_count; i++) + IMFMediaType_Release(stream_types[i]); + if (stream_types != &stream_type) + heap_free(stream_types); return hr; }
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v3: Address comments. --- dlls/winegstreamer/media_source.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index cbee412030d..f730d43422d 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -410,6 +410,19 @@ static HRESULT media_stream_connect_to_sink(struct media_stream *stream)
gst_element_sync_state_with_parent(videoconvert); } + else if (!strcmp(stream_type, "audio/x-raw")) + { + GstElement *audioconvert = gst_element_factory_make("audioconvert", NULL); + + gst_bin_add(GST_BIN(stream->parent_source->container), audioconvert); + + stream->my_sink = gst_element_get_static_pad(audioconvert, "sink"); + + if (!gst_element_link(audioconvert, stream->appsink)) + return E_FAIL; + + gst_element_sync_state_with_parent(audioconvert); + } else { stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
Signed-off-by: Zebediah Figura z.figura12@gmail.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v3: Address comment. --- dlls/mf/tests/mf.c | 1 - dlls/mfplat/tests/mfplat.c | 10 +++++----- dlls/winegstreamer/media_source.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index a63c353a4b5..333da511a55 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1451,7 +1451,6 @@ todo_wine return;
hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd); -todo_wine ok(hr == S_OK, "Failed to create descriptor, hr %#x.\n", hr); if (FAILED(hr)) return; diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 309f7b669a4..15d5bcba3d6 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -576,10 +576,7 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); -todo_wine ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd); @@ -599,6 +596,7 @@ todo_wine ok(hr == S_OK, "Failed to get current media type, hr %#x.\n", hr); hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &guid); ok(hr == S_OK, "Failed to get media sub type, hr %#x.\n", hr); +todo_wine ok(IsEqualGUID(&guid, &MFVideoFormat_M4S2), "Unexpected sub type %s.\n", debugstr_guid(&guid)); IMFMediaType_Release(media_type);
@@ -607,7 +605,10 @@ todo_wine
var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); +todo_wine ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + if (FAILED(hr)) + goto skip_source_tests;
get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var); ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt); @@ -670,11 +671,10 @@ todo_wine
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
+skip_source_tests: IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor);
-skip_source_tests: - hr = IMFMediaSource_Shutdown(mediasource); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index f730d43422d..de15765d31c 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -67,6 +67,7 @@ struct media_source IMFByteStream *byte_stream; struct media_stream **streams; ULONG stream_count; + IMFPresentationDescriptor *pres_desc; GstBus *bus; GstElement *container; GstElement *decodebin; @@ -672,12 +673,12 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * { struct media_source *source = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", source, descriptor); + TRACE("(%p)->(%p)\n", source, descriptor);
if (source->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + return IMFPresentationDescriptor_Clone(source->pres_desc, descriptor); }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, @@ -740,6 +741,8 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) if (source->their_sink) gst_object_unref(GST_OBJECT(source->their_sink));
+ if (source->pres_desc) + IMFPresentationDescriptor_Release(source->pres_desc); if (source->event_queue) IMFMediaEventQueue_Shutdown(source->event_queue); if (source->byte_stream) @@ -840,6 +843,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ GST_STATIC_PAD_TEMPLATE("mf_src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
struct media_source *object = heap_alloc_zero(sizeof(*object)); + IMFStreamDescriptor **descriptors = NULL; unsigned int i; HRESULT hr; int ret; @@ -927,6 +931,25 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ gst_sample_unref(preroll); }
+ /* init presentation descriptor */ + + descriptors = heap_alloc(object->stream_count * sizeof(IMFStreamDescriptor*)); + for (i = 0; i < object->stream_count; i++) + { + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]); + } + + if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) + goto fail; + + for (i = 0; i < object->stream_count; i++) + { + IMFPresentationDescriptor_SelectStream(object->pres_desc, i); + IMFStreamDescriptor_Release(descriptors[i]); + } + heap_free(descriptors); + descriptors = NULL; + object->state = SOURCE_STOPPED;
*out_media_source = object; @@ -935,6 +958,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ heap_free(descriptors); IMFMediaSource_Release(&object->IMFMediaSource_iface); return hr; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
Derek Lesho dlesho@codeweavers.com writes:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
v3: Address comment.
dlls/mf/tests/mf.c | 1 - dlls/mfplat/tests/mfplat.c | 10 +++++----- dlls/winegstreamer/media_source.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 8 deletions(-)
This breaks the tests here:
tools/runtest -q -P wine -T . -M mfreadwrite.dll -p dlls/mfreadwrite/tests/mfreadwrite_test.exe mfplat && touch dlls/mfreadwrite/tests/mfplat.ok mfplat.c:631: Test succeeded inside todo block: Failed to create source reader, hr 0. mfplat.c:726: Test failed: Failed to get a sample, hr 0x80004001. mfplat.c:727: Test failed: Unexpected stream index 2076652916 mfplat.c:728: Test failed: Unexpected stream flags 0x7b06df1a. wine: Unhandled page fault on read access to 00000000 at address 00402D50 (thread 0024), starting debugger... Unhandled exception: page fault on read access to 0x00000000 in 32-bit code (0x00402d50). Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:00402d50 ESP:0064fd50 EBP:0064fdc8 EFLAGS:00010202( R- -- I - - - ) EAX:00000000 EBX:0064fd8c ECX:0064fd00 EDX:ffffffff ESI:0064fd90 EDI:0064fd9c Stack dump: 0x0064fd50: 00000000 00408599 7b06df1a 0064fd90 0x0064fd60: 0064fd8c 0064fda8 0064fda0 7bc480ea 0x0064fd70: 00214e14 80004001 0064fd98 0064fda0 0x0064fd80: 7ffde000 014f0404 014f0554 7b06df1a 0x0064fd90: 7bc73574 00000001 014f0220 0021fbe0 0x0064fda0: 00000000 00000001 00001188 7ffc2000 Backtrace: =>0 0x00402d50 func_mfplat+0xc70() [Z:\home\julliard\wine\wine\include\mfobjects.h:1179] in mfreadwrite_test (0x0064fdc8) 1 0x00404992 run_test+0xe1(name=<is not available>) [Z:\home\julliard\wine\wine\include\wine\test.h:569] in mfreadwrite_test (0x0064fe28) 2 0x004062d3 main+0x182(argv=<is not available>) [Z:\home\julliard\wine\wine\include\wine\test.h:659] in mfreadwrite_test (0x0064fed8) 3 0x00405706 mainCRTStartup+0x75() [Z:\home\julliard\wine\wine\dlls\msvcrt\crt_main.c:62] in mfreadwrite_test (0x0064ff28) 4 0x7b62c78e BaseThreadInitThunk+0xd(unknown=<is not available>, entry=<is not available>) [Z:\home\julliard\wine\wine\dlls\kernel32\thread.c:60] in kernel32 (0x0064ff48) 5 0x7bc55027 RtlSleepConditionVariableSRW+0x196(lock=<is not available>, timeout=<is not available>, flags=<is not available>) [Z:\home\julliard\wine\wine\dlls\ntdll\sync.c:556] in ntdll (0x0064ff5c) 6 0x7bc55250 call_thread_func+0xaf(arg=0x7ffde000) [Z:\home\julliard\wine\wine\dlls\ntdll\thread.c:134] in ntdll (0x0064ffec) 0x00402d50 func_mfplat+0xc70 [Z:\home\julliard\wine\wine\include\mfobjects.h:1179] in mfreadwrite_test: movl 0x0(%eax),%edx 1179 return This->lpVtbl->Release(This);
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v3: Address comments, which include: - Reorganizing handling of uncompressed formats in IMFMediaType->GstCaps conversion. - Remove initial caching of stream type, and instead always reconfigure upon the first media type we receive from the application. - Don't wait after pushing a seek, gstreamer handles this. --- dlls/mfplat/tests/mfplat.c | 8 +- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 305 +++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 125 ++++++++++++ 4 files changed, 431 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 15d5bcba3d6..bffce2bc114 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -605,10 +605,7 @@ todo_wine
var.vt = VT_EMPTY; hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); -todo_wine ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests;
get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var); ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt); @@ -626,10 +623,13 @@ todo_wine hr = IMFMediaStream_RequestSample(video_stream, NULL); if (i == sample_count) break; +todo_wine ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr); if (hr != S_OK) break; } + if (FAILED(hr)) + goto skip_source_tests;
for (i = 0; i < sample_count; ++i) { @@ -667,11 +667,11 @@ todo_wine
hr = IMFMediaStream_RequestSample(video_stream, NULL); ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr); - IMFMediaStream_Release(video_stream);
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
skip_source_tests: + IMFMediaStream_Release(video_stream); IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor);
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 60b38a48f5a..07556802a51 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN; IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN; +GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index de15765d31c..52c52fc64ac 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -55,14 +55,38 @@ struct media_stream { STREAM_INACTIVE, STREAM_SHUTDOWN, + STREAM_RUNNING, } state; DWORD stream_id; };
+enum source_async_op +{ + SOURCE_ASYNC_START, +}; + +struct source_async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + enum source_async_op op; + union + { + struct + { + IMFPresentationDescriptor *descriptor; + GUID format; + PROPVARIANT position; + } start; + } u; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; + IMFAsyncCallback async_commands_callback; LONG ref; + DWORD async_commands_queue; IMFMediaEventQueue *event_queue; IMFByteStream *byte_stream; struct media_stream **streams; @@ -76,6 +100,7 @@ struct media_source { SOURCE_OPENING, SOURCE_STOPPED, + SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; HANDLE no_more_pads_event; @@ -91,7 +116,258 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); }
-static GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len, +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, async_commands_callback); +} + +static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface); +} + +static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI source_async_command_AddRef(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI source_async_command_Release(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + { + if (command->op == SOURCE_ASYNC_START) + PropVariantClear(&command->u.start.position); + heap_free(command); + } + + return refcount; +} + +static const IUnknownVtbl source_async_command_vtbl = +{ + source_async_command_QueryInterface, + source_async_command_AddRef, + source_async_command_Release, +}; + +static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret) +{ + struct source_async_command *command; + + if (!(command = heap_alloc_zero(sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; + command->op = op; + + *ret = command; + + return S_OK; +} + +static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), 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 HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) +{ + ULONG sd_count; + IMFStreamDescriptor *ret; + unsigned int i; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count))) + return NULL; + + for (i = 0; i < sd_count; i++) + { + DWORD stream_id; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret))) + return NULL; + + if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id) + return ret; + + IMFStreamDescriptor_Release(ret); + } + return NULL; +} + +static void start_pipeline(struct media_source *source, struct source_async_command *command) +{ + PROPVARIANT *position = &command->u.start.position; + BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; + GstStateChangeReturn ret; + unsigned int i; + + gst_element_set_state(source->container, GST_STATE_PAUSED); + ret = gst_element_get_state(source->container, NULL, NULL, -1); + assert(ret == GST_STATE_CHANGE_SUCCESS); + + /* seek to beginning on stop->play */ + if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) + { + position->vt = VT_I8; + position->u.hVal.QuadPart = 0; + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream; + IMFStreamDescriptor *sd; + IMFMediaTypeHandler *mth; + IMFMediaType *current_mt; + GstCaps *current_caps; + GstCaps *prev_caps; + DWORD stream_id; + BOOL was_active; + BOOL selected; + + stream = source->streams[i]; + + IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); + + sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected); + IMFStreamDescriptor_Release(sd); + + was_active = stream->state != STREAM_INACTIVE; + + stream->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + + if (selected) + { + IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth); + IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt); + current_caps = caps_from_mf_media_type(current_mt); + g_object_get(stream->appsink, "caps", &prev_caps, NULL); + if (!gst_caps_is_equal(prev_caps, current_caps)) + { + GstEvent *reconfigure_event = gst_event_new_reconfigure(); + g_object_set(stream->appsink, "caps", current_caps, NULL); + gst_pad_push_event(gst_element_get_static_pad(stream->appsink, "sink"), reconfigure_event); + } + + gst_caps_unref(current_caps); + gst_caps_unref(prev_caps); + IMFMediaType_Release(current_mt); + IMFMediaTypeHandler_Release(mth); + } + + if (position->vt != VT_EMPTY) + { + GstEvent *seek_event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0); + GstSample *preroll; + + gst_pad_push_event(stream->my_sink, seek_event); + } + + if (selected) + { + TRACE("Stream %u (%p) selected\n", i, stream); + IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &stream->IMFMediaStream_iface); + + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, + seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position); + } + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + seek_message ? MESourceSeeked : MESourceStarted, + &GUID_NULL, S_OK, position); + + source->state = SOURCE_RUNNING; + + gst_element_set_state(source->container, GST_STATE_PLAYING); +} + +static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + struct source_async_command *command; + IUnknown *state; + HRESULT hr; + + if (source->state == SOURCE_SHUTDOWN) + return S_OK; + + if (FAILED(hr = IMFAsyncResult_GetState(result, &state))) + return hr; + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case SOURCE_ASYNC_START: + start_pipeline(source, command); + break; + } + + IUnknown_Release(state); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = +{ + callback_QueryInterface, + source_async_commands_callback_AddRef, + source_async_commands_callback_Release, + callback_GetParameters, + source_async_commands_Invoke, +}; + +GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) { struct media_source *source = gst_pad_get_element_private(pad); @@ -682,16 +958,30 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource * }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, - const GUID *time_format, const PROPVARIANT *start_position) + const GUID *time_format, const PROPVARIANT *position) { struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr;
- FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position); + TRACE("(%p)->(%p, %p, %p)\n", source, descriptor, time_format, position);
if (source->state == SOURCE_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + if (!(IsEqualIID(time_format, &GUID_NULL))) + return MF_E_UNSUPPORTED_TIME_FORMAT; + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command))) + { + command->u.start.descriptor = descriptor; + command->u.start.format = *time_format; + PropVariantCopy(&command->u.start.position, position); + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + } + + return hr; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -772,6 +1062,9 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) if (source->no_more_pads_event) CloseHandle(source->no_more_pads_event);
+ if (source->async_commands_queue) + MFUnlockWorkQueue(source->async_commands_queue); + return S_OK; }
@@ -852,6 +1145,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ return E_OUTOFMEMORY;
object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; object->ref = 1; object->byte_stream = bytestream; IMFByteStream_AddRef(bytestream); @@ -860,6 +1154,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail;
+ if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) + goto fail; + object->container = gst_bin_new(NULL); object->bus = gst_bus_new(); gst_bus_set_sync_handler(object->bus, mf_src_bus_watch_wrapper, object, NULL); diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 2e8b0978648..43895c2cb5d 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -601,3 +601,128 @@ IMFMediaType *mf_media_type_from_caps(const GstCaps *caps)
return media_type; } + +GstCaps *caps_from_mf_media_type(IMFMediaType *type) +{ + GUID major_type; + GUID subtype; + GstCaps *output = NULL; + + if (FAILED(IMFMediaType_GetMajorType(type, &major_type))) + return NULL; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return NULL; + + if (IsEqualGUID(&major_type, &MFMediaType_Video)) + { + UINT64 frame_rate = 0, frame_size = 0; + DWORD width, height; + UINT32 unused; + GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; + GUID subtype_base; + GstVideoInfo info; + unsigned int i; + + if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + return NULL; + width = frame_size >> 32; + height = frame_size; + + output = gst_caps_new_empty_simple("video/x-raw"); + + for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++) + { + if (IsEqualGUID(uncompressed_video_formats[i].subtype, &subtype)) + { + format = uncompressed_video_formats[i].format; + break; + } + } + + subtype_base = subtype; + subtype_base.Data1 = 0; + if (format == GST_VIDEO_FORMAT_UNKNOWN && IsEqualGUID(&MFVideoFormat_Base, &subtype_base)) + format = gst_video_format_from_fourcc(subtype.Data1); + + if (format == GST_VIDEO_FORMAT_UNKNOWN) + { + FIXME("Unrecognized format %s\n", debugstr_guid(&subtype)); + return NULL; + } + + gst_video_info_set_format(&info, format, width, height); + output = gst_video_info_to_caps(&info); + + if (frame_size) + { + gst_caps_set_simple(output, "width", G_TYPE_INT, width, NULL); + gst_caps_set_simple(output, "height", G_TYPE_INT, height, NULL); + } + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate))) + gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, frame_rate >> 32, (DWORD32) frame_rate, NULL); + return output; + } + else if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + { + DWORD rate, channels, channel_mask, bitrate; + + if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) + { + output = gst_caps_new_empty_simple("audio/x-raw"); + + gst_caps_set_simple(output, "format", G_TYPE_STRING, "F32LE", NULL); + gst_caps_set_simple(output, "layout", G_TYPE_STRING, "interleaved", NULL); + } + else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM)) + { + DWORD bits_per_sample; + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample))) + { + char format[6]; + char type; + + type = bits_per_sample > 8 ? 'S' : 'U'; + + output = gst_caps_new_empty_simple("audio/x-raw"); + + sprintf(format, "%c%u%s", type, bits_per_sample, bits_per_sample > 8 ? "LE" : ""); + + gst_caps_set_simple(output, "format", G_TYPE_STRING, format, NULL); + } + else + { + ERR("Bits per sample not set.\n"); + return NULL; + } + } + else + { + FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype)); + return NULL; + } + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate))) + { + gst_caps_set_simple(output, "rate", G_TYPE_INT, rate, NULL); + } + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels))) + { + gst_caps_set_simple(output, "channels", G_TYPE_INT, channels, NULL); + } + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask))) + { + gst_caps_set_simple(output, "channel-mask", GST_TYPE_BITMASK, (guint64) channel_mask, NULL); + } + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AVG_BITRATE, &bitrate))) + { + gst_caps_set_simple(output, "bitrate", G_TYPE_INT, bitrate, NULL); + } + + return output; + } + + FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type)); + return NULL; +}
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=80123
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat.c:3364: Test failed: Unexpected refcount 1. Unhandled exception: page fault on read access to 0x00000009 in 32-bit code (0x667428c5).
=== debiant (64 bit WoW report) ===
mfplat: Unhandled exception: page fault on execute access to 0x0a2e7823 in 32-bit code (0x0a2e7823).
Report validation errors: mfplat:mfplat crashed (c0000005)
GStreamer parts are:
Signed-off-by: Zebediah Figura z.figura12@gmail.com
This one gives me two warnings:
../../wine-git/dlls/winegstreamer/media_source.c: In function ‘start_pipeline’: ../../wine-git/dlls/winegstreamer/media_source.c:309:24: warning: unused variable ‘preroll’ [-Wunused-variable] 309 | GstSample *preroll; | ^~~~~~~ ../../wine-git/dlls/winegstreamer/mfplat.c: In function ‘caps_from_mf_media_type’: ../../wine-git/dlls/winegstreamer/mfplat.c:620:16: warning: unused variable ‘unused’ [-Wunused-variable] 620 | UINT32 unused; | ^~~~~~
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v3: Move introduction of media_stream's eos member to this patch from patch four. --- dlls/mfplat/tests/mfplat.c | 4 -- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 92 ++++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 69 +++++++++++++++++++++++ 4 files changed, 161 insertions(+), 5 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index bffce2bc114..c54113e5588 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -623,13 +623,10 @@ todo_wine hr = IMFMediaStream_RequestSample(video_stream, NULL); if (i == sample_count) break; -todo_wine ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr); if (hr != S_OK) break; } - if (FAILED(hr)) - goto skip_source_tests;
for (i = 0; i < sample_count; ++i) { @@ -670,7 +667,6 @@ todo_wine
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
-skip_source_tests: IMFMediaStream_Release(video_stream); IMFMediaTypeHandler_Release(handler); IMFPresentationDescriptor_Release(descriptor); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 07556802a51..ff5aff42482 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -58,6 +58,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN; IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN; GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN; +IMFSample *mf_sample_from_gst_buffer(GstBuffer *in) DECLSPEC_HIDDEN;
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 52c52fc64ac..3a95f8761d3 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -58,11 +58,13 @@ struct media_stream STREAM_RUNNING, } state; DWORD stream_id; + BOOL eos; };
enum source_async_op { SOURCE_ASYNC_START, + SOURCE_ASYNC_REQUEST_SAMPLE, };
struct source_async_command @@ -78,6 +80,11 @@ struct source_async_command GUID format; PROPVARIANT position; } start; + struct + { + struct media_stream *stream; + IUnknown *token; + } request_sample; } u; };
@@ -309,6 +316,8 @@ static void start_pipeline(struct media_source *source, struct source_async_comm GstSample *preroll;
gst_pad_push_event(stream->my_sink, seek_event); + + stream->eos = FALSE; }
if (selected) @@ -332,6 +341,59 @@ static void start_pipeline(struct media_source *source, struct source_async_comm gst_element_set_state(source->container, GST_STATE_PLAYING); }
+static void dispatch_end_of_presentation(struct media_source *source) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + unsigned int i; + + /* A stream has ended, check whether all have */ + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + + if (stream->state != STREAM_INACTIVE && !stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + +static void wait_on_sample(struct media_stream *stream, IUnknown *token) +{ + PROPVARIANT empty_var = {.vt = VT_EMPTY}; + GstSample *gst_sample; + GstBuffer *buffer; + IMFSample *sample; + + TRACE("%p, %p\n", stream, token); + + g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample); + if (gst_sample) + { + buffer = gst_sample_get_buffer(gst_sample); + + TRACE("PTS = %llu\n", (unsigned long long int) GST_BUFFER_PTS(buffer)); + + sample = mf_sample_from_gst_buffer(buffer); + gst_sample_unref(gst_sample); + + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + + IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + IMFSample_Release(sample); + } + + g_object_get(stream->appsink, "eos", &stream->eos, NULL); + if (stream->eos) + { + if (token) + IUnknown_Release(token); + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); + dispatch_end_of_presentation(stream->parent_source); + } +} + static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); @@ -351,6 +413,9 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA case SOURCE_ASYNC_START: start_pipeline(source, command); break; + case SOURCE_ASYNC_REQUEST_SAMPLE: + wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token); + break; }
IUnknown_Release(state); @@ -638,13 +703,37 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct source_async_command *command; + HRESULT hr;
TRACE("(%p)->(%p)\n", iface, token);
if (stream->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN;
- return E_NOTIMPL; + if (stream->state == STREAM_INACTIVE) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (stream->eos) + { + return MF_E_END_OF_STREAM; + } + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + { + command->u.request_sample.stream = stream; + if (token) + IUnknown_AddRef(token); + command->u.request_sample.token = token; + + /* Once pause support is added, this will need to put into a stream queue, and synchronization will need to be added*/ + hr = MFPutWorkItem(stream->parent_source->async_commands_queue, &stream->parent_source->async_commands_callback, &command->IUnknown_iface); + } + + return hr; }
static const IMFMediaStreamVtbl media_stream_vtbl = @@ -727,6 +816,7 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD object->stream_id = stream_id;
object->state = STREAM_INACTIVE; + object->eos = FALSE;
if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) goto fail; diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 43895c2cb5d..e50fd2e04ab 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -726,3 +726,72 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type) FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type)); return NULL; } + +/* IMFSample = GstBuffer + IMFBuffer = GstMemory */ + +/* TODO: Future optimization could be to create a custom + IMFMediaBuffer wrapper around GstMemory, and to utilize + gst_memory_new_wrapped on IMFMediaBuffer data. However, + this wouldn't work if we allow the callers to allocate + the buffers. */ + +IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer) +{ + IMFMediaBuffer *mf_buffer = NULL; + GstMapInfo map_info = {0}; + LONGLONG duration, time; + BYTE *mapped_buf = NULL; + IMFSample *out = NULL; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(&out))) + goto done; + + duration = GST_BUFFER_DURATION(gst_buffer); + time = GST_BUFFER_PTS(gst_buffer); + + if (FAILED(hr = IMFSample_SetSampleDuration(out, duration / 100))) + goto done; + + if (FAILED(hr = IMFSample_SetSampleTime(out, time / 100))) + goto done; + + if (!gst_buffer_map(gst_buffer, &map_info, GST_MAP_READ)) + { + hr = E_FAIL; + goto done; + } + + if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &mapped_buf, NULL, NULL))) + goto done; + + memcpy(mapped_buf, map_info.data, map_info.size); + + if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size))) + goto done; + + if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer))) + goto done; + +done: + if (mf_buffer) + IMFMediaBuffer_Release(mf_buffer); + if (map_info.data) + gst_buffer_unmap(gst_buffer, &map_info); + if (FAILED(hr)) + { + ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr); + if (out) + IMFSample_Release(out); + out = NULL; + } + + return out; +}
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=80124
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat: Timeout
=== debiant (32 bit French report) ===
mfplat: mfplat: Timeout
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat: Timeout
=== debiant (32 bit Chinese:China report) ===
mfplat: mfplat: Timeout
=== debiant (32 bit WoW report) ===
mfplat: mfplat: Timeout
=== debiant (64 bit WoW report) ===
mfplat: mfplat: Timeout
GStreamer parts are:
Signed-off-by: Zebediah Figura z.figura12@gmail.com
On 10/8/20 4:49 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/media_source.c | 124 ++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 14 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5f3c43a0204..cbee412030d 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -384,6 +384,43 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample };
+/* Setup a chain of elements which should hopefully allow transformations to any IMFMediaType
- the user throws at us through gstreamer's caps negotiation. */
+static HRESULT media_stream_connect_to_sink(struct media_stream *stream) +{
- GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL);
If you're only using the stream type, I think gst_pad_get_current_caps() has less overhead.
- const gchar *stream_type;
- if (!source_caps)
return E_FAIL;
- stream_type = gst_structure_get_name(gst_caps_get_structure(source_caps, 0));
- gst_caps_unref(source_caps);
- if (!strcmp(stream_type, "video/x-raw"))
- {
GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL);
gst_bin_add(GST_BIN(stream->parent_source->container), videoconvert);
stream->my_sink = gst_element_get_static_pad(videoconvert, "sink");
if (!gst_element_link(videoconvert, stream->appsink))
return E_FAIL;
gst_element_sync_state_with_parent(videoconvert);
- }
- else
- {
stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
- }
- if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK)
return E_FAIL;
- return S_OK;
+}
static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) { struct media_stream *object = heap_alloc_zero(sizeof(*object)); @@ -414,8 +451,8 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD g_object_set(object->appsink, "sync", FALSE, NULL); g_object_set(object->appsink, "max-buffers", 5, NULL);
- object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
- gst_pad_link(object->their_src, object->my_sink);
if (FAILED(hr = media_stream_connect_to_sink(object)))
goto fail;
gst_element_sync_state_with_parent(object->appsink);
@@ -435,28 +472,87 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) { GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src); IMFMediaTypeHandler *type_handler;
- IMFMediaType **stream_types = NULL; IMFMediaType *stream_type = NULL;
- DWORD type_count = 0;
- const gchar *major_type;
- unsigned int i; HRESULT hr;
- stream_type = mf_media_type_from_caps(current_caps);
- gst_caps_unref(current_caps);
- if (!stream_type)
return E_FAIL;
- major_type = gst_structure_get_name(gst_caps_get_structure(current_caps, 0));
- if (!strcmp(major_type, "video/x-raw"))
- {
/* These are the most common native output types of decoders: https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */
This line is very long.
static const GUID * video_types[] =
"static const GUID *const video_types[]"
{
&MFVideoFormat_NV12,
&MFVideoFormat_YV12,
&MFVideoFormat_YUY2,
&MFVideoFormat_IYUV,
&MFVideoFormat_I420,
};
- hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor);
IMFMediaType *base_type = mf_media_type_from_caps(current_caps);
GUID base_subtype;
- IMFMediaType_Release(stream_type);
IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype);
- if (FAILED(hr))
return hr;
stream_types = heap_alloc( sizeof(IMFMediaType *) * ARRAY_SIZE(video_types) + 1);
- if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
return hr;
stream_types[0] = base_type;
type_count = 1;
- hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type);
for (i = 0; i < ARRAY_SIZE(video_types); i++)
{
IMFMediaType *new_type;
- IMFMediaTypeHandler_Release(type_handler);
if (IsEqualGUID(&base_subtype, video_types[i]))
continue;
if (FAILED(hr = MFCreateMediaType(&new_type)))
goto done;
stream_types[type_count++] = new_type;
if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type)))
goto done;
if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i])))
goto done;
}
}
else
{
stream_type = mf_media_type_from_caps(current_caps);
if (stream_type)
{
stream_types = &stream_type;
type_count = 1;
}
}
if (!type_count)
{
ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n");
return E_FAIL;
}
if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor)))
goto done;
if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
goto done;
if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0])))
goto done;
+done:
- gst_caps_unref(current_caps);
- if (type_handler)
IMFMediaTypeHandler_Release(type_handler);
- for (i = 0; i < type_count; i++)
IMFMediaType_Release(stream_types[i]);
- if (stream_types != &stream_type)
return hr;heap_free(stream_types);
}
On 10/12/20 11:08 PM, Zebediah Figura wrote:
On 10/8/20 4:49 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/media_source.c | 124 ++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 14 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5f3c43a0204..cbee412030d 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -384,6 +384,43 @@ static const IMFMediaStreamVtbl media_stream_vtbl = media_stream_RequestSample };
+/* Setup a chain of elements which should hopefully allow transformations to any IMFMediaType
- the user throws at us through gstreamer's caps negotiation. */
+static HRESULT media_stream_connect_to_sink(struct media_stream *stream) +{
- GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL);
If you're only using the stream type, I think gst_pad_get_current_caps() has less overhead.
Sorry, I just realized this isn't valid after all; I don't think the caps would be fixed yet.
- const gchar *stream_type;
- if (!source_caps)
return E_FAIL;
- stream_type = gst_structure_get_name(gst_caps_get_structure(source_caps, 0));
- gst_caps_unref(source_caps);
- if (!strcmp(stream_type, "video/x-raw"))
- {
GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL);
gst_bin_add(GST_BIN(stream->parent_source->container), videoconvert);
stream->my_sink = gst_element_get_static_pad(videoconvert, "sink");
if (!gst_element_link(videoconvert, stream->appsink))
return E_FAIL;
gst_element_sync_state_with_parent(videoconvert);
- }
- else
- {
stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
- }
- if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK)
return E_FAIL;
- return S_OK;
+}
static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) { struct media_stream *object = heap_alloc_zero(sizeof(*object)); @@ -414,8 +451,8 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD g_object_set(object->appsink, "sync", FALSE, NULL); g_object_set(object->appsink, "max-buffers", 5, NULL);
- object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
- gst_pad_link(object->their_src, object->my_sink);
if (FAILED(hr = media_stream_connect_to_sink(object)))
goto fail;
gst_element_sync_state_with_parent(object->appsink);
@@ -435,28 +472,87 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) { GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src); IMFMediaTypeHandler *type_handler;
- IMFMediaType **stream_types = NULL; IMFMediaType *stream_type = NULL;
- DWORD type_count = 0;
- const gchar *major_type;
- unsigned int i; HRESULT hr;
- stream_type = mf_media_type_from_caps(current_caps);
- gst_caps_unref(current_caps);
- if (!stream_type)
return E_FAIL;
- major_type = gst_structure_get_name(gst_caps_get_structure(current_caps, 0));
- if (!strcmp(major_type, "video/x-raw"))
- {
/* These are the most common native output types of decoders: https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */
This line is very long.
static const GUID * video_types[] =
"static const GUID *const video_types[]"
{
&MFVideoFormat_NV12,
&MFVideoFormat_YV12,
&MFVideoFormat_YUY2,
&MFVideoFormat_IYUV,
&MFVideoFormat_I420,
};
- hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor);
IMFMediaType *base_type = mf_media_type_from_caps(current_caps);
GUID base_subtype;
- IMFMediaType_Release(stream_type);
IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype);
- if (FAILED(hr))
return hr;
stream_types = heap_alloc( sizeof(IMFMediaType *) * ARRAY_SIZE(video_types) + 1);
- if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
return hr;
stream_types[0] = base_type;
type_count = 1;
- hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type);
for (i = 0; i < ARRAY_SIZE(video_types); i++)
{
IMFMediaType *new_type;
- IMFMediaTypeHandler_Release(type_handler);
if (IsEqualGUID(&base_subtype, video_types[i]))
continue;
if (FAILED(hr = MFCreateMediaType(&new_type)))
goto done;
stream_types[type_count++] = new_type;
if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type)))
goto done;
if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i])))
goto done;
}
}
else
{
stream_type = mf_media_type_from_caps(current_caps);
if (stream_type)
{
stream_types = &stream_type;
type_count = 1;
}
}
if (!type_count)
{
ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n");
return E_FAIL;
}
if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor)))
goto done;
if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
goto done;
if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0])))
goto done;
+done:
- gst_caps_unref(current_caps);
- if (type_handler)
IMFMediaTypeHandler_Release(type_handler);
- for (i = 0; i < type_count; i++)
IMFMediaType_Release(stream_types[i]);
- if (stream_types != &stream_type)
return hr;heap_free(stream_types);
}
On 10/13/20 10:21 AM, Zebediah Figura wrote:
On 10/12/20 11:08 PM, Zebediah Figura wrote:
If you're only using the stream type, I think gst_pad_get_current_caps() has less overhead.
Sorry, I just realized this isn't valid after all; I don't think the caps would be fixed yet.
That's what I thought at first too, but I tested the change and it seemed to work. Sounds like a case where while it happens to work, it's not guaranteed.
Sorry, wrong patch, please ignore.