diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 684540ffa6..e625c12ffd 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -763,7 +763,7 @@ message ExecuteServiceRequest { message ListEntitiesCameraResponse { option (id) = 43; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_ESP32_CAMERA"; + option (ifdef) = "USE_CAMERA"; string object_id = 1; fixed32 key = 2; @@ -777,7 +777,7 @@ message ListEntitiesCameraResponse { message CameraImageResponse { option (id) = 44; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_ESP32_CAMERA"; + option (ifdef) = "USE_CAMERA"; fixed32 key = 1; bytes data = 2; @@ -786,7 +786,7 @@ message CameraImageResponse { message CameraImageRequest { option (id) = 45; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_ESP32_CAMERA"; + option (ifdef) = "USE_CAMERA"; option (no_delay) = true; bool single = 1; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bb55a2ccf6..6d26af714e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -39,6 +39,11 @@ APIConnection::APIConnection(std::unique_ptr sock, APIServer *pa #else #error "No frame helper defined" #endif + +#ifdef USE_CAMERA + if (camera::Camera::global_camera != nullptr) + this->image_reader_ = std::unique_ptr{camera::Camera::global_camera->create_image_reader()}; +#endif } void APIConnection::start() { this->last_traffic_ = millis(); @@ -149,24 +154,24 @@ void APIConnection::loop() { } } -#ifdef USE_ESP32_CAMERA - if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) { - uint32_t to_send = std::min((size_t) 1024, this->image_reader_.available()); +#ifdef USE_CAMERA + if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) { + uint32_t to_send = std::min((size_t) 1024, this->image_reader_->available()); auto buffer = this->create_buffer(); // fixed32 key = 1; - buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash()); + buffer.encode_fixed32(1, camera::Camera::global_camera->get_object_id_hash()); // bytes data = 2; - buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send); + buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send); // bool done = 3; - bool done = this->image_reader_.available() == to_send; + bool done = this->image_reader_->available() == to_send; buffer.encode_bool(3, done); bool success = this->send_buffer(buffer, 44); if (success) { - this->image_reader_.consume_data(to_send); + this->image_reader_->consume_data(to_send); } if (success && done) { - this->image_reader_.return_image(); + this->image_reader_->return_image(); } } #endif @@ -1061,17 +1066,19 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { } #endif -#ifdef USE_ESP32_CAMERA -void APIConnection::send_camera_state(std::shared_ptr image) { +#ifdef USE_CAMERA +void APIConnection::send_camera_state(std::shared_ptr image) { if (!this->state_subscription_) return; - if (this->image_reader_.available()) + if (!this->image_reader_) return; - if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) || - image->was_requested_by(esphome::esp32_camera::IDLE)) - this->image_reader_.set_image(std::move(image)); + if (this->image_reader_->available()) + return; + if (image->was_requested_by(esphome::camera::API_REQUESTER) || + image->was_requested_by(esphome::camera::IDLE)) + this->image_reader_->set_image(std::move(image)); } -bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { +bool APIConnection::send_camera_info(camera::Camera *camera) { ListEntitiesCameraResponse msg; msg.key = camera->get_object_id_hash(); msg.object_id = camera->get_object_id(); @@ -1084,16 +1091,16 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { return this->send_list_entities_camera_response(msg); } void APIConnection::camera_image(const CameraImageRequest &msg) { - if (esp32_camera::global_esp32_camera == nullptr) + if (camera::Camera::global_camera == nullptr) return; if (msg.single) - esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER); + camera::Camera::global_camera->request_image(esphome::camera::API_REQUESTER); if (msg.stream) { - esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER); + camera::Camera::global_camera->start_stream(esphome::camera::API_REQUESTER); App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() { - esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER); + camera::Camera::global_camera->stop_stream(esphome::camera::API_REQUESTER); }); } } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 043aaee421..77a5b20b29 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -58,9 +58,9 @@ class APIConnection : public APIServerConnection { bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); bool send_text_sensor_info(text_sensor::TextSensor *text_sensor); #endif -#ifdef USE_ESP32_CAMERA - void send_camera_state(std::shared_ptr image); - bool send_camera_info(esp32_camera::ESP32Camera *camera); +#ifdef USE_CAMERA + void send_camera_state(std::shared_ptr image); + bool send_camera_info(camera::Camera *camera); void camera_image(const CameraImageRequest &msg) override; #endif #ifdef USE_CLIMATE @@ -249,8 +249,8 @@ class APIConnection : public APIServerConnection { std::string client_combined_info_; uint32_t client_api_version_major_{0}; uint32_t client_api_version_minor_{0}; -#ifdef USE_ESP32_CAMERA - esp32_camera::CameraImageReader image_reader_; +#ifdef USE_CAMERA + std::unique_ptr image_reader_; #endif bool state_subscription_{false}; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 6e11d7169d..234b96e4d0 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -210,7 +210,8 @@ bool APIServerConnectionBase::send_list_entities_services_response(const ListEnt #endif return this->send_message_(msg, 41); } -#ifdef USE_ESP32_CAMERA + +#ifdef USE_CAMERA bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str()); @@ -218,7 +219,7 @@ bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntit return this->send_message_(msg, 43); } #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str()); @@ -226,8 +227,6 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon return this->send_message_(msg, 44); } #endif -#ifdef USE_ESP32_CAMERA -#endif #ifdef USE_CLIMATE bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) { #ifdef HAS_PROTO_MESSAGE_DUMP @@ -843,7 +842,7 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, break; } case 45: { -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA CameraImageRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1363,7 +1362,7 @@ void APIServerConnection::on_switch_command_request(const SwitchCommandRequest & this->switch_command(msg); } #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { if (!this->is_connection_setup()) { this->on_no_setup_connection(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 51b94bf530..1c424b0582 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -94,13 +94,13 @@ class APIServerConnectionBase : public ProtoService { virtual void on_get_time_response(const GetTimeResponse &value){}; bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg); virtual void on_execute_service_request(const ExecuteServiceRequest &value){}; -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg); #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA bool send_camera_image_response(const CameraImageResponse &msg); #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA virtual void on_camera_image_request(const CameraImageRequest &value){}; #endif #ifdef USE_CLIMATE @@ -361,7 +361,7 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_SWITCH virtual void switch_command(const SwitchCommandRequest &msg) = 0; #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA virtual void camera_image(const CameraImageRequest &msg) = 0; #endif #ifdef USE_CLIMATE @@ -469,7 +469,7 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_SWITCH void on_switch_command_request(const SwitchCommandRequest &msg) override; #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA void on_camera_image_request(const CameraImageRequest &msg) override; #endif #ifdef USE_CLIMATE diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index f16b5a13cf..b353143e42 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -80,10 +80,10 @@ void APIServer::setup() { this->last_connected_ = millis(); -#ifdef USE_ESP32_CAMERA - if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { - esp32_camera::global_esp32_camera->add_image_callback( - [this](const std::shared_ptr &image) { +#ifdef USE_CAMERA + if (camera::Camera::global_camera != nullptr && !camera::Camera::global_camera->is_internal()) { + camera::Camera::global_camera->add_image_callback( + [this](const std::shared_ptr &image) { for (auto &c : this->clients_) { if (!c->remove_) c->send_camera_state(image); diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 8540c5cee8..0be0b84745 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -50,8 +50,8 @@ bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { return this->client_->send_list_entities_services_response(resp); } -#ifdef USE_ESP32_CAMERA -bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) { +#ifdef USE_CAMERA +bool ListEntitiesIterator::on_camera(camera::Camera *camera) { return this->client_->send_camera_info(camera); } #endif diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 1821bbee4c..689789410f 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -37,8 +37,8 @@ class ListEntitiesIterator : public ComponentIterator { bool on_text_sensor(text_sensor::TextSensor *text_sensor) override; #endif bool on_service(UserServiceDescriptor *service) override; -#ifdef USE_ESP32_CAMERA - bool on_camera(esp32_camera::ESP32Camera *camera) override; +#ifdef USE_CAMERA + bool on_camera(camera::Camera *camera) override; #endif #ifdef USE_CLIMATE bool on_climate(climate::Climate *climate) override; diff --git a/esphome/components/camera/__init__.py b/esphome/components/camera/__init__.py new file mode 100644 index 0000000000..7c887793e3 --- /dev/null +++ b/esphome/components/camera/__init__.py @@ -0,0 +1,7 @@ +import esphome.codegen as cg +import esphome.config_validation as cv + +CONFIG_SCHEMA = cv.Schema({}) + +async def to_code(config): + cg.add_define("USE_CAMERA") diff --git a/esphome/components/camera/camera.cpp b/esphome/components/camera/camera.cpp new file mode 100644 index 0000000000..680f786d3a --- /dev/null +++ b/esphome/components/camera/camera.cpp @@ -0,0 +1,13 @@ +#include "camera.h" + +namespace esphome { +namespace camera { + +Camera *Camera::global_camera = nullptr; + +Camera::Camera() { + global_camera = this; +} + +} // namespace camera +} // namespace esphome diff --git a/esphome/components/camera/camera.h b/esphome/components/camera/camera.h new file mode 100644 index 0000000000..4113d456ef --- /dev/null +++ b/esphome/components/camera/camera.h @@ -0,0 +1,76 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/entity_base.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace camera { + +/** Different sources for filtering. + * IDLE: Camera requests to send an image to the API. + * API_REQUESTER: API requests a new image. + * WEB_REQUESTER: ESP32 web server request an image. Ignored by API. + */ +enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER }; + +/** Abstract camera image base class. + * Encapsulates the JPEG encoded data and it is shared among + * all connected clients. + */ +class CameraImage { + public: + virtual uint8_t *get_data_buffer() = 0; + virtual size_t get_data_length() = 0; + virtual bool was_requested_by(CameraRequester requester) const = 0; + virtual ~CameraImage() {} +}; + +/** Abstract image reader base class. + * Keeps track of the data offset of the camera image and + * how many bytes are remaining to read. When the image + * is returned, the shared_ptr is reset and the camera can + * reuse the memory of the camera image. + */ +class CameraImageReader { + public: + virtual void set_image(std::shared_ptr image) = 0; + virtual size_t available() const = 0; + virtual uint8_t *peek_data_buffer() = 0; + virtual void consume_data(size_t consumed) = 0; + virtual void return_image() = 0; + virtual ~CameraImageReader() {} +}; + +/** Abstract camera base class. Collaborates with API. + * 1) API server starts and installs callback (add_image_callback) + * which is called by the camera when a new image is available. + * 2) New API client connects and creates a new image reader (create_image_reader). + * 3) API connection receives protobuf CameraImageRequest and calls request_image. + * 3.a) API connection receives protobuf CameraImageRequest and calls start_stream. + * 4) Camera implementation provides JPEG data in the CameraImage and calls callback. + * 5) API connection sets the image in the image reader. + * 6) API connection consumes data from the image reader and returns the image when finished. + * 7.a) Camera caputes new image and continues with 4) until start_stream is called. + */ +class Camera : public EntityBase { + public: + Camera(); + // Camera implementation invokes callback to publish a new image. + virtual void add_image_callback(std::function)> &&callback) = 0; + /// Returns a new camera image reader that keeps track of the JPEG data in the camera image. + virtual CameraImageReader* create_image_reader() = 0; + // Connection, camera or web server requests one new JPEG image. + virtual void request_image(CameraRequester requester) = 0; + // Connection, camera or web server requests a stream of images. + virtual void start_stream(CameraRequester requester) = 0; + // Connection or web server stops the previously started stream. + virtual void stop_stream(CameraRequester requester) = 0; + virtual ~Camera() {} + /// The singleton instance of the camera implementation. + static Camera *global_camera; +}; + +} // namespace camera +} // namespace esphome diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 2f1f9b90bb..a4b219767a 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -291,7 +291,7 @@ async def to_code(config): cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT])) cg.add(var.set_frame_size(config[CONF_RESOLUTION])) - cg.add_define("USE_ESP32_CAMERA") + cg.add_define("USE_CAMERA") if CORE.using_esp_idf: add_idf_component( diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index e9e9d3cffb..a9b8c9f261 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -13,8 +13,6 @@ static const char *const TAG = "esp32_camera"; /* ---------------- public API (derivated) ---------------- */ void ESP32Camera::setup() { - global_esp32_camera = this; - /* initialize time to now */ this->last_update_ = millis(); @@ -36,7 +34,7 @@ void ESP32Camera::setup() { xTaskCreatePinnedToCore(&ESP32Camera::framebuffer_task, "framebuffer_task", // name 1024, // stack size - nullptr, // task pv params + this, // task pv params 1, // priority nullptr, // handle 1 // core @@ -165,7 +163,7 @@ void ESP32Camera::loop() { const uint32_t now = millis(); if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) { this->last_idle_request_ = now; - this->request_image(IDLE); + this->request_image(camera::IDLE); } // Check if we should fetch a new image @@ -191,7 +189,7 @@ void ESP32Camera::loop() { xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY); return; } - this->current_image_ = std::make_shared(fb, this->single_requesters_ | this->stream_requesters_); + this->current_image_ = std::make_shared(fb, this->single_requesters_ | this->stream_requesters_); ESP_LOGD(TAG, "Got Image: len=%u", fb->len); this->new_image_callback_.call(this->current_image_); @@ -214,8 +212,6 @@ ESP32Camera::ESP32Camera() { this->config_.fb_count = 1; this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY; this->config_.fb_location = CAMERA_FB_IN_PSRAM; - - global_esp32_camera = this; } /* ---------------- setters ---------------- */ @@ -343,7 +339,7 @@ void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) { } /* ---------------- public API (specific) ---------------- */ -void ESP32Camera::add_image_callback(std::function)> &&callback) { +void ESP32Camera::add_image_callback(std::function)> &&callback) { this->new_image_callback_.add(std::move(callback)); } void ESP32Camera::add_stream_start_callback(std::function &&callback) { @@ -352,15 +348,18 @@ void ESP32Camera::add_stream_start_callback(std::function &&callback) { void ESP32Camera::add_stream_stop_callback(std::function &&callback) { this->stream_stop_callback_.add(std::move(callback)); } -void ESP32Camera::start_stream(CameraRequester requester) { +camera::CameraImageReader* ESP32Camera::create_image_reader() { + return new ESP32CameraImageReader; +} +void ESP32Camera::start_stream(camera::CameraRequester requester) { this->stream_start_callback_.call(); this->stream_requesters_ |= (1U << requester); } -void ESP32Camera::stop_stream(CameraRequester requester) { +void ESP32Camera::stop_stream(camera::CameraRequester requester) { this->stream_stop_callback_.call(); this->stream_requesters_ &= ~(1U << requester); } -void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= (1U << requester); } +void ESP32Camera::request_image(camera::CameraRequester requester) { this->single_requesters_ |= (1U << requester); } void ESP32Camera::update_camera_parameters() { sensor_t *s = esp_camera_sensor_get(); /* update image */ @@ -389,39 +388,38 @@ void ESP32Camera::update_camera_parameters() { bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; } bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; } void ESP32Camera::framebuffer_task(void *pv) { + ESP32Camera *that = (ESP32Camera *)pv; while (true) { camera_fb_t *framebuffer = esp_camera_fb_get(); - xQueueSend(global_esp32_camera->framebuffer_get_queue_, &framebuffer, portMAX_DELAY); + xQueueSend(that->framebuffer_get_queue_, &framebuffer, portMAX_DELAY); // return is no-op for config with 1 fb - xQueueReceive(global_esp32_camera->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); + xQueueReceive(that->framebuffer_return_queue_, &framebuffer, portMAX_DELAY); esp_camera_fb_return(framebuffer); } } -ESP32Camera *global_esp32_camera; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - /* ---------------- CameraImageReader class ---------------- */ -void CameraImageReader::set_image(std::shared_ptr image) { - this->image_ = std::move(image); +void ESP32CameraImageReader::set_image(std::shared_ptr image) { + this->image_ = std::static_pointer_cast(image); this->offset_ = 0; } -size_t CameraImageReader::available() const { +size_t ESP32CameraImageReader::available() const { if (!this->image_) return 0; return this->image_->get_data_length() - this->offset_; } -void CameraImageReader::return_image() { this->image_.reset(); } -void CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; } -uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; } +void ESP32CameraImageReader::return_image() { this->image_.reset(); } +void ESP32CameraImageReader::consume_data(size_t consumed) { this->offset_ += consumed; } +uint8_t *ESP32CameraImageReader::peek_data_buffer() { return this->image_->get_data_buffer() + this->offset_; } /* ---------------- CameraImage class ---------------- */ -CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} +ESP32CameraImage::ESP32CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} -camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; } -uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; } -size_t CameraImage::get_data_length() { return this->buffer_->len; } -bool CameraImage::was_requested_by(CameraRequester requester) const { +camera_fb_t *ESP32CameraImage::get_raw_buffer() { return this->buffer_; } +uint8_t *ESP32CameraImage::get_data_buffer() { return this->buffer_->buf; } +size_t ESP32CameraImage::get_data_length() { return this->buffer_->len; } +bool ESP32CameraImage::was_requested_by(camera::CameraRequester requester) const { return (this->requesters_ & (1 << requester)) != 0; } diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 71f47d3c06..d8f0486ea5 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -4,7 +4,7 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" -#include "esphome/core/entity_base.h" +#include "esphome/components/camera/camera.h" #include "esphome/core/helpers.h" #include #include @@ -15,9 +15,6 @@ namespace esp32_camera { class ESP32Camera; -/* ---------------- enum classes ---------------- */ -enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER }; - enum ESP32CameraFrameSize { ESP32_CAMERA_SIZE_160X120, // QQVGA ESP32_CAMERA_SIZE_176X144, // QCIF @@ -73,13 +70,13 @@ enum ESP32SpecialEffect { }; /* ---------------- CameraImage class ---------------- */ -class CameraImage { +class ESP32CameraImage : public camera::CameraImage { public: - CameraImage(camera_fb_t *buffer, uint8_t requester); + ESP32CameraImage(camera_fb_t *buffer, uint8_t requester); camera_fb_t *get_raw_buffer(); - uint8_t *get_data_buffer(); - size_t get_data_length(); - bool was_requested_by(CameraRequester requester) const; + uint8_t *get_data_buffer() override; + size_t get_data_length() override; + bool was_requested_by(camera::CameraRequester requester) const override; protected: camera_fb_t *buffer_; @@ -92,21 +89,21 @@ struct CameraImageData { }; /* ---------------- CameraImageReader class ---------------- */ -class CameraImageReader { +class ESP32CameraImageReader : public camera::CameraImageReader { public: - void set_image(std::shared_ptr image); - size_t available() const; - uint8_t *peek_data_buffer(); - void consume_data(size_t consumed); - void return_image(); + void set_image(std::shared_ptr image) override; + size_t available() const override; + uint8_t *peek_data_buffer() override; + void consume_data(size_t consumed) override; + void return_image() override; protected: - std::shared_ptr image_; + std::shared_ptr image_; size_t offset_{0}; }; /* ---------------- ESP32Camera class ---------------- */ -class ESP32Camera : public Component, public EntityBase { +class ESP32Camera : public Component, public camera::Camera { public: ESP32Camera(); @@ -155,15 +152,15 @@ class ESP32Camera : public Component, public EntityBase { void dump_config() override; float get_setup_priority() const override; /* public API (specific) */ - void start_stream(CameraRequester requester); - void stop_stream(CameraRequester requester); - void request_image(CameraRequester requester); + void start_stream(camera::CameraRequester requester); + void stop_stream(camera::CameraRequester requester); + void request_image(camera::CameraRequester requester); void update_camera_parameters(); - void add_image_callback(std::function)> &&callback); + void add_image_callback(std::function)> &&callback); void add_stream_start_callback(std::function &&callback); void add_stream_stop_callback(std::function &&callback); - + camera::CameraImageReader* create_image_reader() override; protected: /* internal methods */ bool has_requested_image_() const; @@ -199,12 +196,12 @@ class ESP32Camera : public Component, public EntityBase { uint32_t idle_update_interval_{15000}; esp_err_t init_error_{ESP_OK}; - std::shared_ptr current_image_; + std::shared_ptr current_image_; uint8_t single_requesters_{0}; uint8_t stream_requesters_{0}; QueueHandle_t framebuffer_get_queue_; QueueHandle_t framebuffer_return_queue_; - CallbackManager)> new_image_callback_{}; + CallbackManager)> new_image_callback_{}; CallbackManager stream_start_callback_{}; CallbackManager stream_stop_callback_{}; @@ -212,13 +209,10 @@ class ESP32Camera : public Component, public EntityBase { uint32_t last_update_{0}; }; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -extern ESP32Camera *global_esp32_camera; - class ESP32CameraImageTrigger : public Trigger { public: explicit ESP32CameraImageTrigger(ESP32Camera *parent) { - parent->add_image_callback([this](const std::shared_ptr &image) { + parent->add_image_callback([this](const std::shared_ptr &image) { CameraImageData camera_image_data{}; camera_image_data.length = image->get_data_length(); camera_image_data.data = image->get_data_buffer(); diff --git a/esphome/components/esp32_camera_web_server/__init__.py b/esphome/components/esp32_camera_web_server/__init__.py index d8afea27b4..37598c038b 100644 --- a/esphome/components/esp32_camera_web_server/__init__.py +++ b/esphome/components/esp32_camera_web_server/__init__.py @@ -3,7 +3,7 @@ import esphome.codegen as cg from esphome.const import CONF_ID, CONF_PORT, CONF_MODE CODEOWNERS = ["@ayufan"] -DEPENDENCIES = ["esp32_camera"] +DEPENDENCIES = ["camera"] MULTI_CONF = True esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server") diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index 7ca0c56d23..547a5c5361 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -40,7 +40,7 @@ CameraWebServer::CameraWebServer() {} CameraWebServer::~CameraWebServer() {} void CameraWebServer::setup() { - if (!esp32_camera::global_esp32_camera || esp32_camera::global_esp32_camera->is_failed()) { + if (!camera::Camera::global_camera) { this->mark_failed(); return; } @@ -67,8 +67,8 @@ void CameraWebServer::setup() { httpd_register_uri_handler(this->httpd_, &uri); - esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr image) { - if (this->running_ && image->was_requested_by(esp32_camera::WEB_REQUESTER)) { + camera::Camera::global_camera->add_image_callback([this](std::shared_ptr image) { + if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) { this->image_ = std::move(image); xSemaphoreGive(this->semaphore_); } @@ -106,8 +106,8 @@ void CameraWebServer::loop() { } } -std::shared_ptr CameraWebServer::wait_for_image_() { - std::shared_ptr image; +std::shared_ptr CameraWebServer::wait_for_image_() { + std::shared_ptr image; image.swap(this->image_); if (!image) { @@ -170,7 +170,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { uint32_t last_frame = millis(); uint32_t frames = 0; - esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::WEB_REQUESTER); + camera::Camera::global_camera->start_stream(esphome::camera::WEB_REQUESTER); while (res == ESP_OK && this->running_) { auto image = this->wait_for_image_(); @@ -203,7 +203,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR)); } - esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::WEB_REQUESTER); + camera::Camera::global_camera->stop_stream(esphome::camera::WEB_REQUESTER); ESP_LOGI(TAG, "STREAM: closed. Frames: %" PRIu32, frames); @@ -213,7 +213,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { esp_err_t CameraWebServer::snapshot_handler_(struct httpd_req *req) { esp_err_t res = ESP_OK; - esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::WEB_REQUESTER); + camera::Camera::global_camera->request_image(esphome::camera::WEB_REQUESTER); auto image = this->wait_for_image_(); diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.h b/esphome/components/esp32_camera_web_server/camera_web_server.h index f65625554c..99c816de27 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.h +++ b/esphome/components/esp32_camera_web_server/camera_web_server.h @@ -6,7 +6,7 @@ #include #include -#include "esphome/components/esp32_camera/esp32_camera.h" +#include "esphome/components/camera/camera.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" @@ -32,7 +32,7 @@ class CameraWebServer : public Component { void loop() override; protected: - std::shared_ptr wait_for_image_(); + std::shared_ptr wait_for_image_(); esp_err_t handler_(struct httpd_req *req); esp_err_t streaming_handler_(struct httpd_req *req); esp_err_t snapshot_handler_(struct httpd_req *req); @@ -40,7 +40,7 @@ class CameraWebServer : public Component { uint16_t port_{0}; void *httpd_{nullptr}; SemaphoreHandle_t semaphore_; - std::shared_ptr image_; + std::shared_ptr image_; bool running_{false}; Mode mode_{STREAM}; }; diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index da593340c1..c68daa04e5 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -158,16 +158,16 @@ void ComponentIterator::advance() { } break; #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA case IteratorState::CAMERA: - if (esp32_camera::global_esp32_camera == nullptr) { + if (camera::Camera::global_camera == nullptr) { advance_platform = true; } else { - if (esp32_camera::global_esp32_camera->is_internal() && !this->include_internal_) { + if (camera::Camera::global_camera->is_internal() && !this->include_internal_) { advance_platform = success = true; break; } else { - advance_platform = success = this->on_camera(esp32_camera::global_esp32_camera); + advance_platform = success = this->on_camera(camera::Camera::global_camera); } } break; @@ -386,8 +386,8 @@ bool ComponentIterator::on_begin() { return true; } #ifdef USE_API bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; } #endif -#ifdef USE_ESP32_CAMERA -bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; } +#ifdef USE_CAMERA +bool ComponentIterator::on_camera(camera::Camera *camera) { return true; } #endif #ifdef USE_MEDIA_PLAYER bool ComponentIterator::on_media_player(media_player::MediaPlayer *media_player) { return true; } diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index 9e187f6c57..47ba77e249 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -4,8 +4,8 @@ #include "esphome/core/controller.h" #include "esphome/core/helpers.h" -#ifdef USE_ESP32_CAMERA -#include "esphome/components/esp32_camera/esp32_camera.h" +#ifdef USE_CAMERA +#include "esphome/components/camera/camera.h" #endif namespace esphome { @@ -48,8 +48,8 @@ class ComponentIterator { #ifdef USE_API virtual bool on_service(api::UserServiceDescriptor *service); #endif -#ifdef USE_ESP32_CAMERA - virtual bool on_camera(esp32_camera::ESP32Camera *camera); +#ifdef USE_CAMERA + virtual bool on_camera(camera::Camera *camera); #endif #ifdef USE_CLIMATE virtual bool on_climate(climate::Climate *climate) = 0; @@ -123,7 +123,7 @@ class ComponentIterator { #ifdef USE_API SERVICE, #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA CAMERA, #endif #ifdef USE_CLIMATE diff --git a/esphome/core/defines.h b/esphome/core/defines.h index b5511b57eb..1655a54c00 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -102,7 +102,7 @@ #define USE_ESP32_BLE #define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_SERVER -#define USE_ESP32_CAMERA +#define USE_CAMERA #define USE_IMPROV #define USE_MICRO_WAKE_WORD_VAD #define USE_MICROPHONE