Skip to content

Commit 8e37e22

Browse files
authored
Merge pull request #237 from daschr/master
Add multiclient streaming support
2 parents 7d49901 + ba3c09f commit 8e37e22

File tree

4 files changed

+263
-108
lines changed

4 files changed

+263
-108
lines changed

app_httpd.cpp

+27-106
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#include "src/logo.h"
2929
#include "storage.h"
3030

31+
extern "C"{
32+
#include "cam_streamer.h"
33+
}
3134
// Functions from the main .ino
3235
extern void flashLED(int flashtime);
3336
extern void setLamp(int newVal);
@@ -48,7 +51,6 @@ extern int streamPort;
4851
extern char httpURL[];
4952
extern char streamURL[];
5053
extern char default_index[];
51-
extern int8_t streamCount;
5254
extern unsigned long streamsServed;
5355
extern unsigned long imagesServed;
5456
extern int myRotation;
@@ -67,6 +69,8 @@ extern char otaPassword[];
6769
extern unsigned long xclk;
6870
extern int sensorPID;
6971

72+
cam_streamer_t *cam_streamer;
73+
7074
typedef struct {
7175
httpd_req_t *req;
7276
size_t len;
@@ -80,9 +84,6 @@ static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %
8084
httpd_handle_t stream_httpd = NULL;
8185
httpd_handle_t camera_httpd = NULL;
8286

83-
// Flag that can be set to kill all active streams
84-
bool streamKill;
85-
8687
#ifdef __cplusplus
8788
extern "C" {
8889
#endif
@@ -147,7 +148,7 @@ void serialDump() {
147148
int McuTc = (temprature_sens_read() - 32) / 1.8; // celsius
148149
int McuTf = temprature_sens_read(); // fahrenheit
149150
Serial.printf("System up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)\r\n", upDays, upHours, upMin, upSec);
150-
Serial.printf("Active streams: %i, Previous streams: %lu, Images captured: %lu\r\n", streamCount, streamsServed, imagesServed);
151+
Serial.printf("Active streams: %lu, Previous streams: %lu, Images captured: %lu\r\n", cam_streamer_get_num_clients(cam_streamer), streamsServed, imagesServed);
151152
Serial.printf("CPU Freq: %i MHz, Xclk Freq: %i MHz\r\n", ESP.getCpuFreqMHz(), xclk);
152153
Serial.printf("MCU temperature : %i C, %i F (approximate)\r\n", McuTc, McuTf);
153154
Serial.printf("Heap: %i, free: %i, min free: %i, max block: %i\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
@@ -222,102 +223,16 @@ static esp_err_t capture_handler(httpd_req_t *req){
222223
}
223224

224225
static esp_err_t stream_handler(httpd_req_t *req){
225-
camera_fb_t * fb = NULL;
226-
esp_err_t res = ESP_OK;
227-
size_t _jpg_buf_len = 0;
228-
uint8_t * _jpg_buf = NULL;
229-
char * part_buf[64];
230-
231-
streamKill = false;
232-
233-
Serial.println("Stream requested");
234-
if (autoLamp && (lampVal != -1)) setLamp(lampVal);
235-
streamCount = 1; // at present we only have one stream handler, so values are 0 or 1..
236-
flashLED(75); // double flash of status LED
237-
delay(75);
238-
flashLED(75);
239-
240-
static int64_t last_frame = 0;
241-
if(!last_frame) {
242-
last_frame = esp_timer_get_time();
243-
}
244-
245-
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
246-
if(res != ESP_OK){
247-
streamCount = 0;
248-
if (autoLamp && (lampVal != -1)) setLamp(0);
249-
Serial.println("STREAM: failed to set HTTP response type");
250-
return res;
251-
}
252-
253-
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
254-
255-
if(res == ESP_OK){
256-
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
257-
}
258-
259-
while(true){
260-
fb = esp_camera_fb_get();
261-
if (!fb) {
262-
Serial.println("STREAM: failed to acquire frame");
263-
res = ESP_FAIL;
264-
} else {
265-
if(fb->format != PIXFORMAT_JPEG){
266-
Serial.println("STREAM: Non-JPEG frame returned by camera module");
267-
res = ESP_FAIL;
268-
} else {
269-
_jpg_buf_len = fb->len;
270-
_jpg_buf = fb->buf;
271-
}
272-
}
273-
if(res == ESP_OK){
274-
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
275-
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
276-
}
277-
if(res == ESP_OK){
278-
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
279-
}
280-
if(res == ESP_OK){
281-
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
282-
}
283-
if(fb){
284-
esp_camera_fb_return(fb);
285-
fb = NULL;
286-
_jpg_buf = NULL;
287-
} else if(_jpg_buf){
288-
free(_jpg_buf);
289-
_jpg_buf = NULL;
290-
}
291-
if(res != ESP_OK){
292-
// This is the error exit point from the stream loop.
293-
// We end the stream here only if a Hard failure has been encountered or the connection has been interrupted.
294-
Serial.printf("Stream failed, code = %i : %s\r\n", res, esp_err_to_name(res));
295-
break;
296-
}
297-
if((res != ESP_OK) || streamKill){
298-
// We end the stream here when a kill is signalled.
299-
Serial.printf("Stream killed\r\n");
300-
break;
301-
}
302-
int64_t frame_time = esp_timer_get_time() - last_frame;
303-
frame_time /= 1000;
304-
int32_t frame_delay = (minFrameTime > frame_time) ? minFrameTime - frame_time : 0;
305-
delay(frame_delay);
306-
307-
if (debugData) {
308-
Serial.printf("MJPG: %uB %ums, delay: %ums, framerate (%.1ffps)\r\n",
309-
(uint32_t)(_jpg_buf_len),
310-
(uint32_t)frame_time, frame_delay, 1000.0 / (uint32_t)(frame_time + frame_delay));
311-
}
312-
last_frame = esp_timer_get_time();
313-
}
314-
315-
streamsServed++;
316-
streamCount = 0;
317-
if (autoLamp && (lampVal != -1)) setLamp(0);
318-
Serial.println("Stream ended");
319-
last_frame = 0;
320-
return res;
226+
int fd=httpd_req_to_sockfd(req);
227+
if(fd==-1){
228+
printf("[stream_handler] could not get socket fd!\n");
229+
return ESP_FAIL;
230+
}
231+
232+
if(cam_streamer_enqueue_client(cam_streamer, fd))
233+
++streamsServed;
234+
235+
return ESP_OK;
321236
}
322237

323238
static esp_err_t cmd_handler(httpd_req_t *req){
@@ -391,7 +306,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){
391306
else if(!strcmp(variable, "autolamp") && (lampVal != -1)) {
392307
autoLamp = val;
393308
if (autoLamp) {
394-
if (streamCount > 0) setLamp(lampVal);
309+
if (cam_streamer_get_num_clients(cam_streamer) > 0) setLamp(lampVal);
395310
else setLamp(0);
396311
} else {
397312
setLamp(lampVal);
@@ -400,7 +315,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){
400315
else if(!strcmp(variable, "lamp") && (lampVal != -1)) {
401316
lampVal = constrain(val,0,100);
402317
if (autoLamp) {
403-
if (streamCount > 0) setLamp(lampVal);
318+
if (cam_streamer_get_num_clients(cam_streamer) > 0) setLamp(lampVal);
404319
else setLamp(0);
405320
} else {
406321
setLamp(lampVal);
@@ -597,7 +512,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
597512
int McuTf = temprature_sens_read(); // fahrenheit
598513

599514
d+= sprintf(d,"Up: %" PRId64 ":%02i:%02i:%02i (d:h:m:s)<br>\n", upDays, upHours, upMin, upSec);
600-
d+= sprintf(d,"Active streams: %i, Previous streams: %lu, Images captured: %lu<br>\n", streamCount, streamsServed, imagesServed);
515+
d+= sprintf(d,"Active streams: %i, Previous streams: %lu, Images captured: %lu<br>\n", cam_streamer_get_num_clients(cam_streamer), streamsServed, imagesServed);
601516
d+= sprintf(d,"CPU Freq: %i MHz, Xclk Freq: %i MHz<br>\n", ESP.getCpuFreqMHz(), xclk);
602517
d+= sprintf(d,"<span title=\"NOTE: Internal temperature sensor readings can be innacurate on the ESP32-c1 chipset, and may vary significantly between devices!\">");
603518
d+= sprintf(d,"MCU temperature : %i &deg;C, %i &deg;F</span>\n<br>", McuTc, McuTf);
@@ -634,7 +549,7 @@ static esp_err_t dump_handler(httpd_req_t *req){
634549
static esp_err_t stop_handler(httpd_req_t *req){
635550
flashLED(75);
636551
Serial.println("\r\nStream stop requested via Web");
637-
streamKill = true;
552+
cam_streamer_dequeue_all_clients(cam_streamer);
638553
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
639554
return httpd_resp_send(req, NULL, 0);
640555
}
@@ -877,7 +792,13 @@ void startCameraServer(int hPort, int sPort){
877792
httpd_register_uri_handler(stream_httpd, &stream_uri);
878793
httpd_register_uri_handler(stream_httpd, &info_uri);
879794
httpd_register_uri_handler(stream_httpd, &streamviewer_uri);
880-
}
795+
cam_streamer=(cam_streamer_t *) malloc(sizeof(cam_streamer_t));
796+
#ifndef CAM_STREAMER_DESIRED_FPS
797+
#define CAM_STREAMER_DESIRED_FPS 2
798+
#endif
799+
cam_streamer_init(cam_streamer, stream_httpd, CAM_STREAMER_DESIRED_FPS);
800+
cam_streamer_start(cam_streamer);
801+
}
881802
httpd_register_uri_handler(stream_httpd, &favicon_16x16_uri);
882803
httpd_register_uri_handler(stream_httpd, &favicon_32x32_uri);
883804
httpd_register_uri_handler(stream_httpd, &favicon_ico_uri);

0 commit comments

Comments
 (0)