From ec0c03e60c0f4f8880c3bbd176e484771b4ee25b Mon Sep 17 00:00:00 2001 From: zhanghongyuan Date: Mon, 17 Aug 2026 09:56:12 +0800 Subject: [PATCH] fix: use real MJPG decoded size instead of negotiated resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: 修复相机协商与实际分辨率不一致导致的解码失败、画面错位及内存浪费。 --- libcam/libcam_v4l2core/frame_decoder.c | 40 +++++++++++++++ libcam/libcam_v4l2core/gviewv4l2core.h | 1 + libcam/libcam_v4l2core/jpeg_decoder.c | 69 +++++++++++++++++++++++--- libcam/libcam_v4l2core/jpeg_decoder.h | 10 ++++ 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/libcam/libcam_v4l2core/frame_decoder.c b/libcam/libcam_v4l2core/frame_decoder.c index 63366fa88..a5845b13d 100644 --- a/libcam/libcam_v4l2core/frame_decoder.c +++ b/libcam/libcam_v4l2core/frame_decoder.c @@ -134,6 +134,7 @@ int alloc_v4l2_frames(v4l2_dev_t *vd) /*frame queue*/ for(i=0; iframe_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) { @@ -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) @@ -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); + 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; diff --git a/libcam/libcam_v4l2core/gviewv4l2core.h b/libcam/libcam_v4l2core/gviewv4l2core.h index 7d8c7098a..85a0640ae 100644 --- a/libcam/libcam_v4l2core/gviewv4l2core.h +++ b/libcam/libcam_v4l2core/gviewv4l2core.h @@ -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 diff --git a/libcam/libcam_v4l2core/jpeg_decoder.c b/libcam/libcam_v4l2core/jpeg_decoder.c index cf65efa68..e3c8b1ccc 100644 --- a/libcam/libcam_v4l2core/jpeg_decoder.c +++ b/libcam/libcam_v4l2core/jpeg_decoder.c @@ -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) @@ -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); + { + 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) { + 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); @@ -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); @@ -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: diff --git a/libcam/libcam_v4l2core/jpeg_decoder.h b/libcam/libcam_v4l2core/jpeg_decoder.h index f3fa81029..f9aa7cedc 100644 --- a/libcam/libcam_v4l2core/jpeg_decoder.h +++ b/libcam/libcam_v4l2core/jpeg_decoder.h @@ -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: