mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Extend esp32_camera with requester to improve performance (#2813)
This commit is contained in:
		| @@ -20,6 +20,7 @@ namespace esphome { | ||||
| namespace api { | ||||
|  | ||||
| static const char *const TAG = "api.connection"; | ||||
| static const int ESP32_CAMERA_STOP_STREAM = 5000; | ||||
|  | ||||
| APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent) | ||||
|     : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { | ||||
| @@ -704,7 +705,9 @@ void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> | ||||
|     return; | ||||
|   if (this->image_reader_.available()) | ||||
|     return; | ||||
|   this->image_reader_.set_image(std::move(image)); | ||||
|   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)); | ||||
| } | ||||
| bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { | ||||
|   ListEntitiesCameraResponse msg; | ||||
| @@ -722,9 +725,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) { | ||||
|     return; | ||||
|  | ||||
|   if (msg.single) | ||||
|     esp32_camera::global_esp32_camera->request_image(); | ||||
|   if (msg.stream) | ||||
|     esp32_camera::global_esp32_camera->request_stream(); | ||||
|     esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER); | ||||
|   if (msg.stream) { | ||||
|     esp32_camera::global_esp32_camera->start_stream(esphome::esp32_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); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -133,6 +133,13 @@ void ESP32Camera::loop() { | ||||
|     this->current_image_.reset(); | ||||
|   } | ||||
|  | ||||
|   // request idle image every idle_update_interval | ||||
|   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); | ||||
|   } | ||||
|  | ||||
|   // Check if we should fetch a new image | ||||
|   if (!this->has_requested_image_()) | ||||
|     return; | ||||
| @@ -140,7 +147,6 @@ void ESP32Camera::loop() { | ||||
|     // image is still in use | ||||
|     return; | ||||
|   } | ||||
|   const uint32_t now = millis(); | ||||
|   if (now - this->last_update_ <= this->max_update_interval_) | ||||
|     return; | ||||
|  | ||||
| @@ -157,12 +163,12 @@ void ESP32Camera::loop() { | ||||
|     xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY); | ||||
|     return; | ||||
|   } | ||||
|   this->current_image_ = std::make_shared<CameraImage>(fb); | ||||
|   this->current_image_ = std::make_shared<CameraImage>(fb, this->single_requesters_ | this->stream_requesters_); | ||||
|  | ||||
|   ESP_LOGD(TAG, "Got Image: len=%u", fb->len); | ||||
|   this->new_image_callback_.call(this->current_image_); | ||||
|   this->last_update_ = now; | ||||
|   this->single_requester_ = false; | ||||
|   this->single_requesters_ = 0; | ||||
| } | ||||
| void ESP32Camera::framebuffer_task(void *pv) { | ||||
|   while (true) { | ||||
| @@ -258,24 +264,10 @@ void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightnes | ||||
| void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; } | ||||
| float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; } | ||||
| uint32_t ESP32Camera::hash_base() { return 3010542557UL; } | ||||
| void ESP32Camera::request_image() { this->single_requester_ = true; } | ||||
| void ESP32Camera::request_stream() { this->last_stream_request_ = millis(); } | ||||
| bool ESP32Camera::has_requested_image_() const { | ||||
|   if (this->single_requester_) | ||||
|     // single request | ||||
|     return true; | ||||
|  | ||||
|   uint32_t now = millis(); | ||||
|   if (now - this->last_stream_request_ < 5000) | ||||
|     // stream request | ||||
|     return true; | ||||
|  | ||||
|   if (this->idle_update_interval_ != 0 && now - this->last_update_ > this->idle_update_interval_) | ||||
|     // idle update | ||||
|     return true; | ||||
|  | ||||
|   return false; | ||||
| } | ||||
| void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= 1 << requester; } | ||||
| void ESP32Camera::start_stream(CameraRequester requester) { this->stream_requesters_ |= 1 << requester; } | ||||
| void ESP32Camera::stop_stream(CameraRequester requester) { this->stream_requesters_ &= ~(1 << requester); } | ||||
| 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::set_max_update_interval(uint32_t max_update_interval) { | ||||
|   this->max_update_interval_ = max_update_interval; | ||||
| @@ -304,7 +296,10 @@ uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_b | ||||
| 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; } | ||||
| CameraImage::CameraImage(camera_fb_t *buffer) : buffer_(buffer) {} | ||||
| bool CameraImage::was_requested_by(CameraRequester requester) const { | ||||
|   return (this->requesters_ & (1 << requester)) != 0; | ||||
| } | ||||
| CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {} | ||||
|  | ||||
| }  // namespace esp32_camera | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -14,15 +14,19 @@ namespace esp32_camera { | ||||
|  | ||||
| class ESP32Camera; | ||||
|  | ||||
| enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER }; | ||||
|  | ||||
| class CameraImage { | ||||
|  public: | ||||
|   CameraImage(camera_fb_t *buffer); | ||||
|   CameraImage(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; | ||||
|  | ||||
|  protected: | ||||
|   camera_fb_t *buffer_; | ||||
|   uint8_t requesters_; | ||||
| }; | ||||
|  | ||||
| class CameraImageReader { | ||||
| @@ -81,8 +85,9 @@ class ESP32Camera : public Component, public EntityBase { | ||||
|   void dump_config() override; | ||||
|   void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&f); | ||||
|   float get_setup_priority() const override; | ||||
|   void request_stream(); | ||||
|   void request_image(); | ||||
|   void start_stream(CameraRequester requester); | ||||
|   void stop_stream(CameraRequester requester); | ||||
|   void request_image(CameraRequester requester); | ||||
|  | ||||
|  protected: | ||||
|   uint32_t hash_base() override; | ||||
| @@ -104,13 +109,14 @@ class ESP32Camera : public Component, public EntityBase { | ||||
|  | ||||
|   esp_err_t init_error_{ESP_OK}; | ||||
|   std::shared_ptr<CameraImage> current_image_; | ||||
|   uint32_t last_stream_request_{0}; | ||||
|   bool single_requester_{false}; | ||||
|   uint8_t single_requesters_{0}; | ||||
|   uint8_t stream_requesters_{0}; | ||||
|   QueueHandle_t framebuffer_get_queue_; | ||||
|   QueueHandle_t framebuffer_return_queue_; | ||||
|   CallbackManager<void(std::shared_ptr<CameraImage>)> new_image_callback_; | ||||
|   uint32_t max_update_interval_{1000}; | ||||
|   uint32_t idle_update_interval_{15000}; | ||||
|   uint32_t last_idle_request_{0}; | ||||
|   uint32_t last_update_{0}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
| namespace esphome { | ||||
| namespace esp32_camera_web_server { | ||||
|  | ||||
| static const int IMAGE_REQUEST_TIMEOUT = 2000; | ||||
| static const int IMAGE_REQUEST_TIMEOUT = 5000; | ||||
| static const char *const TAG = "esp32_camera_web_server"; | ||||
|  | ||||
| #define PART_BOUNDARY "123456789000000000000987654321" | ||||
| @@ -68,7 +68,7 @@ void CameraWebServer::setup() { | ||||
|   httpd_register_uri_handler(this->httpd_, &uri); | ||||
|  | ||||
|   esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) { | ||||
|     if (this->running_) { | ||||
|     if (this->running_ && image->was_requested_by(esp32_camera::WEB_REQUESTER)) { | ||||
|       this->image_ = std::move(image); | ||||
|       xSemaphoreGive(this->semaphore_); | ||||
|     } | ||||
| @@ -169,11 +169,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) { | ||||
|   uint32_t last_frame = millis(); | ||||
|   uint32_t frames = 0; | ||||
|  | ||||
|   while (res == ESP_OK && this->running_) { | ||||
|     if (esp32_camera::global_esp32_camera != nullptr) { | ||||
|       esp32_camera::global_esp32_camera->request_stream(); | ||||
|     } | ||||
|   esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::WEB_REQUESTER); | ||||
|  | ||||
|   while (res == ESP_OK && this->running_) { | ||||
|     auto image = this->wait_for_image_(); | ||||
|  | ||||
|     if (!image) { | ||||
| @@ -204,6 +202,8 @@ 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); | ||||
|  | ||||
|   ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames); | ||||
|  | ||||
|   return res; | ||||
| @@ -212,9 +212,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; | ||||
|  | ||||
|   if (esp32_camera::global_esp32_camera != nullptr) { | ||||
|     esp32_camera::global_esp32_camera->request_image(); | ||||
|   } | ||||
|   esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::WEB_REQUESTER); | ||||
|  | ||||
|   auto image = this->wait_for_image_(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user