fix: voice interupt
This commit is contained in:
177
main/background_capture_service.cc
Normal file
177
main/background_capture_service.cc
Normal file
@ -0,0 +1,177 @@
|
||||
#include "background_capture_service.h"
|
||||
|
||||
#include "board.h"
|
||||
#include "camera.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "BgCapture"
|
||||
|
||||
BackgroundCaptureService::BackgroundCaptureService() = default;
|
||||
|
||||
BackgroundCaptureService::~BackgroundCaptureService() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void BackgroundCaptureService::Start() {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
if (running_.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = xTaskCreate(
|
||||
&BackgroundCaptureService::TaskEntry,
|
||||
"bg_capture",
|
||||
CONFIG_BACKGROUND_CAPTURE_TASK_STACK_SIZE,
|
||||
this,
|
||||
CONFIG_BACKGROUND_CAPTURE_TASK_PRIORITY,
|
||||
&task_handle_);
|
||||
if (result != pdPASS) {
|
||||
running_.store(false);
|
||||
task_handle_ = nullptr;
|
||||
ESP_LOGE(TAG, "Failed to create background capture task");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackgroundCaptureService::Stop() {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
if (!running_.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (task_handle_ != nullptr) {
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackgroundCaptureService::TaskEntry(void* arg) {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
auto* service = static_cast<BackgroundCaptureService*>(arg);
|
||||
service->Run();
|
||||
service->task_handle_ = nullptr;
|
||||
#else
|
||||
(void)arg;
|
||||
#endif
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
void BackgroundCaptureService::Run() {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
ESP_LOGI(TAG, "Background capture task started");
|
||||
|
||||
while (running_.load()) {
|
||||
if (!CaptureAndSendFrame()) {
|
||||
consecutive_failures_++;
|
||||
auto delay_ms = GetFailureDelayMs();
|
||||
ESP_LOGW(TAG, "Background capture retry in %u ms, failures=%u",
|
||||
delay_ms, consecutive_failures_);
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
continue;
|
||||
}
|
||||
|
||||
consecutive_failures_ = 0;
|
||||
vTaskDelay(pdMS_TO_TICKS(CONFIG_BACKGROUND_CAPTURE_FRAME_INTERVAL_MS));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Background capture task stopped");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BackgroundCaptureService::CaptureAndSendFrame() {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
const size_t free_internal_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
if (free_internal_heap < CONFIG_BACKGROUND_CAPTURE_MIN_FREE_INTERNAL_HEAP) {
|
||||
ESP_LOGW(TAG, "Skip background capture, low internal heap: free=%u threshold=%u",
|
||||
static_cast<unsigned>(free_internal_heap),
|
||||
static_cast<unsigned>(CONFIG_BACKGROUND_CAPTURE_MIN_FREE_INTERNAL_HEAP));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto camera = Board::GetInstance().GetCamera();
|
||||
if (camera == nullptr) {
|
||||
ESP_LOGW(TAG, "No camera available for background capture");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string jpeg_data;
|
||||
if (!camera->CaptureToJpeg(jpeg_data, false)) {
|
||||
ESP_LOGW(TAG, "Failed to capture background frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jpeg_data.empty()) {
|
||||
ESP_LOGW(TAG, "Captured empty background frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
return UploadJpegFrame(jpeg_data);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t BackgroundCaptureService::GetFailureDelayMs() const {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
const uint32_t base_delay_ms = CONFIG_BACKGROUND_CAPTURE_RETRY_INTERVAL_MS;
|
||||
const uint32_t max_delay_ms = CONFIG_BACKGROUND_CAPTURE_MAX_BACKOFF_MS;
|
||||
const uint32_t shift = std::min<uint32_t>(consecutive_failures_ - 1, 4);
|
||||
return std::min<uint32_t>(base_delay_ms << shift, max_delay_ms);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BackgroundCaptureService::UploadJpegFrame(const std::string& jpeg_data) {
|
||||
#if CONFIG_BACKGROUND_CAPTURE_ENABLE
|
||||
const std::string url = CONFIG_BACKGROUND_CAPTURE_UPLOAD_URL;
|
||||
if (url.empty()) {
|
||||
ESP_LOGI(TAG, "Captured background frame: %u bytes", static_cast<unsigned>(jpeg_data.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto network = Board::GetInstance().GetNetwork();
|
||||
if (network == nullptr) {
|
||||
ESP_LOGW(TAG, "No network available for background upload");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string boundary = "----XIAOZHI_BACKGROUND_CAPTURE_BOUNDARY";
|
||||
auto http = network->CreateHttp(3);
|
||||
http->SetHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
|
||||
if (!http->Open("POST", url)) {
|
||||
ESP_LOGW(TAG, "Failed to open background upload URL: %s", url.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_header;
|
||||
file_header += "--" + boundary + "\r\n";
|
||||
file_header += "Content-Disposition: form-data; name=\"file\"; filename=\"frame.jpg\"\r\n";
|
||||
file_header += "Content-Type: image/jpeg\r\n\r\n";
|
||||
http->Write(file_header.c_str(), file_header.size());
|
||||
http->Write(jpeg_data.data(), jpeg_data.size());
|
||||
|
||||
std::string footer;
|
||||
footer += "\r\n--" + boundary + "--\r\n";
|
||||
http->Write(footer.c_str(), footer.size());
|
||||
http->Write("", 0);
|
||||
|
||||
const int status_code = http->GetStatusCode();
|
||||
http->Close();
|
||||
|
||||
if (status_code < 200 || status_code >= 300) {
|
||||
ESP_LOGW(TAG, "Background upload failed, status=%d", status_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Uploaded background frame: %u bytes", static_cast<unsigned>(jpeg_data.size()));
|
||||
return true;
|
||||
#else
|
||||
(void)jpeg_data;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user