From 8aa837172bd511398143ca18d9e8a70807703880 Mon Sep 17 00:00:00 2001 From: Rune Harlyk Date: Sun, 14 May 2023 20:36:40 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=BA=20Moves=20server=20handlers=20to?= =?UTF-8?q?=20lib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/AsyncJpegStreamHandler.h | 140 ------------------ include/CaptivePortalHandler.h | 22 --- .../src/AsyncJpegStreamHandler.cpp | 129 ++++++++++++++++ .../src/AsyncJpegStreamHandler.h | 28 ++++ lib/ServerHandlers/src/WebsocketHandler.h | 5 + .../ServerHandlers/src/Websockethandler.cpp | 19 +-- src/main.cpp | 2 - 7 files changed, 170 insertions(+), 175 deletions(-) delete mode 100644 include/AsyncJpegStreamHandler.h delete mode 100644 include/CaptivePortalHandler.h create mode 100644 lib/ServerHandlers/src/AsyncJpegStreamHandler.cpp create mode 100644 lib/ServerHandlers/src/AsyncJpegStreamHandler.h create mode 100644 lib/ServerHandlers/src/WebsocketHandler.h rename include/WebsocketHandler.h => lib/ServerHandlers/src/Websockethandler.cpp (85%) diff --git a/include/AsyncJpegStreamHandler.h b/include/AsyncJpegStreamHandler.h deleted file mode 100644 index ae47942..0000000 --- a/include/AsyncJpegStreamHandler.h +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include - -typedef struct { - camera_fb_t * fb; - size_t index; -} camera_frame_t; - -#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: %s\r\nContent-Length: %u\r\n\r\n"; - -static const char * JPG_CONTENT_TYPE = "image/jpeg"; - -class AsyncJpegStreamResponse: public AsyncAbstractResponse { - private: - camera_frame_t _frame; - size_t _index; - size_t _jpg_buf_len; - uint8_t * _jpg_buf; - uint64_t lastAsyncRequest; - public: - AsyncJpegStreamResponse(){ - _callback = nullptr; - _code = 200; - _contentLength = 0; - _contentType = STREAM_CONTENT_TYPE; - _sendContentLength = false; - _chunked = true; - _index = 0; - _jpg_buf_len = 0; - _jpg_buf = NULL; - lastAsyncRequest = 0; - memset(&_frame, 0, sizeof(camera_frame_t)); - } - ~AsyncJpegStreamResponse(){ - if(_frame.fb){ - if(_frame.fb->format != PIXFORMAT_JPEG){ - free(_jpg_buf); - } - esp_camera_fb_return(_frame.fb); - } - } - bool _sourceValid() const { - return true; - } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override { - size_t ret = _content(buf, maxLen, _index); - if(ret != RESPONSE_TRY_AGAIN){ - _index += ret; - } - return ret; - } - size_t _content(uint8_t *buffer, size_t maxLen, size_t index){ - if(!_frame.fb || _frame.index == _jpg_buf_len){ - if(index && _frame.fb){ - uint64_t end = (uint64_t)micros(); - int fp = (end - lastAsyncRequest) / 1000; - log_printf("Size: %uKB, Time: %ums (%.1ffps)\n", _jpg_buf_len/1024, fp); - lastAsyncRequest = end; - if(_frame.fb->format != PIXFORMAT_JPEG){ - free(_jpg_buf); - } - esp_camera_fb_return(_frame.fb); - _frame.fb = NULL; - _jpg_buf_len = 0; - _jpg_buf = NULL; - } - if(maxLen < (strlen(STREAM_BOUNDARY) + strlen(STREAM_PART) + strlen(JPG_CONTENT_TYPE) + 8)){ - //log_w("Not enough space for headers"); - return RESPONSE_TRY_AGAIN; - } - //get frame - _frame.index = 0; - - _frame.fb = esp_camera_fb_get(); - if (_frame.fb == NULL) { - log_e("Camera frame failed"); - return 0; - } - - if(_frame.fb->format != PIXFORMAT_JPEG){ - unsigned long st = millis(); - bool jpeg_converted = frame2jpg(_frame.fb, 80, &_jpg_buf, &_jpg_buf_len); - if(!jpeg_converted){ - log_e("JPEG compression failed"); - esp_camera_fb_return(_frame.fb); - _frame.fb = NULL; - _jpg_buf_len = 0; - _jpg_buf = NULL; - return 0; - } - log_i("JPEG: %lums, %uB", millis() - st, _jpg_buf_len); - } else { - _jpg_buf_len = _frame.fb->len; - _jpg_buf = _frame.fb->buf; - } - - //send boundary - size_t blen = 0; - if(index){ - blen = strlen(STREAM_BOUNDARY); - memcpy(buffer, STREAM_BOUNDARY, blen); - buffer += blen; - } - //send header - size_t hlen = sprintf((char *)buffer, STREAM_PART, JPG_CONTENT_TYPE, _jpg_buf_len); - buffer += hlen; - //send frame - hlen = maxLen - hlen - blen; - if(hlen > _jpg_buf_len){ - maxLen -= hlen - _jpg_buf_len; - hlen = _jpg_buf_len; - } - memcpy(buffer, _jpg_buf, hlen); - _frame.index += hlen; - return maxLen; - } - - size_t available = _jpg_buf_len - _frame.index; - if(maxLen > available){ - maxLen = available; - } - memcpy(buffer, _jpg_buf+_frame.index, maxLen); - _frame.index += maxLen; - - return maxLen; - } -}; - -void streamJpg(AsyncWebServerRequest *request){ - AsyncJpegStreamResponse *response = new AsyncJpegStreamResponse(); - if(!response){ - request->send(501); - return; - } - response->addHeader("Access-Control-Allow-Origin", "*"); - request->send(response); -} \ No newline at end of file diff --git a/include/CaptivePortalHandler.h b/include/CaptivePortalHandler.h deleted file mode 100644 index ae9367e..0000000 --- a/include/CaptivePortalHandler.h +++ /dev/null @@ -1,22 +0,0 @@ -#include - -class CaptiveRequestHandler : public AsyncWebHandler { -public: - CaptiveRequestHandler() {} - virtual ~CaptiveRequestHandler() {} - - bool canHandle(AsyncWebServerRequest *request){ - //request->addInterestingHeader("ANY"); - return true; - } - - void handleRequest(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - response->print("Captive Portal"); - response->print("

