mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(jpeg): Add basic jpeg decoder support on esp32p4
This commit is contained in:
parent
6817e9f07c
commit
abc2971d95
21
components/esp_driver_jpeg/CMakeLists.txt
Normal file
21
components/esp_driver_jpeg/CMakeLists.txt
Normal file
@ -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"
|
||||
)
|
12
components/esp_driver_jpeg/Kconfig
Normal file
12
components/esp_driver_jpeg/Kconfig
Normal file
@ -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
|
123
components/esp_driver_jpeg/include/driver/jpeg_decode.h
Normal file
123
components/esp_driver_jpeg/include/driver/jpeg_decode.h
Normal file
@ -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
|
48
components/esp_driver_jpeg/include/driver/jpeg_types.h
Normal file
48
components/esp_driver_jpeg/include/driver/jpeg_types.h
Normal file
@ -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
|
106
components/esp_driver_jpeg/jpeg_common.c
Normal file
106
components/esp_driver_jpeg/jpeg_common.c
Normal file
@ -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;
|
||||
}
|
676
components/esp_driver_jpeg/jpeg_decode.c
Normal file
676
components/esp_driver_jpeg/jpeg_decode.c
Normal file
@ -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");
|
||||
}
|
||||
}
|
47
components/esp_driver_jpeg/jpeg_param.c
Normal file
47
components/esp_driver_jpeg/jpeg_param.c
Normal file
@ -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},
|
||||
};
|
187
components/esp_driver_jpeg/jpeg_parse_marker.c
Normal file
187
components/esp_driver_jpeg/jpeg_parse_marker.c
Normal file
@ -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;
|
||||
}
|
143
components/esp_driver_jpeg/jpeg_private.h
Normal file
143
components/esp_driver_jpeg/jpeg_private.h
Normal file
@ -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
|
43
components/esp_driver_jpeg/private/jpeg_param.h
Normal file
43
components/esp_driver_jpeg/private/jpeg_param.h
Normal file
@ -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
|
121
components/esp_driver_jpeg/private/jpeg_parse_marker.h
Normal file
121
components/esp_driver_jpeg/private/jpeg_parse_marker.h
Normal file
@ -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
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
78
components/hal/include/hal/jpeg_defs.h
Normal file
78
components/hal/include/hal/jpeg_defs.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user