diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c52662..9c0cd62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ add_compile_definitions(BOARD_HAS_PSRAM) set( COMPONENTS - "main esptool_py esp_psram jpegdec task format nvs esp-box t-deck monitor wifi socket rtsp mdns" + "main esptool_py esp_psram jpegdec task format nvs esp-box t-deck monitor wifi ws-s3-touch socket rtsp mdns" CACHE STRING "List of components to include" ) diff --git a/dependencies.lock b/dependencies.lock index 0f3e413..4b5f652 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,6 +1,6 @@ dependencies: espp/adxl345: - component_hash: 13790bcc66d48b0b784d7daf2aa8b3278cf234fa9d06bd2d542dc575c76c1020 + component_hash: bee6d94220074aae25ed3893c31c5e14582432310121d8810b15d5f04d7458e8 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -12,9 +12,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/base_component: - component_hash: 635bfa884f4dcbabbcca3ae50c4a92ba4d4271b81004e26b13fc1647be7a450b + component_hash: 5979a81ac2629505342013bd9c9d8fcaae9d09b411fe9f88dcbdd93b5c325359 dependencies: - name: espp/logger registry_url: https://components.espressif.com @@ -26,9 +26,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/base_peripheral: - component_hash: f1d0fc3623286d98648c9b91e00c312c6222a76eeaddfa23967d3284d6ec3cb6 + component_hash: 7e1a66c9460a608e7fd453c219dcecb8cdd3de1a882206d69e348e8a465c6e15 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -40,9 +40,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/byte90: - component_hash: 965497677115c9a0d7f666aee0dab1a259e065e25f9259dbc68bd9a65838420c + component_hash: ae06da17ee606022eb322b963dc3145e58f88a5a309e5a18b0931e0053f2bc95 dependencies: - name: espp/adxl345 registry_url: https://components.espressif.com @@ -78,9 +78,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/cli: - component_hash: 5ec7838aa1740d49d43163487e272143adbeb7a616ff61dbbfcf287565d76d0b + component_hash: 2b13a35fa82cd1d9a6ad00b323e05c7c5acfb2450f79649b20f184a38928fc29 dependencies: - name: espp/logger registry_url: https://components.espressif.com @@ -92,9 +92,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/codec: - component_hash: 6c5d57326871b6e96ae57f979c6c250f72e44d42a126675069d9d510ecdd3844 + component_hash: 032e1e5265c2db47d8e79a093638bbdf9f524c298b3984fdd49c0f7996cd4667 dependencies: - name: idf require: private @@ -102,9 +102,23 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 + espp/cst816: + component_hash: 9e8e4bdbde07efbd3e0380210fecd7237670058193fa3f4766b2ca4f263a49a1 + dependencies: + - name: espp/base_peripheral + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.20 espp/display: - component_hash: 91d94059ffee2e25821d5175a4538d12fa7a698bdf9d3ee0fe504ffe391d452b + component_hash: d8cee45cabecd2dc4dbc7ef5fef29be3e0a320aba3ea78e0345eb70b2e70e9b1 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -128,9 +142,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/display_drivers: - component_hash: 9139fe86ad52a072507fafcb899f8e17628d5a0282e75c467195686cf06b273c + component_hash: 2231302f9c5ce588647ab4d4c1dd051dd2cfa570884d0fc6d7c6b498f6fe21a2 dependencies: - name: espp/display registry_url: https://components.espressif.com @@ -146,9 +160,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/esp-box: - component_hash: cb43f2e38f4419bff7fcff1729b0156dce58fd2f2bb3f76aca4ca2c297713ba3 + component_hash: e1ddc72b8c6b917827a90959fbfb966c7ea65111e9cd8061d5d1a935a40437b0 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -200,9 +214,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/format: - component_hash: aebe0d55e833d61eb53313dcdd3ee9bd8f2e328d6db1ae3e01bca49b17db39c6 + component_hash: 67ca1433d83f4df8af56094630fb4907f1ceed4a80726aaa59860fbe1a39263a dependencies: - name: idf require: private @@ -210,9 +224,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/gt911: - component_hash: 3b94925186fe8b3098926184280193ce77afd7dc31bef35ac09a3b7bf8ca1c5a + component_hash: b7f4db0c66c08a85f402cfdacd2fbdb0601d3c8e9f6202b35b05560956180ee1 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -224,9 +238,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/i2c: - component_hash: 0b746f8d5c4df78f4437427a39a75242b8f6302f5d43b790a24ab1733f50057d + component_hash: 1f0f3649a42d8f8e61d835934a20de941176660ef77e40dedef00dd5396b88d3 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -246,9 +260,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/icm42607: - component_hash: c6b1749f91f56866df710f0438760ee165c52c013d281f83609f609f1a2faac5 + component_hash: 649aed2ac6df5c3bf8caf06dc5570482d6cb44c0926110e2e96ed54c986acf39 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -264,9 +278,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/input_drivers: - component_hash: f2d94990c47ddd4572972feb7362c8aafe49f92e61bd3e2213ce511cc4bdce82 + component_hash: 75842d675daa6ef1fd694fae847c7f3dfc3f1d4ecea543c8af22a8103f88b8d3 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -282,9 +296,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/interrupt: - component_hash: 00207be0384cb68d79fc0b66d7264a63c967ca0509be459638590de3eb9dcd51 + component_hash: d3d9bd339f3dc1dd3f2247da4f102b65d5282cf265a9761c388b2dec46239738 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -300,9 +314,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/led: - component_hash: 7439dc35497547c352b0fe14bea4f37ca92cfd2df81ccfed0be545378a37d487 + component_hash: cd1759001b64f3a4a664787027e853b39bef7ec0d0ef7b3885b3549c0074be83 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -318,9 +332,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/logger: - component_hash: d8d68bc881f613a4007f5cc18f11f4f270bb93d6f0adfaad80cc65593da03bd9 + component_hash: 0e768020cfbc83fe23c6cba2c830430e5b6a28c21b3706284c40288473538a74 dependencies: - name: espp/format registry_url: https://components.espressif.com @@ -332,9 +346,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/math: - component_hash: 4d27b369dd40eef16bf5f8e9f0f4fb0281f8fe50cc88a1b840b1b134167a1f96 + component_hash: 1105de489f3a6ac40d50a58aa08d8565bfbe4203f62142d62587aa70cee77f33 dependencies: - name: espp/format registry_url: https://components.espressif.com @@ -346,9 +360,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 espp/monitor: - component_hash: a49ecc074a92683fcfda529300a06de3b7be9dec07775c4b690cf75ceaacb097 + component_hash: 8af1b330fccaee56be4562634a0ee40c0c4663ffe3f87a445617d4095a11760a dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -364,9 +378,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/nvs: - component_hash: 4e7fa7cbdaf30ed7f31938070d5aa57f0e00b2f3c8c01cb47c78d5cb4f22dde6 + component_hash: 38e278e9c6f73ff89832852b98e9d205354201adad4416ebf416dd9036223c9e dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -378,9 +392,45 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 + espp/pcf85063: + component_hash: 8eabaad2c6c7b29cbe40c6d37810b9ef2b98158656730b8ce5a6f07e35dd459e + dependencies: + - name: espp/base_peripheral + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/utils + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.20 + espp/qmi8658: + component_hash: 13172051443f95f601fa15518d699cbdfc0fee4a74da3cda584e4aeeb7618c24 + dependencies: + - name: espp/base_peripheral + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/math + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.20 espp/rtsp: - component_hash: 5bd62da21938c448603ca2b13c2b22509d00b4fc52aa3f12e93a595f41baaad4 + component_hash: be8d88bcfaa9979b0b36a2872d7837cb38477e49333061be4c2d511cee7f4b17 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -400,9 +450,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/socket: - component_hash: f02123d81ea8dd83dc93d89e5322f47c1c91e4f1179190ca8f215ada27f7cfd6 + component_hash: abc6091c3ba1aafc37287ecc572c50636a2020158c44b4aab5bd50e7b6951588 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -418,9 +468,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/t-deck: - component_hash: ef1a61308e528aa8b6554e32b08b6e488cc99e5fbe34f33a0e73498802e0ee0a + component_hash: 4961948057fc586693409596f9e7f22a9e62f8510e4814b7e8fd581b11f1905b dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -464,9 +514,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/t_keyboard: - component_hash: c00262a893783b1d5452a9a71a259234abcc691d048307baf9c9b07310a06a1e + component_hash: 86ec7dfc9203156c3dffd404783c5821a70e00cb6099f5e3cb07691efabc4bcc dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -478,9 +528,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.19 espp/task: - component_hash: dd20f4dd144b4fb78ecaec896c0bc660a87eb8a8327d5051ee9852d7b685f1f7 + component_hash: 4923848573f9d07d48552e12d4a684d95decbfc2b902a5db1e60403b2f4f8f86 dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -492,9 +542,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 espp/tt21100: - component_hash: 580f437e9bc0079170bf7461a953bcc17616ff156b99d21069fbe0fb4bfec424 + component_hash: 0a50f122b3f03b483cb425883a2f4d06522505d9aade52ef88a5c4c6c17acf66 dependencies: - name: espp/base_peripheral registry_url: https://components.espressif.com @@ -506,9 +556,19 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.0.18 + version: 1.0.20 + espp/utils: + component_hash: 162dbe79ef4911a5a0353d64a63721d32f54a6f600bd2ec4fb25c538b611f6f6 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.20 espp/wifi: - component_hash: 4c51c4a6916b11834eb9ea3e0fb752e1a738b65c7f4265fc741cb63337dcfd87 + component_hash: bf3a4b2bae1eb7b27a94a11ed827e636bf293db0f6d6fcee6daad5287acf235a dependencies: - name: espp/base_component registry_url: https://components.espressif.com @@ -524,7 +584,61 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.18 + version: 1.0.20 + espp/ws-s3-touch: + component_hash: 03014aa06239d0aa72b6c452b7eadb766de3603329f2d757f946336f0fba8abd + dependencies: + - name: espp/base_component + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/cst816 + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/display + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/display_drivers + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/i2c + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/input_drivers + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/interrupt + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/led + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/pcf85063 + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/qmi8658 + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: espp/task + registry_url: https://components.espressif.com + require: private + version: '>=1.0' + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.20 espressif/esp-dsp: component_hash: 42dce32d46ac93dc11f60d368e29a830e9661c7345d794b8a45c343479cae636 dependencies: @@ -548,7 +662,7 @@ dependencies: idf: source: type: idf - version: 5.4.1 + version: 5.4.2 lvgl/lvgl: component_hash: b702d642e03e95928046d5c6726558e6444e112420c77efa5fdb6650b0a13c5d dependencies: [] @@ -566,9 +680,10 @@ direct_dependencies: - espp/t-deck - espp/task - espp/wifi +- espp/ws-s3-touch - espressif/esp-dsp - espressif/mdns - idf -manifest_hash: 3e7c4d73fec32bcb3110c88480dbb612b4aa8104c05a84ef273c11296d63e3a1 +manifest_hash: 25cebf0828f59a9658091d09c2ab43e822f281c64f36520b9f89f52b1a6fa643 target: esp32s3 version: 2.0.0 diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 7f0075b..d5bccb0 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -11,6 +11,8 @@ menu "Camera Display Configuration" bool "LILYGO T DECK" config HARDWARE_BYTE90 bool "BYTE90" + config HARDWARE_WS_S3_TOUCH + bool "Waveshare S3 Touch" endchoice config ESP_WIFI_SSID diff --git a/main/idf_component.yml b/main/idf_component.yml index b326274..59243fa 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -12,4 +12,5 @@ dependencies: espp/socket: '>=1.0' espp/nvs: '>=1.0' espp/task: '>=1.0' + espp/ws-s3-touch: '>=1.0' espressif/mdns: '>=1.8' diff --git a/main/main.cpp b/main/main.cpp index 35ee06a..ad07179 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -30,14 +30,22 @@ using hal = espp::TDeck; #elif CONFIG_HARDWARE_BYTE90 #include "byte90.hpp" using hal = espp::Byte90; +#elif CONFIG_HARDWARE_WS_S3_TOUCH +#include "ws-s3-touch.hpp" +using hal = espp::WsS3Touch; #else #error "No hardware defined" #endif using namespace std::chrono_literals; +using DisplayDriver = hal::DisplayDriver; static espp::Logger logger({.tag = "Camera Display", .level = espp::Logger::Verbosity::INFO}); +// frame buffers for decoding into +static uint8_t *fb0 = nullptr; +static uint8_t *fb1 = nullptr; +// DRAM for actual vram (used by SPI to send to LCD) static uint8_t *vram0 = nullptr; static uint8_t *vram1 = nullptr; static std::atomic num_frames_received{0}; @@ -45,13 +53,24 @@ static std::atomic num_frames_displayed{0}; static std::atomic elapsed{0}; static std::chrono::high_resolution_clock::time_point connected_time; +// video +static std::unique_ptr video_task_{nullptr}; +static QueueHandle_t video_queue_{nullptr}; +static bool initialize_video(); +static bool video_task_callback(std::mutex &m, std::condition_variable &cv, bool &task_notified); +static void clear_screen(); +static void push_frame(const void *frame); + +// rtsp std::unique_ptr start_rtsp_task; static std::shared_ptr rtsp_client; -static constexpr size_t vram_size = hal::lcd_width() * 50 * 2; // 50 lines of 16-bit pixels +static constexpr int num_rows_in_vram = 50; +static constexpr size_t vram_size = hal::lcd_width() * num_rows_in_vram * sizeof(hal::Pixel); +static constexpr size_t fb_size = hal::lcd_width() * hal::lcd_height() * sizeof(hal::Pixel); static std::mutex jpeg_mutex; static std::condition_variable jpeg_cv; -static constexpr size_t MAX_JPEG_FRAMES = 2; +static constexpr size_t MAX_JPEG_FRAMES = 3; static std::deque> jpeg_frames; bool start_rtsp_client(std::mutex &m, std::condition_variable &cv, bool &task_notified); @@ -71,6 +90,21 @@ extern "C" void app_main(void) { return; } + // allocate some frame buffers for jpeg decoding, which should be screen-size + // and in PSRAM + fb0 = (uint8_t *)heap_caps_malloc(fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + fb1 = (uint8_t *)heap_caps_malloc(fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!fb0 || !fb1) { + logger.error("Could not allocate frame buffers for LCD"); + if (fb0) { + heap_caps_free(fb0); + } + if (fb1) { + heap_caps_free(fb1); + } + return; + } + // allocate some DMA-capable VRAM for jpeg decoding / display operations vram0 = (uint8_t *)heap_caps_malloc(vram_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); vram1 = (uint8_t *)heap_caps_malloc(vram_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); @@ -85,6 +119,19 @@ extern "C" void app_main(void) { return; } + logger.info("Allocated frame buffers: fb0 = {} B, fb1 = {} B", fb_size, fb_size); + logger.info("Allocated VRAM: vram0 = {} B, vram1 = {} B", vram_size, vram_size); + + // initialize the video task + if (!initialize_video()) { + logger.error("Could not initialize video task"); + return; + } + + // clear the screen + logger.info("Clearing screen"); + clear_screen(); + // create the parsing and display task logger.info("Starting display task"); auto display_task = espp::Task::make_unique({.callback = display_task_fn, @@ -107,6 +154,7 @@ extern "C" void app_main(void) { espp::WifiSta wifi_sta( {.ssid = CONFIG_ESP_WIFI_SSID, .password = CONFIG_ESP_WIFI_PASSWORD, + // .phy_rate = WIFI_PHY_RATE_MCS5_SGI, .num_connect_retries = CONFIG_ESP_MAXIMUM_RETRY, .on_connected = []() { logger.info("Connected to WiFi, waiting for IP address"); }, .on_disconnected = @@ -292,6 +340,7 @@ bool start_rtsp_client(std::mutex &m, std::condition_variable &cv, bool &task_no return true; // we're done with our work, no need to run again, so stop the task } +static size_t frame_buffer_index = 0; // function for drawing the minimum compressible units // cppcheck-suppress constParameterCallback int drawMCUs(JPEGDRAW *pDraw) { @@ -301,17 +350,16 @@ int drawMCUs(JPEGDRAW *pDraw) { auto xe = pDraw->x + pDraw->iWidth - 1; auto ye = pDraw->y + pDraw->iHeight - 1; - if (iCount * 2 > vram_size) { - logger.error("Not enough VRAM for image: {} B, available: {} B", iCount * 2, vram_size); - return 0; // not enough VRAM to draw the image + uint16_t *dst = (uint16_t *)(frame_buffer_index ? fb1 : fb0); + uint16_t *src = (uint16_t *)(pDraw->pPixels); + // copy the pixels from the JPEG draw structure to the framebuffer at the + // appropriate position + for (int row = 0; row < pDraw->iHeight; row++) { + // copy a whole row at a time + memcpy(&dst[(ys + row) * hal::lcd_width() + xs], &src[row * pDraw->iWidth], + pDraw->iWidth * sizeof(uint16_t)); } - static size_t frame_buffer_index = 0; - uint8_t *out_img_buf = (uint8_t *)(frame_buffer_index ? vram1 : vram0); - frame_buffer_index = frame_buffer_index ? 0 : 1; - memcpy(out_img_buf, pDraw->pPixels, iCount * 2); - - hal::get().write_lcd_lines(xs, ys, xe, ye, out_img_buf, 0); // returning true (1) tells JPEGDEC to continue decoding. Returning false // (0) would quit decoding immediately. return 1; @@ -335,19 +383,25 @@ bool display_task_fn(std::mutex &m, std::condition_variable &cv) { auto image_data = image->get_data(); logger.info("Decoding image of size {} B, shape = {} x {}", image_data.size(), image->get_width(), image->get_height()); + // update to the current frame buffer index + frame_buffer_index = frame_buffer_index ^ 0x01; if (jpeg.openRAM((uint8_t *)(image_data.data()), image_data.size(), drawMCUs)) { logger.debug("Image size: {} x {}, orientation: {}, bpp: {}", jpeg.getWidth(), jpeg.getHeight(), jpeg.getOrientation(), jpeg.getBpp()); jpeg.setPixelType(RGB565_BIG_ENDIAN); - if (!jpeg.decode(0, 0, 0)) { - logger.error("Error decoding"); + // decode the JPEG image + if (!jpeg.decode(0, 0, JPEG_USES_DMA)) { + logger.debug("Error decoding"); + } else { + num_frames_displayed += 1; + // push the frame for rendering + push_frame(frame_buffer_index ? fb1 : fb0); } } else { logger.error("error opening jpeg image"); } auto end = std::chrono::high_resolution_clock::now(); elapsed = std::chrono::duration(end - start).count(); - num_frames_displayed += 1; // signal that we do not want to stop the task return false; } @@ -440,3 +494,83 @@ bool find_mdns_service(const char *service_name, const char *proto, std::string mdns_query_results_free(results); return found_service; } + +/// Video related functions: + +bool initialize_video() { + if (video_queue_ || video_task_) { + return true; + } + + video_queue_ = xQueueCreate(1, sizeof(uint16_t *)); + using namespace std::placeholders; + video_task_ = espp::Task::make_unique({ + .callback = std::bind(video_task_callback, _1, _2, _3), + .task_config = + {.name = "video task", .stack_size_bytes = 4 * 1024, .priority = 20, .core_id = 1}, + }); + video_task_->start(); + return true; +} + +void clear_screen() { + static int buffer = 0; + xQueueSend(video_queue_, &buffer, portMAX_DELAY); +} + +void IRAM_ATTR push_frame(const void *frame) { xQueueSend(video_queue_, &frame, portMAX_DELAY); } + +bool video_task_callback(std::mutex &m, std::condition_variable &cv, bool &task_notified) { + const void *_frame_ptr; + if (xQueueReceive(video_queue_, &_frame_ptr, portMAX_DELAY) != pdTRUE) { + return false; + } + static constexpr int num_lines_to_write = num_rows_in_vram; + using Pixel = hal::Pixel; + + static auto &hw = hal::get(); + + auto lcd_height = hw.lcd_height(); + auto lcd_width = hw.lcd_width(); + int x_offset = 0; + int y_offset = 0; + DisplayDriver::get_offset(x_offset, y_offset); + + static uint16_t vram_index = 0; // has to be static so that it persists between calls + + // special case: if _frame_ptr is null, then we simply fill the screen with 0 + if (_frame_ptr == nullptr) { + for (int y = 0; y < lcd_height; y += num_lines_to_write) { + Pixel *_buf = (Pixel *)((uint32_t)vram0 * (vram_index ^ 0x01) + (uint32_t)vram1 * vram_index); + int num_lines = std::min(num_lines_to_write, lcd_height - y); + // memset the buffer to 0 + memset(_buf, 0, lcd_width * num_lines * sizeof(Pixel)); + hw.write_lcd_lines(x_offset, y + y_offset, x_offset + lcd_width - 1, + y + y_offset + num_lines - 1, (uint8_t *)&_buf[0], 0); + vram_index = vram_index ^ 0x01; + } + + // now return + return false; + } + + for (int y = 0; y < lcd_height; y += num_lines_to_write) { + uint16_t *_buf = + (uint16_t *)((uint32_t)vram0 * (vram_index ^ 0x01) + (uint32_t)vram1 * vram_index); + int num_lines = std::min(num_lines_to_write, lcd_height - y); + const uint16_t *_frame = (const uint16_t *)_frame_ptr; + for (int i = 0; i < num_lines; i++) { + // write two pixels (32 bits) at a time because it's faster + for (int j = 0; j < lcd_width; j += 2) { + uint32_t *src = (uint32_t *)&_frame[(y + i) * lcd_width + j]; + uint32_t *dst = (uint32_t *)&_buf[i * lcd_width + j]; + dst[0] = src[0]; // copy two pixels (32 bits) at a time + } + } + hw.write_lcd_lines(x_offset, y + y_offset, x_offset + lcd_width - 1, + y + y_offset + num_lines - 1, (uint8_t *)&_buf[0], 0); + vram_index = vram_index ^ 0x01; + } + + return false; +}