diff --git a/components/esp_driver_jpeg/include/driver/jpeg_decode.h b/components/esp_driver_jpeg/include/driver/jpeg_decode.h index 81a21099c8..924a404ba5 100644 --- a/components/esp_driver_jpeg/include/driver/jpeg_decode.h +++ b/components/esp_driver_jpeg/include/driver/jpeg_decode.h @@ -19,7 +19,7 @@ extern "C" { */ typedef struct { jpeg_dec_output_format_t output_format; /*!< JPEG decoder output format */ - jpeg_dec_rgb_element_order rgb_order; /*!< JPEG decoder output order */ + jpeg_dec_rgb_element_order_t rgb_order; /*!< JPEG decoder output order */ jpeg_yuv_rgb_conv_std_t conv_std; /*!< JPEG decoder yuv->rgb standard */ } jpeg_decode_cfg_t; @@ -39,6 +39,13 @@ typedef struct { uint32_t height; /*!< Number of pixels in the vertical direction */ } jpeg_decode_picture_info_t; +/** + * @brief JPEG decoder memory allocation config + */ +typedef struct { + jpeg_dec_buffer_alloc_direction_t buffer_direction; /*!< Buffer direction for jpeg decoder memory allocation */ +} jpeg_decode_memory_alloc_cfg_t; + /** * @brief Acquire a JPEG decode engine with the specified configuration. * @@ -82,19 +89,21 @@ esp_err_t jpeg_decoder_get_info(const uint8_t *bit_stream, uint32_t stream_size, * decoded image data is written to the `decode_outbuf` buffer, and the length of the output image data is * returned through the `out_size` pointer. * - * @note Please make sure that the content of `bit_stream` pointer cannot be modified until this function returns. + * @note 1.Please make sure that the content of `bit_stream` pointer cannot be modified until this function returns. + * 2.Please note that the output size of image is always the multiple of 16 depends on protocol of JPEG. * * @param[in] decoder_engine Handle of the JPEG decoder instance to use for processing. * @param[in] decode_cfg Config structure of decoder. * @param[in] bit_stream Pointer to the buffer containing the input JPEG image data. * @param[in] stream_size Size of the input JPEG image data in bytes. - * @param[out] decode_outbuf Pointer to the buffer that will receive the decoded image data. + * @param[in] decode_outbuf Pointer to the buffer that will receive the decoded image data. + * @param[in] outbuf_size The size of `decode_outbuf` * @param[out] out_size Pointer to a variable that will receive the length of the output image data. * @return * - ESP_OK: JPEG decoder process successfully. * - ESP_ERR_INVALID_ARG: JPEG decoder process failed because of invalid argument. */ -esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size); +esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t outbuf_size, uint32_t *out_size); /** * @brief Release resources used by a JPEG decoder instance. @@ -112,10 +121,12 @@ esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine); /** * @brief A helper function to allocate memory space for JPEG decoder. * - * @param size The size of memory to allocate. + * @param[in] size The size of memory to allocate. + * @param[in] mem_cfg Memory configuration for memory allocation + * @param[out] allocated_size Actual allocated buffer size. * @return Pointer to the allocated memory space, or NULL if allocation fails. */ -void * jpeg_alloc_decoder_mem(size_t size); +void *jpeg_alloc_decoder_mem(size_t size, jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size); #ifdef __cplusplus } diff --git a/components/esp_driver_jpeg/include/driver/jpeg_types.h b/components/esp_driver_jpeg/include/driver/jpeg_types.h index 0c9c72a7f6..fc5f241752 100644 --- a/components/esp_driver_jpeg/include/driver/jpeg_types.h +++ b/components/esp_driver_jpeg/include/driver/jpeg_types.h @@ -36,7 +36,15 @@ typedef enum { typedef enum { JPEG_DEC_RGB_ELEMENT_ORDER_BGR = COLOR_RGB_ELEMENT_ORDER_BGR, /*!< Output the color component in small endian */ JPEG_DEC_RGB_ELEMENT_ORDER_RGB = COLOR_RGB_ELEMENT_ORDER_RGB, /*!< Output the color component in big endian */ -} jpeg_dec_rgb_element_order; +} jpeg_dec_rgb_element_order_t; + +/** + * @brief Enumeration for jpeg decoder alloc buffer direction. + */ +typedef enum { + JPEG_DEC_ALLOC_INPUT_BUFFER = 0, /*!< Alloc the picture input buffer, (compressed format in decoder) */ + JPEG_DEC_ALLOC_OUTPUT_BUFFER = 1, /*!< Alloc the picture output buffer, (decompressed format in decoder) */ +} jpeg_dec_buffer_alloc_direction_t; /** * @brief Type of jpeg decoder handle diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c index 991cf11f5d..c7794eb06e 100644 --- a/components/esp_driver_jpeg/jpeg_decode.c +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -24,6 +24,7 @@ #include "soc/dma2d_channel.h" #include "soc/interrupts.h" #include "esp_dma_utils.h" +#include "esp_private/esp_cache_private.h" #if CONFIG_JPEG_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h // Set the maximum log level for this source file @@ -172,13 +173,12 @@ esp_err_t jpeg_decoder_get_info(const uint8_t *in_buf, uint32_t inbuf_len, jpeg_ return ESP_OK; } -esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size) +esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t outbuf_size, uint32_t *out_size) { ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null"); ESP_RETURN_ON_FALSE(decode_cfg, ESP_ERR_INVALID_ARG, TAG, "jpeg decode config is null"); ESP_RETURN_ON_FALSE(decode_outbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode picture buffer 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 decode bit stream is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer"); - ESP_RETURN_ON_FALSE(((uintptr_t)decode_outbuf % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer"); + ESP_RETURN_ON_FALSE(esp_dma_is_buffer_aligned(decode_outbuf, outbuf_size, ESP_DMA_BUF_LOCATION_PSRAM), ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf or out_buffer size is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer"); esp_err_t ret = ESP_OK; @@ -200,6 +200,9 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_ ESP_GOTO_ON_ERROR(jpeg_parse_header_info_to_hw(decoder_engine), err, TAG, "write header info to hw failed"); ESP_GOTO_ON_ERROR(jpeg_dec_config_dma_descriptor(decoder_engine), err, TAG, "config dma descriptor failed"); + *out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel; + ESP_GOTO_ON_FALSE((*out_size <= outbuf_size), ESP_ERR_INVALID_ARG, err, TAG, "Given buffer size % " PRId32 " is smaller than actual jpeg decode output size % " PRId32 "the hight and width of output picture size will be adjusted to 16 bytes aligned automatically", outbuf_size, *out_size); + dma2d_trans_config_t trans_desc = { .tx_channel_num = 1, .rx_channel_num = 1, @@ -212,10 +215,6 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_ ret = esp_cache_msync((void*)decoder_engine->header_info->buffer_offset, decoder_engine->header_info->buffer_left, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); assert(ret == ESP_OK); - // Before 2DDMA starts. invalid memory space of decoded buffer - ret = esp_cache_msync((void*)decoder_engine->decoded_buf, decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel, ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - assert(ret == ESP_OK); - ESP_GOTO_ON_ERROR(dma2d_enqueue(decoder_engine->dma2d_group_handle, &trans_desc, decoder_engine->trans_desc), err, TAG, "enqueue dma2d failed"); bool need_yield; // Blocking for JPEG decode transaction finishes. @@ -234,12 +233,12 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_ } if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) { + ret = esp_cache_msync((void*)decoder_engine->decoded_buf, outbuf_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + assert(ret == ESP_OK); break; } } - *out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel; - err: xSemaphoreGive(decoder_engine->codec_base->codec_mutex); if (decoder_engine->codec_base->pm_lock) { @@ -280,9 +279,23 @@ esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine) return ESP_OK; } -void * jpeg_alloc_decoder_mem(size_t size) +void *jpeg_alloc_decoder_mem(size_t size, jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size) { - return heap_caps_aligned_calloc(cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA), 1, size, JPEG_MEM_ALLOC_CAPS); + /* + Principle of buffer align. + For output buffer(for decoder is 2DDMA write to PSRAM), both address and size should be aligned according to cache invalidate. + FOr input buffer(for decoder is PSRAM write to 2DDMA), no restriction for any align (both cache writeback and requirement from 2DDMA). + */ + size_t cache_align = 0; + esp_cache_get_alignment(ESP_CACHE_MALLOC_FLAG_PSRAM, &cache_align); + if (mem_cfg->buffer_direction == JPEG_DEC_ALLOC_OUTPUT_BUFFER) { + size = ALIGN_UP(size, cache_align); + *allocated_size = size; + return heap_caps_aligned_calloc(cache_align, 1, size, MALLOC_CAP_SPIRAM); + } else { + *allocated_size = size; + return heap_caps_calloc(1, size, MALLOC_CAP_SPIRAM); + } } /**************************************************************** diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h index d24b91cba5..6a18f4dda6 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -93,7 +93,7 @@ struct jpeg_decoder_t { jpeg_dec_header_info_t *header_info; // Pointer to current picture information jpeg_down_sampling_type_t sample_method; // method of sampling the JPEG picture. jpeg_dec_output_format_t output_format; // picture output format. - jpeg_dec_rgb_element_order rgb_order; // RGB pixel order + jpeg_dec_rgb_element_order_t rgb_order; // RGB pixel order jpeg_yuv_rgb_conv_std_t conv_std; // YUV RGB conversion standard uint8_t pixel; // size per pixel QueueHandle_t evt_queue; // jpeg event from 2DDMA and JPEG engine diff --git a/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_decode.c b/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_decode.c index 74d62a2912..4a0be536b7 100644 --- a/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_decode.c +++ b/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_decode.c @@ -52,12 +52,22 @@ TEST_CASE("JPEG decode performance test for 1080*1920 YUV->RGB picture", "[jpeg] .output_format = JPEG_DECODE_OUT_FORMAT_RGB565, }; - uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1080 * 1920 * 3); + jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER, + }; + + jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER, + }; + + size_t rx_buffer_size; + uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1080 * 1920 * 3, &rx_mem_cfg, &rx_buffer_size); uint32_t out_size_1080p = 0; size_t bit_stream_length = (size_t)image_esp1080_jpg_end - (size_t)image_esp1080_jpg_start; - uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(bit_stream_length); + size_t tx_buffer_size; + uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(bit_stream_length, &tx_mem_cfg, &tx_buffer_size); // Copy bit stream to psram memcpy(tx_buf_1080p, image_esp1080_jpg_start, bit_stream_length); TEST_ESP_OK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle)); @@ -67,7 +77,7 @@ TEST_CASE("JPEG decode performance test for 1080*1920 YUV->RGB picture", "[jpeg] // Decode picture for 50 times, and get the average uint8_t cnt = 50; for (int i = 0; i < cnt; i++) { - TEST_ESP_OK(jpeg_decoder_process(jpgd_handle, &decode_cfg, tx_buf_1080p, bit_stream_length, rx_buf_1080p, &out_size_1080p)); + TEST_ESP_OK(jpeg_decoder_process(jpgd_handle, &decode_cfg, tx_buf_1080p, bit_stream_length, rx_buf_1080p, rx_buffer_size, &out_size_1080p)); } int64_t decode_time = ccomp_timer_stop(); diff --git a/docs/en/api-reference/peripherals/jpeg.rst b/docs/en/api-reference/peripherals/jpeg.rst index 4a66e68be4..4f1d29a227 100644 --- a/docs/en/api-reference/peripherals/jpeg.rst +++ b/docs/en/api-reference/peripherals/jpeg.rst @@ -67,8 +67,19 @@ Overall, You can take following code as reference, the code is going to decode a .rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR, }; - uint8_t *bit_stream = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, bit_stream_size, MALLOC_CAP_SPIRAM); - uint8_t *out_buf = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, 1920 * 1080 * 3, MALLOC_CAP_SPIRAM); // Sufficient space for output images. + size_t tx_buffer_size; + size_t rx_buffer_size; + + jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER, + }; + + jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER, + }; + + uint8_t *bit_stream = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size, &tx_mem_cfg, &tx_buffer_size); + uint8_t *out_buf = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1088 * 3, &rx_mem_cfg, &rx_buffer_size); jpeg_decode_picture_info_t header_info; ESP_ERROR_CHECK(jpeg_decoder_get_info(bit_stream, bit_stream_size, &header_info)); @@ -77,8 +88,9 @@ Overall, You can take following code as reference, the code is going to decode a .. note:: - Firstly, in above code, you should make sure the `bit_stream` and `out_buf` should be :c:macro:`JPEG_BUFFER_MALLOC_ALIGN_VALUE` byte aligned. + Firstly, in above code, you should make sure the `bit_stream` and `out_buf` should be aligned by certain rules. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you malloc a buffer which is aligned in both size and address. Secondly, the content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns. + Thirdly, the width and hight of output picture would be 16 bytes aligned if original picture is formatted by YUV420 or YUV422. For example, if the input picture is 1080*1920, the output picture will be 1088*1920. That is the restriction of jpeg protocol. Please provide sufficient output buffer memory. Thread Safety ^^^^^^^^^^^^^ diff --git a/examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c b/examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c index b5eb6d126f..6d467f337e 100644 --- a/examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c +++ b/examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c @@ -88,6 +88,14 @@ void app_main(void) .output_format = JPEG_DECODE_OUT_FORMAT_GRAY, }; + jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER, + }; + + jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = { + .buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER, + }; + FILE *file_jpg_1080p = fopen(jpg_file_1080, "rb"); ESP_LOGI(TAG, "jpg_file_1080:%s", jpg_file_1080); if (file_jpg_1080p == NULL) { @@ -98,7 +106,8 @@ void app_main(void) fseek(file_jpg_1080p, 0, SEEK_END); int jpeg_size_1080p = ftell(file_jpg_1080p); fseek(file_jpg_1080p, 0, SEEK_SET); - uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_1080p); + size_t tx_buffer_size_1080p = 0; + uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_1080p, &tx_mem_cfg, &tx_buffer_size_1080p); if (tx_buf_1080p == NULL) { ESP_LOGE(TAG, "alloc 1080p tx buffer error"); return; @@ -115,7 +124,8 @@ void app_main(void) fseek(file_jpg_720p, 0, SEEK_END); int jpeg_size_720p = ftell(file_jpg_720p); fseek(file_jpg_720p, 0, SEEK_SET); - uint8_t *tx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_720p); + size_t tx_buffer_size_720p = 0; + uint8_t *tx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_720p, &tx_mem_cfg, &tx_buffer_size_720p); if (tx_buf_720p == NULL) { ESP_LOGE(TAG, "alloc 720p tx buffer error"); return; @@ -123,8 +133,10 @@ void app_main(void) fread(tx_buf_720p, 1, jpeg_size_720p, file_jpg_720p); fclose(file_jpg_720p); - uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1080 * 3); - uint8_t *rx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(720 * 1280); + size_t rx_buffer_size_1080p = 0; + size_t rx_buffer_size_720p = 0; + uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1088 * 3, &rx_mem_cfg, &rx_buffer_size_1080p); + uint8_t *rx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(720 * 1280, &rx_mem_cfg, &rx_buffer_size_720p); if (rx_buf_1080p == NULL) { ESP_LOGE(TAG, "alloc 1080p rx buffer error"); return; @@ -141,8 +153,8 @@ void app_main(void) uint32_t out_size_1080p = 0; uint32_t out_size_720p = 0; - ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb, tx_buf_1080p, jpeg_size_1080p, rx_buf_1080p, &out_size_1080p)); - ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_gray, tx_buf_720p, jpeg_size_720p, rx_buf_720p, &out_size_720p)); + ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb, tx_buf_1080p, jpeg_size_1080p, rx_buf_1080p, rx_buffer_size_1080p, &out_size_1080p)); + ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_gray, tx_buf_720p, jpeg_size_720p, rx_buf_720p, rx_buffer_size_720p, &out_size_720p)); // Write two pictures. FILE *file_rgb_1080p = fopen(raw_file_1080, "wb");