From a5f675750be8709e5a8878fc9dc3258e983c5b04 Mon Sep 17 00:00:00 2001 From: MeowOS Date: Tue, 1 Sep 2026 20:31:49 +0800 Subject: [PATCH] =?UTF-8?q?docs(ncm=20=C2=A725):=20REPORT=20=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0/=E4=BF=AE=E6=B3=95/=E6=B5=8B=E8=AF=95=E8=AF=81?= =?UTF-8?q?=E6=8D=AE/=E6=96=B0ncm=20md5=EF=BC=88commit=20a01e5ca=20?= =?UTF-8?q?=E9=85=8D=E5=A5=97=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §25 记录切音质后重播同曲恒「缓冲中」:根因(player_manager.c 同 idx 新 play intent 不被解码主循环识别)、修法(seq 化 intent 护栏 still_intent + 解码主循环 seq 断 + 外层自然播完收紧 state==PLAYING)、测试证据(player_flow_sim 新 P12 + P1-P11 零回归 + stream_sim 全矩阵)、新 ncm md5 28698dd0e。用户所问「歌单离线缓存/手动刷新」顺带核实: 离线缓存未落地(appstate_load_playlists 每次网络拉取),手动刷新已落地。 --- patch_v2/ncm_app/REPORT.md | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/patch_v2/ncm_app/REPORT.md b/patch_v2/ncm_app/REPORT.md index 7117ea6..b6ed131 100644 --- a/patch_v2/ncm_app/REPORT.md +++ b/patch_v2/ncm_app/REPORT.md @@ -2318,3 +2318,77 @@ e767bb75b7f7ec096b9c9dc01791691d REPORT.md # 本批记录(§24) - 重试仅覆盖「预缓冲(<128KB)前网络失败」;开播后失败不重连(§4.1)。`STREAM_RETRY_BACKOFF_MS=800` 为固定值,未做指数退避多档(retry=1 时一档即可)。 - 未动 sdk/、meow_launcherd/、vendor/;部署链路同 §13.5 留主会话。 + +## 25. 修复:切音质后重播同一曲恒「缓冲中」——同 idx 再 play 未重启换档(2026-09-01,commit `a01e5ca`) + +> 范围:仅 `patch_v2/ncm_app/`。真机用户反馈:切音质后再播同一首歌,声音已喷出但播放页 +> 「采样率/格式信息」处恒显示「缓冲中」。风控:真实 NCM 请求 **0 次**(全离线 stub);未部署真机(留主会话)。 + +### 25.1 根因(文件:行) + +`app/play/player_manager.c`:**同 idx 的新播放意图不被解码主循环识别**——旧 `still_current` +(原 L297-303)与解码主循环(原 L564-577)只认 `cur_song != idx` 与 `state == STOPPED`。 + +切音质后重播同一曲 = `player_manager_play(idx)`,而该 idx 恰与在播曲相同,于是 `play` 只改了 +`player_manager.c` L100 `g_play.seq++`、L95 `g_play.state=BUFFER`,`cur_song` 不变。解码主循环 +每轮比对 `cur != idx`(不变→不 break)与 `st == STOPPED`(BUFFER→不 break),导致: + +1. **旧曲(旧档位)继续喷声**(audio 写照旧走),而 `g_play.state` 停在 `BUFFER` → UI + `plr_draw_cover_dyn` 读 `player_manager_state()==BUFFER` → 恒「缓冲中」(`app/ui/player.c` L268-281); + `dl_done/dl_total` 保持 0,缓冲文案退化为无 %、无 KB 的「缓冲中…」(L279)。 +2. **新档位下载/缓存条目从未发起**(换档 = 新缓存键 `song__`,本应走新下载)。 +3. 旧曲播完后外层线程把 `BUFFER` 当「自然播完」→ `player_manager_next(1)`(原 L693)→ **顺延 + 下一首**,新档位永不生效。 + +`still_current` 同理漏判:同 idx 重播时 `cur==idx && state==BUFFER` 恒真,下载/预缓冲/物化/ +解码各护栏均不终止旧 intent。三条路径(缓存命中/边下边播/.part 续传)统一受影响——根因在 +播放意图层,不在某条取数路径。 + +### 25.2 修法(play intent 以 seq 标识,层层判别) + +- 新增 `still_intent(idx, my_seq)`(`player_manager.c` L306): + `cur==idx && state==BUFFER && seq==my_seq`——严格强于旧 `still_current`(同 idx 重播时 seq 变 + 即判过期),替换全部 `still_current` 调用点。 +- `run_song` 入口捕获 `my_seq`(L509),沿 `song_local_file`(L424)/`song_download_attempt` + (L319)下传,预缓冲(L386)/重试退避(L482)/物化(L522)/预缓冲后(L540)/解码 open 失败 + (L556)各护栏改 `still_intent(idx, my_seq)`。 +- **解码主循环**(L597):`seq != my_seq`(同 idx 新 intent)→ `break` 回外层重跑 `run_song` + (换档重播),不再静默续播旧档。 +- **外层线程**「自然播完」收紧为 `st2 == NCM_PC_PLAYING`(L720):不再把 `BUFFER`(挂起的新 + intent)当「播完」顺延下一首;同 idx 新 intent 落外层后按当前 `cur/state` 重跑 `run_song(idx)`。 + +### 25.3 测试证据(0 真实 NCM 请求) + +| 项 | 方式 | 结果 | +|---|---|---| +| 状态机回归 | **player_flow_sim P1-P11 全量**(缓存命中/下载/.part 续传/重试/门禁,0 网络) | 宿主 + qemu-mipsel **ALL PASS**(零回归) | +| 本 bug 复现 | **player_flow_sim 新 P12**(§25):standard 档缓存命中播 song0 → 切 lossless → 同 idx 再 play | 宿主 + qemu **ALL PASS**:状态回 PLAYING、新档位下载发起(downloads=1)、缓存键 `1000_lossless`、eff_level 按非会员钳顶回填 EXHIGH(UI 据此显示实际档位,不再停 BUFFER「缓冲中」)。修前 P12 断言 FAIL(恒 PLAYING 不了、键仍 `1000_standard`、downloads=0、eff=-1) | +| 边下边播全矩阵 | stream_sim 原 16 用例 + 2 resume(PCM 金标准) | 宿主 **ALL PASS**(后台任务复核;player_manager 未动 stream_src 链,理论零影响) | +| core 回归 | test_ncm_core_host + qemu test_ncm_core_mipsel | PASS | +| mipsel 构建 | build_ncm.sh 自有代码 -Wall -Wextra | **0 warning**(set -e 下任何告警即失败) | +| 宿主离屏 | build_ncm_host.sh + NCM_PAGE=player MEOW_UI_DUMP | rc=0、渲染正常(仅 sdk `meow_ui_vol.c` 既有告警,与本批无关) | + +**状态→UI 对应**:`plr_draw_cover_dyn`(`app/ui/player.c` L268)按 `player_manager_state()` 直判 +——`BUFFER` 画「缓冲中…N%」、否则画时间行/采样率。P12 断言 `state==PLAYING` 即等于「UI 显示 +采样率/时间而非缓冲中」。三条路径的 BUFFER→PLAYING 转换分别由 P1(缓存命中)、P6/P7(边下边播 +下载)、P9(.part 206 续传)覆盖,P12 补同 idx 重播(换档)路径。 + +### 25.4 顺带核实(用户问,未改) + +- **歌单列表「离线缓存」:未落地**——`appstate_load_playlists`(`app/logic/appstate.c` L144) + 每次进入/刷新都走 `ncm_api_user_playlist` 网络拉取(存 `g_st.playlists[]` 内存),无磁盘离线 + 缓存;断网时返回错误、列表空态。歌曲级元数据有落盘(songmeta,滑窗出窗不丢),但「我的歌单」 + 列表本身无持久化。 +- **手动刷新按钮:已落地**——home topbar 刷新动作(↻ action0)→ `home_reload()`(`app/ui/home.c` + L221/L253,后台 worker 非阻塞);歌单详情页 `playlist_refresh()`(`app/ui/playlist.c` L263)重拉当前页。 + +### 25.5 md5(本批产物) + +``` +28698dd0e097c3de73b243de1dae5417 ncm # mipsel 静态 App(新;未部署,留主会话) +26480e69c258d56e17464adf0141d21e ncm_host # 宿主离屏调试二进制(重链) +fe6302544ae8e09eb340b257b2731e71 app/play/player_manager.c # §25:seq 化 intent 护栏 + 解码主循环重启 +d9bfe3d7a6eaa76b00161a4e97196eb8 bench/player_flow_sim.c # 新 P12:同 idx 重播换档重启断言 +``` + +(`REPORT.md` 追加 §25;`WORK_STATE.md` 同步锚点见 devdocs。)