ncm 三期三批: 歌曲对象瘦身(1324→608B)+滑动窗口(NCM_SONG_WINDOW=256, 取消1000截断支撑任意长度歌单)+播放快照解耦 — agent-251
- core/ncm_api.h: ncm_song_t 瘦身 id24/name128/art96/al96/pic256/dt int32 (1324B→608B) - appstate.c 滑动窗口: song_ids 全量 trackIds 索引(16384) + songs[256] 窗口感知 (song_valid 位图/window_base/滑窗 memmove 保留重叠), ensure_range 批网格(100)对齐 预取, songmeta.c 盘缓存 <cache_dir>/meta/<id>.bin (回滚/二次进入 0 网络请求) - playlist.c 触底分页退役→视口 ensure(±24 行), 占位行可直点(播放线程 fetch 物化), footer 改视口区间/总数 - 播放兼容: g_play.play_song 快照(mu), run_song 起播物化后全链路(下载/歌词/IPC META/ 播放页 UI)读快照——窗口滑出当前播放曲不崩; 边界 song_count→track_ids_count - 验证(0 真实请求): pagination_sim 全 PASS(5191 无截断/52 批); window_sim 6000 首 全程下滚 750 步视口全物化/song_detail 恰 60 次/回滚+0 请求/播放 fetch 不滑窗; core 宿主+qemu 全 PASS; mipsel -Wall -Wextra 0 warning, bss 2052068→1080900(-47%) - 未部署真机(设备音频线排查中, 部署留主会话)
This commit is contained in:
1 parent
d8898afa09
commit
997f89f879
18 files changed
+987
-209
No files matched your search
@@ -41,6 +41,18 @@ 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; }
|
||||
|
||||
/* 播放曲快照(REPORT §17):run_song 起播时物化并拷入 g_play.play_song(mu 保护),
|
||||
* 之后播放/歌词/IPC/UI 一律读快照——窗口滑出当前播放曲不影响播放,不许崩。 */
|
||||
int player_manager_current_song(ncm_song_t *out)
|
||||
{
|
||||
if (!out) return -1;
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
int ok = g_play.play_song_valid;
|
||||
if (ok) *out = g_play.play_song;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
void player_manager_set_quality(int level, int cache_gb)
|
||||
{
|
||||
g_level = level;
|
||||
@@ -83,7 +95,7 @@ void player_manager_next(int dir)
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
int c = g_play.cur_song + dir;
|
||||
if (c < 0) c = 0;
|
||||
if (c >= g_st.song_count) c = g_st.song_count - 1;
|
||||
if (c >= g_st.track_ids_count) c = g_st.track_ids_count - 1; /* 滑窗:边界=全量索引数 */
|
||||
if (c != g_play.cur_song) {
|
||||
g_play.cur_song = c;
|
||||
g_play.cmd = 0;
|
||||
@@ -100,6 +112,7 @@ void player_manager_stop(void)
|
||||
g_play.cur_song = -1;
|
||||
g_play.state = NCM_PC_STOPPED;
|
||||
g_play.pos_ms = 0;
|
||||
g_play.play_song_valid = 0; /* 快照失效 */
|
||||
g_play.seq++;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
}
|
||||
@@ -111,12 +124,9 @@ static int download_cb(void *ctx, const unsigned char *data, size_t len)
|
||||
}
|
||||
|
||||
/* 得到本地 mp3 路径 + 文件大小。命中缓存直接返回;否则 URL 降级 + 流式落缓存。
|
||||
* 返回 NCM_OK 或负错误码。 */
|
||||
static int song_local_file(int idx, char *path, size_t psz, int64_t *bytes)
|
||||
* 返回 NCM_OK 或负错误码。id 由调用方(run_song)从播放曲快照取,与窗口解耦(§17)。 */
|
||||
static int song_local_file(const char *id, char *path, size_t psz, int64_t *bytes)
|
||||
{
|
||||
const ncm_song_t *s = &g_st.songs[idx];
|
||||
const char *id = s->id;
|
||||
|
||||
/* 缓存命中 → 离线直接播 */
|
||||
if (ncm_cache_lookup(&g_st.ctx.cache, id, ".mp3", path, psz) == NCM_OK) {
|
||||
struct stat st;
|
||||
@@ -176,9 +186,21 @@ static void set_err(int err, const char *msg)
|
||||
/* 一首歌:下载→解码→输出,直到结束或被打断。返回后由外层处理下一首。 */
|
||||
static void run_song(int idx)
|
||||
{
|
||||
/* §17 滑窗兼容:物化目标曲(窗内命中 / 盘缓存 / song_detail 单曲)并整体快照进
|
||||
* g_play.play_song——之后本曲所有字段读快照,窗口滑出当前播放曲不影响播放。 */
|
||||
ncm_song_t s;
|
||||
if (appstate_song_copy(idx, &s) != 0) {
|
||||
int fr = appstate_song_fetch(idx, &s);
|
||||
if (fr != NCM_OK) { set_err(fr, "load failed"); return; }
|
||||
}
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
g_play.play_song = s;
|
||||
g_play.play_song_valid = 1;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
|
||||
int64_t file_bytes = 0;
|
||||
char path[512];
|
||||
int rc = song_local_file(idx, path, sizeof path, &file_bytes);
|
||||
int rc = song_local_file(s.id, path, sizeof path, &file_bytes);
|
||||
if (rc != NCM_OK) {
|
||||
const char *m = (rc == NCM_ERR_NOTFOUND) ? "no stream (VIP/copyright)"
|
||||
: (rc == NCM_ERR_AUTH) ? "session expired" : "load failed";
|
||||
@@ -188,8 +210,7 @@ static void run_song(int idx)
|
||||
ncm_strlcpy(g_path, path, sizeof g_path);
|
||||
g_file_bytes = file_bytes;
|
||||
|
||||
const ncm_song_t *s = &g_st.songs[idx];
|
||||
int64_t dur = s->duration_ms > 0 ? s->duration_ms : 0;
|
||||
int64_t dur = s.duration_ms > 0 ? s.duration_ms : 0;
|
||||
|
||||
mp3dec_ex_t dec;
|
||||
memset(&dec, 0, sizeof dec);
|
||||
@@ -205,7 +226,7 @@ static void run_song(int idx)
|
||||
g_play.dur_ms = dur;
|
||||
g_play.err = 0;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
meow_log(MEOW_LOG_INFO, "play: start idx=%d id=%s dur=%lldms", idx, s->id, (long long)dur);
|
||||
meow_log(MEOW_LOG_INFO, "play: start idx=%d id=%s dur=%lldms", idx, s.id, (long long)dur);
|
||||
|
||||
mp3d_sample_t pcm[PCM_CHUNK];
|
||||
int64_t frames_done = 0; /* 已输出帧(每通道) */
|
||||
@@ -291,7 +312,7 @@ static void *player_thread(void *arg)
|
||||
int st = g_play.state;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
|
||||
if (cur < 0 || cur >= g_st.song_count || st == NCM_PC_STOPPED) {
|
||||
if (cur < 0 || cur >= g_st.track_ids_count || st == NCM_PC_STOPPED) {
|
||||
usleep(60 * 1000);
|
||||
continue;
|
||||
}
|
||||
@@ -303,9 +324,9 @@ static void *player_thread(void *arg)
|
||||
int st2 = g_play.state;
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
/* 自然播完且无新命令 → 顺延下一首;否则回到循环等待 */
|
||||
if (cur2 >= 0 && cur2 < g_st.song_count && st2 != NCM_PC_STOPPED && cur2 == cur) {
|
||||
if (cur2 >= 0 && cur2 < g_st.track_ids_count && st2 != NCM_PC_STOPPED && cur2 == cur) {
|
||||
int nx = cur + 1;
|
||||
if (nx < g_st.song_count) { player_manager_next(1); }
|
||||
if (nx < g_st.track_ids_count) { player_manager_next(1); }
|
||||
else { player_manager_stop(); }
|
||||
} else {
|
||||
usleep(50 * 1000);
|
||||
@@ -316,23 +337,27 @@ static void *player_thread(void *arg)
|
||||
|
||||
/* ---- 歌词后台加载(二期):UI 不阻塞 —— 持久 worker 监视当前播放歌曲,首播/切歌/自动顺延
|
||||
* 时在独立线程拉取并解析,UI 经 lyric_state() 判「加载中 / 无词」。 ----
|
||||
* 现存 race(§10.9 FM4,UI 读 lyrics 与 worker 写)为良性:数组定长、越界已防护,
|
||||
* 最坏显示一帧旧/空行,下一 tick 自愈;与 g_st.songs 写读竞态同接受。 */
|
||||
* §17:曲目 id 改读播放快照(g_play.play_song,mu 保护),不再依赖窗口内
|
||||
* g_st.songs[]——窗口滑出当前播放曲歌词照常。 */
|
||||
static pthread_mutex_t g_lyrmu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_t g_lyr_thr;
|
||||
static int g_lyr_thr_on;
|
||||
static volatile int g_lyr_state; /* 0=空闲/未拉 1=加载中 2=已拉完 */
|
||||
static char g_lyr_song_id[32]; /* 歌词归属歌曲 id */
|
||||
static char g_lyr_song_id[24]; /* 歌词归属歌曲 id */
|
||||
|
||||
static void *lyric_worker(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
char last_id[32] = "";
|
||||
char last_id[24] = "";
|
||||
while (!g_app_quit) {
|
||||
usleep(300 * 1000);
|
||||
int idx;
|
||||
pthread_mutex_lock(&g_play.mu); idx = g_play.cur_song; pthread_mutex_unlock(&g_play.mu);
|
||||
if (idx < 0 || idx >= g_st.song_count) {
|
||||
char id[24];
|
||||
pthread_mutex_lock(&g_play.mu);
|
||||
int idx = g_play.cur_song;
|
||||
int have = g_play.play_song_valid;
|
||||
ncm_strlcpy(id, have ? g_play.play_song.id : "", sizeof id);
|
||||
pthread_mutex_unlock(&g_play.mu);
|
||||
if (idx < 0 || idx >= g_st.track_ids_count || !have) {
|
||||
lyric_clear();
|
||||
pthread_mutex_lock(&g_lyrmu);
|
||||
g_lyr_song_id[0] = 0;
|
||||
@@ -341,7 +366,6 @@ static void *lyric_worker(void *arg)
|
||||
last_id[0] = 0;
|
||||
continue;
|
||||
}
|
||||
const char *id = g_st.songs[idx].id;
|
||||
if (!id[0]) continue;
|
||||
if (strcmp(id, last_id) == 0) continue; /* 同一曲已拉/在拉 */
|
||||
|
||||
|
||||
Reference in new issue
Block a user