▸
改动总览
| 文件 | 改动要点 | 类型 |
|---|---|---|
vendor/T5/tuyaos/tuyaos_adapter/src/driver/tkl_audio.c |
tkl_ao_put_frame:无限 goto 重试 → 累计 1s 超时,超时丢帧返回 OPRT_TIMEOUT;新增两个宏 |
治本 |
apps/.../audio_player/src/ai_player.h |
新增 PLAYER_QUEUE_DEPTH=8、PLAYER_CMD_POST_TIMEOUT_MS=200 |
兜底 |
apps/.../audio_player/src/svc_ai_player.c |
队列深度 2→8;start/stop 投递超时 0→200ms;修 start 投递失败时 mm_strdup 泄漏 |
兜底 |
为何要配套
有了底层 1s 超时,播放线程最坏 ~1s 脱困;队列加深到 8 + 投递 200ms 超时正好覆盖这个恢复窗口,使 -26369(队列满投递失败)与随后的 -2(start 被拒)不再发生。单改其一不足以根除「隔夜唤醒无声」。
01
tkl_audio.c · 治本
vendor/T5/tuyaos/tuyaos_adapter/src/driver/tkl_audio.c
底层 · 治本
①新增超时宏。ring buffer 仅
DRIVER_SPEAK_FIFO_FRAME_NUM(2)×20ms=40ms,正常播放「满」最多等 ~40ms,1s 超时不会误伤。@@ 紧接 FRAME_SIZE_PER_20MS 宏定义之后 @@
#define FRAME_SIZE_PER_20MS(x) (x * CHANNEL_NUM * TIME_SAMPLE_MS / MS_PER_SEC) #define SPK_WRITE_RETRY_INTERVAL_MS (20) #define SPK_WRITE_TIMEOUT_MS (1000)
②把
tkl_ao_put_frame 里的无限 goto write_spk_retry 换成带累计超时的 do/while,超时即丢帧返回 OPRT_TIMEOUT,让上层 player 线程得以 stop + 恢复。@@ OPERATE_RET tkl_ao_put_frame(...) 内分段写循环 @@
// 分段调用 bk_aud_intf_write_spk_data while (remaining_size > 0) { chunk_size = (remaining_size > spk_ringbuf_size) ? spk_ringbuf_size : remaining_size; write_spk_retry: ret = bk_voice_write_frame_data(g_voice_write_handle, (char *)(pframe->pbuf + offset), chunk_size); // ret = bk_voice_write_spk_data(...); if (ret == 0) { tkl_system_sleep(20); goto write_spk_retry; } UINT_T waited_ms = 0; do { ret = bk_voice_write_frame_data(g_voice_write_handle, (char *)(pframe->pbuf + offset), chunk_size); if (ret != 0) { break; } tkl_system_sleep(SPK_WRITE_RETRY_INTERVAL_MS); waited_ms += SPK_WRITE_RETRY_INTERVAL_MS; } while (waited_ms < SPK_WRITE_TIMEOUT_MS); if (ret == 0) { os_printf("audio spk write timeout(%u ms), drop frame, chunk:%d \r\n", waited_ms, chunk_size); return OPRT_TIMEOUT; } if (ret < 0) { os_printf("audio intf spk semaphore wait fail, ret:%d \r\n", ret); return ret; } offset += chunk_size; remaining_size -= chunk_size; } return OPRT_OK;
部署提醒
vendor/T5/… 是 CDE/embcli 按 make.yaml 下载的依赖,重跑 prepare.sh / embcli update 可能覆盖此文件。要长期生效需反馈给 T5 adapter 上游,或在 prepare 后确认改动仍在。
02
ai_player.h · 兜底宏
apps/tuyaos_demo_wukong_ai/src/miscs/audio_player/src/ai_player.h
播放器层 · 兜底
@@ 紧接 PLAYER_MAX_VOLUME 宏定义之后 @@
#define PLAYER_IDLE_TIMEOUT_MS 0xFFFFffff #define PLAYER_BUSY_TIMEOUT_MS 1 #define PLAYER_MAX_VOLUME 100 #define PLAYER_QUEUE_DEPTH 8 #define PLAYER_CMD_POST_TIMEOUT_MS 200
03
svc_ai_player.c · 兜底应用
apps/tuyaos_demo_wukong_ai/src/miscs/audio_player/src/svc_ai_player.c
播放器层 · 兜底
①队列深度
2 → PLAYER_QUEUE_DEPTH(位于 tuya_ai_player_service_init)。TUYA_CALL_ERR_RETURN(tal_queue_create_init(&s_ai_player_ctx.queue, SIZEOF(AI_PLAYER_MSG_T), 2)); TUYA_CALL_ERR_RETURN(tal_queue_create_init(&s_ai_player_ctx.queue, SIZEOF(AI_PLAYER_MSG_T), PLAYER_QUEUE_DEPTH));
②
tuya_ai_player_start:投递超时 0 → 200ms,且失败时释放 mm_strdup 出来的 value(修内存泄漏)。msg.param.cmd_start.value = value ? mm_strdup(value) : NULL; PR_DEBUG("start player %d %d", src, codec); TUYA_CALL_ERR_RETURN(tal_queue_post(s_ai_player_ctx.queue, &msg, 0)); rt = tal_queue_post(s_ai_player_ctx.queue, &msg, PLAYER_CMD_POST_TIMEOUT_MS); if (OPRT_OK != rt) { PR_ERR("post start cmd failed: %d", rt); if (msg.param.cmd_start.value) { Free(msg.param.cmd_start.value); } return rt; }
③
tuya_ai_player_stop:投递超时 0 → 200ms。msg.cmd = PLAYER_CMD_STOP; PR_DEBUG("stop player %d", player->mode); TUYA_CALL_ERR_RETURN(tal_queue_post(s_ai_player_ctx.queue, &msg, 0)); TUYA_CALL_ERR_RETURN(tal_queue_post(s_ai_player_ctx.queue, &msg, PLAYER_CMD_POST_TIMEOUT_MS));
验证建议
编译后重点验证隔夜待机 → 唤醒:应有声、唤醒处无 1~2s 卡顿、日志不再出现 -26369 / -2;若 DAC 真未恢复,应看到
audio spk write timeout 丢帧日志而非整机 hang。