mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Extend esp32_camera with requester to improve performance (#2813)
This commit is contained in:
parent
9a70bfa471
commit
5844c1767b
4 changed files with 47 additions and 40 deletions
|
@ -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_();
|
||||
|
||||
|
|
Loading…
Reference in a new issue