esp-idf/components/esp_driver_jpeg/jpeg_decode.c

676 lines
29 KiB
C

/*
* 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");
}
}