Skip to content

Commit cde3497

Browse files
committed
feat: Fix issue with streaming as httpd_resp_send_chunk breaks streaming.
1 parent 4b6cd9a commit cde3497

File tree

3 files changed

+70
-16
lines changed

3 files changed

+70
-16
lines changed

app_httpd.cpp

+65-15
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,33 @@ static esp_err_t capture_handler(httpd_req_t *req){
216216
return res;
217217
}
218218

219+
static int sendData(httpd_req_t *req, const char *buf, const size_t len) {
220+
size_t index = 0;
221+
int res = 0;
222+
int attempts = 0;
223+
const int maxAttempts = 100;
224+
do {
225+
res = httpd_send(req, &buf[index], len - index);
226+
if ((res >= 0 && res < (len - index)) || res == HTTPD_SOCK_ERR_TIMEOUT) {
227+
delay(10);
228+
attempts++;
229+
if (res == HTTPD_SOCK_ERR_TIMEOUT && attempts < maxAttempts) {
230+
res = 0;
231+
}
232+
}
233+
if (res >= 0) {
234+
index += res;
235+
}
236+
}
237+
while(res >= 0 && index < len && attempts < maxAttempts);
238+
if (index < len && attempts >= maxAttempts) {
239+
res = -1;
240+
}
241+
return res;
242+
}
243+
219244
static esp_err_t stream_handler(httpd_req_t *req){
220245
camera_fb_t * fb = NULL;
221-
esp_err_t res = ESP_OK;
222246
size_t _jpg_buf_len = 0;
223247
uint8_t * _jpg_buf = NULL;
224248
char * part_buf[64];
@@ -235,39 +259,65 @@ static esp_err_t stream_handler(httpd_req_t *req){
235259
last_frame = esp_timer_get_time();
236260
}
237261

238-
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
239-
if(res != ESP_OK){
262+
const char *httpd_chunked_hdr_str = "HTTP/1.1 %s%sContent-Type: %s%s";
263+
const char *colon_separator = ": ";
264+
const char *cr_lf_seperator = "\r\n";
265+
static char buffer[256 + 1];
266+
const int headerSize = snprintf(buffer, sizeof(buffer) - 1, httpd_chunked_hdr_str, "200 OK", cr_lf_seperator, _STREAM_CONTENT_TYPE, cr_lf_seperator);
267+
int res = sendData(req, buffer, headerSize);
268+
if (res != headerSize) {
269+
// TODO: Make a lambda so as we can re-use...
270+
streamCount = 0;
271+
if (autoLamp && (lampVal != -1)) setLamp(0);
272+
Serial.println("STREAM: failed to set HTTP response type");
273+
return res;
274+
}
275+
const int originSize = snprintf(buffer, sizeof(buffer) - 1, "Access-Control-Allow-Origin: *%s", cr_lf_seperator);
276+
res = sendData(req, buffer, originSize);
277+
if (res != originSize) {
278+
// TODO: Make a lambda so as we can re-use...
240279
streamCount = 0;
241280
if (autoLamp && (lampVal != -1)) setLamp(0);
242281
Serial.println("STREAM: failed to set HTTP response type");
243282
return res;
244283
}
245-
246-
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
247284

248285
while(true){
249286
fb = esp_camera_fb_get();
250287
if (!fb) {
251288
Serial.println("STREAM: failed to acquire frame");
252-
res = ESP_FAIL;
289+
res = -1;
253290
} else {
254291
if(fb->format != PIXFORMAT_JPEG){
255292
Serial.println("STREAM: Non-JPEG frame returned by camera module");
256-
res = ESP_FAIL;
293+
res = -2;
257294
} else {
258295
_jpg_buf_len = fb->len;
259296
_jpg_buf = fb->buf;
260297
}
261298
}
262-
if(res == ESP_OK){
263-
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
299+
if(res > 0){
300+
res = sendData(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
301+
if (res != strlen(_STREAM_BOUNDARY)) {
302+
res = -3;
303+
}
264304
}
265-
if(res == ESP_OK){
305+
if(res > 0){
266306
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
267-
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
307+
res = sendData(req, (const char *)part_buf, hlen);
308+
if (res != hlen) {
309+
res = -4;
310+
}
268311
}
269-
if(res == ESP_OK){
270-
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
312+
if(res > 0){
313+
yield();
314+
res = sendData(req, (const char *)_jpg_buf, _jpg_buf_len);
315+
if (res != _jpg_buf_len) {
316+
Serial.printf("Failed sending chunk of [%d] error: [%d]\n", _jpg_buf_len, res);
317+
Serial.print(_STREAM_BOUNDARY);
318+
Serial.printf((char *)part_buf);
319+
res = -5;
320+
}
271321
}
272322
if(fb){
273323
esp_camera_fb_return(fb);
@@ -277,7 +327,7 @@ static esp_err_t stream_handler(httpd_req_t *req){
277327
free(_jpg_buf);
278328
_jpg_buf = NULL;
279329
}
280-
if(res != ESP_OK){
330+
if(res < 0){
281331
// This is the only exit point from the stream loop.
282332
// We end the stream here only if a Hard failure has been encountered or the connection has been interrupted.
283333
break;
@@ -297,7 +347,7 @@ static esp_err_t stream_handler(httpd_req_t *req){
297347
if (autoLamp && (lampVal != -1)) setLamp(0);
298348
Serial.println("Stream ended");
299349
last_frame = 0;
300-
return res;
350+
return res < 0 ? ESP_FAIL : ESP_OK;
301351
}
302352

303353
static esp_err_t cmd_handler(httpd_req_t *req){

esp32-cam-webserver.ino

+1-1
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ void setup() {
736736
debugOff();
737737
#endif
738738
} else {
739-
Serial.printf("\r\nCamera unavailable due to initialisation errors.\r\n\r\n");
739+
Serial.printf("\r\nCamera unavailable due to initialisation errors [%s].\r\n\r\n", critERR.c_str());
740740
}
741741

742742
// Used when dumping status; these are slow functions, so just do them once during startup

platformio.ini

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ platform_packages = framework-arduinoespressif32@https://github.com/espressif/ar
1717
board = esp32dev
1818
board_build.partitions = min_spiffs.csv
1919
framework = arduino
20+
upload_port = /dev/ttyUSB0
21+
upload_speed = 460800
22+
monitor_port = /dev/ttyUSB0
23+
monitor_speed = 115200
2024
build_flags =
2125
-DBOARD_HAS_PSRAM
2226
-mfix-esp32-psram-cache-issue

0 commit comments

Comments
 (0)