Extend esp32_camera with requester to improve performance (#2813)

This commit is contained in:
Kamil Trzciński 2022-01-09 23:58:49 +01:00 committed by GitHub
parent 9a70bfa471
commit 5844c1767b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 40 deletions

View File

@ -20,6 +20,7 @@ namespace esphome {
namespace api { namespace api {
static const char *const TAG = "api.connection"; 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) APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
@ -704,6 +705,8 @@ void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage>
return; return;
if (this->image_reader_.available()) if (this->image_reader_.available())
return; 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)); this->image_reader_.set_image(std::move(image));
} }
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
@ -722,9 +725,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
return; return;
if (msg.single) if (msg.single)
esp32_camera::global_esp32_camera->request_image(); esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
if (msg.stream) if (msg.stream) {
esp32_camera::global_esp32_camera->request_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 #endif

View File

@ -133,6 +133,13 @@ void ESP32Camera::loop() {
this->current_image_.reset(); 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 // Check if we should fetch a new image
if (!this->has_requested_image_()) if (!this->has_requested_image_())
return; return;
@ -140,7 +147,6 @@ void ESP32Camera::loop() {
// image is still in use // image is still in use
return; return;
} }
const uint32_t now = millis();
if (now - this->last_update_ <= this->max_update_interval_) if (now - this->last_update_ <= this->max_update_interval_)
return; return;
@ -157,12 +163,12 @@ void ESP32Camera::loop() {
xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY); xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
return; 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); ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
this->new_image_callback_.call(this->current_image_); this->new_image_callback_.call(this->current_image_);
this->last_update_ = now; this->last_update_ = now;
this->single_requester_ = false; this->single_requesters_ = 0;
} }
void ESP32Camera::framebuffer_task(void *pv) { void ESP32Camera::framebuffer_task(void *pv) {
while (true) { while (true) {
@ -258,24 +264,10 @@ void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightnes
void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; } void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; }
float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; } float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
uint32_t ESP32Camera::hash_base() { return 3010542557UL; } uint32_t ESP32Camera::hash_base() { return 3010542557UL; }
void ESP32Camera::request_image() { this->single_requester_ = true; } void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= 1 << requester; }
void ESP32Camera::request_stream() { this->last_stream_request_ = millis(); } void ESP32Camera::start_stream(CameraRequester requester) { this->stream_requesters_ |= 1 << requester; }
bool ESP32Camera::has_requested_image_() const { void ESP32Camera::stop_stream(CameraRequester requester) { this->stream_requesters_ &= ~(1 << requester); }
if (this->single_requester_) bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; }
// 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;
}
bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; } bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) { void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) {
this->max_update_interval_ = 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_; } camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; }
uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; } uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; }
size_t CameraImage::get_data_length() { return this->buffer_->len; } 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 esp32_camera
} // namespace esphome } // namespace esphome

View File

@ -14,15 +14,19 @@ namespace esp32_camera {
class ESP32Camera; class ESP32Camera;
enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER };
class CameraImage { class CameraImage {
public: public:
CameraImage(camera_fb_t *buffer); CameraImage(camera_fb_t *buffer, uint8_t requester);
camera_fb_t *get_raw_buffer(); camera_fb_t *get_raw_buffer();
uint8_t *get_data_buffer(); uint8_t *get_data_buffer();
size_t get_data_length(); size_t get_data_length();
bool was_requested_by(CameraRequester requester) const;
protected: protected:
camera_fb_t *buffer_; camera_fb_t *buffer_;
uint8_t requesters_;
}; };
class CameraImageReader { class CameraImageReader {
@ -81,8 +85,9 @@ class ESP32Camera : public Component, public EntityBase {
void dump_config() override; void dump_config() override;
void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&f); void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&f);
float get_setup_priority() const override; float get_setup_priority() const override;
void request_stream(); void start_stream(CameraRequester requester);
void request_image(); void stop_stream(CameraRequester requester);
void request_image(CameraRequester requester);
protected: protected:
uint32_t hash_base() override; uint32_t hash_base() override;
@ -104,13 +109,14 @@ class ESP32Camera : public Component, public EntityBase {
esp_err_t init_error_{ESP_OK}; esp_err_t init_error_{ESP_OK};
std::shared_ptr<CameraImage> current_image_; std::shared_ptr<CameraImage> current_image_;
uint32_t last_stream_request_{0}; uint8_t single_requesters_{0};
bool single_requester_{false}; uint8_t stream_requesters_{0};
QueueHandle_t framebuffer_get_queue_; QueueHandle_t framebuffer_get_queue_;
QueueHandle_t framebuffer_return_queue_; QueueHandle_t framebuffer_return_queue_;
CallbackManager<void(std::shared_ptr<CameraImage>)> new_image_callback_; CallbackManager<void(std::shared_ptr<CameraImage>)> new_image_callback_;
uint32_t max_update_interval_{1000}; uint32_t max_update_interval_{1000};
uint32_t idle_update_interval_{15000}; uint32_t idle_update_interval_{15000};
uint32_t last_idle_request_{0};
uint32_t last_update_{0}; uint32_t last_update_{0};
}; };

View File

@ -14,7 +14,7 @@
namespace esphome { namespace esphome {
namespace esp32_camera_web_server { 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"; static const char *const TAG = "esp32_camera_web_server";
#define PART_BOUNDARY "123456789000000000000987654321" #define PART_BOUNDARY "123456789000000000000987654321"
@ -68,7 +68,7 @@ void CameraWebServer::setup() {
httpd_register_uri_handler(this->httpd_, &uri); httpd_register_uri_handler(this->httpd_, &uri);
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) { 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); this->image_ = std::move(image);
xSemaphoreGive(this->semaphore_); xSemaphoreGive(this->semaphore_);
} }
@ -169,11 +169,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
uint32_t last_frame = millis(); uint32_t last_frame = millis();
uint32_t frames = 0; uint32_t frames = 0;
while (res == ESP_OK && this->running_) { esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::WEB_REQUESTER);
if (esp32_camera::global_esp32_camera != nullptr) {
esp32_camera::global_esp32_camera->request_stream();
}
while (res == ESP_OK && this->running_) {
auto image = this->wait_for_image_(); auto image = this->wait_for_image_();
if (!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)); 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); ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
return res; 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 CameraWebServer::snapshot_handler_(struct httpd_req *req) {
esp_err_t res = ESP_OK; esp_err_t res = ESP_OK;
if (esp32_camera::global_esp32_camera != nullptr) { esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::WEB_REQUESTER);
esp32_camera::global_esp32_camera->request_image();
}
auto image = this->wait_for_image_(); auto image = this->wait_for_image_();