fix(ncm §33): 流媒专项批六项——流式SIGSEGV防御日志/档位保真(无损级必须flac+显示档按实际)/封面4xx不重试/收藏显隐/缓存命名迁移/topbar位图图标

- §2① ncm_api.c level_attempt: 无损级(hires/lossless及以上)命中必须 type=flac,返回 mp3 视为该档不可用续降
  (修「hires 档静默给 mp3 被当命中」)
- §2② player_manager.c eff_level_for_resp: 显示档按响应实际格式/码率——mp3 绝不显示 Hi-Res(≤128k→标准, 否则极高),
  flac 采信链 eff; br 未知钳 ≤EXHIGH
- §1 流式 SIGSEGV(《十面埋伏》sig=11) 审计+UBSAN 探针未复现 → 落地防御日志(dl start/done/interrupt + play done fin/exp/fail/underrun)
- §3 cover.c: download_cover 回填 rc/status; 确定性 4xx(404等)不重试立即跳下一张, 仅瞬时错误退避(rc=%d status=%d 日志)
- §4 playlist.c: pl_show_fav 我创建(is_collected==0)隐藏收藏钮, 我收藏保留, 搜索歌单保守显示(评论槽并入无空洞)
- §5 ncm_cache.c 双点→单点迁移 + player_manager song_local_file 旧键→当前档位键(不重下)
- §6 home.c: topbar 动作 ⚙↻⌕ 迁移 sb_icons 位图(meow_topappbar_set_item_icon, 修 4k 字库豆腐)
- 测试: test_ncm_core 新 test_grade_flac_gate + test_cache 双点迁移断言; degrade_sim/player_flow_sim 桩 flac 化
- 验证: stream/player_flow/window/search/cache_evict/degrade sim 全 PASS + test_ncm_core host+mipsel(qemu) + mem_sim + build_ncm.sh 0 warning
- 新 ncm md5 cb818786 (text 1017110/bss 1362500), 未部署留主会话; 0 真实 NCM 业务请求
This commit is contained in:
AskaEth committed 2026-09-02 04:20:46 +08:00
1 parent 68ce46a5d8
commit 44a790dc1d
13 files changed
+425 -24

No files matched your search

+22 -4
View File
@@ -66,8 +66,10 @@ static int cover_sink_write(void *ctx, const unsigned char *data, size_t len)
}
/* 下载 url 到 cache 写者 w(流式,写进其临时文件),成功后从临时文件解码并返回 img。
* 失败返回 NULL(不 commit,调用方 abort)。写入 *out_len(body 字节数)。 */
static meow_img *download_cover(const char *url, ncm_cache_writer_t *w, size_t *out_len)
* 失败返回 NULL(不 commit,调用方 abort)。*out_len=body 字节数;*out_rc 与 *out_status
* 回填下载 rc 与 HTTP status(封面 4xx 加固判定:确定性 4xx 不重试,REPORT §NEW 任务3)。 */
static meow_img *download_cover(const char *url, ncm_cache_writer_t *w, size_t *out_len,
int *out_rc, int *out_status)
{
ncm_http_req_t req;
memset(&req, 0, sizeof req);
@@ -80,6 +82,8 @@ static meow_img *download_cover(const char *url, ncm_cache_writer_t *w, size_t *
ncm_http_resp_t resp;
cover_sink_t sink = { w, 0 };
int rc = ncm_http_download(&req, cover_sink_write, &sink, NCM_COVER_MAX, &resp);
if (out_rc) *out_rc = rc;
if (out_status) *out_status = resp.status;
if (rc != NCM_OK || resp.status != 200 || sink.total == 0) return NULL;
if (out_len) *out_len = sink.total;
/* 从写者临时文件解码(未 commit,文件仍在 .tmp;解码到内存 RGBA,与落盘无关) */
@@ -150,6 +154,7 @@ static void *cover_worker(void *arg)
cover_cache_id(url, key, sizeof key);
size_t blen = 0;
meow_img *img = NULL;
int drc = NCM_ERR_NET, dstatus = 0; /* 本目标最近一次下载 rc/HTTP status(4xx 判定用) */
/* ① 缓存命中直接解码(免下载、离线可看;LRU:lookup 刷新 mtime) */
char cpath[512];
@@ -165,7 +170,7 @@ static void *cover_worker(void *arg)
ncm_cache_writer_t *w = NULL;
if (ncm_cache_open(&g_st.ctx.cache, key, ".jpg", &w) == NCM_OK) {
blen = 0;
img = download_cover(url, w, &blen);
img = download_cover(url, w, &blen, &drc, &dstatus);
if (img) {
pthread_mutex_lock(&g_cm);
save_cover_raw(ncm_cache_writer_tmp_path(w), blen);
@@ -184,7 +189,20 @@ static void *cover_worker(void *arg)
/* 内存瘦身:全尺寸封面解码后降采样到 ≤480px 驻留(省 ~7MB anon,OOM 修复)。 */
if (img) img = cover_cap(img);
if (!img) {
/* 失败:指数退避重试,连续 COVER_RETRY_MAX 次放弃(不无声死等;
/* 失败分诊(REPORT §NEW 任务3):确定性 4xx(404 死链等)**不重试**,立即清目标
* 跳下一张(重试也白费);仅瞬时错误(超时/网络/TLS/5xx)指数退避重试。
* 日志打 rc/status(排查资产:先前只见 "fetch failed" 不知 HTTP 层返了什么)。 */
int deterministic = (dstatus >= 400 && dstatus < 500 && dstatus != 408 && dstatus != 429);
meow_log(MEOW_LOG_WARN, "cover: fetch fail url=%.64s rc=%d status=%d%s",
url, drc, dstatus, deterministic ? " (4xx, no retry)" : "");
if (deterministic) {
pthread_mutex_lock(&g_cm);
if (strcmp(g_tgt_url, url) == 0) g_tgt_url[0] = 0; /* 立即跳下一张 */
pthread_mutex_unlock(&g_cm);
backoff_ms = 0; fails = 0;
continue;
}
/* 瞬时错误:指数退避重试,连续 COVER_RETRY_MAX 次放弃(不无声死等;
* 目标保留期间 UI 每帧重请求由 cover_request 去重挡住)。 */
fails++;
if (fails >= COVER_RETRY_MAX) {