Skip to content

Commit 271487a

Browse files
authored
Merge pull request #20 from strange-v/dev
HW decoding on ESP32-P4 and bug-fixes
2 parents b8f8485 + 24582e0 commit 271487a

File tree

3 files changed

+172
-43
lines changed

3 files changed

+172
-43
lines changed

components/remote_webview/idf_component.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,11 @@ dependencies:
33
- espressif/esp_websocket_client
44
- espressif/esp-dsp
55
- jpegdec
6+
7+
- esp_driver_jpeg:
8+
require: private
9+
rules: [ { if: target == "esp32p4" } ]
10+
11+
- esp_mm:
12+
require: private
13+
rules: [ { if: target == "esp32p4" } ]

components/remote_webview/remote_webview.cpp

Lines changed: 140 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
#include "esp_timer.h"
1010
#include "esp_heap_caps.h"
1111
#include "esp_websocket_client.h"
12+
#include "esp_efuse.h"
1213
#include "freertos/FreeRTOS.h"
1314
#include "freertos/task.h"
1415

1516
namespace esphome {
1617
namespace remote_webview {
1718

18-
static const char *const TAG = "Remove_WebView";
19+
static const char *const TAG = "Remote_WebView";
1920
RemoteWebView *RemoteWebView::self_ = nullptr;
2021

2122
void RemoteWebView::setup() {
@@ -26,7 +27,10 @@ void RemoteWebView::setup() {
2627
return;
2728
}
2829

29-
q_decode_ = xQueueCreate(cfg::decode_queue_depth, sizeof(WsMsg));
30+
display_width_ = display_->get_width();
31+
display_height_ = display_->get_height();
32+
33+
q_decode_ = xQueueCreate(cfg::decode_queue_depth, sizeof(WsMsg));
3034
ws_send_mtx_ = xSemaphoreCreateMutex();
3135

3236
start_decode_task_();
@@ -35,8 +39,45 @@ void RemoteWebView::setup() {
3539
if (touch_) {
3640
touch_listener_ = new RemoteWebViewTouchListener(this);
3741
touch_->register_listener(touch_listener_);
38-
ESP_LOGI(TAG, "touch listener registered");
42+
ESP_LOGD(TAG, "touch listener registered");
43+
}
44+
45+
#if REMOTE_WEBVIEW_HW_JPEG
46+
jpeg_decode_engine_cfg_t jcfg = {
47+
.timeout_ms = 200,
48+
};
49+
if (jpeg_new_decoder_engine(&jcfg, &hw_dec_) != ESP_OK) {
50+
hw_dec_ = nullptr;
51+
}
52+
53+
if (hw_dec_) {
54+
const int W = display_->get_width();
55+
const int H = display_->get_height();
56+
const int aligned_w = (W + 15) & ~15;
57+
const int aligned_h = (H + 15) & ~15;
58+
59+
const size_t max_buffer_size = (size_t)aligned_w * (size_t)aligned_h * 2u;
60+
61+
jpeg_decode_memory_alloc_cfg_t in_cfg { .buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER };
62+
jpeg_decode_memory_alloc_cfg_t out_cfg { .buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER };
63+
64+
hw_decode_input_buf_ = (uint8_t*)jpeg_alloc_decoder_mem((uint32_t)max_buffer_size, &in_cfg, &hw_decode_input_size_);
65+
hw_decode_output_buf_ = (uint8_t*)jpeg_alloc_decoder_mem((uint32_t)max_buffer_size, &out_cfg, &hw_decode_output_size_);
66+
67+
if (!hw_decode_input_buf_ || !hw_decode_output_buf_) {
68+
ESP_LOGE(TAG, "Failed to allocate HW decoder buffers");
69+
if (hw_decode_input_buf_) free(hw_decode_input_buf_);
70+
if (hw_decode_output_buf_) free(hw_decode_output_buf_);
71+
hw_decode_input_buf_ = nullptr;
72+
hw_decode_output_buf_ = nullptr;
73+
jpeg_del_decoder_engine(hw_dec_);
74+
hw_dec_ = nullptr;
75+
} else {
76+
ESP_LOGD(TAG, "HW decoder buffers allocated: input=%u, output=%u",
77+
(unsigned)hw_decode_input_size_, (unsigned)hw_decode_output_size_);
78+
}
3979
}
80+
#endif
4081
}
4182

4283
void RemoteWebView::dump_config() {
@@ -49,6 +90,12 @@ void RemoteWebView::dump_config() {
4990
ESP_LOGCONFIG(TAG, " display: %dx%d", display_->get_width(), display_->get_height());
5091
}
5192

93+
#if REMOTE_WEBVIEW_HW_JPEG
94+
ESP_LOGCONFIG(TAG, " hw_jpeg: %s", hw_dec_ ? "yes" : "no");
95+
#else
96+
ESP_LOGCONFIG(TAG, " hw_jpeg: no");
97+
#endif
98+
5299
ESP_LOGCONFIG(TAG, " server: %s:%d", server_host_.c_str(), server_port_);
53100
ESP_LOGCONFIG(TAG, " url: %s", url_.c_str());
54101

@@ -79,7 +126,7 @@ bool RemoteWebView::open_url(const std::string &s) {
79126

80127
if (ws_send_open_url_(s.c_str(), 0)) {
81128
url_ = s;
82-
ESP_LOGI(TAG, "opened URL: %s", s.c_str());
129+
ESP_LOGD(TAG, "opened URL: %s", s.c_str());
83130
return true;
84131
}
85132

@@ -145,7 +192,7 @@ void RemoteWebView::ws_event_handler_(void *handler_arg, esp_event_base_t, int32
145192

146193
case WEBSOCKET_EVENT_DISCONNECTED:
147194
if (self_) self_->ws_client_ = nullptr;
148-
ESP_LOGW(TAG, "[ws] disconnected");
195+
ESP_LOGI(TAG, "[ws] disconnected");
149196
if (self_) self_->last_keepalive_us_ = 0;
150197
reasm_reset_(*r);
151198
break;
@@ -160,8 +207,11 @@ void RemoteWebView::ws_event_handler_(void *handler_arg, esp_event_base_t, int32
160207

161208
if (e->payload_offset == 0) {
162209
reasm_reset_(*r);
163-
if ((size_t)e->payload_len > cfg::ws_max_message_bytes) {
164-
ESP_LOGE(TAG, "WS message too large: %u > %u", (unsigned)e->payload_len, (unsigned)cfg::ws_max_message_bytes);
210+
const size_t max_allowed = (self_ && self_->max_bytes_per_msg_ > 0)
211+
? (size_t)self_->max_bytes_per_msg_
212+
: cfg::ws_max_message_bytes;
213+
if ((size_t)e->payload_len > max_allowed) {
214+
ESP_LOGE(TAG, "WS message too large: %u > %u", (unsigned)e->payload_len, (unsigned)max_allowed);
165215
break;
166216
}
167217
r->total = (size_t)e->payload_len;
@@ -252,15 +302,12 @@ void RemoteWebView::process_frame_packet_(const uint8_t *data, size_t len)
252302
frame_bytes_ += len;
253303
frame_tiles_ += fi.tile_count;
254304

255-
const int FB_W = display_ ? display_->get_width() : 480;
256-
const int FB_H = display_ ? display_->get_height() : 480;
257-
258305
for (uint16_t i = 0; i < fi.tile_count; i++) {
259306
proto::TileHeader th{};
260307
if (!proto::parse_tile_header(data, len, th, off)) return;
261308
if (off + th.dlen > len) return;
262309

263-
if (th.w == 0 || th.h == 0 || th.w > FB_W || th.h > FB_H) {
310+
if (th.w == 0 || th.h == 0 || th.w > display_width_ || th.h > display_height_) {
264311
off += th.dlen;
265312
continue;
266313
}
@@ -270,7 +317,6 @@ void RemoteWebView::process_frame_packet_(const uint8_t *data, size_t len)
270317
}
271318

272319
off += th.dlen;
273-
taskYIELD();
274320
}
275321

276322
if (fi.flags & proto::kFlafLastOfFrame) {
@@ -305,19 +351,68 @@ void RemoteWebView::process_frame_stats_packet_(const uint8_t *data, size_t len)
305351
}
306352

307353
bool RemoteWebView::decode_jpeg_tile_to_lcd_(int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len) {
308-
if (!display_ || !data || len == 0) return false;
354+
if (!data || !len) return false;
309355

356+
#if REMOTE_WEBVIEW_HW_JPEG
357+
if (hw_dec_ && hw_decode_input_buf_ && hw_decode_output_buf_) {
358+
jpeg_decode_picture_info_t hdr{};
359+
if (jpeg_decoder_get_info(data, (uint32_t)len, &hdr) != ESP_OK || !hdr.width || !hdr.height) {
360+
return decode_jpeg_tile_software_(dst_x, dst_y, data, len);
361+
}
362+
363+
const int aligned_w = (hdr.width + 15) & ~15;
364+
const int aligned_h = (hdr.height + 15) & ~15;
365+
const uint32_t out_sz = (uint32_t)aligned_w * (uint32_t)aligned_h * 2u;
366+
367+
if (aligned_w != (int)hdr.width) {
368+
ESP_LOGW(TAG, "jpeg dimensions not aligned: %u x %u", (unsigned)hdr.width, (unsigned)hdr.height);
369+
return decode_jpeg_tile_software_(dst_x, dst_y, data, len);
370+
}
371+
372+
if (len > hw_decode_input_size_ || out_sz > hw_decode_output_size_) {
373+
ESP_LOGW(TAG, "tile too large for HW decoder buffers");
374+
return decode_jpeg_tile_software_(dst_x, dst_y, data, len);
375+
}
376+
377+
jpeg_decode_cfg_t jcfg{};
378+
jcfg.output_format = JPEG_DECODE_OUT_FORMAT_RGB565;
379+
jcfg.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR;
380+
jcfg.conv_std = JPEG_YUV_RGB_CONV_STD_BT709;
381+
382+
memcpy(hw_decode_input_buf_, data, len);
383+
384+
uint32_t written = 0;
385+
esp_err_t dr = jpeg_decoder_process(hw_dec_, &jcfg, hw_decode_input_buf_, (uint32_t)len,
386+
hw_decode_output_buf_, (uint32_t)hw_decode_output_size_, &written);
387+
388+
if (dr != ESP_OK) {
389+
return decode_jpeg_tile_software_(dst_x, dst_y, data, len);
390+
}
391+
392+
display_->draw_pixels_at(dst_x, dst_y, (int)hdr.width, (int)hdr.height, hw_decode_output_buf_,
393+
esphome::display::COLOR_ORDER_RGB,
394+
esphome::display::COLOR_BITNESS_565,
395+
rgb565_big_endian_);
396+
397+
return true;
398+
}
399+
#endif // REMOTE_WEBVIEW_HW_JPEG
400+
401+
return decode_jpeg_tile_software_(dst_x, dst_y, data, len);
402+
}
403+
404+
bool RemoteWebView::decode_jpeg_tile_software_(int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len) {
310405
if (!jd_.openRAM((uint8_t*)data, (int)len, &RemoteWebView::jpeg_draw_cb_s_)) {
311-
ESP_LOGW(TAG, "openRAM failed (len=%u) err=%d", (unsigned)len, jd_.getLastError());
406+
ESP_LOGE(TAG, "openRAM failed (len=%u) err=%d", (unsigned)len, jd_.getLastError());
312407
return false;
313408
}
314409

315-
jd_.setMaxOutputSize(4 * 2048);
410+
jd_.setMaxOutputSize(8 * 2048);
316411
jd_.setPixelType(rgb565_big_endian_ ? RGB565_BIG_ENDIAN : RGB565_LITTLE_ENDIAN);
317412

318413
const int rc = jd_.decode(dst_x, dst_y, 0);
319414
if (rc == 0) {
320-
ESP_LOGW(TAG, "decode rc=%d err=%d", rc, jd_.getLastError());
415+
ESP_LOGE(TAG, "decode rc=%d err=%d", rc, jd_.getLastError());
321416
jd_.close();
322417
return false;
323418
}
@@ -330,24 +425,19 @@ int RemoteWebView::jpeg_draw_cb_s_(JPEGDRAW *p) {
330425
}
331426

332427
int RemoteWebView::jpeg_draw_cb_(JPEGDRAW *p) {
333-
if (!display_) return 0;
334-
335428
int32_t x = p->x, y = p->y, w = p->iWidth, h = p->iHeight;
336-
const int FB_W = display_->get_width();
337-
const int FB_H = display_->get_height();
338-
339-
if (x >= FB_W || y >= FB_H) return 1;
340-
if (x + w > FB_W) w = FB_W - x;
341-
if (y + h > FB_H) h = FB_H - y;
429+
430+
if (x >= display_width_ || y >= display_height_) return 1;
431+
if (x + w > display_width_) w = display_width_ - x;
432+
if (y + h > display_height_) h = display_height_ - y;
342433
if (w <= 0 || h <= 0) return 1;
343434

344-
const bool big_endian = rgb565_big_endian_;
345435
display_->draw_pixels_at(
346436
x, y, w, h,
347437
(const uint8_t *)p->pPixels,
348438
esphome::display::COLOR_ORDER_RGB,
349439
esphome::display::COLOR_BITNESS_565,
350-
big_endian
440+
rgb565_big_endian_
351441
);
352442

353443
return 1;
@@ -357,7 +447,6 @@ bool RemoteWebView::ws_send_touch_event_(proto::TouchType type, int x, int y, ui
357447
if (!ws_client_ || !ws_send_mtx_ || !esp_websocket_client_is_connected(ws_client_))
358448
return false;
359449

360-
// clamp into 16-bit
361450
if (x < 0) x = 0; if (y < 0) y = 0;
362451
if (x > 65535) x = 65535; if (y > 65535) y = 65535;
363452

@@ -384,10 +473,10 @@ bool RemoteWebView::ws_send_open_url_(const char *url, uint16_t flags) {
384473

385474
const uint32_t n = (uint32_t) strlen(url);
386475
const size_t total = sizeof(proto::OpenURLHeader) + (size_t) n;
387-
if (total > cfg::ws_max_message_bytes) return false;
476+
477+
if (total > 16 * 1024) return false;
388478

389-
// try PSRAM first
390-
auto *pkt = (uint8_t *) heap_caps_malloc(total, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
479+
auto *pkt = (uint8_t *) heap_caps_malloc(total, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
391480
if (!pkt) pkt = (uint8_t *) heap_caps_malloc(total, MALLOC_CAP_8BIT);
392481
if (!pkt) return false;
393482

@@ -495,21 +584,31 @@ std::string RemoteWebView::resolve_device_id_() const {
495584
if (!device_id_.empty()) return device_id_;
496585

497586
uint8_t mac[6] = {0};
587+
esp_err_t err = ESP_FAIL;
588+
498589
#if ESP_IDF_VERSION_MAJOR >= 5
499-
if (esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) {
500-
(void) esp_read_mac(mac, ESP_MAC_BT); // best-effort fallback
590+
err = esp_read_mac(mac, ESP_MAC_WIFI_STA);
591+
if (err != ESP_OK) {
592+
err = esp_read_mac(mac, ESP_MAC_BT);
593+
}
594+
if (err != ESP_OK) {
595+
err = esp_read_mac(mac, ESP_MAC_ETH);
596+
}
597+
if (err != ESP_OK) {
598+
err = esp_efuse_mac_get_default(mac);
501599
}
502600
#else
503-
// Older IDF: try to get base MAC from eFuse
504-
// (some SDKs declare esp_efuse_mac_get_default in esp_system.h)
505-
extern "C" esp_err_t esp_efuse_mac_get_default(uint8_t *mac);
506-
if (esp_efuse_mac_get_default) {
507-
(void) esp_efuse_mac_get_default(mac);
508-
} else {
509-
(void) esp_read_mac(mac, ESP_MAC_WIFI_STA);
601+
err = esp_efuse_mac_get_default(mac);
602+
if (err != ESP_OK) {
603+
err = esp_read_mac(mac, ESP_MAC_WIFI_STA);
510604
}
511605
#endif
512606

607+
if (err != ESP_OK) {
608+
ESP_LOGW(TAG, "Failed to read MAC address, using random ID");
609+
snprintf((char*)mac, sizeof(mac), "%06lx", (unsigned long)esp_random());
610+
}
611+
513612
char buf[32];
514613
snprintf(buf, sizeof(buf), "esp32-%02x%02x%02x%02x%02x%02x",
515614
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
@@ -524,10 +623,8 @@ std::string RemoteWebView::build_ws_uri_() const {
524623
const std::string id = resolve_device_id_();
525624
append_q_str_(uri, "id", id.c_str());
526625

527-
const int W = display_ ? display_->get_width() : 0;
528-
const int H = display_ ? display_->get_height() : 0;
529-
append_q_int_(uri, "w", W);
530-
append_q_int_(uri, "h", H);
626+
append_q_int_(uri, "w", display_width_);
627+
append_q_int_(uri, "h", display_height_);
531628

532629
append_q_int_(uri, "r", rotation_);
533630
append_q_int_(uri, "ts", tile_size_);

components/remote_webview/remote_webview.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@
1212
#include "freertos/queue.h"
1313
#include "freertos/semphr.h"
1414

15+
#if defined(CONFIG_IDF_TARGET_ESP32P4)
16+
#include "driver/jpeg_decode.h"
17+
#define REMOTE_WEBVIEW_HW_JPEG 1
18+
#else
19+
#define REMOTE_WEBVIEW_HW_JPEG 0
20+
#endif
21+
#if defined(CONFIG_IDF_TARGET_ESP32P4)
22+
#include "esp_cache.h"
23+
#define REMOTE_WEBVIEW_HAS_CACHE_MSYNC 1
24+
#else
25+
#define REMOTE_WEBVIEW_HAS_CACHE_MSYNC 0
26+
#endif
27+
1528
namespace esphome {
1629
namespace remote_webview {
1730

@@ -58,6 +71,8 @@ class RemoteWebView : public Component {
5871
display::Display *display_{nullptr};
5972
touchscreen::Touchscreen *touch_ = nullptr;
6073
class RemoteWebViewTouchListener *touch_listener_ = nullptr;
74+
int display_width_{0};
75+
int display_height_{0};
6176
std::string url_;
6277
std::string server_host_;
6378
std::string device_id_;
@@ -73,6 +88,14 @@ class RemoteWebView : public Component {
7388
bool rgb565_big_endian_{true};
7489
int rotation_{0};
7590

91+
#if REMOTE_WEBVIEW_HW_JPEG
92+
jpeg_decoder_handle_t hw_dec_{nullptr};
93+
uint8_t *hw_decode_input_buf_{nullptr};
94+
uint8_t *hw_decode_output_buf_{nullptr};
95+
size_t hw_decode_input_size_{0};
96+
size_t hw_decode_output_size_{0};
97+
#endif
98+
7699
uint64_t last_move_us_{0};
77100
uint64_t last_keepalive_us_{0};
78101

@@ -103,6 +126,7 @@ class RemoteWebView : public Component {
103126
void process_frame_packet_(const uint8_t *data, size_t len);
104127
void process_frame_stats_packet_(const uint8_t *data, size_t len);
105128
bool decode_jpeg_tile_to_lcd_(int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len);
129+
bool decode_jpeg_tile_software_(int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len);
106130

107131
static int jpeg_draw_cb_s_(JPEGDRAW *p);
108132
int jpeg_draw_cb_(JPEGDRAW *p);

0 commit comments

Comments
 (0)