mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
777 lines
33 KiB
C
777 lines
33 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"
|
|
#include "esp_private/esp_cache_private.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";
|
|
|
|
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 = JPEG_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 interrupt 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;
|
|
uint8_t hivi = 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);
|
|
|
|
jpeg_get_bytes(header_info, 1);
|
|
jpeg_get_bytes(header_info, 1);
|
|
hivi = jpeg_get_bytes(header_info, 1);
|
|
break;
|
|
}
|
|
// This function only used for get width and height. 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;
|
|
|
|
switch (hivi) {
|
|
case 0x11:
|
|
picture_info->sample_method = JPEG_DOWN_SAMPLING_YUV444;
|
|
break;
|
|
case 0x21:
|
|
picture_info->sample_method = JPEG_DOWN_SAMPLING_YUV422;
|
|
break;
|
|
case 0x22:
|
|
picture_info->sample_method = JPEG_DOWN_SAMPLING_YUV420;
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "Sampling factor cannot be recognized");
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
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 outbuf_size, 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_dma_mem_info_t dma_mem_info = {
|
|
.dma_alignment_bytes = 4,
|
|
};
|
|
//TODO: IDF-9637
|
|
ESP_RETURN_ON_FALSE(esp_dma_is_buffer_alignment_satisfied(decode_outbuf, outbuf_size, dma_mem_info), ESP_ERR_INVALID_ARG, TAG, "jpeg decode decode_outbuf or out_buffer size 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");
|
|
|
|
*out_size = decoder_engine->header_info->process_h * decoder_engine->header_info->process_v * decoder_engine->bit_per_pixel / 8;
|
|
ESP_GOTO_ON_FALSE((*out_size <= outbuf_size), ESP_ERR_INVALID_ARG, err, TAG, "Given buffer size % " PRId32 " is smaller than actual jpeg decode output size % " PRId32 "the height and width of output picture size will be adjusted to 16 bytes aligned automatically", outbuf_size, *out_size);
|
|
|
|
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);
|
|
|
|
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_val = xQueueReceive(decoder_engine->evt_queue, &jpeg_dma2d_event, decoder_engine->timeout_tick);
|
|
ESP_GOTO_ON_FALSE(ret_val == pdTRUE, ESP_ERR_TIMEOUT, err, TAG, "jpeg-dma2d handle jpeg decode timeout, please check image accuracy and `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);
|
|
ret = ESP_ERR_INVALID_STATE;
|
|
goto err;
|
|
}
|
|
|
|
if (jpeg_dma2d_event.dma_evt & JPEG_DMA2D_RX_EOF) {
|
|
ret = esp_cache_msync((void*)decoder_engine->decoded_buf, outbuf_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
assert(ret == ESP_OK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 ESP_OK;
|
|
|
|
err:
|
|
dma2d_force_end(decoder_engine->trans_desc, &need_yield);
|
|
xSemaphoreGive(decoder_engine->codec_base->codec_mutex);
|
|
if (decoder_engine->codec_base->pm_lock) {
|
|
esp_pm_lock_release(decoder_engine->codec_base->pm_lock);
|
|
}
|
|
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");
|
|
|
|
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->trans_desc) {
|
|
free(decoder_engine->trans_desc);
|
|
}
|
|
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);
|
|
}
|
|
ESP_RETURN_ON_ERROR(jpeg_release_codec_handle(decoder_engine->codec_base), TAG, "release codec failed");
|
|
free(decoder_engine);
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
void *jpeg_alloc_decoder_mem(size_t size, const jpeg_decode_memory_alloc_cfg_t *mem_cfg, size_t *allocated_size)
|
|
{
|
|
/*
|
|
Principle of buffer align.
|
|
For output buffer(for decoder is 2DDMA write to PSRAM), both address and size should be aligned according to cache invalidate.
|
|
FOr input buffer(for decoder is PSRAM write to 2DDMA), no restriction for any align (both cache writeback and requirement from 2DDMA).
|
|
*/
|
|
size_t cache_align = 0;
|
|
esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &cache_align);
|
|
if (mem_cfg->buffer_direction == JPEG_DEC_ALLOC_OUTPUT_BUFFER) {
|
|
size = JPEG_ALIGN_UP(size, cache_align);
|
|
*allocated_size = size;
|
|
return heap_caps_aligned_calloc(cache_align, 1, size, MALLOC_CAP_SPIRAM);
|
|
} else {
|
|
*allocated_size = size;
|
|
return heap_caps_calloc(1, size, MALLOC_CAP_SPIRAM);
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
* 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->bit_per_pixel = color_hal_pixel_format_get_bit_depth(picture_format);
|
|
if (decoder_engine->no_color_conversion == false) {
|
|
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;
|
|
case JPEG_DECODE_OUT_FORMAT_YUV444:
|
|
best_hb_idx = JPEG_DEC_YUV444_HB;
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "wrong, we don't support decode to such format.");
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
best_hb_idx = JPEG_DEC_DIRECT_OUTPUT_HB;
|
|
}
|
|
|
|
uint8_t sample_method_idx = 0;
|
|
switch (decoder_engine->sample_method) {
|
|
case JPEG_DOWN_SAMPLING_YUV444:
|
|
sample_method_idx = 0;
|
|
break;
|
|
case JPEG_DOWN_SAMPLING_YUV422:
|
|
sample_method_idx = 1;
|
|
break;
|
|
case JPEG_DOWN_SAMPLING_YUV420:
|
|
sample_method_idx = 2;
|
|
break;
|
|
case JPEG_DOWN_SAMPLING_GRAY:
|
|
sample_method_idx = 3;
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "wrong, we don't support such sampling mode.");
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
uint32_t dma_hb = dec_hb_tbl[sample_method_idx][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_YUV444) {
|
|
if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV422) {
|
|
rx_csc_option = DMA2D_CSC_RX_YUV422_TO_YUV444;
|
|
} else if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV420) {
|
|
rx_csc_option = DMA2D_CSC_RX_YUV420_TO_YUV444;
|
|
}
|
|
} else {
|
|
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_color_space_support_check(jpeg_decoder_handle_t decoder_engine)
|
|
{
|
|
if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV444) {
|
|
if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV422 || decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV420) {
|
|
ESP_LOGE(TAG, "Detected YUV444 but want to convert to YUV422/YUV420, which is not supported");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
} else if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV422) {
|
|
if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV420) {
|
|
ESP_LOGE(TAG, "Detected YUV422 but want to convert to YUV420, which is not supported");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
} else if (decoder_engine->sample_method == JPEG_DOWN_SAMPLING_YUV420) {
|
|
if (decoder_engine->output_format == JPEG_DECODE_OUT_FORMAT_YUV422) {
|
|
ESP_LOGE(TAG, "Detected YUV420 but want to convert to YUV422, which is not supported");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ESP_RETURN_ON_ERROR(jpeg_color_space_support_check(decoder_engine), TAG, "jpeg decoder not support the combination of output format and down sampling format");
|
|
|
|
if ((uint32_t)decoder_engine->sample_method == (uint32_t)decoder_engine->output_format) {
|
|
decoder_engine->no_color_conversion = true;
|
|
}
|
|
|
|
// 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_UNDETECT) {
|
|
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");
|
|
}
|
|
}
|