feat(ncm §21): 边下边播阶段1+2——stream_src流式源+decoder双路open_cb+下载线程预缓冲/stall联动+P2 HTTP空闲超时+cache fd写者+stream_sim全矩阵PCM金标准(0真实请求,未部署)
This commit is contained in:
1 parent
dbee37e1d6
commit
9e25db77cc
18 files changed
+1335
-136
No files matched your search
@@ -1876,3 +1876,115 @@ ac5b29c40488f34e9220b14105bf63c2 build_ncm_host.sh # +account.c
|
||||
- vipType(login_status profile.vipType 10/11 口径)未用——BIZ_NOTES §1.2 标注非代码证据,
|
||||
以 vip/info 双端点字段判定为准;真机若 vip/info 全挂可再加 profile.vipType 兜底。
|
||||
- meow_sheet 无单项置灰(SDK 不在范围):未解锁档以「·未解锁」标注代替置灰。
|
||||
|
||||
## 21. 边下边播阶段 1+2:stream_src 流式源 + 全链 PCM 金标准仿真(2026-09-01)
|
||||
|
||||
> 范围:仅 `patch_v2/ncm_app/`。按 devdocs/plan/NCM_STREAM_PLAY.md 方案 A 实现
|
||||
> (F1-F7 实测事实为红线)。阶段 3(.part 断点续传 / LRU 防淘汰 / 失败重试 1 次)
|
||||
> **本批明确不做**。风控纪律:真实 NCM 请求 **0 次**;**未部署真机**(留主会话)。
|
||||
|
||||
### 21.1 stream_src:只读流式字节源(app/play/stream_src.h/.c,新)
|
||||
|
||||
- 语义:对**下载中的缓存 .tmp 文件**提供 fread 语义只读视图——只读 fd + `pread`
|
||||
(不动下载者文件位置),mutex/condvar 同步;状态 = written/final/failed/abort/read_pos。
|
||||
- `stream_src_read_cb` 严格 fread 语义:**填满才返回**,真 EOF(final 且读到 written)
|
||||
才短读;100ms timedwait 兜底醒(防 note_written 丢失信号死等)。
|
||||
- 再缓冲迟滞:单次调用累计等待 >500ms 后,要求前方可用 ≥64KB 才放行(仅本次调用
|
||||
尚未产出任何字节时下闸),避免慢网 1 字节一放行的抖动空转。
|
||||
- 统计:underruns(**按「本 read 调用是否发生过等待」计一次**——原 `done==0&&avail==0`
|
||||
口径被 minimp3 refill 模式绕过,实测 wait=4s 但 underruns=0,已修)/ wait_ms /
|
||||
stalled(单次等待 >1s 置位,供播放循环联动 audio_out_pause)。
|
||||
- dr_flac 适配三回调(read/seek/tell)。**SEEK_END 修正**:下载中(!final)按 dr_flac
|
||||
约定返回 FALSE 跳过元数据总长校验;final 后按 final+offset 应答——此前 origin=2 落进
|
||||
SEEK_CUR 分支导致 drflac_open 恒失败(/tmp/df_dbg.c 实证,修复后 44.1k/96k flac 均开成功)。
|
||||
|
||||
### 21.2 decoder 双路切回调(app/play/decoder.h/.c)
|
||||
|
||||
- 新 `ncm_dec_open_src(out, stream_src_t*, ...)`;`ncm_dec_open(path)` 改为包一层
|
||||
静态 stream_src(src_owned=1,dec_close 连带释放)——本地文件路径零行为变化。
|
||||
- mp3 = `mp3dec_ex_open_cb`(`MP3D_SEEK_TO_BYTE | MP3D_DO_NOT_SCAN`,io 存 decoder 内);
|
||||
flac = `drflac_open(stream_src_drflac_read, dec_flac_seek, dec_flac_tell, src, NULL)`
|
||||
(枚举与 drflac_int64 签名不兼容 → 静态适配器转换);格式 sniff 改 `stream_src_peek`。
|
||||
- mp3 seek **不再关重开**:直接 `mp3dec_ex_seek`(F4:open_cb 下 SEEK_TO_BYTE 直达,
|
||||
已下载区 seek 零等待)。
|
||||
|
||||
### 21.3 播放链(app/play/player_manager.c)
|
||||
|
||||
- `song_src_t{src, w, dl_thr, dl_rc, url, maxb, total}`:缓存未命中 → 起 dl_thread 跑
|
||||
`ncm_http_download`(cb:abort 检查 → `ncm_cache_write` → `stream_src_note_written`)。
|
||||
- 预缓冲:`stream_src_wait(128KB, 100ms)` 循环(still_current 护栏,切歌即 abort+join+
|
||||
cache_abort);出口判据 = aborted 或 (failed 且 written<128KB) 才失败——**断流但已过
|
||||
门槛 → 放行**(F5:短读即真 EOF,播已下载部分)。
|
||||
- run_song 走 `ncm_dec_open_src`;解码循环 stalled 沿 → `audio_out_pause(1)`、恢复沿
|
||||
`pause(0)`(停 PCM 时钟不丢流);underruns/wait_ms 统计变化才写 g_play。
|
||||
- 曲尾 `song_src_dispose`:abort+join → dl_rc==OK 则 commit+evict,否则 cache_abort。
|
||||
- g_play 新增 underruns/underrun_ms/stalled(app/ncm_app_internal.h;play/next 清零)。
|
||||
|
||||
### 21.4 HTTP/缓存底座(core/ncm_http.c/.h、core/ncm_cache.c/.h)
|
||||
|
||||
- P2:30s 绝对 deadline 拆为两段——连接/握手/请求/响应头维持 **15s 绝对钳**;
|
||||
hdr_done 后启用 **body 空闲超时 15s**(`NCM_HTTP_BODY_IDLE_MS`,每 ret>0 顺延),
|
||||
WANT_READ/WRITE 按 hdr_done 二选一——慢速长下载不再被总 deadline 误杀,真挂死仍 15s 断。
|
||||
- ncm_cache writer 由 FILE* 改 fd(`open(O_WRONLY|O_CREAT|O_TRUNC)` + write(2) 循环
|
||||
直写;commit = fsync+close+rename 原子落地);新增 `ncm_cache_writer_tmp_path()`
|
||||
访问器(stream_src 按 .tmp 路径 pread,无需知道 writer 内部)。
|
||||
|
||||
### 21.5 验证记录(0 真实 NCM 请求)
|
||||
|
||||
| 项 | 方式 | 结果 |
|
||||
|---|---|---|
|
||||
| 边下边播全矩阵 | **bench/stream_sim.c**(新:假网络线程[速率/抖动/cut40/flaky]→真实 ncm_cache→真实 stream_src→真实 decoder→audio_out null sink+PCM 捕获;金标准=同文件离线解码 memcmp):10MB/s、400kbps、160kbps × normal/seek_dl/seek_wait/cut40/flaky + flac_stream@400kbps = **16 用例** | 宿主 + qemu-mipsel 双平台 **ALL PASS**;正常/断续 PCM 与整首**逐字节一致**,断流与 40% 前缀逐字节一致 |
|
||||
| 首开声延迟 | 同上矩阵实测 | 10MB/s=0.00s(宿主)/0.13s(qemu);400kbps=2.60s/2.69s;160kbps=6.54s/6.57s |
|
||||
| 播放状态机 | player_flow_sim **P1-P8**(适配:ncm_dec_open_src stub、ncm_cache_open 真空 .tmp、commit 移曲尾 dispose → P7 半程先 stop 再等 commit 落地防 stub 污染断言) | **ALL PASS** |
|
||||
| 解码回归 | test_decode 三样本(flac44=132300f/44.1k/16、flac96=192000f/96k/32、mp3=393984f/48k/16);另 `git show HEAD:` 取旧 decoder.c 编 test_decode_old,三样本 WAV 新旧**逐字节一致** | **3/3 PASS** + OLD-VS-NEW IDENTICAL |
|
||||
| qemu-mipsel 解码 | test_decode_mipsel 三样本(重链 +stream_src) | **3/3 PASS**(112×/35.5×/3.2× RT) |
|
||||
| core 回归 | build_core.sh → test_ncm_core_host + qemu-mipsel test_ncm_core_mipsel | **全 PASS**(含 vip 解析/档位表/缓存命名) |
|
||||
| 链路回归 | cache_evict_sim / cover_chain_sim / audiod_chain_sim / degrade_sim / window_sim / pagination_sim / toggle_sim(全部重编新 ncm_cache.c/audio_out.c/appstate.c) | **全 PASS**(audiod 14/14 等) |
|
||||
| mipsel 构建 | build_ncm.sh 自有代码 -Wall -Wextra | **0 warning**;text 969996(+7.1KB vs §20) |
|
||||
| 宿主离屏 | build_ncm_host.sh + home/player/settings MEOW_UI_DUMP | 全 rc=0,播放页渲染核验正常(sdk meow_ui_vol.c 既有告警除外) |
|
||||
|
||||
### 21.6 ⚠️ 断言口径偏差记录(主会话定夺)
|
||||
|
||||
- 任务原断言「首开声 <2s@400kbps」与 **F3 红线物理冲突**:mp3 open_cb 强制预读
|
||||
MINIMP3_IO_SIZE=128KB → 128KB/50KB/s = **2.62s 下限**,<2s 不可达。sim 实测 2.60-2.72s。
|
||||
本批**未擅自偏离已批准方案**,按可达阈值断言(400kbps <4s,注释写明冲突)。
|
||||
若要 <2s,选项:MINIMP3_IO_SIZE 减半至 64KB(→~1.31s,重扫 vendor 头文件默认值,
|
||||
需复测全矩阵);或预缓冲门槛与 IO_SIZE 解耦(首 64KB 即开声,留给 decoder 内部 refill 等)。
|
||||
- flaky 用例 underrun>0 由单用例硬断言改**跨速率聚合断言**:单次断续网是否打空缓冲
|
||||
取决于解码速度(qemu 慢→缓冲消耗慢→宿主 1 次 qemu 0 次,皆正常),PCM 逐字节一致
|
||||
才是硬判据;聚合断言(宿主=3/qemu=1)仍覆盖统计通路功能。
|
||||
|
||||
### 21.7 md5(本批产物 + 改动文件)
|
||||
|
||||
```
|
||||
0dc39a6cf1115fb19644af3cba4c017d ncm # mipsel 静态 App(新;未部署,留主会话)
|
||||
bd95bbaa0fc2ac3a60bce7bb7cc120ab ncm_host # 宿主离屏调试二进制
|
||||
1385361ab09bf4c2ec1db0087451e5c2 app/play/stream_src.c # 新
|
||||
1b803c7f9b70ad3df18689143a1b92a8 app/play/stream_src.h # 新
|
||||
1b0197c6c4f27675e932024740aaeeab app/play/decoder.c # open_cb 双路 + ncm_dec_open_src
|
||||
7fd0b09abbb2b75ab4c04e31211bedb2 app/play/decoder.h
|
||||
fda5d4bb09bb26073b34986aa75cceee app/play/player_manager.c # 下载线程+预缓冲+stall 联动+曲尾 commit
|
||||
62aeff05b618bc6838b81aadd6c7cebb app/ncm_app_internal.h # g_play underruns/underrun_ms/stalled
|
||||
15c73a3ee5bc07ff2c26dbd26ff5e321 core/ncm_http.c # P2 空闲超时
|
||||
b7ed6c165bbc42b36bde43fe634b1cec core/ncm_http.h
|
||||
422181883426e1289115bb9ec6bae986 core/ncm_cache.c # fd writer + tmp_path 访问器
|
||||
fe78a119bde44d57c4cae2e5b276c646 core/ncm_cache.h
|
||||
ce7e1ece2577547d54f6c4cc1cf45bdf bench/stream_sim.c # 新:全矩阵+PCM 金标准
|
||||
36abe7cdcf4681e18fed595fbc65435d bench/player_flow_sim.c # stub 适配+P7 时序修正
|
||||
a26350ee298f63e828e06489fee35b92 build_ncm.sh # +stream_src.c
|
||||
a80ee35f243ac28379cd1adeb61548ed build_ncm_host.sh # +stream_src.c
|
||||
(test/test_ncm_core_host 重编产物随 build_core.sh 更新)
|
||||
```
|
||||
|
||||
### 21.8 诚实出入 / 遗留
|
||||
|
||||
- **真机未部署未验**(留主会话统一)。真机验收点:①缓存未命中歌曲 2-3s 内开声
|
||||
(400kbps 档约 2.6s)②弱网(限速 160-400kbps)播放不卡死、underrun 后自动恢复、
|
||||
stalled 时暂停不走时钟 ③播放中切歌/退出:下载线程即 abort、无残留 .tmp ④断网半曲
|
||||
播已下载部分后自然结束、不重复下载 ⑤完整下完的曲下次命中缓存秒开。
|
||||
- **阶段 3 未做**(方案明确排除):.part 断点续传(断流曲下次仍从头下)、LRU 防淘汰
|
||||
(下载中的 .tmp 可被 evict 边缘情形)、下载失败重试 1 次(现为 0 重试直接失败)。
|
||||
- flac 大元数据块(大封面 PICTURE 块)open 时可能多等下载(drflac 需扫过元数据链才到
|
||||
首帧);实测 44.1k 向量 400kbps 首开声 3.82s(vs mp3 2.60s)即此因。
|
||||
- UI 菊花/缓冲提示未接 g_play.stalled(字段已备,UI 批再做)。
|
||||
- 断续网 underrun 聚合断言为时序容忍口径(§21.6),非判据放松——PCM 金标准不变。
|
||||
Reference in new issue
Block a user