diff --git a/.codespellrc b/.codespellrc index d0a118902f..34d7b2674e 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] -skip = build,*.yuv,components/fatfs/src/*,alice.txt +skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb ignore-words-list = ser,dout,rsource,fram,inout write-changes = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9f8b82306..bfefa6992c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,8 @@ repos: .*_pb2.py| .*.pb-c.h| .*.pb-c.c| - .*.yuv + .*.yuv| + .*.rgb )$ - id: end-of-file-fixer exclude: *whitespace_excludes diff --git a/components/esp_driver_jpeg/CMakeLists.txt b/components/esp_driver_jpeg/CMakeLists.txt index deaf8ff247..9e6422c263 100644 --- a/components/esp_driver_jpeg/CMakeLists.txt +++ b/components/esp_driver_jpeg/CMakeLists.txt @@ -13,6 +13,12 @@ if(CONFIG_SOC_JPEG_CODEC_SUPPORTED) "jpeg_decode.c" ) endif() + if(CONFIG_SOC_JPEG_ENCODE_SUPPORTED) + list(APPEND srcs + "jpeg_emit_marker.c" + "jpeg_encode.c" + ) + endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/esp_driver_jpeg/include/driver/jpeg_decode.h b/components/esp_driver_jpeg/include/driver/jpeg_decode.h index 4153a1d0b4..0d2d818ac3 100644 --- a/components/esp_driver_jpeg/include/driver/jpeg_decode.h +++ b/components/esp_driver_jpeg/include/driver/jpeg_decode.h @@ -126,7 +126,7 @@ esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine); * @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, jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size); +void *jpeg_alloc_decoder_mem(size_t size, const jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size); #ifdef __cplusplus } diff --git a/components/esp_driver_jpeg/include/driver/jpeg_encode.h b/components/esp_driver_jpeg/include/driver/jpeg_encode.h new file mode 100644 index 0000000000..79e5535543 --- /dev/null +++ b/components/esp_driver_jpeg/include/driver/jpeg_encode.h @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "jpeg_types.h" +#include "hal/jpeg_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief JPEG encoder configure structure + */ +typedef struct { + uint32_t height; /*!< Number of pixels in the horizontal direction */ + uint32_t width; /*!< Number of pixels in the vertical direction */ + 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 */ +} jpeg_encode_cfg_t; + +/** + * @brief Configuration parameters for the JPEG encode engine. + */ +typedef struct { + int intr_priority; /*!< JPEG interrupt priority, if set to 0, driver will select the default priority (1,2,3). */ + int timeout_ms; /*!< JPEG timeout threshold for handling a picture, should larger than valid decode time in ms. For example, for 30fps decode, this value must larger than 34. -1 means wait forever */ +} jpeg_encode_engine_cfg_t; + +/** + * @brief JPEG encoder memory allocation config + */ +typedef struct { + jpeg_enc_buffer_alloc_direction_t buffer_direction; /*!< Buffer direction for jpeg decoder memory allocation */ +} jpeg_encode_memory_alloc_cfg_t; + +/** + * @brief Allocate JPEG encoder + * + * @param[in] enc_eng_cfg config for jpeg encoder + * @param[out] ret_encoder handle for jpeg encoder + * @return + * - ESP_OK: JPEG encoder initialized successfully. + * - ESP_ERR_INVALID_ARG: JPEG encoder initialization failed because of invalid argument. + * - ESP_ERR_NO_MEM: Create JPEG encoder failed because of out of memory. + */ +esp_err_t jpeg_new_encoder_engine(const jpeg_encode_engine_cfg_t *enc_eng_cfg, jpeg_encoder_handle_t *ret_encoder); + +/** + * @brief Process encoding of JPEG data using the specified encoder engine. + * + * This function processes the encoding of JPEG data using the provided encoder engine + * and configuration. It takes an input buffer containing the raw image data, performs + * encoding based on the configuration settings, and outputs the compressed bitstream. + * + * @param[in] encoder_engine Handle to the JPEG encoder engine to be used for encoding. + * @param[in] encode_cfg Pointer to the configuration structure for the JPEG encoding process. + * @param[in] encode_inbuf Pointer to the input buffer containing the raw image data. + * @param[in] inbuf_size Size of the input buffer in bytes. + * @param[in] encode_outbuf Pointer to the output buffer where the compressed bitstream will be stored. + * @param[in] outbuf_size The size of output buffer. + * @param[out] out_size Pointer to a variable where the size of the output bitstream will be stored. + * + * @return + * - ESP_OK: JPEG encoder process successfully. + * - ESP_ERR_INVALID_ARG: JPEG encoder process failed because of invalid argument. + * - ESP_ERR_TIMEOUT: JPEG encoder process timeout. + */ +esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_encode_cfg_t *encode_cfg, const uint8_t *encode_inbuf, uint32_t inbuf_size, uint8_t *encode_outbuf, uint32_t outbuf_size, uint32_t *out_size); + +/** + * @brief Release resources used by a JPEG encoder instance. + * + * This function releases the resources used by the specified JPEG encoder instance. The encoder instance is + * specified by the `encoder_engine` parameter. + * + * @param[in] encoder_engine Handle of the JPEG encoder instance to release resources for. + * @return + * - ESP_OK: Delete JPEG encoder successfully. + * - ESP_ERR_INVALID_ARG: Delete JPEG encoder failed because of invalid argument. + */ +esp_err_t jpeg_del_encoder_engine(jpeg_encoder_handle_t encoder_engine); + +/** + * @brief A helper function to allocate memory space for JPEG encoder. + * + * @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_encoder_mem(size_t size, const jpeg_encode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/include/driver/jpeg_types.h b/components/esp_driver_jpeg/include/driver/jpeg_types.h index fc5f241752..8a98db6062 100644 --- a/components/esp_driver_jpeg/include/driver/jpeg_types.h +++ b/components/esp_driver_jpeg/include/driver/jpeg_types.h @@ -46,11 +46,38 @@ typedef enum { JPEG_DEC_ALLOC_OUTPUT_BUFFER = 1, /*!< Alloc the picture output buffer, (decompressed format in decoder) */ } jpeg_dec_buffer_alloc_direction_t; +/** + * @brief Enumeration for jpeg input format. + */ +typedef enum { + JPEG_ENCODE_IN_FORMAT_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< input RGB888 format */ + JPEG_ENCODE_IN_FORMAT_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< input RGB565 format */ + JPEG_ENCODE_IN_FORMAT_GRAY = COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY8), /*!< input GRAY format */ +} jpeg_enc_input_format_t; + +/** + * @brief Enumeration for jpeg encoder alloc buffer direction. + */ +typedef enum { + JPEG_ENC_ALLOC_INPUT_BUFFER = 0, /*!< Alloc the picture input buffer, (decompressed format in encoder) */ + JPEG_ENC_ALLOC_OUTPUT_BUFFER = 1, /*!< Alloc the picture output buffer, (compressed format in encoder) */ +} jpeg_enc_buffer_alloc_direction_t; + /** * @brief Type of jpeg decoder handle */ typedef struct jpeg_decoder_t *jpeg_decoder_handle_t; +/** + * @brief Type of jpeg codec handle + */ +typedef struct jpeg_codec_t *jpeg_codec_handle_t; + +/** + * @brief Type of jpeg encoder handle + */ +typedef struct jpeg_encoder_t *jpeg_encoder_handle_t; + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_jpeg/jpeg_decode.c b/components/esp_driver_jpeg/jpeg_decode.c index 76c4f00d54..767c1d1693 100644 --- a/components/esp_driver_jpeg/jpeg_decode.c +++ b/components/esp_driver_jpeg/jpeg_decode.c @@ -36,8 +36,6 @@ static const char *TAG = "jpeg.decoder"; -#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) - static void s_decoder_error_log_print(uint32_t status); static esp_err_t jpeg_dec_config_dma_descriptor(jpeg_decoder_handle_t decoder_engine); static esp_err_t jpeg_parse_marker(jpeg_decoder_handle_t decoder_engine, const uint8_t *in_buf, uint32_t inbuf_len); @@ -77,7 +75,7 @@ esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, j uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); uint32_t alignment = cache_line_size; - size_t dma_desc_mem_size = ALIGN_UP(sizeof(dma2d_descriptor_t), cache_line_size); + size_t dma_desc_mem_size = JPEG_ALIGN_UP(sizeof(dma2d_descriptor_t), cache_line_size); decoder_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(decoder_engine->rxlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg decode rxlink"); @@ -279,7 +277,7 @@ esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine) return ESP_OK; } -void *jpeg_alloc_decoder_mem(size_t size, jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size) +void *jpeg_alloc_decoder_mem(size_t size, const jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size) { /* Principle of buffer align. @@ -289,7 +287,7 @@ void *jpeg_alloc_decoder_mem(size_t size, jpeg_decode_memory_alloc_cfg_t *mem_cf 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); + size = JPEG_ALIGN_UP(size, cache_align); *allocated_size = size; return heap_caps_aligned_calloc(cache_align, 1, size, MALLOC_CAP_SPIRAM); } else { diff --git a/components/esp_driver_jpeg/jpeg_emit_marker.c b/components/esp_driver_jpeg/jpeg_emit_marker.c new file mode 100644 index 0000000000..23bf3baed9 --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_emit_marker.c @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_log.h" +#include "jpeg_private.h" +#include "private/jpeg_param.h" +#include "private/jpeg_emit_marker.h" +#include "hal/jpeg_defs.h" +#include "esp_private/esp_cache_private.h" + +#define JPEG_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define JPEG_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +static void emit_byte(jpeg_enc_header_info_t *header_info, uint8_t i) +{ + header_info->header_buf[header_info->header_len] = i; + header_info->header_len = header_info->header_len + 1; +} + +static void emit_word(jpeg_enc_header_info_t *header_info, uint16_t i) +{ + emit_byte(header_info, i >> 8); + emit_byte(header_info, i & 0xFF); +} + +static void emit_marker(jpeg_enc_header_info_t *header_info, uint8_t marker) +{ + emit_byte(header_info, 0xFF); + emit_byte(header_info, marker); +} + +static void emit_dht(jpeg_enc_header_info_t *header_info, uint8_t *bits, uint8_t *val, int index, bool ac_flag) +{ + emit_marker(header_info, JPEG_M_DHT & 0xff); + int length = 0; + + for (int i = 0; i < 16; i++) { + length += bits[i]; + } + + emit_word(header_info, length + 2 + 1 + 16); + emit_byte(header_info, index + (ac_flag << 4)); + + for (int i = 0; i < 16; i++) { + emit_byte(header_info, bits[i]); + } + + for (int i = 0; i < length; i++) { + emit_byte(header_info, val[i]); + } +} + +static void compute_quant_table(uint32_t *quant_table, const uint32_t *basic_table, uint32_t quality) +{ + int scaling_factor = 0; + if (quality < 50) { + scaling_factor = 5000 / quality; + } else { + scaling_factor = 200 - quality * 2; + } + for (int i = 0; i < 64; i++) { + int temp = *basic_table++; + temp = (temp * scaling_factor + 50L) / 100L; + *quant_table++ = JPEG_MIN(JPEG_MAX(temp, 1), 255); + } +} + +esp_err_t emit_soi_marker(jpeg_enc_header_info_t *header_info) +{ + emit_marker(header_info, JPEG_M_SOI & 0xff); + return ESP_OK; +} + +esp_err_t emit_app0_marker(jpeg_enc_header_info_t *header_info) +{ + emit_marker(header_info, JPEG_M_APP0 & 0xff); + emit_word(header_info, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); + // 0x4A46494600 for JIF0 + emit_byte(header_info, 0x4A); + emit_byte(header_info, 0x46); + emit_byte(header_info, 0x49); + emit_byte(header_info, 0x46); + emit_byte(header_info, 0x00); + + // Major version + emit_byte(header_info, 1); + // Minor version + emit_byte(header_info, 1); + // Density unit (0: no unit, 1: inch 2: cm) + emit_byte(header_info, 0); + // X direction density + emit_word(header_info, 1); + // Y direction density + emit_word(header_info, 1); + // No thumbnail image + emit_byte(header_info, 0); + emit_byte(header_info, 0); + return ESP_OK; +} + +esp_err_t emit_dqt_marker(jpeg_enc_header_info_t *header_info) +{ + compute_quant_table(header_info->m_quantization_tables[0], luminance_quantization_table, header_info->quality); + compute_quant_table(header_info->m_quantization_tables[1], chrominance_quantization_table, header_info->quality); + for (int i = 0; i < ((header_info->num_components == 3) ? 2 : 1); i++) { + emit_marker(header_info, JPEG_M_DQT & 0xff); + emit_word(header_info, 64 + 1 + 2); + emit_byte(header_info, (i)); + + for (int j = 0; j < 64; j++) { + emit_byte(header_info, (uint8_t)(header_info->m_quantization_tables[i][zigzag_arr[j]])); + } + } + + return ESP_OK; +} + +esp_err_t emit_sof_marker(jpeg_enc_header_info_t *header_info) +{ + uint8_t comp_h_samp[3] = {0}; + uint8_t comp_v_samp[3] = {0}; + switch (header_info->sub_sample) { + case JPEG_DOWN_SAMPLING_YUV444: { + comp_h_samp[0] = 1; + comp_v_samp[0] = 1; + comp_h_samp[1] = 1; + comp_v_samp[1] = 1; + comp_h_samp[2] = 1; + comp_v_samp[2] = 1; + break; + } + case JPEG_DOWN_SAMPLING_YUV422: { + comp_h_samp[0] = 2; + comp_v_samp[0] = 1; + comp_h_samp[1] = 1; + comp_v_samp[1] = 1; + comp_h_samp[2] = 1; + comp_v_samp[2] = 1; + break; + } + case JPEG_DOWN_SAMPLING_YUV420: { + comp_h_samp[0] = 2; + comp_v_samp[0] = 2; + comp_h_samp[1] = 1; + comp_v_samp[1] = 1; + comp_h_samp[2] = 1; + comp_v_samp[2] = 1; + break; + } + case JPEG_DOWN_SAMPLING_GRAY: { + comp_h_samp[0] = 1; + comp_v_samp[0] = 1; + comp_h_samp[1] = 1; + comp_v_samp[1] = 1; + comp_h_samp[2] = 1; + comp_v_samp[2] = 1; + break; + } + default: + break; + } + + emit_marker(header_info, JPEG_M_SOF0 & 0xff); /* baseline */ + emit_word(header_info, 3 * header_info->num_components + 2 + 5 + 1); + emit_byte(header_info, 8); /* precision */ + emit_word(header_info, header_info->origin_v); + emit_word(header_info, header_info->origin_h); + emit_byte(header_info, header_info->num_components); + + for (int i = 0; i < header_info->num_components; i++) { + emit_byte(header_info, (i + 1)); + emit_byte(header_info, (comp_h_samp[i] << 4) + comp_v_samp[i]); + emit_byte(header_info, (i > 0)); + } + return ESP_OK; +} + +esp_err_t emit_dht_marker(jpeg_enc_header_info_t *header_info) +{ + uint8_t m_huff_bits[2][2][JPEG_HUFFMAN_BITS_LEN_TABLE_LEN] = {0}; + uint8_t m_huff_val[2][2][JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {0}; + memcpy(m_huff_bits[0][0], luminance_dc_coefficients, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_val[0][0], luminance_dc_values, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_bits[0][1], luminance_ac_coefficients, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_val[0][1], luminance_ac_values, JPEG_HUFFMAN_AC_VALUE_TABLE_LEN); + memcpy(m_huff_bits[1][0], chrominance_dc_coefficients, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_val[1][0], chrominance_dc_values, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_bits[1][1], chrominance_ac_coefficients, JPEG_HUFFMAN_DC_VALUE_TABLE_LEN); + memcpy(m_huff_val[1][1], chrominance_ac_values, JPEG_HUFFMAN_AC_VALUE_TABLE_LEN); + + emit_dht(header_info, m_huff_bits[0][0], m_huff_val[0][0], 0, false); + emit_dht(header_info, m_huff_bits[0][1], m_huff_val[0][1], 0, true); + + if (header_info->num_components == 3) { + emit_dht(header_info, m_huff_bits[1][0], m_huff_val[1][0], 1, false); + emit_dht(header_info, m_huff_bits[1][1], m_huff_val[1][1], 1, true); + } + + return ESP_OK; +} + +esp_err_t emit_sos_marker(jpeg_enc_header_info_t *header_info) +{ + emit_marker(header_info, JPEG_M_SOS & 0xff); + emit_word(header_info, 2 * header_info->num_components + 2 + 1 + 3); + emit_byte(header_info, header_info->num_components); + + for (int i = 0; i < header_info->num_components; i++) { + emit_byte(header_info, i + 1); + + if (i == 0) { + emit_byte(header_info, (0 << 4) + 0); + } else { + emit_byte(header_info, (1 << 4) + 1); + } + } + + emit_byte(header_info, 0); /* spectral selection */ + emit_byte(header_info, 63); + emit_byte(header_info, 0); + return ESP_OK; +} + +esp_err_t emit_com_marker(jpeg_enc_header_info_t *header_info) +{ + // Calculate how many bytes should be compensate to make it byte aligned. + size_t cache_align = 0; + esp_cache_get_alignment(ESP_CACHE_MALLOC_FLAG_PSRAM, &cache_align); + // compensate_size = aligned_size - SOS marker size(2 * header_info->num_components + 2 + 1 + 3 + 2) - COM marker size(4). + uint32_t compensate_size = ((header_info->header_len / cache_align + 1) * cache_align) - header_info->header_len - (2 * header_info->num_components + 2 + 1 + 3 + 2) - 4; + emit_marker(header_info, JPEG_M_COM & 0xff); + emit_word(header_info, compensate_size); + for (int i = 0; i < compensate_size; i++) { + emit_byte(header_info, 0); + } + + return ESP_OK; +} diff --git a/components/esp_driver_jpeg/jpeg_encode.c b/components/esp_driver_jpeg/jpeg_encode.c new file mode 100644 index 0000000000..2ac9ad1196 --- /dev/null +++ b/components/esp_driver_jpeg/jpeg_encode.c @@ -0,0 +1,491 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.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 +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "esp_log.h" +#include "esp_attr.h" +#include "hal/jpeg_ll.h" +#include "hal/cache_hal.h" +#include "hal/cache_ll.h" +#include "esp_private/dma2d.h" +#include "jpeg_private.h" +#include "driver/jpeg_encode.h" +#include "private/jpeg_param.h" +#include "private/jpeg_emit_marker.h" +#include "esp_check.h" +#include "esp_cache.h" +#include "esp_private/esp_cache_private.h" +#include "esp_intr_alloc.h" +#include "soc/dma2d_channel.h" + +static const char *TAG = "jpeg.encoder"; + +static esp_err_t s_jpeg_set_header_info(jpeg_encoder_handle_t encoder_engine); +static uint32_t s_dma_desc_get_len(dma2d_descriptor_t *dsc); +static bool s_jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data); +static bool s_jpeg_enc_transaction_on_job_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config); +static void s_cfg_desc(jpeg_encoder_handle_t encoder_engine, dma2d_descriptor_t *dsc, uint8_t en_2d, uint8_t mode, uint16_t vb, uint16_t hb, uint8_t eof, uint32_t pbyte, uint8_t owner, uint16_t va, uint16_t ha, uint8_t *buf, dma2d_descriptor_t *next_dsc); +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 void jpeg_encoder_isr_handle_default(void *arg) +{ + jpeg_encoder_handle_t encoder_engine = (jpeg_encoder_handle_t) arg; + portBASE_TYPE HPTaskAwoken = pdFALSE; + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + jpeg_enc_dma2d_evt_t s_event = { + .dma_evt = 0, + .encoder_status = 0, + }; + uint32_t value = jpeg_ll_get_intr_status(hal->dev); + jpeg_ll_clear_intr_mask(hal->dev, value); + s_event.encoder_status = value; + xQueueSendFromISR(encoder_engine->evt_queue, &s_event, &HPTaskAwoken); + + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static esp_err_t s_jpeg_set_header_info(jpeg_encoder_handle_t encoder_engine) +{ + encoder_engine->header_info->header_len = 0; + ESP_RETURN_ON_ERROR(emit_soi_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_app0_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_dqt_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_sof_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_dht_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_com_marker(encoder_engine->header_info), TAG, "marker emit failed"); + ESP_RETURN_ON_ERROR(emit_sos_marker(encoder_engine->header_info), TAG, "marker emit failed"); + 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 + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + jpeg_encoder_handle_t encoder_engine = NULL; + ESP_RETURN_ON_FALSE(enc_eng_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + encoder_engine = (jpeg_encoder_handle_t)heap_caps_calloc(1, sizeof(jpeg_encoder_t), MALLOC_CAP_8BIT); + ESP_RETURN_ON_FALSE(encoder_engine, ESP_ERR_NO_MEM, TAG, "no memory for jpeg encoder"); + + uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + 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); + 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); + 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; + + ESP_GOTO_ON_ERROR(jpeg_acquire_codec_handle(&encoder_engine->codec_base), err, TAG, "JPEG encoder acquires codec handle failed"); + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + encoder_engine->evt_queue = xQueueCreateWithCaps(2, sizeof(jpeg_enc_dma2d_evt_t), JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(encoder_engine->evt_queue, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue"); + + encoder_engine->timeout_tick = (enc_eng_cfg->timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(enc_eng_cfg->timeout_ms); + jpeg_ll_clear_intr_mask(hal->dev, JPEG_LL_ENCODER_EVENT_INTR); + + ESP_GOTO_ON_ERROR(jpeg_check_intr_priority(encoder_engine->codec_base, enc_eng_cfg->intr_priority), err, TAG, "set group interrupt priority failed"); + if (enc_eng_cfg->intr_priority) { + ESP_RETURN_ON_FALSE(1 << (enc_eng_cfg->intr_priority) & JPEG_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", enc_eng_cfg->intr_priority); + } + int isr_flags = JPEG_INTR_ALLOC_FLAG; + if (enc_eng_cfg->intr_priority) { + isr_flags |= 1 << (enc_eng_cfg->intr_priority); + } + + ret = jpeg_isr_register(encoder_engine->codec_base, jpeg_encoder_isr_handle_default, encoder_engine, JPEG_LL_ENCODER_EVENT_INTR, isr_flags, &encoder_engine->intr_handle); + ESP_GOTO_ON_ERROR(ret, err, TAG, "install jpeg decode interrupt failed"); + + dma2d_pool_config_t dma2d_group_config = { + .pool_id = 0, + }; + ESP_ERROR_CHECK(dma2d_acquire_pool(&dma2d_group_config, &encoder_engine->dma2d_group_handle)); + + encoder_engine->trans_desc = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(encoder_engine->trans_desc, ESP_ERR_NO_MEM, err, TAG, "No memory for dma2d descriptor"); + + encoder_engine->header_info = (jpeg_enc_header_info_t*)heap_caps_calloc(1, sizeof(jpeg_enc_header_info_t), JPEG_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(encoder_engine->header_info, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg header information structure"); + + *ret_encoder = encoder_engine; + return ESP_OK; +err: + if (encoder_engine) { + jpeg_del_encoder_engine(encoder_engine); + } + return ret; +} + +esp_err_t jpeg_encoder_process(jpeg_encoder_handle_t encoder_engine, const jpeg_encode_cfg_t *encode_cfg, const uint8_t *encode_inbuf, uint32_t inbuf_size, uint8_t *bit_stream, uint32_t outbuf_size, uint32_t *out_size) +{ + 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(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"); + + esp_err_t ret = ESP_OK; + + if (encoder_engine->codec_base->pm_lock) { + ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(encoder_engine->codec_base->pm_lock), TAG, "acquire pm_lock failed"); + } + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + uint8_t *raw_buffer = (uint8_t*)encode_inbuf; + uint32_t compressed_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); + /* Reset queue */ + xQueueReset(encoder_engine->evt_queue); + + jpeg_enc_format_hb_t best_hb_idx = 0; + + encoder_engine->picture_format = encode_cfg->src_type; + color_space_pixel_format_t picture_format; + picture_format.color_type_id = encoder_engine->picture_format; + + switch (encode_cfg->src_type) { + case JPEG_ENCODE_IN_FORMAT_RGB888: + encoder_engine->color_space = JPEG_ENC_SRC_RGB888; + best_hb_idx = JPEG_ENC_SRC_RGB888_HB; + break; + case JPEG_ENCODE_IN_FORMAT_RGB565: + encoder_engine->color_space = JPEG_ENC_SRC_RGB565; + best_hb_idx = JPEG_ENC_SRC_RGB565_HB; + break; + case JPEG_ENCODE_IN_FORMAT_GRAY: + encoder_engine->color_space = JPEG_ENC_SRC_GRAY; + best_hb_idx = JPEG_ENC_SRC_GRAY_HB; + break; + default: + ESP_LOGE(TAG, "wrong, we don't support encode from such format."); + ret = ESP_ERR_NOT_SUPPORTED; + goto err; + } + 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; + encoder_engine->header_info->origin_v = encode_cfg->height; + encoder_engine->header_info->header_buf = bit_stream; + + s_jpeg_enc_config_picture_color_space(encoder_engine); + 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_add_tail(hal->dev, true); + jpeg_ll_enable_ff_check(hal->dev, true); + jpeg_ll_set_qnr_presition(hal->dev, 0); + ESP_GOTO_ON_ERROR(s_jpeg_set_header_info(encoder_engine), err, 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]); + + uint32_t dma_hb = enc_hb_tbl[best_hb_idx][encoder_engine->header_info->sub_sample]; + uint32_t dma_vb = encoder_engine->mcuy; + + 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, err, 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); + + // 2D direction + memset(encoder_engine->txlink, 0, sizeof(dma2d_descriptor_t)); + 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(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, encoder_engine->header_info->origin_v * encoder_engine->header_info->origin_h * encoder_engine->bytes_per_pixel, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + assert(ret == ESP_OK); + + dma2d_trans_config_t trans_desc = { + .tx_channel_num = 1, + .rx_channel_num = 1, + .channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_TX_REORDER, + .user_config = encoder_engine, + .on_job_picked = s_jpeg_enc_transaction_on_job_picked, + }; + + ESP_GOTO_ON_ERROR(dma2d_enqueue(encoder_engine->dma2d_group_handle, &trans_desc, encoder_engine->trans_desc), err, TAG, "DMA2D enqueue failed"); + + while (1) { + jpeg_enc_dma2d_evt_t s_rcv_event; + BaseType_t ret_val = xQueueReceive(encoder_engine->evt_queue, &s_rcv_event, encoder_engine->timeout_tick); + ESP_GOTO_ON_FALSE(ret_val == pdTRUE, ESP_ERR_TIMEOUT, err, TAG, "jpeg-dma2d handle jpeg decode timeout, please check `timeout_ms`"); + + if (s_rcv_event.encoder_status != 0) { + s_encoder_error_log_print(s_rcv_event.encoder_status); + ret = ESP_ERR_INVALID_STATE; + goto err; + } + + if (s_rcv_event.dma_evt & JPEG_DMA2D_RX_EOF) { + compressed_size = s_dma_desc_get_len(encoder_engine->rxlink); + compressed_size = JPEG_ALIGN_UP(compressed_size, cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)); + ESP_GOTO_ON_ERROR(esp_cache_msync((void*)(bit_stream + encoder_engine->header_info->header_len), compressed_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C), err, TAG, "sync memory to cache failed"); + break; + } + } + + compressed_size += encoder_engine->header_info->header_len; + *out_size = compressed_size; + +err: + xSemaphoreGive(encoder_engine->codec_base->codec_mutex); + if (encoder_engine->codec_base->pm_lock) { + ESP_RETURN_ON_ERROR(esp_pm_lock_release(encoder_engine->codec_base->pm_lock), TAG, "release pm_lock failed"); + } + return ret; +} + +esp_err_t jpeg_del_encoder_engine(jpeg_encoder_handle_t encoder_engine) +{ + ESP_RETURN_ON_FALSE(encoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg encoder handle is null"); + ESP_RETURN_ON_ERROR(jpeg_release_codec_handle(encoder_engine->codec_base), TAG, "release codec failed"); + + if (encoder_engine) { + if (encoder_engine->rxlink) { + free(encoder_engine->rxlink); + } + if (encoder_engine->txlink) { + free(encoder_engine->txlink); + } + if (encoder_engine->header_info) { + free(encoder_engine->header_info); + } + if (encoder_engine->trans_desc) { + free(encoder_engine->trans_desc); + } + if (encoder_engine->evt_queue) { + vQueueDeleteWithCaps(encoder_engine->evt_queue); + } + if (encoder_engine->dma2d_group_handle) { + dma2d_release_pool(encoder_engine->dma2d_group_handle); + } + if (encoder_engine->intr_handle) { + jpeg_isr_deregister(encoder_engine->codec_base, encoder_engine->intr_handle); + } + free(encoder_engine); + } + return ESP_OK; +} + +void *jpeg_alloc_encoder_mem(size_t size, const jpeg_encode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size) +{ + /* + 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_ENC_ALLOC_OUTPUT_BUFFER) { + size = JPEG_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); + } +} + +/**************************************************************** + * DMA related functions + ****************************************************************/ + +static uint32_t s_dma_desc_get_len(dma2d_descriptor_t *dsc) +{ + uint32_t len = 0; + len |= dsc->ha_length; + len = len << 14; + len |= dsc->hb_length; + return len; +} + +static bool s_jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + jpeg_encoder_handle_t encoder_engine = (jpeg_encoder_handle_t) user_data; + portBASE_TYPE higher_priority_task_awoken = pdFALSE; + jpeg_enc_dma2d_evt_t s_event = { + .dma_evt = 0, + .encoder_status = 0, + }; + s_event.dma_evt = JPEG_DMA2D_RX_EOF; + xQueueSendFromISR(encoder_engine->evt_queue, &s_event, &higher_priority_task_awoken); + + return higher_priority_task_awoken; +} + +static void jpeg_enc_config_dma_trans_ability(jpeg_encoder_handle_t encoder_engine) +{ + // set transfer ability + dma2d_transfer_ability_t transfer_ability_config_tx = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + + dma2d_transfer_ability_t transfer_ability_config_rx = { + .data_burst_length = DMA2D_DATA_BURST_LENGTH_128, + .desc_burst_en = true, + .mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE, + }; + + switch (encoder_engine->header_info->sub_sample) { + case JPEG_DOWN_SAMPLING_YUV444: + transfer_ability_config_tx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8; + break; + case JPEG_DOWN_SAMPLING_YUV422: + transfer_ability_config_tx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_16; + break; + case JPEG_DOWN_SAMPLING_YUV420: + transfer_ability_config_tx.mb_size = DMA2D_MACRO_BLOCK_SIZE_16_16; + break; + case JPEG_DOWN_SAMPLING_GRAY: + transfer_ability_config_tx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8; + break; + default: + break; + } + + dma2d_set_transfer_ability(encoder_engine->dma2d_tx_channel, &transfer_ability_config_tx); + dma2d_set_transfer_ability(encoder_engine->dma2d_rx_channel, &transfer_ability_config_rx); +} + +static bool s_jpeg_enc_transaction_on_job_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + assert(channel_num == 2); + jpeg_encoder_handle_t encoder_engine = (jpeg_encoder_handle_t) user_config; + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + + uint32_t rx_idx = 0; + uint32_t tx_idx = 0; + if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_TX) { + rx_idx = 1; + tx_idx = 0; + } else { + rx_idx = 0; + tx_idx = 1; + } + dma2d_channel_handle_t tx_chan = dma2d_chans[tx_idx].chan; + dma2d_channel_handle_t rx_chan = dma2d_chans[rx_idx].chan; + + encoder_engine->dma2d_tx_channel = tx_chan; + encoder_engine->dma2d_rx_channel = rx_chan; + + // 2ddma connect + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_JPEG_ENCODER, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_JPEG_TX, + }; + dma2d_connect(tx_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_JPEG_RX; + dma2d_connect(rx_chan, &trig_periph); + + jpeg_enc_config_dma_trans_ability(encoder_engine); + + dma2d_csc_config_t tx_csc = { + .tx_csc_option = DMA2D_CSC_TX_NONE, + }; + dma2d_configure_color_space_conversion(tx_chan, &tx_csc); + + static dma2d_rx_event_callbacks_t jpeg_dec_cbs = { + .on_recv_eof = s_jpeg_rx_eof, + }; + + dma2d_register_rx_event_callbacks(rx_chan, &jpeg_dec_cbs, encoder_engine); + dma2d_set_desc_addr(tx_chan, (intptr_t)encoder_engine->txlink); + dma2d_set_desc_addr(rx_chan, (intptr_t)encoder_engine->rxlink); + dma2d_start(tx_chan); + dma2d_start(rx_chan); + jpeg_ll_enable_intr_mask(hal->dev, JPEG_LL_ENCODER_EVENT_INTR); + jpeg_ll_process_start(hal->dev); + + return false; +} + +static void s_cfg_desc(jpeg_encoder_handle_t encoder_engine, dma2d_descriptor_t *dsc, uint8_t en_2d, uint8_t mode, uint16_t vb, uint16_t hb, uint8_t eof, uint32_t pbyte, uint8_t owner, uint16_t va, uint16_t ha, uint8_t *buf, dma2d_descriptor_t *next_dsc) +{ + dsc->dma2d_en = en_2d; + dsc->mode = mode; + dsc->vb_size = vb; + dsc->hb_length = hb; + dsc->pbyte = pbyte; + dsc->suc_eof = eof; + dsc->owner = owner; + dsc->va_size = va; + dsc->ha_length = ha; + dsc->buffer = buf; + dsc->next = next_dsc; + esp_err_t ret = esp_cache_msync((void*)dsc, encoder_engine->dma_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + assert(ret == ESP_OK); +} + +static void s_jpeg_enc_config_picture_color_space(jpeg_encoder_handle_t encoder_engine) +{ + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + color_space_pixel_format_t picture_format; + jpeg_ll_config_picture_color_space(hal->dev, encoder_engine->color_space); + picture_format.color_type_id = encoder_engine->picture_format; + encoder_engine->bytes_per_pixel = color_hal_pixel_format_get_bit_depth(picture_format); + if (encoder_engine->color_space == JPEG_ENC_SRC_GRAY) { + encoder_engine->header_info->num_components = 1; + } else { + encoder_engine->header_info->num_components = 3; + } +} + +static void s_jpeg_enc_select_sample_mode(jpeg_encoder_handle_t encoder_engine) +{ + jpeg_hal_context_t *hal = &encoder_engine->codec_base->hal; + switch (encoder_engine->header_info->sub_sample) { + case JPEG_DOWN_SAMPLING_YUV444: + encoder_engine->mcux = 8; + encoder_engine->mcuy = 8; + break; + case JPEG_DOWN_SAMPLING_YUV422: + encoder_engine->mcux = 16; + encoder_engine->mcuy = 8; + break; + case JPEG_DOWN_SAMPLING_YUV420: + encoder_engine->mcux = 16; + encoder_engine->mcuy = 16; + break; + case JPEG_DOWN_SAMPLING_GRAY: + encoder_engine->mcux = 8; + encoder_engine->mcuy = 8; + break; + default: + break; + } + if (encoder_engine->header_info->sub_sample != JPEG_DOWN_SAMPLING_GRAY) { + // Not needed to call this function if 1 channel color down sampling + jpeg_ll_sample_mode_select(hal->dev, encoder_engine->header_info->sub_sample); + } +} + +static void s_encoder_error_log_print(uint32_t status) +{ + if (status & JPEG_LL_RLE_PARALLEL_ERR) { + ESP_LOGE(TAG, "Run length encoding error occurs"); + } + if (status & JPEG_LL_EN_FRAME_EOF_ERR) { + ESP_LOGE(TAG, "The number of data units obtained after decoding a frame of image is different from the number of data units calculated based on the image resolution configured by the software"); + } +} diff --git a/components/esp_driver_jpeg/jpeg_param.c b/components/esp_driver_jpeg/jpeg_param.c index c83af6f63f..59bf6bb547 100644 --- a/components/esp_driver_jpeg/jpeg_param.c +++ b/components/esp_driver_jpeg/jpeg_param.c @@ -45,3 +45,161 @@ const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX] = { {48, 32, 32, 48, 0}, {96, 0, 0, 0, 96}, }; + +/** + * @brief DMA2D best hb value table for JPEG compression. + * + * This two-dimensional array represents a Huffman encoding table for JPEG + * compression. It is used to decode the Huffman-coded symbols in the compressed + * data stream during the encoding process. + */ +const uint32_t enc_hb_tbl[JPEG_ENC_BEST_HB_MAX][JPEG_DOWN_SAMPLING_MAX] = { + {40, 32, 32, 0}, + {0, 64, 0, 0}, + {64, 64, 48, 0}, + {0, 0, 0, 128} +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.3 (for luminance DC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00' + */ +const uint8_t luminance_dc_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN] = { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.3 (for luminance DC coefficients), the set of values following this list is + * X'00 01 02 03 04 05 06 07 08 09 0A 0B' + */ +const uint8_t luminance_dc_values[JPEG_HUFFMAN_DC_VALUE_TABLE_LEN] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D' + */ +const uint8_t luminance_ac_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN] = { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC values) + */ +const uint8_t luminance_ac_values[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.4 (for chrominance DC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00' + */ +const uint8_t chrominance_dc_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN] = { + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.4 (for luminance DC coefficients), the set of values following this list is + * X'00 01 02 03 04 05 06 07 08 09 0A 0B' + */ +const uint8_t chrominance_dc_values[JPEG_HUFFMAN_DC_VALUE_TABLE_LEN] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77' + */ +const uint8_t chrominance_ac_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN] = { + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for chrominance AC values) + */ +const uint8_t chrominance_ac_values[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/** +* @brief This array stores the luminance quantization values for each block in an image.(JPEG standard sections K.1) +* The luminance_quantization_table array is of type uint32_t and has a size of 64 elements. +* +* These values are part of the quantization table used in the JPEG image compression standard. +* They are employed during the process of quantizing the discrete cosine transform (DCT) coefficients +* of an image's luminance component, allowing for lossy compression. +* +* The quantization process involves dividing the DCT coefficients by these values to reduce the precision of the data, +* which results in higher compression ratios but also introduces some loss of image quality. +* +* Each value in the array corresponds to a specific position in the 8x8 luminance quantization matrix used in JPEG compression. +* +* These specific values are critical for achieving a balance between compression efficiency and visual quality +* in JPEG image compression, and they have become a fundamental component of the standard. +*/ +const uint32_t luminance_quantization_table[JPEG_QUANTIZATION_TABLE_LEN] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +/** +* @brief This array stores the chrominance quantization values for each block in an image.(JPEG standard sections K.2) +* The chrominance_quantization_table array is of type uint32_t and has a size of 64 elements. +* +* These values are part of the quantization table used in the JPEG image compression standard. +* They are employed during the process of quantizing the discrete cosine transform (DCT) coefficients +* of an image's chrominance component, allowing for lossy compression. +* +* The quantization process involves dividing the DCT coefficients by these values to reduce the precision of the data, +* which results in higher compression ratios but also introduces some loss of image quality. +* +* Each value in the array corresponds to a specific position in the 8x8 chrominance quantization matrix used in JPEG compression. +* +* These specific values are critical for achieving a balance between compression efficiency and visual quality +* in JPEG image compression, and they have become a fundamental component of the standard. +*/ +const uint32_t chrominance_quantization_table[JPEG_QUANTIZATION_TABLE_LEN] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; diff --git a/components/esp_driver_jpeg/jpeg_private.h b/components/esp_driver_jpeg/jpeg_private.h index 18754a8244..2154de0f38 100644 --- a/components/esp_driver_jpeg/jpeg_private.h +++ b/components/esp_driver_jpeg/jpeg_private.h @@ -30,7 +30,10 @@ extern "C" { // JPEG encoder and decoder shares same interrupt ID. #define JPEG_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED) +#define JPEG_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + typedef struct jpeg_decoder_t jpeg_decoder_t; +typedef struct jpeg_encoder_t jpeg_encoder_t; typedef struct jpeg_codec_t jpeg_codec_t; typedef struct jpeg_codec_t *jpeg_codec_handle_t; @@ -122,6 +125,52 @@ typedef struct { uint32_t jpgd_status; // jpeg decoder status, (triggered from jpeg interrupt) } jpeg_dma2d_dec_evt_t; +typedef enum { + JPEG_ENC_SRC_RGB888_HB = 0, // Input RGB888 format + // TODO: Support encoder source format for yuv422 + // JPEG_ENC_SRC_YUV422_HB = 1, // Input YUV422 format + JPEG_ENC_SRC_RGB565_HB = 2, // Input RGB565 format + JPEG_ENC_SRC_GRAY_HB = 3, // Input GRAY format + JPEG_ENC_BEST_HB_MAX, +} jpeg_enc_format_hb_t; + +typedef struct { + jpeg_dma2d_evt_enum_t dma_evt; // jpeg-2ddma event, (triggered from 2ddma interrupt) + uint32_t encoder_status; // jpeg encoder status, (triggered from jpeg interrupt) +} jpeg_enc_dma2d_evt_t; + +typedef struct { + uint8_t *header_buf; // Pointer to the header of jpeg header buffer + uint32_t header_len; // Record for header length + uint32_t m_quantization_tables[2][JPEG_QUANTIZATION_TABLE_LEN]; // quantization tables + uint8_t num_components; // number of components + uint32_t origin_h; // horizontal of original picture + uint32_t origin_v; // vertical of original picture + uint32_t quality; // JPEG compressed quality. + jpeg_down_sampling_type_t sub_sample; // Picture sub-sampling method +} jpeg_enc_header_info_t; + +struct jpeg_encoder_t { + jpeg_codec_t *codec_base; // Pointer to jpeg codec hardware base + jpeg_enc_src_type_t color_space; // Picture source color space + jpeg_enc_input_format_t picture_format; // Source picture format + jpeg_enc_header_info_t *header_info; // Pointer to header buffer information + uint32_t bytes_per_pixel; // Bytes per pixel of source image format + uint8_t mcux; // the best value of minimum coding unit horizontal unit + uint8_t mcuy; // minimum coding unit vertical unit + jpeg_isr_handler_t *intr_handle; // jpeg encoder interrupt handler + TickType_t timeout_tick; // timeout value for jpeg decoder (in cpu tick). + QueueHandle_t evt_queue; // jpeg event from 2DDMA and JPEG engine + // dma_handles + dma2d_pool_handle_t dma2d_group_handle; // 2D-DMA group handle + dma2d_descriptor_t *rxlink; // Pointer to 2D-DMA rx descriptor + dma2d_descriptor_t *txlink; // Pointer to 2D-DMA tx descriptor + uint32_t dma_desc_size; // tx and rx linker alignment + dma2d_trans_t* trans_desc; // DMA2D transaction descriptor + dma2d_channel_handle_t dma2d_rx_channel; // DMA2D RX channel handle + dma2d_channel_handle_t dma2d_tx_channel; // DMA2D TX channel handle +}; + #define JPEG_DMA2D_2D_ENABLE (1) // DMA2D two dimension enable #define JPEG_DMA2D_2D_DISABLE (0) // DMA2D one dimension enable #define JPEG_DMA2D_MAX_SIZE (0x3fff) // DMA2D max size (low 14 bit) diff --git a/components/esp_driver_jpeg/private/jpeg_emit_marker.h b/components/esp_driver_jpeg/private/jpeg_emit_marker.h new file mode 100644 index 0000000000..b1460453c2 --- /dev/null +++ b/components/esp_driver_jpeg/private/jpeg_emit_marker.h @@ -0,0 +1,130 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_log.h" +#include "esp_attr.h" +#include "../jpeg_private.h" +#include "driver/jpeg_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Emit Start of Image (SOI) marker. + * + * This function emits the Start of Image (SOI) marker, which indicates the beginning of a JPEG image. + * It writes the SOI marker to the output buffer specified in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted SOI marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing SOI marker. + */ +esp_err_t emit_soi_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Application 0 (APP0) marker. + * + * This function emits the Application 0 (APP0) marker, which is used to provide information about the JFIF (JPEG File Interchange Format) + * or other application-specific data. It writes the APP0 marker and the corresponding data segment to the output buffer specified + * in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted APP0 marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing APP0 marker. + */ +esp_err_t emit_app0_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Define Quantization Table (DQT) marker. + * + * This function emits the Define Quantization Table (DQT) marker, which is used to specify the quantization table(s) + * used for encoding the image data. It writes the DQT marker and the corresponding quantization table(s) to the output buffer + * specified in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted DQT marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing DQT marker. + */ +esp_err_t emit_dqt_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Start of Frame (SOF) marker. + * + * This function emits the Start of Frame (SOF) marker, which is used to define the basic parameters of the image frame, + * such as the image dimensions and the number of color components. It writes the SOF marker and the corresponding + * frame header data to the output buffer specified in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted SOF marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing SOF marker. + */ +esp_err_t emit_sof_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Define Huffman Table (DHT) marker. + * + * This function emits the Define Huffman Table (DHT) marker, which is used to specify the Huffman coding tables + * used for encoding the image data. It writes the DHT marker and the corresponding Huffman coding table(s) to the + * output buffer specified in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted DHT marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing DHT marker. + */ +esp_err_t emit_dht_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Start of Scan (SOS) marker. + * + * This function emits the Start of Scan (SOS) marker, which is used to specify the image component layout and + * the Huffman coding tables used for encoding the image data. It writes the SOS marker and the corresponding scan + * header data to the output buffer specified in the `header_info` structure. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted SOS marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing SOS marker. + */ +esp_err_t emit_sos_marker(jpeg_enc_header_info_t *header_info); + +/** + * @brief Emit Comment (COM) marker. + * + * This function is used for adjust picture header size. Picture body follows alignment rules. So in header emit stage, + * We add bytes in COM sector to adjust picture header size. + * + * @param[in] header_info Pointer to the structure containing JPEG encoding header information. + * + * @return + * - ESP_OK: Successfully emitted SOS marker. + * - ESP_ERR_INVALID_ARG: Invalid argument passed. + * - ESP_FAIL: Error occurred while writing SOS marker. + */ +esp_err_t emit_com_marker(jpeg_enc_header_info_t *header_info); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_jpeg/private/jpeg_param.h b/components/esp_driver_jpeg/private/jpeg_param.h index 77a46be88f..4e1c2fc56c 100644 --- a/components/esp_driver_jpeg/private/jpeg_param.h +++ b/components/esp_driver_jpeg/private/jpeg_param.h @@ -38,6 +38,105 @@ extern const uint8_t zigzag_arr[64]; */ extern const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX]; +/** + * @brief DMA2D best hb value table for JPEG compression. + * + * This two-dimensional array represents a Huffman encoding table for JPEG + * compression. It is used to decode the Huffman-coded symbols in the compressed + * data stream during the encoding process. + */ +extern const uint32_t enc_hb_tbl[JPEG_ENC_BEST_HB_MAX][JPEG_DOWN_SAMPLING_MAX]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.3 (for luminance DC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00' + */ +extern const uint8_t luminance_dc_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.3 (for luminance DC coefficients), the set of values following this list is + * X'00 01 02 03 04 05 06 07 08 09 0A 0B' + */ +extern const uint8_t luminance_dc_values[JPEG_HUFFMAN_DC_VALUE_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D' + */ +extern const uint8_t luminance_ac_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC values) + */ +extern const uint8_t luminance_ac_values[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.4 (for chrominance DC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00' + */ +extern const uint8_t chrominance_dc_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.4 (for luminance DC coefficients), the set of values following this list is + * X'00 01 02 03 04 05 06 07 08 09 0A 0B' + */ +extern const uint8_t chrominance_dc_values[JPEG_HUFFMAN_DC_VALUE_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for luminance AC coefficients), the 16 bytes which specify the list of code lengths for the table are + * X'00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77' + */ +extern const uint8_t chrominance_ac_coefficients[JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; + +/** + * @brief Setup the standard Huffman tables (JPEG standard sections K.3.3) + * For table K.5 (for chrominance AC values) + */ +extern const uint8_t chrominance_ac_values[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; + +/** +* @brief This array stores the luminance quantization values for each block in an image.(JPEG standard sections K.1) +* The luminance_quantization_table array is of type uint32_t and has a size of 64 elements. +* +* These values are part of the quantization table used in the JPEG image compression standard. +* They are employed during the process of quantizing the discrete cosine transform (DCT) coefficients +* of an image's luminance component, allowing for lossy compression. +* +* The quantization process involves dividing the DCT coefficients by these values to reduce the precision of the data, +* which results in higher compression ratios but also introduces some loss of image quality. +* +* Each value in the array corresponds to a specific position in the 8x8 luminance quantization matrix used in JPEG compression. +* +* These specific values are critical for achieving a balance between compression efficiency and visual quality +* in JPEG image compression, and they have become a fundamental component of the standard. +*/ +extern const uint32_t luminance_quantization_table[JPEG_QUANTIZATION_TABLE_LEN]; + +/** +* @brief This array stores the chrominance quantization values for each block in an image.(JPEG standard sections K.2) +* The chrominance_quantization_table array is of type uint32_t and has a size of 64 elements. +* +* These values are part of the quantization table used in the JPEG image compression standard. +* They are employed during the process of quantizing the discrete cosine transform (DCT) coefficients +* of an image's chrominance component, allowing for lossy compression. +* +* The quantization process involves dividing the DCT coefficients by these values to reduce the precision of the data, +* which results in higher compression ratios but also introduces some loss of image quality. +* +* Each value in the array corresponds to a specific position in the 8x8 chrominance quantization matrix used in JPEG compression. +* +* These specific values are critical for achieving a balance between compression efficiency and visual quality +* in JPEG image compression, and they have become a fundamental component of the standard. +*/ +extern const uint32_t chrominance_quantization_table[JPEG_QUANTIZATION_TABLE_LEN]; + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/jpeg_ll.h b/components/hal/esp32p4/include/hal/jpeg_ll.h index f84f6d0a02..3eebfd7235 100644 --- a/components/hal/esp32p4/include/hal/jpeg_ll.h +++ b/components/hal/esp32p4/include/hal/jpeg_ll.h @@ -506,7 +506,7 @@ static inline void jpeg_ll_sample_mode_select(jpeg_dev_t *hw, jpeg_sample_mode_t sample_sel = 1; break; case JPEG_SAMPLE_MODE_YUV420: - sample_sel = 0; + sample_sel = 2; break; default: HAL_ASSERT(false); @@ -632,6 +632,28 @@ static inline uint32_t jpeg_ll_get_intr_status(jpeg_dev_t *hw) return hw->int_st.val; } +static inline void jpeg_ll_config_picture_color_space(jpeg_dev_t *hw, jpeg_enc_src_type_t color_space) +{ + uint8_t cs = 0; + switch (color_space) { + case JPEG_ENC_SRC_RGB888: + cs = 0; + break; + case JPEG_ENC_SRC_YUV422: + cs = 1; + break; + case JPEG_ENC_SRC_RGB565: + cs = 2; + break; + case JPEG_ENC_SRC_GRAY: + cs = 3; + break; + default: + abort(); + } + hw->config.color_space = cs; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/jpeg_hal.h b/components/hal/include/hal/jpeg_hal.h index a2181be541..ee307d76fa 100644 --- a/components/hal/include/hal/jpeg_hal.h +++ b/components/hal/include/hal/jpeg_hal.h @@ -103,6 +103,15 @@ typedef void (*jpeg_config_quantization_coefficient_t)(jpeg_soc_handle_t hw, uin */ extern jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX]; +/** + * Set the quantization coefficients for luminance and chrominance in the JPEG hardware accelerator context. + * + * @param hal Pointer to the JPEG hardware accelerator context. + * @param lqnr Pointer to an array of luminance quantization coefficients. + * @param cqnr Pointer to an array of chrominance quantization coefficients. + */ +void jpeg_hal_set_quantization_coefficient(jpeg_hal_context_t *hal, uint32_t *lqnr, uint32_t *cqnr); + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/jpeg_types.h b/components/hal/include/hal/jpeg_types.h index e3bae21d92..f81ff82df8 100644 --- a/components/hal/include/hal/jpeg_types.h +++ b/components/hal/include/hal/jpeg_types.h @@ -71,6 +71,16 @@ typedef enum { JPEG_DOWN_SAMPLING_MAX, /*!< Max value of sample enumeration */ } jpeg_down_sampling_type_t; +/** + * @brief JPEG encoder source formats. + */ +typedef enum { + JPEG_ENC_SRC_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< JPEG encoder source RGB888 */ + JPEG_ENC_SRC_YUV422 = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV422), /*!< JPEG encoder source YUV422 */ + JPEG_ENC_SRC_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< JPEG encoder source RGB565 */ + JPEG_ENC_SRC_GRAY = COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY8), /*!< JPEG encoder source GRAY */ +} jpeg_enc_src_type_t; + #ifdef __cplusplus } #endif diff --git a/components/hal/jpeg_hal.c b/components/hal/jpeg_hal.c index 2b0fdf8f52..b2705e1e7d 100644 --- a/components/hal/jpeg_hal.c +++ b/components/hal/jpeg_hal.c @@ -95,3 +95,14 @@ jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX] = { jpeg_ll_write_quantization_coefficient_t2, jpeg_ll_write_quantization_coefficient_t3, }; + +void jpeg_hal_set_quantization_coefficient(jpeg_hal_context_t *hal, uint32_t *lqnr, uint32_t *cqnr) +{ + jpeg_ll_set_access_qnr_ram_mode(hal->dev, 1); + jpeg_ll_luminance_qnr_table_id(hal->dev, 0); + jpeg_ll_chrominance_qnr_table_id(hal->dev, 1); + jpeg_ll_write_quantization_coefficient_t0(hal->dev, lqnr); + jpeg_ll_write_quantization_coefficient_t1(hal->dev, cqnr); + jpeg_ll_write_quantization_coefficient_t2(hal->dev, lqnr); + jpeg_ll_write_quantization_coefficient_t3(hal->dev, cqnr); +} diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 0721d69751..bb82cdd0fa 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1534,3 +1534,7 @@ config SOC_JPEG_CODEC_SUPPORTED config SOC_JPEG_DECODE_SUPPORTED bool default y + +config SOC_JPEG_ENCODE_SUPPORTED + bool + default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index c94f187ec7..0a6ea57ef6 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -625,4 +625,4 @@ /*--------------------------- JPEG --------------------------------*/ #define SOC_JPEG_CODEC_SUPPORTED (1) #define SOC_JPEG_DECODE_SUPPORTED (1) -// #define SOC_JPEG_ENCODE_SUPPORTED (1) // TODO: IDF-6512 +#define SOC_JPEG_ENCODE_SUPPORTED (1)