mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-30 12:50:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			778 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			778 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), err2, TAG, "jpeg parse marker failed");
 | |
|     ESP_GOTO_ON_ERROR(jpeg_parse_header_info_to_hw(decoder_engine), err2, TAG, "write header info to hw failed");
 | |
|     ESP_GOTO_ON_ERROR(jpeg_dec_config_dma_descriptor(decoder_engine), err2, 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, err2, 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), err2, 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, err1, 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 err1;
 | |
|         }
 | |
| 
 | |
|         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;
 | |
| 
 | |
| err1:
 | |
|     dma2d_force_end(decoder_engine->trans_desc, &need_yield);
 | |
| err2:
 | |
|     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");
 | |
|     }
 | |
| }
 | 
