[PATCH v3 3/5] winegstreamer: Request native buffer alignment using video pool meta.

Rémi Bernon wine at gitlab.winehq.org
Thu Jun 2 01:31:33 CDT 2022


From: Rémi Bernon <rbernon at 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 at 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))
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/139



More information about the wine-devel mailing list