fix(ncm): 三连修 — 封面切歌不更新/缓冲无反馈失败无声/暂停切歌边界 (REPORT §18)
- cover worker 忙等死循环(成功后不清目标,每圈解码+换入+gen++,CPU 100%+每帧整盘重绘) → 成功清目标转怠速; 失败指数退避 0.5-4s 连 8 次放弃+WARN 日志; 去重加在拉目标; cover_get 持锁+cover_put 修绘制期 UAF; g_host_mock 防真网守卫 - 封面兜底污染修复: 不再把单曲 al_pic 写进 g_st.cover_url(否则 al_pic 缺失曲回落成 上一首封面且撞去重 → 封面永停); plr_page_enter 强制重估自愈; pl_page_enter 回填歌单封面 - 缓冲 UI: BUFFER 态「缓冲中… N%」(g_play.dl_done/total, 下载回调累加); 播放错误中文 错误条+snack(旧版 err 无 UI 消费者=无声死等); STOPPED 态播放键=重试; play/next 清 err - 写失败 set_err 修连环跳歌(旧版静默 break 被外层当自然播完,流死时跳完歌单+狂发 song_url); still_current 护栏修下载途中切歌的状态/快照过期覆盖 - 验证(0 真实请求): cover_chain_sim 7 组+player_flow_sim P1-P5 全 PASS, 旧代码对照 T2/P2/P4 必败坐实根因; audiod_chain_sim 14/14; pagination/window/core+qemu 回归 PASS; 宿主+mipsel 自有代码 0 warning; 9 页离屏渲染 rc=0 (含新 mock 页 playerbuf/playererr) - ncm md5 50d41bbf4a379e925699ba013b997ffd (未部署真机, 留主会话)
This commit is contained in:
1 parent
614694ad4a
commit
6200a41b71
13 files changed
+820
-20
No files matched your search
@@ -41,6 +41,15 @@ int64_t player_manager_pos_ms(void) { int64_t p; pthread_mutex_lock(&g_play.mu);
|
||||
int64_t player_manager_dur_ms(void) { int64_t d; pthread_mutex_lock(&g_play.mu); d = g_play.dur_ms; pthread_mutex_unlock(&g_play.mu); return d; }
|
||||
int player_manager_error(void) { int e; pthread_mutex_lock(&g_play.mu); e = g_play.err; pthread_mutex_unlock(&g_play.mu); return e; }
|
||||
|
||||
/* 本曲下载进度(UI 缓冲反馈,§18)。total=0 表示未知(song_url 未给 size / 缓存命中)。 */
|
||||
void player_manager_dl(int64_t *done, int64_t *total)
|
||||
{
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
if (done) *done = g_play.dl_done;
|
||||
if (total) *total = g_play.dl_total;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
}
|
||||
|
||||
/* 播放曲快照(REPORT §17):run_song 起播时物化并拷入 g_play.play_song(mu 保护),
|
||||
* 之后播放/歌词/IPC/UI 一律读快照——窗口滑出当前播放曲不影响播放,不许崩。 */
|
||||
int player_manager_current_song(ncm_song_t *out)
|
||||
@@ -70,6 +79,9 @@ void player_manager_play(int song_idx)
|
||||
g_play.cmd = 0;
|
||||
g_play.state = NCM_PC_BUFFER;
|
||||
g_play.pos_ms = 0;
|
||||
g_play.err = 0; /* 新播放意图清旧错误(UI 错误条随新点歌消失) */
|
||||
g_play.dl_done = 0;
|
||||
g_play.dl_total = 0;
|
||||
g_play.seq++;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
}
|
||||
@@ -101,6 +113,9 @@ void player_manager_next(int dir)
|
||||
g_play.cmd = 0;
|
||||
g_play.state = NCM_PC_BUFFER;
|
||||
g_play.pos_ms = 0;
|
||||
g_play.err = 0;
|
||||
g_play.dl_done = 0;
|
||||
g_play.dl_total = 0;
|
||||
g_play.seq++;
|
||||
}
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
@@ -120,6 +135,10 @@ void player_manager_stop(void)
|
||||
/* ---- 每首歌的播放体(在外层循环内调用)---- */
|
||||
static int download_cb(void *ctx, const unsigned char *data, size_t len)
|
||||
{
|
||||
/* 下载进度反馈(UI「缓冲中… N%」,§18):回调在播放线程,锁内仅累加。 */
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.dl_done += (int64_t)len;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
return ncm_cache_write((ncm_cache_writer_t *)ctx, data, len);
|
||||
}
|
||||
|
||||
@@ -138,6 +157,9 @@ static int song_local_file(const char *id, char *path, size_t psz, int64_t *byte
|
||||
int rc = ncm_api_song_url_auto(g_st.ctx.device_id, g_st.ctx.cookie, id, g_level, &u);
|
||||
if (rc != NCM_OK) return rc;
|
||||
if (!u.url[0]) return NCM_ERR_NOTFOUND;
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.dl_total = (u.size > 0) ? u.size : 0; /* 缓冲进度% 分母(0=未知只转菊花) */
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
|
||||
/* 写前检查:池大小超限先按 LRU 淘汰(此线程尚未开 temp,无在途文件被误删风险) */
|
||||
ncm_cache_evict(&g_st.ctx.cache);
|
||||
@@ -183,6 +205,16 @@ static void set_err(int err, const char *msg)
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
}
|
||||
|
||||
/* 切歌竞态护栏(§18):物化/下载期间用户可能已切歌——只有仍是当前目标且处于 BUFFER
|
||||
* (本次播放意图未过期)才继续,否则直接回外层(外层会以新 cur 重跑 run_song)。 */
|
||||
static int still_current(int idx)
|
||||
{
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
int ok = (g_play.cur_song == idx) && (g_play.state == NCM_PC_BUFFER);
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* 一首歌:下载→解码→输出,直到结束或被打断。返回后由外层处理下一首。 */
|
||||
static void run_song(int idx)
|
||||
{
|
||||
@@ -193,6 +225,7 @@ static void run_song(int idx)
|
||||
int fr = appstate_song_fetch(idx, &s);
|
||||
if (fr != NCM_OK) { set_err(fr, "load failed"); return; }
|
||||
}
|
||||
if (!still_current(idx)) return; /* 物化期间已切歌/停止:不写过期快照 */
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.play_song = s;
|
||||
g_play.play_song_valid = 1;
|
||||
@@ -202,11 +235,13 @@ static void run_song(int idx)
|
||||
char path[512];
|
||||
int rc = song_local_file(s.id, path, sizeof path, &file_bytes);
|
||||
if (rc != NCM_OK) {
|
||||
if (!still_current(idx)) return; /* 下载期间已切歌:静默放弃(缓存已落盘不亏) */
|
||||
const char *m = (rc == NCM_ERR_NOTFOUND) ? "no stream (VIP/copyright)"
|
||||
: (rc == NCM_ERR_AUTH) ? "session expired" : "load failed";
|
||||
set_err(rc, m);
|
||||
return;
|
||||
}
|
||||
if (!still_current(idx)) return; /* 下载完成但已切歌:勿开解码/勿动状态 */
|
||||
ncm_strlcpy(g_path, path, sizeof g_path);
|
||||
g_file_bytes = file_bytes;
|
||||
|
||||
@@ -287,7 +322,12 @@ static void run_song(int idx)
|
||||
break;
|
||||
}
|
||||
int frames = (int)(got / 2);
|
||||
if (audio_out_write(pcm, frames) != frames) break;
|
||||
/* 写失败(流死/audiod 断链):置错停播——旧版静默 break 会被外层当「自然播完」
|
||||
* 自动顺延下一首,流死时连环跳歌并狂发 song_url 请求(§18 修)。 */
|
||||
if (audio_out_write(pcm, frames) != frames) {
|
||||
if (!g_app_quit) set_err(NCM_ERR_IO, "audio write failed");
|
||||
break;
|
||||
}
|
||||
frames_done += frames;
|
||||
/* 更新进度(~200ms 节流)*/
|
||||
int64_t pos = frames_done * 1000 / 44100;
|
||||
|
||||
Reference in new issue
Block a user