Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions libcam/libcam_v4l2core/frame_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ int alloc_v4l2_frames(v4l2_dev_t *vd)
/*frame queue*/
for(i=0; i<vd->frame_queue_size; ++i)
{
vd->frame_queue[i].yuv_frame_max_size = (size_t) framesizeIn;
vd->frame_queue[i].yuv_frame = calloc((size_t) framesizeIn, sizeof(uint8_t));
if(vd->frame_queue[i].yuv_frame == NULL)
{
Expand Down Expand Up @@ -346,6 +347,7 @@ void clean_v4l2_frames(v4l2_dev_t *vd)
free(vd->frame_queue[i].yuv_frame);
vd->frame_queue[i].yuv_frame = NULL;
}
vd->frame_queue[i].yuv_frame_max_size = 0;
}

if(vd->h264_last_IDR)
Expand Down Expand Up @@ -828,6 +830,44 @@ int decode_v4l2_frame(v4l2_dev_t *vd, v4l2_frame_buff_t *frame)
//}
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;
if (jpeg_get_decoded_size(&real_w, &real_h) == 0 &&
real_w > 0 && real_h > 0 &&
(real_w != frame->width || real_h != frame->height))
{
if(verbosity > 0)
fprintf(stderr, "V4L2_CORE: (jpeg decoder) frame size %dx%d != negotiated %dx%d, using real size\n",
real_w, real_h, frame->width, frame->height);
frame->width = real_w;
frame->height = real_h;
}

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);
Comment on lines +850 to +853

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.

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;
}
}
}
}
ret = E_OK;
break;

Expand Down
1 change: 1 addition & 0 deletions libcam/libcam_v4l2core/gviewv4l2core.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ typedef struct _v4l2_frame_buff_t {
uint64_t timestamp; // captured frame timestamp

uint8_t *raw_frame; // pointer to raw frame
size_t yuv_frame_max_size; //maximum size for decoded yuv frame (bytes); MJPG is trimmed to real decoded size after 1st frame
uint8_t *yuv_frame; // pointer to decoded yuv frame
uint8_t *h264_frame; // pointer to regular or demultiplexed h264 frame
uint8_t *tmp_buffer; //temporary buffer used in decoding
Expand Down
69 changes: 61 additions & 8 deletions libcam/libcam_v4l2core/jpeg_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -1430,8 +1430,6 @@ int jpeg_init_decoder(int width, int height)
}

codec_data->context->pix_fmt = AV_PIX_FMT_YUV422P;
codec_data->context->width = width;
codec_data->context->height = height;
//jpeg_ctx->context->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);

// Initialize hardware device context (VA-API)
Expand Down Expand Up @@ -1582,9 +1580,26 @@ int jpeg_decode(uint8_t *out_buf, uint8_t *in_buf, int size)
printf(" - %s\n", getAvutil()->m_av_get_pix_fmt_name((enum AVPixelFormat)AV_PIX_FMT_YUV420P));
}
#if LIBAVUTIL_VER_AT_LEAST(54,6)
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.

int dec_w = sw_frame->width;
int dec_h = sw_frame->height;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
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) {
fprintf(stderr, "V4L2_CORE: (jpeg decoder) realloc failed for tmp_frame\n");
getAvutil()->m_av_frame_free(&sw_frame);
return -1;
}
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);
}
if (sw_frame->format == AV_PIX_FMT_NV12) {
nv12_to_yu12(out_buf, jpeg_ctx->tmp_frame, jpeg_ctx->width, jpeg_ctx->height);
getAvutil()->m_av_frame_free(&sw_frame);
Expand All @@ -1609,9 +1624,26 @@ int jpeg_decode(uint8_t *out_buf, uint8_t *in_buf, int size)
}
decodeCount++;
}
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,
codec_data->context->pix_fmt, jpeg_ctx->width, jpeg_ctx->height, 1);
{
int dec_w = codec_data->picture->width;
int dec_h = codec_data->picture->height;
enum AVPixelFormat dec_fmt = codec_data->picture->format;
int need = getAvutil()->m_av_image_get_buffer_size(dec_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) {
fprintf(stderr, "V4L2_CORE: (jpeg decoder) realloc failed for tmp_frame\n");
return -1;
}
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*) codec_data->picture->data, codec_data->picture->linesize,
dec_fmt, dec_w, dec_h, 1);
}
#else
avpicture_layout((AVPicture *) codec_data->picture, codec_data->dec_ctx->pix_fmt,
jpeg_ctx->width, jpeg_ctx->height, jpeg_ctx->tmp_frame, jpeg_ctx->pic_size);
Expand Down Expand Up @@ -1640,6 +1672,27 @@ int jpeg_decode(uint8_t *out_buf, uint8_t *in_buf, int size)

}

/*
* get real (decoded) frame size
* args:
* out_w - pointer to receive real decoded width (may be NULL)
* out_h - pointer to receive real decoded height (may be NULL)
*
* asserts:
* none
*
* returns: 0 on success (valid size available); negative if not initialized
* or not decoded yet
*/
int jpeg_get_decoded_size(int *out_w, int *out_h)
{
if (jpeg_ctx == NULL || jpeg_ctx->width <= 0 || jpeg_ctx->height <= 0)
return -1;
if (out_w) *out_w = jpeg_ctx->width;
if (out_h) *out_h = jpeg_ctx->height;
return 0;
}

/*
* close (m)jpeg decoder context
* args:
Expand Down
10 changes: 10 additions & 0 deletions libcam/libcam_v4l2core/jpeg_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ int jpeg_init_decoder(int width, int height);
*/
int jpeg_decode(uint8_t *out_buf, uint8_t *in_buf, int size);

/*
* get real (decoded) frame size after jpeg_decode()
* args:
* out_w - pointer to receive real decoded width (may be NULL)
* out_h - pointer to receive real decoded height (may be NULL)
*
* returns: 0 - OK; negative - not initialized or not decoded yet
*/
int jpeg_get_decoded_size(int *out_w, int *out_h);

/*
* close (m)jpeg decoder context
* args:
Expand Down
Loading