feat(jpeg): Add basic jpeg decoder support on esp32p4

pull/13309/head
Cao Sen Miao 2024-02-28 15:11:32 +08:00
rodzic 6817e9f07c
commit abc2971d95
22 zmienionych plików z 1814 dodań i 14 usunięć

Wyświetl plik

@ -0,0 +1,21 @@
set(srcs)
set(public_include "include")
# JPEG related source files
if(CONFIG_SOC_JPEG_CODEC_SUPPORTED)
list(APPEND srcs
"jpeg_common.c"
"jpeg_param.c"
)
if(CONFIG_SOC_JPEG_DECODE_SUPPORTED)
list(APPEND srcs
"jpeg_parse_marker.c"
"jpeg_decode.c"
)
endif()
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${public_include}
PRIV_REQUIRES "esp_mm"
)

Wyświetl plik

@ -0,0 +1,12 @@
menu "ESP-Driver:JPEG-Codec Configurations"
depends on SOC_JPEG_CODEC_SUPPORTED
config JPEG_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for JPEG driver.
Note that, this option only controls the JPEG driver log, won't affect other drivers.
Please also note, enable this option will make jpeg codec process speed much slower.
endmenu

Wyświetl plik

@ -0,0 +1,123 @@
/*
* 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"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuration parameters for a JPEG decoder image process.
*/
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_yuv_rgb_conv_std_t conv_std; /*!< JPEG decoder yuv->rgb standard */
} jpeg_decode_cfg_t;
/**
* @brief Configuration parameters for the JPEG decoder engine.
*/
typedef struct {
int intr_priority; /*!< JPEG interrupt priority, if set to 0, driver will select the default priority (1,2,3). */
} jpeg_decode_engine_cfg_t;
/**
* @brief Stucture for jpeg decode header
*/
typedef struct {
uint32_t width; /*!< Number of pixels in the horizontal direction */
uint32_t height; /*!< Number of pixels in the vertical direction */
} jpeg_decode_picture_info_t;
/**
* @brief Acquire a JPEG decode engine with the specified configuration.
*
* 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.
*
* @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.
* @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);
/**
* @brief Helper function for getting information about a JPEG image.
*
* This function analyzes the provided JPEG image data and retrieves information about the image,
* such as its width, height. The image data is specified by the `bit_stream` pointer and the `stream_size` parameter.
* The resulting image information is returned through the `picture_info` structure.
*
* @note This function doesn't depend on any jpeg hardware, it helps user to know jpeg information from jpeg header. For example,
* 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[out] picture_info Pointer to the structure that will receive the image information.
* @return
* - ESP_OK: JPEG decoder get jpg image header successfully.
* - ESP_ERR_INVALID_ARG: JPEG decoder get header info failed because of invalid argument.
*/
esp_err_t jpeg_decoder_get_info(const uint8_t *bit_stream, uint32_t stream_size, jpeg_decode_picture_info_t *picture_info);
/**
* @brief Process a JPEG image with the specified decoder instance.
*
* This function processes the provided JPEG image data using the specified JPEG decoder instance. The input
* JPEG image data is specified by the `bit_stream` pointer and the `stream_size` parameter. The resulting
* decoded image data is written to the `decode_outbuf` buffer, and the length of the output image data is
* returned through the `out_size` pointer.
*
* @note Please make sure that the content of `bit_stream` pointer cannot be modified until this function returns.
*
* @param[in] decoder_engine Handle of the JPEG decoder instance to use for processing.
* @param[in] decode_cfg Config structure of decoder.
* @param[in] bit_stream Pointer to the buffer containing the input JPEG image data.
* @param[in] stream_size Size of the input JPEG image data in bytes.
* @param[out] decode_outbuf Pointer to the buffer that will receive the decoded image data.
* @param[out] out_size Pointer to a variable that will receive the length of the output image data.
* @return
* - ESP_OK: JPEG decoder process successfully.
* - ESP_ERR_INVALID_ARG: JPEG decoder process failed because of invalid argument.
*/
esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size);
/**
* @brief Release resources used by a JPEG decoder instance.
*
* This function releases the resources used by the specified JPEG decoder instance. The decoder instance is
* specified by the `decoder_engine` parameter.
*
* @param decoder_engine Handle of the JPEG decoder instance to release resources for.
* @return
* - ESP_OK: Delete JPEG decoder successfully.
* - ESP_ERR_INVALID_ARG: Delete JPEG decoder failed because of invalid argument.
*/
esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine);
/**
* @brief A helper function to allocate memory space for JPEG decoder.
*
* @param size The size of memory to allocate.
* @return Pointer to the allocated memory space, or NULL if allocation fails.
*/
void * jpeg_alloc_decoder_mem(size_t size);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "hal/color_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Enumeration for jpeg output format.
*/
typedef enum {
JPEG_DECODE_OUT_FORMAT_RGB888 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB888), /*!< output RGB888 format */
JPEG_DECODE_OUT_FORMAT_RGB565 = COLOR_TYPE_ID(COLOR_SPACE_RGB, COLOR_PIXEL_RGB565), /*!< output RGB565 format */
JPEG_DECODE_OUT_FORMAT_GRAY = COLOR_TYPE_ID(COLOR_SPACE_GRAY, COLOR_PIXEL_GRAY8), /*!< output the gray picture */
} jpeg_dec_output_format_t;
/**
* @brief Enumeration for YUV and RGB conversion standard
*/
typedef enum {
JPEG_YUV_RGB_CONV_STD_BT601 = COLOR_CONV_STD_RGB_YUV_BT601, /*!< BT601 */
JPEG_YUV_RGB_CONV_STD_BT709 = COLOR_CONV_STD_RGB_YUV_BT709, /*!< BT709 */
} jpeg_yuv_rgb_conv_std_t;
/**
* @brief Enumeration for jpeg big/small endian output.
*/
typedef enum {
JPEG_DEC_RGB_ELEMENT_ORDER_BGR = COLOR_RGB_ELEMENT_ORDER_BGR, /*!< Output the color component in small endian */
JPEG_DEC_RGB_ELEMENT_ORDER_RGB = COLOR_RGB_ELEMENT_ORDER_RGB, /*!< Output the color component in big endian */
} jpeg_dec_rgb_element_order;
/**
* @brief Type of jpeg decoder handle
*/
typedef struct jpeg_decoder_t *jpeg_decoder_handle_t;
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "esp_err.h"
#include "hal/jpeg_ll.h"
#include "esp_private/periph_ctrl.h"
#include "jpeg_private.h"
#include "hal/jpeg_hal.h"
#include "esp_memory_utils.h"
#include "driver/jpeg_types.h"
#include "sys/lock.h"
#include "sdkconfig.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_check.h"
static const char *TAG = "jpeg.common";
typedef struct jpeg_platform_t {
_lock_t mutex; // platform level mutex lock.
jpeg_codec_handle_t jpeg_codec; // JPEG codec instances.
uint32_t count; // reference count used to protect group install/uninstall.
} jpeg_platform_t;
static jpeg_platform_t s_jpeg_platform = {}; // singleton platform
esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec)
{
#if CONFIG_JPEG_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
bool new_codec = false;
jpeg_codec_t *codec = NULL;
_lock_acquire(&s_jpeg_platform.mutex);
if (!s_jpeg_platform.jpeg_codec) {
new_codec = true;
codec = heap_caps_calloc(1, sizeof(jpeg_codec_t), JPEG_MEM_ALLOC_CAPS);
if (codec) {
s_jpeg_platform.jpeg_codec = codec;
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);
// init the clock
PERIPH_RCC_ATOMIC() {
jpeg_ll_enable_bus_clock(true);
jpeg_ll_reset_module_register();
}
jpeg_hal_init(&codec->hal);
} else {
ESP_LOGE(TAG, "No more memory for acquiring JPEG codec module");
ret = ESP_ERR_NO_MEM;
}
}
if (codec) {
s_jpeg_platform.count++;
}
if (new_codec) {
ESP_LOGD(TAG, "new jpeg module has been acquired at (%p)", codec);
}
*jpeg_new_codec = codec;
_lock_release(&s_jpeg_platform.mutex);
return ret;
}
esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec)
{
bool do_deinitialize = false;
_lock_acquire(&s_jpeg_platform.mutex);
if (s_jpeg_platform.jpeg_codec) {
s_jpeg_platform.count--;
if (s_jpeg_platform.count == 0) {
do_deinitialize = true;
s_jpeg_platform.jpeg_codec = NULL;
if (jpeg_codec->codec_mux) {
vSemaphoreDeleteWithCaps(jpeg_codec->codec_mux);
jpeg_codec->codec_mux = NULL;
}
PERIPH_RCC_ATOMIC() {
jpeg_ll_enable_bus_clock(false);
}
free(jpeg_codec);
}
}
if (do_deinitialize) {
ESP_LOGD(TAG, "delete codec");
}
_lock_release(&s_jpeg_platform.mutex);
return ESP_OK;
}

