Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/media_source.c | 113 +++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 16 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5f3c43a0204..520f3c3177e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -384,6 +384,42 @@ 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_resolve(struct media_stream *stream) +{ + GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL); + + if (!source_caps) + return E_FAIL; + + if (!strcmp(gst_structure_get_name(gst_caps_get_structure(source_caps, 0)), "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"); + + assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK); + assert(gst_element_link(videoconvert, stream->appsink)); + + gst_element_sync_state_with_parent(videoconvert); + + g_object_set(stream->appsink, "caps", source_caps, NULL); + } + else + { + stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink"); + g_object_set(stream->appsink, "caps", source_caps, NULL); + assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK); + } + + gst_caps_unref(source_caps); + + 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 +450,7 @@ 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); + media_stream_resolve(object);
gst_element_sync_state_with_parent(object->appsink);
@@ -435,28 +470,74 @@ 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; + unsigned int i; HRESULT hr; + if (!strcmp(gst_structure_get_name(gst_caps_get_structure(current_caps, 0)), "video/x-raw")) + { + GstElementFactory *videoconvert_factory = gst_element_factory_find("videoconvert"); + /* output every format supported by videoconvert */ + const GList *template_list = gst_element_factory_get_static_pad_templates(videoconvert_factory); + for (;template_list; template_list = template_list->next) + { + GstStaticPadTemplate *template = (GstStaticPadTemplate *)template_list->data; + GstCaps *src_caps; + GValueArray *formats; + if (template->direction != GST_PAD_SRC) + continue; + src_caps = gst_static_pad_template_get_caps(template); + gst_structure_get_list(gst_caps_get_structure(src_caps, 0), "format", &formats); + stream_types = heap_alloc( sizeof(IMFMediaType*) * formats->n_values ); + for (i = 0; i < formats->n_values; i++) + { + GValue *format = g_value_array_get_nth(formats, i); + GstCaps *modified_caps = gst_caps_copy(current_caps); + gst_caps_set_value(modified_caps, "format", format); + stream_types[type_count] = mf_media_type_from_caps(modified_caps); + gst_caps_unref(modified_caps); + if (stream_types[type_count]) + type_count++; + } + g_value_array_free(formats); + gst_caps_unref(src_caps); + break; + } + } + else + { + stream_type = mf_media_type_from_caps(current_caps); + if (stream_type) + { + stream_types = &stream_type; + type_count = 1; + } + }
- stream_type = mf_media_type_from_caps(current_caps); gst_caps_unref(current_caps); - if (!stream_type) + if (!type_count) + { + ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); return E_FAIL; + }
- hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor); - - IMFMediaType_Release(stream_type); - - if (FAILED(hr)) - return hr; + if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor))) + goto done;
if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) - return hr; - - hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type); - - IMFMediaTypeHandler_Release(type_handler); - + goto done; + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0]))) + goto done; + +done: + 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 --- dlls/winegstreamer/media_source.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 520f3c3177e..8ad2c6b5ebc 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -408,6 +408,21 @@ static HRESULT media_stream_resolve(struct media_stream *stream)
g_object_set(stream->appsink, "caps", source_caps, NULL); } + else if (!strcmp(gst_structure_get_name(gst_caps_get_structure(source_caps, 0)), "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"); + + assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK); + assert(gst_element_link(audioconvert, stream->appsink)); + + gst_element_sync_state_with_parent(audioconvert); + + g_object_set(stream->appsink, "caps", source_caps, NULL); + } else { stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
On 9/29/20 5:08 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/media_source.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 520f3c3177e..8ad2c6b5ebc 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -408,6 +408,21 @@ static HRESULT media_stream_resolve(struct media_stream *stream)
g_object_set(stream->appsink, "caps", source_caps, NULL); }
- else if (!strcmp(gst_structure_get_name(gst_caps_get_structure(source_caps, 0)), "audio/x-raw"))
I'd recommend assigning a local variable to gst_structure_get_name(...).
- {
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");
assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK);
assert(gst_element_link(audioconvert, stream->appsink));
gst_element_sync_state_with_parent(audioconvert);
g_object_set(stream->appsink, "caps", source_caps, NULL);
- } else { stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mf/tests/mf.c | 1 - dlls/mfplat/tests/mfplat.c | 10 +++++----- dlls/winegstreamer/media_source.c | 29 +++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 2c02b1d8e54..6903e9c10e6 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 8ad2c6b5ebc..9585b0101ba 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; @@ -659,12 +660,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, @@ -727,6 +728,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) @@ -827,6 +830,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; @@ -914,6 +918,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 (unsigned int 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 (unsigned int 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; @@ -922,6 +945,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ if (descriptors) + heap_free(descriptors); IMFMediaSource_Release(&object->IMFMediaSource_iface); return hr; }
On 9/29/20 5:08 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mf/tests/mf.c | 1 - dlls/mfplat/tests/mfplat.c | 10 +++++----- dlls/winegstreamer/media_source.c | 29 +++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 2c02b1d8e54..6903e9c10e6 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 8ad2c6b5ebc..9585b0101ba 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;
@@ -659,12 +660,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, @@ -727,6 +728,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)
if (source->event_queue) IMFMediaEventQueue_Shutdown(source->event_queue); if (source->byte_stream)IMFPresentationDescriptor_Release(source->pres_desc);
@@ -827,6 +830,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;
@@ -914,6 +918,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 (unsigned int i = 0; i < object->stream_count; i++)
Misplaced variable initializer.
- {
IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]);
- }
- if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc)))
goto fail;
- for (unsigned int i = 0; i < object->stream_count; i++)
And here.
{
IMFPresentationDescriptor_SelectStream(object->pres_desc, i);
IMFStreamDescriptor_Release(descriptors[i]);
}
heap_free(descriptors);
descriptors = NULL;
object->state = SOURCE_STOPPED;
*out_media_source = object;
@@ -922,6 +945,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
- if (descriptors)
IMFMediaSource_Release(&object->IMFMediaSource_iface); return hr;heap_free(descriptors);
}
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 149 +++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+)
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/mfplat.c b/dlls/winegstreamer/mfplat.c index 2e8b0978648..9aa17ad00ab 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -601,3 +601,152 @@ 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, framerate_num, framerate_den; + UINT32 unused; + + if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + return NULL; + width = frame_size >> 32; + height = frame_size; + if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate))) + { + frame_rate = TRUE; + framerate_num = 0; + framerate_den = 1; + } + else + { + framerate_num = frame_rate >> 32; + framerate_den = frame_rate; + } + + /* Check if type is uncompressed */ + if (SUCCEEDED(MFCalculateImageSize(&subtype, 100, 100, &unused))) + { + GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; + unsigned int i; + + 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; + } + } + + if (format == GST_VIDEO_FORMAT_UNKNOWN) + { + format = gst_video_format_from_fourcc(subtype.Data1); + } + + if (format == GST_VIDEO_FORMAT_UNKNOWN) + { + FIXME("Unrecognized format %s\n", debugstr_guid(&subtype)); + return NULL; + } + else + { + GstVideoInfo info; + + gst_video_info_set_format(&info, format, width, height); + output = gst_video_info_to_caps(&info); + } + + } + else { + FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype)); + return NULL; + } + + + 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 (frame_rate) + gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, framerate_num, framerate_den, 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; +}
On 9/29/20 5:08 PM, Derek Lesho wrote:
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
Please don't introduce functions before using them, that results in dead code. Instead introduce the caller that uses this function first.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 8 +- dlls/winegstreamer/media_source.c | 316 +++++++++++++++++++++++++++++- 2 files changed, 316 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/media_source.c b/dlls/winegstreamer/media_source.c index 9585b0101ba..581d30ced8f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -55,14 +55,39 @@ struct media_stream { STREAM_INACTIVE, STREAM_SHUTDOWN, + STREAM_RUNNING, } state; DWORD stream_id; + BOOL eos; +}; + +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 +101,7 @@ struct media_source { SOURCE_OPENING, SOURCE_STOPPED, + SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; HANDLE no_more_pads_event; @@ -91,7 +117,266 @@ 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; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count))) + return NULL; + + for (unsigned int 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 HRESULT 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; + + gst_element_set_state(source->container, GST_STATE_PAUSED); + assert(gst_element_get_state(source->container, NULL, NULL, -1) == 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 (unsigned int 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; + BOOL changed_caps; + + 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); + changed_caps = !gst_caps_is_equal(prev_caps, current_caps); + + if (changed_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); + GstMessage *reset_time; + + gst_pad_push_event(stream->my_sink, seek_event); + if ((reset_time = gst_bus_poll(source->bus, GST_MESSAGE_RESET_TIME, -1))) + gst_message_unref(reset_time); + + stream->eos = FALSE; + } + + 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); + gst_element_get_state(source->container, NULL, NULL, -1); + + return S_OK; +} + +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; + default: + ; + } + + 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); @@ -236,6 +521,8 @@ GstBusSyncReply bus_watch(GstBus *bus, GstMessage *message, gpointer user) g_error_free(err); g_free(dbg_info); break; + case GST_MESSAGE_RESET_TIME: + return GST_BUS_PASS; default: break; } @@ -669,16 +956,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) @@ -759,6 +1060,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; }
@@ -839,6 +1143,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); @@ -847,6 +1152,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);
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=79509
Your paranoid android.
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:2698: Test failed: Unexpected counter value 0. Unhandled exception: page fault on execute access to 0x0a2e7823 in 32-bit code (0x0a2e7823).
Report validation errors: mfplat:mfplat crashed (c0000005)
On 9/29/20 5:08 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mfplat/tests/mfplat.c | 8 +- dlls/winegstreamer/media_source.c | 316 +++++++++++++++++++++++++++++- 2 files changed, 316 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/media_source.c b/dlls/winegstreamer/media_source.c index 9585b0101ba..581d30ced8f 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -55,14 +55,39 @@ struct media_stream { STREAM_INACTIVE, STREAM_SHUTDOWN,
} state; DWORD stream_id;STREAM_RUNNING,
- BOOL eos;
+};
+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 +101,7 @@ struct media_source { SOURCE_OPENING, SOURCE_STOPPED,
} state; HANDLE no_more_pads_event;SOURCE_RUNNING, SOURCE_SHUTDOWN,
@@ -91,7 +117,266 @@ 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;
- if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count)))
return NULL;
- for (unsigned int i = 0; i < sd_count; i++)
Misplaced variable initializer.
- {
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 HRESULT 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;
- gst_element_set_state(source->container, GST_STATE_PAUSED);
- assert(gst_element_get_state(source->container, NULL, NULL, -1) == GST_STATE_CHANGE_SUCCESS);
It's bad practice to put code with side-effects inside of assert(); it may be a macro which evaluates to nothing (i.e. if NDEBUG is defined).
- /* 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 (unsigned int i = 0; i < source->stream_count; i++)
Misplaced variable initializer.
- {
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;
BOOL changed_caps;
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);
changed_caps = !gst_caps_is_equal(prev_caps, current_caps);
Why is this a local variable?
if (changed_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);
GstMessage *reset_time;
gst_pad_push_event(stream->my_sink, seek_event);
if ((reset_time = gst_bus_poll(source->bus, GST_MESSAGE_RESET_TIME, -1)))
gst_message_unref(reset_time);
You really don't want to call gst_bus_poll(), and why do you need to do this anyway?
stream->eos = FALSE;
}
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);
- gst_element_get_state(source->container, NULL, NULL, -1);
- return S_OK;
+}
+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;
default:
;
"break" may be a little more idiomatic, but I would just omit the "default" branch.
- }
- 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); @@ -236,6 +521,8 @@ GstBusSyncReply bus_watch(GstBus *bus, GstMessage *message, gpointer user) g_error_free(err); g_free(dbg_info); break;
case GST_MESSAGE_RESET_TIME:
}return GST_BUS_PASS; default: break;
@@ -669,16 +956,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) @@ -759,6 +1060,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;
}
@@ -839,6 +1143,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);
@@ -847,6 +1152,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);
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 4 -- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 91 ++++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 90 ++++++++++++++++++++++++++++++ 4 files changed, 181 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 581d30ced8f..9b67dfe9217 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -64,6 +64,7 @@ struct media_stream enum source_async_op { SOURCE_ASYNC_START, + SOURCE_ASYNC_REQUEST_SAMPLE, };
struct source_async_command @@ -79,6 +80,11 @@ struct source_async_command GUID format; PROPVARIANT position; } start; + struct + { + struct media_stream *stream; + IUnknown *token; + } request_sample; } u; };
@@ -339,6 +345,61 @@ static HRESULT start_pipeline(struct media_source *source, struct source_async_c return S_OK; }
+static void dispatch_end_of_presentation(struct media_source *source) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + + /* A stream has ended, check whether all have */ + for (unsigned int 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 HRESULT 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); + return S_OK; + } + + return S_OK; +} + static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); @@ -358,6 +419,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; default: ; } @@ -649,13 +713,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 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 = @@ -739,6 +827,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 9aa17ad00ab..72da4539abf 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -750,3 +750,93 @@ 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) +{ + IMFSample *out = NULL; + LONGLONG duration, time; + int buffer_count; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(&out))) + goto fail; + + duration = GST_BUFFER_DURATION(gst_buffer); + time = GST_BUFFER_PTS(gst_buffer); + + if (FAILED(IMFSample_SetSampleDuration(out, duration / 100))) + goto fail; + + if (FAILED(IMFSample_SetSampleTime(out, time / 100))) + goto fail; + + buffer_count = gst_buffer_n_memory(gst_buffer); + + for (unsigned int i = 0; i < buffer_count; i++) + { + GstMemory *memory = gst_buffer_get_memory(gst_buffer, i); + IMFMediaBuffer *mf_buffer = NULL; + GstMapInfo map_info; + BYTE *buf_data; + + if (!memory) + { + hr = E_FAIL; + goto loop_done; + } + + if (!(gst_memory_map(memory, &map_info, GST_MAP_READ))) + { + hr = E_FAIL; + goto loop_done; + } + + if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + memcpy(buf_data, map_info.data, map_info.size); + + gst_memory_unmap(memory, &map_info); + + if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer))) + goto loop_done; + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size))) + goto loop_done; + + if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer))) + goto loop_done; + + loop_done: + if (mf_buffer) + IMFMediaBuffer_Release(mf_buffer); + if (memory) + gst_memory_unref(memory); + if (FAILED(hr)) + goto fail; + } + + return out; + fail: + ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr); + IMFSample_Release(out); + return NULL; +}
On 9/29/20 5:08 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mfplat/tests/mfplat.c | 4 -- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/media_source.c | 91 ++++++++++++++++++++++++++++++- dlls/winegstreamer/mfplat.c | 90 ++++++++++++++++++++++++++++++ 4 files changed, 181 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 581d30ced8f..9b67dfe9217 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -64,6 +64,7 @@ struct media_stream enum source_async_op { SOURCE_ASYNC_START,
- SOURCE_ASYNC_REQUEST_SAMPLE,
};
struct source_async_command @@ -79,6 +80,11 @@ struct source_async_command GUID format; PROPVARIANT position; } start;
struct
{
struct media_stream *stream;
IUnknown *token;
} u;} request_sample;
};
@@ -339,6 +345,61 @@ static HRESULT start_pipeline(struct media_source *source, struct source_async_c return S_OK; }
+static void dispatch_end_of_presentation(struct media_source *source) +{
- PROPVARIANT empty = {.vt = VT_EMPTY};
- /* A stream has ended, check whether all have */
- for (unsigned int i = 0; i < source->stream_count; i++)
Misplaced variable initializer.
- {
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 HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token)
The return value of this function is never used, and always S_OK.
+{
- 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);
return S_OK;
This statement is superfluous.
- }
- return S_OK;
+}
static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); @@ -358,6 +419,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; default: ;
@@ -649,13 +713,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 into a stream queue, and synchronization will need to be added*/
"need to into"
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 = @@ -739,6 +827,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 9aa17ad00ab..72da4539abf 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -750,3 +750,93 @@ 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) +{
- IMFSample *out = NULL;
- LONGLONG duration, time;
- int buffer_count;
- HRESULT hr;
- if (FAILED(hr = MFCreateSample(&out)))
goto fail;
- duration = GST_BUFFER_DURATION(gst_buffer);
- time = GST_BUFFER_PTS(gst_buffer);
- if (FAILED(IMFSample_SetSampleDuration(out, duration / 100)))
goto fail;
- if (FAILED(IMFSample_SetSampleTime(out, time / 100)))
goto fail;
- buffer_count = gst_buffer_n_memory(gst_buffer);
- for (unsigned int i = 0; i < buffer_count; i++)
Misplaced initializer.
You may also find it distinctly easier to use gst_buffer_map().
- {
GstMemory *memory = gst_buffer_get_memory(gst_buffer, i);
IMFMediaBuffer *mf_buffer = NULL;
GstMapInfo map_info;
BYTE *buf_data;
if (!memory)
{
hr = E_FAIL;
goto loop_done;
}
if (!(gst_memory_map(memory, &map_info, GST_MAP_READ)))
{
hr = E_FAIL;
goto loop_done;
}
if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
{
gst_memory_unmap(memory, &map_info);
goto loop_done;
}
if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL)))
{
gst_memory_unmap(memory, &map_info);
goto loop_done;
}
memcpy(buf_data, map_info.data, map_info.size);
gst_memory_unmap(memory, &map_info);
if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
goto loop_done;
if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
goto loop_done;
if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
goto loop_done;
loop_done:
if (mf_buffer)
IMFMediaBuffer_Release(mf_buffer);
if (memory)
gst_memory_unref(memory);
if (FAILED(hr))
goto fail;
- }
- return out;
- fail:
Again, please omit spaces before labels.
- ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
- IMFSample_Release(out);
- return NULL;
+}
On 9/29/20 5:08 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/winegstreamer/media_source.c | 113 +++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 16 deletions(-)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 5f3c43a0204..520f3c3177e 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -384,6 +384,42 @@ 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_resolve(struct media_stream *stream)
You return an HRESULT from this function, but don't use it anywhere.
This also strikes me as not a particularly great name; you aren't really "resolving" anything here; rather, you're adding post-processing elements.
+{
- GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL);
- if (!source_caps)
return E_FAIL;
- if (!strcmp(gst_structure_get_name(gst_caps_get_structure(source_caps, 0)), "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");
assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK);
This statement is done in both sides of the branch.
assert(gst_element_link(videoconvert, stream->appsink));
gst_element_sync_state_with_parent(videoconvert);
g_object_set(stream->appsink, "caps", source_caps, NULL);
As is this one, but moreover, what's the point of doing this?
- }
- else
- {
stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
g_object_set(stream->appsink, "caps", source_caps, NULL);
assert(gst_pad_link(stream->their_src, stream->my_sink) == GST_PAD_LINK_OK);
- }
- gst_caps_unref(source_caps);
- 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 +450,7 @@ 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);
media_stream_resolve(object);
gst_element_sync_state_with_parent(object->appsink);
@@ -435,28 +470,74 @@ 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;
- unsigned int i; HRESULT hr;
- if (!strcmp(gst_structure_get_name(gst_caps_get_structure(current_caps, 0)), "video/x-raw"))
- {
GstElementFactory *videoconvert_factory = gst_element_factory_find("videoconvert");
/* output every format supported by videoconvert */
const GList *template_list = gst_element_factory_get_static_pad_templates(videoconvert_factory);
for (;template_list; template_list = template_list->next)
{
GstStaticPadTemplate *template = (GstStaticPadTemplate *)template_list->data;
GstCaps *src_caps;
GValueArray *formats;
if (template->direction != GST_PAD_SRC)
continue;
src_caps = gst_static_pad_template_get_caps(template);
gst_structure_get_list(gst_caps_get_structure(src_caps, 0), "format", &formats);
stream_types = heap_alloc( sizeof(IMFMediaType*) * formats->n_values );
for (i = 0; i < formats->n_values; i++)
{
GValue *format = g_value_array_get_nth(formats, i);
GstCaps *modified_caps = gst_caps_copy(current_caps);
gst_caps_set_value(modified_caps, "format", format);
stream_types[type_count] = mf_media_type_from_caps(modified_caps);
gst_caps_unref(modified_caps);
if (stream_types[type_count])
type_count++;
}
g_value_array_free(formats);
gst_caps_unref(src_caps);
break;
}
I think it would be far simpler to query the videoconvert element you've actually just added.
- }
- else
- { I think in that case you would potentially call gst_caps_intersect
stream_type = mf_media_type_from_caps(current_caps);
if (stream_type)
{
stream_types = &stream_type;
type_count = 1;
}
- }
- stream_type = mf_media_type_from_caps(current_caps); gst_caps_unref(current_caps);
- if (!stream_type)
- if (!type_count)
- {
ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); return E_FAIL;
- }
- hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor);
- IMFMediaType_Release(stream_type);
- if (FAILED(hr))
return hr;
if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor)))
goto done;
if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
return hr;
- hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type);
- IMFMediaTypeHandler_Release(type_handler);
goto done;
- if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0])))
goto done;
+done:
- if (type_handler)
IMFMediaTypeHandler_Release(type_handler);
- for (i = 0; i < type_count; i++)
IMFMediaType_Release(stream_types[i]);
- if (stream_types != &stream_type)
You could probably avoid this sort of thing by just calling MFCreateStreamDescriptor() inside of the if/else switch.
return hr;heap_free(stream_types);
}
On 10/2/20 11:10 AM, Zebediah Figura wrote:
I think it would be far simpler to query the videoconvert element you've actually just added.
I'm not exactly sure what you mean by this. Do you mean querying the caps of videoconvert's source pad and using the formats there to build the descriptor? This seems like it would be approximately as complex as the current solution, but I'll do it that way if you prefer. Or is there some other solution I'm not thinking of?
FWIW, I queried the caps of the videoconvert's source pad just to test, and saw [1], so if taking that route I think I'd have to add something to remove duplicate formats.
On 10/5/20 10:22 AM, Derek Lesho wrote:
On 10/2/20 11:10 AM, Zebediah Figura wrote:
I think it would be far simpler to query the videoconvert element you've actually just added.
I'm not exactly sure what you mean by this. Do you mean querying the caps of videoconvert's source pad and using the formats there to build the descriptor? This seems like it would be approximately as complex as the current solution, but I'll do it that way if you prefer. Or is there some other solution I'm not thinking of?
Essentially that, yes, and ideally querying it after you've connected. This saves you code to retrieve the pad template, for one thing. It's also more conceptually correct—there's no guarantee that a pad will actually be able to produce all of the formats in its template. That probably won't matter for videoconvert in practice, granted, but what will probably matter for videoconvert is the order of formats returned—videoconvert has code in it to prefer "easier" conversions to harder ones.
FWIW, I queried the caps of the videoconvert's source pad just to test, and saw [1], so if taking that route I think I'd have to add something to remove duplicate formats.
Presumably, yes, unless you can get away with just not doing that. I don't know about Media Foundation, but it's not unheard of for quartz filters to report the same format multiple times, and I've yet to see an application break because of it.
Note also that one potential alternative to the approach in this patch is to do what quartz does, and just offer a fixed list of video formats (plus the "default" format that the pad proposes). Both approaches seem architecturally sensible to me, but I'm not immediately sure which one is easier. The quartz approach is likely easier if you ever want to manually prefer certain output formats, though.
You could probably avoid this sort of thing by just calling MFCreateStreamDescriptor() inside of the if/else switch.
I decided against doing this in v2 of the patch, since it would require adding more cleanup code inside the branches; either through a second goto or copying heap_free to three failure paths.