diff --git a/app/src/routes/peripherals/camera/Camera.svelte b/app/src/routes/peripherals/camera/Camera.svelte
index 9c7e2fe..89dc387 100644
--- a/app/src/routes/peripherals/camera/Camera.svelte
+++ b/app/src/routes/peripherals/camera/Camera.svelte
@@ -2,37 +2,16 @@
import { user } from '$lib/stores/user';
import SettingsCard from "$lib/components/SettingsCard.svelte";
import Camera from '~icons/mdi/camera-outline'
- import VideoCamera from '~icons/mdi/videocam-outline'
- import Reload from '~icons/mdi/reload'
import Record from '~icons/mdi/radio-button-unchecked'
- import Recording from '~icons/mdi/radio-button-checked'
-
- import Spinner from '$lib/components/Spinner.svelte';
import CameraSetting from './CameraSetting.svelte';
- import { onDestroy, onMount } from 'svelte';
+ import { onDestroy } from 'svelte';
const ws_token = `?access_token=${$user.bearer_token}`
- let stillId = 0
-
- let recording = false
-
- let videoMode = false
-
- const takeStill = () => stillId += 1
-
- const toggleMode = () => {
- videoMode = !videoMode
- }
-
- let timer:number
-
- onMount(() => {
- timer = setInterval(takeStill, 1000)
- })
+ let source = "/api/camera/stream"+ ws_token;
onDestroy(() => {
- clearInterval(timer)
+ source = ""
})
@@ -40,28 +19,6 @@
Camera
-
+
-
-
-
-
-
\ No newline at end of file
diff --git a/esp32/lib/ESP32-sveltekit/CameraService.cpp b/esp32/lib/ESP32-sveltekit/CameraService.cpp
index 07fb827..2066a12 100644
--- a/esp32/lib/ESP32-sveltekit/CameraService.cpp
+++ b/esp32/lib/ESP32-sveltekit/CameraService.cpp
@@ -17,6 +17,16 @@ camera_fb_t *safe_camera_fb_get() {
return fb;
}
+sensor_t *safe_sensor_get() {
+ sensor_t *s = NULL;
+ if (xSemaphoreTake(cameraMutex, portMAX_DELAY) == pdTRUE) {
+ s = esp_camera_sensor_get();
+ }
+ return s;
+}
+
+void safe_sensor_return() { xSemaphoreGive(cameraMutex); }
+
CameraService::CameraService(PsychicHttpServer *server,
TaskManager *taskManager,
SecurityManager *securityManager)
@@ -30,9 +40,15 @@ void CameraService::begin() {
STILL_SERVICE_PATH, HTTP_GET,
_securityManager->wrapRequest(
std::bind(&CameraService::cameraStill, this, std::placeholders::_1),
- AuthenticationPredicates::IS_AUTHENTICATED));
+ AuthenticationPredicates::IS_AUTHENTICATED));
+ _server->on(STREAM_SERVICE_PATH, HTTP_GET,
+ _securityManager->wrapRequest(
+ std::bind(&CameraService::cameraStream, this,
+ std::placeholders::_1),
+ AuthenticationPredicates::IS_AUTHENTICATED));
ESP_LOGV("CameraService", "Registered GET endpoint: %s", STILL_SERVICE_PATH);
+ ESP_LOGV("CameraService", "Registered GET endpoint: %s", STREAM_SERVICE_PATH);
}
esp_err_t CameraService::InitializeCamera() {
@@ -82,9 +98,69 @@ esp_err_t CameraService::cameraStill(PsychicRequest *request) {
request->reply(500, "text/plain", "Camera capture failed");
return ESP_FAIL;
}
- PsychicStreamResponse response = PsychicStreamResponse(request, "image/jpeg", "capture.jpg");
+ PsychicStreamResponse response =
+ PsychicStreamResponse(request, "image/jpeg", "capture.jpg");
response.beginSend();
response.write(fb->buf, fb->len);
esp_camera_fb_return(fb);
return response.endSend();
}
+
+void streamTask(void *pv) {
+ esp_err_t res = ESP_OK;
+
+ PsychicRequest *request = static_cast(pv);
+
+ httpd_req_t *copy = nullptr;
+ res = httpd_req_async_handler_begin(request->request(), ©);
+ if (res != ESP_OK) {
+ return;
+ }
+ PsychicHttpServer *server = request->server();
+ PsychicRequest new_request = PsychicRequest(server, copy);
+ request = &new_request;
+
+ PsychicStreamResponse response = PsychicStreamResponse(request, _STREAM_CONTENT_TYPE);
+ camera_fb_t *fb = NULL;
+
+ char *part_buf[64];
+ size_t buf_len = 0;
+ uint8_t *buf = NULL;
+ int64_t fr_start = esp_timer_get_time();
+
+ response.beginSend();
+
+ for (;;) {
+ fb = safe_camera_fb_get();
+ if (!fb) {
+ ESP_LOGE("Stream", "Camera capture failed");
+ break;
+ }
+ if(fb->format != PIXFORMAT_JPEG){
+ if(!frame2jpg(fb, 80, &buf, &buf_len)) break;
+ } else {
+ buf_len = fb->len;
+ buf = fb->buf;
+ }
+
+ size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, buf_len);
+ size_t w = response.write((const char *)part_buf, hlen);
+ w += response.write((const char *)buf, buf_len);
+ w += response.write((char *)_STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
+ if (w == 62) break;
+ esp_camera_fb_return(fb);
+ buf = NULL;
+ taskYIELD();
+ int64_t delay = 30000ll - esp_timer_get_time() - fr_start;
+ if (delay > 0) vTaskDelay(pdMS_TO_TICKS(delay));
+ }
+ response.endSend();
+ httpd_req_async_handler_complete(copy);
+ vTaskDelete(NULL);
+}
+
+esp_err_t CameraService::cameraStream(PsychicRequest *request) {
+ _taskManager->createTask(streamTask, "Stream client task", 4096, request, 0);
+ vTaskDelay(pdMS_TO_TICKS(100));
+ return ESP_OK;
+}
\ No newline at end of file
diff --git a/esp32/lib/ESP32-sveltekit/CameraService.h b/esp32/lib/ESP32-sveltekit/CameraService.h
index b610eb5..620c8fa 100644
--- a/esp32/lib/ESP32-sveltekit/CameraService.h
+++ b/esp32/lib/ESP32-sveltekit/CameraService.h
@@ -1,15 +1,16 @@
#ifndef CameraService_h
#define CameraService_h
-#define CAMERA_MODEL_AI_THINKER
-
#include
-#include
#include
#include
#include
#include
#include
+#include
+
+#define CAMERA_MODEL_AI_THINKER
+#include
#define STREAM_SERVICE_PATH "/api/camera/stream"
#define STILL_SERVICE_PATH "/api/camera/still"
@@ -17,6 +18,8 @@
#define PART_BOUNDARY "frame"
camera_fb_t *safe_camera_fb_get();
+sensor_t* safe_sensor_get();
+void safe_sensor_return();
class CameraService
{
@@ -31,6 +34,7 @@ class CameraService
SecurityManager *_securityManager;
PsychicStream _videoStream;
esp_err_t cameraStill(PsychicRequest *request);
+ esp_err_t cameraStream(PsychicRequest *request);
esp_err_t InitializeCamera();
};
diff --git a/esp32/lib/ESP32-sveltekit/CameraSettingsService.h b/esp32/lib/ESP32-sveltekit/CameraSettingsService.h
index a7c14db..fe162ad 100644
--- a/esp32/lib/ESP32-sveltekit/CameraSettingsService.h
+++ b/esp32/lib/ESP32-sveltekit/CameraSettingsService.h
@@ -1,6 +1,7 @@
#ifndef CameraSettingsService_h
#define CameraSettingsService_h
+#include
#include
#include
#include
@@ -137,11 +138,42 @@ class CameraSettingsService : public StatefulService {
void begin() {
_httpEndpoint.begin();
_eventEndpoint.begin();
+ sensor_t *s = safe_sensor_get();
+ _state.pixformat = s->pixformat;
+ _state.framesize = s->status.framesize;
+ _state.brightness = s->status.brightness;
+ _state.contrast = s->status.contrast;
+ _state.saturation = s->status.saturation;
+ _state.sharpness = s->status.sharpness;
+ _state.denoise = s->status.denoise;
+ _state.gainceiling = (gainceiling_t)s->status.gainceiling;
+ _state.quality = s->status.quality;
+ _state.colorbar = s->status.colorbar;
+ _state.awb_gain = s->status.awb_gain;
+ _state.wb_mode = s->status.wb_mode;
+ _state.aec2 = s->status.aec2;
+ _state.ae_level = s->status.ae_level;
+ _state.aec_value = s->status.aec_value;
+ _state.agc_gain = s->status.agc_gain;
+ _state.bpc = s->status.bpc;
+ _state.wpc = s->status.wpc;
+ _state.special_effect = s->status.special_effect;
+ _state.raw_gma = s->status.raw_gma;
+ _state.lenc = s->status.lenc;
+ _state.hmirror = s->status.hmirror;
+ _state.vflip = s->status.vflip;
+ _state.dcw = s->status.dcw;
+ safe_sensor_return();
}
void updateCamera() {
ESP_LOGI("CameraSettings", "Updating camera settings");
- sensor_t *s = esp_camera_sensor_get();
+ sensor_t *s = safe_sensor_get();
+ if (!s) {
+ ESP_LOGE("CameraSettings", "Failed to update camera settings");
+ safe_sensor_return();
+ return;
+ }
s->set_pixformat(s, _state.pixformat);
s->set_framesize(s, _state.framesize);
s->set_brightness(s, _state.brightness);
@@ -152,14 +184,11 @@ class CameraSettingsService : public StatefulService {
s->set_gainceiling(s, _state.gainceiling);
s->set_quality(s, _state.quality);
s->set_colorbar(s, _state.colorbar);
- s->set_whitebal(s, _state.whitebal);
s->set_awb_gain(s, _state.awb_gain);
s->set_wb_mode(s, _state.wb_mode);
- s->set_exposure_ctrl(s, _state.exposure_ctrl);
s->set_aec2(s, _state.aec2);
s->set_ae_level(s, _state.ae_level);
s->set_aec_value(s, _state.aec_value);
- s->set_gain_ctrl(s, _state.gain_ctrl);
s->set_agc_gain(s, _state.agc_gain);
s->set_bpc(s, _state.bpc);
s->set_wpc(s, _state.wpc);
@@ -169,6 +198,7 @@ class CameraSettingsService : public StatefulService {
s->set_hmirror(s, _state.hmirror);
s->set_vflip(s, _state.vflip);
s->set_dcw(s, _state.dcw);
+ safe_sensor_return();
}
private: