Skip to content

Commit 6c9f9d1

Browse files
committed
feat: use ESP32-P4 Hardware JPEG Encoder
1 parent 20fe606 commit 6c9f9d1

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

main/Kconfig.projbuild

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,14 @@ config RECEIVE_CUSTOM_MESSAGE
635635
menu "Camera Configuration"
636636
depends on !IDF_TARGET_ESP32
637637

638+
config XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
639+
bool "Enable Hardware JPEG Encoder"
640+
default y
641+
depends on SOC_JPEG_ENCODE_SUPPORTED
642+
help
643+
Use hardware JPEG encoder on ESP32-P4 to encode image to JPEG.
644+
See https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/api-reference/peripherals/jpeg.html for more details.
645+
638646
config XIAOZHI_ENABLE_CAMERA_DEBUG_MODE
639647
bool "Enable Camera Debug Mode"
640648
default n

main/display/lvgl_display/jpg/image_to_jpeg.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
#include "esp_jpeg_common.h"
88
#include "esp_jpeg_enc.h"
9+
#if CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
10+
#include "driver/jpeg_encode.h"
11+
#endif
912
#include "image_to_jpeg.h"
1013

1114
#define TAG "image_to_jpeg"
@@ -156,6 +159,161 @@ static uint8_t* convert_input_to_encoder_buf(const uint8_t* src, uint16_t width,
156159
return rgb;
157160
}
158161

