Feature/add usb camera example

This commit is contained in:
Li Jun Ru
2023-09-14 11:37:09 +08:00
committed by Xu Xin
parent 03b08e4c98
commit 3e8f21f689
16 changed files with 1399 additions and 147 deletions
+54 -147
View File
@@ -1,8 +1,8 @@
.build_template: &build_template
stage: build
image: espressif/idf:release-v5.0
tags:
- build
image: ${IMAGE}
variables:
# Enable ccache for all build jobs. See configure_ci_environment.sh for more ccache related settings.
IDF_CCACHE_ENABLE: "1"
@@ -31,20 +31,13 @@
expire_in: 1 week
variables:
IDF_CI_BUILD: "1"
# By configuring this macro, you can append the compiled configuration file.
# For example, using "sdkconf.etc=default" specifies the default sdkconfig file.
EXAMPLE_CONFIG: "="
script:
- pip install --upgrade idf-component-manager
- pip install idf_build_apps
- python -m idf_build_apps build -vv
-p $EXAMPLE_DIR
-t all
--config "sdkconfig.ci.*="
--build-dir build_@t_@w
--build-log build_log.txt
--size-file size.json
--check-warnings
--ignore-warning-str "$WARNING_STR"
--collect-size-info size_info.txt
--default-build-targets $IDF_TARGET
- python tools/build_apps.py ${EXAMPLE_DIR} --config ${EXAMPLE_CONFIG} -t esp32s3 -vv
.build_matter_examples: &build_matter_examples
<<: *build_template
@@ -87,183 +80,97 @@ build_example_matter_switch_v5.1:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/matter_switch
build_example_chatgpt_demo_v5.0:
build_example_chatgpt_demo:
extends:
- .build_examples_template
- .rules:build:example_chatgpt_demo
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/chatgpt_demo
build_example_chatgpt_demo_latest:
extends:
- .build_examples_template
- .rules:build:example_chatgpt_demo
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/chatgpt_demo
build_example_factory_demo_v5.0:
build_example_factory_demo:
extends:
- .build_examples_template
- .rules:build:example_factory_demo
#image: espressif/idf:release-v5.0 don't support release-v5.0 now.
image: espressif/idf:release-v5.1
parallel:
matrix:
#- IMAGE: espressif/idf:release-v5.0 don't support release-v5.0 now.
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/factory_demo
build_example_factory_demo_v5.1:
extends:
- .build_examples_template
- .rules:build:example_factory_demo
image: espressif/idf:release-v5.1
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/factory_demo
build_example_factory_demo_latest:
extends:
- .build_examples_template
- .rules:build:example_factory_demo
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/factory_demo
build_example_image_display_v5.0:
build_example_image_display:
extends:
- .build_examples_template
- .rules:build:example_image_display
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/image_display
build_example_image_display_v5.1:
extends:
- .build_examples_template
- .rules:build:example_image_display
image: espressif/idf:release-v5.1
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/image_display
build_example_image_display_latest:
extends:
- .build_examples_template
- .rules:build:example_image_display
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/image_display
build_example_lv_demos_v5.0:
build_example_lv_demos:
extends:
- .build_examples_template
- .rules:build:example_lv_demos
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/lv_demos
build_example_lv_demos_v5.1:
extends:
- .build_examples_template
- .rules:build:example_lv_demos
image: espressif/idf:release-v5.1
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/lv_demos
build_example_lv_demos_latest:
extends:
- .build_examples_template
- .rules:build:example_lv_demos
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/lv_demos
build_example_mp3_demo_v5.0:
build_example_mp3_demo:
extends:
- .build_examples_template
- .rules:build:example_mp3_demo
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/mp3_demo
build_example_mp3_demo_v5.1:
build_example_usb_camera_lcd_display:
extends:
- .build_examples_template
- .rules:build:example_mp3_demo
image: espressif/idf:release-v5.1
- .rules:build:example_usb_camera_lcd_display
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/mp3_demo
EXAMPLE_DIR: examples/usb_camera_lcd_display
build_example_mp3_demo_latest:
extends:
- .build_examples_template
- .rules:build:example_mp3_demo
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/mp3_demo
build_example_usb_headset_v5.0:
build_example_usb_headset:
extends:
- .build_examples_template
- .rules:build:example_usb_headset
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/usb_headset
build_example_usb_headset_v5.1:
extends:
- .build_examples_template
- .rules:build:example_usb_headset
image: espressif/idf:release-v5.1
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/usb_headset
build_example_usb_headset_latest:
extends:
- .build_examples_template
- .rules:build:example_usb_headset
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/usb_headset
build_example_watering_demo_v5.0:
build_example_watering_demo:
extends:
- .build_examples_template
- .rules:build:example_watering_demo
image: espressif/idf:release-v5.0
parallel:
matrix:
- IMAGE: espressif/idf:release-v5.0
- IMAGE: espressif/idf:release-v5.1
- IMAGE: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/watering_demo
build_example_watering_demo_v5.1:
extends:
- .build_examples_template
- .rules:build:example_watering_demo
image: espressif/idf:release-v5.1
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/watering_demo
build_example_watering_demo_latest:
extends:
- .build_examples_template
- .rules:build:example_watering_demo
image: espressif/idf:latest
variables:
IDF_TARGET: esp32s3
EXAMPLE_DIR: examples/watering_demo
+12
View File
@@ -25,6 +25,9 @@
.patterns-example_mp3_demo: &patterns-example_mp3_demo
- "examples/mp3_demo/**/*"
.patterns-example_usb_camera_lcd_display: &patterns-example_usb_camera_lcd_display
- "examples/usb_camera_lcd_display/**/*"
.patterns-example_usb_headset: &patterns-example_usb_headset
- "examples/usb_headset/**/*"
@@ -101,6 +104,15 @@
- <<: *if-dev-push
changes: *patterns-example_mp3_demo
.rules:build:example_usb_camera_lcd_display:
rules:
- <<: *if-protected
- <<: *if-label-build
- <<: *if-dev-push
changes: *patterns-components_bsp
- <<: *if-dev-push
changes: *patterns-example_usb_camera_lcd_display
.rules:build:example_usb_headset:
rules:
- <<: *if-protected
@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(usb_camera_lcd_display)
+54
View File
@@ -0,0 +1,54 @@
## USB Stream LCD Display Example
| Board | Support Status |
| ----------------- | -------------- |
| ESP32-S3-BOX | NO |
| ESP32-S3-BOX-Lite | NO |
| ESP32-S3-BOX-3 | YES |
This routine demonstrates how to use the [usb_stream](https://components.espressif.com/components/espressif/usb_stream) component to acquire a USB camera image and display it adaptively on the LCD screen.
* Pressing the boot button can switch the display resolution.
* For better performance, please use ESP-IDF release/v5.0 or above versions.
* Currently only images with a resolution smaller than the screen resolution are displayed
* When the image width is equal to the screen width, the refresh rate is at its highest.
## Hardware
* An ESP32-S3-BOX-3 development board with 320*240 LCD.
* A [USB camera](https://docs.espressif.com/projects/espressif-esp-iot-solution/zh_CN/latest/usb/usb_stream.html#id1) (Can be connected to USB port or Expansion Connector).
* An USB Type-C cable for Power supply and programming (Please connect to UART port instead of USB port)
Note:
Error message with `A fatal error occurred: Could not open /dev/ttyACM0, the port doesn't exist`: Please first make sure development board connected, then make board into "Download Boot" mode to upload by following steps:
1. keep pressing "BOOT(SW2)" button
2. short press "RST(SW1)" button
3. release "BOOT(SW2)".
4. upload program and reset
## PSRAM 120M DDR
The PSRAM 120M DDR feature is intended to achieve the best performance of RGB LCD. It is only available with ESP-IDF **release/v5.1** and above. It can be used by enabling the `IDF_EXPERIMENTAL_FEATURES`, `SPIRAM_SPEED_120M`, `SPIRAM_MODE_OCT` options. see [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/flash_psram_config.html#all-supported-modes-and-speeds) for more details.
**Note: The PSRAM 120 MHz DDR is an experimental feature and it has temperature risks as below.**
* Cannot guarantee normal functioning with a temperature higher than 65 degrees Celsius.
* Temperature changes can also cause the crash of accessing to PSRAM/Flash, see [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/flash_psram_config.html#all-supported-modes-and-speeds) for more details.
### How To Use
Note:
1. First delete existing `build`, `sdkconfig` , `sdkconfig.old`
```
rm -rf build sdkconfig sdkconfig.old
```
2. Use the command line to enable the relevant configuration
```
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.psram_octal_120m" reconfigure
```
3. Compile and burn
```
idf.py build flash monitor
```
@@ -0,0 +1,7 @@
idf_component_register(
SRCS "src/audio_malloc.c"
INCLUDE_DIRS "include"
)
target_link_libraries(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/libs/${CONFIG_IDF_TARGET}/libesp_codec.a")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u audio_free -u audio_calloc_inner -u audio_calloc")
@@ -0,0 +1,120 @@
// Copyright 2022 Espressif Systems (Shanghai) CO., LTD
// All rights reserved.
#pragma once
#include "stdint.h"
/* Error code */
typedef enum {
JPEG_ERR_OK = 0, /* Succeeded */
JPEG_ERR_FAIL = -1, /* Device error or wrong termination of input stream */
JPEG_ERR_MEM = -2, /* Insufficient memory pool for the image */
JPEG_ERR_NO_MORE_DATA = -3, /* Input data is not enough */
JPEG_ERR_PAR = -4, /* Parameter error */
JPEG_ERR_FMT1 = -5, /* Data format error (may be damaged data) */
JPEG_ERR_FMT2 = -6, /* Right format but not supported */
JPEG_ERR_FMT3 = -7 /* Not supported JPEG standard */
} jpeg_error_t;
/* Jpeg raw data type.
* raw data ---> encoder ---> encoded data ---> transmit ---> decoder ---> raw data
*/
typedef enum {
JPEG_RAW_TYPE_GRAY = 0, /*!< Grayscale. encoder supported. decoder un-supported.*/
JPEG_RAW_TYPE_RGB888 = 1, /*!< RGB888. encoder supported. decoder supported. */
JPEG_RAW_TYPE_RGBA = 2, /*!< RGBA. encoder supported. decoder un-supported.*/
JPEG_RAW_TYPE_YCbYCr = 3, /*!< Y422. encoder supported. decoder un-supported.*/
JPEG_RAW_TYPE_YCbY2YCrY2 = 4, /*!< Y420. encoder supported. decoder un-supported.*/
JPEG_RAW_TYPE_RGB565_BE = 5, /*!< RGB565. The data is big end. encoder un-supported. decoder supported. */
JPEG_RAW_TYPE_RGB565_LE = 6, /*!< RGB565. The data is little end. encoder un-supported. decoder supported.*/
} jpeg_raw_type_t;
/* JPEG chroma subsampling factors */
typedef enum {
JPEG_SUB_SAMPLE_Y = 0, /*!< Grayscale */
JPEG_SUB_SAMPLE_YUV444 = 1, /*!< data order:YUV 1x1x1 3 pixel per MCU Y444*/
JPEG_SUB_SAMPLE_YUV422 = 2, /*!< data order:YUYV 2x1x1 2 pixel per MCU Y422*/
JPEG_SUB_SAMPLE_YUV420 = 3 /*!< data order:YUY2YVY2 4x1x1 1.25 pixel per MCU Y420)*/
} jpeg_subsampling_t;
typedef enum {
JPEG_ROTATE_0D = 0, /*!< Source image rotates clockwise 0 degree before encoding */
JPEG_ROTATE_90D = 1, /*!< Source image rotates clockwise 90 degree before encoding */
JPEG_ROTATE_180D = 2, /*!< Source image rotates clockwise 180 degree before encoding */
JPEG_ROTATE_270D = 3, /*!< Source image rotates clockwise 270 degree before encoding */
} jpeg_rotate_t;
/**
* @brief Convert YUV to RGB
*
* @param[in] sub_sample chroma subsampling factors. Grayscale is un-supported. The others are supported.
* @param[in] raw_type raw data type. RGB888 and RGB565 are supported. The others are un-supported.
* @param[in] yuv_image input YUV image data. The buffer must be aligned 16 byte.
* @param[in] width image width
* @param[in] height image height
* @param[out] rgb_image output RGB image data
*
* @return jpeg_error_t
* - JPEG_ERR_OK: on success
* - Others: error occurs
*/
jpeg_error_t jpeg_yuv2rgb(jpeg_subsampling_t sub_sample, jpeg_raw_type_t raw_type, uint8_t *yuv_image, int width, int height, uint8_t *rgb_image);
/**
* @brief Allocated buffer. And the buffer address will be aligned.
*
* @param[in] size Allocate buffer size.
* @param[in] aligned Aligned byte
*
* @return positive: Allocate buffer address
* NULL: failed
*/
void *jpeg_malloc_align(int size, int aligned);
/**
* @brief Free buffer. The buffer address come from `jpeg_malloc_align`
*
* @param[in] data The buffer address.
*
*/
void jpeg_free_align(void *data);
/**
* Example usage:
* @code{c}
*
* void* yu2_rgb_demo()
* {
* // allocate memory
* int width = 320;
* int height = 240;
* int yuv_pixel = 3; //JPEG_SUB_SAMPLE_YUV444:3 JPEG_SUB_SAMPLE_YUV422:2 JPEG_SUB_SAMPLE_YUV420:1.5
* int yuv_image_size = width * height * yuv_pixel;
* uint8_t* yuv_image = (uint8_t*)calloc(1, yuv_image_size);
* if(yuv_image == NULL) {
* return;
* }
* int rgb_pixel = 3; // JPEG_RAW_TYPE_RGB888:3 JPEG_RAW_TYPE_RGB565_LE/JPEG_RAW_TYPE_RGB565_BE:2
* int rgb_image_size = width * height * rgb_pixel;
* uint8_t* rgb_image = (uint8_t*)jpeg_malloc_align(rgb_image_size, 16);
* if(rgb_image == NULL) {
* return;
* }
* // read yuv data
* memset(yuv_image, 128, yuv_image_size)
* for (int i= 0; i< yuv_image_size; i+=3) {
* yuv_image[i] = i / 3;
* }
* // convert
* jpeg_error_t ret = jpeg_yuv2rgb(JPEG_SUB_SAMPLE_YUV444, JPEG_RAW_TYPE_RGB888, yuv_image, width, height, rgb_image);
* // print rgb data
* for (int i= 0; i< rgb_image_size; i++) {
* printf("%x ", rgb_image[i]&0xff);
* }
* jpeg_free_align(rgb_image);
* free(yuv_image);
* }
*
* @endcode
*/
@@ -0,0 +1,195 @@
// Copyright 2021 Espressif Systems (Shanghai) CO., LTD
// All rights reserved.
#ifndef ESP_JPEG_DEC_H
#define ESP_JPEG_DEC_H
/**
* @file esp_jpeg_dec.h
* @brief Create an JPEG decoder.
* Currently, support functions as follows:
* - Support variety of width and height to decoder
* - Support RGB888 RGB565(big end) RGB565(little end) raw data to output
* - Support 0, 90 180 270 degree clockwise rotation, under width and height are multiply of 8.
* The encoder do ASM optimization in ESP32S3. The encoder frame rate performs better than the others chips.
* @version 1.0.0
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_jpeg_common.h"
#define DEFAULT_JPEG_DEC_CONFIG() { \
.output_type = JPEG_RAW_TYPE_RGB565_LE, \
.rotate = JPEG_ROTATE_0D, \
}
#define JPEG_DEC_MAX_MARKER_CHECK_LEN (1024)
typedef void* jpeg_dec_handle_t;
/* Jpeg dec user need to config */
typedef struct {
jpeg_raw_type_t output_type; /*!< jpeg_dec_out_type 1:rgb888 0:rgb565 */
jpeg_rotate_t rotate; /*!< Supports 0, 90 180 270 degree clockwise rotation.
Under width % 8 == 0. height % 8 = 0 conditions, rotation enabled. Otherwise unsupported */
} jpeg_dec_config_t;
/* Jpeg dec out info */
typedef struct {
int width; /* Number of pixels in the horizontal direction */
int height; /* Number of pixels in the vertical direction */
int component_num; /* Number of color component*/
uint8_t component_id[3]; /* ID of color component*/
uint8_t x_factory[3]; /* Size factory in the x direction*/
uint8_t y_factory[3]; /* Size factory in the y direction*/
uint8_t huffbits[2][2][16]; /* Huffman bit distribution tables [id][dcac] */
uint16_t huffdata[2][2][256]; /* Huffman decoded data tables [id][dcac] */
uint8_t qtid[3]; /* Quantization table ID of each component */
int16_t qt_tbl[4][64]; /* De-quantizer tables [id] */
} jpeg_dec_header_info_t;
/* Jpeg dec io control */
typedef struct {
unsigned char *inbuf; /* The input buffer pointer */
int inbuf_len; /* The number of the input buffer */
int inbuf_remain; /* Not used number of the in buffer */
unsigned char *outbuf; /* The decoded data is placed.The buffer must be aligned 16 byte. */
} jpeg_dec_io_t;
/**
* @brief Create a Jpeg decode handle, set user config info to decode handle
*
* @param[in] config The configuration information
*
* @return other values: The JPEG decoder handle
* NULL: failed
*/
jpeg_dec_handle_t *jpeg_dec_open(jpeg_dec_config_t *config);
/**
* @brief Parse picture data header, and out put info to user
*
* @param[in] jpeg_dec jpeg decoder handle
*
* @param[in] io struct of jpeg_dec_io_t
*
* @param[out] out_info output info struct to user
*
* @return jpeg_error_t
* - JPEG_ERR_OK: on success
* - Others: error occurs
*/
jpeg_error_t jpeg_dec_parse_header(jpeg_dec_handle_t *jpeg_dec, jpeg_dec_io_t *io, jpeg_dec_header_info_t *out_info);
/**
* @brief Decode one Jpeg picture
*
* @param[in] jpeg_dec jpeg decoder handle
*
* @param[in] io struct of jpeg_dec_io_t
*
* @return jpeg_error_t
* - JPEG_ERR_OK: on success
* - Others: error occurs
*/
jpeg_error_t jpeg_dec_process(jpeg_dec_handle_t *jpeg_dec, jpeg_dec_io_t *io);
/**
* @brief Deinitialize Jpeg decode handle
*
* @param[in] jpeg_dec jpeg decoder handle
*
* @return jpeg_error_t
* - JPEG_ERR_OK: on success
* - Others: error occurs
*/
jpeg_error_t jpeg_dec_close(jpeg_dec_handle_t *jpeg_dec);
/**
* Example usage:
* @code{c}
*
* // Function for decode one jpeg picture
* // input_buf input picture data
* // len input picture data length
* int esp_jpeg_decoder_one_picture(unsigned char *input_buf, int len, unsigned char **output_buf)
* {
* // Generate default configuration
* jpeg_dec_config_t config = DEFAULT_JPEG_DEC_CONFIG();
*
* // Empty handle to jpeg_decoder
* jpeg_dec_handle_t jpeg_dec = NULL;
*
* // Create jpeg_dec
* jpeg_dec = jpeg_dec_open(&config);
*
* // Create io_callback handle
* jpeg_dec_io_t *jpeg_io = calloc(1, sizeof(jpeg_dec_io_t));
* if (jpeg_io == NULL) {
* return ESP_FAIL;
* }
*
* // Create out_info handle
* jpeg_dec_header_info_t *out_info = calloc(1, sizeof(jpeg_dec_header_info_t));
* if (out_info == NULL) {
* return ESP_FAIL;
* }
*
* // Set input buffer and buffer len to io_callback
* jpeg_io->inbuf = input_buf;
* jpeg_io->inbuf_len = len;
*
* int ret = 0;
* // Parse jpeg picture header and get picture for user and decoder
* ret = jpeg_dec_parse_header(jpeg_dec, jpeg_io, out_info);
* if (ret < 0) {
* return ret;
* }
*
* // Calloc out_put data buffer and update inbuf ptr and inbuf_len
* int outbuf_len;
* if (config.output_type == JPEG_RAW_TYPE_RGB565_LE
* || config.output_type == JPEG_RAW_TYPE_RGB565_BE) {
* outbuf_len = out_info->width * out_info->height * 2;
* } else if (config.output_type == JPEG_RAW_TYPE_RGB888) {
* outbuf_len = out_info->width * out_info->height * 3;
* } else {
* return ESP_FAIL;
* }
* unsigned char *out_buf = jpeg_malloc_align(outbuf_len, 16);
* if (out_buf == NULL) {
* return ESP_FAIL;
* }
* jpeg_io->outbuf = out_buf;
* *output_buf = out_buf;
* int inbuf_consumed = jpeg_io->inbuf_len - jpeg_io->inbuf_remain;
* jpeg_io->inbuf = input_buf + inbuf_consumed;
* jpeg_io->inbuf_len = jpeg_io->inbuf_remain;
*
* // Start decode jpeg raw data
* ret = jpeg_dec_process(jpeg_dec, jpeg_io);
* if (ret < 0) {
* return ret;
* }
*
* // Decoder deinitialize
* jpeg_free_align(out_buf);
* jpeg_dec_close(jpeg_dec);
* free(out_info);
* free(jpeg_io);
* return ESP_OK;
* }
*
* @endcode
*
*/
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,244 @@
// Copyright 2022 Espressif Systems (Shanghai) CO., LTD
// All rights reserved.
#pragma once
/**
* @file esp_jpeg_enc.h
* @brief Create an JPEG encoder.
* Currently, support functions as follows:
* - Support variety of width and height to encoder
* - Support RGB888 RGBA YCbYCr YCbY2YCrY2 GRAY raw data to encode
* - Support sub-sample(YUV444 YUV422 Yuv420)
* - Support quality(1-100)
* - Support 0, 90 180 270 degree clockwise rotation, under src_type = JPEG_RAW_TYPE_YCbYCr,
* subsampling = JPEG_SUB_SAMPLE_YUV420, width and height are multiply of 16 and
* src_type = JPEG_RAW_TYPE_YCbYCr, subsampling = JPEG_SUB_SAMPLE_Y, width and height are multiply of 8.
* - Support dual-task
* - Support two mode encoder, respectively block encoder and one image encoder
* The encoder does ASM optimization in ESP32S3. The encoder frame rate performs better than the others chips.
* Under src_type = JPEG_RAW_TYPE_YCbYCr, subsampling = JPEG_SUB_SAMPLE_YUV420, width % 16 == 0. height % 16 = 0 conditions,
* memory consumption is about 9K.Others, with wider the image and larger `subsampling`, the greater memory consumption will be.
*
* @version 1.0.0
*
*/
#include "stdbool.h"
#include "stdint.h"
#include "esp_jpeg_common.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEFAULT_JPEG_ENC_CONFIG() \
{ \
.width = 320, \
.height = 240, \
.src_type = JPEG_RAW_TYPE_YCbYCr, \
.subsampling = JPEG_SUB_SAMPLE_YUV420, \
.quality = 40, \
.task_enable = false, \
.hfm_task_priority = 13, \
.hfm_task_core = 1, \
.rotate = JPEG_ROTATE_0D, \
}
/* JPEG configure information*/
typedef struct jpeg_info {
int width; /*!< Image width */
int height; /*!< Image height */
jpeg_raw_type_t src_type; /*!< Input image type */
jpeg_subsampling_t subsampling; /*!< JPEG chroma subsampling factors. If `src_type` is JPEG_RAW_TYPE_YCbY2YCrY2, this must be `JPEG_SUB_SAMPLE_Y` or `JPEG_SUB_SAMPLE_YUV420`. Others is un-support */
uint8_t quality; /*!< Quality: 1-100, higher is better. Typical values are around 40 - 100. */
bool task_enable; /*!< True: `jpeg_enc_open` would create task to finish part of encoding work. false: no task help the encoder encode */
uint8_t hfm_task_priority; /*!< Task priority.If task_enable is true, this must be set */
uint8_t hfm_task_core; /*!< Task core.If task_enable is true, this must be set */
jpeg_rotate_t rotate; /*!< Supports 0, 90 180 270 degree clockwise rotation.
Under src_type = JPEG_RAW_TYPE_YCbYCr, subsampling = JPEG_SUB_SAMPLE_YUV420, width % 16 == 0. height % 16 = 0 conditions, rotation enabled.
Otherwise unsupported */
} jpeg_enc_info_t;
/**
* @brief Create an JPEG handle to encode
*
* @param info The configuration information
*
* @return >0: The JPEG encoder handle
* NULL: refer to `jpeg_error_t`
*/
void *jpeg_enc_open(jpeg_enc_info_t *info);
/**
* @brief Encode one image
*
* @param handle The JPEG encoder handle. It gained from `jpeg_enc_open`
* @param in_buf The input buffer, It needs a completed image. The buffer must be aligned 16 byte.
* @param inbuf_size The size of `in_buf`. The value must be size of a completed image.
* @param out_buf The output buffer, it saves a completed JPEG image. The size must be gather than 700 bytes.
* @param outbuf_size The size of output buffer
* @param out_size The size of JPEG image
*
* @return `JPEG_ERR_OK`: It has finished to encode one image.
* other values refer to `jpeg_error_t`
*/
jpeg_error_t jpeg_enc_process(const void *handle, const uint8_t *in_buf, int inbuf_size, uint8_t *out_buf, int outbuf_size, int *out_size);
/**
* @brief Get block size. Block size is minimum process unit.
*
* @param handle The JPEG encoder handle. It gained from `jpeg_enc_open`
*
* @return positive value block size
*/
int jpeg_enc_get_block_size(const void *handle);
/**
* @brief Encode block size image. Get block size from `jpeg_enc_get_block_size`
*
* @param handle The JPEG encoder handle. It gained from `jpeg_enc_open`
* @param in_buf The input buffer, It needs a completed image.
* @param inbuf_size The size of `in_buf`. Get block size from `jpeg_enc_get_block_size`
* @param out_buf The output buffer, it saves a completed JPEG image. The size must be gather than 700 bytes.
* @param outbuf_size The size of output buffer
* @param out_size The size of JPEG image
*
* @return block size: It has finished to encode current block size image.
* `JPEG_ERR_OK`: It has finished to encode one image.
* `JPEG_ERR_FAIL`: Encoder failed
*/
int jpeg_enc_process_with_block(const void *handle, const uint8_t *in_buf, int inbuf_size, uint8_t *out_buf, int outbuf_size, int *out_size);
/**
* @brief Deinit JPEG handle
*
* @param handle The JPEG encoder handle. It gained from `jpeg_enc_open`
*
* @return `JPEG_ERR_OK` It has finished to deinit.
*/
jpeg_error_t jpeg_enc_close(void *handle);
/**
* @brief Reset quality.
*
* @param handle The JPEG encoder handle. It gained from `jpeg_enc_open`
* @param q Quality: 1-100, higher is better. Typical values are around 40 - 100.
*
* @return JPEG_ERR_Ok succeed
* others failed
*/
jpeg_error_t jpeg_enc_set_quality(const void *handle, uint8_t q);
/**
* Example usage:
* demo1: It is to encode one image using `jpeg_enc_process`
* @code{c}
*
* int esp_jpeg_enc_demo1 () {
* /// configure encoder
* jpeg_enc_info_t info = DEFAULT_JPEG_ENC_CONFIG();
*
* /// allocate input buffer to fill original image stream.
* int in_len = info.width *info.height * 2;
* char *inbuf = jpeg_malloc_align(in_len);
* if(inbuf == NULL) {
* goto exit;
* }
* /// allocate output buffer to fill encoded image stream.
* int out_len = info.width * info.height * 2;
* char *outbuf = malloc(out_len);
* if (inbuf == NULL) {
* goto exit;
* }
* int out_size = 0;
* void* el = jpeg_enc_open(&info);
* if (el == NULL) {
* goto exit;
* }
* char in_name[100] = "test.yuv";
* char out_name[100] = "test.jpg";
* FILE *in = fopen(in_name, "rb");
* if (el == NULL){
* goto exit;
* }
* FILE *out = fopen(out_name, "wb");
* if (el == NULL) {
* fclose(in);
* goto exit;
* }
* int ret = fread(inbuf, 1, in_len, in);
* if(ret <= 0) {
* fclose(in);
* fclose(out);
* goto exit;
* }
* jpeg_enc_process(el, inbuf, in_len, outbuf, out_len, &out_size);
* ret = fwrite(outbuf, 1, out_size, out);
* if (ret <= 0){
* fclose(in);
* fclose(out);
* goto exit;
* }
* fclose(in);
* fclose(out);
* exit:
* jpeg_enc_close(el);
* free(inbuf);
* free(outbuf);
* }
* demo2: It is to encode one image using `jpeg_enc_process_with_block`
* int esp_jpeg_enc_demo2 () {
* jpeg_enc_info_t info = DEFAULT_JPEG_ENC_CONFIG();
* int in_len = jpeg_enc_get_block_size(el);
* char *inbuf = jpeg_malloc_align(in_len);
* if(inbuf == NULL) {
* goto exit;
* }
* int num_times = info.width * info.height * 3 / in_len;
* int out_len = info.width * info.height * 2;
* char *outbuf = malloc(out_len);
* if (inbuf == NULL){
* goto exit;
* }
* int out_size = 0;
* void* el = jpeg_enc_open(&info);
* if (el == NULL){
* goto exit;
* }
*
* char in_name[100] = "test.yuv";
* char out_name[100] = "test.jpg";
* FILE *in = fopen(in_name, "rb") if (el == NULL)
* {
* goto exit;
* }
* FILE *out = fopen(out_name, "wb") if (el == NULL)
* {
* fclose(in);
* goto exit;
* }
* for (size_t j = 0; j < num_times; j++) {
* int ret = fread(inbuf, 1, in_len, in);
* if(ret <= 0) {
* ret = fwrite(outbuf, 1, out_size, out);
* fclose(in);
* fclose(out);
* goto exit;
* }
* jpeg_enc_process_with_block(el, inbuf, in_len, outbuf, out_len, &out_size);
* }
* fclose(in);
* fclose(out);
* exit:
* jpeg_enc_close(el);
* free(inbuf);
* free(outbuf);
* }
* @endcode
*/
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,40 @@
#include <stdlib.h>
#include "sdkconfig.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
// #define audio_malloc malloc
// #define audio_free free
// #define audio_strdup strdup
// #define audio_calloc calloc
// #define audio_realloc realloc
void *audio_calloc_inner(size_t n, size_t size)
{
void *data = NULL;
#if CONFIG_SPIRAM_BOOT_INIT
data = heap_caps_calloc_prefer(n, size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM);
#else
data = heap_caps_calloc(n, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#endif
#ifdef ENABLE_AUDIO_MEM_TRACE
ESP_LOGI("AUIDO_MEM", "calloc_inner:%p, size:%d, called:0x%08x", data, size, (intptr_t)__builtin_return_address(0) - 2);
#endif
return data;
}
void *audio_calloc(size_t n, size_t size)
{
return calloc(n, size);
}
// void* realloc(void* ptr, size_t size)
// {
// return heap_caps_realloc_default(ptr, size);
// }
void audio_free(void *ptr)
{
free(ptr);
}
@@ -0,0 +1 @@
idf_component_register(SRCS main.c)
@@ -0,0 +1,5 @@
dependencies:
espressif/esp-box-3: "^1.0.0"
idf: ">=5.0"
usb_stream:
version: "~1.1.0"
+449
View File
@@ -0,0 +1,449 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "bsp/esp-bsp.h"
#include "bsp/display.h"
#include "esp_log.h"
#include "esp_jpeg_dec.h"
#include "esp_timer.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "usb_stream.h"
#include "iot_button.h"
static const char *TAG = "uvc_camera_lcd_demo";
/****************** configure the example working mode *******************************/
#define DEMO_UVC_XFER_BUFFER_SIZE (35 * 1024) // Double buffer
#define DEMO_KEY_RESOLUTION "resolution" // The key of the resolution stored in nvs
#define DEMO_SWITCH_BUTTON_IO 0 // The button to switch resolution
#define DEMO_MAX_H 680 // The max width of the camera
#define DEMO_MAX_V 480 // The max height of the camera
#define BIT0_FRAME_START (0x01 << 0)
static EventGroupHandle_t s_evt_handle;
typedef struct
{
uint16_t width;
uint16_t height;
} camera_frame_size_t;
typedef struct
{
camera_frame_size_t camera_frame_size;
uvc_frame_size_t *camera_frame_list;
size_t camera_frame_list_num;
size_t camera_currect_frame_index;
} camera_resolution_info_t;
static camera_resolution_info_t camera_resolution_info = {0};
static uint8_t *jpg_frame_buf = NULL;
static uint8_t *xfer_buffer_a = NULL;
static uint8_t *xfer_buffer_b = NULL;
static uint8_t *frame_buffer = NULL;
static uint16_t current_width = 0;
static uint16_t current_height = 0;
static lv_obj_t *camera_canvas = NULL;
static lv_obj_t *label = NULL;
static jpeg_error_t esp_jpeg_decoder_one_picture(uint8_t *input_buf, int len, uint8_t *output_buf)
{
esp_err_t ret = ESP_OK;
// Generate default configuration
jpeg_dec_config_t config = DEFAULT_JPEG_DEC_CONFIG();
config.output_type = JPEG_RAW_TYPE_RGB565_BE;
// Empty handle to jpeg_decoder
jpeg_dec_handle_t jpeg_dec = NULL;
// Create jpeg_dec
jpeg_dec = jpeg_dec_open(&config);
// Create io_callback handle
jpeg_dec_io_t *jpeg_io = calloc(1, sizeof(jpeg_dec_io_t));
if (jpeg_io == NULL)
{
return ESP_FAIL;
}
// Create out_info handle
jpeg_dec_header_info_t *out_info = calloc(1, sizeof(jpeg_dec_header_info_t));
if (out_info == NULL)
{
return ESP_FAIL;
}
// Set input buffer and buffer len to io_callback
jpeg_io->inbuf = input_buf;
jpeg_io->inbuf_len = len;
// Parse jpeg picture header and get picture for user and decoder
ret = jpeg_dec_parse_header(jpeg_dec, jpeg_io, out_info);
if (ret < 0)
{
goto _exit;
}
jpeg_io->outbuf = output_buf;
int inbuf_consumed = jpeg_io->inbuf_len - jpeg_io->inbuf_remain;
jpeg_io->inbuf = input_buf + inbuf_consumed;
jpeg_io->inbuf_len = jpeg_io->inbuf_remain;
// Start decode jpeg raw data
ret = jpeg_dec_process(jpeg_dec, jpeg_io);
if (ret < 0)
{
goto _exit;
}
_exit:
// Decoder deinitialize
jpeg_dec_close(jpeg_dec);
free(out_info);
free(jpeg_io);
return ret;
}
static void adaptive_jpg_frame_buffer(size_t length)
{
if (jpg_frame_buf != NULL)
{
free(jpg_frame_buf);
}
jpg_frame_buf = (uint8_t *)heap_caps_aligned_alloc(16, length, MALLOC_CAP_SPIRAM);
assert(jpg_frame_buf != NULL);
}
static void _camera_display(uint8_t *lcd_buffer)
{
bsp_display_lock(0);
lv_canvas_set_buffer(camera_canvas, lcd_buffer, current_width, current_height, LV_IMG_CF_TRUE_COLOR);
lv_label_set_text_fmt(label, "#FF0000 %d*%d#", current_width, current_height);
bsp_display_unlock();
}
static void camera_frame_cb(uvc_frame_t *frame, void *ptr)
{
ESP_LOGI(TAG, "uvc callback! frame_format = %d, seq = %" PRIu32 ", width = %" PRIu32 ", height = %" PRIu32 ", length = %u, ptr = %d",
frame->frame_format, frame->sequence, frame->width, frame->height, frame->data_bytes, (int)ptr);
if (current_width != frame->width || current_height != frame->height)
{
current_width = frame->width;
current_height = frame->height;
adaptive_jpg_frame_buffer(current_width * current_height * 2);
}
esp_jpeg_decoder_one_picture((uint8_t *)frame->data, frame->data_bytes, jpg_frame_buf);
_camera_display(jpg_frame_buf);
vTaskDelay(pdMS_TO_TICKS(1));
}
static esp_err_t _display_init(void)
{
bsp_display_start();
bsp_display_backlight_on(); // Set display brightness to 100%
bsp_display_lock(0);
camera_canvas = lv_canvas_create(lv_scr_act());
assert(camera_canvas);
lv_obj_center(camera_canvas);
label = lv_label_create(lv_scr_act());
lv_label_set_recolor(label, true);
lv_obj_set_pos(label, 0, 0);
lv_label_set_text(label, "Insert a camera, press boot for resolution.");
bsp_display_unlock();
return ESP_OK;
}
static void _get_value_from_nvs(char *key, void *value, size_t *size)
{
nvs_handle_t my_handle;
esp_err_t err = nvs_open("memory", NVS_READWRITE, &my_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err));
}
else
{
err = nvs_get_blob(my_handle, key, value, size);
switch (err)
{
case ESP_OK:
break;
case ESP_ERR_NVS_NOT_FOUND:
ESP_LOGI(TAG, "%s is not initialized yet!", key);
break;
default:
ESP_LOGE(TAG, "Error (%s) reading!\n", esp_err_to_name(err));
}
nvs_close(my_handle);
}
}
static esp_err_t _set_value_to_nvs(char *key, void *value, size_t size)
{
nvs_handle_t my_handle;
esp_err_t err = nvs_open("memory", NVS_READWRITE, &my_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err));
return ESP_FAIL;
}
else
{
err = nvs_set_blob(my_handle, key, value, size);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "NVS set failed %s", esp_err_to_name(err));
}
err = nvs_commit(my_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "NVS commit failed");
}
nvs_close(my_handle);
}
return err;
}
static esp_err_t _usb_stream_init(void)
{
uvc_config_t uvc_config = {
.frame_interval = FRAME_INTERVAL_FPS_30,
.xfer_buffer_size = DEMO_UVC_XFER_BUFFER_SIZE,
.xfer_buffer_a = xfer_buffer_a,
.xfer_buffer_b = xfer_buffer_b,
.frame_buffer_size = DEMO_UVC_XFER_BUFFER_SIZE,
.frame_buffer = frame_buffer,
.frame_cb = &camera_frame_cb,
.frame_cb_arg = NULL,
.frame_width = FRAME_RESOLUTION_ANY,
.frame_height = FRAME_RESOLUTION_ANY,
.flags = FLAG_UVC_SUSPEND_AFTER_START,
};
esp_err_t ret = uvc_streaming_config(&uvc_config);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "uvc streaming config failed");
}
return ret;
}
static size_t _find_current_resolution(camera_frame_size_t *camera_frame_size)
{
if (camera_resolution_info.camera_frame_list == NULL)
{
return -1;
}
size_t i = 0;
while (i < camera_resolution_info.camera_frame_list_num)
{
if (camera_frame_size->width >= camera_resolution_info.camera_frame_list[i].width && camera_frame_size->height >= camera_resolution_info.camera_frame_list[i].height)
{
/* Find next resolution
If current resolution is the min resolution, switch to the max resolution*/
camera_frame_size->width = camera_resolution_info.camera_frame_list[i].width;
camera_frame_size->height = camera_resolution_info.camera_frame_list[i].height;
break;
}
else if (i == camera_resolution_info.camera_frame_list_num - 1)
{
camera_frame_size->width = camera_resolution_info.camera_frame_list[i].width;
camera_frame_size->height = camera_resolution_info.camera_frame_list[i].height;
break;
}
i++;
}
ESP_LOGI(TAG, "Current resolution is %dx%d", camera_frame_size->width, camera_frame_size->height);
return i;
}
static void switch_button_press_down_cb(void *arg, void *data)
{
if (camera_resolution_info.camera_frame_list == NULL || xEventGroupWaitBits(s_evt_handle, BIT0_FRAME_START, false, false, pdMS_TO_TICKS(10)) != pdTRUE)
{
return;
}
ESP_LOGI(TAG, "old resolution is %d*%d", camera_resolution_info.camera_frame_size.width, camera_resolution_info.camera_frame_size.height);
if (++camera_resolution_info.camera_currect_frame_index >= camera_resolution_info.camera_frame_list_num)
{
camera_resolution_info.camera_currect_frame_index = 0;
}
camera_resolution_info.camera_frame_size.width = camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].width;
camera_resolution_info.camera_frame_size.height = camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].height;
ESP_LOGI(TAG, "currect resolution is %d*%d", camera_resolution_info.camera_frame_size.width, camera_resolution_info.camera_frame_size.height);
/* Save the new camera resolution to nsv */
usb_streaming_control(STREAM_UVC, CTRL_SUSPEND, NULL);
ESP_ERROR_CHECK(uvc_frame_size_reset(camera_resolution_info.camera_frame_size.width,
camera_resolution_info.camera_frame_size.height,
FPS2INTERVAL(30)));
ESP_ERROR_CHECK(_set_value_to_nvs(DEMO_KEY_RESOLUTION, &camera_resolution_info.camera_frame_size, sizeof(camera_frame_size_t)));
usb_streaming_control(STREAM_UVC, CTRL_RESUME, NULL);
}
static esp_err_t _switch_button_init(void)
{
button_config_t button_config = {
.type = BUTTON_TYPE_GPIO,
.gpio_button_config = {
.gpio_num = DEMO_SWITCH_BUTTON_IO,
.active_level = 0,
},
};
button_handle_t button_handle = iot_button_create(&button_config);
assert(button_handle != NULL);
esp_err_t ret = iot_button_register_cb(button_handle, BUTTON_PRESS_DOWN, switch_button_press_down_cb, NULL);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "button register callback fail");
}
return ret;
}
static void _stream_state_changed_cb(usb_stream_state_t event, void *arg)
{
switch (event)
{
case STREAM_CONNECTED:
{
/* Get camera resolution in nvs*/
size_t size = sizeof(camera_frame_size_t);
_get_value_from_nvs(DEMO_KEY_RESOLUTION, &camera_resolution_info.camera_frame_size, &size);
size_t frame_index = 0;
uvc_frame_size_list_get(NULL, &camera_resolution_info.camera_frame_list_num, NULL);
if (camera_resolution_info.camera_frame_list_num)
{
ESP_LOGI(TAG, "UVC: get frame list size = %u, current = %u", camera_resolution_info.camera_frame_list_num, frame_index);
uvc_frame_size_t *_frame_list = (uvc_frame_size_t *)malloc(camera_resolution_info.camera_frame_list_num * sizeof(uvc_frame_size_t));
camera_resolution_info.camera_frame_list = (uvc_frame_size_t *)realloc(camera_resolution_info.camera_frame_list, camera_resolution_info.camera_frame_list_num * sizeof(uvc_frame_size_t));
if (NULL == camera_resolution_info.camera_frame_list)
{
ESP_LOGE(TAG, "camera_resolution_info.camera_frame_list");
}
uvc_frame_size_list_get(_frame_list, NULL, NULL);
for (size_t i = 0; i < camera_resolution_info.camera_frame_list_num; i++)
{
if (_frame_list[i].width <= DEMO_MAX_H && _frame_list[i].height <= DEMO_MAX_V)
{
camera_resolution_info.camera_frame_list[frame_index++] = _frame_list[i];
ESP_LOGI(TAG, "\tpick frame[%u] = %ux%u", i, _frame_list[i].width, _frame_list[i].height);
}
else
{
ESP_LOGI(TAG, "\tdrop frame[%u] = %ux%u", i, _frame_list[i].width, _frame_list[i].height);
}
}
camera_resolution_info.camera_frame_list_num = frame_index;
if (camera_resolution_info.camera_frame_size.width != 0 && camera_resolution_info.camera_frame_size.height != 0)
{
camera_resolution_info.camera_currect_frame_index = _find_current_resolution(&camera_resolution_info.camera_frame_size);
}
else
{
camera_resolution_info.camera_currect_frame_index = 0;
}
if (-1 == camera_resolution_info.camera_currect_frame_index)
{
ESP_LOGE(TAG, "fine current resolution fail");
break;
}
ESP_ERROR_CHECK(uvc_frame_size_reset(camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].width,
camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].height, FPS2INTERVAL(30)));
camera_frame_size_t camera_frame_size = {
.width = camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].width,
.height = camera_resolution_info.camera_frame_list[camera_resolution_info.camera_currect_frame_index].height,
};
ESP_ERROR_CHECK(_set_value_to_nvs(DEMO_KEY_RESOLUTION, &camera_frame_size, sizeof(camera_frame_size_t)));
if (_frame_list != NULL)
{
free(_frame_list);
}
/* Wait USB Camera connected */
usb_streaming_control(STREAM_UVC, CTRL_RESUME, NULL);
xEventGroupSetBits(s_evt_handle, BIT0_FRAME_START);
}
else
{
ESP_LOGW(TAG, "UVC: get frame list size = %u", camera_resolution_info.camera_frame_list_num);
}
ESP_LOGI(TAG, "Device connected");
break;
}
case STREAM_DISCONNECTED:
xEventGroupClearBits(s_evt_handle, BIT0_FRAME_START);
ESP_LOGI(TAG, "Device disconnected");
break;
default:
ESP_LOGE(TAG, "Unknown event");
break;
}
}
void app_main(void)
{
esp_log_level_set("*", ESP_LOG_INFO);
/* Initialize NVS */
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
/* NVS partition was truncated and needs to be erased */
ESP_ERROR_CHECK(nvs_flash_erase());
/* Retry nvs_flash_init */
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
/* Create event group */
s_evt_handle = xEventGroupCreate();
if (s_evt_handle == NULL)
{
ESP_LOGE(TAG, "line-%u event group create failed", __LINE__);
assert(0);
}
/* malloc double buffer for usb payload, xfer_buffer_size >= frame_buffer_size*/
// xfer_buffer_a = (uint8_t *)heap_caps_calloc(1, DEMO_UVC_XFER_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
xfer_buffer_a = (uint8_t *)heap_caps_malloc(DEMO_UVC_XFER_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
assert(xfer_buffer_a != NULL);
xfer_buffer_b = (uint8_t *)heap_caps_malloc(DEMO_UVC_XFER_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
assert(xfer_buffer_b != NULL);
/* malloc frame buffer for a jpeg frame*/
frame_buffer = (uint8_t *)heap_caps_malloc(DEMO_UVC_XFER_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
assert(frame_buffer != NULL);
/* Initialize the screen */
ESP_ERROR_CHECK(_display_init());
/* Initialize the button to switch resolution */
ESP_ERROR_CHECK(_switch_button_init());
/* Initialize the screen according to the resolution stored in nvs */
ESP_ERROR_CHECK(_usb_stream_init());
/* Register a callback to control uvc frame size */
ESP_ERROR_CHECK(usb_streaming_state_register(&_stream_state_changed_cb, NULL));
/* Start stream with pre-configs, usb stream driver will create multi-tasks internal
to handle usb data from different pipes, and user's callback will be called after new frame ready. */
ESP_ERROR_CHECK(usb_streaming_start());
ESP_ERROR_CHECK(usb_streaming_connect_wait(portMAX_DELAY));
}
@@ -0,0 +1,51 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_TARGET_ESP32S3=y
#
# ESP System Settings
#
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
#
# FreeRTOS
#
#
# Kernel
#
CONFIG_FREERTOS_HZ=1000
#
# Compiler options
#
CONFIG_COMPILER_OPTIMIZATION_PERF=y
#
# ESP PSRAM
#
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
#
# SPI RAM config
#
CONFIG_SPIRAM_MODE_OCT=y
#
# Serial flasher config
#
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
#
# LCD
#
CONFIG_BSP_LCD_RGB_BUFFER_NUMS=2
CONFIG_BSP_LCD_DRAW_BUF_HEIGHT=150
CONFIG_BSP_LCD_DRAW_BUF_DOUBLE=y
CONFIG_LV_COLOR_16_SWAP=y
@@ -0,0 +1,16 @@
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
#
# Serial flasher config
#
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
#
# SPI RAM config
#
CONFIG_SPIRAM_SPEED_120M=y
#
# Cache config
#
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
+145
View File
@@ -0,0 +1,145 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
#
# SPDX-License-Identifier: Apache-2.0
"""
This file is used in CI generate binary files for different kinds of apps
"""
import argparse
import sys
import os
import re
from pathlib import Path
from typing import List
from idf_build_apps import LOGGER, App, build_apps, find_apps, setup_logging
PROJECT_ROOT = Path(__file__).parent.parent.absolute()
APPS_BUILD_PER_JOB = 30
IGNORE_WARNINGS = [
]
def _get_idf_version():
if os.environ.get('IDF_VERSION'):
return os.environ.get('IDF_VERSION')
version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake')
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
ver = {}
with open(version_path) as f:
for line in f:
m = regex.match(line)
if m:
ver[m.group(1)] = m.group(2)
return '{}.{}'.format(int(ver['MAJOR']), int(ver['MINOR']))
def get_cmake_apps(
paths,
target,
config_rules_str,
default_build_targets,
): # type: (List[str], str, List[str]) -> List[App]
idf_ver = _get_idf_version()
apps = find_apps(
paths,
recursive=True,
target=target,
build_dir=f'{idf_ver}/build_@t_@w',
config_rules_str=config_rules_str,
build_log_path='build_log.txt',
size_json_path='size.json',
check_warnings=True,
preserve=True,
default_build_targets=default_build_targets,
manifest_files=[
],
)
return apps
def main(args): # type: (argparse.Namespace) -> None
default_build_targets = args.default_build_targets.split(',') if args.default_build_targets else None
apps = get_cmake_apps(args.paths, args.target, args.config, default_build_targets)
if args.exclude_apps:
apps_to_build = [app for app in apps if app.name not in args.exclude_apps]
else:
apps_to_build = apps[:]
LOGGER.info('Found %d apps after filtering', len(apps_to_build))
LOGGER.info(
'Suggest setting the parallel count to %d for this build job',
len(apps_to_build) // APPS_BUILD_PER_JOB + 1,
)
ret_code = build_apps(
apps_to_build,
parallel_count=args.parallel_count,
parallel_index=args.parallel_index,
dry_run=False,
collect_size_info=args.collect_size_info,
keep_going=True,
ignore_warning_strs=IGNORE_WARNINGS,
copy_sdkconfig=True,
)
sys.exit(ret_code)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Build all the apps for different test types. Will auto remove those non-test apps binaries',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('paths', nargs='*', help='Paths to the apps to build.')
parser.add_argument(
'-t', '--target',
default='all',
help='Build apps for given target. could pass "all" to get apps for all targets',
)
parser.add_argument(
'--config',
default=['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default'],
action='append',
help='Adds configurations (sdkconfig file names) to build. This can either be '
'FILENAME[=NAME] or FILEPATTERN. FILENAME is the name of the sdkconfig file, '
'relative to the project directory, to be used. Optional NAME can be specified, '
'which can be used as a name of this configuration. FILEPATTERN is the name of '
'the sdkconfig file, relative to the project directory, with at most one wildcard. '
'The part captured by the wildcard is used as the name of the configuration.',
)
parser.add_argument(
'--parallel-count', default=1, type=int, help='Number of parallel build jobs.'
)
parser.add_argument(
'--parallel-index',
default=1,
type=int,
help='Index (1-based) of the job, out of the number specified by --parallel-count.',
)
parser.add_argument(
'--collect-size-info',
type=argparse.FileType('w'),
help='If specified, the test case name and size info json will be written to this file',
)
parser.add_argument(
'--exclude-apps',
nargs='*',
help='Exclude build apps',
)
parser.add_argument(
'--default-build-targets',
default=None,
help='default build targets used in manifest files',
)
parser.add_argument(
'-v', '--verbose',
action='count', default=0,
help='Show verbose log message',
)
arguments = parser.parse_args()
if not arguments.paths:
arguments.paths = [PROJECT_ROOT]
setup_logging(verbose=arguments.verbose) # Info
main(arguments)