From 7673d3ee22088e01fbe2ce5ea6ed70ef18cb913b Mon Sep 17 00:00:00 2001 From: RubenKelevra Date: Thu, 17 Jul 2025 19:46:57 +0200 Subject: [PATCH] docs(readme): harden jpg_stream_httpd_handler example & fix bugs Changes: * Use `%zu` in _STREAM_PART for size_t-safe Content-Length formatting. * Fix `part_buf` type: `char part_buf[64]` (was array of char pointers). * initialize local JPEG buffer vars: `jpg_buf_len`/`jpg_buf` * drop leading underscores on them for consistency with other locals * Bound `snprintf()` by `sizeof(part_buf)`; capture `int hlen`. * Detect and log header truncation with required/available sizes; fail gracefully. * Break out of loop on JPEG compression failure to avoid invalid buffer use. * Send header chunk only when formatting succeeds. * Guard FPS calculation against divide-by-zero; compute once as `fps`. * Remove unsafe/needless pointer cast. * Minor style cleanups for readability. --- README.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index def7aa1fff..774bf423a7 100644 --- a/README.md +++ b/README.md @@ -249,14 +249,14 @@ esp_err_t jpg_httpd_handler(httpd_req_t *req){ #define PART_BOUNDARY "123456789000000000000987654321" static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; -static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; +static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %zu\r\n\r\n"; esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; - size_t _jpg_buf_len; - uint8_t * _jpg_buf; - char * part_buf[64]; + size_t jpg_buf_len = 0; + uint8_t * jpg_buf = NULL; + char part_buf[64]; static int64_t last_frame = 0; if(!last_frame) { last_frame = esp_timer_get_time(); @@ -275,30 +275,36 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ break; } if(fb->format != PIXFORMAT_JPEG){ - bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); + bool jpeg_converted = frame2jpg(fb, 80, &jpg_buf, &jpg_buf_len); if(!jpeg_converted){ ESP_LOGE(TAG, "JPEG compression failed"); esp_camera_fb_return(fb); res = ESP_FAIL; + break; } } else { - _jpg_buf_len = fb->len; - _jpg_buf = fb->buf; + jpg_buf_len = fb->len; + jpg_buf = fb->buf; } if(res == ESP_OK){ res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); } if(res == ESP_OK){ - size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); - - res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); + int hlen = snprintf(part_buf, sizeof(part_buf), _STREAM_PART, jpg_buf_len); + if(hlen < 0 || hlen >= sizeof(part_buf)){ + ESP_LOGE(TAG, "Header truncated (%d bytes needed >= %zu buffer)", + hlen, sizeof(part_buf)); + res = ESP_FAIL; + } else { + res = httpd_resp_send_chunk(req, part_buf, (size_t)hlen); + } } if(res == ESP_OK){ - res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); + res = httpd_resp_send_chunk(req, (const char *)jpg_buf, jpg_buf_len); } if(fb->format != PIXFORMAT_JPEG){ - free(_jpg_buf); + free(jpg_buf); } esp_camera_fb_return(fb); if(res != ESP_OK){ @@ -308,9 +314,10 @@ esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){ int64_t frame_time = fr_end - last_frame; last_frame = fr_end; frame_time /= 1000; + float fps = frame_time > 0 ? 1000.0f / (float)frame_time : 0.0f; ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)", - (uint32_t)(_jpg_buf_len/1024), - (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time); + (uint32_t)(jpg_buf_len/1024), + (uint32_t)frame_time, fps); } last_frame = 0;