fix(ncm §25): 切音质后重播同一曲被卡「缓冲中」——play intent 以 seq 标识(同 idx 再 play 重启换档)

真机反馈:切音质后再播同一首歌,声音已出但播放页恒显示「缓冲中」。

根因(app/play/player_manager.c):同 idx 的新播放意图不被解码主循环识别。
切音质后重播同曲 = player_manager_play(idx),而 idx 与在播曲相同——cur_song 不变
(仍是 idx)、state 由 PLAYING 置 BUFFER、seq++。旧仍_current(cur==idx && state==BUFFER
恒真)与解码主循环(只认 cur!=idx / STOPPED)均不放过,于是:
  ①旧曲(旧档位)继续出声、state 停在 BUFFER → UI 恒「缓冲中…」(dl_done=0 无百分比);
  ②新档位下载/缓存条目从未发起;
  ③旧曲收尾后外层把 BUFFER 当「自然播完」顺延下一首——新档位永不生效。
三条取数路径(缓存命中/边下边播/.part续传)统一受影响(根因在 intent 层)。

修法:play intent 以 seq 标识,层层判别「我的 intent 是否仍有效」:
  - 新增 still_intent(idx, my_seq)(cur==idx && state==BUFFER && seq==my_seq),取代
    仍_current(同 idx 重播时 seq 变即判过期),贯穿 预缓冲/重试退避/物化/解码 open 各护栏;
  - run_song 入口捕获 my_seq,沿 song_local_file/song_download_attempt 下传;
  - 解码主循环 seq != my_seq → break 回外层重跑 run_song(换档重播);
  - 外层「自然播完」收紧为 state==PLAYING,不把 BUFFER(挂起新 intent)当播完顺延下一首。

验证:player_flow_sim 新增 P12(同 idx 重播换档重启)——修前恒 PLAYING 不了/键仍旧档/
downloads=0/eff=-1;修后 state 回 PLAYING、下载新档位、缓存键 1000_lossless、eff_level
按非会员钳顶回填 EXHIGH。P1-P11 零回归(宿主+qemu-mipsel ALL PASS)。build_ncm.sh
自有代码 -Wall -Wextra 0 warning。0 真实 NCM 请求。未部署真机(留主会话)。
REPORT.md 另记(docs commit)。
This commit is contained in:
MeowOS committed 2026-09-01 20:30:59 +08:00
1 parent 51281e609f
commit a01e5ca18a
4 files changed
+79 -18

No files matched your search

+34
View File
@@ -11,6 +11,9 @@
* P4 下载途中切歌 → 旧曲下载完成后**不得**把状态改回 PLAYING/写过期快照
* (still_current 护栏;旧代码在切歌后 ~下载完成点 会把 BUFFER 覆盖成 PLAYING);
* P5 错误后 play(cur) 重试 → 状态重新走 BUFFER→PLAYING,err 清零。
* P12 切音质后重播同一曲(§25):同 idx 在播时再 play —— 修前被解码主循环忽略
* (旧曲继续出声、state 恒 BUFFER/「缓冲中」,收尾被外层当播完顺延下一首);
* 修后 seq 变化即重启换档,状态回 PLAYING、新档位下载、eff_level 回填。
*
* 编译(宿主,ncm_app 目录下):
* gcc -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
@@ -508,6 +511,37 @@ int main(void)
CHECK(player_manager_error() != 0, "err set after retry exhausted (rc=%d)", player_manager_error());
g_dl_fail = 0;
printf("== P12: 切音质后重播同一曲(同 idx 在播时再 play)-> 换档重启,非卡「缓冲中」(§25)==\n");
usleep(300 * 1000);
g_eof = 0; g_reads_left = -1; g_cache_ok = 1; g_url_rc = NCM_OK; g_fee = 0;
g_part_size = 0; g_http_status = 200; g_416_first = 0; g_dl_delay_ms = 0; g_dl_fail = 0;
g_vip = NCM_VIP_NONE; g_writes = 0; g_downloads = 0; g_url_calls = 0; g_last_key[0] = 0;
/* 以 standard 档播放 song0(缓存命中,PLAYING) */
player_manager_set_quality(NCM_LEVEL_STANDARD, 10);
player_manager_play(0);
CHECK(wait_state(NCM_PC_PLAYING, 3000), "song0 playing at standard (cache hit)");
CHECK(g_writes > 0, "standard song actually emitting audio (writes=%d)", g_writes);
/* 切音质到 lossless,然后重播同一曲(同 idx=0 再次 play)。
* 新档位 = 新缓存条目(lookup 必 miss)→ 如实置 g_cache_ok=0 强制走下载(边下边播)。 */
player_manager_set_quality(NCM_LEVEL_LOSSLESS, 10);
g_cache_ok = 0;
player_manager_play(0);
/* §25 修前:同 idx 重播被解码主循环忽略(cur 不变、state 仍 BUFFER)→ 旧曲继续出声、
* 恒「缓冲中」,且旧曲收尾被外层当「自然播完」顺延下一首(新档位永不生效)。
* 修后:seq 变化即 break → 外层重跑 run_song(0) 换档下载 → 状态回到 PLAYING
* (UI 显示采样率/时间而非「缓冲中」)。 */
CHECK(wait_state(NCM_PC_PLAYING, 4000),
"re-tap same idx -> back to PLAYING (expect 换档开播,非卡 缓冲中)");
CHECK(g_downloads >= 1, "re-tap started new-quality download (downloads=%d)", g_downloads);
CHECK(strcmp(g_last_key, "1000_lossless") == 0,
"re-tap cache key=%s (expect 1000_lossless = 新档位)", g_last_key);
/* sim 账号非会员 → lossless 被 §20 钳顶到 EXHIGH;关键是 eff_level 已按新意图回填
* (UI 据此显示实际生效档,不再停 BUFFER/「缓冲中」)。 */
CHECK(player_manager_eff_level() == NCM_LEVEL_EXHIGH,
"re-tap eff_level=%d (expect EXHIGH=非会员钳顶的新档意图)", player_manager_eff_level());
g_eof = 1;
wait_state(NCM_PC_STOPPED, 3000);
usleep(200 * 1000);
player_manager_stop_thread();
printf(g_fail ? "== PLAYER FLOW SIM: FAIL ==\n" : "== PLAYER FLOW SIM: ALL PASS ==\n");