PR #24328 opened by yinwenjie
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24328
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24328.patch

V4l2 drivers may return buffer width & height with aligned values, which
may be different from frame width & height. Bytesperlines and strides
should be check for aligned buffers when copying pixel data. When width
aligned, copy with planar; otherwise, copy line-by-line to dest buffer.

Co-authored-by: Renjiang Han <[email protected]>
Signed-off-by: Wenjie Yin <[email protected]>

# Summary of changes

Briefly describe what this PR does and why.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From 3fda94e1309bead4d39ea4b2cc42d13f8cdf48b4 Mon Sep 17 00:00:00 2001
From: Wenjie Yin <[email protected]>
Date: Wed, 29 Oct 2025 13:59:47 +0800
Subject: [PATCH] libavcodec/v4l2_buffers: fixing buffer alignment issue

V4l2 drivers may return buffer width & height with aligned values, which
may be different from frame width & height. Bytesperlines and strides
should be check for aligned buffers when copying pixel data. When width
aligned, copy with planar; otherwise, copy line-by-line to dest buffer.

Co-authored-by: Renjiang Han <[email protected]>
Signed-off-by: Wenjie Yin <[email protected]>
---
 libavcodec/v4l2_buffers.c | 147 ++++++++++++++++++++++++++++++++------
 1 file changed, 126 insertions(+), 21 deletions(-)

diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c
index f96c26771b..30433885eb 100644
--- a/libavcodec/v4l2_buffers.c
+++ b/libavcodec/v4l2_buffers.c
@@ -26,9 +26,11 @@
 #include <sys/mman.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <poll.h>
 #include "libavcodec/avcodec.h"
 #include "libavutil/attributes.h"
+#include "libavutil/imgutils.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/refstruct.h"
 #include "v4l2_context.h"
@@ -360,14 +362,133 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, 
V4L2Buffer *avbuf)
     return 0;
 }
 
