Skip to content

fix: use real MJPG decoded size instead of negotiated resolution - #520

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix-372317-fix-mjpg-decoded-size-mismatch
Aug 18, 2026
Merged

fix: use real MJPG decoded size instead of negotiated resolution#520
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
add-uos:fix-372317-fix-mjpg-decoded-size-mismatch

Conversation

@add-uos

@add-uos add-uos commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Do not preset decoder width/height before avcodec_open2(); let FFmpeg parse the real size from the JPEG SOF header. Copy decoded frames using the actual sw_frame/picture dimensions and realloc buffers when needed. Add jpeg_get_decoded_size() and trim yuv_frame to the real decoded size.

不在 avcodec_open2() 前预设解码器宽高,改由 FFmpeg 从 JPEG SOF 头解析 真实尺寸。按实际解码帧尺寸拷贝并在需要时 realloc 缓冲。新增
jpeg_get_decoded_size(),并在首帧后将 yuv_frame 收紧到真实解码尺寸。

Log: 修复MJPG解码尺寸与协商分辨率不一致导致的解码失败和画面错位
PMS: BUG-372317
Influence: 修复相机协商与实际分辨率不一致导致的解码失败、画面错位及内存浪费。

Summary by Sourcery

Use the JPEG’s actual decoded dimensions throughout MJPEG frame processing instead of relying on the negotiated resolution.

New Features:

  • Expose the actual decoded MJPEG frame dimensions after decoding.

Bug Fixes:

  • Fix MJPEG decoding failures and image misalignment when the decoded JPEG size differs from the negotiated resolution.

Enhancements:

  • Resize decoded-frame buffers to match the actual MJPEG dimensions and avoid retaining unnecessary memory.
  • Allow FFmpeg to determine JPEG dimensions from the image data rather than forcing negotiated dimensions before decoder initialization.

@sourcery-ai

sourcery-ai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adjust MJPG decoding to use the actual decoded frame size from FFmpeg instead of the negotiated resolution, and propagate that real size through buffer management and frame metadata so frames are copied, resized, and trimmed safely.

Sequence diagram for MJPG decode using real decoded size

sequenceDiagram
    participant FrameDecoder as decode_v4l2_frame
    participant JpegDecoder as jpeg_decoder
    participant FFmpeg
    participant FrameBuffer as v4l2_frame_buff_t

    FrameDecoder->>JpegDecoder: jpeg_decode(out_buf, in_buf, size)
    JpegDecoder->>FFmpeg: m_av_image_get_buffer_size(format, dec_w, dec_h, 1)
    FFmpeg-->>JpegDecoder: buffer_size
    JpegDecoder->>JpegDecoder: realloc(tmp_frame, buffer_size)
    JpegDecoder->>FFmpeg: m_av_image_copy_to_buffer(tmp_frame, pic_size, data, linesize, format, dec_w, dec_h, 1)
    JpegDecoder->>JpegDecoder: update jpeg_ctx.width, jpeg_ctx.height
    JpegDecoder-->>FrameDecoder: ret

    FrameDecoder->>JpegDecoder: jpeg_get_decoded_size(out_w, out_h)
    JpegDecoder-->>FrameDecoder: real_w, real_h
    FrameDecoder->>FrameDecoder: adjust frame.width, frame.height
    FrameDecoder->>FrameBuffer: realloc(yuv_frame, need)
    FrameDecoder->>FrameBuffer: update yuv_frame_max_size
Loading

File-Level Changes

Change Details Files
Let FFmpeg determine the actual MJPG decode size and copy frames using the real decoded dimensions with dynamic tmp buffer sizing.
  • Stop presetting codec context width/height before avcodec_open2 so FFmpeg reads the real size from the JPEG SOF header.
  • When copying from sw_frame, derive dec_w/dec_h from sw_frame->width/height and compute the needed buffer size via av_image_get_buffer_size.
  • Reallocate jpeg_ctx->tmp_frame and update jpeg_ctx->pic_size when the decoded frame requires a larger buffer, then copy using the real decoded width/height.
  • Mirror the same dynamic sizing and copy logic when reading from codec_data->picture, including using picture->format for buffer size computation.
  • Keep jpeg_ctx->width/height in sync with the last decoded frame size to reflect the real decoded dimensions.