This is out captive portal front page.

"); - response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); - response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); - response->print(""); - request->send(response); - } -}; \ No newline at end of file diff --git a/lib/ServerHandlers/src/AsyncJpegStreamHandler.cpp b/lib/ServerHandlers/src/AsyncJpegStreamHandler.cpp new file mode 100644 index 0000000..f7c0a25 --- /dev/null +++ b/lib/ServerHandlers/src/AsyncJpegStreamHandler.cpp @@ -0,0 +1,129 @@ +#include + +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: %s\r\nContent-Length: %u\r\n\r\n"; + +static const char * JPG_CONTENT_TYPE = "image/jpeg"; + +AsyncJpegStreamResponse::AsyncJpegStreamResponse(){ + _callback = nullptr; + _code = 200; + _contentLength = 0; + _contentType = STREAM_CONTENT_TYPE; + _sendContentLength = false; + _chunked = true; + _index = 0; + _jpg_buf_len = 0; + _jpg_buf = NULL; + lastAsyncRequest = 0; + memset(&_frame, 0, sizeof(camera_frame_t)); +} + +AsyncJpegStreamResponse::~AsyncJpegStreamResponse(){ + if(_frame.fb){ + if(_frame.fb->format != PIXFORMAT_JPEG){ + free(_jpg_buf); + } + esp_camera_fb_return(_frame.fb); + } +} + +/*bool AsyncJpegStreamResponse::_sourceValid() { + return true; +}*/ + +size_t AsyncJpegStreamResponse::_fillBuffer(uint8_t *buf, size_t maxLen) { + size_t ret = _content(buf, maxLen, _index); + if(ret != RESPONSE_TRY_AGAIN){ + _index += ret; + } + return ret; +} + +size_t AsyncJpegStreamResponse::_content(uint8_t *buffer, size_t maxLen, size_t index){ + if(!_frame.fb || _frame.index == _jpg_buf_len) { + if(index && _frame.fb){ + uint64_t end = (uint64_t)micros(); + int fp = (end - lastAsyncRequest) / 1000; + log_printf("Size: %uKB, Time: %ums (%.1ffps)\n", _jpg_buf_len/1024, fp); + lastAsyncRequest = end; + if(_frame.fb->format != PIXFORMAT_JPEG){ + free(_jpg_buf); + } + esp_camera_fb_return(_frame.fb); + _frame.fb = NULL; + _jpg_buf_len = 0; + _jpg_buf = NULL; + } + if(maxLen < (strlen(STREAM_BOUNDARY) + strlen(STREAM_PART) + strlen(JPG_CONTENT_TYPE) + 8)){ + //log_w("Not enough space for headers"); + return RESPONSE_TRY_AGAIN; + } + //get frame + _frame.index = 0; + + _frame.fb = esp_camera_fb_get(); + if (_frame.fb == NULL) { + log_e("Camera frame failed"); + return 0; + } + + if(_frame.fb->format != PIXFORMAT_JPEG){ + unsigned long st = millis(); + bool jpeg_converted = frame2jpg(_frame.fb, 80, &_jpg_buf, &_jpg_buf_len); + if(!jpeg_converted){ + log_e("JPEG compression failed"); + esp_camera_fb_return(_frame.fb); + _frame.fb = NULL; + _jpg_buf_len = 0; + _jpg_buf = NULL; + return 0; + } + log_i("JPEG: %lums, %uB", millis() - st, _jpg_buf_len); + } else { + _jpg_buf_len = _frame.fb->len; + _jpg_buf = _frame.fb->buf; + } + + //send boundary + size_t blen = 0; + if(index){ + blen = strlen(STREAM_BOUNDARY); + memcpy(buffer, STREAM_BOUNDARY, blen); + buffer += blen; + } + //send header + size_t hlen = sprintf((char *)buffer, STREAM_PART, JPG_CONTENT_TYPE, _jpg_buf_len); + buffer += hlen; + //send frame + hlen = maxLen - hlen - blen; + if(hlen > _jpg_buf_len){ + maxLen -= hlen - _jpg_buf_len; + hlen = _jpg_buf_len; + } + memcpy(buffer, _jpg_buf, hlen); + _frame.index += hlen; + return maxLen; + } + + size_t available = _jpg_buf_len - _frame.index; + if(maxLen > available){ + maxLen = available; + } + memcpy(buffer, _jpg_buf+_frame.index, maxLen); + _frame.index += maxLen; + + return maxLen; +} + + +void streamJpg(AsyncWebServerRequest *request){ + AsyncJpegStreamResponse *response = new AsyncJpegStreamResponse(); + if(!response){ + request->send(501); + return; + } + response->addHeader("Access-Control-Allow-Origin", "*"); + request->send(response); +} \ No newline at end of file diff --git a/lib/ServerHandlers/src/AsyncJpegStreamHandler.h b/lib/ServerHandlers/src/AsyncJpegStreamHandler.h new file mode 100644 index 0000000..5d47063 --- /dev/null +++ b/lib/ServerHandlers/src/AsyncJpegStreamHandler.h @@ -0,0 +1,28 @@ +#include +#include + +typedef struct { + camera_fb_t * fb; + size_t index; +} camera_frame_t; + +#define PART_BOUNDARY "123456789000000000000987654321" + +class AsyncJpegStreamResponse: public AsyncAbstractResponse { + private: + camera_frame_t _frame; + size_t _index; + size_t _jpg_buf_len; + uint8_t * _jpg_buf; + uint64_t lastAsyncRequest; + public: + AsyncJpegStreamResponse(); + ~AsyncJpegStreamResponse(); + bool _sourceValid() const { + return true; + } + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + size_t _content(uint8_t *buffer, size_t maxLen, size_t index); +}; + +void streamJpg(AsyncWebServerRequest *request); \ No newline at end of file diff --git a/lib/ServerHandlers/src/WebsocketHandler.h b/lib/ServerHandlers/src/WebsocketHandler.h new file mode 100644 index 0000000..73a32fd --- /dev/null +++ b/lib/ServerHandlers/src/WebsocketHandler.h @@ -0,0 +1,5 @@ +#include + +extern uint8_t wsData[6]; + +void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); \ No newline at end of file diff --git a/include/WebsocketHandler.h b/lib/ServerHandlers/src/Websockethandler.cpp similarity index 85% rename from include/WebsocketHandler.h rename to lib/ServerHandlers/src/Websockethandler.cpp index e116f57..03cfa0c 100644 --- a/include/WebsocketHandler.h +++ b/lib/ServerHandlers/src/Websockethandler.cpp @@ -1,4 +1,6 @@ -#include +#include + +uint8_t wsData[6] = {}; void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ if(type == WS_EVT_CONNECT){ @@ -16,25 +18,20 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT String msg = ""; if(info->final && info->index == 0 && info->len == len){ //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); + //Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); if(info->opcode == WS_TEXT){ for(size_t i=0; i < info->len; i++) { msg += (char) data[i]; } } else { - char buff[3]; + char buff[6]; for(size_t i=0; i < info->len; i++) { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; + wsData[i] = (uint8_t) data[i]; + //sprintf(buff, "%02x ", (uint8_t) data[i]); + //msg += buff ; } } - Serial.printf("%s\n",msg.c_str()); - - if(info->opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); } else { //message is comprised of multiple frames or the frame is split into multiple packets if(info->index == 0){ diff --git a/src/main.cpp b/src/main.cpp index cb13687..d8c920f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include @@ -104,7 +103,6 @@ void setupServer(){ ws.onEvent(onWsEvent); server.addHandler(&ws); server.on("/stream", HTTP_GET, streamJpg); - server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html"); server.begin(); }