docs(jpeg): Add basic programming guide for jpeg decoder

This commit is contained in:
Cao Sen Miao 2024-02-28 15:16:57 +08:00
parent 518c7f5b14
commit d11030ade6
16 changed files with 320 additions and 59 deletions

View File

@ -17,5 +17,5 @@ endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${public_include}
PRIV_REQUIRES "esp_mm"
PRIV_REQUIRES "esp_mm" "esp_pm"
)

View File

@ -19,9 +19,7 @@ extern "C" {
*/
typedef struct {
jpeg_dec_output_format_t output_format; /*!< JPEG decoder output format */
union {
jpeg_dec_rgb_element_order rgb_order; /*!< JPEG decoder output order */
};
jpeg_dec_rgb_element_order rgb_order; /*!< JPEG decoder output order */
jpeg_yuv_rgb_conv_std_t conv_std; /*!< JPEG decoder yuv->rgb standard */
} jpeg_decode_cfg_t;
@ -30,6 +28,7 @@ typedef struct {
*/
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_decode_engine_cfg_t;
/**
@ -45,16 +44,16 @@ typedef struct {
*
* This function acquires a JPEG decode engine with the specified configuration. The configuration
* parameters are provided through the `dec_eng_cfg` structure, and the resulting JPEG decoder handle
* is returned through the `ret_jpgd_handle` pointer.
* is returned through the `ret_decoder` pointer.
*
* @param[in] dec_eng_cfg Pointer to the JPEG decode engine configuration.
* @param[out] ret_jpgd_handle Pointer to a variable that will receive the JPEG decoder handle.
* @param[out] ret_decoder Pointer to a variable that will receive the JPEG decoder handle.
* @return
* - ESP_OK: JPEG decoder initialized successfully.
* - ESP_ERR_INVALID_ARG: JPEG decoder initialization failed because of invalid argument.
* - ESP_ERR_NO_MEM: Create JPEG decoder failed because of out of memory.
*/
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_jpgd_handle);
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_decoder);
/**
* @brief Helper function for getting information about a JPEG image.
@ -67,7 +66,7 @@ esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, j
* user can get picture width and height via this function and malloc a reasonable size buffer for jpeg engine process.
*
* @param[in] bit_stream Pointer to the buffer containing the JPEG image data.
* @param[in] stream_size Size of the JPEG image data in bytes.
* @param[in] stream_size Size of the JPEG image data in bytes. Note that parse beginning partial of picture also works, but the beginning partial should be enough given.
* @param[out] picture_info Pointer to the structure that will receive the image information.
* @return
* - ESP_OK: JPEG decoder get jpg image header successfully.

View File

@ -13,6 +13,7 @@
#include "esp_memory_utils.h"
#include "driver/jpeg_types.h"
#include "sys/lock.h"
#include "sys/queue.h"
#include "sdkconfig.h"
#if CONFIG_JPEG_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
@ -46,16 +47,20 @@ esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec)
codec = heap_caps_calloc(1, sizeof(jpeg_codec_t), JPEG_MEM_ALLOC_CAPS);
if (codec) {
s_jpeg_platform.jpeg_codec = codec;
codec->intr_priority = -1;
codec->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
codec->codec_mux = xSemaphoreCreateBinaryWithCaps(JPEG_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(codec->codec_mux, ESP_ERR_NO_MEM, TAG, "No memory for codec mutex");
xSemaphoreGive(codec->codec_mux);
codec->codec_mutex = xSemaphoreCreateBinaryWithCaps(JPEG_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(codec->codec_mutex, ESP_ERR_NO_MEM, TAG, "No memory for codec mutex");
SLIST_INIT(&codec->jpeg_isr_handler_list);
xSemaphoreGive(codec->codec_mutex);
// init the clock
PERIPH_RCC_ATOMIC() {
jpeg_ll_enable_bus_clock(true);
jpeg_ll_reset_module_register();
}
#if CONFIG_PM_ENABLE
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "jpeg_codec", &codec->pm_lock), TAG, "create pm lock failed");
#endif
jpeg_hal_init(&codec->hal);
} else {
ESP_LOGE(TAG, "No more memory for acquiring JPEG codec module");
@ -86,9 +91,12 @@ esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec)
do_deinitialize = true;
s_jpeg_platform.jpeg_codec = NULL;
if (jpeg_codec->codec_mux) {
vSemaphoreDeleteWithCaps(jpeg_codec->codec_mux);
jpeg_codec->codec_mux = NULL;
if (jpeg_codec->codec_mutex) {
vSemaphoreDeleteWithCaps(jpeg_codec->codec_mutex);
jpeg_codec->codec_mutex = NULL;
}
if (jpeg_codec->pm_lock) {
esp_pm_lock_delete(jpeg_codec->pm_lock);
}
PERIPH_RCC_ATOMIC() {
jpeg_ll_enable_bus_clock(false);
@ -104,3 +112,87 @@ esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec)
return ESP_OK;
}
/*---------------------------------------------------------------
JPEG INTERRUPT HANDLER
---------------------------------------------------------------*/
static void jpeg_isr(void* arg)
{
jpeg_codec_handle_t jpeg_codec = (jpeg_codec_handle_t) arg;
uint32_t status = jpeg_ll_get_intr_status(jpeg_codec->hal.dev);
jpeg_isr_handler_t* it;
SLIST_FOREACH(it, &jpeg_codec->jpeg_isr_handler_list, next) {
if (it->mask & status) {
(*it->handler)(it->handler_arg);
}
}
jpeg_ll_clear_intr_mask(jpeg_codec->hal.dev, status);
}
esp_err_t jpeg_isr_register(jpeg_codec_handle_t jpeg_codec, intr_handler_t handler, void* handler_arg, uint32_t jpeg_intr_mask, uint32_t flags, jpeg_isr_handler_t** jpeg_intr_handler)
{
if (jpeg_codec->intr_handle == NULL) {
// The jpeg codec interrupt has not been allocated.
esp_err_t err = esp_intr_alloc_intrstatus(ETS_JPEG_INTR_SOURCE, flags, (uint32_t)jpeg_ll_get_interrupt_status_reg(jpeg_codec->hal.dev), JPEG_LL_DECODER_EVENT_INTR | JPEG_LL_ENCODER_EVENT_INTR, &jpeg_isr, jpeg_codec, &jpeg_codec->intr_handle);
if (err != ESP_OK) {
return err;
}
}
jpeg_isr_handler_t* item = heap_caps_calloc(1, sizeof(jpeg_isr_handler_t), JPEG_MEM_ALLOC_CAPS);
if (item == NULL) {
return ESP_ERR_NO_MEM;
}
item->handler = handler;
item->handler_arg = handler_arg;
item->mask = jpeg_intr_mask;
item->flags = flags;
SLIST_INSERT_HEAD(&jpeg_codec->jpeg_isr_handler_list, item, next);
*jpeg_intr_handler = item;
return ESP_OK;
}
esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t *jpeg_intr_handler)
{
jpeg_isr_handler_t* it;
jpeg_isr_handler_t* prev = NULL;
bool found = false;
SLIST_FOREACH(it, &jpeg_codec->jpeg_isr_handler_list, next) {
if (it == jpeg_intr_handler) {
if (it == SLIST_FIRST(&jpeg_codec->jpeg_isr_handler_list)) {
SLIST_REMOVE_HEAD(&jpeg_codec->jpeg_isr_handler_list, next);
} else {
SLIST_REMOVE_AFTER(prev, next);
}
found = true;
break;
}
prev = it;
free(prev);
}
if (unlikely(found != true)) {
return ESP_ERR_INVALID_STATE;
}
if (SLIST_EMPTY(&jpeg_codec->jpeg_isr_handler_list)) {
// All interrupt is removed
if (jpeg_codec->intr_handle) {
esp_intr_free(jpeg_codec->intr_handle);
}
}
return ESP_OK;
}
esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority)
{
esp_err_t ret = ESP_OK;
bool intr_priority_conflict = false;
if (jpeg_codec->intr_priority == -1) {
jpeg_codec->intr_priority = intr_priority;
} else if (intr_priority != 0) {
intr_priority_conflict = (jpeg_codec->intr_priority != intr_priority);
}
ESP_RETURN_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, TAG, "intr_priority conflict, already is %d but attempt to %d", jpeg_codec->intr_priority, intr_priority);
return ret;
}

