-- v3: winegstreamer/wg_transform: Support creating encoder transform. winegstreamer/wg_transform: Introduce transform_create_converter_elements. winegstreamer/wg_transform: Introduce transform_create_decoder_elements.
From: Ziqing Hui zhui@codeweavers.com
MF_MT_INTERLACE_MODE, MF_MT_VIDEO_NOMINAL_RANGE and MF_MT_PIXEL_ASPECT_RATIO are not required in input type. However, if they appears, they should match output type. --- dlls/winegstreamer/video_encoder.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/video_encoder.c b/dlls/winegstreamer/video_encoder.c index ee27175ee4f..1bfe26261af 100644 --- a/dlls/winegstreamer/video_encoder.c +++ b/dlls/winegstreamer/video_encoder.c @@ -280,6 +280,7 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM struct video_encoder *encoder = impl_from_IMFTransform(iface); IMFMediaType *good_input_type; GUID major, subtype; + UINT64 ratio; BOOL result; HRESULT hr; ULONG i; @@ -317,10 +318,14 @@ static HRESULT WINAPI transform_SetInputType(IMFTransform *iface, DWORD id, IMFM if (i == encoder->input_type_count) return MF_E_INVALIDMEDIATYPE;
+ if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &ratio)) + || FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &ratio))) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(hr = video_encoder_create_input_type(encoder, &subtype, &good_input_type))) return hr; hr = IMFMediaType_Compare(good_input_type, (IMFAttributes *)type, - MF_ATTRIBUTES_MATCH_OUR_ITEMS, &result); + MF_ATTRIBUTES_MATCH_INTERSECTION, &result); IMFMediaType_Release(good_input_type); if (FAILED(hr) || !result) return MF_E_INVALIDMEDIATYPE;
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/video_encoder.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/video_encoder.c b/dlls/winegstreamer/video_encoder.c index 1bfe26261af..e15c799d81f 100644 --- a/dlls/winegstreamer/video_encoder.c +++ b/dlls/winegstreamer/video_encoder.c @@ -367,6 +367,7 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF { IMFMediaType_Release(encoder->output_type); encoder->output_type = NULL; + memset(&encoder->output_info, 0, sizeof(encoder->output_info)); } if (encoder->wg_transform) { @@ -412,6 +413,11 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF
/* FIXME: Add MF_MT_MPEG_SEQUENCE_HEADER attribute. */
+ /* FIXME: Hardcode a size that native uses for 1920 * 1080. + * And hope it's large enough to make things work for now. + * The right way is to calculate it based on frame width and height. */ + encoder->output_info.cbSize = 0x3bc400; + if (encoder->wg_transform) { wg_transform_destroy(encoder->wg_transform); @@ -627,10 +633,6 @@ HRESULT h264_encoder_create(REFIID riid, void **out) h264_encoder_output_types, ARRAY_SIZE(h264_encoder_output_types), &encoder))) return hr;
- /* FIXME: Hardcode a size that is large enough to make things work for now. - * The right way is to calculate the size based on output frame size. */ - encoder->output_info.cbSize = 0x3bc400; - TRACE("Created h264 encoder transform %p.\n", &encoder->IMFTransform_iface);
hr = IMFTransform_QueryInterface(&encoder->IMFTransform_iface, riid, out);
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/wg_transform.c | 80 ++++++++++++++++++------------- 1 file changed, 46 insertions(+), 34 deletions(-)
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 76dbd347064..8b710e68e70 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -468,11 +468,55 @@ static GstCaps *transform_get_parsed_caps(GstCaps *caps, const char *media_type) return parsed_caps; }
+static bool transform_create_decoder_elements(struct wg_transform *transform, + const gchar *input_mime, const gchar *output_mime, GstElement **first, GstElement **last) +{ + GstCaps *parsed_caps = NULL, *sink_caps = NULL; + GstElement *element; + bool ret = false; + + if (!strcmp(input_mime, "audio/x-raw") || !strcmp(input_mime, "video/x-raw")) + return true; + + if (!(parsed_caps = transform_get_parsed_caps(transform->input_caps, input_mime))) + return false; + + /* Since we append conversion elements, we don't want to filter decoders + * based on the actual output caps now. Matching decoders with the + * raw output media type should be enough. + */ + if (!(sink_caps = gst_caps_new_empty_simple(output_mime))) + goto done; + + if ((element = find_element(GST_ELEMENT_FACTORY_TYPE_PARSER, transform->input_caps, parsed_caps)) + && !append_element(transform->container, element, first, last)) + goto done; + else if (!element) + { + gst_caps_unref(parsed_caps); + parsed_caps = gst_caps_ref(transform->input_caps); + } + + if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, parsed_caps, sink_caps)) + || !append_element(transform->container, element, first, last)) + goto done; + + set_max_threads(element); + + ret = true; + +done: + if (sink_caps) + gst_caps_unref(sink_caps); + if (parsed_caps) + gst_caps_unref(parsed_caps); + return ret; +} + NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; GstElement *first = NULL, *last = NULL, *element; - GstCaps *sink_caps = NULL, *parsed_caps = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; const gchar *input_mime, *output_mime; GstPadTemplate *template = NULL; @@ -531,34 +575,9 @@ NTSTATUS wg_transform_create(void *args) gst_pad_set_query_function(transform->my_sink, transform_sink_query_cb); gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb);
- if (!(parsed_caps = transform_get_parsed_caps(transform->input_caps, input_mime))) - goto out; - - /* Since we append conversion elements, we don't want to filter decoders - * based on the actual output caps now. Matching decoders with the - * raw output media type should be enough. - */ - if (!(sink_caps = gst_caps_new_empty_simple(output_mime))) + if (!transform_create_decoder_elements(transform, input_mime, output_mime, &first, &last)) goto out;
- if (strcmp(input_mime, "audio/x-raw") && strcmp(input_mime, "video/x-raw")) - { - if ((element = find_element(GST_ELEMENT_FACTORY_TYPE_PARSER, transform->input_caps, parsed_caps)) - && !append_element(transform->container, element, &first, &last)) - goto out; - else if (!element) - { - gst_caps_unref(parsed_caps); - parsed_caps = gst_caps_ref(transform->input_caps); - } - - if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, parsed_caps, sink_caps)) - || !append_element(transform->container, element, &first, &last)) - goto out; - - set_max_threads(element); - } - if (g_str_has_prefix(output_mime, "audio/")) { if (strcmp(output_mime, "audio/x-raw")) @@ -632,9 +651,6 @@ NTSTATUS wg_transform_create(void *args) || !push_event(transform->my_src, event)) goto out;
- gst_caps_unref(parsed_caps); - gst_caps_unref(sink_caps); - GST_INFO("Created winegstreamer transform %p.", transform); params->transform = (wg_transform_t)(ULONG_PTR)transform; return STATUS_SUCCESS; @@ -650,10 +666,6 @@ out: gst_object_unref(transform->my_src); if (transform->input_caps) gst_caps_unref(transform->input_caps); - if (parsed_caps) - gst_caps_unref(parsed_caps); - if (sink_caps) - gst_caps_unref(sink_caps); if (transform->allocator) wg_allocator_destroy(transform->allocator); if (transform->drain_query)
From: Ziqing Hui zhui@codeweavers.com
--- dlls/winegstreamer/wg_transform.c | 100 ++++++++++++++++-------------- 1 file changed, 55 insertions(+), 45 deletions(-)
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 8b710e68e70..a30a773ca1e 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -513,10 +513,62 @@ done: return ret; }
+static bool transform_create_converter_elements(struct wg_transform *transform, + const gchar *output_mime, GstElement **first, GstElement **last) +{ + GstElement *element; + + if (g_str_has_prefix(output_mime, "audio/")) + { + if (strcmp(output_mime, "audio/x-raw")) + { + GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + return false; + } + else + { + /* The MF audio decoder transforms allow decoding to various formats + * as well as resampling the audio at the same time, whereas + * GStreamer decoder plugins usually only support decoding to a + * single format and at the original rate. + * + * The WMA decoder transform also has output samples interleaved on + * Windows, whereas GStreamer avdec_wmav2 output uses + * non-interleaved format. + */ + if (!(element = create_element("audioconvert", "base")) + || !append_element(transform->container, element, first, last)) + return false; + if (!(element = create_element("audioresample", "base")) + || !append_element(transform->container, element, first, last)) + return false; + } + } + + if (g_str_has_prefix(output_mime, "video/")) + { + if (strcmp(output_mime, "video/x-raw")) + { + GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + return false; + } + else + { + if (!(element = create_element("videoconvert", "base")) + || !append_element(transform->container, element, first, last)) + return false; + /* Let GStreamer choose a default number of threads. */ + gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); + } + } + + return true; +} + NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; - GstElement *first = NULL, *last = NULL, *element; + GstElement *first = NULL, *last = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; const gchar *input_mime, *output_mime; GstPadTemplate *template = NULL; @@ -577,50 +629,8 @@ NTSTATUS wg_transform_create(void *args)
if (!transform_create_decoder_elements(transform, input_mime, output_mime, &first, &last)) goto out; - - if (g_str_has_prefix(output_mime, "audio/")) - { - if (strcmp(output_mime, "audio/x-raw")) - { - GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); - goto out; - } - else - { - /* The MF audio decoder transforms allow decoding to various formats - * as well as resampling the audio at the same time, whereas - * GStreamer decoder plugins usually only support decoding to a - * single format and at the original rate. - * - * The WMA decoder transform also has output samples interleaved on - * Windows, whereas GStreamer avdec_wmav2 output uses - * non-interleaved format. - */ - if (!(element = create_element("audioconvert", "base")) - || !append_element(transform->container, element, &first, &last)) - goto out; - if (!(element = create_element("audioresample", "base")) - || !append_element(transform->container, element, &first, &last)) - goto out; - } - } - - if (g_str_has_prefix(output_mime, "video/")) - { - if (strcmp(output_mime, "video/x-raw")) - { - GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); - goto out; - } - else - { - if (!(element = create_element("videoconvert", "base")) - || !append_element(transform->container, element, &first, &last)) - goto out; - /* Let GStreamer choose a default number of threads. */ - gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); - } - } + if (!transform_create_converter_elements(transform, output_mime, &first, &last)) + goto out;
if (!link_src_to_element(transform->my_src, first)) goto out;
From: Ziqing Hui zhui@codeweavers.com
--- dlls/mf/tests/transform.c | 25 ++++++++---- dlls/winegstreamer/wg_transform.c | 67 +++++++++++++++---------------- 2 files changed, 50 insertions(+), 42 deletions(-)
diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index ef0bec60ed5..3e902aa67f3 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -3910,12 +3910,12 @@ static void test_h264_encoder(void) ATTR_RATIO(MF_MT_FRAME_SIZE, actual_width, actual_height), ATTR_RATIO(MF_MT_FRAME_RATE, 30000, 1001), ATTR_UINT32(MF_MT_AVG_BITRATE, 193540), - ATTR_BLOB(MF_MT_MPEG_SEQUENCE_HEADER, test_h264_sequence_header, sizeof(test_h264_sequence_header)), + ATTR_BLOB(MF_MT_MPEG_SEQUENCE_HEADER, test_h264_sequence_header, sizeof(test_h264_sequence_header), .todo = TRUE), ATTR_UINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), ATTR_UINT32(test_attr_guid, 0), {0}, }; - static const MFT_OUTPUT_STREAM_INFO expect_output_info[] = {{.cbSize = 0x8000}, {.cbSize = 0x3bc400}}; + MFT_OUTPUT_STREAM_INFO output_info, expect_output_info[] = {{.cbSize = 0x8000}, {.cbSize = 0x3bc400}}; MFT_REGISTER_TYPE_INFO output_type = {MFMediaType_Video, MFVideoFormat_H264}; MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_NV12}; IMFMediaType *media_type; @@ -3940,10 +3940,7 @@ static void test_h264_encoder(void) check_mft_get_info(class_id, &expect_mft_info);
hr = CoCreateInstance(class_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform); - todo_wine ok(hr == S_OK, "CoCreateInstance returned %#lx.\n", hr); - if (hr != S_OK) - goto failed;
check_interface(transform, &IID_IMFTransform, TRUE); check_interface(transform, &IID_IMediaObject, FALSE); @@ -3977,8 +3974,13 @@ static void test_h264_encoder(void)
check_mft_set_output_type_required(transform, output_type_desc); check_mft_set_output_type(transform, output_type_desc, S_OK); - check_mft_get_output_current_type(transform, expect_output_type_desc); - check_mft_get_output_stream_info(transform, S_OK, &expect_output_info[0]); + check_mft_get_output_current_type_(__LINE__, transform, expect_output_type_desc, FALSE, TRUE); + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); + ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); + check_member(output_info, expect_output_info[0], "%#lx", dwFlags); + todo_wine + check_member(output_info, expect_output_info[0], "%#lx", cbSize); + check_member(output_info, expect_output_info[0], "%#lx", cbAlignment);
/* Input types can now be enumerated. */ i = -1; @@ -4032,7 +4034,14 @@ static void test_h264_encoder(void) if (IsEqualGUID(test_attributes[i].key, &MF_MT_FRAME_SIZE)) check_mft_get_output_stream_info(transform, S_OK, &expect_output_info[1]); else - check_mft_get_output_stream_info(transform, S_OK, &expect_output_info[0]); + { + hr = IMFTransform_GetOutputStreamInfo(transform, 0, &output_info); + ok(hr == S_OK, "GetOutputStreamInfo returned %#lx\n", hr); + check_member(output_info, expect_output_info[0], "%#lx", dwFlags); + todo_wine + check_member(output_info, expect_output_info[0], "%#lx", cbSize); + check_member(output_info, expect_output_info[0], "%#lx", cbAlignment); + }
winetest_pop_context(); } diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index a30a773ca1e..deb99b78529 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -520,51 +520,47 @@ static bool transform_create_converter_elements(struct wg_transform *transform,
if (g_str_has_prefix(output_mime, "audio/")) { - if (strcmp(output_mime, "audio/x-raw")) - { - GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + /* The MF audio decoder transforms allow decoding to various formats + * as well as resampling the audio at the same time, whereas + * GStreamer decoder plugins usually only support decoding to a + * single format and at the original rate. + * + * The WMA decoder transform also has output samples interleaved on + * Windows, whereas GStreamer avdec_wmav2 output uses + * non-interleaved format. + */ + if (!(element = create_element("audioconvert", "base")) + || !append_element(transform->container, element, first, last)) + return false; + if (!(element = create_element("audioresample", "base")) + || !append_element(transform->container, element, first, last)) return false; - } - else - { - /* The MF audio decoder transforms allow decoding to various formats - * as well as resampling the audio at the same time, whereas - * GStreamer decoder plugins usually only support decoding to a - * single format and at the original rate. - * - * The WMA decoder transform also has output samples interleaved on - * Windows, whereas GStreamer avdec_wmav2 output uses - * non-interleaved format. - */ - if (!(element = create_element("audioconvert", "base")) - || !append_element(transform->container, element, first, last)) - return false; - if (!(element = create_element("audioresample", "base")) - || !append_element(transform->container, element, first, last)) - return false; - } }
if (g_str_has_prefix(output_mime, "video/")) { - if (strcmp(output_mime, "video/x-raw")) - { - GST_FIXME("output caps %"GST_PTR_FORMAT" not implemented!", transform->output_caps); + if (!(element = create_element("videoconvert", "base")) + || !append_element(transform->container, element, first, last)) return false; - } - else - { - if (!(element = create_element("videoconvert", "base")) - || !append_element(transform->container, element, first, last)) - return false; - /* Let GStreamer choose a default number of threads. */ - gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); - } + /* Let GStreamer choose a default number of threads. */ + gst_util_set_object_arg(G_OBJECT(element), "n-threads", "0"); }
return true; }
+static bool transform_create_encoder_element(struct wg_transform *transform, + const gchar *output_mime, GstElement **first, GstElement **last) +{ + GstElement *element; + + if (!strcmp(output_mime, "audio/x-raw") || !strcmp(output_mime, "video/x-raw")) + return true; + + return (element = find_element(GST_ELEMENT_FACTORY_TYPE_ENCODER, NULL, transform->output_caps)) + && append_element(transform->container, element, first, last); +} + NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; @@ -627,10 +623,13 @@ NTSTATUS wg_transform_create(void *args) gst_pad_set_query_function(transform->my_sink, transform_sink_query_cb); gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb);
+ /* Create elements. */ if (!transform_create_decoder_elements(transform, input_mime, output_mime, &first, &last)) goto out; if (!transform_create_converter_elements(transform, output_mime, &first, &last)) goto out; + if (!transform_create_encoder_element(transform, output_mime, &first, &last)) + goto out;
if (!link_src_to_element(transform->my_src, first)) goto out;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=146957
Your paranoid android.
=== debian11 (build log) ===
WineRunWineTest.pl:error: The task timed out
On Wed Jul 10 07:28:53 2024 +0000, Rémi Bernon wrote:
This doesn't really belong to `transform_create_converter_elements`.
OK, I move this part into a new helper transform_create_encoder_element.
This merge request was approved by Rémi Bernon.
This merge request was approved by Elizabeth Figura.