Implement a camera livestream handler (#2286)

This commit is contained in:
Slider0007
2023-04-04 22:06:53 +02:00
committed by GitHub
parent 63ac38a52d
commit e7bfba4b01
5 changed files with 148 additions and 11 deletions

View File

@@ -32,6 +32,14 @@
static const char *TAG = "CAM";
/* Camera live stream */
#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: image/jpeg\r\nContent-Length: %u\r\n\r\n";
static camera_config_t camera_config = {
.pin_pwdn = CAM_PIN_PWDN,
.pin_reset = CAM_PIN_RESET,
@@ -521,6 +529,74 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay)
}
esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn)
{
esp_err_t res = ESP_OK;
size_t fb_len = 0;
int64_t fr_start;
char * part_buf[64];
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started");
if (FlashlightOn) {
LEDOnOff(true);
LightOnOff(true);
}
//httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local
httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
while(1)
{
fr_start = esp_timer_get_time();
camera_fb_t *fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
fb = esp_camera_fb_get();
if (!fb) {
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToStream: Camera framebuffer not available");
break;
}
fb_len = fb->len;
if (res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if (res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)fb->buf, fb_len);
}
if (res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
esp_camera_fb_return(fb);
int64_t fr_end = esp_timer_get_time();
ESP_LOGD(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
if (res != ESP_OK){ // Exit loop, e.g. also when closing the webpage
break;
}
int64_t fr_delta_ms = (fr_end - fr_start) / 1000;
if (CAM_LIVESTREAM_REFRESHRATE > fr_delta_ms) {
const TickType_t xDelay = (CAM_LIVESTREAM_REFRESHRATE - fr_delta_ms) / portTICK_PERIOD_MS;
ESP_LOGD(TAG, "Stream: sleep for: %ldms", (long) xDelay*10);
vTaskDelay(xDelay);
}
}
LEDOnOff(false);
LightOnOff(false);
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped");
return res;
}
void CCamera::LightOnOff(bool status)
{
GpioHandler* gpioHandler = gpio_handler_get();

View File

@@ -40,6 +40,7 @@ class CCamera {
void LightOnOff(bool status);
void LEDOnOff(bool status);
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn);
void SetQualitySize(int qual, framesize_t resol);
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);

View File

@@ -208,6 +208,40 @@ esp_err_t handler_init(httpd_req_t *req)
}
esp_err_t handler_stream(httpd_req_t *req)
{
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("handler_stream - Start");
ESP_LOGD(TAG, "handler_stream uri: %s", req->uri);
#endif
char _query[50];
char _value[10];
bool flashlightOn = false;
if (httpd_req_get_url_query_str(req, _query, 50) == ESP_OK)
{
// ESP_LOGD(TAG, "Query: %s", _query);
if (httpd_query_key_value(_query, "flashlight", _value, 10) == ESP_OK)
{
#ifdef DEBUG_DETAIL_ON
ESP_LOGD(TAG, "flashlight is found%s", _size);
#endif
if (strlen(_value) > 0)
flashlightOn = true;
}
}
Camera.CaptureToStream(req, flashlightOn);
#ifdef DEBUG_DETAIL_ON
LogFile.WriteHeapInfo("handler_stream - Done");
#endif
return ESP_OK;
}
esp_err_t handler_flow_start(httpd_req_t *req) {
#ifdef DEBUG_DETAIL_ON
@@ -1094,4 +1128,10 @@ void register_server_main_flow_task_uri(httpd_handle_t server)
camuri.handler = handler_get_heap;
camuri.user_ctx = (void*) "Heap";
httpd_register_uri_handler(server, &camuri);
camuri.uri = "/stream";
camuri.handler = handler_stream;
camuri.user_ctx = (void*) "stream";
httpd_register_uri_handler(server, &camuri);
}