mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 00:31:58 +00:00
[esp32_camera] Replace std::function callbacks with CameraListener interface (#12165)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -107,12 +107,7 @@ void APIServer::setup() {
|
||||
|
||||
#ifdef USE_CAMERA
|
||||
if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) {
|
||||
camera::Camera::instance()->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
|
||||
for (auto &c : this->clients_) {
|
||||
if (!c->flags_.remove)
|
||||
c->set_camera_state(image);
|
||||
}
|
||||
});
|
||||
camera::Camera::instance()->add_listener(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -544,6 +539,15 @@ void APIServer::on_log(uint8_t level, const char *tag, const char *message, size
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CAMERA
|
||||
void APIServer::on_camera_image(const std::shared_ptr<camera::CameraImage> &image) {
|
||||
for (auto &c : this->clients_) {
|
||||
if (!c->flags_.remove)
|
||||
c->set_camera_state(image);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void APIServer::on_shutdown() {
|
||||
this->shutting_down_ = true;
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
#include "esphome/components/camera/camera.h"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -36,6 +39,10 @@ class APIServer : public Component,
|
||||
,
|
||||
public logger::LogListener
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
,
|
||||
public camera::CameraListener
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
APIServer();
|
||||
@@ -49,6 +56,9 @@ class APIServer : public Component,
|
||||
#ifdef USE_LOGGER
|
||||
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override;
|
||||
#endif
|
||||
#ifdef USE_API_PASSWORD
|
||||
bool check_password(const uint8_t *password_data, size_t password_len) const;
|
||||
void set_password(const std::string &password);
|
||||
|
||||
@@ -35,6 +35,21 @@ inline const char *to_string(PixelFormat format) {
|
||||
return "PIXEL_FORMAT_UNKNOWN";
|
||||
}
|
||||
|
||||
// Forward declaration
|
||||
class CameraImage;
|
||||
|
||||
/** Listener interface for camera events.
|
||||
*
|
||||
* Components can implement this interface to receive camera notifications
|
||||
* (new images, stream start/stop) without the overhead of std::function callbacks.
|
||||
*/
|
||||
class CameraListener {
|
||||
public:
|
||||
virtual void on_camera_image(const std::shared_ptr<CameraImage> &image) {}
|
||||
virtual void on_stream_start() {}
|
||||
virtual void on_stream_stop() {}
|
||||
};
|
||||
|
||||
/** Abstract camera image base class.
|
||||
* Encapsulates the JPEG encoded data and it is shared among
|
||||
* all connected clients.
|
||||
@@ -87,12 +102,12 @@ struct CameraImageSpec {
|
||||
};
|
||||
|
||||
/** 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.
|
||||
* 1) API server starts and registers as a listener (add_listener)
|
||||
* to receive new images from the camera.
|
||||
* 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.
|
||||
* 4) Camera implementation provides JPEG data in the CameraImage and notifies listeners.
|
||||
* 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 captures a new image and continues with 4) until start_stream is called.
|
||||
@@ -100,8 +115,8 @@ struct CameraImageSpec {
|
||||
class Camera : public EntityBase, public Component {
|
||||
public:
|
||||
Camera();
|
||||
// Camera implementation invokes callback to publish a new image.
|
||||
virtual void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&callback) = 0;
|
||||
/// Add a listener to receive camera events
|
||||
virtual void add_listener(CameraListener *listener) = 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.
|
||||
|
||||
@@ -205,7 +205,9 @@ void ESP32Camera::loop() {
|
||||
this->current_image_ = std::make_shared<ESP32CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
|
||||
|
||||
ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
|
||||
this->new_image_callback_.call(this->current_image_);
|
||||
for (auto *listener : this->listeners_) {
|
||||
listener->on_camera_image(this->current_image_);
|
||||
}
|
||||
this->last_update_ = now;
|
||||
this->single_requesters_ = 0;
|
||||
}
|
||||
@@ -357,21 +359,16 @@ void ESP32Camera::set_frame_buffer_location(camera_fb_location_t fb_location) {
|
||||
}
|
||||
|
||||
/* ---------------- public API (specific) ---------------- */
|
||||
void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) {
|
||||
this->new_image_callback_.add(std::move(callback));
|
||||
}
|
||||
void ESP32Camera::add_stream_start_callback(std::function<void()> &&callback) {
|
||||
this->stream_start_callback_.add(std::move(callback));
|
||||
}
|
||||
void ESP32Camera::add_stream_stop_callback(std::function<void()> &&callback) {
|
||||
this->stream_stop_callback_.add(std::move(callback));
|
||||
}
|
||||
void ESP32Camera::start_stream(camera::CameraRequester requester) {
|
||||
this->stream_start_callback_.call();
|
||||
for (auto *listener : this->listeners_) {
|
||||
listener->on_stream_start();
|
||||
}
|
||||
this->stream_requesters_ |= (1U << requester);
|
||||
}
|
||||
void ESP32Camera::stop_stream(camera::CameraRequester requester) {
|
||||
this->stream_stop_callback_.call();
|
||||
for (auto *listener : this->listeners_) {
|
||||
listener->on_stream_stop();
|
||||
}
|
||||
this->stream_requesters_ &= ~(1U << requester);
|
||||
}
|
||||
void ESP32Camera::request_image(camera::CameraRequester requester) { this->single_requesters_ |= (1U << requester); }
|
||||
|
||||
@@ -165,9 +165,8 @@ class ESP32Camera : public camera::Camera {
|
||||
void request_image(camera::CameraRequester requester) override;
|
||||
void update_camera_parameters();
|
||||
|
||||
void add_image_callback(std::function<void(std::shared_ptr<camera::CameraImage>)> &&callback) override;
|
||||
void add_stream_start_callback(std::function<void()> &&callback);
|
||||
void add_stream_stop_callback(std::function<void()> &&callback);
|
||||
/// Add a listener to receive camera events
|
||||
void add_listener(camera::CameraListener *listener) override { this->listeners_.push_back(listener); }
|
||||
camera::CameraImageReader *create_image_reader() override;
|
||||
|
||||
protected:
|
||||
@@ -210,9 +209,7 @@ class ESP32Camera : public camera::Camera {
|
||||
uint8_t stream_requesters_{0};
|
||||
QueueHandle_t framebuffer_get_queue_;
|
||||
QueueHandle_t framebuffer_return_queue_;
|
||||
CallbackManager<void(std::shared_ptr<camera::CameraImage>)> new_image_callback_{};
|
||||
CallbackManager<void()> stream_start_callback_{};
|
||||
CallbackManager<void()> stream_stop_callback_{};
|
||||
std::vector<camera::CameraListener *> listeners_;
|
||||
|
||||
uint32_t last_idle_request_{0};
|
||||
uint32_t last_update_{0};
|
||||
@@ -221,33 +218,27 @@ class ESP32Camera : public camera::Camera {
|
||||
#endif // USE_I2C
|
||||
};
|
||||
|
||||
class ESP32CameraImageTrigger : public Trigger<CameraImageData> {
|
||||
class ESP32CameraImageTrigger : public Trigger<CameraImageData>, public camera::CameraListener {
|
||||
public:
|
||||
explicit ESP32CameraImageTrigger(ESP32Camera *parent) {
|
||||
parent->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
|
||||
CameraImageData camera_image_data{};
|
||||
camera_image_data.length = image->get_data_length();
|
||||
camera_image_data.data = image->get_data_buffer();
|
||||
this->trigger(camera_image_data);
|
||||
});
|
||||
explicit ESP32CameraImageTrigger(ESP32Camera *parent) { parent->add_listener(this); }
|
||||
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override {
|
||||
CameraImageData camera_image_data{};
|
||||
camera_image_data.length = image->get_data_length();
|
||||
camera_image_data.data = image->get_data_buffer();
|
||||
this->trigger(camera_image_data);
|
||||
}
|
||||
};
|
||||
|
||||
class ESP32CameraStreamStartTrigger : public Trigger<> {
|
||||
class ESP32CameraStreamStartTrigger : public Trigger<>, public camera::CameraListener {
|
||||
public:
|
||||
explicit ESP32CameraStreamStartTrigger(ESP32Camera *parent) {
|
||||
parent->add_stream_start_callback([this]() { this->trigger(); });
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ESP32CameraStreamStartTrigger(ESP32Camera *parent) { parent->add_listener(this); }
|
||||
void on_stream_start() override { this->trigger(); }
|
||||
};
|
||||
class ESP32CameraStreamStopTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit ESP32CameraStreamStopTrigger(ESP32Camera *parent) {
|
||||
parent->add_stream_stop_callback([this]() { this->trigger(); });
|
||||
}
|
||||
|
||||
protected:
|
||||
class ESP32CameraStreamStopTrigger : public Trigger<>, public camera::CameraListener {
|
||||
public:
|
||||
explicit ESP32CameraStreamStopTrigger(ESP32Camera *parent) { parent->add_listener(this); }
|
||||
void on_stream_stop() override { this->trigger(); }
|
||||
};
|
||||
|
||||
} // namespace esp32_camera
|
||||
|
||||
@@ -67,12 +67,14 @@ void CameraWebServer::setup() {
|
||||
|
||||
httpd_register_uri_handler(this->httpd_, &uri);
|
||||
|
||||
camera::Camera::instance()->add_image_callback([this](std::shared_ptr<camera::CameraImage> image) {
|
||||
if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) {
|
||||
this->image_ = std::move(image);
|
||||
xSemaphoreGive(this->semaphore_);
|
||||
}
|
||||
});
|
||||
camera::Camera::instance()->add_listener(this);
|
||||
}
|
||||
|
||||
void CameraWebServer::on_camera_image(const std::shared_ptr<camera::CameraImage> &image) {
|
||||
if (this->running_ && image->was_requested_by(camera::WEB_REQUESTER)) {
|
||||
this->image_ = image;
|
||||
xSemaphoreGive(this->semaphore_);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraWebServer::on_shutdown() {
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace esp32_camera_web_server {
|
||||
|
||||
enum Mode { STREAM, SNAPSHOT };
|
||||
|
||||
class CameraWebServer : public Component {
|
||||
class CameraWebServer : public Component, public camera::CameraListener {
|
||||
public:
|
||||
CameraWebServer();
|
||||
~CameraWebServer();
|
||||
@@ -31,6 +31,9 @@ class CameraWebServer : public Component {
|
||||
void set_mode(Mode mode) { this->mode_ = mode; }
|
||||
void loop() override;
|
||||
|
||||
/// CameraListener interface
|
||||
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<camera::CameraImage> wait_for_image_();
|
||||
esp_err_t handler_(struct httpd_req *req);
|
||||
|
||||
Reference in New Issue
Block a user