diff --git a/app_httpd.cpp b/app_httpd.cpp index 6265f1a..2eb4b53 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -28,9 +28,18 @@ #include "src/logo.h" #include "storage.h" -extern "C"{ +#if __has_include("myconfig.h") + struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; + #include "myconfig.h" +#endif + +#ifndef MIN_FRAME_TIME + #warning "MIN_FRAME_TIME undefined, using default value of 500" + #define MIN_FRAME_TIME 500 +#endif + #include "cam_streamer.h" -} + // Functions from the main .ino extern void flashLED(int flashtime); extern void setLamp(int newVal); @@ -302,7 +311,7 @@ static esp_err_t cmd_handler(httpd_req_t *req){ else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); else if(!strcmp(variable, "rotate")) myRotation = val; - else if(!strcmp(variable, "min_frame_time")) minFrameTime = val; + else if(!strcmp(variable, "min_frame_time")) cam_streamer_set_frame_delay(cam_streamer, val); else if(!strcmp(variable, "autolamp") && (lampVal != -1)) { autoLamp = val; if (autoLamp) { @@ -793,10 +802,7 @@ void startCameraServer(int hPort, int sPort){ httpd_register_uri_handler(stream_httpd, &info_uri); httpd_register_uri_handler(stream_httpd, &streamviewer_uri); cam_streamer=(cam_streamer_t *) malloc(sizeof(cam_streamer_t)); -#ifndef CAM_STREAMER_DESIRED_FPS -#define CAM_STREAMER_DESIRED_FPS 2 -#endif - cam_streamer_init(cam_streamer, stream_httpd, CAM_STREAMER_DESIRED_FPS); + cam_streamer_init(cam_streamer, stream_httpd, MIN_FRAME_TIME); cam_streamer_start(cam_streamer); } httpd_register_uri_handler(stream_httpd, &favicon_16x16_uri); diff --git a/cam_streamer.c b/cam_streamer.cpp similarity index 60% rename from cam_streamer.c rename to cam_streamer.cpp index 7dc8fe0..bce8b57 100644 --- a/cam_streamer.c +++ b/cam_streamer.cpp @@ -7,9 +7,24 @@ #include #include #include +#include #include "cam_streamer.h" +#if __has_include("myconfig.h") +struct station { + const char ssid[65]; + const char password[65]; + const bool dhcp; +}; +#include "myconfig.h" +#endif + +#ifndef CAM_STREAMER_MAX_CLIENTS +#warning "CAM_STREAMER_MAX_CLIENTS undefined, using default value of 10" +#define CAM_STREAMER_MAX_CLIENTS 10 +#endif + #define PART_BOUNDARY "123456789000000000000987654321" #define _STREAM_HEADERS "HTTP/1.1 200 OK\r\n"\ @@ -18,46 +33,60 @@ "Keep-Alive: timeout=15\r\n"\ "Content-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY "\r\n" +#define _TEXT_HEADERS "HTTP/1.1 200 OK\r\n"\ + "Access-Control-Allow-Origin: *\r\n"\ + "Connection: Close\r\n"\ + "Content-Type: text/plain\r\n\r\n" + +extern bool debugData; +extern int lampVal; +extern bool autoLamp; +extern void setLamp(int); + 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 inline void print_debug(const char *fmt, ...) { + if(debugData) { + va_list l; + va_start(l, fmt); + vprintf(fmt, l); + va_end(l); + } +} + static uint8_t is_send_error(int r) { switch(r) { case HTTPD_SOCK_ERR_INVALID: -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] invalid argument occured!\n"); -#endif + print_debug("[cam_streamer] invalid argument occured!\n"); return 1; case HTTPD_SOCK_ERR_TIMEOUT: -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] timeout/interrupt occured!\n"); -#endif + print_debug("[cam_streamer] timeout/interrupt occured!\n"); return 1; case HTTPD_SOCK_ERR_FAIL: -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] unrecoverable error while send()!\n"); -#endif + print_debug("[cam_streamer] unrecoverable error while send()!\n"); return 1; case ESP_ERR_INVALID_ARG: -#ifdef DEBUG_DEFAULT_ON - printf("[text-streamer] session closed!\n"); -#endif + print_debug("[text-streamer] session closed!\n"); return 1; default: -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] sent %d bytes!\n", r); -#endif + print_debug("[cam_streamer] sent %d bytes!\n", r); return 0; } } -void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t fps) { +void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t frame_delay) { memset(s, 0, sizeof(cam_streamer_t)); - s->frame_delay=1000000/fps; + s->frame_delay=1000*frame_delay; s->clients=xQueueCreate(CAM_STREAMER_MAX_CLIENTS*2, sizeof(int)); s->server=server; } +// frame_delay must be in ms (not us) +void cam_streamer_set_frame_delay(cam_streamer_t *s, uint16_t frame_delay) { + s->frame_delay=1000*frame_delay; +} + static void cam_streamer_update_frame(cam_streamer_t *s) { uint8_t l=0; while(!__atomic_compare_exchange_n(&s->buf_lock, &l, 1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { @@ -73,17 +102,13 @@ static void cam_streamer_update_frame(cam_streamer_t *s) { s->last_updated=esp_timer_get_time(); s->part_len=snprintf(s->part_buf, 64, _STREAM_PART, s->buf->len); __atomic_store_n(&s->buf_lock, 0, __ATOMIC_RELAXED); -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] fetched new frame\n"); -#endif + print_debug("[cam_streamer] fetched new frame\n"); } static void cam_streamer_decrement_num_clients(cam_streamer_t *s) { size_t num_clients=s->num_clients; while(num_clients>0 && !__atomic_compare_exchange_n(&s->num_clients, &num_clients, num_clients-1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] num_clients decremented\n"); -#endif + print_debug("[cam_streamer] num_clients decremented\n"); } void cam_streamer_task(void *p) { @@ -93,35 +118,35 @@ void cam_streamer_task(void *p) { int fd; unsigned int n_entries; for(;;) { - while(!(n_entries=uxQueueMessagesWaiting(s->clients))) + while(!(n_entries=uxQueueMessagesWaiting(s->clients))) { + if(autoLamp && lampVal!=-1) setLamp(0); vTaskSuspend(NULL); + if(autoLamp && lampVal!=-1) setLamp(lampVal); + } current_time=esp_timer_get_time(); if((current_time-last_time)frame_delay) vTaskDelay((s->frame_delay-(current_time-last_time))/(1000*portTICK_PERIOD_MS)); - last_time=current_time; cam_streamer_update_frame(s); + print_debug("[cam_streamer] frame_size: %luB %lums\n", s->buf->len, (current_time-last_time)/1000); + last_time=current_time; + for(unsigned int i=0; iclients, &fd, 10/portTICK_PERIOD_MS)==pdFALSE) { -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] failed to dequeue fd!\n"); -#endif + print_debug("[cam_streamer] failed to dequeue fd!\n"); continue; } -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] dequeued fd %d\n", fd); - printf("[cam_streamer] sending part: \"%.*s\"\n", (int) s->part_len, s->part_buf); -#endif + print_debug("[cam_streamer] fd %d dequeued\n", fd); if(is_send_error(httpd_socket_send(s->server, fd, s->part_buf, s->part_len, 0))) { cam_streamer_decrement_num_clients(s); continue; } - if(is_send_error(httpd_socket_send(s->server, fd, s->buf->buf, s->buf->len, 0))) { + if(is_send_error(httpd_socket_send(s->server, fd, (const char *) s->buf->buf, s->buf->len, 0))) { cam_streamer_decrement_num_clients(s); continue; } @@ -132,9 +157,7 @@ void cam_streamer_task(void *p) { } xQueueSend(s->clients, (void *) &fd, 10/portTICK_PERIOD_MS); -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] fd %d requeued\n", fd); -#endif + print_debug("[cam_streamer] fd %d requeued\n", fd); } } } @@ -142,10 +165,8 @@ void cam_streamer_task(void *p) { void cam_streamer_start(cam_streamer_t *s) { BaseType_t r=xTaskCreate(cam_streamer_task, "cam_streamer", 10*1024, (void *) s, tskIDLE_PRIORITY+3, &s->task); -#ifdef DEBUG_DEFAULT_ON if(r!=pdPASS) - printf("[cam_streamer] failed to create task!\n"); -#endif + print_debug("[cam_streamer] failed to create task!\n"); } void cam_streamer_stop(cam_streamer_t *s) { @@ -162,35 +183,41 @@ void cam_streamer_dequeue_all_clients(cam_streamer_t *s) { } bool cam_streamer_enqueue_client(cam_streamer_t *s, int fd) { -#ifdef DEBUG_DEFAULT_ON - printf("sending stream headers:\n%s\nLength: %d\n", _STREAM_HEADERS, strlen(_STREAM_HEADERS)); -#endif + if(s->num_clients>=CAM_STREAMER_MAX_CLIENTS) { + if(httpd_socket_send(s->server, fd, _TEXT_HEADERS, strlen(_TEXT_HEADERS), 0)) { + print_debug("failed sending text headers!\n"); + return false; + } + +#define EMSG "too many clients" + if(httpd_socket_send(s->server, fd, EMSG, strlen(EMSG), 0)) { + print_debug("failed sending message\n"); + return false; + } +#undef EMSG + close(fd); + return false; + } + + print_debug("sending stream headers:\n%s\nLength: %d\n", _STREAM_HEADERS, strlen(_STREAM_HEADERS)); if(is_send_error(httpd_socket_send(s->server, fd, _STREAM_HEADERS, strlen(_STREAM_HEADERS), 0))) { -#ifdef DEBUG_DEFAULT_ON - printf("failed sending headers!\n"); -#endif + print_debug("failed sending headers!\n"); return false; } if(is_send_error(httpd_socket_send(s->server, fd, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY), 0))) { -#ifdef DEBUG_DEFAULT_ON - printf("failed sending boundary!\n"); -#endif + print_debug("failed sending boundary!\n"); return false; } const BaseType_t r=xQueueSend(s->clients, (void *) &fd, 10*portTICK_PERIOD_MS); if(r!=pdTRUE) { -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] failed to enqueue fd %d\n", fd); -#endif + print_debug("[cam_streamer] failed to enqueue fd %d\n", fd); #define EMSG "failed to enqueue" httpd_socket_send(s->server, fd, EMSG, strlen(EMSG), 0); #undef EMSG } else { -#ifdef DEBUG_DEFAULT_ON - printf("[cam_streamer] socket %d enqueued\n", fd); -#endif + print_debug("[cam_streamer] socket %d enqueued\n", fd); __atomic_fetch_add(&s->num_clients, 1, __ATOMIC_RELAXED); vTaskResume(s->task); } diff --git a/cam_streamer.h b/cam_streamer.h index a128926..2a1500c 100644 --- a/cam_streamer.h +++ b/cam_streamer.h @@ -11,7 +11,6 @@ #include #include -#define CAM_STREAMER_MAX_CLIENTS 10 typedef struct { QueueHandle_t clients; TaskHandle_t task; @@ -25,7 +24,8 @@ typedef struct { size_t num_clients; } cam_streamer_t; -void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t fps); +void cam_streamer_init(cam_streamer_t *s, httpd_handle_t server, uint16_t frame_delay); +void cam_streamer_set_frame_delay(cam_streamer_t *s, uint16_t frame_delay); void cam_streamer_task(void *p); void cam_streamer_start(cam_streamer_t *s); void cam_streamer_stop(cam_streamer_t *s); diff --git a/esp32-cam-webserver.ino b/esp32-cam-webserver.ino index a39c013..a05f1c1 100644 --- a/esp32-cam-webserver.ino +++ b/esp32-cam-webserver.ino @@ -37,13 +37,12 @@ // Primary config, or defaults. #if __has_include("myconfig.h") - struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; // do no edit + struct station { const char ssid[65]; const char password[65]; const bool dhcp;}; // do no edit #include "myconfig.h" #else #warning "Using Defaults: Copy myconfig.sample.h to myconfig.h and edit that to use your own settings" #define WIFI_AP_ENABLE #define CAMERA_MODEL_AI_THINKER - struct station { const char ssid[65]; const char password[65]; const bool dhcp;} stationList[] = {{"ESP32-CAM-CONNECT","InsecurePassword", true}}; #endif diff --git a/myconfig.sample.h b/myconfig.sample.h index 86efcaf..3ea701b 100644 --- a/myconfig.sample.h +++ b/myconfig.sample.h @@ -22,7 +22,7 @@ * just replace your ssid and password in the line below. */ -struct station stationList[] = {{"my_ssid","my_password", true}}; +const struct station stationList[] = {{"my_ssid","my_password", true}}; /* * You can extend the stationList[] above with additional SSID+Password pairs @@ -148,6 +148,9 @@ struct station stationList[] = {{"ssid1", "pass1", true}, // max_fps = 1000/min_frame_time // #define MIN_FRAME_TIME 500 +// Maximum number of clients of the stream +// #define CAM_STREAMER_MAX_CLIENTS 10 + /* * Additional Features *