162+
#if CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
163+
static jpeg_encoder_handle_t s_hw_jpeg_handle = NULL;
164+
165+
static bool hw_jpeg_ensure_inited(void) {
166+
if (s_hw_jpeg_handle) {
167+
return true;
168+
}
169+
jpeg_encode_engine_cfg_t eng_cfg = {
170+
.intr_priority = 0,
171+
.timeout_ms = 100,
172+
};
173+
esp_err_t er = jpeg_new_encoder_engine(&eng_cfg, &s_hw_jpeg_handle);
174+
if (er != ESP_OK) {
175+
ESP_LOGE(TAG, "jpeg_new_encoder_engine failed: %d", (int)er);
176+
s_hw_jpeg_handle = NULL;
177+
return false;
178+
}
179+
return true;
180+
}
181+
182+
static uint8_t* convert_input_to_hw_encoder_buf(const uint8_t* src, uint16_t width, uint16_t height, v4l2_pix_fmt_t format,
183+
jpeg_enc_input_format_t* out_fmt, int* out_size) {
184+
if (format == V4L2_PIX_FMT_GREY) {
185+
int sz = (int)width * (int)height;
186+
uint8_t* buf = (uint8_t*)malloc_psram(sz);
187+
if (!buf)
188+
return NULL;
189+
memcpy(buf, src, sz);
190+
if (out_fmt)
191+
*out_fmt = JPEG_ENCODE_IN_FORMAT_GRAY;
192+
if (out_size)
193+
*out_size = sz;
194+
return buf;
195+
}
196+
197+
if (format == V4L2_PIX_FMT_RGB24) {
198+
int sz = (int)width * (int)height * 3;
199+
uint8_t* buf = (uint8_t*)malloc_psram(sz);
200+
if (!buf) {
201+
ESP_LOGE(TAG, "malloc_psram failed");
202+
return NULL;
203+
}
204+
memcpy(buf, src, sz);
205+
if (out_fmt)
206+
*out_fmt = JPEG_ENCODE_IN_FORMAT_RGB888;
207+
if (out_size)
208+
*out_size = sz;
209+
return buf;
210+
}
211+
212+
if (format == V4L2_PIX_FMT_RGB565) {
213+
int sz = (int)width * (int)height * 2;
214+
uint8_t* buf = (uint8_t*)malloc_psram(sz);
215+
if (!buf)
216+
return NULL;
217+
memcpy(buf, src, sz);
218+
if (out_fmt)
219+
*out_fmt = JPEG_ENCODE_IN_FORMAT_RGB565;
220+
if (out_size)
221+
*out_size = sz;
222+
return buf;
223+
}
224+
225+
if (format == V4L2_PIX_FMT_YUYV) {
226+
// 硬件需要 | Y1 V Y0 U | 的“大端”格式,因此需要 bswap16
227+
int sz = (int)width * (int)height * 2;
228+
uint16_t* buf = (uint16_t*)malloc_psram(sz);
229+
if (!buf)
230+
return NULL;
231+
const uint16_t* bsrc = (const uint16_t*)src;
232+
for (int i = 0; i < sz / 2; i++) {
233+
buf[i] = __builtin_bswap16(bsrc[i]);
234+
}
235+
if (out_fmt)
236+
*out_fmt = JPEG_ENCODE_IN_FORMAT_YUV422;
237+
if (out_size)
238+
*out_size = sz;
239+
return (uint8_t*)buf;
240+
}
241+
242+
return NULL;
243+
}
244+
245+
static bool encode_with_hw_jpeg(const uint8_t* src, size_t src_len, uint16_t width, uint16_t height,
246+
v4l2_pix_fmt_t format, uint8_t quality, uint8_t** jpg_out, size_t* jpg_out_len,
247+
jpg_out_cb cb, void* cb_arg) {
248+
if (quality < 1)
249+
quality = 1;
250+
if (quality > 100)
251+
quality = 100;
252+
253+
jpeg_enc_input_format_t enc_src_type = JPEG_ENCODE_IN_FORMAT_RGB888;
254+
int enc_in_size = 0;
255+
uint8_t* enc_in = convert_input_to_hw_encoder_buf(src, width, height, format, &enc_src_type, &enc_in_size);
256+
if (!enc_in) {
257+
ESP_LOGW(TAG, "hw jpeg: unsupported format, fallback to sw");
258+
return false;
259+
}
260+
261+
if (!hw_jpeg_ensure_inited()) {
262+
free(enc_in);
263+
return false;
264+
}
265+
266+
jpeg_encode_cfg_t enc_cfg = {0};
267+
enc_cfg.width = width;
268+
enc_cfg.height = height;
269+
enc_cfg.src_type = enc_src_type;
270+
enc_cfg.image_quality = quality;
271+
enc_cfg.sub_sample = (enc_src_type == JPEG_ENCODE_IN_FORMAT_GRAY) ? JPEG_DOWN_SAMPLING_GRAY : JPEG_DOWN_SAMPLING_YUV422;
272+
273+
size_t out_cap = (size_t)width * (size_t)height * 3 / 2 + 64 * 1024;
274+
if (out_cap < 128 * 1024)
275+
out_cap = 128 * 1024;
276+
jpeg_encode_memory_alloc_cfg_t jpeg_enc_output_mem_cfg = { .buffer_direction = JPEG_ENC_ALLOC_OUTPUT_BUFFER };
277+
size_t out_cap_aligned = 0;
278+
uint8_t* outbuf = (uint8_t*)jpeg_alloc_encoder_mem(out_cap, &jpeg_enc_output_mem_cfg, &out_cap_aligned);
279+
if (!outbuf) {
280+
free(enc_in);
281+
ESP_LOGE(TAG, "alloc out buffer failed");
282+
return false;
283+
}
284+
285+
uint32_t out_len = 0;
286+
esp_err_t er = jpeg_encoder_process(s_hw_jpeg_handle, &enc_cfg, enc_in, (uint32_t)enc_in_size, outbuf, (uint32_t)out_cap_aligned, &out_len);
287+
free(enc_in);
288+
289+
if (er != ESP_OK) {
290+
free(outbuf);
291+
ESP_LOGE(TAG, "jpeg_encoder_process failed: %d", (int)er);
292+
return false;
293+
}
294+
295+
if (cb) {
296+
cb(cb_arg, 0, outbuf, (size_t)out_len);
297+
cb(cb_arg, 1, NULL, 0);
298+
free(outbuf);
299+
if (jpg_out)
300+
*jpg_out = NULL;
301+
if (jpg_out_len)
302+
*jpg_out_len = 0;
303+
return true;
304+
}
305+
306+
if (jpg_out && jpg_out_len) {
307+
*jpg_out = outbuf;
308+
*jpg_out_len = (size_t)out_len;
309+
return true;
310+
}
311+
312+
free(outbuf);
313+
return true;
314+
}
315+
#endif // CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
316+
159317
static bool encode_with_esp_new_jpeg(const uint8_t* src, size_t src_len, uint16_t width, uint16_t height,
160318
v4l2_pix_fmt_t format, uint8_t quality, uint8_t** jpg_out, size_t* jpg_out_len,
161319
jpg_out_cb cb, void* cb_arg) {
@@ -235,10 +393,22 @@ static bool encode_with_esp_new_jpeg(const uint8_t* src, size_t src_len, uint16_
235393

236394
bool image_to_jpeg(uint8_t* src, size_t src_len, uint16_t width, uint16_t height, v4l2_pix_fmt_t format,
237395
uint8_t quality, uint8_t** out, size_t* out_len) {
396+
#if CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
397+
if (encode_with_hw_jpeg(src, src_len, width, height, format, quality, out, out_len, NULL, NULL)) {
398+
return true;
399+
}
400+
// Fallback to esp_new_jpeg
401+
#endif
238402
return encode_with_esp_new_jpeg(src, src_len, width, height, format, quality, out, out_len, NULL, NULL);
239403
}
240404

241405
bool image_to_jpeg_cb(uint8_t* src, size_t src_len, uint16_t width, uint16_t height, v4l2_pix_fmt_t format,
242406
uint8_t quality, jpg_out_cb cb, void* arg) {
407+
#if CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
408+
if (encode_with_hw_jpeg(src, src_len, width, height, format, quality, NULL, NULL, cb, arg)) {
409+
return true;
410+
}
411+
// Fallback to esp_new_jpeg
412+
#endif
243413
return encode_with_esp_new_jpeg(src, src_len, width, height, format, quality, NULL, NULL, cb, arg);
244414
}

0 commit comments

Comments
 (0)