+/**
+ * Copy a software frame into one contiguous V4L2 memory plane while keeping
+ * the visible frame geometry separate from the driver-aligned storage layout.
+ */
+static int v4l2_buffer_swframe_to_single_buffer(const AVFrame *frame,
+                                                V4L2Buffer *out,
+                                                uint32_t pixel_format,
+                                                unsigned int width,
+                                                unsigned int height,
+                                                unsigned int bytesperline,
+                                                unsigned int sizeimage)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+    uint8_t *dst_data[4];
+    int active_linesizes[4], storage_linesizes[4];
+    int dst_linesizes[4] = { 0 };
+    int i, layout_size, planes_nb = 0, ret;
+    size_t length = out->plane_info[0].length;
+
+    if (!desc || out->num_planes < 1 ||
+        frame->width <= 0 || frame->height <= 0 ||
+        width > INT_MAX || height > INT_MAX || bytesperline > INT_MAX ||
+        frame->width > width || frame->height > height)
+        return AVERROR(EINVAL);
+
+    /* Active linesizes describe pixels; storage linesizes include alignment. 
*/
+    ret = av_image_fill_linesizes(active_linesizes, frame->format,
+                                  frame->width);
+    if (ret < 0)
+        return ret;
+
+    ret = av_image_fill_linesizes(storage_linesizes, frame->format, width);
+    if (ret < 0)
+        return ret;
+
+    for (i = 0; i < desc->nb_components; i++)
+        planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);
+
+    if (!bytesperline)
+        bytesperline = storage_linesizes[0];
+
+    if (bytesperline < storage_linesizes[0])
+        return AVERROR(EINVAL);
+
+    if (planes_nb == 1) {
+        dst_linesizes[0] = bytesperline;
+    } else {
+        /*
+         * The supported contiguous multi-plane formats have 8-bit luma, so
+         * bytesperline is also the padded luma width used to derive chroma
+         * strides.
+         */
+        if (storage_linesizes[0] != width)
+            return AVERROR(EINVAL);
+
+        ret = av_image_fill_linesizes(dst_linesizes, frame->format,
+                                      bytesperline);
+        if (ret < 0 || dst_linesizes[0] != bytesperline)
+            return ret < 0 ? ret : AVERROR(EINVAL);
+    }
+
+    /* Storage height determines plane offsets; only visible rows are copied. 
*/
+    layout_size = av_image_fill_pointers(dst_data, frame->format, height,
+                                         out->plane_info[0].mm_addr,
+                                         dst_linesizes);
+    if (layout_size < 0)
+        return layout_size;
+
+    /* Reject the complete layout before any write can exceed driver storage. 
*/
+    if ((size_t)layout_size > length ||
+        (sizeimage && (size_t)layout_size > sizeimage))
+        return AVERROR(EINVAL);
+
+    /* av_image_copy2() requires each active row to fit both line strides. */
+    for (i = 0; i < planes_nb; i++) {
+        int64_t src_linesize = frame->linesize[i];
+
+        if (src_linesize < 0)
+            src_linesize = -src_linesize;
+
+        if (!frame->data[i] || !dst_data[i] ||
+            src_linesize < active_linesizes[i] ||
+            dst_linesizes[i] < active_linesizes[i])
+            return AVERROR(EINVAL);
+    }
+
+    /* AVFrame stores Y, U, V while YVU formats place V before U. */
+    switch (pixel_format) {
+    case V4L2_PIX_FMT_YVU410:
+    case V4L2_PIX_FMT_YVU420:
+        FFSWAP(uint8_t *, dst_data[1], dst_data[2]);
+        break;
+    }
+
+    /* Copy visible pixels only; aligned rows and columns remain padding. */
+    av_image_copy2(dst_data, dst_linesizes, frame->data, frame->linesize,
+                   frame->format, frame->width, frame->height);
+
+    if (V4L2_TYPE_IS_MULTIPLANAR(out->buf.type)) {
+        out->planes[0].bytesused = layout_size;
+        out->planes[0].length = length;
+    } else {
+        out->buf.bytesused = layout_size;
+        out->buf.length = length;
+    }
+
+    return 0;
+}
+
 static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
 {
     int i, ret;
     struct v4l2_format fmt = out->context->format;
     int pixel_format = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
                        fmt.fmt.pix_mp.pixelformat : fmt.fmt.pix.pixelformat;
+    int width        = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
+                         fmt.fmt.pix_mp.width : fmt.fmt.pix.width;
     int height       = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
                        fmt.fmt.pix_mp.height : fmt.fmt.pix.height;
+    int bytesperline = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
+                                (fmt.fmt.pix_mp.num_planes ?
+                                 fmt.fmt.pix_mp.plane_fmt[0].bytesperline : 0) 
:
+                                fmt.fmt.pix.bytesperline;
+    int sizeimage    = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ?
+                                (fmt.fmt.pix_mp.num_planes ?
+                                 fmt.fmt.pix_mp.plane_fmt[0].sizeimage : 0) :
+                                 fmt.fmt.pix.sizeimage;
     int is_planar_format = 0;
 
     switch (pixel_format) {
@@ -394,27 +515,11 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame 
*frame, V4L2Buffer *out)
         is_planar_format = 1;
     }
 
-    if (!is_planar_format) {
-        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
-        int planes_nb = 0;
-        int offset = 0;
-
-        for (i = 0; i < desc->nb_components; i++)
-            planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1);
-
-        for (i = 0; i < planes_nb; i++) {
-            int size, h = height;
-            if (i == 1 || i == 2) {
-                h = AV_CEIL_RSHIFT(h, desc->log2_chroma_h);
-            }
-            size = frame->linesize[i] * h;
-            ret = v4l2_bufref_to_buf(out, 0, frame->data[i], size, offset);
-            if (ret)
-                return ret;
-            offset += size;
-        }
-        return 0;
-    }
+    if (!is_planar_format)
+        return v4l2_buffer_swframe_to_single_buffer(frame, out,
+                                                    pixel_format, width,
+                                                    height, bytesperline,
+                                                    sizeimage);
 
     for (i = 0; i < out->num_planes; i++) {
         ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, 
frame->buf[i]->size, 0);
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to