libcam/libcam_v4l2core/jpeg_decoder.c
Expose the real decoded MJPG frame size and use it to adjust V4L2 frame metadata and YUV buffers.
  • Add jpeg_get_decoded_size() that returns the current decoded width/height from jpeg_ctx after a successful decode.
  • Declare jpeg_get_decoded_size() in jpeg_decoder.h so other modules can query the decoded size.
  • In decode_v4l2_frame, after jpeg_decode succeeds, query jpeg_get_decoded_size and override frame->width/height if they differ from the negotiated resolution, logging a warning.
  • Based on the real decoded width/height, compute the required YUV buffer size (YUV420: wh3/2) and realloc frame->yuv_frame if the required size differs from the current maximum.
  • Initialize frame_queue[i].yuv_frame_max_size when allocating yuv_frame and reset it on cleanup, and update it after trimming to the real decoded size.
  • Extend v4l2_frame_buff_t with yuv_frame_max_size to track the maximum allocated decoded YUV buffer per frame.
libcam/libcam_v4l2core/jpeg_decoder.c
libcam/libcam_v4l2core/jpeg_decoder.h
libcam/libcam_v4l2core/frame_decoder.c
libcam/libcam_v4l2core/gviewv4l2core.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In both jpeg_decode paths where you compute need and realloc(jpeg_ctx->tmp_frame, need), if realloc fails you still call m_av_image_copy_to_buffer with jpeg_ctx->pic_size smaller than need, which risks a buffer overrun; consider bailing out or falling back when need > jpeg_ctx->pic_size and realloc returns NULL.
  • When computing size_t need = (size_t)real_w * (size_t)real_h * 3 / 2 in decode_v4l2_frame, it may be safer to guard against integer overflow for very large resolutions (e.g., by checking the multiplication operands before performing the multiplication).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In both jpeg_decode paths where you compute `need` and `realloc(jpeg_ctx->tmp_frame, need)`, if `realloc` fails you still call `m_av_image_copy_to_buffer` with `jpeg_ctx->pic_size` smaller than `need`, which risks a buffer overrun; consider bailing out or falling back when `need > jpeg_ctx->pic_size` and `realloc` returns NULL.
- When computing `size_t need = (size_t)real_w * (size_t)real_h * 3 / 2` in `decode_v4l2_frame`, it may be safer to guard against integer overflow for very large resolutions (e.g., by checking the multiplication operands before performing the multiplication).

## Individual Comments

