From d2498485aabe11768211a9b1ab415f701d7de9da Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Fri, 27 Feb 2026 10:59:01 +0800 Subject: [PATCH 1/2] feat(jpeg): Add config for reversing input pixel format in jpeg encoder Also add format check on both encoder and decoder Closes https://github.com/espressif/esp-idf/issues/18262 --- .../include/driver/jpeg_encode.h | 1 + components/esp_driver_jpeg/jpeg_decode.c | 11 ++++++ components/esp_driver_jpeg/jpeg_encode.c | 34 ++++++++++++++++--- docs/en/api-reference/peripherals/jpeg.rst | 3 ++ docs/zh_CN/api-reference/peripherals/jpeg.rst | 3 ++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/components/esp_driver_jpeg/include/driver/jpeg_encode.h b/components/esp_driver_jpeg/include/driver/jpeg_encode.h index c782bd5ce2..c8c1d96b11 100644 --- a/components/esp_driver_jpeg/include/driver/jpeg_encode.h +++ b/components/esp_driver_jpeg/include/driver/jpeg_encode.h @@ -24,6 +24,7 @@ typedef struct { jpeg_enc_input_format_t src_type; /*!< Source type of raw image to be encoded, see `jpeg_enc_src_type_t` */ jpeg_down_sampling_type_t sub_sample; /*!< JPEG subsampling method */ uint32_t image_quality; /*!< JPEG compressing quality, value from 1-100, higher value means higher quality */ + bool pixel_reverse; /*!< Whether to reverse the input pixel order, for detailed pixel order please refer to TRM */ } jpeg_encode_cfg_t; /** diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c index 2673c3a307..d9cb7f87f4 100644 --- a/components/esp_driver_jpeg/jpeg_decode.c +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -628,6 +628,17 @@ static esp_err_t jpeg_color_space_support_check(jpeg_decoder_handle_t decoder_en } } #endif + if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_GRAY) { + if (decoder_engine->output_format != JPEG_DECODE_OUT_FORMAT_GRAY) { + ESP_LOGE(TAG, "Detected GRAY but want to convert to other format, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + } else { + if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_GRAY) { + ESP_LOGE(TAG, "Detected not GRAY but want to convert to GRAY, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + } return ESP_OK; } diff --git a/components/esp_driver_jpeg/jpeg_encode.c b/components/esp_driver_jpeg/jpeg_encode.c index 0a9b81683d..8805a7d541 100644 --- a/components/esp_driver_jpeg/jpeg_encode.c +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -40,6 +40,7 @@ static void s_cfg_desc(jpeg_encoder_handle_t encoder_engine, dma2d_descriptor_t static void s_jpeg_enc_config_picture_color_space(jpeg_encoder_handle_t encoder_engine); static void s_jpeg_enc_select_sample_mode(jpeg_encoder_handle_t encoder_engine); static void s_encoder_error_log_print(uint32_t status); +static esp_err_t jpeg_enc_validate_sub_sample(jpeg_enc_src_type_t color_space, jpeg_down_sampling_type_t sub_sample); static void jpeg_encoder_isr_handle_default(void *arg) { @@ -73,6 +74,31 @@ static esp_err_t s_jpeg_set_header_info(jpeg_encoder_handle_t encoder_engine) return ESP_OK; } +static esp_err_t jpeg_enc_validate_sub_sample(jpeg_enc_src_type_t color_space, jpeg_down_sampling_type_t sub_sample) +{ + if (color_space == JPEG_ENC_SRC_GRAY) { + if (sub_sample != JPEG_DOWN_SAMPLING_GRAY) { + ESP_LOGE(TAG, "Detected GRAY but want to convert to other format, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + } else if (sub_sample == JPEG_DOWN_SAMPLING_GRAY) { + ESP_LOGE(TAG, "Detected not GRAY but want to convert to GRAY, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + + if (color_space == JPEG_ENC_SRC_YUV422) { + if (sub_sample == JPEG_DOWN_SAMPLING_YUV444) { + ESP_LOGE(TAG, "Detected YUV422 but want to convert to YUV444, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + } else if (color_space == JPEG_ENC_SRC_YUV420 && sub_sample != JPEG_DOWN_SAMPLING_YUV420) { + ESP_LOGE(TAG, "Detected YUV420 but want to convert to YUV422/YUV444, which is not supported"); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + esp_err_t jpeg_new_encoder_engine(const jpeg_encode_engine_cfg_t *enc_eng_cfg, jpeg_encoder_handle_t *ret_encoder) { #if CONFIG_JPEG_ENABLE_DEBUG_LOG @@ -142,9 +168,6 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ ESP_RETURN_ON_FALSE(encode_inbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg encode picture buffer is null"); ESP_RETURN_ON_FALSE(out_size, ESP_ERR_INVALID_ARG, TAG, "jpeg encode picture out_size is null"); ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg encode bit stream is not aligned, please use jpeg_alloc_encoder_mem to malloc your buffer"); - if (encode_cfg->src_type == JPEG_ENCODE_IN_FORMAT_YUV422) { - ESP_RETURN_ON_FALSE(encode_cfg->sub_sample == JPEG_DOWN_SAMPLING_YUV422, ESP_ERR_INVALID_ARG, TAG, "Sub sampling is not supported under this source type"); - } esp_err_t ret = ESP_OK; @@ -198,6 +221,9 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ ret = ESP_ERR_NOT_SUPPORTED; goto err2; } + + ESP_GOTO_ON_ERROR(jpeg_enc_validate_sub_sample(encoder_engine->color_space, encode_cfg->sub_sample), err2, TAG, "format and subsampling check failed"); + encoder_engine->header_info->sub_sample = encode_cfg->sub_sample; encoder_engine->header_info->quality = encode_cfg->image_quality; encoder_engine->header_info->origin_h = encode_cfg->width; @@ -208,7 +234,7 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ s_jpeg_enc_select_sample_mode(encoder_engine); jpeg_ll_set_picture_height(hal->dev, encoder_engine->header_info->origin_v); jpeg_ll_set_picture_width(hal->dev, encoder_engine->header_info->origin_h); - jpeg_ll_pixel_reverse(hal->dev, false); + jpeg_ll_pixel_reverse(hal->dev, encode_cfg->pixel_reverse); jpeg_ll_add_tail(hal->dev, true); jpeg_ll_enable_ff_check(hal->dev, true); jpeg_ll_set_qnr_presition(hal->dev, 0); diff --git a/docs/en/api-reference/peripherals/jpeg.rst b/docs/en/api-reference/peripherals/jpeg.rst index 96ca38f3f2..bc6660d7c3 100644 --- a/docs/en/api-reference/peripherals/jpeg.rst +++ b/docs/en/api-reference/peripherals/jpeg.rst @@ -10,6 +10,8 @@ JPEG is a commonly used method of lossy compression for digital images, particul JPEG codec on {IDF_TARGET_NAME} is an image codec, which is based on the JPEG baseline standard, for compressing and decompressing images to reduce the bandwidth required to transmit images or the space required to store images, making it possible to process large-resolution images. But please note, at one time, the codec engine can only work as either encoder or decoder. +For more hardware features of JPEG codec, please refer to the `JPEG codec <{IDF_TARGET_TRM_EN_URL}#jpegcodec>`__ section in {IDF_TARGET_NAME} Technical Reference Manual, for more details. + Functional Overview ------------------- @@ -187,6 +189,7 @@ Below is the example of code that encodes a 1080*1920 picture: .image_quality = 80, .width = 1920, .height = 1080, + .pixel_reverse = false, // Whether to reverse the pixel order of the input image, or pixel order detail please refer to technical reference manual }; uint8_t *raw_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p); diff --git a/docs/zh_CN/api-reference/peripherals/jpeg.rst b/docs/zh_CN/api-reference/peripherals/jpeg.rst index cd523aa224..86b02af29e 100644 --- a/docs/zh_CN/api-reference/peripherals/jpeg.rst +++ b/docs/zh_CN/api-reference/peripherals/jpeg.rst @@ -10,6 +10,8 @@ JPEG 常用于数字图像,尤其是数码摄影图像的有损压缩。压缩 {IDF_TARGET_NAME} 的 JPEG 编解码器是一种基于 JPEG 基线标准的图像编解码器,可以压缩和解压缩图像,从而降低传输图像所需的带宽或存储图像所需的空间,可以处理高分辨率的图像。但请注意,编解码器引擎不能同时作为编码器和解码器工作。 +关于更多 JPEG 编解码器的硬件特性,请参考 {IDF_TARGET_NAME} 技术参考手册中的 `JPEG 编解码器 <{IDF_TARGET_TRM_EN_URL}#jpegcodec>`__ 章节,了解更多详情。 + 功能概述 -------- @@ -187,6 +189,7 @@ JPEG 编码器引擎 .image_quality = 80, .width = 1920, .height = 1080, + .pixel_reverse = false, // 是否反转输入图像的像素顺序,或像素顺序细节请参考技术参考手册 }; uint8_t *raw_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p); From fe950466f48261352316eba91eea6caa4aecda57 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Wed, 4 Mar 2026 11:27:25 +0800 Subject: [PATCH 2/2] feat(jpeg): Fix some potential encoder issues esp_driver_jpeg allocates inadequate DMA memory size for descriptor in jpeg_encode.c Closes https://github.com/espressif/esp-idf/issues/18296 Potential integer underflow and uninitialized pointer dereference in jpeg_encode.c Closes https://github.com/espressif/esp-idf/issues/18297 Invalid DMA2D block RW mode configuration for 1D direction in jpeg_encode.c Closes https://github.com/espressif/esp-idf/issues/18298 --- components/esp_driver_jpeg/jpeg_encode.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/esp_driver_jpeg/jpeg_encode.c b/components/esp_driver_jpeg/jpeg_encode.c index 8805a7d541..ce564a8330 100644 --- a/components/esp_driver_jpeg/jpeg_encode.c +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -115,9 +115,9 @@ esp_err_t jpeg_new_encoder_engine(const jpeg_encode_engine_cfg_t *enc_eng_cfg, j uint32_t alignment = cache_line_size; size_t dma_desc_mem_size = JPEG_ALIGN_UP(sizeof(dma2d_descriptor_t), cache_line_size); - encoder_engine->rxlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, sizeof(dma2d_descriptor_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); + encoder_engine->rxlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(encoder_engine->rxlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg encoder rxlink"); - encoder_engine->txlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, sizeof(dma2d_descriptor_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); + encoder_engine->txlink = (dma2d_descriptor_t*)heap_caps_aligned_calloc(alignment, 1, dma_desc_mem_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | JPEG_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(encoder_engine->txlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg encoder txlink"); encoder_engine->dma_desc_size = dma_desc_mem_size; @@ -166,6 +166,7 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ ESP_RETURN_ON_FALSE(encoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg encode handle is null"); ESP_RETURN_ON_FALSE(encode_cfg, ESP_ERR_INVALID_ARG, TAG, "jpeg encode config is null"); ESP_RETURN_ON_FALSE(encode_inbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg encode picture buffer is null"); + ESP_RETURN_ON_FALSE(bit_stream, ESP_ERR_INVALID_ARG, TAG, "jpeg encode output buffer is null"); ESP_RETURN_ON_FALSE(out_size, ESP_ERR_INVALID_ARG, TAG, "jpeg encode picture out_size is null"); ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg encode bit stream is not aligned, please use jpeg_alloc_encoder_mem to malloc your buffer"); @@ -179,6 +180,7 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; uint8_t *raw_buffer = (uint8_t*)encode_inbuf; uint32_t compressed_size; + uint32_t payload_buf_size; xSemaphoreTake(encoder_engine->codec_base->codec_mutex, portMAX_DELAY); jpeg_ll_soft_rst(hal->dev); jpeg_ll_set_codec_mode(hal->dev, JPEG_CODEC_ENCODER); @@ -240,6 +242,8 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ jpeg_ll_set_qnr_presition(hal->dev, 0); ESP_GOTO_ON_ERROR(s_jpeg_set_header_info(encoder_engine), err2, TAG, "set header failed"); jpeg_hal_set_quantization_coefficient(hal, encoder_engine->header_info->m_quantization_tables[0], encoder_engine->header_info->m_quantization_tables[1]); + ESP_GOTO_ON_FALSE(outbuf_size > encoder_engine->header_info->header_len, ESP_ERR_INVALID_ARG, err2, TAG, "output buffer is too small for jpeg header"); + payload_buf_size = outbuf_size - encoder_engine->header_info->header_len; uint8_t sample_method_idx = 0; switch (encoder_engine->header_info->sub_sample) { @@ -266,11 +270,11 @@ esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_ ESP_GOTO_ON_FALSE((encoder_engine->header_info->header_len % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_STATE, err2, TAG, "The header is not cache line aligned, please check"); // 1D direction - memset(encoder_engine->rxlink, 0, sizeof(dma2d_descriptor_t)); - s_cfg_desc(encoder_engine, encoder_engine->rxlink, JPEG_DMA2D_2D_DISABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE, outbuf_size & JPEG_DMA2D_MAX_SIZE, 0, JPEG_DMA2D_EOF_NOT_LAST, 1, DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, outbuf_size >> JPEG_DMA2D_1D_HIGH_14BIT, 0, bit_stream + encoder_engine->header_info->header_len, NULL); + memset(encoder_engine->rxlink, 0, encoder_engine->dma_desc_size); + s_cfg_desc(encoder_engine, encoder_engine->rxlink, JPEG_DMA2D_2D_DISABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, payload_buf_size & JPEG_DMA2D_MAX_SIZE, 0, JPEG_DMA2D_EOF_NOT_LAST, 1, DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, payload_buf_size >> JPEG_DMA2D_1D_HIGH_14BIT, 0, bit_stream + encoder_engine->header_info->header_len, NULL); // 2D direction - memset(encoder_engine->txlink, 0, sizeof(dma2d_descriptor_t)); + memset(encoder_engine->txlink, 0, encoder_engine->dma_desc_size); s_cfg_desc(encoder_engine, encoder_engine->txlink, JPEG_DMA2D_2D_ENABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_MULTIPLE, dma_vb, dma_hb, JPEG_DMA2D_EOF_NOT_LAST, dma2d_desc_pixel_format_to_pbyte_value(encoder_engine->picture_format), DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, encoder_engine->header_info->origin_v, encoder_engine->header_info->origin_h, raw_buffer, NULL); ret = esp_cache_msync((void*)raw_buffer, inbuf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);