Wyświetl plik

@ -0,0 +1,676 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "esp_err.h"
#include "jpeg_private.h"
#include "private/jpeg_parse_marker.h"
#include "private/jpeg_param.h"
#include "driver/jpeg_decode.h"
#include "esp_heap_caps.h"
#include "esp_private/dma2d.h"
#include "hal/jpeg_ll.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#include "hal/jpeg_defs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "soc/interrupts.h"
#include "soc/dma2d_channel.h"
#include "soc/interrupts.h"
#include "esp_dma_utils.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_cache.h"
#include "esp_check.h"
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);
static esp_err_t jpeg_parse_header_info_to_hw(jpeg_decoder_handle_t decoder_engine);
static bool jpeg_dec_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *users_config);
static void jpeg_decoder_isr_handle_default(void *arg)
{
jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) arg;
jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal;
portBASE_TYPE HPTaskAwoken = pdFALSE;
jpeg_dma2d_dec_evt_t dec_evt = {
.jpgd_status = 0,
.dma_evt = 0,
};
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);
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)
{
#if CONFIG_JPEG_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
jpeg_decoder_handle_t decoder_engine = NULL;
ESP_RETURN_ON_FALSE(dec_eng_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
decoder_engine = (jpeg_decoder_handle_t)heap_caps_calloc(1, sizeof(jpeg_decoder_t), JPEG_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_NO_MEM, TAG, "no memory for jpeg decode");
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);
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");
decoder_engine->txlink = (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->txlink, ESP_ERR_NO_MEM, err, TAG, "no memory for jpeg decode txlink");
decoder_engine->dma_desc_size = dma_desc_mem_size;
decoder_engine->header_info = (jpeg_dec_header_info_t*)heap_caps_calloc(1, sizeof(jpeg_dec_header_info_t), JPEG_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(decoder_engine->header_info, ESP_ERR_NO_MEM, err, TAG, "no memory for picture info");
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;
/// 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);
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);
}
int isr_flags = JPEG_INTR_ALLOC_FLAG;
if (dec_eng_cfg->intr_priority) {
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);
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");
dma2d_pool_config_t dma2d_client_config = {
.pool_id = 0,
};
ESP_GOTO_ON_ERROR(dma2d_acquire_pool(&dma2d_client_config, &decoder_engine->dma2d_group_handle), err, TAG, "dma2d client acquire failed");
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;
return ESP_OK;
err:
if (decoder_engine) {
jpeg_del_decoder_engine(decoder_engine);
}
return ret;
}
esp_err_t jpeg_decoder_get_info(const uint8_t *in_buf, uint32_t inbuf_len, jpeg_decode_picture_info_t *picture_info)
{
ESP_RETURN_ON_FALSE(in_buf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer is NULL");
ESP_RETURN_ON_FALSE(inbuf_len != 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer length is 0");
jpeg_dec_header_info_t* header_info = (jpeg_dec_header_info_t*)heap_caps_calloc(1, sizeof(jpeg_dec_header_info_t), JPEG_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(header_info, ESP_ERR_NO_MEM, TAG, "no memory for picture info");
header_info->buffer_offset = (uint8_t *)in_buf;
header_info->buffer_left = inbuf_len;
header_info->header_size = 0;
uint16_t height = 0;
uint16_t width = 0;
uint8_t thischar = 0;
uint8_t lastchar = 0;
while (header_info->buffer_left) {
lastchar = thischar;
thischar = jpeg_get_bytes(header_info, 1);
uint16_t marker = (lastchar << 8 | thischar);
switch (marker) {
case JPEG_M_SOF0:
jpeg_get_bytes(header_info, 2);
jpeg_get_bytes(header_info, 1);
height = jpeg_get_bytes(header_info, 2);
width = jpeg_get_bytes(header_info, 2);
break;
}
// This function only used for get width and hight. So only read SOF marker is enough.
// Can be extended if picture information is extended.
if (marker == JPEG_M_SOF0) {
break;
}
}
picture_info->height = height;
picture_info->width = width;
free(header_info);
return ESP_OK;
}
esp_err_t jpeg_decoder_process(jpeg_decoder_handle_t decoder_engine, const jpeg_decode_cfg_t *decode_cfg, const uint8_t *bit_stream, uint32_t stream_size, uint8_t *decode_outbuf, uint32_t *out_size)
{
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle 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);
/* Reset queue */
xQueueReset(decoder_engine->evt_handle);
decoder_engine->output_format = decode_cfg->output_format;
decoder_engine->rgb_order = decode_cfg->rgb_order;
decoder_engine->conv_std = decode_cfg->conv_std;
decoder_engine->decoded_buf = decode_outbuf;
ESP_GOTO_ON_ERROR(jpeg_parse_marker(decoder_engine, bit_stream, stream_size), err, TAG, "jpeg parse marker failed");
ESP_GOTO_ON_ERROR(jpeg_parse_header_info_to_hw(decoder_engine), err, TAG, "write header info to hw failed");
ESP_GOTO_ON_ERROR(jpeg_dec_config_dma_descriptor(decoder_engine), err, TAG, "config dma descriptor failed");
dma2d_trans_config_t trans_desc = {
.tx_channel_num = 1,
.rx_channel_num = 1,
.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_RX_REORDER,
.user_config = decoder_engine,
.on_job_picked = jpeg_dec_transaction_on_picked,
};
// Before 2DDMA starts. sync buffer from cache to psram
ret = esp_cache_msync((void*)decoder_engine->header_info->buffer_offset, decoder_engine->header_info->buffer_left, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
assert(ret == ESP_OK);
// Before 2DDMA starts. invalid memory space of decoded buffer
ret = esp_cache_msync((void*)decoder_engine->decoded_buf, decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel, ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
assert(ret == ESP_OK);
ESP_GOTO_ON_ERROR(dma2d_enqueue(decoder_engine->dma2d_group_handle, &trans_desc, decoder_engine->trans_desc), err, TAG, "enqueue dma2d failed");
bool need_yield;
// Blocking for JPEG decode transaction finishes.
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;
}
// 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);
return ESP_ERR_INVALID_STATE;
}
if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) {
break;
}
}
*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;
err:
xSemaphoreGive(decoder_engine->codec_base->codec_mux);
return ret;
}
esp_err_t jpeg_del_decoder_engine(jpeg_decoder_handle_t decoder_engine)
{
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null");
ESP_RETURN_ON_ERROR(jpeg_release_codec_handle(decoder_engine->codec_base), TAG, "release codec failed");
if (decoder_engine) {
if (decoder_engine->rxlink) {
free(decoder_engine->rxlink);
}
if (decoder_engine->txlink) {
free(decoder_engine->txlink);
}
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->dma2d_group_handle) {
dma2d_release_pool(decoder_engine->dma2d_group_handle);
}
free(decoder_engine);
}
return ESP_OK;
}
void * jpeg_alloc_decoder_mem(size_t size)
{
return heap_caps_aligned_calloc(cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA), 1, size, JPEG_MEM_ALLOC_CAPS);
}
/****************************************************************
* DMA related functions
****************************************************************/
static void cfg_desc(jpeg_decoder_handle_t decoder_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, decoder_engine->dma_desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
assert(ret == ESP_OK);
}
static esp_err_t jpeg_dec_config_dma_descriptor(jpeg_decoder_handle_t decoder_engine)
{
ESP_LOGD(TAG, "Config 2DDMA parameter start");
jpeg_dec_format_hb_t best_hb_idx = 0;
color_space_pixel_format_t picture_format;
picture_format.color_type_id = decoder_engine->output_format;
decoder_engine->pixel = color_hal_pixel_format_get_bit_depth(picture_format) / 8;
switch (decoder_engine->output_format) {
case JPEG_DECODE_OUT_FORMAT_RGB888:
best_hb_idx = JPEG_DEC_RGB888_HB;
break;
case JPEG_DECODE_OUT_FORMAT_RGB565:
best_hb_idx = JPEG_DEC_RGB565_HB;
break;
case JPEG_DECODE_OUT_FORMAT_GRAY:
best_hb_idx = JPEG_DEC_GRAY_HB;
break;
default:
ESP_LOGE(TAG, "wrong, we don't support decode to such format.");
return ESP_ERR_NOT_SUPPORTED;
}
uint32_t dma_hb = dec_hb_tbl[decoder_engine->sample_method][best_hb_idx];
uint32_t dma_vb = decoder_engine->header_info->mcuy;
// Configure tx link descriptor
cfg_desc(decoder_engine, decoder_engine->txlink, JPEG_DMA2D_2D_DISABLE, DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, decoder_engine->header_info->buffer_left & JPEG_DMA2D_MAX_SIZE, decoder_engine->header_info->buffer_left & JPEG_DMA2D_MAX_SIZE, JPEG_DMA2D_EOF_NOT_LAST, 1, DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA, (decoder_engine->header_info->buffer_left >> JPEG_DMA2D_1D_HIGH_14BIT), (decoder_engine->header_info->buffer_left >> JPEG_DMA2D_1D_HIGH_14BIT), decoder_engine->header_info->buffer_offset, NULL);
// Configure rx link descriptor
cfg_desc(decoder_engine, decoder_engine->rxlink, 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, decoder_engine->header_info->process_v, decoder_engine->header_info->process_h, decoder_engine->decoded_buf, NULL);
return ESP_OK;
}
static bool jpeg_rx_eof(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data)
{
portBASE_TYPE higher_priority_task_awoken = pdFALSE;
jpeg_dma2d_dec_evt_t dec_evt = {
.jpgd_status = 0,
.dma_evt = 0,
};
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);
return higher_priority_task_awoken;
}
static void jpeg_dec_config_dma_csc(jpeg_decoder_handle_t decoder_engine, dma2d_channel_handle_t rx_chan)
{
dma2d_scramble_order_t post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0;
dma2d_csc_rx_option_t rx_csc_option = DMA2D_CSC_RX_NONE;
// Config output Endians
if (decoder_engine->rgb_order == JPEG_DEC_RGB_ELEMENT_ORDER_RGB) {
if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB565) {
post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_0_1;
} else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB888) {
post_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2;
}
}
// Configure color space conversion option.
if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB565) {
if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT601) {
rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB565_601;
} else if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT709) {
rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB565_709;
}
} else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_RGB888) {
if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT601) {
rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB888_601;
} else if (decoder_engine->conv_std == JPEG_YUV_RGB_CONV_STD_BT709) {
rx_csc_option = DMA2D_CSC_RX_YUV420_TO_RGB888_709;
}
} else if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_GRAY) {
rx_csc_option = DMA2D_CSC_RX_NONE;
}
dma2d_csc_config_t rx_csc = {
.post_scramble = post_scramble,
.rx_csc_option = rx_csc_option,
};
dma2d_configure_color_space_conversion(rx_chan, &rx_csc);
}
static void jpeg_dec_config_dma_trans_ability(jpeg_decoder_handle_t decoder_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 (decoder_engine->sample_method) {
case JPEG_DOWN_SAMPLING_YUV444:
transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8;
break;
case JPEG_DOWN_SAMPLING_YUV422:
transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_16;
break;
case JPEG_DOWN_SAMPLING_YUV420:
transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_16_16;
break;
case JPEG_DOWN_SAMPLING_GRAY:
transfer_ability_config_rx.mb_size = DMA2D_MACRO_BLOCK_SIZE_8_8;
break;
default:
break;
}
dma2d_set_transfer_ability(decoder_engine->dma2d_tx_channel, &transfer_ability_config_tx);
dma2d_set_transfer_ability(decoder_engine->dma2d_rx_channel, &transfer_ability_config_rx);
}
static bool jpeg_dec_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *users_config)
{
assert(channel_num == 2);
jpeg_decoder_handle_t decoder_engine = (jpeg_decoder_handle_t) users_config;
jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal;
ESP_LOGD(TAG, "2ddma transaction callbacks start");
uint32_t rx_idx, tx_idx;
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;
decoder_engine->dma2d_rx_channel = rx_chan;
decoder_engine->dma2d_tx_channel = tx_chan;
// 2ddma connect
dma2d_trigger_t trig_periph = {
.periph = DMA2D_TRIG_PERIPH_JPEG_DECODER,
.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_dec_config_dma_trans_ability(decoder_engine);
jpeg_dec_config_dma_csc(decoder_engine, rx_chan);
dma2d_rx_event_callbacks_t jpeg_dec_cbs = {
.on_recv_eof = jpeg_rx_eof,
};
dma2d_register_rx_event_callbacks(rx_chan, &jpeg_dec_cbs, decoder_engine);
dma2d_set_desc_addr(tx_chan, (intptr_t)decoder_engine->txlink);
dma2d_set_desc_addr(rx_chan, (intptr_t)decoder_engine->rxlink);
dma2d_start(tx_chan);
dma2d_start(rx_chan);
jpeg_ll_process_start(hal->dev);
return false;
}
static esp_err_t jpeg_parse_header_info_to_hw(jpeg_decoder_handle_t decoder_engine)
{
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null");
jpeg_dec_header_info_t *header_info = decoder_engine->header_info;
jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal;
for (int i = 0; i < header_info->qt_tbl_num; i++) {
dqt_func[i](hal->dev, header_info->qt_tbl[i]);
}
jpeg_ll_set_picture_height(hal->dev, header_info->process_v);
jpeg_ll_set_picture_width(hal->dev, header_info->process_h);
jpeg_ll_set_decode_component_num(hal->dev, header_info->nf);
for (int i = 0; i < header_info->nf; i++) {
sof_func[i](hal->dev, header_info->ci[i], header_info->hi[i], header_info->vi[i], header_info->qtid[i]);
}
// If number of image component frame is 3, get the sampling method with sampling factor.
if (header_info->nf == 3) {
switch (header_info->hivi[0]) {
case 0x11:
decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV444;
break;
case 0x21:
decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV422;
break;
case 0x22:
decoder_engine->sample_method = JPEG_DOWN_SAMPLING_YUV420;
break;
default:
ESP_LOGE(TAG, "Sampling factor cannot be recognized");
return ESP_ERR_INVALID_STATE;
}
}
if (header_info->nf == 1) {
if (decoder_engine->output_format != JPEG_DECODE_OUT_FORMAT_GRAY) {
ESP_LOGE(TAG, "your jpg is a gray style picture, but your output format is wrong");
return ESP_ERR_NOT_SUPPORTED;
}
decoder_engine->sample_method = JPEG_DOWN_SAMPLING_GRAY;
}
// Write DHT information
dht_func[0][0](hal, header_info->huffbits[0][0], header_info->huffcode[0][0], header_info->tmp_huff);
dht_func[0][1](hal, header_info->huffbits[0][1], header_info->huffcode[0][1], header_info->tmp_huff);
dht_func[1][0](hal, header_info->huffbits[1][0], header_info->huffcode[1][0], header_info->tmp_huff);
dht_func[1][1](hal, header_info->huffbits[1][1], header_info->huffcode[1][1], header_info->tmp_huff);
if (header_info->dri_marker) {
jpeg_ll_set_restart_interval(hal->dev, header_info->ri);
}
return ESP_OK;
}
static esp_err_t jpeg_parse_marker(jpeg_decoder_handle_t decoder_engine, const uint8_t *in_buf, uint32_t inbuf_len)
{
ESP_RETURN_ON_FALSE(decoder_engine, ESP_ERR_INVALID_ARG, TAG, "jpeg decode handle is null");
ESP_RETURN_ON_FALSE(in_buf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer is NULL");
ESP_RETURN_ON_FALSE(inbuf_len != 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode input buffer length is 0");
jpeg_dec_header_info_t* header_info = decoder_engine->header_info;
jpeg_hal_context_t *hal = &decoder_engine->codec_base->hal;
memset(decoder_engine->header_info, 0, sizeof(jpeg_dec_header_info_t));
decoder_engine->header_info->buffer_offset = (uint8_t*)in_buf;
decoder_engine->header_info->buffer_left = inbuf_len;
decoder_engine->total_size = inbuf_len;
decoder_engine->header_info->header_size = 0;
jpeg_ll_soft_rst(hal->dev);
jpeg_ll_set_codec_mode(hal->dev, JPEG_CODEC_DECODER);
// Digital issue. Needs to set height and width to 0 before decoding a new picture.
jpeg_ll_set_picture_height(hal->dev, 0);
jpeg_ll_set_picture_width(hal->dev, 0);
while (header_info->buffer_left) {
uint8_t lastchar = jpeg_get_bytes(header_info, 1);
uint8_t thischar = jpeg_get_bytes(header_info, 1);
uint16_t marker = (lastchar << 8 | thischar);
switch (marker) {
case JPEG_M_SOI:
break;
case JPEG_M_APP0:
case JPEG_M_APP1:
case JPEG_M_APP2:
case JPEG_M_APP3:
case JPEG_M_APP4:
case JPEG_M_APP5:
case JPEG_M_APP6:
case JPEG_M_APP7:
case JPEG_M_APP8:
case JPEG_M_APP9:
case JPEG_M_APP10:
case JPEG_M_APP11:
case JPEG_M_APP12:
case JPEG_M_APP13:
case JPEG_M_APP14:
case JPEG_M_APP15:
ESP_RETURN_ON_ERROR(jpeg_parse_appn_marker(header_info), TAG, "deal appn marker failed");
break;
case JPEG_M_COM:
ESP_RETURN_ON_ERROR(jpeg_parse_com_marker(header_info), TAG, "deal com marker failed");
break;
case JPEG_M_DQT:
ESP_RETURN_ON_ERROR(jpeg_parse_dqt_marker(header_info), TAG, "deal dqt marker failed");
break;
case JPEG_M_SOF0:
ESP_RETURN_ON_ERROR(jpeg_parse_sof_marker(header_info), TAG, "deal sof marker failed");
break;
case JPEG_M_SOF1:
case JPEG_M_SOF2:
case JPEG_M_SOF3:
case JPEG_M_SOF5:
case JPEG_M_SOF6:
case JPEG_M_SOF7:
case JPEG_M_SOF9:
case JPEG_M_SOF10:
case JPEG_M_SOF11:
case JPEG_M_SOF13:
case JPEG_M_SOF14:
case JPEG_M_SOF15:
ESP_LOGE(TAG, "Only baseline-DCT is supported.");
return ESP_ERR_NOT_SUPPORTED;
case JPEG_M_DRI:
ESP_RETURN_ON_ERROR(jpeg_parse_dri_marker(header_info), TAG, "deal dri marker failed");
break;
case JPEG_M_DHT:
ESP_RETURN_ON_ERROR(jpeg_parse_dht_marker(header_info), TAG, "deal dht marker failed");
break;
case JPEG_M_SOS:
ESP_RETURN_ON_ERROR(jpeg_parse_sos_marker(header_info), TAG, "deal sos marker failed");
break;
}
if (marker == JPEG_M_SOS) {
break;
}
}
// Update information after parse marker finishes
decoder_engine->header_info->buffer_left = decoder_engine->total_size - decoder_engine->header_info->header_size;
return ESP_OK;
}
static void s_decoder_error_log_print(uint32_t status)
{
if (status & JPEG_LL_INTR_CID_ERR) {
ESP_LOGE(TAG, "decoded component ID is different from the component ID configured by the software");
}
if (status & JPEG_LL_INTR_C_DHT_DC_ID) {
ESP_LOGE(TAG, "decoded DC Huffman table ID of each component is not the software configured DC0 Huffman table ID or DC1 Huffman table ID");
}
if (status & JPEG_LL_INTR_C_DHT_AC_ID) {
ESP_LOGE(TAG, "decoded AC Huffman table ID of each component is not the software configured AC0 Huffman table ID or AC1 Huffman table ID");
}
if (status & JPEG_LL_INTR_C_DQT_ID) {
ESP_LOGE(TAG, "decoded quantization table ID of each component is different from the software configured quantization table ID");
}
if (status & JPEG_LL_INTR_RST_UXP_ERR) {
ESP_LOGE(TAG, "JPEG_RESTART_INTERVAL configured by the software is 0 but the RST marker is parsed by the decoder");
}
if (status & JPEG_LL_INTR_RST_CHECK_NON_ERR) {
ESP_LOGE(TAG, "JPEG_RESTART_INTERVAL configured by the software is non-0 but the RST marker cannot be parsed by the decoder");
}
if (status & JPEG_LL_INTR_RST_CHECK_POS_ERR) {
ESP_LOGE(TAG, "the MCU number between two parsed RST markers is not equal to the JPEG_RESTART_INTERVAL configured by the software");
}
if (status & JPEG_LL_INTR_SCAN_CHECK_NONE) {
ESP_LOGE(TAG, "an image frame has multiple SCANs to be decoded and the SOS marker is not parsed within (JPEG_SOS_CHECK_BYTE_NUM + 1) bytes in any SCAN header information");
}
if (status & JPEG_LL_INTR_SCAN_POS_ERR) {
ESP_LOGE(TAG, "the position of the SCAN header information parsed by the decoder is wrong");
}
if (status & JPEG_LL_INTR_UXP_DET) {
ESP_LOGE(TAG, "marker parsed by the decoder is not supported by the hardware");
}
if (status & JPEG_LL_INTR_DE_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");
}
if (status & JPEG_LL_INTR_DE_FRAME_EOF_LACK) {
ESP_LOGE(TAG, "the bitstream of a image is completely read from 2D DMA but the EOF marker or EOI marker is not read");
}
if (status & JPEG_LL_INTR_SOS_UNMATCH_ERR) {
ESP_LOGE(TAG, "the number of components in the SCAN header information parsed by the decoder is 0 or the header length in the SCAN header information parsed by the decoder does not match the actual header length.");
}
if (status & JPEG_LL_INTR_MARKER_ERR_FST) {
ESP_LOGE(TAG, "there is an error in the first SCAN header information parsed by the decoder.");
}
if (status & JPEG_LL_INTR_MARKER_ERR_OTHER) {
ESP_LOGE(TAG, "there is an error in the non-first SCAN header information parsed by the decoder.");
}
if (status & JPEG_LL_INTR_UNDET) {
ESP_LOGE(TAG, "the bitstream of a image is completely read from 2D DMA but the SOS marker is not read");
}
if (status & JPEG_LL_INTR_DECODE_TIMEOUT) {
ESP_LOGE(TAG, "decoder is timeout");
}
}

Wyświetl plik

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "driver/jpeg_types.h"
#include "jpeg_private.h"
/**
* @brief Zigzag scan pattern array.
*
* This array represents a zigzag scan pattern for reordering the coefficients
* in a block of data. It is commonly used in JPEG compression and decompression
* algorithms to optimize encoding and decoding processes.
*
* The array consists of 64 elements, where each element represents the position
* of a coefficient in the zigzag pattern. The coefficients are traversed in the
* order specified by this array to achieve efficient compression and decompression.
*
* @note The values in this array are zero-indexed.
*/
const uint8_t zigzag_arr[64] = {
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63
};
/**
* @brief DMA2D best hb value table for JPEG decompression.
*
* This two-dimensional array represents a Huffman decoding table for JPEG
* decompression. It is used to decode the Huffman-coded symbols in the compressed
* data stream during the decoding process.
*/
const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX] = {
{40, 40, 40, 32, 0},
{64, 32, 32, 64, 0},
{48, 32, 32, 48, 0},
{96, 0, 0, 0, 96},
};

Wyświetl plik

@ -0,0 +1,187 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "esp_err.h"
#include <math.h>
#include "jpeg_private.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "private/jpeg_param.h"
#include "hal/jpeg_types.h"
#include "esp_check.h"
#include "sdkconfig.h"
static const char *TAG = "jpeg.decoder";
static uint8_t jpeg_get_char(jpeg_dec_header_info_t *header_info)
{
uint8_t c = header_info->buffer_offset[0];
header_info->buffer_offset++;
header_info->header_size++;
header_info->buffer_left--;
return c;
}
uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes)
{
uint32_t result = 0;
for (int i = 0; i < num_bytes; i++) {
uint8_t byte = jpeg_get_char(header_info);
result |= (byte << ((num_bytes - i - 1) * 8));
}
return result;
}
esp_err_t jpeg_parse_appn_marker(jpeg_dec_header_info_t *header_info)
{
uint32_t skip_num = jpeg_get_bytes(header_info, 2);
header_info->buffer_offset += (skip_num - 2);
header_info->header_size += (skip_num - 2);
header_info->buffer_left -= (skip_num - 2);
return ESP_OK;
}
esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info)
{
uint32_t skip_num = jpeg_get_bytes(header_info, 2);
header_info->buffer_offset += (skip_num - 2);
header_info->header_size += (skip_num - 2);
header_info->buffer_left -= (skip_num - 2);
return ESP_OK;
}
esp_err_t jpeg_parse_dqt_marker(jpeg_dec_header_info_t *header_info)
{
uint32_t n = 0, i = 0, prec = 0;
uint32_t temp = 0;
uint32_t length_num = jpeg_get_bytes(header_info, 2);
length_num -= 2;
while (length_num) {
n = jpeg_get_bytes(header_info, 1);
prec = n >> 4;
n &= 0x0F;
length_num -= 1;
// read quantization entries, in zig-zag order
for (i = 0; i < 64; i++) {
temp = jpeg_get_bytes(header_info, 1);
length_num -= 1;
if (prec) {
temp = (temp << 8) + jpeg_get_bytes(header_info, 1);
length_num -= 1;
}
header_info->qt_tbl[n][zigzag_arr[i]] = temp;
}
header_info->qt_tbl_num++;
}
return ESP_OK;
}
esp_err_t jpeg_parse_sof_marker(jpeg_dec_header_info_t *header_info)
{
jpeg_get_bytes(header_info, 2);
if (jpeg_get_bytes(header_info, 1) != 8) {
ESP_LOGE(TAG, "Sample precision is not 8");
return ESP_ERR_INVALID_STATE;
}
uint16_t height = jpeg_get_bytes(header_info, 2);
header_info->origin_v = height;
header_info->process_v = height;
uint16_t width = jpeg_get_bytes(header_info, 2);
header_info->origin_h = width;
header_info->process_h = width;
if ((width * height % 8) != 0) {
ESP_LOGE(TAG, "Picture sizes not divisible by 8 are not supported");
return ESP_ERR_INVALID_STATE;
}
uint8_t nf = jpeg_get_bytes(header_info, 1);
if (nf >= 4 || nf == 0) {
ESP_LOGE(TAG, "Only frame less or equal than 4 is supported.");
return ESP_ERR_INVALID_STATE;
}
header_info->nf = nf;
for (int i = 0; i < nf; i++) {
header_info->ci[i] = jpeg_get_bytes(header_info, 1);
header_info->hivi[i] = jpeg_get_bytes(header_info, 1);
header_info->vi[i] = header_info->hivi[i] & 0x0f;
header_info->hi[i] = (header_info->hivi[i] & 0xf0) >> 4;
header_info->qtid[i] = jpeg_get_bytes(header_info, 1);
}
// Set MCU block pixel according to factor. (For 3 components, we only use Y factor)
header_info->mcux = header_info->hi[0] * 8;
header_info->mcuy = header_info->vi[0] * 8;
// The vertical and horizontal in process must be divided by mcu block.
if (header_info->origin_v % header_info->mcuy != 0) {
header_info->process_v = (ceil(header_info->origin_v / header_info->mcuy) + 1) * header_info->mcuy;
}
if (header_info->origin_h % header_info->mcux != 0) {
header_info->process_h = (ceil(header_info->origin_h / header_info->mcux) + 1) * header_info->mcux;
}
return ESP_OK;
}
esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info)
{
// Recording num_left in DHT sector, not including length bytes (2 bytes).
uint32_t num_left = jpeg_get_bytes(header_info, 2) - 2;
while (num_left) {
uint32_t np = 0;
// Get information of huffman table
header_info->huffinfo.info = jpeg_get_bytes(header_info, 1);
for (int i = 0; i < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; i++) {
header_info->huffbits[header_info->huffinfo.type][header_info->huffinfo.id][i] = jpeg_get_bytes(header_info, 1);
// Record number of patterns.
np += header_info->huffbits[header_info->huffinfo.type][header_info->huffinfo.id][i];
}
for (int i = 0; i < np; i++) {
header_info->huffcode[header_info->huffinfo.type][header_info->huffinfo.id][i] = jpeg_get_bytes(header_info, 1);
}
num_left -= (1 + JPEG_HUFFMAN_BITS_LEN_TABLE_LEN + np);
}
return ESP_OK;
}
esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info)
{
uint16_t lr = jpeg_get_bytes(header_info, 2);
if (lr != 4) {
ESP_LOGE(TAG, "DRI marker got but stream length is insufficient, the length you got is %" PRIu16, lr);
return ESP_ERR_INVALID_SIZE;
}
header_info->ri = jpeg_get_bytes(header_info, 2);
header_info->dri_marker = true;
return ESP_OK;
}
esp_err_t jpeg_parse_sos_marker(jpeg_dec_header_info_t *header_info)
{
// Got the SOS marker, but need to recover this and feed to 2DDMA.
header_info->buffer_offset -= 2;
header_info->header_size -= 2;
header_info->buffer_left += 2;
return ESP_OK;
}

