GithubHelp home page GithubHelp logo

Comments (6)

noweare1 avatar noweare1 commented on August 16, 2024

This happens when a task is taking up too many cpu cycles. The doc that describes the operation of the esp32 watch dog timer is here. You will have to assign
the offending task to be monitored by the wdt then use "esp_task_wdt_reset()" somewhere in that task to reset the wdt.

from espasyncwebserver.

kRYstall9 avatar kRYstall9 commented on August 16, 2024

Hi @noweare1, thanks for answering. I've already tried adding the esp_task_wdt_reset() inside the callback, but it didn't help. Since the connection will be closed on request->send(), right now I have no idea on how to implement something that can help me achieving the live streaming. With the WebServer.h library(The one not async) I've been able to achieve that, but it takes too much time to send frames through server.send() or server.sendContent()

from espasyncwebserver.

noweare1 avatar noweare1 commented on August 16, 2024

I am just learning to use the espAsynchWebServer library. I am also trying to stream frames using this library. It took me a while to figure out how streaming works with the espressif Camera example.

Some of the magic is actually in the header files. The header "multipart/x-mixed-replace;boundary=" PART_BOUNDARY" is part of the response to the browser. That header tells the browser that it will pushing images to the browser without having the browser ask for the next image (which saves time). So you might want to take a look at that.

from espasyncwebserver.

kRYstall9 avatar kRYstall9 commented on August 16, 2024

Hi @noweare1 , thanks again for answering. I took a look at some examples I found and came up to this.

#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";


void handleStreaming(AsyncWebServerRequest *request){
    AsyncWebServerResponse* response = request->beginChunkedResponse(JPG_CONTENT_TYPE, _sendContent);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
}

size_t _sendContent(uint8_t* buffer, size_t maxLen, size_t index){
    Serial.println("MAX LEN: " + String(maxLen));
    Serial.println("INDEX: " + String(index));
    Serial.println("STRLEN BOUNDARY: " + String(strlen(STREAM_BOUNDARY)));
    Serial.println("STRLEN PART: " + String(strlen(STREAM_PART)));
    Serial.println("STRLEN CONTENT TYPE: " + String(strlen(JPG_CONTENT_TYPE)));

    esp_err_t err = ESP_OK;
    vTaskDelay(10);
    if(!_frame.fb || _frame.index == _jpg_buf_len){
        Serial.println("Need a new frame");
        //Ho bisogno di un nuovo frame
        if(index && _frame.fb){
            //Ho gia' inizializzato il frame almeno una volta
            if(_frame.fb->format != PIXFORMAT_JPEG){
                Serial.println("Previous frame was not in JPEG format");
                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("image/jpeg") + 8)){
            Serial.println("Cannot send the frame. Retry!");
            return RESPONSE_TRY_AGAIN;
        }
        //Getting new frame
        _frame.index = 0;
        Serial.println("Getting a new frame");
        _frame.fb = esp_camera_fb_get();
        if(_frame.fb == NULL){
            Serial.println("Camera failed");
            if(esp_camera_deinit() == ESP_OK){
                camera_init();
            };
            return 0;
        }

        if(_frame.fb->format != PIXFORMAT_JPEG){
            Serial.println("new frame not in JPEG format");
            bool jpeg_converted = frame2jpg(_frame.fb, 80, &_jpg_buf, &_jpg_buf_len);
            if(!jpeg_converted){
                Serial.println("Conversion failed");
                _frame.fb = NULL;
                _jpg_buf_len = 0;
                _jpg_buf = NULL;
                return  0;
            }
            esp_task_wdt_reset();
        }
        Serial.println("New frame's already in JPEG format");
        Serial.println("Sending boundary");
        size_t boundaryLen = 0;
        if(index){
            boundaryLen = strlen(STREAM_BOUNDARY);
            memcpy(buffer, STREAM_BOUNDARY, boundaryLen);
            buffer += boundaryLen;
        }
        //send header
        Serial.println("Sending header");
        size_t headerLen = sprintf((char*)buffer, STREAM_PART, "image/jpeg", _jpg_buf_len);
        buffer += headerLen;
        //send frame
        headerLen = maxLen - headerLen - boundaryLen;
        Serial.println("Sending frame");
        if(headerLen > _jpg_buf_len){
            maxLen -= headerLen - _jpg_buf_len;
            headerLen = _jpg_buf_len;
        }
        memcpy(buffer, _jpg_buf, headerLen);
        _frame.index += headerLen;
        return maxLen;
    }
    Serial.println("Sending frame 2");
    size_t available = _jpg_buf_len - _frame.index;
    if(maxLen > available){
        maxLen = available;
    }
    memcpy(buffer, _jpg_buf+ _frame.index , maxLen);
    _frame.index += maxLen;
    esp_task_wdt_reset();
    return maxLen;
}

