fix(ncm): 歌单切换两bug — ①暂停态跨歌单点歌旧信息(play同曲护栏改按曲目id判同曲而非裸队列下标,切歌单后同下标换歌被吞play→修play_is_same_song) ②切歌单封面残留(playlist_tick补cover_changed/ack+invalidate重画头块);附带占位行cover_request野id修复;宿主P15/covswitch PASS,热更ncm

This commit is contained in:
AskaEth committed 2026-09-04 00:14:37 +08:00
1 parent 62ec405dca
commit 6eccb17f9b
5 files changed
+150 -17

No files matched your search

+42 -2
View File
@@ -16,6 +16,9 @@
* 修后 seq 变化即重启换档,状态回 PLAYING、新档位下载、eff_level 回填。
* P13 陈旧 hires.mp3(flac 门禁前静默降级产物,hires 键下存 mp3):song_local_file
* 迁移重命名到 exhigh 键(song_<id>_exhigh.mp3)+ 本档视为 miss 走下载(§NEW 任务2)。
* P14 歌单里点「正在播的同曲」→ 只聚焦不重播(任务3 re-entry,同曲 id 判据)。
* P15 跨歌单切换后同一队列下标点歌 → 不被同曲护栏吞,起播(Bug2 回归:真机日志
* `play: re-enter current idx=0 state=2 -> no restart`,新歌永不播)。
*
* 编译(宿主,ncm_app 目录下):
* gcc -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
@@ -61,13 +64,16 @@ void meow_audio_on_preempt(meow_audio_preempt_fn cb, void *ctx) { (void)cb; (voi
/* core/ncm_common.c 提供 ncm_now_ms(墙钟毫秒);本 sim 不链 ncm_common.o,提供同语义桩。 */
int64_t ncm_now_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (int64_t)ts.tv_sec*1000 + ts.tv_nsec/1000000; }
/* ---- appstate stub:窗内全命中,id=1000+idx;fee 可注入(§20 P8) ---- */
/* ---- appstate stub:窗内全命中,id=g_song_base+1000+idx;fee 可注入(§20 P8)----
* g_song_base 模拟「播放队列内容变化」:P14 跨歌单切换后,同一队列下标指向另一首歌
* (base 0→5000,idx=0 由 id=1000 变 id=6000),用于回归 Bug2 的同曲重入护栏。 */
static int g_song_base = 0;
static int g_fee = 0;
int appstate_song_copy(int gidx, ncm_song_t *out)
{
if (gidx < 0 || gidx >= g_st.track_ids_count) return -1;
memset(out, 0, sizeof *out);
snprintf(out->id, sizeof out->id, "%d", 1000 + gidx);
snprintf(out->id, sizeof out->id, "%d", g_song_base + 1000 + gidx);
snprintf(out->name, sizeof out->name, "Song%d", gidx);
out->duration_ms = 180000;
out->fee = g_fee;
@@ -77,6 +83,10 @@ int appstate_song_fetch(int gidx, ncm_song_t *out) { return appstate_song_copy(g
/* §23:player_manager 改走 appstate_queue_*(队列来源)——sim 恒 PLAYLAST 队列 = 窗口 */
int appstate_queue_count(void) { return g_st.track_ids_count; }
int appstate_queue_song(int idx, ncm_song_t *out) { return appstate_song_copy(idx, out); }
/* P14 相关:player_manager_play 的同曲护栏(play_is_same_song)会读队列来源 + 搜索数组;
* sim 恒歌单窗口队列、搜索数组为空。appstate.c 未链入,两符号此处提供桩。 */
int appstate_get_queue(void) { return NCM_QUEUE_PLAYLIST; }
int appstate_search_song(int i, ncm_song_t *out) { (void)i; (void)out; return NCM_ERR_ARG; }
/* ---- 缓存/网络 stub ----
* g_cache_ok=1 → lookup 命中(给真实存在的 /tmp 文件,免下载);
@@ -650,6 +660,36 @@ int main(void)
usleep(200 * 1000);
player_manager_stop_thread();
printf("== P15: 跨歌单切换后同 idx 点歌不被「同曲只聚焦」吞(Bug2 回归)==\n");
/* 线程已停 → 纯逻辑判护栏(无竞态)。手工造「歌单 A 的歌停在 idx=0(id=1000)」的
* 播放快照态;然后模拟切到歌单 B(g_song_base 0→5000:同一队列下标 idx=0 现指向 id=6000
* 的另一首歌——真机日志实锤 `play: re-enter current idx=0 state=2 -> no restart`:新歌
* 永不起播、播放页停在上首+暂停)。护栏改按曲目 id 判同曲后,跨歌单同下标点歌必须起播。 */
pthread_mutex_lock(&g_play.mu);
g_play.cur_song = 0;
g_play.state = NCM_PC_PAUSED;
g_play.play_song_valid = 1;
memset(&g_play.play_song, 0, sizeof g_play.play_song);
snprintf(g_play.play_song.id, sizeof g_play.play_song.id, "1000");
g_play.seq = 90; g_play.pos_ms = 1000; g_play.err = 0;
g_play.dl_done = 0; g_play.dl_total = 0;
pthread_mutex_unlock(&g_play.mu);
int seq_before = g_play.seq; /* 线程已停,无锁读安全 */
/* (a) 同歌单点「正在播的同曲」(同下标、同 id)→ 护栏放行:确证同曲 → 只聚焦不重播 */
g_song_base = 0;
player_manager_play(0);
CHECK(player_manager_state() == NCM_PC_PAUSED && player_manager_playing_song() == 0 &&
g_play.seq == seq_before,
"same-playlist re-tap -> PAUSED/cur=0/seq=%d (expect 不重播)", g_play.seq);
/* (b) 切到歌单 B:队列内容变化(idx=0 现为 id=6000 的另一首)→ 同 idx 点歌必须起播 */
g_song_base = 5000;
player_manager_play(0);
CHECK(player_manager_state() == NCM_PC_BUFFER && g_play.seq == seq_before + 1,
"cross-playlist idx=0 tap -> BUFFER/seq=%d (expect 起播,不被吞)", g_play.seq);
printf(g_fail ? "== PLAYER FLOW SIM: FAIL ==\n" : "== PLAYER FLOW SIM: ALL PASS ==\n");
return g_fail;
}