Wyświetl plik

@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdatomic.h>
#include "esp_private/dma2d.h"
#include "driver/jpeg_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "hal/jpeg_hal.h"
#include "esp_intr_types.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define JPEG_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
#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)
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;
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
};
typedef enum {
// TODO: Support DR and YUV444 on decoder.
//JPEG_DEC_DR_HB = 0, /*!< Direct output */
//JPEG_DEC_YUV444_HB = 1, /*!< output YUV444 format */
JPEG_DEC_RGB888_HB = 2, /*!< output RGB888 format */
JPEG_DEC_RGB565_HB = 3, /*!< output RGB565 format */
JPEG_DEC_GRAY_HB = 4, /*!< output the gray picture */
JPEG_DEC_BEST_HB_MAX, /*!< Max value of output formats */
} jpeg_dec_format_hb_t;
typedef struct {
uint8_t *buffer_offset; // Pointer points to picture buffer.
uint32_t buffer_left; // Data left in the picture.
uint32_t header_size; // record the picture header size.
uint32_t process_v; // vertical output in processing, multiple of mcu.
uint32_t process_h; // horizontal output in processing, multiple of mcu.
uint32_t origin_v; // vertical for the origin size of picture.
uint32_t origin_h; // horizontal for the origin size of picture.
uint8_t mcux; // the best value of minimum coding unit horizontal unit
uint8_t mcuy; // minimum coding unit vertical unit
uint8_t qt_tbl_num; // quantization table number
uint32_t qt_tbl[JPEG_COMPONENT_NUMBER_MAX][JPEG_QUANTIZATION_TABLE_LEN]; // quantization table content [id]
uint8_t nf; // number of frames
uint8_t ci[JPEG_COMPONENT_NUMBER_MAX]; // Component identifier.
uint8_t hivi[JPEG_COMPONENT_NUMBER_MAX]; // H&V sampling factor
uint8_t hi[JPEG_COMPONENT_NUMBER_MAX]; // Horizontal sampling factor
uint8_t vi[JPEG_COMPONENT_NUMBER_MAX]; // Vertical sampling factor
uint8_t qtid[JPEG_COMPONENT_NUMBER_MAX]; // Quantization table destination selector
jpeg_huffman_table_info_t huffinfo; // Huffman table specification information
uint8_t huffbits[2][2][JPEG_HUFFMAN_BITS_LEN_TABLE_LEN]; // Huffman bit distribution tables [id][dcac]
uint8_t huffcode[2][2][JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; // Huffman decoded data tables [id][dcac]
uint32_t tmp_huff[JPEG_HUFFMAN_AC_VALUE_TABLE_LEN]; // temp buffer to store huffman code
bool dri_marker; // If we have dri marker in table
uint8_t ri; // Restart interval
} jpeg_dec_header_info_t;
struct jpeg_decoder_t {
jpeg_codec_t *codec_base; // Pointer to jpeg codec hardware base
jpeg_dec_header_info_t *header_info; // Pointer to current picture information
jpeg_down_sampling_type_t sample_method; // method of sampling the JPEG picture.
jpeg_dec_output_format_t output_format; // picture output format.
jpeg_dec_rgb_element_order rgb_order; // RGB pixel order
jpeg_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
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
//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_channel_handle_t dma2d_rx_channel; // DMA2D RX channel handle
dma2d_channel_handle_t dma2d_tx_channel; // DMA2D TX channel handle
dma2d_trans_t* trans_desc; // DMA2D transaction descriptor
};
typedef enum {
JPEG_DMA2D_RX_EOF = BIT(0), // DMA2D rx eof event
JPEG_DMA2D_RX_DONE = BIT(1), // DMA2D rx done event
JPEG_DMA2D_TX_DONE = BIT(2), // DMA2D tx done event
} jpeg_dma2d_evt_enum_t;
typedef struct {
jpeg_dma2d_evt_enum_t dma_evt; // jpeg-2ddma event, (triggered from 2ddma interrupt)
uint32_t jpgd_status; // jpeg decoder status, (triggered from jpeg interrupt)
} jpeg_dma2d_dec_evt_t;
#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)
#define JPEG_DMA2D_EOF_LAST (1) // DMA2D EOF last
#define JPEG_DMA2D_EOF_NOT_LAST (0) // DMA2D EOF not last
#define JPEG_DMA2D_1D_HIGH_14BIT (14) // 1D high 14 bites
/**
* @brief Acquire a JPEG codec handle.
*
* @param[out] jpeg_new_codec Pointer to the variable where the acquired JPEG codec handle will be stored.
*
* @return
* - ESP_OK if the JPEG codec handle is successfully acquired.
* - ESP_ERR_NO_MEM if there is not enough memory to acquire the codec handle.
* - Other error codes indicating the cause of the failure.
*/
esp_err_t jpeg_acquire_codec_handle(jpeg_codec_handle_t *jpeg_new_codec);
/**
* @brief Release a JPEG codec handle.
*
* @param[in] jpeg_codec The JPEG codec handle to release.
*
* @return
* - ESP_OK if the JPEG codec handle is successfully released.
* - ESP_ERR_INVALID_ARG if the provided JPEG codec handle is invalid.
* - Other error codes indicating the cause of the failure.
*/
esp_err_t jpeg_release_codec_handle(jpeg_codec_handle_t jpeg_codec);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "driver/jpeg_types.h"
#include "../jpeg_private.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Zigzag scan pattern array.
*
* This array represents a zigzag scan pattern for reordering the coefficients
* in a block of data. It is commonly used in JPEG compression and decompression
* algorithms to optimize encoding and decoding processes.
*
* The array consists of 64 elements, where each element represents the position
* of a coefficient in the zigzag pattern. The coefficients are traversed in the
* order specified by this array to achieve efficient compression and decompression.
*
* @note The values in this array are zero-indexed.
*/
extern const uint8_t zigzag_arr[64];
/**
* @brief DMA2D best hb value table for JPEG decompression.
*
* This two-dimensional array represents a Huffman decoding table for JPEG
* decompression. It is used to decode the Huffman-coded symbols in the compressed
* data stream during the decoding process.
*/
extern const uint32_t dec_hb_tbl[JPEG_DOWN_SAMPLING_MAX][JPEG_DEC_BEST_HB_MAX];
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/jpeg_types.h"
#include "../jpeg_private.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Retrieves a specified number of bytes from the JPEG decoder.
*
* @param header_info The handle to the JPEG information.
* @param num_bytes The number of bytes to retrieve from the decoder.
*
* @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);
/**
* @brief Parses with the APPn (application specific) marker in a JPEG file.
*
* This function is called when the decoder encounters an APPn marker in the
* input stream. The function handles any application-specific data contained
* within the marker and performs necessary processing or actions based on the
* specific application requirements.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_appn_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the COM (comment) marker in a JPEG file.
*
* This function is called when the decoder encounters a COM marker in the input stream.
* The function handles any comment data contained within the marker and performs any
* necessary processing or actions based on the comment information.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_com_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the DQT (quantization table) marker in a JPEG file.
*
* This function is called when the decoder encounters a DQT marker in the input stream.
* The function handles the quantization table data contained within the marker and
* performs any necessary processing or actions based on the quantization table information.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_dqt_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the SOF (Start of Frame) marker in a JPEG file.
*
* This function only used for verify there is an SOF marker, the content of frame
* would be handle in hardware.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_sof_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the DHT (Huffman table) marker in a JPEG file.
*
* This function is called when the decoder encounters a DHT marker in the input stream.
* The function handles the Huffman table data contained within the marker and performs
* any necessary processing or actions based on the Huffman table information.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_dht_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the SOS (Start of Scan) marker in a JPEG file.
*
* This function is called when the decoder encounters a SOS marker in the input stream.
* The function handles the scan header data contained within the marker and performs
* any necessary processing or actions based on the scan information.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_sos_marker(jpeg_dec_header_info_t *header_info);
/**
* @brief Parses with the DRI (Define Restart Interval) marker in a JPEG file.
*
* This function is called when the decoder encounters a DRI marker in the input stream.
* The function handles the restart interval data contained within the marker and performs
* any necessary processing or actions based on the restart interval information.
*
* @param[in] header_info Pointer to the JPEG picture information.
*
* @return ESP_OK on success, or an appropriate error code if an error occurred.
*/
esp_err_t jpeg_parse_dri_marker(jpeg_dec_header_info_t *header_info);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -8,6 +8,7 @@
#include "esp_assert.h"
#include "hal/lcd_types.h"
#include "hal/mipi_dsi_types.h"
#include "hal/color_types.h"
#ifdef __cplusplus
extern "C" {
@ -34,8 +35,8 @@ typedef struct esp_lcd_panel_t *esp_lcd_panel_handle_t; /*!< Type of LCD p
* @brief RGB element order
*/
typedef enum {
LCD_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */
LCD_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */
LCD_RGB_ELEMENT_ORDER_RGB = COLOR_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */
LCD_RGB_ELEMENT_ORDER_BGR = COLOR_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */
} lcd_rgb_element_order_t;
/** @cond */

Wyświetl plik

@ -213,6 +213,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND srcs "brownout_hal.c")
endif()
if(CONFIG_SOC_JPEG_CODEC_SUPPORTED)
list(APPEND srcs "jpeg_hal.c")
endif()
if(CONFIG_SOC_GPSPI_SUPPORTED)
list(APPEND srcs
"spi_hal.c"

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -112,7 +112,7 @@ static inline void jpeg_ll_reset_module_register(void)
static inline void jpeg_ll_dht_ac0_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table)
{
uint32_t element_number = 0;
for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
element_number += (uint32_t)huffman_bits_table[idx];
hw->dht_totlen_ac0.dht_totlen_ac0 = element_number;
hw->dht_codemin_ac0.dht_codemin_ac0 = minimum_code_table[idx];
@ -143,7 +143,7 @@ static inline void jpeg_ll_dht_ac0_write_value(jpeg_dev_t *hw, uint8_t *huffman_
static inline void jpeg_ll_dht_ac1_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table)
{
uint32_t element_number = 0;
for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
element_number += (uint32_t)huffman_bits_table[idx];
hw->dht_totlen_ac1.dht_totlen_ac1 = element_number;
hw->dht_codemin_ac1.dht_codemin_ac1 = minimum_code_table[idx];
@ -174,7 +174,7 @@ static inline void jpeg_ll_dht_ac1_write_value(jpeg_dev_t *hw, uint8_t *huffman_
static inline void jpeg_ll_dht_dc0_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table)
{
uint32_t element_number = 0;
for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
element_number += (uint32_t)huffman_bits_table[idx];
hw->dht_totlen_dc0.dht_totlen_dc0 = element_number;
hw->dht_codemin_dc0.dht_codemin_dc0 = minimum_code_table[idx];
@ -205,7 +205,7 @@ static inline void jpeg_ll_dht_dc0_write_value(jpeg_dev_t *hw, uint8_t *huffman_
static inline void jpeg_ll_dht_dc1_write_codeword(jpeg_dev_t *hw, uint8_t *huffman_bits_table, uint32_t *minimum_code_table)
{
uint32_t element_number = 0;
for (int idx = 0; idx < JEPG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
for (int idx = 0; idx < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; idx++) {
element_number += (uint32_t)huffman_bits_table[idx];
hw->dht_totlen_dc1.dht_totlen_dc1 = element_number;
hw->dht_codemin_dc1.dht_codemin_dc1 = minimum_code_table[idx];

Wyświetl plik

@ -138,6 +138,18 @@ typedef enum {
COLOR_CONV_STD_RGB_YUV_BT709, /*!< YUV<->RGB conversion standard: BT.709 */
} color_conv_std_rgb_yuv_t;
/*---------------------------------------------------------------
Color Endian
---------------------------------------------------------------*/
/**
* @brief RGB element order
*/
typedef enum {
COLOR_RGB_ELEMENT_ORDER_RGB, /*!< RGB element order: RGB */
COLOR_RGB_ELEMENT_ORDER_BGR, /*!< RGB element order: BGR */
} color_rgb_element_order_t;
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* @brief Enum to define JPEG marker codes
*/
typedef enum {
JPEG_M_SOF0 = 0xFFC0, ///< Start of Frame 0
JPEG_M_SOF1 = 0xFFC1, ///< Start of Frame 1
JPEG_M_SOF2 = 0xFFC2, ///< Start of Frame 2
JPEG_M_SOF3 = 0xFFC3, ///< Start of Frame 3
JPEG_M_SOF5 = 0xFFC5, ///< Start of Frame 5
JPEG_M_SOF6 = 0xFFC6, ///< Start of Frame 6
JPEG_M_SOF7 = 0xFFC7, ///< Start of Frame 7
JPEG_M_JPG = 0xFFC8, ///< JPEG Extension
JPEG_M_SOF9 = 0xFFC9, ///< Start of Frame 9
JPEG_M_SOF10 = 0xFFCA, ///< Start of Frame 10
JPEG_M_SOF11 = 0xFFCB, ///< Start of Frame 11
JPEG_M_SOF13 = 0xFFCD, ///< Start of Frame 13
JPEG_M_SOF14 = 0xFFCE, ///< Start of Frame 14
JPEG_M_SOF15 = 0xFFCF, ///< Start of Frame 15
JPEG_M_DHT = 0xFFC4, ///< Define Huffman Table
JPEG_M_DAC = 0xFFCC, ///< Define Arithmetic Coding Conditioning
JPEG_M_RST0 = 0xFFD0, ///< Restart with modulo 8 count 0
JPEG_M_RST1 = 0xFFD1, ///< Restart with modulo 8 count 1
JPEG_M_RST2 = 0xFFD2, ///< Restart with modulo 8 count 2
JPEG_M_RST3 = 0xFFD3, ///< Restart with modulo 8 count 3
JPEG_M_RST4 = 0xFFD4, ///< Restart with modulo 8 count 4
JPEG_M_RST5 = 0xFFD5, ///< Restart with modulo 8 count 5
JPEG_M_RST6 = 0xFFD6, ///< Restart with modulo 8 count 6
JPEG_M_RST7 = 0xFFD7, ///< Restart with modulo 8 count 7
JPEG_M_SOI = 0xFFD8, ///< Start of Image
JPEG_M_EOI = 0xFFD9, ///< End of Image
JPEG_M_SOS = 0xFFDA, ///< Start of Scan
JPEG_M_DQT = 0xFFDB, ///< Define Quantization Table
JPEG_M_DNL = 0xFFDC, ///< Define Number of Lines
JPEG_M_DRI = 0xFFDD, ///< Define Restart Interval
JPEG_M_DHP = 0xFFDE, ///< Define Hierarchical Progression
JPEG_M_EXP = 0xFFDF, ///< Expand Reference Component(s)
JPEG_M_APP0 = 0xFFE0, ///< Application Segment 0
JPEG_M_APP1 = 0xFFE1, ///< Application Segment 1
JPEG_M_APP2 = 0xFFE2, ///< Application Segment 2
JPEG_M_APP3 = 0xFFE3, ///< Application Segment 3
JPEG_M_APP4 = 0xFFE4, ///< Application Segment 4
JPEG_M_APP5 = 0xFFE5, ///< Application Segment 5
JPEG_M_APP6 = 0xFFE6, ///< Application Segment 6
JPEG_M_APP7 = 0xFFE7, ///< Application Segment 7
JPEG_M_APP8 = 0xFFE8, ///< Application Segment 8
JPEG_M_APP9 = 0xFFE9, ///< Application Segment 9
JPEG_M_APP10 = 0xFFEA, ///< Application Segment 10
JPEG_M_APP11 = 0xFFEB, ///< Application Segment 11
JPEG_M_APP12 = 0xFFEC, ///< Application Segment 12
JPEG_M_APP13 = 0xFFED, ///< Application Segment 13
JPEG_M_APP14 = 0xFFEE, ///< Application Segment 14
JPEG_M_APP15 = 0xFFEF, ///< Application Segment 15
JPEG_M_JPG0 = 0xFFF0, ///< Reserved for JPEG extension
JPEG_M_JPG13 = 0xFFFD, ///< Reserved for JPEG extension
JPEG_M_COM = 0xFFFE, ///< Comment
JPEG_M_TEM = 0xFF01, ///< Temporary use in arithmetic coding
} __attribute__((packed)) jpeg_marker_code_t;
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,7 @@
#pragma once
#include <stdint.h>
#include "hal/jpeg_types.h"
#ifdef __cplusplus
extern "C" {
@ -41,6 +42,67 @@ void jpeg_hal_init(jpeg_hal_context_t *hal);
*/
void jpeg_hal_deinit(jpeg_hal_context_t *hal);
/**
* @brief Function pointer typedef for configuring DHT tables in JPEG decoding.
*
* This function pointer typedef represents a callback function that can be used
* to configure the DHT (Huffman) tables in the JPEG decoding process. It takes
* as input a JPEG HAL context, as well as pointers to arrays representing the
* minimum codes, bit lengths, and Huffman codes for the DHT tables.
*
* @param hal The JPEG HAL context.
* @param huffbits Pointer to the array of bit lengths for the DHT tables.
* @param huffcode Pointer to the array of Huffman codes for the DHT tables.
*/
typedef void (*jpeg_config_dht_table_t)(jpeg_hal_context_t *hal, uint8_t *huffbits, uint8_t *huffcode, uint32_t *tmp_huff);
/**
* @brief Array of function pointers for configuring DHT tables in JPEG decoding.
*
* This two-dimensional array represents a collection of function pointers that
* can be used to configure the DHT (Huffman) tables in the JPEG decoding process.
*/
extern jpeg_config_dht_table_t dht_func[DHT_TC_NUM][DHT_TH_NUM];
/**
* Typedef for a function pointer to configure frame information in JPEG.
*
* This function is used to configure frame information in JPEG hardware.
*
* @param hw The JPEG SOC handle.
* @param identifier The identifier of the frame.
* @param horizontal_factor The horizontal factor for chroma subsampling.
* @param vertical_factor The vertical factor for chroma subsampling.
* @param qun_table_id The quantization table ID.
*/
typedef void (*jpeg_config_frame_info_t)(jpeg_soc_handle_t hw, uint8_t identifier, uint8_t horizontal_factor, uint8_t vertical_factor, uint8_t qun_table_id);
/**
* Array of function pointers to configure frame information in JPEG.
*
* This array holds function pointers to configure frame information in JPEG hardware.
* Each element corresponds to a specific component number.
*/
extern jpeg_config_frame_info_t sof_func[JPEG_COMPONENT_NUMBER_MAX];
/**
* Typedef for a function pointer to configure quantization coefficients in JPEG.
*
* This function is used to configure quantization coefficients in JPEG hardware.
*
* @param hw The JPEG SOC handle.
* @param quantization_table Pointer to the quantization table.
*/
typedef void (*jpeg_config_quantization_coefficient_t)(jpeg_soc_handle_t hw, uint32_t *quantization_table);
/**
* Array of function pointers to configure quantization coefficients in JPEG.
*
* This array holds function pointers to configure quantization coefficients in JPEG hardware.
* Each element corresponds to a specific component number.
*/
extern jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX];
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,10 +15,14 @@
extern "C" {
#endif
#define JEPG_HUFFMAN_BITS_LEN_TABLE_LEN (16)
#define JPEG_HUFFMAN_BITS_LEN_TABLE_LEN (16)
#define JPEG_HUFFMAN_AC_VALUE_TABLE_LEN (256)
#define JPEG_HUFFMAN_DC_VALUE_TABLE_LEN (16)
#define JPEG_QUANTIZATION_TABLE_LEN (64)
#define JPEG_COMPONENT_NUMBER_MAX (4)
#define DHT_TC_NUM (2) /// Table type
#define DHT_TH_NUM (2) /// Huffman table destination identifier
/**
* @brief Enum for JPEG codec working mode.
@ -37,7 +41,7 @@ typedef struct {
} jpeg_component_factor_t;
/**
* @brief Enum for JEPG sampling mode.
* @brief Enum for JPEG sampling mode.
*/
typedef enum {
JPEG_SAMPLE_MODE_YUV444 = COLOR_PIXEL_YUV444, ///< sample in YUV444
@ -45,6 +49,27 @@ typedef enum {
JPEG_SAMPLE_MODE_YUV420 = COLOR_PIXEL_YUV420, ///< sample in YUV420
} jpeg_sample_mode_t;
/**
* @brief Structure for huffman information
*/
typedef union {
struct {
uint8_t id : 4; ///< Huffman table id
uint8_t type : 4; ///< Huffman table type
};
uint8_t info; ///< Information of huffman table
} jpeg_huffman_table_info_t;
/**
* @brief Enumeration for jpeg decoder sample methods.
*/
typedef enum {
JPEG_DOWN_SAMPLING_YUV444 = 0, /*!< Sample by YUV444 */
JPEG_DOWN_SAMPLING_YUV422 = 1, /*!< Sample by YUV422 */
JPEG_DOWN_SAMPLING_YUV420 = 2, /*!< Sample by YUV420 */
JPEG_DOWN_SAMPLING_GRAY = 3, /*!< Sample the gray picture */
JPEG_DOWN_SAMPLING_MAX, /*!< Max value of sample enumeration */
} jpeg_down_sampling_type_t;
#ifdef __cplusplus
}

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,3 +17,81 @@ void jpeg_hal_deinit(jpeg_hal_context_t *hal)
{
hal->dev = NULL;
}
/*-----------------------------------------------------------------------*/
/* Config huffman code tables with a DHT segment */
/*-----------------------------------------------------------------------*/
static void jpeg_create_minicode_tbl(uint8_t *huffbits, uint32_t *huffmin, uint32_t *tmp_huff)
{
int total_len = 0;
/* Re-build huffman code word table */
for (int j = 0, i = 0, hc = 0; i < JPEG_HUFFMAN_BITS_LEN_TABLE_LEN; i++, hc <<= 1) {
int b = huffbits[i];
// If a codeword length does not exist, the configuration value is 0xFFFF.
if (huffbits[i] == 0) {
huffmin[i] = 0xffff;
continue;
}
total_len += huffbits[i];
while (b--) {
tmp_huff[j++] = hc++;
}
if (huffbits[i] != 0) {
huffmin[i] = tmp_huff[total_len - huffbits[i]];
huffmin[i] <<= (15 - i);
}
}
}
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_ll_dht_ac0_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_ac0_write_value(hal->dev, huffcode);
}
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_ll_dht_ac1_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_ac1_write_value(hal->dev, huffcode);
}
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_ll_dht_dc0_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_dc0_write_value(hal->dev, huffcode);
}
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_ll_dht_dc1_write_codeword(hal->dev, huffbits, huffmin);
jpeg_ll_dht_dc1_write_value(hal->dev, huffcode);
}
jpeg_config_dht_table_t dht_func[DHT_TC_NUM][DHT_TH_NUM] = {
{jpeg_hal_config_dc0_table, jpeg_hal_config_dc1_table},
{jpeg_hal_config_ac0_table, jpeg_hal_config_ac1_table},
};
jpeg_config_frame_info_t sof_func[JPEG_COMPONENT_NUMBER_MAX] = {
jpeg_ll_set_frame_info_component0,
jpeg_ll_set_frame_info_component1,
jpeg_ll_set_frame_info_component2,
jpeg_ll_set_frame_info_component3,
};
jpeg_config_quantization_coefficient_t dqt_func[JPEG_COMPONENT_NUMBER_MAX] = {
jpeg_ll_write_quantization_coefficient_t0,
jpeg_ll_write_quantization_coefficient_t1,
jpeg_ll_write_quantization_coefficient_t2,
jpeg_ll_write_quantization_coefficient_t3,
};

Wyświetl plik

@ -1406,3 +1406,11 @@ config SOC_MEM_TCM_SUPPORTED
config SOC_EMAC_USE_IO_MUX
bool
default y
config SOC_JPEG_CODEC_SUPPORTED
bool
default y
config SOC_JPEG_DECODE_SUPPORTED
bool
default y

Wyświetl plik

@ -1400,7 +1400,7 @@ typedef union {
} jpeg_version_reg_t;
typedef struct {
typedef struct jpeg_dev_t {
volatile jpeg_config_reg_t config;
volatile jpeg_dqt_info_reg_t dqt_info;
volatile jpeg_pic_size_reg_t pic_size;

Wyświetl plik

@ -597,3 +597,8 @@
/*--------------------------- EMAC --------------------------------*/
#define SOC_EMAC_USE_IO_MUX (1) /*!< GPIO matrix is used to select GPIO pads */
/*--------------------------- JPEG --------------------------------*/
#define SOC_JPEG_CODEC_SUPPORTED (1)
#define SOC_JPEG_DECODE_SUPPORTED (1)
// #define SOC_JPEG_ENCODE_SUPPORTED (1) // TODO: IDF-6512