feat(perf): accelerate image download and raster conversion
This commit is contained in:
@@ -40,6 +40,7 @@ idf_component_register(
|
||||
"platform/internal"
|
||||
REQUIRES
|
||||
bt
|
||||
esp_coex
|
||||
esp_wifi
|
||||
esp_netif
|
||||
esp_event
|
||||
|
||||
@@ -34,7 +34,7 @@ config TQ_Z_IMAGE_MODEL
|
||||
|
||||
config TQ_Z_IMAGE_DEFAULT_SIZE
|
||||
string "Z-Image default output size"
|
||||
default "1024*1536"
|
||||
default "512*768"
|
||||
|
||||
config TQ_Z_IMAGE_TIMEOUT_MS
|
||||
int "Z-Image generation timeout (ms)"
|
||||
@@ -56,6 +56,20 @@ config TQ_Z_IMAGE_MAX_PNG_BYTES
|
||||
range 65536 8388608
|
||||
default 4194304
|
||||
|
||||
config TQ_Z_IMAGE_HTTP_BUFFER_BYTES
|
||||
int "Z-Image HTTP client buffer bytes"
|
||||
range 4096 32768
|
||||
default 16384
|
||||
|
||||
config TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES
|
||||
int "Z-Image HTTP stream read chunk bytes"
|
||||
range 4096 32768
|
||||
default 16384
|
||||
|
||||
config TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD
|
||||
bool "Prefer Wi-Fi RF during image download"
|
||||
default y
|
||||
|
||||
config TQ_VOICE_API_KEY
|
||||
string "Voice API Key for DashScope WebSocket (optional)"
|
||||
default "sk-7a50eca6856d4afb968ac3bf512f6d1b"
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "esp_coexist.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_API_ENDPOINT
|
||||
#define CONFIG_TQ_Z_IMAGE_API_ENDPOINT "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"
|
||||
@@ -19,7 +24,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE
|
||||
#define CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE "1024*1536"
|
||||
#define CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE "512*768"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_TIMEOUT_MS
|
||||
@@ -38,8 +43,31 @@
|
||||
#define CONFIG_TQ_Z_IMAGE_MAX_PNG_BYTES (4 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES
|
||||
#define CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES 16384
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES
|
||||
#define CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES 16384
|
||||
#endif
|
||||
|
||||
#define IMAGE_HTTP_READ_CHUNK_BYTES ((size_t)CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES)
|
||||
#define IMAGE_HTTP_BUFFER_BYTES CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES
|
||||
#define IMAGE_PREWARM_TIMEOUT_MS 2500
|
||||
#define IMAGE_PREWARM_STACK 5120
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
#define IMAGE_WORKER_CORE_ID 0
|
||||
#else
|
||||
#define IMAGE_WORKER_CORE_ID 1
|
||||
#endif
|
||||
|
||||
static const char *TAG = "image_generation";
|
||||
|
||||
typedef struct {
|
||||
char oss_origin[160];
|
||||
} image_prewarm_task_arg_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
@@ -47,6 +75,40 @@ typedef struct {
|
||||
size_t max_len;
|
||||
} bytes_buffer_t;
|
||||
|
||||
static SemaphoreHandle_t s_prewarm_lock;
|
||||
static bool s_prewarm_running;
|
||||
static char s_last_oss_origin[160] = "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/";
|
||||
|
||||
static bool image_generation_boost_coex_for_download(void) {
|
||||
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD
|
||||
esp_err_t rc = esp_coex_preference_set(ESP_COEX_PREFER_WIFI);
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGW(TAG, "failed to prefer Wi-Fi coexist mode, rc=0x%x", (unsigned)rc);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "coexist prefer Wi-Fi enabled for image download");
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void image_generation_restore_coex_after_download(bool boosted) {
|
||||
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD
|
||||
if (!boosted) {
|
||||
return;
|
||||
}
|
||||
esp_err_t rc = esp_coex_preference_set(ESP_COEX_PREFER_BALANCE);
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGW(TAG, "failed to restore coexist balance mode, rc=0x%x", (unsigned)rc);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "coexist preference restored to balance");
|
||||
#else
|
||||
(void)boosted;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void image_generation_fill_err(char *err, size_t err_len, const char *msg) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "%s", msg != NULL ? msg : "unknown error");
|
||||
@@ -149,6 +211,206 @@ static const char *image_generation_api_key(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void prewarm_lock_init_if_needed(void) {
|
||||
if (s_prewarm_lock != NULL) {
|
||||
return;
|
||||
}
|
||||
s_prewarm_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
|
||||
static bool url_extract_origin(const char *url, char *out_origin, size_t out_origin_len) {
|
||||
if (url == NULL || out_origin == NULL || out_origin_len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *scheme = strstr(url, "://");
|
||||
if (scheme == NULL || scheme == url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *host = scheme + 3;
|
||||
if (*host == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *path = strchr(host, '/');
|
||||
size_t host_len = path != NULL ? (size_t)(path - host) : strlen(host);
|
||||
if (host_len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int n = snprintf(out_origin,
|
||||
out_origin_len,
|
||||
"%.*s://%.*s/",
|
||||
(int)(scheme - url),
|
||||
url,
|
||||
(int)host_len,
|
||||
host);
|
||||
return n > 0 && (size_t)n < out_origin_len;
|
||||
}
|
||||
|
||||
static void image_generation_remember_oss_origin(const char *image_url) {
|
||||
char origin[160] = {0};
|
||||
if (!url_extract_origin(image_url, origin, sizeof(origin))) {
|
||||
return;
|
||||
}
|
||||
|
||||
prewarm_lock_init_if_needed();
|
||||
if (s_prewarm_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
strlcpy(s_last_oss_origin, origin, sizeof(s_last_oss_origin));
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
}
|
||||
|
||||
static void image_generation_copy_oss_origin(char *out_origin, size_t out_origin_len) {
|
||||
if (out_origin == NULL || out_origin_len == 0) {
|
||||
return;
|
||||
}
|
||||
out_origin[0] = '\0';
|
||||
|
||||
prewarm_lock_init_if_needed();
|
||||
if (s_prewarm_lock == NULL) {
|
||||
strlcpy(out_origin, s_last_oss_origin, out_origin_len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
strlcpy(out_origin, s_last_oss_origin, out_origin_len);
|
||||
return;
|
||||
}
|
||||
strlcpy(out_origin, s_last_oss_origin, out_origin_len);
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
}
|
||||
|
||||
static esp_err_t image_generation_http_prewarm_head(const char *url,
|
||||
const char *auth_header,
|
||||
uint32_t timeout_ms) {
|
||||
if (url == NULL || url[0] == '\0') {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_http_client_config_t cfg = {
|
||||
.url = url,
|
||||
.method = HTTP_METHOD_HEAD,
|
||||
.timeout_ms = (int)timeout_ms,
|
||||
.keep_alive_enable = true,
|
||||
.buffer_size = 1024,
|
||||
.buffer_size_tx = 1024,
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
esp_http_client_handle_t client = esp_http_client_init(&cfg);
|
||||
if (client == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (auth_header != NULL && auth_header[0] != '\0') {
|
||||
esp_http_client_set_header(client, "Authorization", auth_header);
|
||||
}
|
||||
|
||||
esp_err_t rc = esp_http_client_open(client, 0);
|
||||
if (rc == ESP_OK) {
|
||||
(void)esp_http_client_fetch_headers(client);
|
||||
esp_http_client_close(client);
|
||||
}
|
||||
esp_http_client_cleanup(client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void image_generation_prewarm_task(void *arg) {
|
||||
image_prewarm_task_arg_t *task_arg = (image_prewarm_task_arg_t *)arg;
|
||||
char oss_origin[160] = {0};
|
||||
if (task_arg != NULL) {
|
||||
strlcpy(oss_origin, task_arg->oss_origin, sizeof(oss_origin));
|
||||
free(task_arg);
|
||||
}
|
||||
|
||||
int64_t start_ms = esp_timer_get_time() / 1000;
|
||||
const char *api_key = image_generation_api_key();
|
||||
char auth_header[192] = {0};
|
||||
const char *auth = NULL;
|
||||
if (api_key != NULL) {
|
||||
snprintf(auth_header, sizeof(auth_header), "Bearer %s", api_key);
|
||||
auth = auth_header;
|
||||
}
|
||||
|
||||
esp_err_t gen_rc = image_generation_http_prewarm_head(CONFIG_TQ_Z_IMAGE_API_ENDPOINT,
|
||||
auth,
|
||||
IMAGE_PREWARM_TIMEOUT_MS);
|
||||
esp_err_t oss_rc = image_generation_http_prewarm_head(oss_origin,
|
||||
NULL,
|
||||
IMAGE_PREWARM_TIMEOUT_MS);
|
||||
int64_t done_ms = esp_timer_get_time() / 1000;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"https prewarm done, gen_rc=0x%x, oss_rc=0x%x, cost_ms=%lld",
|
||||
(unsigned)gen_rc,
|
||||
(unsigned)oss_rc,
|
||||
(long long)(done_ms - start_ms));
|
||||
|
||||
prewarm_lock_init_if_needed();
|
||||
if (s_prewarm_lock != NULL && xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
s_prewarm_running = false;
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
} else {
|
||||
s_prewarm_running = false;
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void image_generation_schedule_prewarm(void) {
|
||||
prewarm_lock_init_if_needed();
|
||||
if (s_prewarm_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
if (s_prewarm_running) {
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
return;
|
||||
}
|
||||
s_prewarm_running = true;
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
|
||||
image_prewarm_task_arg_t *arg = (image_prewarm_task_arg_t *)calloc(1, sizeof(*arg));
|
||||
if (arg == NULL) {
|
||||
if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
s_prewarm_running = false;
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
} else {
|
||||
s_prewarm_running = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
image_generation_copy_oss_origin(arg->oss_origin, sizeof(arg->oss_origin));
|
||||
|
||||
BaseType_t ok = xTaskCreatePinnedToCore(image_generation_prewarm_task,
|
||||
"img_prewarm",
|
||||
IMAGE_PREWARM_STACK,
|
||||
arg,
|
||||
4,
|
||||
NULL,
|
||||
IMAGE_WORKER_CORE_ID);
|
||||
if (ok != pdPASS) {
|
||||
free(arg);
|
||||
if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
s_prewarm_running = false;
|
||||
xSemaphoreGive(s_prewarm_lock);
|
||||
} else {
|
||||
s_prewarm_running = false;
|
||||
}
|
||||
ESP_LOGW(TAG, "failed to create prewarm task");
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t http_read_full_response(esp_http_client_handle_t client,
|
||||
bytes_buffer_t *out_body,
|
||||
char *err,
|
||||
@@ -158,14 +420,21 @@ static esp_err_t http_read_full_response(esp_http_client_handle_t client,
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
char *tmp = (char *)malloc_prefer_psram(IMAGE_HTTP_READ_CHUNK_BYTES);
|
||||
if (tmp == NULL) {
|
||||
image_generation_fill_err(err, err_len, "no memory for http read buffer");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int n = esp_http_client_read(client, tmp, sizeof(tmp));
|
||||
int n = esp_http_client_read(client, tmp, IMAGE_HTTP_READ_CHUNK_BYTES);
|
||||
if (n < 0) {
|
||||
free(tmp);
|
||||
image_generation_fill_err(err, err_len, "http read failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (n == 0) {
|
||||
free(tmp);
|
||||
if (esp_http_client_is_complete_data_received(client)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -175,6 +444,7 @@ static esp_err_t http_read_full_response(esp_http_client_handle_t client,
|
||||
|
||||
esp_err_t append_rc = bytes_buffer_append(out_body, tmp, (size_t)n);
|
||||
if (append_rc != ESP_OK) {
|
||||
free(tmp);
|
||||
image_generation_fill_err(err, err_len, "http response too large");
|
||||
return append_rc;
|
||||
}
|
||||
@@ -231,6 +501,16 @@ static esp_err_t http_request_collect(const esp_http_client_config_t *config,
|
||||
image_generation_fill_err(err, err_len, "http response too large");
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (content_len > 0) {
|
||||
esp_err_t reserve_rc = bytes_buffer_reserve(out_body, (size_t)content_len);
|
||||
if (reserve_rc != ESP_OK) {
|
||||
esp_http_client_close(client);
|
||||
esp_http_client_cleanup(client);
|
||||
image_generation_fill_err(err, err_len, "no memory for http response");
|
||||
return reserve_rc;
|
||||
}
|
||||
}
|
||||
if (out_status_code != NULL) {
|
||||
*out_status_code = esp_http_client_get_status_code(client);
|
||||
}
|
||||
@@ -402,9 +682,9 @@ static esp_err_t image_generation_invoke_model(const image_generation_request_t
|
||||
.url = CONFIG_TQ_Z_IMAGE_API_ENDPOINT,
|
||||
.method = HTTP_METHOD_POST,
|
||||
.timeout_ms = (int)req->generation_timeout_ms,
|
||||
.keep_alive_enable = false,
|
||||
.buffer_size = 2048,
|
||||
.buffer_size_tx = 2048,
|
||||
.keep_alive_enable = true,
|
||||
.buffer_size = IMAGE_HTTP_BUFFER_BYTES,
|
||||
.buffer_size_tx = IMAGE_HTTP_BUFFER_BYTES,
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
@@ -491,6 +771,8 @@ static esp_err_t image_generation_invoke_model(const image_generation_request_t
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
image_generation_remember_oss_origin(out_result->image_url);
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"z-image response ready, request_id=%s, model_size=%ux%u, image_url_len=%u",
|
||||
out_result->request_id[0] != '\0' ? out_result->request_id : "-",
|
||||
@@ -507,13 +789,14 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re
|
||||
char *err,
|
||||
size_t err_len) {
|
||||
ESP_LOGI(TAG, "generated image download start, timeout_ms=%u", (unsigned)timeout_ms);
|
||||
bool coex_boosted = image_generation_boost_coex_for_download();
|
||||
|
||||
esp_http_client_config_t config = {
|
||||
.url = out_result->image_url,
|
||||
.method = HTTP_METHOD_GET,
|
||||
.timeout_ms = (int)timeout_ms,
|
||||
.keep_alive_enable = false,
|
||||
.buffer_size = 2048,
|
||||
.keep_alive_enable = true,
|
||||
.buffer_size = IMAGE_HTTP_BUFFER_BYTES,
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
};
|
||||
|
||||
@@ -537,6 +820,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re
|
||||
(unsigned)rc,
|
||||
(err != NULL && err[0] != '\0') ? err : "download failed");
|
||||
bytes_buffer_free(&resp);
|
||||
image_generation_restore_coex_after_download(coex_boosted);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -545,6 +829,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re
|
||||
if (rc != ESP_OK) {
|
||||
bytes_buffer_free(&resp);
|
||||
image_generation_fill_err(err, err_len, "image download failed");
|
||||
image_generation_restore_coex_after_download(coex_boosted);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
extract_remote_error_message((const char *)resp.data, http_status, err, err_len);
|
||||
@@ -553,6 +838,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re
|
||||
http_status,
|
||||
(err != NULL && err[0] != '\0') ? err : "image download failed");
|
||||
bytes_buffer_free(&resp);
|
||||
image_generation_restore_coex_after_download(coex_boosted);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@@ -563,12 +849,14 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re
|
||||
bytes_buffer_free(&resp);
|
||||
image_generation_fill_err(err, err_len, "downloaded image is not png");
|
||||
ESP_LOGW(TAG, "generated image download invalid png signature");
|
||||
image_generation_restore_coex_after_download(coex_boosted);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
out_result->png = resp.data;
|
||||
out_result->png_len = resp.len;
|
||||
ESP_LOGI(TAG, "generated image download done, png_bytes=%u", (unsigned)out_result->png_len);
|
||||
image_generation_restore_coex_after_download(coex_boosted);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -615,6 +903,8 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
actual.has_seed ? "set" : "auto",
|
||||
(unsigned)actual.generation_timeout_ms,
|
||||
(unsigned)actual.download_timeout_ms);
|
||||
|
||||
int64_t t0_ms = esp_timer_get_time() / 1000;
|
||||
esp_err_t gen_rc = image_generation_invoke_model(&actual, out_result, err, err_len);
|
||||
if (gen_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
@@ -625,7 +915,11 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
return gen_rc;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "image generate stage done, request_id=%s", out_result->request_id[0] != '\0' ? out_result->request_id : "-");
|
||||
int64_t t_gen_done_ms = esp_timer_get_time() / 1000;
|
||||
ESP_LOGI(TAG,
|
||||
"image generate stage done, request_id=%s, gen_cost_ms=%lld",
|
||||
out_result->request_id[0] != '\0' ? out_result->request_id : "-",
|
||||
(long long)(t_gen_done_ms - t0_ms));
|
||||
esp_err_t dl_rc = image_generation_download_png(out_result, actual.download_timeout_ms, err, err_len);
|
||||
if (dl_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
@@ -636,10 +930,15 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
return dl_rc;
|
||||
}
|
||||
|
||||
int64_t t_dl_done_ms = esp_timer_get_time() / 1000;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"image generation pipeline done, request_id=%s, png_bytes=%u",
|
||||
"image generation pipeline done, request_id=%s, png_bytes=%u, gen_ms=%lld, download_ms=%lld, total_ms=%lld",
|
||||
out_result->request_id[0] != '\0' ? out_result->request_id : "-",
|
||||
(unsigned)out_result->png_len);
|
||||
(unsigned)out_result->png_len,
|
||||
(long long)(t_gen_done_ms - t0_ms),
|
||||
(long long)(t_dl_done_ms - t_gen_done_ms),
|
||||
(long long)(t_dl_done_ms - t0_ms));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#define PNG_DECODE_MAX_BYTES (2 * 1024 * 1024)
|
||||
#define PNG_DECODE_MAX_WIDTH 1600
|
||||
|
||||
static const uint8_t s_bit_mask[8] = {0x80u, 0x40u, 0x20u, 0x10u, 0x08u, 0x04u, 0x02u, 0x01u};
|
||||
|
||||
static void set_black(uint8_t *buf, uint16_t width, uint16_t x, uint16_t y) {
|
||||
if (x >= width) {
|
||||
return;
|
||||
@@ -40,30 +42,6 @@ static void *calloc_prefer_psram(size_t n, size_t size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static uint8_t sample_gray8_scaled(const uint8_t *gray,
|
||||
uint16_t src_width,
|
||||
uint16_t src_height,
|
||||
bool scale_to_width,
|
||||
uint16_t dst_height,
|
||||
uint16_t x,
|
||||
uint16_t y) {
|
||||
uint16_t src_y = scale_to_width
|
||||
? (uint16_t)(((uint32_t)y * src_height) / dst_height)
|
||||
: y;
|
||||
if (src_y >= src_height) {
|
||||
src_y = (uint16_t)(src_height - 1);
|
||||
}
|
||||
|
||||
uint16_t src_x = scale_to_width
|
||||
? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH)
|
||||
: x;
|
||||
if (src_x >= src_width) {
|
||||
src_x = (uint16_t)(src_width - 1);
|
||||
}
|
||||
|
||||
return gray[(size_t)src_y * src_width + src_x];
|
||||
}
|
||||
|
||||
static uint8_t gray8_to_work_luma(uint8_t gray, bool invert) {
|
||||
return invert ? (uint8_t)(255u - gray) : gray;
|
||||
}
|
||||
@@ -165,9 +143,45 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint16_t *x_map = (uint16_t *)alloc_prefer_psram(sizeof(uint16_t) * RASTER_WIDTH);
|
||||
uint16_t *y_map = (uint16_t *)alloc_prefer_psram(sizeof(uint16_t) * dst_height);
|
||||
size_t total_px = (size_t)RASTER_WIDTH * dst_height;
|
||||
uint8_t *luma = (uint8_t *)alloc_prefer_psram(total_px);
|
||||
if (x_map == NULL || y_map == NULL || luma == NULL) {
|
||||
free(x_map);
|
||||
free(y_map);
|
||||
free(luma);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "no memory");
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
|
||||
uint16_t src_x = scale_to_width
|
||||
? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH)
|
||||
: x;
|
||||
if (src_x >= src_width) {
|
||||
src_x = (uint16_t)(src_width - 1);
|
||||
}
|
||||
x_map[x] = src_x;
|
||||
}
|
||||
for (uint16_t y = 0; y < dst_height; ++y) {
|
||||
uint16_t src_y = scale_to_width
|
||||
? (uint16_t)(((uint32_t)y * src_height) / dst_height)
|
||||
: y;
|
||||
if (src_y >= src_height) {
|
||||
src_y = (uint16_t)(src_height - 1);
|
||||
}
|
||||
y_map[y] = src_y;
|
||||
}
|
||||
|
||||
size_t len = (size_t)RASTER_BYTES_PER_ROW * dst_height;
|
||||
uint8_t *raster = (uint8_t *)calloc_prefer_psram(1, len);
|
||||
if (raster == NULL) {
|
||||
free(x_map);
|
||||
free(y_map);
|
||||
free(luma);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "no memory");
|
||||
}
|
||||
@@ -176,17 +190,19 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
|
||||
|
||||
uint32_t hist[256] = {0};
|
||||
uint64_t sum_luma = 0;
|
||||
uint32_t total_px = (uint32_t)RASTER_WIDTH * (uint32_t)dst_height;
|
||||
for (uint16_t y = 0; y < dst_height; ++y) {
|
||||
const uint8_t *src_row = gray + (size_t)y_map[y] * src_width;
|
||||
uint8_t *dst_luma_row = luma + (size_t)y * RASTER_WIDTH;
|
||||
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
|
||||
uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y);
|
||||
uint8_t src_v = src_row[x_map[x]];
|
||||
uint8_t work_v = gray8_to_work_luma(src_v, invert);
|
||||
dst_luma_row[x] = work_v;
|
||||
hist[work_v]++;
|
||||
sum_luma += work_v;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t otsu_thr = compute_otsu_threshold(hist, total_px);
|
||||
uint8_t otsu_thr = compute_otsu_threshold(hist, (uint32_t)total_px);
|
||||
uint8_t mean_luma = total_px > 0 ? (uint8_t)(sum_luma / total_px) : (uint8_t)160;
|
||||
|
||||
/* Keep backward compatibility: threshold still works as bias around 160. */
|
||||
@@ -208,26 +224,27 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
|
||||
bool use_dither = (err_curr != NULL && err_next != NULL);
|
||||
|
||||
for (uint16_t y = 0; y < dst_height; ++y) {
|
||||
const uint8_t *luma_row = luma + (size_t)y * RASTER_WIDTH;
|
||||
uint8_t *raster_row = raster + (size_t)y * RASTER_BYTES_PER_ROW;
|
||||
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
|
||||
uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y);
|
||||
int luma = (int)gray8_to_work_luma(src_v, invert);
|
||||
int px_luma = (int)luma_row[x];
|
||||
if (use_dither) {
|
||||
luma += err_curr[x + 1u];
|
||||
if (luma < 0) {
|
||||
luma = 0;
|
||||
} else if (luma > 255) {
|
||||
luma = 255;
|
||||
px_luma += err_curr[x + 1u];
|
||||
if (px_luma < 0) {
|
||||
px_luma = 0;
|
||||
} else if (px_luma > 255) {
|
||||
px_luma = 255;
|
||||
}
|
||||
}
|
||||
|
||||
bool black = luma < final_thr;
|
||||
bool black = px_luma < final_thr;
|
||||
if (black) {
|
||||
set_black(raster, RASTER_WIDTH, x, y);
|
||||
raster_row[x >> 3] |= s_bit_mask[x & 7u];
|
||||
}
|
||||
|
||||
if (use_dither) {
|
||||
int quantized = black ? 0 : 255;
|
||||
int e = luma - quantized;
|
||||
int e = px_luma - quantized;
|
||||
|
||||
if ((x + 1u) < RASTER_WIDTH) {
|
||||
err_curr[x + 2u] += (int16_t)((e * 7) / 16);
|
||||
@@ -248,6 +265,9 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
|
||||
|
||||
free(err_curr);
|
||||
free(err_next);
|
||||
free(x_map);
|
||||
free(y_map);
|
||||
free(luma);
|
||||
|
||||
*out_width = dst_width;
|
||||
*out_height = dst_height;
|
||||
|
||||
@@ -20,6 +20,30 @@ static int s_retry_num;
|
||||
static bool s_ready;
|
||||
static esp_netif_t *s_sta_netif;
|
||||
|
||||
static void wifi_manager_apply_sta_throughput_profile(void) {
|
||||
const uint8_t protocol = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N;
|
||||
esp_err_t proto_rc = esp_wifi_set_protocol(WIFI_IF_STA, protocol);
|
||||
if (proto_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG, "failed to set 11bgn protocol mask, rc=0x%x", (unsigned)proto_rc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Wi-Fi protocol mask set to 11b/11g/11n");
|
||||
}
|
||||
|
||||
esp_err_t bw_rc = esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40);
|
||||
if (bw_rc == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Wi-Fi bandwidth set to HT40");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "failed to set HT40, fallback to HT20, rc=0x%x", (unsigned)bw_rc);
|
||||
bw_rc = esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT20);
|
||||
if (bw_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG, "failed to set HT20 fallback, rc=0x%x", (unsigned)bw_rc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Wi-Fi bandwidth set to HT20");
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_event_handler(void *arg,
|
||||
esp_event_base_t event_base,
|
||||
int32_t event_id,
|
||||
@@ -78,6 +102,13 @@ static esp_err_t start_sta_mode(void) {
|
||||
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "connected to AP SSID:%s", CONFIG_TQ_WIFI_SSID);
|
||||
wifi_manager_apply_sta_throughput_profile();
|
||||
esp_err_t ps_rc = esp_wifi_set_ps(WIFI_PS_NONE);
|
||||
if (ps_rc == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Wi-Fi power save disabled (WIFI_PS_NONE)");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "failed to set WIFI_PS_NONE, rc=0x%x", (unsigned)ps_rc);
|
||||
}
|
||||
s_ready = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,24 @@ CONFIG_BT_NIMBLE_ROLE_CENTRAL=y
|
||||
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=247
|
||||
# CONFIG_ESP_WIFI_SOFTAP_SUPPORT is not set
|
||||
CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=n
|
||||
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=24
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=256
|
||||
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=32
|
||||
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=64
|
||||
CONFIG_ESP_WIFI_TX_BA_WIN=16
|
||||
CONFIG_ESP_WIFI_RX_BA_WIN=32
|
||||
CONFIG_LWIP_WND_SCALE=y
|
||||
CONFIG_LWIP_TCP_RCV_SCALE=3
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=57344
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=262144
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=192
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=256
|
||||
CONFIG_TQ_WIFI_SSID="TalkingQ"
|
||||
CONFIG_TQ_WIFI_PASSWORD="TalkingQ123"
|
||||
CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE="512*768"
|
||||
CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES=16384
|
||||
CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES=16384
|
||||
CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD=y
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
|
||||
Reference in New Issue
Block a user