### Comment 1
<location path="libcam/libcam_v4l2core/jpeg_decoder.c" line_range="1586-1585" />
<code_context>
+            {
+                int dec_w = sw_frame->width;
+                int dec_h = sw_frame->height;
+                int need = getAvutil()->m_av_image_get_buffer_size(sw_frame->format, dec_w, dec_h, 1);
+                if (need > 0 && (size_t)need > (size_t)jpeg_ctx->pic_size) {
+                    uint8_t *p = realloc(jpeg_ctx->tmp_frame, (size_t)need);
+                    if (p) { jpeg_ctx->tmp_frame = p; jpeg_ctx->pic_size = need; }
+                }
+                jpeg_ctx->width = dec_w;
+                jpeg_ctx->height = dec_h;
+                getAvutil()->m_av_image_copy_to_buffer(jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
+                                                       (const uint8_t * const*) sw_frame->data, sw_frame->linesize,
+                                                       sw_frame->format, dec_w, dec_h, 1);
</code_context>
<issue_to_address>
**issue (bug_risk):** Potential buffer overflow if realloc fails but `need` exceeds `jpeg_ctx->pic_size`.

If `need > jpeg_ctx->pic_size` and `realloc` returns NULL, we still call `av_image_copy_to_buffer` with `dec_w/dec_h` but a buffer sized only for the old `pic_size`, so the copy can overflow. We should either return an error when `realloc` fails in this case, or constrain the copy to dimensions that fit within the existing `pic_size` to avoid undefined behavior.
</issue_to_address>

### Comment 2
<location path="libcam/libcam_v4l2core/frame_decoder.c" line_range="850-853" />
<code_context>
+
+                if (real_w > 0 && real_h > 0)
+                {
+                    size_t need = (size_t)real_w * (size_t)real_h * 3 / 2;
+                    if (need > 0 && need != frame->yuv_frame_max_size)
+                    {
+                        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
+                        if (p)
+                        {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Guard against size_t overflow when computing `need` and clarify trimming behavior.

The expression `(size_t)real_w * (size_t)real_h * 3 / 2` can overflow `size_t` if a malformed stream reports very large dimensions, yielding a too-small `need` passed to `realloc`. Please add a bound check (e.g., max width/height or verifying the product stays below a safe limit) and treat out-of-range sizes as an error. Also consider only shrinking when `need < yuv_frame_max_size` and growing when `need > yuv_frame_max_size` to avoid unnecessary reallocs if other code adjusts `yuv_frame_max_size` for other formats.

Suggested implementation:

```c
                if (real_w > 0 && real_h > 0)
                {
                    size_t pixels;
                    size_t need;

                    /* guard against overflow in pixels = real_w * real_h */
                    if ((size_t)real_w > 0 &&
                        (size_t)real_h > 0 &&
                        (size_t)real_w > SIZE_MAX / (size_t)real_h)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) decoded frame size %dx%d too large, rejecting\n",
                                    real_w, real_h);
                        return -1;
                    }

                    pixels = (size_t)real_w * (size_t)real_h;

                    /* guard against overflow in need = pixels * 3 / 2 */
                    if (pixels > SIZE_MAX / 3)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) decoded frame size %dx%d too large, rejecting\n",
                                    real_w, real_h);
                        return -1;
                    }

                    need = pixels * 3 / 2;
                    if (need == 0)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) computed zero-sized YUV buffer for %dx%d\n",
                                    real_w, real_h);
                        return -1;
                    }

                    /* Only reallocate when we actually need to grow or shrink */
                    if (need > frame->yuv_frame_max_size)
                    {
                        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
                        if (!p)
                        {
                            if (verbosity > 0)
                                fprintf(stderr,
                                        "V4L2_CORE: (jpeg decoder) realloc to %zu bytes failed (grow)\n",
                                        need);
                            return -1;
                        }
                        frame->yuv_frame = p;
                        frame->yuv_frame_max_size = need;
                    }
                    else if (need < frame->yuv_frame_max_size)
                    {
                        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
                        if (!p)
                        {
                            if (verbosity > 0)
                                fprintf(stderr,
                                        "V4L2_CORE: (jpeg decoder) realloc to %zu bytes failed (shrink)\n",
                                        need);
                            return -1;
                        }
                        frame->yuv_frame = p;
                        frame->yuv_frame_max_size = need;
                    }

```

1. Ensure `SIZE_MAX` is available by including `<limits.h>` at the top of `libcam/libcam_v4l2core/frame_decoder.c` if it is not already included.
2. The `return -1;` error paths assume this code is inside a function that returns an `int`-like status code; if this function has a different return type or uses `goto`-based error handling, replace the `return -1;` with the appropriate error handling (e.g., `goto error;`).
3. If shrinking the buffer is not desired in this code path (to preserve a larger `yuv_frame_max_size` set for other formats), you can remove the `else if (need < frame->yuv_frame_max_size)` block, leaving only the `need > frame->yuv_frame_max_size` grow case.
</issue_to_address>

### Comment 3
<location path="libcam/libcam_v4l2core/jpeg_decoder.c" line_range="1583" />
<code_context>
             if(verbosity > 3)
                 fprintf(stderr, "V4L2_CORE: (jpeg decoder) decode frame of size %i\n", ret);
+            if (ret >= 0)
+            {
+                int real_w = 0;
+                int real_h = 0;
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the duplicated buffer sizing and jpeg_ctx width/height update logic into a shared helper to simplify both decode paths and keep them in sync.

You can reduce the new complexity by factoring the duplicated “ensure buffer and update size” logic into a small helper and reusing it in both call sites.

For example, near the top of this file (or as `static` in the same translation unit):

```c
static void jpeg_ensure_tmp_frame_capacity(enum AVPixelFormat fmt,
                                           int dec_w, int dec_h)
{
    int need = getAvutil()->m_av_image_get_buffer_size(fmt, dec_w, dec_h, 1);
    if (need > 0 && (size_t)need > (size_t)jpeg_ctx->pic_size) {
        uint8_t *p = realloc(jpeg_ctx->tmp_frame, (size_t)need);
        if (p) {
            jpeg_ctx->tmp_frame = p;
            jpeg_ctx->pic_size = need;
        }
        /* if realloc fails, keep old buffer/size; behavior unchanged */
    }
    jpeg_ctx->width  = dec_w;
    jpeg_ctx->height = dec_h;
}
```

Then both blocks shrink to the buffer copy plus the helper call, keeping behavior identical:

**NV12 path:**

```c
{
    int dec_w = sw_frame->width;
    int dec_h = sw_frame->height;

    jpeg_ensure_tmp_frame_capacity(sw_frame->format, dec_w, dec_h);

    getAvutil()->m_av_image_copy_to_buffer(
        jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
        (const uint8_t * const*)sw_frame->data, sw_frame->linesize,
        sw_frame->format, dec_w, dec_h, 1
    );
}
if (sw_frame->format == AV_PIX_FMT_NV12) {
    nv12_to_yu12(out_buf, jpeg_ctx->tmp_frame, jpeg_ctx->width, jpeg_ctx->height);
    ...
}
```

**Non-NV12 path:**

```c
{
    int dec_w = codec_data->picture->width;
    int dec_h = codec_data->picture->height;
    enum AVPixelFormat dec_fmt = codec_data->picture->format;

    jpeg_ensure_tmp_frame_capacity(dec_fmt, dec_w, dec_h);

    getAvutil()->m_av_image_copy_to_buffer(
        jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
        (const uint8_t * const*)codec_data->picture->data,
        codec_data->picture->linesize,
        dec_fmt, dec_w, dec_h, 1
    );
}
```

This keeps all the new dynamic sizing behavior (including `jpeg_ctx->width/height` updates) but centralizes the logic so future changes only touch one place and both paths stay consistent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread libcam/libcam_v4l2core/jpeg_decoder.c
Comment on lines +850 to +853
size_t need = (size_t)real_w * (size_t)real_h * 3 / 2;
if (need > 0 && need != frame->yuv_frame_max_size)
{
uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Guard against size_t overflow when computing need and clarify trimming behavior.

The expression (size_t)real_w * (size_t)real_h * 3 / 2 can overflow size_t if a malformed stream reports very large dimensions, yielding a too-small need passed to realloc. Please add a bound check (e.g., max width/height or verifying the product stays below a safe limit) and treat out-of-range sizes as an error. Also consider only shrinking when need < yuv_frame_max_size and growing when need > yuv_frame_max_size to avoid unnecessary reallocs if other code adjusts yuv_frame_max_size for other formats.

Suggested implementation:

                if (real_w > 0 && real_h > 0)
                {
                    size_t pixels;
                    size_t need;

                    /* guard against overflow in pixels = real_w * real_h */
                    if ((size_t)real_w > 0 &&
                        (size_t)real_h > 0 &&
                        (size_t)real_w > SIZE_MAX / (size_t)real_h)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) decoded frame size %dx%d too large, rejecting\n",
                                    real_w, real_h);
                        return -1;
                    }

                    pixels = (size_t)real_w * (size_t)real_h;

                    /* guard against overflow in need = pixels * 3 / 2 */
                    if (pixels > SIZE_MAX / 3)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) decoded frame size %dx%d too large, rejecting\n",
                                    real_w, real_h);
                        return -1;
                    }

                    need = pixels * 3 / 2;
                    if (need == 0)
                    {
                        if (verbosity > 0)
                            fprintf(stderr,
                                    "V4L2_CORE: (jpeg decoder) computed zero-sized YUV buffer for %dx%d\n",
                                    real_w, real_h);
                        return -1;
                    }

                    /* Only reallocate when we actually need to grow or shrink */
                    if (need > frame->yuv_frame_max_size)
                    {
                        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
                        if (!p)
                        {
                            if (verbosity > 0)
                                fprintf(stderr,
                                        "V4L2_CORE: (jpeg decoder) realloc to %zu bytes failed (grow)\n",
                                        need);
                            return -1;
                        }
                        frame->yuv_frame = p;
                        frame->yuv_frame_max_size = need;
                    }
                    else if (need < frame->yuv_frame_max_size)
                    {
                        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
                        if (!p)
                        {
                            if (verbosity > 0)
                                fprintf(stderr,
                                        "V4L2_CORE: (jpeg decoder) realloc to %zu bytes failed (shrink)\n",
                                        need);
                            return -1;
                        }
                        frame->yuv_frame = p;
                        frame->yuv_frame_max_size = need;
                    }
  1. Ensure SIZE_MAX is available by including <limits.h> at the top of libcam/libcam_v4l2core/frame_decoder.c if it is not already included.
  2. The return -1; error paths assume this code is inside a function that returns an int-like status code; if this function has a different return type or uses goto-based error handling, replace the return -1; with the appropriate error handling (e.g., goto error;).
  3. If shrinking the buffer is not desired in this code path (to preserve a larger yuv_frame_max_size set for other formats), you can remove the else if (need < frame->yuv_frame_max_size) block, leaving only the need > frame->yuv_frame_max_size grow case.

getAvutil()->m_av_image_copy_to_buffer(jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
(const uint8_t * const*) sw_frame->data, sw_frame->linesize,
sw_frame->format, jpeg_ctx->width, jpeg_ctx->height, 1);
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the duplicated buffer sizing and jpeg_ctx width/height update logic into a shared helper to simplify both decode paths and keep them in sync.

You can reduce the new complexity by factoring the duplicated “ensure buffer and update size” logic into a small helper and reusing it in both call sites.

For example, near the top of this file (or as static in the same translation unit):

static void jpeg_ensure_tmp_frame_capacity(enum AVPixelFormat fmt,
                                           int dec_w, int dec_h)
{
    int need = getAvutil()->m_av_image_get_buffer_size(fmt, dec_w, dec_h, 1);
    if (need > 0 && (size_t)need > (size_t)jpeg_ctx->pic_size) {
        uint8_t *p = realloc(jpeg_ctx->tmp_frame, (size_t)need);
        if (p) {
            jpeg_ctx->tmp_frame = p;
            jpeg_ctx->pic_size = need;
        }
        /* if realloc fails, keep old buffer/size; behavior unchanged */
    }
    jpeg_ctx->width  = dec_w;
    jpeg_ctx->height = dec_h;
}

Then both blocks shrink to the buffer copy plus the helper call, keeping behavior identical:

NV12 path:

{
    int dec_w = sw_frame->width;
    int dec_h = sw_frame->height;

    jpeg_ensure_tmp_frame_capacity(sw_frame->format, dec_w, dec_h);

    getAvutil()->m_av_image_copy_to_buffer(
        jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
        (const uint8_t * const*)sw_frame->data, sw_frame->linesize,
        sw_frame->format, dec_w, dec_h, 1
    );
}
if (sw_frame->format == AV_PIX_FMT_NV12) {
    nv12_to_yu12(out_buf, jpeg_ctx->tmp_frame, jpeg_ctx->width, jpeg_ctx->height);
    ...
}

Non-NV12 path:

{
    int dec_w = codec_data->picture->width;
    int dec_h = codec_data->picture->height;
    enum AVPixelFormat dec_fmt = codec_data->picture->format;

    jpeg_ensure_tmp_frame_capacity(dec_fmt, dec_w, dec_h);

    getAvutil()->m_av_image_copy_to_buffer(
        jpeg_ctx->tmp_frame, jpeg_ctx->pic_size,
        (const uint8_t * const*)codec_data->picture->data,
        codec_data->picture->linesize,
        dec_fmt, dec_w, dec_h, 1
    );
}

This keeps all the new dynamic sizing behavior (including jpeg_ctx->width/height updates) but centralizes the logic so future changes only touch one place and both paths stay consistent.

Do not preset decoder width/height before avcodec_open2(); let FFmpeg
parse the real size from the JPEG SOF header. Copy decoded frames using
the actual sw_frame/picture dimensions and realloc buffers when needed.
Add jpeg_get_decoded_size() and trim yuv_frame to the real decoded size.

不在 avcodec_open2() 前预设解码器宽高,改由 FFmpeg 从 JPEG SOF 头解析
真实尺寸。按实际解码帧尺寸拷贝并在需要时 realloc 缓冲。新增
jpeg_get_decoded_size(),并在首帧后将 yuv_frame 收紧到真实解码尺寸。

Log: 修复MJPG解码尺寸与协商分辨率不一致导致的解码失败和画面错位
PMS: BUG-372317
Influence: 修复相机协商与实际分辨率不一致导致的解码失败、画面错位及内存浪费。
@add-uos
add-uos force-pushed the fix-372317-fix-mjpg-decoded-size-mismatch branch from f04d3d0 to ec0c03e Compare August 18, 2026 02:12
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:90分

■ 【总体评价】

代码实现了MJPEG解码尺寸的动态适配,有效修复了内存越界问题
逻辑正确且未引入新安全漏洞,通过缓存大小避免了频繁内存分配,质量良好

■ 【详细分析】

  • 1.语法逻辑(完全正确)✓

decode_v4l2_frame 函数中正确使用 realloc 扩展 yuv_frame,并检查了返回值 p,在失败时保留了原 frame->yuv_frame 指针且返回错误码,避免了内存泄漏。jpeg_decode 函数中针对硬解和软解分支均实现了动态缓冲区调整,硬解失败时正确调用了 m_av_frame_free 释放资源。
潜在问题:decode_v4l2_framerealloc 失败直接返回 E_ALLOC_ERR,可能导致上层处理异常中断视频流,而非降级使用原缓冲区。
建议:考虑在 realloc 失败时记录警告并继续使用原有缓冲区(需确保后续拷贝不会越界,可能需要截断),或保持现状但确保上层有重试机制。

  • 2.代码质量(良好)✓

新增了 yuv_frame_max_size 字段用于记录缓冲区实际大小,并在 alloc_v4l2_framesclean_v4l2_frames 中正确进行了初始化和清理。新增的 jpeg_get_decoded_size 函数带有完整的 Doxygen 风格注释,参数检查严密。
潜在问题:jpeg_decode 中存在两处几乎相同的动态计算 needrealloc 的代码块,存在一定重复。
建议:可考虑提取一个内部静态函数如 ensure_tmp_frame_capacity(int need) 来减少代码重复。

  • 3.代码性能(良好)✓

通过引入 yuv_frame_max_size 和复用已有的 pic_size,仅在解码分辨率发生变化且大于现有缓冲区时才触发 realloc,避免了每一帧都进行内存重分配,性能开销可控。
潜在问题:无
建议:无

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 1 个,持平 0 个
本次修改修复了原有的因实际解码尺寸与协商尺寸不一致导致的缓冲区溢出漏洞,未引入任何新的安全风险。所有内存分配均进行了大小校验和失败处理。

  • 安全漏洞1(【高危】缓冲区溢出):[已修复] 在 [decode_v4l2_frame/jpeg_decode] 中,原代码假设解码尺寸固定,当实际 MJPEG 流分辨率变大时,导致写入超出 yuv_frametmp_frame 分配的边界。本次修改通过动态检测并 realloc 修复了该问题。 ——非常重要

  • 建议:继续保持对外部输入(解码器返回的宽高)的边界检查,防止异常分辨率导致过大的内存分配。

■ 【改进建议代码示例】

// 在 frame_decoder.c 的 decode_v4l2_frame 函数中,增加整数溢出保护
if (real_w > 0 && real_h > 0)
{
    // 检查乘法溢出
    if ((size_t)real_w > (SIZE_MAX / (size_t)real_h) || 
        ((size_t)real_w * (size_t)real_h) > (SIZE_MAX / 3 * 2)) {
        if(verbosity > 0)
            fprintf(stderr, "V4L2_CORE: (jpeg decoder) integer overflow for size %dx%d\n", real_w, real_h);
        return E_ALLOC_ERR;
    }
    
    size_t need = (size_t)real_w * (size_t)real_h * 3 / 2;
    if (need > 0 && need != frame->yuv_frame_max_size)
    {
        uint8_t *p = (uint8_t *)realloc(frame->yuv_frame, need);
        if (p)
        {
            frame->yuv_frame = p;
            frame->yuv_frame_max_size = need;
            if(verbosity > 0)
                fprintf(stderr, "V4L2_CORE: (jpeg decoder) yuv_frame trimmed %dx%d (%zu bytes)\n",
                        real_w, real_h, need);
        }
        else
        {
            if(verbosity > 0)
                fprintf(stderr, "V4L2_CORE: (jpeg decoder) realloc failed for yuv_frame, keep old size\n");
            return E_ALLOC_ERR;
        }
    }
}

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@add-uos

add-uos commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot
deepin-bot Bot merged commit e03bbbf into linuxdeepin:master Aug 18, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants