esp32_https_server icon indicating copy to clipboard operation
esp32_https_server copied to clipboard

Web server requests timeout while streaming MJPEG via a resource handler

Open marciot opened this issue 4 years ago • 1 comments

Describe the bug

Maybe not a bug, more of a limitation of the current implementation and a feature request for a way around this problem.

I have created a handler for streaming MJPEG video via a multipart form. This handler serves data continuously until the client closes the connection. I've noticed that while a stream is active, all other connections to the web server timeout.

At first, I was confused by this, because ESP32 is supposed to allow multiple connections, but it appears as if this means is that while it can allow multiple sequential requests over an open connection, only one request handler can run at a time.

My question is whether it would be possible to have a special handler for HTTP streams. Since esp32_https_server allows websockets, which also run continuously, I assume it is theoretically possible, but my guess is that it would need to be a special type of connection object for HTTP streams, just like WebsocketHandler. The difference would be where instead of having a single handler function that is called once, there are multiple functions, one for setting up the connection, one for emitting each "frame" in the stream, and a last for cleaning up.

How To Reproduce

Create a handler that streams video as a multipart form:

void handleCameraStream(HTTPRequest * req, HTTPResponse * res) {
        #define PART_BOUNDARY "123456789000000000000987654321"
        constexpr char* STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
        constexpr char* STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
        constexpr char* STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

        camera_fb_t * fb = NULL;
        char part_buf[64];

        res->setHeader("Content-Type", STREAM_CONTENT_TYPE);
        res->setHeader("Access-Control-Allow-Origin","*");

        while(1){
            fb = esp_camera_fb_get();
            if (!fb) {
                res->setStatusText("Camera capture failed");
                res->setStatusCode(400);
                return;
            }
            size_t hlen = snprintf(part_buf, 64, STREAM_PART, fb->len);
            if(res->write((unsigned char*)part_buf, hlen) != hlen) break;
            if(res->write((unsigned char*)fb->buf, fb->len) != fb->len) break;
            if(res->write((unsigned char*)STREAM_BOUNDARY, strlen(STREAM_BOUNDARY)) != strlen(STREAM_BOUNDARY)) break;
            esp_camera_fb_return(fb);
        }
        esp_camera_fb_return(fb);
}

...

webServer.registerNode(  new ResourceNode("/capture.mjpg", "GET", &handleCameraStream));

Expected Behavior

The user should be able to connect to "http//esp32_server/capture.mjpg", leave that window open, and still be able to access other resources on the web server.

Actual Behavior

Once the user makes a connection to "http//esp32_server/capture.mjpg", all accesses to the server time out until that window is closed.

ESP32 Module Please provide specifications of your module

  • RAM/PSRAM: ESP32-CAM
  • Flash Size: 4GB
  • Other special properties:

Software (please complete the following information if applicable)

  • IDE and Version: Arduino 1.9.0-beta
  • OS: Windows
  • Client used to access the server: Chrome

Additional context Add any other context about the problem here.

marciot avatar Aug 20 '21 15:08 marciot

Hello, I think this is an inherent "problem" of this webserver implementation - it runs all requests in one loop. If one of the requests does not end, all other requests are blocked and time out. Although this implementation is a very nice and fast webserver it does not really work "asynchronous". But for Arduino on ESP32 I currently do not know a better one. If you can/want to use ESP-IDF directly you could check if this is something which would fit your needs. It works totally asynchronous, is (high-level) cpp, non-Arduino and quite stable. Unfortunately for some reason this solution works a little bit slow...

squonk11 avatar Aug 21 '21 15:08 squonk11