|
6 | 6 |
|
7 | 7 | #include "esp_jpeg_common.h" |
8 | 8 | #include "esp_jpeg_enc.h" |
| 9 | +#if CONFIG_XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER |
| 10 | +#include "driver/jpeg_encode.h" |
| 11 | +#endif |
9 | 12 | #include "image_to_jpeg.h" |
10 | 13 |
|
11 | 14 | #define TAG "image_to_jpeg" |
@@ -156,6 +159,161 @@ static uint8_t* convert_input_to_encoder_buf(const uint8_t* src, uint16_t width, |
156 | 159 | return rgb; |
157 | 160 | } |
158 | 161 |
|
| 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 | + |
159 | 317 | static bool encode_with_esp_new_jpeg(const uint8_t* src, size_t src_len, uint16_t width, uint16_t height, |
160 | 318 | v4l2_pix_fmt_t format, uint8_t quality, uint8_t** jpg_out, size_t* jpg_out_len, |
161 | 319 | 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_ |
235 | 393 |
|
236 | 394 | bool image_to_jpeg(uint8_t* src, size_t src_len, uint16_t width, uint16_t height, v4l2_pix_fmt_t format, |
237 | 395 | 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 |
238 | 402 | return encode_with_esp_new_jpeg(src, src_len, width, height, format, quality, out, out_len, NULL, NULL); |
239 | 403 | } |
240 | 404 |
|
241 | 405 | bool image_to_jpeg_cb(uint8_t* src, size_t src_len, uint16_t width, uint16_t height, v4l2_pix_fmt_t format, |
242 | 406 | 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 |
243 | 413 | return encode_with_esp_new_jpeg(src, src_len, width, height, format, quality, NULL, NULL, cb, arg); |
244 | 414 | } |
0 commit comments