I can see in the console that it is indeed working, but:

  • I'm not able to see the image on the browser page
  • Still getting Task watchdog got triggered after a while

EDIT:
I forgot to assign _frame.fb->buf to _jpg_buf and _frame.fb->len to _jpg_buf_len. But still, I'm not able to see the images on a web page

from espasyncwebserver.

kRYstall9 avatar kRYstall9 commented on August 16, 2024

@noweare1 I managed to solve the problem. I had to create a class that extends the AsyncAbstractResponse class. Then I had to override the _fillBuffer method and it happened to be working.

#include "AsyncJpegStreamResponse.h"
#include <cstring>

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;
    memset(&_frame, 0, sizeof(camera_frame_t));
}

AsyncJpegStreamResponse::~AsyncJpegStreamResponse() {
    Serial.println("In the constructor");
    if (_frame.fb) {
        Serial.println("Frame fb not null");
        if (_frame.fb->format != PIXFORMAT_JPEG) {
            free(_jpg_buf);
        }
        esp_camera_fb_return(_frame.fb);
    }
}

bool AsyncJpegStreamResponse::_sourceValid() const {
    return true;
}

size_t AsyncJpegStreamResponse::_fillBuffer(uint8_t* buf, size_t maxLen) {
    Serial.println("In the _fillBuffer function");
    size_t ret = _sendContent(buf, maxLen, _index);
    if (ret != RESPONSE_TRY_AGAIN) {
        _index += ret;
    }
    return ret;
}

size_t AsyncJpegStreamResponse::_sendContent(uint8_t* buffer, size_t maxLen, size_t index) {
    Serial.println("In the _sendContent function");
    if (!_frame.fb || _frame.index == _jpg_buf_len) {
        if (index && _frame.fb) {
            if (_frame.fb->format != PIXFORMAT_JPEG) {
                free(_jpg_buf);
            }
            esp_camera_fb_return(_frame.fb);
            _jpg_buf = NULL;
            _jpg_buf_len = 0;
            _frame.fb = NULL;
        }

        if (maxLen < (strlen(STREAM_BOUNDARY) + strlen(STREAM_PART) + strlen(JPG_CONTENT_TYPE) + 8)) {
            return RESPONSE_TRY_AGAIN;
        }

        _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) {
            bool _jpg_converted = frame2jpg(_frame.fb, 80, &_jpg_buf, &_jpg_buf_len);
            if (!_jpg_converted) {
                log_e("JPEG compression failed");
                esp_camera_fb_return(_frame.fb);
                _frame.fb = NULL;
                _jpg_buf_len = 0;
                _jpg_buf = NULL;
                return 0;
            }
        } else {
            _jpg_buf = _frame.fb->buf;
            _jpg_buf_len = _frame.fb->len;
        }

        size_t blen = 0;
        if (index) {
            blen = strlen(STREAM_BOUNDARY);
            memcpy(buffer, STREAM_BOUNDARY, blen);
            buffer += blen;
        }

        size_t hlen = sprintf((char*) buffer, STREAM_PART, JPG_CONTENT_TYPE, _jpg_buf_len);
        buffer += hlen;
        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;
}

Then, in your main.cpp, simply call

AsyncWebServerResponse* response = new AsyncJpegStreamResponse();
request->send(response);

from espasyncwebserver.

noweare1 avatar noweare1 commented on August 16, 2024

Congratulations ! I will have to follow the code to see what you did. I mostly program using C. I am not a C++ programmer so this could be a little challenging for me. The espressif IDF has a webserver module that also supports websockets. I have been looking at that library also.

from espasyncwebserver.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.