Bug1: SDK meow_ui_media 按 g_last_state 本地判 play/pause,但 ncm 走自有 ipc_media 线程不经 SDK meta 上报 → g_last_state 恒 0 → 恒发 cmd=1(播放),播放中按键无效。 修:SDK 164 恒发 cmd=6 toggle(删 g_last_state 判定),ncm apply_playctl case 6 按 player_manager_state() 当前态翻转(源是状态权威);launcher meow_media/util 本就透传 cmd 不动结构,仅 meow_ipc.h/SPEC_REF 注释补 6=toggle。媒体卡片/NP 页 按钮(按 launcher 侧 s->state 判,来自 META 是准的)维持现状。 Bug2: App 前台时状态栏为 launcher 叠加层,App 内 SDK 改音量/亮度只写 settings.conf+DAC,launcher meow_settings 内存表不感知 → g_sys.vol 停旧值。 修:app_overlay_statusbar 前插 app_overlay_settings_poll——1s 节流 stat settings.conf,mtime 变才 meow_settings_init 重读+sync 缓存+g_sys.vol/brightness (meow_settings 新增 meow_settings_file_path() 访问器防硬编码分叉)。 验证:宿主 test_sdk_input_keys 19/19(K4 改 toggle 断言)、test_media +cmd=6 透传用例 ALL PASS、ncm bench/toggle_sim 10/10(新)、player_flow/cache_evict sim PASS、ncm core 宿主+qemu-mipsel PASS、test_settings_brightness PASS; mipsel 交叉 -Wall -Wextra 0 warning(改动文件单独验+整构建)。 真机(0.7.91):ncm 热更(.new+mv)后 autoplay(Docu) 起播缓存曲 → meow_ipc playctl 6 二连发 → paused(pos=139691)/resumed 日志实证 toggle 全链路。 md5:ncm 8f10eee5 / meow_ui.a 7b359ec9(gitignore)/ launcher eba4f2b2 (/usr/bin 只读,fw_content.conf 已更新待固件)。物理键 164 实体按压留用户终验。
358 lines
20 KiB
C
358 lines
20 KiB
C
/* ncm_app_internal.h — dev.player.ncm 应用内部共享头(UI 各页 + app 装配层 + 宿主测试面)。
|
||
*
|
||
* 层级(对齐 hello_app/app.c 批② + files_app):
|
||
* - app.c main + 装配 + 事件分发(把 meow_ui_run 的平铺回调路由到各页)
|
||
* - app/ui/<page>.c 每页一个 view 文件(login/home/playlist/player/settings)
|
||
* - app/ui/common/chrome.c 共享 chrome(topbar / 内容区几何 / 空态 / 提示)
|
||
* - app/logic/ appstate(全局 ctx/歌单/歌曲/播放态)+ lyric(LRC 解析) + cover(封面缓存)
|
||
* - app/play/ player_manager(播放线程) + audio_out(audiod/ALSA/文件输出) + ipc_media(媒体会话 IPC)
|
||
*
|
||
* 铁律(APPS.md §3.4.1 / WORK_STATE.md):物理键禁映射——on_key 不处理任何键语义;
|
||
* 返回=左缘左滑(on_swipe LEFT)/返回钮/meow_ui_request_exit;左缘返回:播放页→歌单→主页→退出。
|
||
* 播放/网络/IPC 用独立线程,UI 不卡。媒体会话经 meow_ipc.h 帧格式最小客户端上报
|
||
* MSG_META + 收 MSG_PLAYCTL(SDK 尚无媒体封装,App 内实现)。
|
||
*/
|
||
#ifndef NCM_APP_INTERNAL_H
|
||
#define NCM_APP_INTERNAL_H
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
#include <pthread.h>
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_view.h"
|
||
#include "ncm.h" /* 核心库伞头(ctx/api/login/cache/...) */
|
||
|
||
/* ---- Activity 归属(§6.1 ①)---- */
|
||
#define ACT_LOGIN 0 /* 扫码登录页(根,未登录) */
|
||
#define ACT_HOME 1 /* 我的歌单(根/主页,已登录) */
|
||
#define ACT_PLAYLIST 2 /* 歌单详情(曲目列表) */
|
||
#define ACT_PLAYER 3 /* 播放页(可横竖屏切,v1 允许切) */
|
||
#define ACT_SETTINGS 4 /* 设置页 */
|
||
#define ACT_SEARCH 5 /* 搜索页(顶栏搜索按钮进入) */
|
||
|
||
/* ---- 布局 ---- */
|
||
#define APP_W 480
|
||
#define APP_H 720
|
||
#define APP_TOPBAR_H 56
|
||
#define APP_ROW_H 56 /* 列表行高 */
|
||
#define APP_PAD 16
|
||
#define APP_OP_W 48 /* 行右侧操作区触达宽 */
|
||
#define APP_FOOTER_H 26
|
||
|
||
/* ---- 数据规模(内存纪律:歌单/歌曲大列表虚拟化显示,数组有界;硬约束 R3P2 ~14MB)---- */
|
||
#define NCM_PLAYLIST_MAX 120 /* 我的歌单上限 */
|
||
/* 滑动窗口(REPORT §17,三期第三批,取代 §14 的 NCM_SONG_MAX=1000 硬截断):
|
||
* song_ids[NCM_TRACKID_MAX] = detail(/v6/playlist/detail, n=1)取回的全量 trackIds
|
||
* (int64×16384=128KB bss,支撑任意长度歌单,不再 1000 截断);
|
||
* songs[NCM_SONG_WINDOW] = 物化滑动窗口(256 首 × ~608B ≈ 152KB bss,与歌单长度无关);
|
||
* song_valid[NCM_SONG_WINDOW]= 槽位有效位图(songs[i] ↔ 全局下标 window_base+i);
|
||
* 滚动出窗的槽位失效(数据已序列化到 <cache_dir>/meta/<id>.bin),入窗经 song_detail 按需
|
||
* 拉取(盘缓存命中免请求)。列表 count = track_ids_count(全量),占位行驱动后台 ensure。 */
|
||
#define NCM_TRACKID_MAX 16384 /* 全量 trackIds 索引上限(128KB bss) */
|
||
#define NCM_SONG_WINDOW 256 /* 滑动窗口大小(内存物化歌曲对象数上限) */
|
||
#define NCM_SONG_BATCH 100 /* song_detail 分批每批条数(≤500 安全窗内取保守 100;
|
||
* 窗口补载粒度;c 数组缓冲 clist[4096] 装 100 首 ≈2KB) */
|
||
#define NCM_LYRIC_LINES 600 /* 歌词行上限 */
|
||
#define NCM_COVER_MAX 288*1024 /* 封面下载限幅 ~288KB */
|
||
#define NCM_SEARCH_MAX 32 /* 搜索结果上限(v1 只显示前 32 条,无大缓冲) */
|
||
|
||
/* ---- 播放/媒体会话状态(play 线程 ↔ UI/IPC 共享,mu 保护)---- */
|
||
enum {
|
||
NCM_PC_STOPPED = 0,
|
||
NCM_PC_PLAYING = 1,
|
||
NCM_PC_PAUSED = 2,
|
||
NCM_PC_BUFFER = 3, /* 缓冲/加载中 */
|
||
};
|
||
|
||
/* 播放器命令(IPC MSG_PLAYCTL cmd 一一对应)*/
|
||
enum {
|
||
NCM_CTL_PLAY = 1,
|
||
NCM_CTL_PAUSE = 2,
|
||
NCM_CTL_PREV = 3,
|
||
NCM_CTL_NEXT = 4,
|
||
NCM_CTL_SEEK = 5,
|
||
NCM_CTL_TOGGLE = 6, /* 播放/暂停切换(物理键 164 恒发此;按当前态翻转) */
|
||
};
|
||
|
||
/* 跨线程播放运行时状态(main 只读展示;play 线程写,IPC 线程读)。 */
|
||
typedef struct ncm_play_run {
|
||
pthread_mutex_t mu;
|
||
int state; /* NCM_PC_* */
|
||
int cur_song; /* 当前曲目在 g_st.songs[] 下标;-1=无 */
|
||
int64_t pos_ms; /* 当前播放位置(播放进程推进,seek 写此) */
|
||
int64_t dur_ms; /* 当前曲目标称时长 */
|
||
int cmd; /* 暂存最新内部命令(play/pause/prev/next/seek)*/
|
||
int64_t cmd_seek_ms; /* cmd==SEEK 目标 */
|
||
int seq; /* 播放代际(每次 start 递增,用于区分过期信号)*/
|
||
int err; /* 最近一次播放错误(0=无) */
|
||
char errmsg[96];
|
||
int64_t dl_done; /* 本曲已下载字节(缓冲期进度反馈,§18) */
|
||
int64_t dl_total; /* 本曲标称大小(song_url 返回;0=未知) */
|
||
int has_track; /* 当前是否有可播曲目(含 url 获取中) */
|
||
/* 播放曲快照(REPORT §17):run_song 起播时把歌曲对象整体拷入(mu 保护),
|
||
* 之后播放/歌词/IPC/UI 全读快照而非窗口——窗口滑出当前播放曲也不影响播放。 */
|
||
ncm_song_t play_song;
|
||
int play_song_valid; /* 1=快照有效(stop 清 0) */
|
||
} ncm_play_run_t;
|
||
|
||
/* ---- 音质档位字符串(设置页 + 持久化用)---- */
|
||
extern const char *media; /* 占位 */
|
||
|
||
/* ---- App 全局状态(main 线程拥有;play 线程经 ncm_play_run 共享)---- */
|
||
typedef struct ncm_app_state {
|
||
ncm_ctx_t ctx; /* 核心库上下文(device_id/cookie/cache) */
|
||
char data_dir[512]; /* 数据目录(MeowData/dev.player.ncm/data) */
|
||
char cache_dir[512];
|
||
|
||
/* 登录 */
|
||
char unikey[64];
|
||
char qr_url[256];
|
||
unsigned char qr_mods[177*177]; /* QR 位图矩阵(1=暗) */
|
||
int qr_size;
|
||
int login_state; /* NCM_LOGIN_* */
|
||
int logged_in;
|
||
|
||
/* 账号 */
|
||
char uid[32];
|
||
char nickname[128];
|
||
|
||
/* 我的歌单 */
|
||
ncm_playlist_t playlists[NCM_PLAYLIST_MAX];
|
||
int playlist_count;
|
||
int active_playlist; /* 当前打开歌单的下标;-1=无 */
|
||
|
||
/* 当前歌单曲目(id 用 int64_t:网易云歌曲 id 可 >2^31,MIPS32 long 溢出)。
|
||
* 滑动窗口模型(REPORT §17):
|
||
* song_ids[] = detail 返回的全量 trackIds(≤ NCM_TRACKID_MAX,全局下标索引);
|
||
* songs[] = 物化滑动窗口,songs[i] ↔ 全局下标 window_base+i;
|
||
* song_valid[] = 槽位有效位图(出窗=失效,入窗=物化后置位);
|
||
* window_base = 窗口起始全局下标;song_count = 窗口内有效槽位数(≤ NCM_SONG_WINDOW);
|
||
* track_ids_count = trackIds 总数(列表 count = track_ids_count,任意长度无截断)。
|
||
* 窗口读写由 appstate.c 内 g_song_mu 保护;外部一律走 appstate_song_copy/ensure_range。 */
|
||
int64_t song_ids[NCM_TRACKID_MAX];
|
||
ncm_song_t songs[NCM_SONG_WINDOW];
|
||
uint8_t song_valid[NCM_SONG_WINDOW];
|
||
int window_base;
|
||
int song_count;
|
||
int track_ids_count; /* detail 取回的 trackIds 总数(≤ NCM_TRACKID_MAX) */
|
||
|
||
/* 播放页视图态 */
|
||
int playing_song; /* 正在播放歌曲在 songs[] 下标(UI 侧缓存);-1=无 */
|
||
meow_img *cover_img; /* 当前封面(解码后;UI 持有,play 线程不碰) */
|
||
char cover_url[512]; /* 当前封面 URL(playlist/song cover) */
|
||
|
||
/* 歌词(播放器加载后由 play 线程填充,UI 读)*/
|
||
pthread_mutex_t lyric_mu;
|
||
int lyric_line_count;
|
||
int cur_lyric_line;
|
||
|
||
/* 设置(持久化 MeowData/dev.player.ncm/data)*/
|
||
int level; /* NCM_LEVEL_* */
|
||
int cache_limit_gb;
|
||
|
||
/* 搜索(结果复用 g_st.songs[] 浅拷贝区;仅搜索页上下文有效)*/
|
||
char search_query[256];
|
||
int search_count; /* 当前搜索结果数(含 0=无/未搜) */
|
||
volatile int search_loading; /* 1=搜索请求在途 */
|
||
int search_err; /* 最近一次搜索错误(0=ok) */
|
||
} ncm_app_state_t;
|
||
|
||
extern ncm_app_state_t g_st; /* 全局状态(app.c 定义) */
|
||
extern ncm_play_run_t g_play; /* 播放运行态(app.c 定义) */
|
||
extern volatile int g_app_quit; /* 请求退出(线程停) */
|
||
extern volatile int g_net_ready; /* 网络/核心库线程安全可用 */
|
||
extern int g_host_mock; /* 1=宿主/测试:跳过网络,用预置数据 */
|
||
|
||
/* ---- App 侧调色板(ui/common/ncm_colors.c)----
|
||
* 品牌红 + 播放页/登录专属色。中性色由 meow_theme_get() 语义色提供。§3.2/§3.3/§5.4。 */
|
||
typedef struct ncm_pal {
|
||
uint32_t red; /* 主 accent:day #C20C0C / night #EC4141 */
|
||
uint32_t red_lit; /* 亮点变体:day #EC4141 / night #FF6A6A */
|
||
uint32_t red_ctr; /* 红底浅块:day #FBE9E9 / night #3A1919 */
|
||
uint32_t on_red; /* 红底前景(#FFFFFF) */
|
||
uint32_t on_red_dim; /* 红底次前景(白 ~88%,文本用) */
|
||
uint32_t hero; /* 登录 hero 深红压场:day #C20C0C / night #7A0E0E */
|
||
uint32_t player_bg; /* 播放页沉浸底:day #FFFDF9 / night #0C0D10 */
|
||
uint32_t disc; /* 黑胶盘体:day #1A1A1A / night #141414 */
|
||
uint32_t disc_edge; /* 盘缘槽纹:day #2A2A2A / night #232323 */
|
||
} ncm_pal_t;
|
||
extern int g_dark; /* 1=黑红(night) 0=白红(day) */
|
||
const ncm_pal_t *ncm_palette(int dark); /* dark==g_dark */
|
||
void ncm_on_theme(meow_theme_mode m); /* 主题侦听:明暗变化 → 整 App 重绘 */
|
||
void ncm_fill_round(int x, int y, int w, int h, int r, uint32_t color); /* 填充圆角矩形 */
|
||
void ncm_draw_button(int x, int y, int w, int h, const char *label,
|
||
uint32_t bg, uint32_t fg); /* App 侧品牌红/FILLED 按钮 */
|
||
int first_glyph(const char *s, char *out, size_t sz); /* 取首个 UTF-8 字形(头像首字) */
|
||
const char *ncm_text_fit(const char *s, int maxw, int size); /* 按宽裁剪("..."结尾;静态缓冲) */
|
||
|
||
/* ---- 通用矩形 ---- */
|
||
typedef struct { int x, y, w, h; } ncm_rect_t;
|
||
#define NR(x,y,w,h) ((ncm_rect_t){ (x),(y),(w),(h) })
|
||
|
||
/* ---- chrome(ui/common/chrome.c)---- */
|
||
int ncm_statusbar(void);
|
||
ncm_rect_t ncm_topbar_rect(void);
|
||
ncm_rect_t ncm_content_rect(void);
|
||
meow_widget *ncm_topbar_create(const char *title, int back, const char *action_label);
|
||
void ncm_draw_empty(ncm_rect_t r, const char *kind, const char *what);
|
||
void ncm_draw_footer(ncm_rect_t r, const char *text);
|
||
void ncm_snack(const char *text); /* 底部 2s 提示条(非阻塞) */
|
||
void chrome_tick(void); /* 周期检查 snack 到期清除 */
|
||
|
||
/* ---- 页面(ui/<page>.c)---- */
|
||
/* login */
|
||
meow_view *login_view_create(void);
|
||
int login_is_active(void);
|
||
void login_on_tap(int x, int y);
|
||
void login_tick(uint64_t now);
|
||
int login_start(void); /* 触发 unikey 获取(宿主测试:无网络则返回) */
|
||
const char *login_status_str(int state);
|
||
|
||
/* home */
|
||
meow_view *home_view_create(void);
|
||
int home_is_active(void);
|
||
int home_on_tap(int x, int y);
|
||
void home_swipe(int dir, int dist);
|
||
void home_touch_down(int x, int y);
|
||
void home_touch_move(int x, int y);
|
||
void home_reload(void); /* 刷新我的歌单(后台 worker,非阻塞) */
|
||
void home_tick(uint64_t now); /* UI tick:检查歌单 worker 是否完成并刷新 */
|
||
int home_go_up(void); /* 左缘返回:到底则退出 */
|
||
|
||
/* playlist */
|
||
meow_view *playlist_view_create(void);
|
||
int playlist_is_active(void);
|
||
int playlist_on_tap(int x, int y);
|
||
void playlist_swipe(int dir, int dist);
|
||
void playlist_touch_down(int x, int y);
|
||
void playlist_touch_move(int x, int y);
|
||
void playlist_open(int idx); /* 打开第 idx 个歌单(下拉进歌单详情) */
|
||
void playlist_refresh(void);
|
||
void playlist_tick(uint64_t now);/* UI tick:检查歌单 worker 是否完成并刷新 */
|
||
|
||
/* player */
|
||
meow_view *player_view_create(void);
|
||
int player_is_active(void);
|
||
void player_on_tap(int x, int y);
|
||
void player_tick(uint64_t now); /* 播放进度/歌词滚动 */
|
||
void player_play_song(int idx); /* 从歌单第 idx 首开始播放 */
|
||
void player_pause_toggle(int on);
|
||
void player_next(int dir); /* dir=+1 下一首 / -1 上一首 */
|
||
void player_seek(int64_t ms);
|
||
void player_on_swipe(int dir, int dist); /* 横屏切换/返回 */
|
||
int player_set_screen(int landscape); /* 1=横屏 0=竖屏;返回之前 */
|
||
int player_screen(void);
|
||
|
||
/* settings */
|
||
meow_view *settings_view_create(void);
|
||
int settings_is_active(void);
|
||
int settings_on_tap(int x, int y);
|
||
void settings_swipe(int dir, int dist);
|
||
void settings_touch_down(int x, int y);
|
||
void settings_touch_move(int x, int y);
|
||
void settings_open(void); /* 进设置页 */
|
||
void settings_reload(void);
|
||
|
||
/* search */
|
||
meow_view *search_view_create(void);
|
||
int search_is_active(void);
|
||
int search_on_tap(int x, int y);
|
||
void search_swipe(int dir, int dist);
|
||
void search_touch_down(int x, int y);
|
||
void search_touch_move(int x, int y);
|
||
void search_open(void); /* 进搜索页 */
|
||
void search_submit(const char *kw); /* 用户确认后发起搜索(风控:仅在确认时发) */
|
||
void search_tick(uint64_t now); /* UI tick:检查搜索 worker 完成并刷新 */
|
||
|
||
/* ---- logic ---- */
|
||
int appstate_init(void); /* 初始化 ctx/data/cookie/设置;0=成功 */
|
||
void appstate_load_settings(void);
|
||
void appstate_save_settings(void);
|
||
void appstate_set_logged_in(const char *uid, const char *nickname);
|
||
void appstate_logout(void);
|
||
int appstate_ensure_uid(void); /* 若 uid 缺失则 login_status_check 取回;0=成功 */
|
||
int appstate_load_playlists(void); /* 拉我的歌单 */
|
||
int appstate_load_playlist_songs(int idx); /* 兼容:载索引 + 首个窗口(内部走 ensure) */
|
||
int appstate_load_playlist_songs_page(int idx, int offset, int limit);
|
||
/* 兼容入口(滑动窗口语义,REPORT §17):
|
||
* offset=0 重头(detail 全量 trackIds 重建索引、清窗口、
|
||
* 物化 [0,limit));offset>0 = 物化 [offset,offset+limit)
|
||
* (滑窗+盘缓存+song_detail 补载,内部 ensure_range)。 */
|
||
int appstate_song_ensure_range(int lo, int hi);
|
||
/* 滑动窗口核心:确保全局下标 [lo,hi) 已物化。
|
||
* 窗口未覆盖先滑窗(重叠区保留,出窗槽位失效);
|
||
* 缺失槽位先读盘缓存(<cache_dir>/meta/<id>.bin),
|
||
* miss 攒批 ≤NCM_SONG_BATCH 走 song_detail,成功落盘。
|
||
* worker 线程调用(含网络);返回 NCM_OK/负错误码。 */
|
||
int appstate_range_loaded(int lo, int hi); /* 1=[lo,hi) 全部在窗内且有效(UI 判是否需 ensure) */
|
||
int appstate_song_copy(int gidx, ncm_song_t *out); /* 锁内读窗:0=已物化拷出;-1=窗外/未物化 */
|
||
int appstate_song_fetch(int gidx, ncm_song_t *out); /* 任意全局下标物化(窗→盘→song_detail 单曲),
|
||
* 不滑窗(播放线程用,避免与浏览视口抢窗口) */
|
||
void appstate_adopt_songs(int n); /* 收编外部已填的 songs[0..n)(搜索结果)为窗口 base=0,
|
||
* 同步 song_ids/track_ids_count(播放上下文) */
|
||
void appstate_apply_playlist_cover(int idx);
|
||
void appstate_set_playing_song(int idx, const char *cover_url);
|
||
int appstate_current_playing(void); /* 返回当前播放歌曲下标(UI) */
|
||
void autoplay_maybe(void); /* 真机联调 hook(/tmp/ncm_autoplay 触发,自动播已缓存曲目,一次性) */
|
||
|
||
/* lyric */
|
||
int lyric_parse(const char *lrc, const char *tlrc); /* 填充 g_lyric_* */
|
||
int lyric_line_at(int64_t ms); /* 当前行 */
|
||
const char *lyric_line_text(int i);
|
||
const char *lyric_line_tl(int i);
|
||
int lyric_count(void);
|
||
void lyric_clear(void);
|
||
int lyric_state(void); /* 0=空闲/未拉 1=加载中 2=已拉完(可能 0 行=无词) */
|
||
|
||
/* cover */
|
||
void cover_request(const char *url); /* 提交目标封面 URL(后台线程下载+解码) */
|
||
meow_img *cover_get(void); /* 当前封面(**持锁返回**;NULL=无;用后必须 cover_put) */
|
||
void cover_put(void); /* 配对 cover_get 释放锁(§18 修绘制期 UAF) */
|
||
int cover_changed(void); /* 1=有新封面待绘制(UI tick 判变) */
|
||
void cover_ack(void); /* UI 消费一次(已见当前版本) */
|
||
const char *cover_current_path(void); /* 封面原始文件路径(供 launcher 媒体卡片读);NULL=无 */
|
||
void cover_cache_reset(void);
|
||
|
||
/* ---- play(app/play/)---- */
|
||
/* audio_out(后端:默认 SINK_AUDIOD=meow_audiod 独占服务;env NCM_AUDIO_SINK=file/null/alsa
|
||
* 或 MEOW_AUDIO_DIRECT=1 选调试后端,见 audio_out.c 文件头) */
|
||
int audio_out_open(int sample_rate, int channels, int bits); /* 0=成功;bits∈{16,32}(§19 flac>16bit→32) */
|
||
int audio_out_write(const void *samples, int frames); /* <0=错误/结束;样本位宽=open 的 bits */
|
||
int audio_out_pause(int paused); /* 1=暂停(停 PCM 时钟不丢流) 0=恢复(流死自动重开);非 AUDIOD 后端恒 0 */
|
||
void audio_out_close(void);
|
||
void audio_out_set_target(int ms); /* 预留:缓冲目标 */
|
||
int audio_out_buffered_ms(void);
|
||
|
||
/* player_manager:播放线程(下载+解码+输出)驱动 */
|
||
int player_manager_start(void); /* 起播放线程;0=成功 */
|
||
void player_manager_stop(void); /* 停播(清曲目/置 STOPPED) */
|
||
void player_manager_stop_thread(void);/* 停线程(app 退出时 join) */
|
||
void player_manager_play(int song_idx); /* 加载并播放 */
|
||
void player_manager_pause(int on); /* on=1 暂停 */
|
||
void player_manager_next(int dir); /* dir=+1/-1 */
|
||
void player_manager_seek(int64_t ms);
|
||
void player_manager_set_quality(int level, int cache_gb);
|
||
int player_manager_state(void); /* NCM_PC_* */
|
||
int player_manager_playing_song(void);
|
||
int64_t player_manager_pos_ms(void);
|
||
int64_t player_manager_dur_ms(void);
|
||
int player_manager_load_lyric(void); /* 为当前曲目拉歌词并解析 */
|
||
int player_manager_error(void);
|
||
int player_manager_current_song(ncm_song_t *out); /* 播放曲快照锁内拷贝;0=有效 -1=无(REPORT §17) */
|
||
void player_manager_dl(int64_t *done, int64_t *total); /* 本曲下载进度(字节;total=0 未知,§18) */
|
||
|
||
/* songmeta(app/logic/songmeta.c):歌曲元数据本地缓存(<cache_dir>/meta/<id>.bin),
|
||
* 拉过的 song_detail 结果序列化落盘,再次入窗免重复请求(REPORT §17)。 */
|
||
int songmeta_load(const char *cache_dir, const char *song_id, ncm_song_t *out); /* NCM_OK / NCM_ERR_NOENT */
|
||
int songmeta_save(const char *cache_dir, const char *song_id, const ncm_song_t *s);
|
||
|
||
/* ipc_media:媒体会话上报/收指令 */
|
||
int ipc_media_start(void); /* 起 IPC 线程;0=成功 */
|
||
void ipc_media_stop(void);
|
||
|
||
/* ---- 宿主测试面(app/test/test_ncm_app.c 驱动真实页面)---- */
|
||
void app_host_set_mock(int mode); /* 0=无 1=home 2=playlist 3=player 4=settings 5=login */
|
||
void app_host_paint_page(int page); /* 构建指定页并整帧绘制到 back_buf(供 dump)*/
|
||
|
||
#endif /* NCM_APP_INTERNAL_H */
|