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 {
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,6 +705,8 @@ void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage>
return;
if (this->image_reader_.available())
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));
}
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
@ -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

View file

@ -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

View file

@ -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};
};

View file

@ -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_();