forked from espressif/esp-idf
Merge branch 'feature/jpeg_encoder' into 'master'
feat(jpge): Add basic support for jpeg encoder 🖼️ See merge request espressif/esp-idf!29411
This commit is contained in:
@@ -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
|
||||
|
@@ -11,3 +11,4 @@ project(jpeg_test)
|
||||
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp720.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp1080.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "resources/esp480.rgb" BINARY)
|
||||
|
@@ -1,7 +1,18 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_jpeg_decode.c"
|
||||
)
|
||||
|
||||
if(CONFIG_SOC_JPEG_DECODE_SUPPORTED)
|
||||
list(APPEND srcs
|
||||
"test_jpeg_decode.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_JPEG_ENCODE_SUPPORTED)
|
||||
list(APPEND srcs
|
||||
"test_jpeg_encode.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_driver_jpeg unity esp_psram test_utils
|
||||
WHOLE_ARCHIVE)
|
||||
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "driver/jpeg_encode.h"
|
||||
#include "esp_log.h"
|
||||
#include "test_jpeg_performance.h"
|
||||
#include "esp_system.h"
|
||||
#include "ccomp_timer.h"
|
||||
|
||||
extern const uint8_t image_esp480_rgb_start[] asm("_binary_esp480_rgb_start");
|
||||
extern const uint8_t image_esp480_rgb_end[] asm("_binary_esp480_rgb_end");
|
||||
|
||||
TEST_CASE("JPEG encode driver memory leaking check", "[jpeg]")
|
||||
{
|
||||
jpeg_encoder_handle_t encoder_handle;
|
||||
|
||||
jpeg_encode_engine_cfg_t encode_eng_cfg = {
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
int size = esp_get_free_heap_size();
|
||||
for (uint32_t i = 0; i <= 10; i++) {
|
||||
TEST_ESP_OK(jpeg_new_encoder_engine(&encode_eng_cfg, &encoder_handle));
|
||||
TEST_ESP_OK(jpeg_del_encoder_engine(encoder_handle));
|
||||
}
|
||||
|
||||
TEST_ASSERT_INT_WITHIN(400, size, esp_get_free_heap_size());
|
||||
}
|
||||
|
||||
TEST_CASE("JPEG encode performance test for 480*640 RGB->YUV picture", "[jpeg]")
|
||||
{
|
||||
jpeg_encoder_handle_t jpeg_handle = NULL;
|
||||
|
||||
jpeg_encode_engine_cfg_t encode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
jpeg_encode_cfg_t enc_config = {
|
||||
.src_type = JPEG_ENCODE_IN_FORMAT_RGB888,
|
||||
.sub_sample = JPEG_DOWN_SAMPLING_YUV422,
|
||||
.image_quality = 80,
|
||||
.width = 640,
|
||||
.height = 480,
|
||||
};
|
||||
|
||||
jpeg_encode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
|
||||
jpeg_encode_memory_alloc_cfg_t tx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER,
|
||||
};
|
||||
|
||||
size_t rx_buffer_size = 0;
|
||||
uint8_t *jpg_buf_480p = (uint8_t*)jpeg_alloc_encoder_mem(480 * 640 * 3, &rx_mem_cfg, &rx_buffer_size);
|
||||
|
||||
uint32_t jpg_size_480p = 0;
|
||||
|
||||
size_t bit_stream_length = (size_t)image_esp480_rgb_end - (size_t)image_esp480_rgb_start;
|
||||
|
||||
size_t tx_buffer_size = 0;
|
||||
uint8_t *raw_buf_480p = (uint8_t*)jpeg_alloc_encoder_mem(bit_stream_length, &tx_mem_cfg, &tx_buffer_size);
|
||||
// Copy bit stream to psram
|
||||
memcpy(raw_buf_480p, image_esp480_rgb_start, bit_stream_length);
|
||||
TEST_ESP_OK(jpeg_new_encoder_engine(&encode_eng_cfg, &jpeg_handle));
|
||||
|
||||
ccomp_timer_start();
|
||||
|
||||
// Decode picture for 50 times, and get the average
|
||||
uint8_t cnt = 50;
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
TEST_ESP_OK(jpeg_encoder_process(jpeg_handle, &enc_config, raw_buf_480p, bit_stream_length, jpg_buf_480p, rx_buffer_size, &jpg_size_480p));
|
||||
}
|
||||
int64_t encode_time = ccomp_timer_stop();
|
||||
|
||||
TEST_PERFORMANCE_GREATER_THAN(JPEG_ENCODE_RGB888_2_480P_PERFORMANCE, "480p from rgb888 -> *.jpg speed is %lld fps", 1 * 1000 * 1000 / (encode_time / cnt));
|
||||
|
||||
free(jpg_buf_480p);
|
||||
free(raw_buf_480p);
|
||||
TEST_ESP_OK(jpeg_del_encoder_engine(jpeg_handle));
|
||||
}
|
@@ -11,8 +11,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Decoder performance lists
|
||||
#define IDF_PERFORMANCE_MIN_JPEG_DECODE_1080P_2_RGB565_PERFORMANCE 40 // 40 fps for 1080p decoder.
|
||||
|
||||
// Encoder performance lists
|
||||
#define IDF_PERFORMANCE_MIN_JPEG_ENCODE_RGB888_2_480P_PERFORMANCE 160 // 160 fps for 480p encoder.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, , 0x6000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
factory, app, factory, , 2M,
|
|
Binary file not shown.
@@ -7,3 +7,9 @@ CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
|
||||
# Partition table configurations
|
||||
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -1538,3 +1538,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)
|
||||
|
@@ -21,6 +21,7 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_af.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_encode.h \
|
||||
$(PROJECT_PATH)/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h \
|
||||
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/adc_channel.h \
|
||||
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/clk_tree_defs.h \
|
||||
|
@@ -25,6 +25,7 @@ Peripherals API
|
||||
lcd/index
|
||||
:SOC_GP_LDO_SUPPORTED: ldo_regulator
|
||||
ledc
|
||||
:SOC_JPEG_CODEC_SUPPORTED: jpeg
|
||||
:SOC_MIPI_CSI_SUPPORTED: camera_driver
|
||||
:SOC_MCPWM_SUPPORTED: mcpwm
|
||||
:SOC_PARLIO_SUPPORTED: parlio
|
||||
@@ -39,7 +40,6 @@ Peripherals API
|
||||
spi_master
|
||||
spi_slave
|
||||
:SOC_SPI_SUPPORT_SLAVE_HD_VER2: spi_slave_hd
|
||||
:SOC_JPEG_CODEC_SUPPORTED: jpeg
|
||||
:SOC_TEMP_SENSOR_SUPPORTED: temp_sensor
|
||||
:SOC_TOUCH_SENSOR_SUPPORTED: touch_pad
|
||||
:esp32s2: touch_element
|
||||
|
@@ -1,5 +1,5 @@
|
||||
JPEG Decoder
|
||||
============
|
||||
JPEG Encoder and Decoder
|
||||
========================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
@@ -15,6 +15,7 @@ The JPEG driver offers following services:
|
||||
|
||||
- `Resource Allocation <#resource-allocation>`__ - covers how to allocate JPEG resources with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- `JPEG Decoder Engine <#jpeg_decoder_engine>`__ - covers behavior of JPEG decoder engine. Introduce how to use decoder engine functions to decode an image (from jpg format to raw format).
|
||||
- `JPEG Encoder Engine <#jpeg_encoder_engine>`__ - covers behavior of JPEG encoder engine. Introduce how to use encoder engine functions to encode an image (from raw format to jpg format).
|
||||
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
|
||||
@@ -47,6 +48,29 @@ Uninstall JPEG decoder engine
|
||||
|
||||
If a previously installed JPEG engine is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`jpeg_del_decoder_engine`, so that to release the underlying hardware.
|
||||
|
||||
Install JPEG encoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The JPEG encoder engine requires the configuration specified by :cpp:type:`jpeg_encode_engine_cfg_t`.
|
||||
|
||||
If the configurations in :cpp:type:`jpeg_encode_engine_cfg_t` is specified, users can call :cpp:func:`jpeg_new_encoder_engine` to allocate and initialize a JPEG decoder engine. This function will return an JPEG decoder handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_encoder_handle_t encoder_engine;
|
||||
|
||||
jpeg_encode_engine_cfg_t encode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_new_encoder_engine(&encode_eng_cfg, &encoder_engine));
|
||||
|
||||
Uninstall JPEG encoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed JPEG engine is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`jpeg_del_encoder_engine`, so that the underlying hardware is released.
|
||||
|
||||
JPEG Decoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -86,11 +110,50 @@ Overall, You can take following code as reference, the code is going to decode a
|
||||
uint32_t out_size = 0;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb, bit_stream, bit_stream_size, out_buf, &out_size));
|
||||
|
||||
.. note::
|
||||
|
||||
Firstly, in above code, you should make sure the `bit_stream` and `out_buf` should be aligned by certain rules. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you malloc a buffer which is aligned in both size and address.
|
||||
Secondly, the content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns.
|
||||
Thirdly, the width and hight of output picture would be 16 bytes aligned if original picture is formatted by YUV420 or YUV422. For example, if the input picture is 1080*1920, the output picture will be 1088*1920. That is the restriction of jpeg protocol. Please provide sufficient output buffer memory.
|
||||
There are some Tips that can help you use this driver more accurately:
|
||||
|
||||
Firstly, in above code, you should make sure the `bit_stream` and `out_buf` should be aligned by certain rules. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you malloc a buffer which is aligned in both size and address.
|
||||
|
||||
Secondly, the content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns.
|
||||
|
||||
Thirdly, the width and height of output picture would be 16 bytes aligned if original picture is formatted by YUV420 or YUV422. For example, if the input picture is 1080*1920, the output picture will be 1088*1920. That is the restriction of jpeg protocol. Please provide sufficient output buffer memory.
|
||||
|
||||
JPEG Encoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the JPEG encoder driver by :cpp:func:`jpeg_new_encoder_engine`, {IDF_TARGET_NAME} is ready to decode JPEG pictures by :cpp:func:`jpeg_encoder_process`. :cpp:func:`jpeg_encoder_process` is flexible for decoding different types of pictures by a configurable parameter called :cpp:type:`jpeg_encode_cfg_t`:
|
||||
|
||||
Below is the example of code that encodes a 1080*1920 picture:
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_encode_cfg_t enc_config = {
|
||||
.src_type = JPEG_ENCODE_IN_FORMAT_RGB888,
|
||||
.sub_sample = JPEG_DOWN_SAMPLING_YUV422,
|
||||
.image_quality = 80,
|
||||
.width = 1920,
|
||||
.height = 1080,
|
||||
};
|
||||
|
||||
uint8_t *raw_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p);
|
||||
if (raw_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 1080p tx buffer error");
|
||||
return;
|
||||
}
|
||||
uint8_t *jpg_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p / 10); // Assume that compression ratio of 10 to 1
|
||||
if (jpg_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc jpg_buf_1080p error");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_encoder_process(jpeg_handle, &enc_config, raw_buf_1080p, raw_size_1080p, jpg_buf_1080p, &jpg_size_1080p););
|
||||
|
||||
There are some Tips that can help you use this driver more accurately:
|
||||
|
||||
Firstly, in above code, you should make sure the `raw_buf_1080p` and `jpg_buf_1080p` should aligned by calling :cpp:func:`jpeg_alloc_encoder_mem`.
|
||||
|
||||
Secondly, the content of `raw_buf_1080p` buffer should not be changed until :cpp:func:`jpeg_encoder_process` returns.
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
@@ -109,5 +172,9 @@ API Reference
|
||||
|
||||
.. include-build-file:: inc/jpeg_decode.inc
|
||||
|
||||
.. only:: SOC_JPEG_ENCODE_SUPPORTED
|
||||
|
||||
.. include-build-file:: inc/jpeg_encode.inc
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_jpeg/include/driver/jpeg_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/jpeg_types.inc
|
||||
|
@@ -126,6 +126,12 @@ examples/peripherals/jpeg/jpeg_decode:
|
||||
depends_components:
|
||||
- esp_driver_jpeg
|
||||
|
||||
examples/peripherals/jpeg/jpeg_encode:
|
||||
disable:
|
||||
- if: SOC_JPEG_ENCODE_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_jpeg
|
||||
|
||||
examples/peripherals/lcd/i2c_oled:
|
||||
disable:
|
||||
- if: SOC_I2C_SUPPORTED != 1
|
||||
|
6
examples/peripherals/jpeg/jpeg_encode/CMakeLists.txt
Normal file
6
examples/peripherals/jpeg/jpeg_encode/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(jpeg_encode)
|
56
examples/peripherals/jpeg/jpeg_encode/README.md
Normal file
56
examples/peripherals/jpeg/jpeg_encode/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# JPEG encode example
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates how to use the JPEG hardware [encoder](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/jpeg.html) to encode a 1080p picture:
|
||||
|
||||
This example makes use of the hardware-based JPEG encoder. If you have multiple pictures that need to be decoded, such as *.rgb -> *.jpg, you can use this example to accelerate encoding.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* An Espressif development board based on a chip listed in supported targets
|
||||
* A USB cable for power supply and serial communication
|
||||
* Computer with ESP-IDF installed and configured
|
||||
* The raw picture is the only source that you need to prepare (We have an [esp1080p.rgb](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/jpeg/jpeg_encode/resources/esp1080.rgb) in resources folder, you can also get it from [jpeg_decode](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/jpeg/jpeg_decode) example).
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Before you start build and flash this example, please put the image `esp1080.rgb` in your sdcard.
|
||||
|
||||
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```bash
|
||||
I (1114) jpeg.example: Initializing SD card
|
||||
I (1114) gpio: GPIO[43]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1124) gpio: GPIO[44]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1134) gpio: GPIO[39]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1144) gpio: GPIO[40]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1154) gpio: GPIO[41]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1164) gpio: GPIO[42]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1414) gpio: GPIO[42]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
Name: SD64G
|
||||
Type: SDHC/SDXC
|
||||
Speed: 40.00 MHz (limit: 40.00 MHz)
|
||||
Size: 60906MB
|
||||
CSD: ver=2, sector_size=512, capacity=124735488 read_bl_len=9
|
||||
SSR: bus_width=4
|
||||
I (1434) jpeg.example: infile_1080p:/sdcard/esp1080.rgb
|
||||
I (5174) jpeg.example: outfile:/sdcard/outjpg.jpg
|
||||
I (5284) jpeg.example: Card unmounted
|
||||
I (5284) main_task: Returned from app_main()
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "jpeg_encode_main.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -0,0 +1,9 @@
|
||||
menu "JPEG Encode Example menu"
|
||||
|
||||
config EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
bool "Format the card if mount failed"
|
||||
default n
|
||||
help
|
||||
If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if
|
||||
the mount has failed.
|
||||
endmenu
|
136
examples/peripherals/jpeg/jpeg_encode/main/jpeg_encode_main.c
Normal file
136
examples/peripherals/jpeg/jpeg_encode/main/jpeg_encode_main.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/jpeg_encode.h"
|
||||
|
||||
static const char *TAG = "jpeg.example";
|
||||
static sdmmc_card_t *s_card;
|
||||
#define MOUNT_POINT "/sdcard"
|
||||
|
||||
const static char s_infile_1080p[] = "/sdcard/esp1080.rgb";
|
||||
const static char s_outfile_1080p[] = "/sdcard/outjpg.jpg";
|
||||
|
||||
static esp_err_t sdcard_init(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.format_if_mount_failed = true,
|
||||
#else
|
||||
.format_if_mount_failed = false,
|
||||
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
slot_config.width = 4;
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &s_card);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
||||
"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
||||
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// Card has been initialized, print its properties
|
||||
sdmmc_card_print_info(stdout, s_card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdcard_deinit(void)
|
||||
{
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
esp_vfs_fat_sdcard_unmount(mount_point, s_card);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(sdcard_init());
|
||||
uint32_t raw_size_1080p;
|
||||
uint32_t jpg_size_1080p;
|
||||
jpeg_encoder_handle_t jpeg_handle;
|
||||
|
||||
FILE *file_raw_1080p = fopen(s_infile_1080p, "rb");
|
||||
ESP_LOGI(TAG, "s_infile_1080p:%s", s_infile_1080p);
|
||||
if (file_raw_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_raw_1080p error");
|
||||
return;
|
||||
}
|
||||
|
||||
jpeg_encode_engine_cfg_t encode_eng_cfg = {
|
||||
.timeout_ms = 70,
|
||||
};
|
||||
|
||||
jpeg_encode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
|
||||
jpeg_encode_memory_alloc_cfg_t tx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER,
|
||||
};
|
||||
|
||||
jpeg_new_encoder_engine(&encode_eng_cfg, &jpeg_handle);
|
||||
// Read 1080p raw picture
|
||||
fseek(file_raw_1080p, 0, SEEK_END);
|
||||
raw_size_1080p = ftell(file_raw_1080p);
|
||||
fseek(file_raw_1080p, 0, SEEK_SET);
|
||||
size_t tx_buffer_size = 0;
|
||||
uint8_t *raw_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p, &tx_mem_cfg, &tx_buffer_size);
|
||||
if (raw_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 1080p tx buffer error");
|
||||
return;
|
||||
}
|
||||
fread(raw_buf_1080p, 1, raw_size_1080p, file_raw_1080p);
|
||||
fclose(file_raw_1080p);
|
||||
|
||||
size_t rx_buffer_size = 0;
|
||||
uint8_t *jpg_buf_1080p = (uint8_t*)jpeg_alloc_encoder_mem(raw_size_1080p / 10, &rx_mem_cfg, &rx_buffer_size); // Assume that compression ratio of 10 to 1
|
||||
if (jpg_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc jpg_buf_1080p error");
|
||||
return;
|
||||
}
|
||||
|
||||
jpeg_encode_cfg_t enc_config = {
|
||||
.src_type = JPEG_ENCODE_IN_FORMAT_RGB888,
|
||||
.sub_sample = JPEG_DOWN_SAMPLING_YUV422,
|
||||
.image_quality = 80,
|
||||
.width = 1920,
|
||||
.height = 1080,
|
||||
};
|
||||
|
||||
jpeg_encoder_process(jpeg_handle, &enc_config, raw_buf_1080p, raw_size_1080p, jpg_buf_1080p, rx_buffer_size, &jpg_size_1080p);
|
||||
|
||||
FILE *file_jpg_1080p = fopen(s_outfile_1080p, "wb");
|
||||
ESP_LOGI(TAG, "outfile:%s", s_outfile_1080p);
|
||||
if (file_jpg_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_jpg_1080p error");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(jpg_buf_1080p, 1, jpg_size_1080p, file_jpg_1080p);
|
||||
fclose(file_jpg_1080p);
|
||||
|
||||
sdcard_deinit();
|
||||
ESP_LOGI(TAG, "Card unmounted");
|
||||
}
|
97
examples/peripherals/jpeg/jpeg_encode/resources/esp1080.rgb
Normal file
97
examples/peripherals/jpeg/jpeg_encode/resources/esp1080.rgb
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
||||
# SPIRAM configurations
|
||||
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
Reference in New Issue
Block a user