mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/jpeg_decoder' into 'master'
feat(jpgd): Add jpeg decode support on esp32p4 🖼️ (goal: Trying to merge in March 🛰️ ) See merge request espressif/esp-idf!28639
This commit is contained in:
commit
3e0a672d91
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" "esp_pm"
|
||||
)
|
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
|
122
components/esp_driver_jpeg/include/driver/jpeg_decode.h
Normal file
122
components/esp_driver_jpeg/include/driver/jpeg_decode.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 */
|
||||
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). */
|
||||
int timeout_ms; /*!< JPEG timeout threshold for handling a picture, should larger than valid decode time in ms. For example, for 30fps decode, this value must larger than 34. -1 means wait forever */
|
||||
} jpeg_decode_engine_cfg_t;
|
||||
|
||||
/**
|
||||
* @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_decoder` pointer.
|
||||
*
|
||||
* @param[in] dec_eng_cfg Pointer to the JPEG decode engine configuration.
|
||||
* @param[out] ret_decoder Pointer to a variable that will receive the JPEG decoder handle.
|
||||
* @return
|
||||
* - ESP_OK: JPEG decoder initialized successfully.
|
||||
* - ESP_ERR_INVALID_ARG: JPEG decoder initialization failed because of invalid argument.
|
||||
* - ESP_ERR_NO_MEM: Create JPEG decoder failed because of out of memory.
|
||||
*/
|
||||
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_decoder);
|
||||
|
||||
/**
|
||||
* @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. Note that parse beginning partial of picture also works, but the beginning partial should be enough given.
|
||||
* @param[out] picture_info Pointer to the structure that will receive the image information.
|
||||
* @return
|
||||
* - ESP_OK: JPEG decoder get jpg image header successfully.
|
||||
* - 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
|
198
components/esp_driver_jpeg/jpeg_common.c
Normal file
198
components/esp_driver_jpeg/jpeg_common.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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 "sys/queue.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->intr_priority = -1;
|
||||
codec->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
codec->codec_mutex = xSemaphoreCreateBinaryWithCaps(JPEG_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(codec->codec_mutex, ESP_ERR_NO_MEM, TAG, "No memory for codec mutex");
|
||||
SLIST_INIT(&codec->jpeg_isr_handler_list);
|
||||
xSemaphoreGive(codec->codec_mutex);
|
||||
// init the clock
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
jpeg_ll_enable_bus_clock(true);
|
||||
jpeg_ll_reset_module_register();
|
||||
}
|
||||
#if CONFIG_PM_ENABLE
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "jpeg_codec", &codec->pm_lock), TAG, "create pm lock failed");
|
||||
#endif
|
||||
jpeg_hal_init(&codec->hal);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No more memory for acquiring JPEG codec module");
|
||||
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_mutex) {
|
||||
vSemaphoreDeleteWithCaps(jpeg_codec->codec_mutex);
|
||||
jpeg_codec->codec_mutex = NULL;
|
||||
}
|
||||
if (jpeg_codec->pm_lock) {
|
||||
esp_pm_lock_delete(jpeg_codec->pm_lock);
|
||||
}
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
jpeg_ll_enable_bus_clock(false);
|
||||
}
|
||||
free(jpeg_codec);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_deinitialize) {
|
||||
ESP_LOGD(TAG, "delete codec");
|
||||
}
|
||||
_lock_release(&s_jpeg_platform.mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
JPEG INTERRUPT HANDLER
|
||||
---------------------------------------------------------------*/
|
||||
|
||||
static void jpeg_isr(void* arg)
|
||||
{
|
||||
jpeg_codec_handle_t jpeg_codec = (jpeg_codec_handle_t) arg;
|
||||
uint32_t status = jpeg_ll_get_intr_status(jpeg_codec->hal.dev);
|
||||
jpeg_isr_handler_t* it;
|
||||
SLIST_FOREACH(it, &jpeg_codec->jpeg_isr_handler_list, next) {
|
||||
if (it->mask & status) {
|
||||
(*it->handler)(it->handler_arg);
|
||||
}
|
||||
}
|
||||
jpeg_ll_clear_intr_mask(jpeg_codec->hal.dev, status);
|
||||
}
|
||||
|
||||
esp_err_t jpeg_isr_register(jpeg_codec_handle_t jpeg_codec, intr_handler_t handler, void* handler_arg, uint32_t jpeg_intr_mask, uint32_t flags, jpeg_isr_handler_t** jpeg_intr_handler)
|
||||
{
|
||||
if (jpeg_codec->intr_handle == NULL) {
|
||||
// The jpeg codec interrupt has not been allocated.
|
||||
esp_err_t err = esp_intr_alloc_intrstatus(ETS_JPEG_INTR_SOURCE, flags, (uint32_t)jpeg_ll_get_interrupt_status_reg(jpeg_codec->hal.dev), JPEG_LL_DECODER_EVENT_INTR | JPEG_LL_ENCODER_EVENT_INTR, &jpeg_isr, jpeg_codec, &jpeg_codec->intr_handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_isr_handler_t* item = heap_caps_calloc(1, sizeof(jpeg_isr_handler_t), JPEG_MEM_ALLOC_CAPS);
|
||||
if (item == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
item->handler = handler;
|
||||
item->handler_arg = handler_arg;
|
||||
item->mask = jpeg_intr_mask;
|
||||
item->flags = flags;
|
||||
SLIST_INSERT_HEAD(&jpeg_codec->jpeg_isr_handler_list, item, next);
|
||||
*jpeg_intr_handler = item;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t *jpeg_intr_handler)
|
||||
{
|
||||
jpeg_isr_handler_t* it;
|
||||
jpeg_isr_handler_t* prev = NULL;
|
||||
bool found = false;
|
||||
SLIST_FOREACH(it, &jpeg_codec->jpeg_isr_handler_list, next) {
|
||||
if (it == jpeg_intr_handler) {
|
||||
if (it == SLIST_FIRST(&jpeg_codec->jpeg_isr_handler_list)) {
|
||||
SLIST_REMOVE_HEAD(&jpeg_codec->jpeg_isr_handler_list, next);
|
||||
} else {
|
||||
SLIST_REMOVE_AFTER(prev, next);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
prev = it;
|
||||
free(prev);
|
||||
}
|
||||
if (unlikely(found != true)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (SLIST_EMPTY(&jpeg_codec->jpeg_isr_handler_list)) {
|
||||
// All interrupt is removed
|
||||
if (jpeg_codec->intr_handle) {
|
||||
esp_intr_free(jpeg_codec->intr_handle);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
bool intr_priority_conflict = false;
|
||||
if (jpeg_codec->intr_priority == -1) {
|
||||
jpeg_codec->intr_priority = intr_priority;
|
||||
} else if (intr_priority != 0) {
|
||||
intr_priority_conflict = (jpeg_codec->intr_priority != intr_priority);
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, TAG, "intr_priority conflict, already is %d but attempt to %d", jpeg_codec->intr_priority, intr_priority);
|
||||
return ret;
|
||||
}
|
675
components/esp_driver_jpeg/jpeg_decode.c
Normal file
675
components/esp_driver_jpeg/jpeg_decode.c
Normal file
@ -0,0 +1,675 @@
|
||||
/*
|
||||
* 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_queue, &dec_evt, &HPTaskAwoken);
|
||||
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t jpeg_new_decoder_engine(const jpeg_decode_engine_cfg_t *dec_eng_cfg, jpeg_decoder_handle_t *ret_decoder)
|
||||
{
|
||||
#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;
|
||||
|
||||
decoder_engine->timeout_tick = (dec_eng_cfg->timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(dec_eng_cfg->timeout_ms);
|
||||
/// init jpeg interrupt.
|
||||
jpeg_ll_clear_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR);
|
||||
|
||||
ESP_GOTO_ON_ERROR(jpeg_check_intr_priority(decoder_engine->codec_base, dec_eng_cfg->intr_priority), err, TAG, "set group intrrupt priority failed");
|
||||
if (dec_eng_cfg->intr_priority) {
|
||||
ESP_RETURN_ON_FALSE(1 << (dec_eng_cfg->intr_priority) & JPEG_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", dec_eng_cfg->intr_priority);
|
||||
}
|
||||
int isr_flags = JPEG_INTR_ALLOC_FLAG;
|
||||
if (dec_eng_cfg->intr_priority) {
|
||||
isr_flags |= 1 << (dec_eng_cfg->intr_priority);
|
||||
}
|
||||
|
||||
ret = jpeg_isr_register(decoder_engine->codec_base, jpeg_decoder_isr_handle_default, decoder_engine, JPEG_LL_DECODER_EVENT_INTR, isr_flags, &decoder_engine->intr_handle);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "install jpeg decode interrupt failed");
|
||||
|
||||
jpeg_ll_enable_intr_mask(hal->dev, JPEG_LL_DECODER_EVENT_INTR);
|
||||
|
||||
// Initialize queue
|
||||
decoder_engine->evt_queue = xQueueCreateWithCaps(2, sizeof(jpeg_dma2d_dec_evt_t), JPEG_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(decoder_engine->evt_queue, ESP_ERR_NO_MEM, err, TAG, "No memory for event queue");
|
||||
|
||||
dma2d_pool_config_t dma2d_client_config = {
|
||||
.pool_id = 0,
|
||||
};
|
||||
|
||||
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_decoder = 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_cfg, ESP_ERR_INVALID_ARG, TAG, "jpeg decode config is null");
|
||||
ESP_RETURN_ON_FALSE(decode_outbuf, ESP_ERR_INVALID_ARG, TAG, "jpeg decode picture buffer is null");
|
||||
ESP_RETURN_ON_FALSE(((uintptr_t)bit_stream % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode bit stream is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer");
|
||||
ESP_RETURN_ON_FALSE(((uintptr_t)decode_outbuf % cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA)) == 0, ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf is not aligned, please use jpeg_alloc_decoder_mem to malloc your buffer");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (decoder_engine->codec_base->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(decoder_engine->codec_base->pm_lock), TAG, "acquire pm_lock failed");
|
||||
}
|
||||
|
||||
xSemaphoreTake(decoder_engine->codec_base->codec_mutex, portMAX_DELAY);
|
||||
/* Reset queue */
|
||||
xQueueReset(decoder_engine->evt_queue);
|
||||
|
||||
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_queue, &jpeg_dma2d_event, decoder_engine->timeout_tick);
|
||||
ESP_GOTO_ON_FALSE(ret == pdTRUE, ESP_ERR_TIMEOUT, err, TAG, "jpeg-dma2d handle jpeg decode timeout, please check `timeout_ms` ");
|
||||
|
||||
// Dealing with JPEG event
|
||||
if (jpeg_dma2d_event.jpgd_status != 0) {
|
||||
uint32_t status = jpeg_dma2d_event.jpgd_status;
|
||||
s_decoder_error_log_print(status);
|
||||
dma2d_force_end(decoder_engine->trans_desc, &need_yield);
|
||||
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->pixel;
|
||||
|
||||
err:
|
||||
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
|
||||
if (decoder_engine->codec_base->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(decoder_engine->codec_base->pm_lock), TAG, "release pm_lock failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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->evt_queue) {
|
||||
vQueueDeleteWithCaps(decoder_engine->evt_queue);
|
||||
}
|
||||
if (decoder_engine->dma2d_group_handle) {
|
||||
dma2d_release_pool(decoder_engine->dma2d_group_handle);
|
||||
}
|
||||
if (decoder_engine->intr_handle) {
|
||||
jpeg_isr_deregister(decoder_engine->codec_base, decoder_engine->intr_handle);
|
||||
}
|
||||
free(decoder_engine);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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_queue, &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;
|
||||
}
|
197
components/esp_driver_jpeg/jpeg_private.h
Normal file
197
components/esp_driver_jpeg/jpeg_private.h
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
#include "sys/queue.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 "esp_pm.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)
|
||||
|
||||
typedef struct jpeg_decoder_t jpeg_decoder_t;
|
||||
typedef struct jpeg_codec_t jpeg_codec_t;
|
||||
typedef struct jpeg_codec_t *jpeg_codec_handle_t;
|
||||
|
||||
typedef struct jpeg_isr_handler_ {
|
||||
uint32_t mask;
|
||||
intr_handler_t handler;
|
||||
void* handler_arg;
|
||||
uint32_t flags;
|
||||
SLIST_ENTRY(jpeg_isr_handler_) next;
|
||||
} jpeg_isr_handler_t;
|
||||
|
||||
struct jpeg_codec_t {
|
||||
SemaphoreHandle_t codec_mutex; // pretend that one picture is in process, no other picture can interrupt current stage.
|
||||
jpeg_hal_context_t hal; // Hal layer for each port(bus)
|
||||
portMUX_TYPE spinlock; // To protect pre-group register level concurrency access
|
||||
intr_handle_t intr_handle; // jpeg codec interrupt handler
|
||||
int intr_priority; // jpeg codec interrupt priority
|
||||
SLIST_HEAD(jpeg_isr_handler_list_, jpeg_isr_handler_) jpeg_isr_handler_list; // List for jpeg interrupt.
|
||||
esp_pm_lock_handle_t pm_lock; // power manange lock
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
// 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_queue; // jpeg event from 2DDMA and JPEG engine
|
||||
uint8_t *decoded_buf; // pointer to the rx buffer.
|
||||
uint32_t total_size; // jpeg picture origin size (in bytes)
|
||||
TickType_t timeout_tick; // timeout value for jpeg decoder (in cpu tick).
|
||||
jpeg_isr_handler_t *intr_handle; // jpeg decoder interrupt handler
|
||||
//dma handles
|
||||
dma2d_pool_handle_t dma2d_group_handle; // 2D-DMA group handle
|
||||
dma2d_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);
|
||||
|
||||
/**
|
||||
* @brief Register an ISR handler for JPEG interrupt
|
||||
*
|
||||
* This function registers an Interrupt Service Routine (ISR) handler for JPEG interrupt.
|
||||
*
|
||||
* @param jpeg_codec The JPEG codec handle
|
||||
* @param handler The ISR handler function to be registered
|
||||
* @param handler_arg An argument to be passed to the ISR handler function
|
||||
* @param jpeg_intr_mask The JPEG interrupt mask value
|
||||
* @param flags Additional flags for ISR registration
|
||||
* @param jpeg_intr_handler JPEG interrupt handler
|
||||
* @return esp_err_t Returns ESP_OK on success, or an error code on failure
|
||||
*/
|
||||
esp_err_t jpeg_isr_register(jpeg_codec_handle_t jpeg_codec, intr_handler_t handler, void* handler_arg, uint32_t jpeg_intr_mask, uint32_t flags, jpeg_isr_handler_t** jpeg_intr_handler);
|
||||
|
||||
/**
|
||||
* @brief Deregister an ISR handler for JPEG interrupt
|
||||
*
|
||||
* This function deregisters an Interrupt Service Routine (ISR) handler for JPEG interrupt.
|
||||
*
|
||||
* @param jpeg_codec The JPEG codec handle
|
||||
* @param handler The ISR handler function to be deregistered
|
||||
* @param handler_arg The argument previously passed to the ISR handler function
|
||||
* @param jpeg_intr_handler JPEG interrupt handler
|
||||
* @return esp_err_t Returns ESP_OK on success, or an error code on failure
|
||||
*/
|
||||
esp_err_t jpeg_isr_deregister(jpeg_codec_handle_t jpeg_codec, jpeg_isr_handler_t *jpeg_intr_handler);
|
||||
|
||||
/**
|
||||
* @brief Check the interrupt priority for JPEG codec
|
||||
*
|
||||
* This function checks the interrupt priority for the JPEG codec to ensure it meets the specified requirements.
|
||||
*
|
||||
* @param jpeg_codec The JPEG codec handle
|
||||
* @param intr_priority The interrupt priority value to be checked
|
||||
* @return esp_err_t Returns ESP_OK if the interrupt priority meets the requirements, or an error code on failure
|
||||
*/
|
||||
esp_err_t jpeg_check_intr_priority(jpeg_codec_handle_t jpeg_codec, int intr_priority);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
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
|
123
components/esp_driver_jpeg/private/jpeg_parse_marker.h
Normal file
123
components/esp_driver_jpeg/private/jpeg_parse_marker.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 "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.
|
||||
*
|
||||
* @note num_bytes should not larger than 4, because the return value is uint32.
|
||||
*
|
||||
* @return The retrieved bytes as a 32-bit unsigned integer.
|
||||
*/
|
||||
uint32_t jpeg_get_bytes(jpeg_dec_header_info_t *header_info, uint8_t num_bytes);
|
||||
|
||||
/**
|
||||
* @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
|
@ -0,0 +1,7 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
components/esp_driver_jpeg/test_apps/jpeg_test_apps:
|
||||
disable:
|
||||
- if: SOC_JPEG_CODEC_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_jpeg
|
@ -0,0 +1,13 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(jpeg_test)
|
||||
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp720.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp1080.jpg" BINARY)
|
@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
@ -0,0 +1,7 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_jpeg_decode.c"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_driver_jpeg unity esp_psram test_utils
|
||||
WHOLE_ARCHIVE)
|
@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
ccomp_timer: "^1.0.0"
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in JPEG driver, so we reserved this threshold when checking memory leak
|
||||
// A better way to check a potential memory leak is running a same case by twice, for the second time, the memory usage delta should be zero
|
||||
#define LEAKS (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(LEAKS);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// _ ____ _____ ____ _____ _____ ____ _____
|
||||
// | | _ \| ____/ ___| |_ _| ____/ ___|_ _|
|
||||
// _ | | |_) | _|| | _ | | | _| \___ \ | |
|
||||
// | |_| | __/| |__| |_| | | | | |___ ___) || |
|
||||
// \___/|_| |_____\____| |_| |_____|____/ |_|
|
||||
|
||||
printf(" _ ____ _____ ____ _____ _____ ____ _____ \n");
|
||||
printf(" | | _ \\| ____/ ___| |_ _| ____/ ___|_ _|\n");
|
||||
printf(" _ | | |_) | _|| | _ | | | _| \\___ \\ | | \n");
|
||||
printf("| |_| | __/| |__| |_| | | | | |___ ___) || | \n");
|
||||
printf(" \\___/|_| |_____\\____| |_| |_____|____/ |_| \n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "driver/jpeg_decode.h"
|
||||
#include "esp_log.h"
|
||||
#include "test_jpeg_performance.h"
|
||||
#include "esp_system.h"
|
||||
#include "ccomp_timer.h"
|
||||
|
||||
extern const uint8_t image_esp1080_jpg_start[] asm("_binary_esp1080_jpg_start");
|
||||
extern const uint8_t image_esp1080_jpg_end[] asm("_binary_esp1080_jpg_end");
|
||||
|
||||
TEST_CASE("JPEG decode driver memory leaking check", "[jpeg]")
|
||||
{
|
||||
jpeg_decoder_handle_t jpgd_handle;
|
||||
|
||||
jpeg_decode_engine_cfg_t decode_eng_cfg = {
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
int size = esp_get_free_heap_size();
|
||||
for (uint32_t i = 0; i <= 3; i++) {
|
||||
TEST_ESP_OK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
TEST_ESP_OK(jpeg_del_decoder_engine(jpgd_handle));
|
||||
}
|
||||
|
||||
TEST_ASSERT_INT_WITHIN(400, size, esp_get_free_heap_size());
|
||||
}
|
||||
|
||||
TEST_CASE("JPEG decode performance test for 1080*1920 YUV->RGB picture", "[jpeg]")
|
||||
{
|
||||
jpeg_decoder_handle_t jpgd_handle;
|
||||
|
||||
jpeg_decode_engine_cfg_t decode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
jpeg_decode_cfg_t decode_cfg = {
|
||||
.output_format = JPEG_DECODE_OUT_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1080 * 1920 * 3);
|
||||
uint32_t out_size_1080p = 0;
|
||||
|
||||
size_t bit_stream_length = (size_t)image_esp1080_jpg_end - (size_t)image_esp1080_jpg_start;
|
||||
|
||||
uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(bit_stream_length);
|
||||
// Copy bit stream to psram
|
||||
memcpy(tx_buf_1080p, image_esp1080_jpg_start, bit_stream_length);
|
||||
TEST_ESP_OK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
|
||||
|
||||
ccomp_timer_start();
|
||||
|
||||
// Decode picture for 50 times, and get the average
|
||||
uint8_t cnt = 50;
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
TEST_ESP_OK(jpeg_decoder_process(jpgd_handle, &decode_cfg, tx_buf_1080p, bit_stream_length, rx_buf_1080p, &out_size_1080p));
|
||||
}
|
||||
int64_t decode_time = ccomp_timer_stop();
|
||||
|
||||
TEST_PERFORMANCE_GREATER_THAN(JPEG_DECODE_1080P_2_RGB565_PERFORMANCE, "1080p from *jpg -> rgb565 speed is %lld fps", 1 * 1000 * 1000 / (decode_time / cnt));
|
||||
|
||||
free(rx_buf_1080p);
|
||||
free(tx_buf_1080p);
|
||||
TEST_ESP_OK(jpeg_del_decoder_engine(jpgd_handle));
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define IDF_PERFORMANCE_MIN_JPEG_DECODE_1080P_2_RGB565_PERFORMANCE 40 // 40 fps for 1080p decoder.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,17 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_jpeg(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
@ -0,0 +1,5 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
@ -0,0 +1,9 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# SPIRAM configurations
|
||||
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
@ -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
|
||||
*/
|
||||
@ -74,6 +74,9 @@ typedef enum {
|
||||
JPEG_LL_EN_FRAME_EOF_LACK = (1 << 16),
|
||||
} jpeg_ll_encoder_intr_t;
|
||||
|
||||
#define JPEG_LL_ENCODER_EVENT_INTR (JPEG_LL_RLE_PARALLEL_ERR | \
|
||||
JPEG_LL_EN_FRAME_EOF_ERR)
|
||||
|
||||
/**
|
||||
* @brief Enable the hardware clock for JPEG module
|
||||
*
|
||||
@ -112,7 +115,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 +146,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 +177,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 +208,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_hal_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_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
|
||||
jpeg_ll_dht_ac0_write_codeword(hal->dev, huffbits, huffmin);
|
||||
jpeg_ll_dht_ac0_write_value(hal->dev, huffcode);
|
||||
}
|
||||
|
||||
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_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
|
||||
jpeg_ll_dht_ac1_write_codeword(hal->dev, huffbits, huffmin);
|
||||
jpeg_ll_dht_ac1_write_value(hal->dev, huffcode);
|
||||
}
|
||||
|
||||
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_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
|
||||
jpeg_ll_dht_dc0_write_codeword(hal->dev, huffbits, huffmin);
|
||||
jpeg_ll_dht_dc0_write_value(hal->dev, huffcode);
|
||||
}
|
||||
|
||||
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_hal_create_minicode_tbl(huffbits, huffmin, tmp_huff);
|
||||
jpeg_ll_dht_dc1_write_codeword(hal->dev, huffbits, huffmin);
|
||||
jpeg_ll_dht_dc1_write_value(hal->dev, huffcode);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -1410,3 +1410,11 @@ config SOC_MEM_NON_CONTIGUOUS_SRAM
|
||||
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 @@
|
||||
#define SOC_MEM_NON_CONTIGUOUS_SRAM (1)
|
||||
/*--------------------------- 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
|
||||
|
@ -150,6 +150,8 @@ ANA_CMPR_DOCS = ['api-reference/peripherals/ana_cmpr.rst']
|
||||
|
||||
SPI_SLAVE_HD_DOCS = ['api-reference/peripherals/spi_slave_hd.rst']
|
||||
|
||||
JPEG_DOCS = ['api-reference/peripherals/jpeg.rst']
|
||||
|
||||
QEMU_DOCS = ['api-guides/tools/qemu.rst']
|
||||
|
||||
ESP32_DOCS = ['api-reference/system/himem.rst',
|
||||
@ -235,6 +237,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
|
||||
'SOC_WIFI_MESH_SUPPORT':WIFI_MESH_DOCS,
|
||||
'SOC_SPI_SUPPORT_SLAVE_HD_VER2':SPI_SLAVE_HD_DOCS,
|
||||
'SOC_WIFI_NAN_SUPPORT':NAN_DOCS,
|
||||
'SOC_JPEG_CODEC_SUPPORTED':JPEG_DOCS,
|
||||
'esp32':ESP32_DOCS,
|
||||
'esp32s2':ESP32S2_DOCS,
|
||||
'esp32s3':ESP32S3_DOCS,
|
||||
|
@ -12,3 +12,6 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
|
||||
$(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \
|
||||
$(PROJECT_PATH)/components/usb/include/usb/usb_types_stack.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/jpeg_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \
|
||||
|
@ -36,6 +36,7 @@ Peripherals API
|
||||
spi_master
|
||||
spi_slave
|
||||
:SOC_SPI_SUPPORT_SLAVE_HD_VER2: spi_slave_hd
|
||||
:SOC_JPEG_CODEC_SUPPORTED: jpeg
|
||||
:SOC_TEMP_SENSOR_SUPPORTED: temp_sensor
|
||||
:SOC_TOUCH_SENSOR_SUPPORTED: touch_pad
|
||||
:esp32s2: touch_element
|
||||
|
101
docs/en/api-reference/peripherals/jpeg.rst
Normal file
101
docs/en/api-reference/peripherals/jpeg.rst
Normal file
@ -0,0 +1,101 @@
|
||||
JPEG Decoder
|
||||
============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
JPEG is a commonly used method of lossy compression for digital images, particularly for those images produced by digital photography. The degree of compression can be adjusted, allowing a selectable tradeoff between storage size and image quality. JPEG typically achieves 10:1 compression with little perceptible loss in image quality.
|
||||
|
||||
JPEG codec on {IDF_TARGET_NAME} is an image codec, which is based on the JPEG baseline standard, for compressing and decompressing images to reduce the bandwidth required to transmit images or the space required to store images, making it possible to process large-resolution images. But please note, at one time, the codec engine can only work as either encoder or decoder.
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
|
||||
The JPEG driver offers following services:
|
||||
|
||||
- `Resource Allocation <#resource-allocation>`__ - covers how to allocate JPEG resources with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- `JPEG Decoder Engine <#jpeg_decoder_engine>`__ - covers behavior of JPEG decoder engine. Introduce how to use decoder engine functions to decode an image (from jpg format to raw format).
|
||||
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install JPEG decoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
JPEG decoder engine requires the configuration that specified by :cpp:type:`jpeg_decode_engine_cfg_t`:
|
||||
|
||||
- :cpp:member:`jpeg_decode_engine_cfg_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`jpeg_decode_engine_cfg_t::intr_priority` Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)).
|
||||
|
||||
If the configurations in :cpp:type:`jpeg_decode_engine_cfg_t` is specified, users can call :cpp:func:`jpeg_new_decoder_engine` to allocate and initialize a JPEG decoder engine. This function will return an JPEG decoder handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_decoder_handle_t jpgd_handle;
|
||||
|
||||
jpeg_decode_engine_cfg_t decode_eng_cfg = {
|
||||
.intr_priority = 0,
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
|
||||
|
||||
|
||||
Uninstall JPEG decoder engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed JPEG engine is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`jpeg_del_decoder_engine`, so that to release the underlying hardware.
|
||||
|
||||
JPEG Decoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
After installing the JPEG decoder driver by :cpp:func:`jpeg_new_decoder_engine`, {IDF_TARGET_NAME} is ready to decode JPEG pictures by :cpp:func:`jpeg_decoder_process`. :cpp:func:`jpeg_decoder_process` is flexible for decoding different types of pictures by a configurable parameter called :cpp:type:`jpeg_decode_cfg_t`:
|
||||
|
||||
- :cpp:member:`jpeg_decode_cfg_t::output_format` Set the output raw image format.
|
||||
- :cpp:member:`jpeg_decode_cfg_t::rgb_order` Set the output pixel order. (RGB or BGR).
|
||||
- :cpp:member:`jpeg_decode_cfg_t::jpeg_yuv_rgb_conv_std_t` Set the output YUV and RGB conversion standard (BT601 or BT709)
|
||||
|
||||
Moreover, our jpeg decoder api provides a helper function which helps you get the basic information of your given image. Calling :cpp:func:`jpeg_decoder_get_info` would return the picture information structure called :cpp:func:`jpeg_decoder_get_info`. If you already know the picture basic information, this functions is unnecessary to be called.
|
||||
|
||||
Overall, You can take following code as reference, the code is going to decode a 1080*1920 picture.
|
||||
|
||||
.. code:: c
|
||||
|
||||
jpeg_decode_cfg_t decode_cfg_rgb = {
|
||||
.output_format = JPEG_DECODE_OUT_FORMAT_RGB888,
|
||||
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
|
||||
};
|
||||
|
||||
uint8_t *bit_stream = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, bit_stream_size, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *out_buf = (uint8_t*)heap_caps_aligned_calloc(JPEG_BUFFER_MALLOC_ALIGN_VALUE, 1, 1920 * 1080 * 3, MALLOC_CAP_SPIRAM); // Sufficient space for output images.
|
||||
|
||||
jpeg_decode_picture_info_t header_info;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_get_info(bit_stream, bit_stream_size, &header_info));
|
||||
uint32_t out_size = 0;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb, bit_stream, bit_stream_size, out_buf, &out_size));
|
||||
|
||||
.. note::
|
||||
|
||||
Firstly, in above code, you should make sure the `bit_stream` and `out_buf` should be :c:macro:`JPEG_BUFFER_MALLOC_ALIGN_VALUE` byte aligned.
|
||||
Secondly, the content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns.
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`jpeg_new_decoder_engine`, :cpp:func:`jpeg_decoder_get_info`, :cpp:func:`jpeg_decoder_process`, and :cpp:func:`jpeg_del_decoder_engine` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_JPEG_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. only:: SOC_JPEG_DECODE_SUPPORTED
|
||||
|
||||
.. include-build-file:: inc/jpeg_decode.inc
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_jpeg/include/driver/jpeg_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/jpeg_types.inc
|
@ -36,6 +36,7 @@
|
||||
spi_master
|
||||
spi_slave
|
||||
:SOC_SPI_SUPPORT_SLAVE_HD_VER2: spi_slave_hd
|
||||
:SOC_JPEG_CODEC_SUPPORTED: jpeg
|
||||
:SOC_TEMP_SENSOR_SUPPORTED: temp_sensor
|
||||
:SOC_TOUCH_SENSOR_SUPPORTED: touch_pad
|
||||
:esp32s2: touch_element
|
||||
|
1
docs/zh_CN/api-reference/peripherals/jpeg.rst
Normal file
1
docs/zh_CN/api-reference/peripherals/jpeg.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/peripherals/jpeg.rst
|
@ -116,6 +116,12 @@ examples/peripherals/i2s/i2s_recorder:
|
||||
- esp_driver_spi
|
||||
- esp_driver_i2s
|
||||
|
||||
examples/peripherals/jpeg/jpeg_decode:
|
||||
disable:
|
||||
- if: SOC_JPEG_CODEC_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_jpeg
|
||||
|
||||
examples/peripherals/lcd/i2c_oled:
|
||||
disable:
|
||||
- if: SOC_I2C_SUPPORTED != 1
|
||||
|
6
examples/peripherals/jpeg/jpeg_decode/CMakeLists.txt
Normal file
6
examples/peripherals/jpeg/jpeg_decode/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(jpeg_decode)
|
58
examples/peripherals/jpeg/jpeg_decode/README.md
Normal file
58
examples/peripherals/jpeg/jpeg_decode/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# JPEG decode example
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates how to use the JPEG hardware decoder to decode a 1080p and a 720p picture:
|
||||
|
||||
If you have a bunch of big JPEG picture need to be decoded, such as `*.jpg` -> `*.rgb`, and this example uses hardware JPEG decoder to accelerate the decoding.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Prerequisites Required
|
||||
|
||||
This example demonstrates the flexibility of decoding pictures by decoding two different sizes: one in 1080p and another in 720p. It showcases how you can easily modify the code to meet your specific requirements, such as only decoding 1080p photos.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Before you start build and flash this example, please put the image `esp720.jpg` and `esp1080.jpg` in your sdcard.
|
||||
|
||||
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```bash
|
||||
I (1116) jpeg.example: Initializing SD card
|
||||
I (1116) gpio: GPIO[43]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1126) gpio: GPIO[44]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1136) gpio: GPIO[39]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1146) gpio: GPIO[40]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1156) gpio: GPIO[41]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1166) gpio: GPIO[42]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1416) gpio: GPIO[42]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
Name: SD64G
|
||||
Type: SDHC/SDXC
|
||||
Speed: 40.00 MHz (limit: 40.00 MHz)
|
||||
Size: 60906MB
|
||||
CSD: ver=2, sector_size=512, capacity=124735488 read_bl_len=9
|
||||
SSR: bus_width=4
|
||||
I (1436) jpeg.example: jpg_file_1080:/sdcard/esp1080.jpg
|
||||
I (1696) jpeg.example: jpg_file_1080:/sdcard/esp720.jpg
|
||||
I (1796) jpeg.example: header parsed, width is 1920, height is 1080
|
||||
I (1846) jpeg.example: raw_file_1080:/sdcard/out.rgb
|
||||
I (11836) jpeg.example: raw_file_720:/sdcard/out2.rgb
|
||||
I (13336) jpeg.example: Card unmounted
|
||||
I (13336) main_task: Returned from app_main()
|
||||
```
|
||||
|
||||
Moreover, we provided a helper script called `open_rgb.py`, which can help you easily see the outputs on your computer. For requirements component you need, you can call `pip install -r requirements.txt` under `examples/peripheral/jpeg/jpeg_decode` folder.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "jpeg_decode_main.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,9 @@
|
||||
menu "JPEG Decode Example menu"
|
||||
|
||||
config EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
bool "Format the card if mount failed"
|
||||
default n
|
||||
help
|
||||
If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if
|
||||
the mount has failed.
|
||||
endmenu
|
169
examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c
Normal file
169
examples/peripherals/jpeg/jpeg_decode/main/jpeg_decode_main.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "esp_attr.h"
|
||||
#include "driver/jpeg_decode.h"
|
||||
|
||||
static const char *TAG = "jpeg.example";
|
||||
static sdmmc_card_t *s_card;
|
||||
#define MOUNT_POINT "/sdcard"
|
||||
|
||||
const static char jpg_file_1080[] = "/sdcard/esp1080.jpg";
|
||||
const static char raw_file_1080[] = "/sdcard/out.rgb";
|
||||
const static char jpg_file_720[] = "/sdcard/esp720.jpg";
|
||||
const static char raw_file_720[] = "/sdcard/out2.rgb";
|
||||
|
||||
static esp_err_t sdcard_init(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.format_if_mount_failed = true,
|
||||
#else
|
||||
.format_if_mount_failed = false,
|
||||
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
slot_config.width = 4;
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &s_card);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
||||
"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
||||
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// Card has been initialized, print its properties
|
||||
sdmmc_card_print_info(stdout, s_card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdcard_deinit(void)
|
||||
{
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
esp_vfs_fat_sdcard_unmount(mount_point, s_card);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(sdcard_init());
|
||||
|
||||
jpeg_decoder_handle_t jpgd_handle;
|
||||
|
||||
jpeg_decode_engine_cfg_t decode_eng_cfg = {
|
||||
.timeout_ms = 40,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle));
|
||||
|
||||
jpeg_decode_cfg_t decode_cfg_rgb = {
|
||||
.output_format = JPEG_DECODE_OUT_FORMAT_RGB888,
|
||||
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
|
||||
};
|
||||
|
||||
jpeg_decode_cfg_t decode_cfg_gray = {
|
||||
.output_format = JPEG_DECODE_OUT_FORMAT_GRAY,
|
||||
};
|
||||
|
||||
FILE *file_jpg_1080p = fopen(jpg_file_1080, "rb");
|
||||
ESP_LOGI(TAG, "jpg_file_1080:%s", jpg_file_1080);
|
||||
if (file_jpg_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_jpg_1080p error");
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file_jpg_1080p, 0, SEEK_END);
|
||||
int jpeg_size_1080p = ftell(file_jpg_1080p);
|
||||
fseek(file_jpg_1080p, 0, SEEK_SET);
|
||||
uint8_t *tx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_1080p);
|
||||
if (tx_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 1080p tx buffer error");
|
||||
return;
|
||||
}
|
||||
fread(tx_buf_1080p, 1, jpeg_size_1080p, file_jpg_1080p);
|
||||
fclose(file_jpg_1080p);
|
||||
|
||||
FILE *file_jpg_720p = fopen(jpg_file_720, "rb");
|
||||
ESP_LOGI(TAG, "jpg_file_1080:%s", jpg_file_720);
|
||||
if (file_jpg_720p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_jpg_720p error");
|
||||
return;
|
||||
}
|
||||
fseek(file_jpg_720p, 0, SEEK_END);
|
||||
int jpeg_size_720p = ftell(file_jpg_720p);
|
||||
fseek(file_jpg_720p, 0, SEEK_SET);
|
||||
uint8_t *tx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size_720p);
|
||||
if (tx_buf_720p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 720p tx buffer error");
|
||||
return;
|
||||
}
|
||||
fread(tx_buf_720p, 1, jpeg_size_720p, file_jpg_720p);
|
||||
fclose(file_jpg_720p);
|
||||
|
||||
uint8_t *rx_buf_1080p = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1080 * 3);
|
||||
uint8_t *rx_buf_720p = (uint8_t*)jpeg_alloc_decoder_mem(720 * 1280);
|
||||
if (rx_buf_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 1080p rx buffer error");
|
||||
return;
|
||||
}
|
||||
if (rx_buf_720p == NULL) {
|
||||
ESP_LOGE(TAG, "alloc 720p rx buffer error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the jpg header information (This step is optional)
|
||||
jpeg_decode_picture_info_t header_info;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_get_info(tx_buf_1080p, jpeg_size_1080p, &header_info));
|
||||
ESP_LOGI(TAG, "header parsed, width is %" PRId32 ", height is %" PRId32, header_info.width, header_info.height);
|
||||
|
||||
uint32_t out_size_1080p = 0;
|
||||
uint32_t out_size_720p = 0;
|
||||
ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb, tx_buf_1080p, jpeg_size_1080p, rx_buf_1080p, &out_size_1080p));
|
||||
ESP_ERROR_CHECK(jpeg_decoder_process(jpgd_handle, &decode_cfg_gray, tx_buf_720p, jpeg_size_720p, rx_buf_720p, &out_size_720p));
|
||||
|
||||
// Write two pictures.
|
||||
FILE *file_rgb_1080p = fopen(raw_file_1080, "wb");
|
||||
ESP_LOGI(TAG, "raw_file_1080:%s", raw_file_1080);
|
||||
if (file_rgb_1080p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_rgb_1080p error");
|
||||
return;
|
||||
}
|
||||
fwrite(rx_buf_1080p, 1, out_size_1080p, file_rgb_1080p);
|
||||
fclose(file_rgb_1080p);
|
||||
|
||||
FILE *file_rgb_720p = fopen(raw_file_720, "wb");
|
||||
ESP_LOGI(TAG, "raw_file_720:%s", raw_file_720);
|
||||
if (file_rgb_720p == NULL) {
|
||||
ESP_LOGE(TAG, "fopen file_rgb_720p error");
|
||||
return;
|
||||
}
|
||||
fwrite(rx_buf_720p, 1, out_size_720p, file_rgb_720p);
|
||||
fclose(file_rgb_720p);
|
||||
|
||||
sdcard_deinit();
|
||||
ESP_LOGI(TAG, "Card unmounted");
|
||||
|
||||
}
|
91
examples/peripherals/jpeg/jpeg_decode/open_rgb.py
Normal file
91
examples/peripherals/jpeg/jpeg_decode/open_rgb.py
Normal file
@ -0,0 +1,91 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import argparse
|
||||
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
|
||||
def open_picture(path): # type: (str) -> list[int]
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
f.close()
|
||||
new_data = [int(x) for x in data]
|
||||
return new_data
|
||||
|
||||
|
||||
def picture_show_rgb888(data, h, w): # type: (list[int], int, int) -> None
|
||||
data = np.array(data).reshape(h, w, 3).astype(np.uint8)
|
||||
cv.imshow('data', data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_rgb565(data, h, w): # type: (list[int], int, int) -> None
|
||||
|
||||
new_data = [0] * ((len(data) // 2) * 3)
|
||||
for i in range(len(data)):
|
||||
if i % 2 != 0:
|
||||
new_data[3 * (i - 1) // 2 + 2] = (data[i] & 0xf8)
|
||||
new_data[3 * (i - 1) // 2 + 1] |= (data[i] & 0x7) << 5
|
||||
else:
|
||||
new_data[3 * i // 2] = (data[i] & 0x1f) << 3
|
||||
new_data[3 * i // 2 + 1] |= (data[i] & 0xe0) >> 3
|
||||
|
||||
new_data = np.array(new_data).reshape(h, w, 3).astype(np.uint8)
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_gray(data, h, w): # type: (list[int], int, int) -> None
|
||||
new_data = np.array(data).reshape(h, w, 1).astype(np.uint8)
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def main(): # type: () -> None
|
||||
parser = argparse.ArgumentParser(description='which mode need to show')
|
||||
|
||||
parser.add_argument(
|
||||
'--pic_path',
|
||||
type=str,
|
||||
help='What is the path of your picture',
|
||||
required=True)
|
||||
|
||||
parser.add_argument(
|
||||
'--pic_type',
|
||||
type=str,
|
||||
help='What type you want to show',
|
||||
required=True,
|
||||
choices=['rgb565', 'rgb888', 'gray'])
|
||||
|
||||
parser.add_argument(
|
||||
'--hight',
|
||||
type=int,
|
||||
help='the picture hight',
|
||||
default=480)
|
||||
|
||||
parser.add_argument(
|
||||
'--width',
|
||||
type=int,
|
||||
help='the picture width',
|
||||
default=640)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
hight = args.hight
|
||||
width = args.width
|
||||
|
||||
data = open_picture(args.pic_path)
|
||||
|
||||
if (args.pic_type == 'rgb565'):
|
||||
picture_show_rgb565(data, hight, width)
|
||||
elif (args.pic_type == 'rgb888'):
|
||||
picture_show_rgb888(data, hight, width)
|
||||
elif (args.pic_type == 'gray'):
|
||||
picture_show_gray(data, hight, width)
|
||||
else:
|
||||
print('This type is not supported in this script!')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2
examples/peripherals/jpeg/jpeg_decode/requirements.txt
Normal file
2
examples/peripherals/jpeg/jpeg_decode/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
opencv-python
|
||||
numpy
|
BIN
examples/peripherals/jpeg/jpeg_decode/resources/esp1080.jpg
Normal file
BIN
examples/peripherals/jpeg/jpeg_decode/resources/esp1080.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
examples/peripherals/jpeg/jpeg_decode/resources/esp720.jpg
Normal file
BIN
examples/peripherals/jpeg/jpeg_decode/resources/esp720.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
6
examples/peripherals/jpeg/jpeg_decode/sdkconfig.defaults
Normal file
6
examples/peripherals/jpeg/jpeg_decode/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
# SPIRAM configurations
|
||||
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
Loading…
Reference in New Issue
Block a user