Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v1: v5 of 528e687 was meant to revert the change from gst_pad_query_caps to gst_pad_get_current_caps at zebediah's suggestion, however, I somehow managed to resend a duplicate of v4 instead. This resolves that. --- dlls/winegstreamer/media_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0991710755c..d6c7837e544 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -388,7 +388,7 @@ static const IMFMediaStreamVtbl media_stream_vtbl = 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_get_current_caps(stream->their_src); + GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL); const gchar *stream_type;
if (!source_caps)
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v7: Resolves mfreadwrite tests. --- dlls/mf/tests/mf.c | 1 - dlls/mfplat/tests/mfplat.c | 10 +++++----- dlls/mfreadwrite/tests/mfplat.c | 15 ++++++++++----- dlls/winegstreamer/media_source.c | 28 ++++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index b110c0ec9ef..be8241a51d7 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -1452,7 +1452,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 971469ec574..6a1a5bafc1a 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -574,10 +574,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); @@ -597,6 +594,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);
@@ -605,7 +603,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); @@ -668,11 +669,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/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 84581f9be9a..cfe68cb6736 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -627,14 +627,13 @@ static void test_source_reader(void) stream = get_resource_stream("test.wav");
hr = MFCreateSourceReaderFromByteStream(stream, NULL, &reader); -todo_wine - ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr); - if (FAILED(hr)) { + skip("MFCreateSourceReaderFromByteStream() failed, is G-Streamer missing?\n"); IMFByteStream_Release(stream); return; } + ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr);
/* Access underlying media source object. */ hr = IMFSourceReader_GetServiceForStream(reader, MF_SOURCE_READER_MEDIASOURCE, &GUID_NULL, &IID_IMFMediaSource, @@ -723,7 +722,10 @@ todo_wine
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); +todo_wine ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); + if (hr != S_OK) + goto skip_read_sample; ok(actual_index == 0, "Unexpected stream index %u\n", actual_index); ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); IMFSample_Release(sample); @@ -784,6 +786,8 @@ todo_wine ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags); ok(!sample, "Unexpected sample object.\n");
+skip_read_sample: + /* Flush. */ hr = IMFSourceReader_Flush(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM); ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr); @@ -814,10 +818,11 @@ todo_wine IMFSourceReaderCallback_Release(&callback->IMFSourceReaderCallback_iface);
hr = MFCreateSourceReaderFromByteStream(stream, attributes, &reader); +todo_wine ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr); IMFAttributes_Release(attributes); - - IMFSourceReader_Release(reader); + if (hr == S_OK) + IMFSourceReader_Release(reader);
IMFByteStream_Release(stream); } diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index d6c7837e544..9b2c2174318 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; @@ -673,12 +674,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, @@ -741,6 +742,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) @@ -841,6 +844,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; @@ -928,6 +932,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; @@ -936,6 +959,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
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfreadwrite/reader.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 96a82b798ab..7dcae5a1cfa 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -439,7 +439,8 @@ static HRESULT source_reader_new_stream_handler(struct source_reader *reader, IM }
if (reader->streams[i].requests) - source_reader_request_sample(reader, &reader->streams[i]); + if (FAILED(source_reader_request_sample(reader, &reader->streams[i]))) + WakeAllConditionVariable(&reader->sample_event); } break; } @@ -1780,10 +1781,17 @@ static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD ind stream->requests++; if (FAILED(hr = source_reader_request_sample(reader, stream))) WARN("Failed to request a sample, hr %#x.\n", hr); + if (stream->stream && !(stream->flags & STREAM_FLAG_SAMPLE_REQUESTED)) + { + *actual_index = index; + *stream_flags = MF_SOURCE_READERF_ERROR; + *timestamp = 0; + break; + } SleepConditionVariableCS(&reader->sample_event, &reader->cs, INFINITE); } - - source_reader_get_read_result(reader, stream, flags, &hr, actual_index, stream_flags, + if (SUCCEEDED(hr)) + source_reader_get_read_result(reader, stream, flags, &hr, actual_index, stream_flags, timestamp, sample); } }
Is there any evidence that it works this way?
On 10/27/20 11:53 AM, Nikolay Sivov wrote:
Is there any evidence that it works this way?
No, but I doubt that any application relies on ReadSample blocking forever when RequestSample fails. The only reason I put it in was so the mfreadwrite tests don't fail on patch 4 before path 5. Yes, I could merge the patches but they are already fairly large and implement different functionality.
On 10/27/20 8:20 PM, Derek Lesho wrote:
On 10/27/20 11:53 AM, Nikolay Sivov wrote:
Is there any evidence that it works this way?
No, but I doubt that any application relies on ReadSample blocking forever when RequestSample fails. The only reason I put it in was so the mfreadwrite tests don't fail on patch 4 before path 5. Yes, I could merge the patches but they are already fairly large and implement different functionality.
If you want to change this you'll have to show with a dummy source that it does happen. If you want to skip tests differently, simply return failure from Start() in earlier patch, then add RequestSample(), then fix return value for Start(), if there is no other way.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v7: Fixed Gstreamer warning stemming from no longer setting initial caps during stream setup. --- dlls/mfplat/tests/mfplat.c | 8 +- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 307 +++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 130 +++++++++++++ 4 files changed, 438 insertions(+), 8 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 6a1a5bafc1a..a080035e477 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -603,10 +603,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); @@ -624,10 +621,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) { @@ -665,11 +665,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 57d40d19a89..3a8020b6760 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -79,6 +79,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 9b2c2174318..9c6e7bfd539 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,260 @@ 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 (!prev_caps || !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); + if (prev_caps) + gst_caps_unref(prev_caps); + IMFMediaType_Release(current_mt); + IMFMediaTypeHandler_Release(mth); + } + + g_object_set(stream->appsink, "drop", !selected, NULL); + + 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); + + 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); @@ -683,16 +961,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) @@ -773,6 +1065,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; }
@@ -853,6 +1148,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); @@ -861,6 +1157,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..46bbb2ac2b1 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -601,3 +601,133 @@ 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; + 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))) + { + /* Darksiders: Warmastered Edition uses a MF_MT_FRAME_RATE of 0, + and gstreamer won't accept an undefined number as the framerate. */ + if (!(DWORD32)frame_rate) + frame_rate = 1; + 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; +}
Signed-off-by: Zebediah Figura z.figura12@gmail.com
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- v7: Updated mfreadwrite tests. --- dlls/mfplat/tests/mfplat.c | 4 -- dlls/mfreadwrite/tests/mfplat.c | 4 +- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 92 ++++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 69 +++++++++++++++++++++++ 5 files changed, 164 insertions(+), 6 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index a080035e477..8f9fb3faff3 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -621,13 +621,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) { @@ -668,7 +665,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/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index cfe68cb6736..a44f7ca55d0 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -722,7 +722,6 @@ static void test_source_reader(void)
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); -todo_wine ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); if (hr != S_OK) goto skip_read_sample; @@ -748,8 +747,11 @@ todo_wine ×tamp, &sample); ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); ok(actual_index == 0, "Unexpected stream index %u\n", actual_index); +todo_wine +{ ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags); ok(!sample, "Unexpected sample object.\n"); +}
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN, &actual_index, &stream_flags, ×tamp, &sample); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 3a8020b6760..28e424439d8 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -80,6 +80,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 9c6e7bfd539..ef694bf194a 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; };
@@ -311,6 +318,8 @@ static void start_pipeline(struct media_source *source, struct source_async_comm GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
gst_pad_push_event(stream->my_sink, seek_event); + + stream->eos = FALSE; }
if (selected) @@ -334,6 +343,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); @@ -353,6 +415,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); @@ -640,13 +705,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 = @@ -729,6 +818,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 46bbb2ac2b1..908f3d83ef9 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -731,3 +731,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=81068
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
Signed-off-by: Zebediah Figura z.figura12@gmail.com
This one fails for me for mfplat:
(wine:2937130): GStreamer-CRITICAL **: 18:23:01.083: gst_value_collect_fraction: assertion 'collect_values[1].v_int != 0' failed
(wine:2937130): GStreamer-CRITICAL **: 18:23:01.083: passed '0' as denominator for `GstFraction' 0120:err:mfplat:bus_watch multiqueue0: Internal data stream error. 0120:err:mfplat:bus_watch ../plugins/elements/gstmultiqueue.c(2670): gst_multi_queue_sink_event (): /GstBin:bin0/GstDecodeBin:decodebin0/GstMultiQueue:multiqueue0: streaming stopped, reason not-negotiated (-4)
and for mfreadwrite:
mfplat.c:765: Test failed: Unexpected stream flags 0. mfplat.c:779: Test failed: Unexpected stream flags 0. mfplat.c:786: Test failed: Unexpected stream flags 0. mfplat.c:793: Test failed: Unexpected stream flags 0.
or sometimes like this
mfplat.c:765: Test failed: Unexpected stream flags 0.
Gstreamer assertion already happens with patch 4/5.