From: Rémi Bernon rbernon@codeweavers.com
To support VA-API plugins we need to implement video frame copy as well, as the plugins simply ignore downstream buffer alignment requirements.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45988 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47084 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49715 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52183 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/mf/tests/mf.c | 3 - dlls/winegstreamer/wg_transform.c | 129 +++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 12 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 3fe819ba23d..81bfafbda04 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7284,9 +7284,7 @@ static void test_h264_decoder(void) ok(hr == S_OK, "ConvertToContiguousBuffer returned %#lx\n", hr); hr = IMFMediaBuffer_Lock(media_buffer, &data, NULL, &length); ok(hr == S_OK, "Lock returned %#lx\n", hr); - todo_wine ok(length == nv12_frame_len, "got length %lu\n", length); - if (length != nv12_frame_len) goto skip_nv12_tests;
for (i = 0; i < actual_aperture.Area.cy; ++i) { @@ -7302,7 +7300,6 @@ static void test_h264_decoder(void)
check_sample(output.pSample, nv12_frame_data, output_file);
-skip_nv12_tests: ret = IMFSample_Release(output.pSample); ok(ret == 0, "Release returned %lu\n", ret);
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 0c1654567e5..e0824b21491 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -52,6 +52,7 @@ struct wg_transform GstSegment segment; GstBufferList *input; guint input_max_length; + guint output_plane_align; GstAtomicQueue *output_queue; GstSample *output_sample; bool output_caps_changed; @@ -69,6 +70,20 @@ static bool is_caps_video(GstCaps *caps) return g_str_has_prefix(media_type, "video/"); }
+static bool align_video_info_planes(gsize plane_align, GstVideoInfo *info, GstVideoAlignment *align) +{ + gst_video_alignment_reset(align); + + align->padding_right = ((plane_align + 1) - (info->width & plane_align)) & plane_align; + align->padding_bottom = ((plane_align + 1) - (info->height & plane_align)) & plane_align; + align->stride_align[0] = plane_align; + align->stride_align[1] = plane_align; + align->stride_align[2] = plane_align; + align->stride_align[3] = plane_align; + + return gst_video_info_align(info, align); +} + static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) { struct wg_transform *transform = gst_pad_get_element_private(pad); @@ -102,7 +117,9 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery { case GST_QUERY_ALLOCATION: { - GstStructure *config; + gsize plane_align = transform->output_plane_align; + GstStructure *config, *params; + GstVideoAlignment align; gboolean needs_pool; GstBufferPool *pool; GstVideoInfo info; @@ -113,19 +130,36 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery break;
if (!gst_video_info_from_caps(&info, caps) + || !align_video_info_planes(plane_align, &info, &align) || !(pool = gst_video_buffer_pool_new())) break;
+ if ((params = gst_structure_new("video-meta", + "padding-top", G_TYPE_UINT, align.padding_top, + "padding-bottom", G_TYPE_UINT, align.padding_bottom, + "padding-left", G_TYPE_UINT, align.padding_left, + "padding-right", G_TYPE_UINT, align.padding_right, + NULL))) + gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, params); + if (!(config = gst_buffer_pool_get_config(pool))) GST_ERROR("Failed to get pool %p config.", pool); else { + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); + gst_buffer_pool_config_set_video_alignment(config, &align); + gst_buffer_pool_config_set_params(config, caps, info.size, 0, 0); if (!gst_buffer_pool_set_config(pool, config)) GST_ERROR("Failed to set pool %p config.", pool); }
+ /* Prevent pool reconfiguration, we don't want another alignment. */ + if (!gst_buffer_pool_set_active(pool, true)) + GST_ERROR("Pool %p failed to activate.", pool); + gst_query_add_allocation_pool(query, pool, info.size, 0, 0);
GST_INFO("Proposing pool %p, buffer size %#zx, for query %p.", @@ -292,6 +326,7 @@ NTSTATUS wg_transform_create(void *args) if (!(transform->output_queue = gst_atomic_queue_new(8))) goto out; transform->input_max_length = 1; + transform->output_plane_align = 0;
if (!(src_caps = wg_format_to_caps(&input_format))) goto out; @@ -333,6 +368,7 @@ NTSTATUS wg_transform_create(void *args) * to match its expectations. */ transform->input_max_length = 16; + transform->output_plane_align = 15; if (!(element = create_element("h264parse", "base")) || !transform_append_element(transform, element, &first, &last)) goto out; @@ -494,17 +530,69 @@ NTSTATUS wg_transform_push_data(void *args) return STATUS_SUCCESS; }
-static NTSTATUS read_transform_output_data(GstBuffer *buffer, struct wg_sample *sample) +static bool copy_video_buffer(GstBuffer *buffer, GstCaps *caps, gsize plane_align, + struct wg_sample *sample, gsize *total_size) { - GstMapInfo info; + GstVideoFrame src_frame, dst_frame; + GstVideoInfo src_info, dst_info; + GstVideoAlignment align; + GstBuffer *dst_buffer; + bool ret = false;
- if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) + if (!gst_video_info_from_caps(&src_info, caps)) { - GST_ERROR("Failed to map buffer %p", buffer); - sample->size = 0; - return STATUS_UNSUCCESSFUL; + GST_ERROR("Failed to get video info from caps."); + return false; }
+ dst_info = src_info; + if (!align_video_info_planes(plane_align, &dst_info, &align)) + { + GST_ERROR("Failed to align video info."); + return false; + } + if (sample->max_size < dst_info.size) + { + GST_ERROR("Output buffer is too small."); + return false; + } + + if (!(dst_buffer = gst_buffer_new_wrapped_full(0, sample->data, sample->max_size, + 0, sample->max_size, 0, NULL))) + { + GST_ERROR("Failed to wrap wg_sample into GstBuffer"); + return false; + } + gst_buffer_set_size(dst_buffer, dst_info.size); + *total_size = sample->size = dst_info.size; + + if (!gst_video_frame_map(&src_frame, &src_info, buffer, GST_MAP_READ)) + GST_ERROR("Failed to map source frame."); + else + { + if (!gst_video_frame_map(&dst_frame, &dst_info, dst_buffer, GST_MAP_WRITE)) + GST_ERROR("Failed to map destination frame."); + else + { + if (!(ret = gst_video_frame_copy(&dst_frame, &src_frame))) + GST_ERROR("Failed to copy video frame."); + gst_video_frame_unmap(&dst_frame); + } + gst_video_frame_unmap(&src_frame); + } + + gst_buffer_unref(dst_buffer); + return ret; +} + +static bool copy_buffer(GstBuffer *buffer, GstCaps *caps, struct wg_sample *sample, + gsize *total_size) +{ + GstMapInfo info; + + if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) + return false; + if (sample->max_size >= info.size) sample->size = info.size; else @@ -519,6 +607,28 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, struct wg_sample * if (sample->flags & WG_SAMPLE_FLAG_INCOMPLETE) gst_buffer_resize(buffer, sample->size, -1);
+ *total_size = info.size; + return true; +} + +static NTSTATUS read_transform_output_data(GstBuffer *buffer, GstCaps *caps, gsize plane_align, + struct wg_sample *sample) +{ + gsize total_size; + bool ret; + + if (is_caps_video(caps)) + ret = copy_video_buffer(buffer, caps, plane_align, sample, &total_size); + else + ret = copy_buffer(buffer, caps, sample, &total_size); + + if (!ret) + { + GST_ERROR("Failed to copy buffer %p", buffer); + sample->size = 0; + return STATUS_UNSUCCESSFUL; + } + if (GST_BUFFER_PTS_IS_VALID(buffer)) { sample->flags |= WG_SAMPLE_FLAG_HAS_PTS; @@ -528,7 +638,7 @@ static NTSTATUS read_transform_output_data(GstBuffer *buffer, struct wg_sample * { GstClockTime duration = GST_BUFFER_DURATION(buffer) / 100;
- duration = (duration * sample->size) / info.size; + duration = (duration * sample->size) / total_size; GST_BUFFER_DURATION(buffer) -= duration * 100; if (GST_BUFFER_PTS_IS_VALID(buffer)) GST_BUFFER_PTS(buffer) += duration * 100; @@ -592,7 +702,8 @@ NTSTATUS wg_transform_read_data(void *args) return STATUS_SUCCESS; }
- if ((status = read_transform_output_data(output_buffer, sample))) + if ((status = read_transform_output_data(output_buffer, output_caps, + transform->output_plane_align, sample))) return status;
if (!(sample->flags & WG_SAMPLE_FLAG_INCOMPLETE))