mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(jpeg_encoder): Add the basic support for jpeg encoder
This commit is contained in:
parent
b02a2eaf1a
commit
22ec65adef
@ -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
|
||||
|
@ -22,7 +22,8 @@ repos:
|
||||
.*_pb2.py|
|
||||
.*.pb-c.h|
|
||||
.*.pb-c.c|
|
||||
.*.yuv
|
||||
.*.yuv|
|
||||
.*.rgb
|
||||
)$
|
||||
- id: end-of-file-fixer
|
||||
exclude: *whitespace_excludes
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
}
|
||||
|
103
components/esp_driver_jpeg/include/driver/jpeg_encode.h
Normal file
103
components/esp_driver_jpeg/include/driver/jpeg_encode.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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
|
@ -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
|
||||
|
@ -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 {
|
||||
|
243
components/esp_driver_jpeg/jpeg_emit_marker.c
Normal file
243
components/esp_driver_jpeg/jpeg_emit_marker.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
491
components/esp_driver_jpeg/jpeg_encode.c
Normal file
491
components/esp_driver_jpeg/jpeg_encode.c
Normal file
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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");
|
||||
}
|
||||
}
|
@ -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
|
||||
};
|
||||
|
@ -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)
|
||||
|
130
components/esp_driver_jpeg/private/jpeg_emit_marker.h
Normal file
130
components/esp_driver_jpeg/private/jpeg_emit_marker.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user