ncm A2: 音频输出迁移 meow_audiod(SINK_AUDIOD 默认后端)— 修无声根因

- audio_out.c: 新增 SINK_AUDIOD 默认(SDK meow_audio_* 连 audiod);NCM_AUDIO_SINK=file/null/alsa
  与 MEOW_AUDIO_DIRECT=1 保留调试后端;ALSA 保底修两大坑(hw_params 全放行初始化 + mask 清零
  再单点)——旧 memset0 interval=「只能取0」→ HW_PARAMS 恒 EINVAL 即播放无声根因
- 新增 audio_out_pause:PAUSED 沿向 audiod 发 pause(停时钟不丢流);resume 流死自动重开
- player_manager: 注册 meow_audio_on_preempt → g_play 命令通道暂停;播放事件全量 meow_log
  (open 参数/grant_*/write 错误节流/切歌/暂停/seek/done)
- appstate/app: /tmp/ncm_autoplay 真机联调 hook(仅播已缓存曲目,绝不发 song_url)
- bench/audiod_chain_sim.c: 真实 audiod_host 假 socket 全链离线回归 14 项全 PASS
- 验证: host 0 warning / WAV 回归 / qemu core PASS / mipsel 0 warning;真机 OPEN→grant 已通,
  持续写帧被 M2.90 设备 PCM 播放内核硬挂死阻断(REPORT §16.3,4/4 复现,留主会话)
- ncm md5 db5b78ad9e8f40e6306484efe2a08eb3(已热更设备)
This commit is contained in:
MeowOS committed 2026-09-01 01:17:21 +08:00
1 parent 9bbc3a1467
commit e0136213fe
10 files changed
+598 -41

No files matched your search

+34 -4
View File
@@ -26,6 +26,15 @@ static int64_t g_file_bytes;
static int g_level; /* 音质档(来自设置) */
static int g_cache_gb;
/* 被抢占回调(SDK 读线程分发;audiod 后开流赢):走 g_play 命令通道暂停(内部持锁),
* 不直接动锁外状态。恢复时 audio_out_pause(0) 会检测流死并自动重开(见 audio_out.c)。 */
static void on_audio_preempt(const char *by_app, void *ctx)
{
(void)ctx;
meow_log(MEOW_LOG_WARN, "play: preempted by [%s] -> pause", (by_app && by_app[0]) ? by_app : "?");
player_manager_pause(1);
}
int player_manager_state(void) { int s; pthread_mutex_lock(&g_play.mu); s = g_play.state; pthread_mutex_unlock(&g_play.mu); return s; }
int player_manager_playing_song(void) { int s; pthread_mutex_lock(&g_play.mu); s = g_play.cur_song; pthread_mutex_unlock(&g_play.mu); return s; }
int64_t player_manager_pos_ms(void) { int64_t p; pthread_mutex_lock(&g_play.mu); p = g_play.pos_ms; pthread_mutex_unlock(&g_play.mu); return p; }
@@ -196,10 +205,12 @@ static void run_song(int idx)
g_play.dur_ms = dur;
g_play.err = 0;
pthread_mutex_unlock(&g_play.mu);
meow_log(MEOW_LOG_INFO, "play: start idx=%d id=%s dur=%lldms", idx, s->id, (long long)dur);
mp3d_sample_t pcm[PCM_CHUNK];
int64_t frames_done = 0; /* 已输出帧(每通道) */
int need_seek_ms = -1;
int paused_edge = 0; /* 暂停沿:进暂停已向 audiod 发 pause */
for (;;) {
if (g_app_quit) break;
@@ -212,15 +223,30 @@ static void run_song(int idx)
int st = g_play.state;
pthread_mutex_unlock(&g_play.mu);
if (cur != idx) break; /* 切歌 → 回外层 */
if (st == NCM_PC_STOPPED) break;
if (cur != idx) { meow_log(MEOW_LOG_INFO, "play: switch idx=%d -> %d", idx, cur); break; } /* 切歌 → 回外层 */
if (st == NCM_PC_STOPPED) { meow_log(MEOW_LOG_INFO, "play: stopped idx=%d", idx); break; }
if (cmd == NCM_CTL_SEEK && seek_ms >= 0) { need_seek_ms = (int)seek_ms; }
if (st == NCM_PC_PAUSED) { usleep(40 * 1000); continue; } /* 冻结 */
if (st == NCM_PC_PAUSED) {
if (!paused_edge) { /* 进暂停:停 PCM 时钟但不丢流(audiod MSG_AUDIO_PAUSE) */
paused_edge = 1;
meow_log(MEOW_LOG_INFO, "play: paused idx=%d pos=%lldms", idx,
(long long)(frames_done * 1000 / 44100));
audio_out_pause(1);
}
usleep(40 * 1000);
continue; /* 冻结 */
}
if (paused_edge) { /* 出暂停:恢复 audiod 流(流死则自动重开) */
paused_edge = 0;
meow_log(MEOW_LOG_INFO, "play: resumed idx=%d", idx);
if (audio_out_pause(0) != 0) { set_err(NCM_ERR_IO, "audio resume failed"); break; }
}
if (need_seek_ms >= 0) {
/* 重开并 seek:关闭后重开(字节 offset 近似) */
meow_log(MEOW_LOG_INFO, "play: seek idx=%d -> %dms", idx, need_seek_ms);
audio_out_close();
mp3dec_ex_close(&dec);
memset(&dec, 0, sizeof dec);
@@ -235,7 +261,10 @@ static void run_song(int idx)
}
size_t got = mp3dec_ex_read(&dec, pcm, PCM_CHUNK);
if (got == 0) break; /* 播完 */
if (got == 0) { /* 播完 */
meow_log(MEOW_LOG_INFO, "play: done idx=%d frames=%lld", idx, (long long)frames_done);
break;
}
int frames = (int)(got / 2);
if (audio_out_write(pcm, frames) != frames) break;
frames_done += frames;
@@ -377,6 +406,7 @@ int player_manager_start(void)
g_level = g_st.level;
g_cache_gb = g_st.cache_limit_gb;
pthread_mutex_init(&g_play.mu, NULL);
meow_audio_on_preempt(on_audio_preempt, NULL); /* 被抢占 → 暂停(SDK 读线程分发) */
if (pthread_create(&g_thr, NULL, player_thread, NULL) != 0) return -1;
g_thr_on = 1;
return 0;