View File

@ -55,14 +55,14 @@ static void jpeg_decoder_isr_handle_default(void *arg)
uint32_t value = jpeg_ll_get_intr_status(hal->dev);
jpeg_ll_clear_intr_mask(hal->dev, value);
dec_evt.jpgd_status = value;
xQueueSendFromISR(decoder_engine->evt_handle, &dec_evt, &HPTaskAwoken);
xQueueSendFromISR(decoder_engine->evt_queue, &dec_evt, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_jpgd_handle)
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_decoder)
{
#if CONFIG_JPEG_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
@ -90,11 +90,11 @@ esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, j
ESP_GOTO_ON_ERROR(jpeg_acquire_codec_handle(&decoder_engine->codec_base), err, TAG, "JPEG decode acquires codec handle failed");
jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal;
decoder_engine->timeout_tick = (dec_eng_cfg->timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(dec_eng_cfg->timeout_ms);
/// init jpeg interrupt.
portENTER_CRITICAL(&decoder_engine->codec_base->spinlock);
jpeg_ll_clear_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR);
portEXIT_CRITICAL(&decoder_engine->codec_base->spinlock);
ESP_GOTO_ON_ERROR(jpeg_check_intr_priority(decoder_engine->codec_base, dec_eng_cfg->intr_priority), err, TAG, "set group intrrupt priority failed");
if (dec_eng_cfg->intr_priority) {
ESP_RETURN_ON_FALSE(1 << (dec_eng_cfg->intr_priority) & JPEG_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", dec_eng_cfg->intr_priority);
}
@ -103,16 +103,14 @@ esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, j
isr_flags |= 1 << (dec_eng_cfg->intr_priority);
}
ret = esp_intr_alloc_intrstatus(ETS_JPEG_INTR_SOURCE, isr_flags, (uint32_t)jpeg_ll_get_interrupt_status_reg(hal->dev), JPEG_LL_DECODER_EVENT_INTR, jpeg_decoder_isr_handle_default, decoder_engine, &decoder_engine->intr_handle);
ret = jpeg_isr_register(decoder_engine->codec_base, jpeg_decoder_isr_handle_default, decoder_engine, JPEG_LL_DECODER_EVENT_INTR, isr_flags, &decoder_engine->intr_handle);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install jpeg decode interrupt failed");
portENTER_CRITICAL(&decoder_engine->codec_base->spinlock);
jpeg_ll_enable_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR);
portEXIT_CRITICAL(&decoder_engine->codec_base->spinlock);
// Initialize queue
decoder_engine->evt_handle = xQueueCreateWithCaps(2, sizeof(jpeg_dma2d_dec_evt_t), JPEG_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(decoder_engine->evt_handle, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue");
decoder_engine->evt_queue = xQueueCreateWithCaps(2, sizeof(jpeg_dma2d_dec_evt_t), JPEG_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(decoder_engine->evt_queue, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue");
dma2d_pool_config_t dma2d_client_config = {
.pool_id = 0,
@ -123,7 +121,7 @@ esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, j
decoder_engine->trans_desc = (dma2d_trans_t *)heap_caps_calloc(1, SIZEOF_DMA2D_TRANS_T, JPEG_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(decoder_engine->trans_desc, ESP_ERR_NO_MEM, err, TAG, "No memory for dma2d descriptor");
*ret_jpgd_handle = decoder_engine;
*ret_decoder = decoder_engine;
return ESP_OK;
err:
@ -177,15 +175,20 @@ esp_err_t jpeg_decoder_get_info(const uint8_t *in_buf, uint32_t inbuf_len, jpeg_
esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size)
{
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null");
ESP_RETURN_ON_FALSE(decode_cfg, ESP_ERR_INVALID_ARG, TAG, "jpeg decode config is null");
ESP_RETURN_ON_FALSE(decode_outbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode picture buffer is null");
ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode bit stream is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer");
ESP_RETURN_ON_FALSE(((uintptr_t)decode_outbuf % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer");
esp_err_t ret = ESP_OK;
xSemaphoreTake(decoder_engine->codec_base->codec_mux, portMAX_DELAY);
if (decoder_engine->codec_base->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(decoder_engine->codec_base->pm_lock), TAG, "acquire pm_lock failed");
}
xSemaphoreTake(decoder_engine->codec_base->codec_mutex, portMAX_DELAY);
/* Reset queue */
xQueueReset(decoder_engine->evt_handle);
xQueueReset(decoder_engine->evt_queue);
decoder_engine->output_format = decode_cfg->output_format;
decoder_engine->rgb_order = decode_cfg->rgb_order;
@ -218,19 +221,15 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_
// Blocking for JPEG decode transaction finishes.
while (1) {
jpeg_dma2d_dec_evt_t jpeg_dma2d_event;
BaseType_t ret = xQueueReceive(decoder_engine->evt_handle, &jpeg_dma2d_event, portMAX_DELAY);
if (ret == pdFALSE) {
ESP_LOGE(TAG, "jpeg-dma2d handle jpeg decode timeout");
xSemaphoreGive(decoder_engine->codec_base->codec_mux);
return ESP_ERR_TIMEOUT;
}
BaseType_t ret = xQueueReceive(decoder_engine->evt_queue, &jpeg_dma2d_event, decoder_engine->timeout_tick);
ESP_GOTO_ON_FALSE(ret == pdTRUE, ESP_ERR_TIMEOUT, err, TAG, "jpeg-dma2d handle jpeg decode timeout, please check `timeout_ms` ");
// Dealing with JPEG event
if (jpeg_dma2d_event.jpgd_status != 0) {
uint32_t status = jpeg_dma2d_event.jpgd_status;
s_decoder_error_log_print(status);
dma2d_force_end(decoder_engine->trans_desc, &need_yield);
xSemaphoreGive(decoder_engine->codec_base->codec_mux);
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
return ESP_ERR_INVALID_STATE;
}
@ -239,13 +238,13 @@ esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_
}
}
*out_size = decoder_engine->header_info->origin_h * decoder_engine->header_info->origin_v * decoder_engine->pixel;
xSemaphoreGive(decoder_engine->codec_base->codec_mux);
return ESP_OK;
*out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel;
err:
xSemaphoreGive(decoder_engine->codec_base->codec_mux);
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
if (decoder_engine->codec_base->pm_lock) {
ESP_RETURN_ON_ERROR(esp_pm_lock_release(decoder_engine->codec_base->pm_lock), TAG, "release pm_lock failed");
}
return ret;
}
@ -264,15 +263,15 @@ esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine)
if (decoder_engine->header_info) {
free(decoder_engine->header_info);
}
if (decoder_engine->intr_handle) {
esp_intr_free(decoder_engine->intr_handle);
}
if (decoder_engine->evt_handle) {
vQueueDeleteWithCaps(decoder_engine->evt_handle);
if (decoder_engine->evt_queue) {
vQueueDeleteWithCaps(decoder_engine->evt_queue);
}
if (decoder_engine->dma2d_group_handle) {
dma2d_release_pool(decoder_engine->dma2d_group_handle);
}
if (decoder_engine->intr_handle) {
jpeg_isr_deregister(decoder_engine->codec_base, decoder_engine->intr_handle);
}
free(decoder_engine);
}
return ESP_OK;
@ -348,7 +347,7 @@ static bool jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *e
};
jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) user_data;
dec_evt.dma_evt = JPEG_DMA2D_RX_EOF;
xQueueSendFromISR(decoder_engine->evt_handle, &dec_evt, &higher_priority_task_awoken);
xQueueSendFromISR(decoder_engine->evt_queue, &dec_evt, &higher_priority_task_awoken);
return higher_priority_task_awoken;
}

View File

@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdatomic.h>
#include "sys/queue.h"
#include "esp_private/dma2d.h"
#include "driver/jpeg_types.h"
#include "freertos/FreeRTOS.h"
@ -15,6 +16,7 @@
#include "freertos/task.h"
#include "hal/jpeg_hal.h"
#include "esp_intr_types.h"
#include "esp_pm.h"
#include "sdkconfig.h"
#ifdef __cplusplus
@ -26,16 +28,28 @@ extern "C" {
#define JPEG_ALLOW_INTR_PRIORITY_MASK (ESP_INTR_FLAG_LOWMED)
// JPEG encoder and decoder shares same interrupt ID.
#define JPEG_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED)
#define JPEG_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED)
typedef struct jpeg_decoder_t jpeg_decoder_t;
typedef struct jpeg_codec_t jpeg_codec_t;
typedef struct jpeg_codec_t *jpeg_codec_handle_t;
typedef struct jpeg_isr_handler_ {
uint32_t mask;
intr_handler_t handler;
void* handler_arg;
uint32_t flags;
SLIST_ENTRY(jpeg_isr_handler_) next;
} jpeg_isr_handler_t;
struct jpeg_codec_t {
SemaphoreHandle_t codec_mux; // pretend that one picture is in process, no other picture can interrupt current stage.
jpeg_hal_context_t hal; // Hal layer for each port(bus)
portMUX_TYPE spinlock; // To protect pre-group register level concurrency access
SemaphoreHandle_t codec_mutex; // pretend that one picture is in process, no other picture can interrupt current stage.
jpeg_hal_context_t hal; // Hal layer for each port(bus)
portMUX_TYPE spinlock; // To protect pre-group register level concurrency access
intr_handle_t intr_handle; // jpeg codec interrupt handler
int intr_priority; // jpeg codec interrupt priority
SLIST_HEAD(jpeg_isr_handler_list_, jpeg_isr_handler_) jpeg_isr_handler_list; // List for jpeg interrupt.
esp_pm_lock_handle_t pm_lock; // power manange lock
};
typedef enum {
@ -82,12 +96,13 @@ struct jpeg_decoder_t {
jpeg_dec_rgb_element_order rgb_order; // RGB pixel order
jpeg_yuv_rgb_conv_std_t conv_std; // YUV RGB conversion standard
uint8_t pixel; // size per pixel
QueueHandle_t evt_handle; // jpeg event from 2DDMA and JPEG engine
QueueHandle_t evt_queue; // jpeg event from 2DDMA and JPEG engine
uint8_t *decoded_buf; // pointer to the rx buffer.
uint32_t total_size; // jpeg picture origin size (in bytes)
intr_handle_t intr_handle; // jpeg decoder interrupt handler
TickType_t timeout_tick; // timeout value for jpeg decoder (in cpu tick).
jpeg_isr_handler_t *intr_handle; // jpeg decoder interrupt handler
//dma handles
dma2d_pool_handle_t dma2d_group_handle; // 2D-DMA group handle
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
@ -138,6 +153,45 @@ esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec);
*/
esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec);
/**
* @brief Register an ISR handler for JPEG interrupt
*
* This function registers an Interrupt Service Routine (ISR) handler for JPEG interrupt.
*
* @param jpeg_codec The JPEG codec handle
* @param handler The ISR handler function to be registered
* @param handler_arg An argument to be passed to the ISR handler function
* @param jpeg_intr_mask The JPEG interrupt mask value
* @param flags Additional flags for ISR registration
* @param jpeg_intr_handler JPEG interrupt handler
* @return esp_err_t Returns ESP_OK on success, or an error code on failure
*/
esp_err_t jpeg_isr_register(jpeg_codec_handle_t jpeg_codec, intr_handler_t handler, void* handler_arg, uint32_t jpeg_intr_mask, uint32_t flags, jpeg_isr_handler_t** jpeg_intr_handler);
/**
* @brief Deregister an ISR handler for JPEG interrupt
*
* This function deregisters an Interrupt Service Routine (ISR) handler for JPEG interrupt.
*
* @param jpeg_codec The JPEG codec handle
* @param handler The ISR handler function to be deregistered
* @param handler_arg The argument previously passed to the ISR handler function
* @param jpeg_intr_handler JPEG interrupt handler
* @return esp_err_t Returns ESP_OK on success, or an error code on failure
*/
esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t *jpeg_intr_handler);
/**
* @brief Check the interrupt priority for JPEG codec
*
* This function checks the interrupt priority for the JPEG codec to ensure it meets the specified requirements.
*
* @param jpeg_codec The JPEG codec handle
* @param intr_priority The interrupt priority value to be checked
* @return esp_err_t Returns ESP_OK if the interrupt priority meets the requirements, or an error code on failure
*/
esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority);
#ifdef __cplusplus
}
#endif

View File

@ -21,6 +21,8 @@ extern "C" {
* @param header_info The handle to the JPEG information.
* @param num_bytes The number of bytes to retrieve from the decoder.
*
* @note num_bytes should not larger than 4, because the return value is uint32.
*
* @return The retrieved bytes as a 32-bit unsigned integer.
*/
uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes);

View File

@ -26,10 +26,11 @@ TEST_CASE("JPEG decode driver memory leaking check", "[jpeg]")
jpeg_decoder_handle_t jpgd_handle;
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.timeout_ms = 40,
};
int size = esp_get_free_heap_size();
for (uint32_t i = 0; i <= 5; i++) {
for (uint32_t i = 0; i <= 3; i++) {
TEST_ESP_OK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ESP_OK(jpeg_del_decoder_engine(jpgd_handle));
@ -44,6 +45,7 @@ TEST_CASE("JPEG decode performance test for 1080*1920 YUV->RGB picture", "[jpeg]
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.intr_priority = 0,
.timeout_ms = 40,
};
jpeg_decode_cfg_t decode_cfg = {

View File

@ -74,6 +74,9 @@ typedef enum {
JPEG_LL_EN_FRAME_EOF_LACK = (1 << 16),
} jpeg_ll_encoder_intr_t;
#define JPEG_LL_ENCODER_EVENT_INTR (JPEG_LL_RLE_PARALLEL_ERR | \
JPEG_LL_EN_FRAME_EOF_ERR)
/**
* @brief Enable the hardware clock for JPEG module
*

View File

@ -22,7 +22,7 @@ void jpeg_hal_deinit(jpeg_hal_context_t *hal)
/* Config huffman code tables with a DHT segment */
/*-----------------------------------------------------------------------*/
static void jpeg_create_minicode_tbl(uint8_t *huffbits, uint32_t *huffmin, uint32_t *tmp_huff)
static void jpeg_hal_create_minicode_tbl(uint8_t *huffbits, uint32_t *huffmin, uint32_t *tmp_huff)
{
int total_len = 0;
/* Re-build huffman code word table */
@ -48,7 +48,7 @@ static void jpeg_create_minicode_tbl(uint8_t *huffbits, uint32_t *huffmin, uint3
void jpeg_hal_config_ac0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff)
{
uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {};
jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_ll_dht_ac0_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_ac0_write_value(hal->dev, huffcode);
}
@ -56,7 +56,7 @@ void jpeg_hal_config_ac0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8
void jpeg_hal_config_ac1_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff)
{
uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {};
jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_ll_dht_ac1_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_ac1_write_value(hal->dev, huffcode);
}
@ -64,7 +64,7 @@ void jpeg_hal_config_ac1_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8
void jpeg_hal_config_dc0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff)
{
uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {};
jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_ll_dht_dc0_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_dc0_write_value(hal->dev, huffcode);
}
@ -72,7 +72,7 @@ void jpeg_hal_config_dc0_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8
void jpeg_hal_config_dc1_table(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff)
{
uint32_t huffmin[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN] = {};
jpeg_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
jpeg_ll_dht_dc1_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_dc1_write_value(hal->dev, huffcode);
}

View File

@ -150,6 +150,8 @@ ANA_CMPR_DOCS = ['api-reference/peripherals/ana_cmpr.rst']
SPI_SLAVE_HD_DOCS = ['api-reference/peripherals/spi_slave_hd.rst']
JPEG_DOCS = ['api-reference/peripherals/jpeg.rst']
QEMU_DOCS = ['api-guides/tools/qemu.rst']
ESP32_DOCS = ['api-reference/system/himem.rst',
@ -235,6 +237,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
'SOC_WIFI_MESH_SUPPORT':WIFI_MESH_DOCS,
'SOC_SPI_SUPPORT_SLAVE_HD_VER2':SPI_SLAVE_HD_DOCS,
'SOC_WIFI_NAN_SUPPORT':NAN_DOCS,
'SOC_JPEG_CODEC_SUPPORTED':JPEG_DOCS,
'esp32':ESP32_DOCS,
'esp32s2':ESP32S2_DOCS,
'esp32s3':ESP32S3_DOCS,

View File

@ -12,3 +12,6 @@ INPUT += \
$(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \
$(PROJECT_PATH)/components/usb/include/usb/usb_types_stack.h \
$(PROJECT_PATH)/components/hal/include/hal/jpeg_types.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \

View File

@ -36,6 +36,7 @@ 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

View File

@ -0,0 +1,101 @@
JPEG Decoder
============
Introduction
------------
JPEG is a commonly used method of lossy compression for digital images, particularly for those images produced by digital photography. The degree of compression can be adjusted, allowing a selectable tradeoff between storage size and image quality. JPEG typically achieves 10:1 compression with little perceptible loss in image quality.
JPEG codec on {IDF_TARGET_NAME} is an image codec, which is based on the JPEG baseline standard, for compressing and decompressing images to reduce the bandwidth required to transmit images or the space required to store images, making it possible to process large-resolution images. But please note, at one time, the codec engine can only work as either encoder or decoder.
Functional Overview
-------------------
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).
- `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.
Resource Allocation
^^^^^^^^^^^^^^^^^^^
Install JPEG decoder engine
~~~~~~~~~~~~~~~~~~~~~~~~~~~
JPEG decoder engine requires the configuration that specified by :cpp:type:`jpeg_decode_engine_cfg_t`:
- :cpp:member:`jpeg_decode_engine_cfg_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`jpeg_decode_engine_cfg_t::intr_priority` Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)).
If the configurations in :cpp:type:`jpeg_decode_engine_cfg_t` is specified, users can call :cpp:func:`jpeg_new_decoder_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_decoder_handle_t jpgd_handle;
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.intr_priority = 0,
.timeout_ms = 40,
};
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
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.
JPEG Decoder Engine
^^^^^^^^^^^^^^^^^^^
After installing the JPEG decoder driver by :cpp:func:`jpeg_new_decoder_engine`, {IDF_TARGET_NAME} is ready to decode JPEG pictures by :cpp:func:`jpeg_decoder_process`. :cpp:func:`jpeg_decoder_process` is flexible for decoding different types of pictures by a configurable parameter called :cpp:type:`jpeg_decode_cfg_t`:
- :cpp:member:`jpeg_decode_cfg_t::output_format` Set the output raw image format.
- :cpp:member:`jpeg_decode_cfg_t::rgb_order` Set the output pixel order. (RGB or BGR).
- :cpp:member:`jpeg_decode_cfg_t::jpeg_yuv_rgb_conv_std_t` Set the output YUV and RGB conversion standard (BT601 or BT709)
Moreover, our jpeg decoder api provides a helper function which helps you get the basic information of your given image. Calling :cpp:func:`jpeg_decoder_get_info` would return the picture information structure called :cpp:func:`jpeg_decoder_get_info`. If you already know the picture basic information, this functions is unnecessary to be called.
Overall, You can take following code as reference, the code is going to decode a 1080*1920 picture.
.. code:: c
jpeg_decode_cfg_t decode_cfg_rgb = {
.output_format = JPEG_DECODE_OUT_FORMAT_RGB888,
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
};
uint8_t *bit_stream = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, bit_stream_size, MALLOC_CAP_SPIRAM);
uint8_t *out_buf = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, 1920 * 1080 * 3, MALLOC_CAP_SPIRAM); // Sufficient space for output images.
jpeg_decode_picture_info_t header_info;
ESP_ERROR_CHECK(jpeg_decoder_get_info(bit_stream, bit_stream_size, &header_info));
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 :c:macro:`JPEG_BUFFER_MALLOC_ALIGN_VALUE` byte aligned.
Secondly, the content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns.
Thread Safety
^^^^^^^^^^^^^
The factory function :cpp:func:`jpeg_new_decoder_engine`, :cpp:func:`jpeg_decoder_get_info`, :cpp:func:`jpeg_decoder_process`, and :cpp:func:`jpeg_del_decoder_engine` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
Kconfig Options
^^^^^^^^^^^^^^^
- :ref:`CONFIG_JPEG_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
API Reference
-------------
.. only:: SOC_JPEG_DECODE_SUPPORTED
.. include-build-file:: inc/jpeg_decode.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

View File

@ -36,6 +36,7 @@
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

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/jpeg.rst

View File

@ -74,7 +74,7 @@ void app_main(void)
jpeg_decoder_handle_t jpgd_handle;
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.timeout_ms = 40,
};
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));