/* meow_media.h — MeowOS 媒体会话(阶段 D · 附录 B / D1)。 * * 定位:媒体会话注册表(App 声明 MF_PLAYBACK + 上报 MSG_META → 成为媒体源; * 最近活跃者=当前源)。通知中心媒体卡片 / Now Playing 页 / 物理媒体键 / 控制器统一消费; * PLAYCTL 经会话的连接 fd 直接下发。语义对齐 FRAMEWORK_DESIGN 附录 B 的「启动器表现」与 * 附录 D1(MPRIS 最近活跃源选择)。 * * 设计: * - 会话=一个 App 一个槽(app_id 归并;单 App 恒 1 槽,防多发 META 占满)。 * - 当前源=last_active_ms 最大者(最近上报 META)。多源时此即「最近活跃」。 * - 判定源死亡:播放中(1)/缓冲(3) 超 MEOW_MEDIA_STALE_MS 无 META → 清槽回空态(App 死/ * 服务断);暂停(2)/停止(0) 不超时(暂停属合法驻留,停止由 state 判空态)。 * - 空态:无会话,或当前源 state==0(停止)。 * * 自包含:不依赖 launcher static 符号;发送/日志经 meow_media_ops 注入(launcher 用 * ipc_send / logmsg;宿主单测用 socket write / NULL)。可独立单测(test_media.c)。 */ #ifndef MEOW_MEDIA_H #define MEOW_MEDIA_H #include #include #include "meow_ipc.h" /* 同时维护的媒体会话上限(防御;一个 App 一槽) */ #define MEOW_MEDIA_MAX 4 /* 播放中(1)/缓冲(3) 无 META 超此毫秒 → 判源死清槽(回空态);暂停/停止不超时 */ #define MEOW_MEDIA_STALE_MS 30000u /* state 取值(与 m_meta_t.state 一致) */ #define MEOW_MEDIA_STOPPED 0u #define MEOW_MEDIA_PLAYING 1u #define MEOW_MEDIA_PAUSED 2u #define MEOW_MEDIA_BUFFER 3u /* 媒体会话(一个 App 一个槽)。fd=最近上报 META 的连接 fd(下发 PLAYCTL 用)。 */ typedef struct meow_media_session { int used; int fd; char app_id[MEOW_APP_ID_MAX]; uint64_t last_active_ms; /* 最近 META 时刻(monotonic ms,进度推进用) */ uint64_t active_seq; /* 最近活跃净序(全局递增,供「最近活跃」严格排序) */ uint32_t state; uint32_t pos_ms; uint32_t dur_ms; uint32_t flags; char title[128]; char artist[128]; char album[128]; char artwork[192]; char lyric[192]; /* 歌词源路径/槽位(沿用 META.lyric) */ /* 批3:当前曲 live 歌词(App 经 MSG_META_LYRIC 上报;有界缓冲,新 seq 重置)。 * lyric_lrc/lyric_tlrc 为当前曲歌词原文/翻译文本(桌面歌词页 meow_lyric_parse 用); * lyric_seq=歌曲代际(App 新歌/新词自增,launcher 见新 seq 清旧词)。 */ uint32_t lyric_seq; uint32_t lyric_lrc_len; uint32_t lyric_tlrc_len; char lyric_lrc[MEOW_MEDIA_LRC_MAX]; char lyric_tlrc[MEOW_MEDIA_TLRC_MAX]; } meow_media_session_t; /* 依赖注入:send=发一帧(launcher 用 ipc_send 包装;宿主单测用 socket 写); * log=日志(launcher 用 logmsg;NULL=静默)。send 返回写入字节。 */ typedef struct meow_media_ops { size_t (*send)(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len); void (*log)(const char *fmt, ...); } meow_media_ops; /* 注入 ops(send/log);NULL 清空。launcher 启动时(meow_ipc_init)调用一次。 */ void meow_media_init(const meow_media_ops *ops); /* App 上报 META:按 m->app_id 归并到会话(无则建槽);记录 fd/last_active; * 截断安全(不越界)。返回 0=成功,-1=会话满/参数非法。 */ int meow_media_meta(const m_meta_t *m, int fd); /* 当前源 = 最近活跃会话(last_active_ms 最大)。无会话返回 NULL。 */ const meow_media_session_t *meow_media_current(void); /* 是否有活动媒体源:当前源存在且 state != 0(停止)。通知中心卡片/播放页据此显示。 */ int meow_media_has_source(void); /* §10.5 叠加会话保活判定:当前源(最近活跃会话)存在、state==PLAYING 且 app_id 匹配。 * launcher 据此决定通知中心叠加不 SIGSTOP 该 App(保 PCM 供流),SDK present 层自抑上屏。 */ int meow_media_app_playing(const char *app_id); /* 批①后台常驻判定:该 app 的媒体会话「活跃可驻留」= 会话存在且 state != STOPPED * (PLAYING/PAUSED/BUFFER 皆常驻)。按 app 自身会话判(sess_find),不读最近活跃源—— * 多源时不误判「当前源≠本 App」。launcher 转后台保活依据。 */ int meow_media_app_resident(const char *app_id); /* 向当前源下发 PLAYCTL(卡片按钮/播放页/媒体键/控制器)。返回 0=已发,-1=无源/发送失败。 */ int meow_media_playctl(uint32_t cmd, int64_t seek_ms); /* 向指定 app 的会话下发 PLAYCTL;无匹配会话返回 -1。 */ int meow_media_playctl_to(const char *app_id, uint32_t cmd, int64_t seek_ms); /* 清某 app 的会话(App 退出/源死亡/主动停止时调)。app_id 空/NULL=清全部。 */ void meow_media_clear(const char *app_id); /* 主循环周期调(~1s):播放中(1)/缓冲(3) 超时无 META → 清槽。 */ void meow_media_tick(void); /* 显示用虚拟位置:播放中按墙钟推进 pos_ms(clamp 到 dur);暂停/缓冲/停止原样。 */ uint32_t meow_media_display_pos(const meow_media_session_t *s); /* ---- 歌词(批3 · live lyric 数据源)---- * App 上报当前曲歌词(MSG_META_LYRIC 分片):按 app_id 归并到会话,seq 为歌曲代际、 * 新代际清旧词;分片按 kind(0=原文/1=翻译)/off 写入会话内有序缓冲(超 MEOW_MEDIA_LRC_MAX/ * MEOW_MEDIA_TLRC_MAX 截断)。会话不存在(未先报 META 注册)→ 丢弃返回 -1(防 fd=0 槽)。 * meow_media_lyric_valid/lrc/tlrc:桌面歌词页从当前会话取歌词文本,交由批1 meow_lyric_parse * 解析 + meow_lyric_line_at(meow_media_display_pos(s)) 驱动批2 meow_lyric_view 实时滚动。 */ int meow_media_lyric(const m_lyric_t *l); /* 0=已写入;-1=会话不存在/参数非法 */ int meow_media_lyric_valid(const meow_media_session_t *s); /* 会话携带有效歌词(lrc 非空) */ const char *meow_media_lyric_lrc(const meow_media_session_t *s); /* 原文;无则 "" */ const char *meow_media_lyric_tlrc(const meow_media_session_t *s); /* 翻译;无则 "" */ /* 调试打印(经 ops->log,前缀+每会话一行)。 */ void meow_media_dump(const char *prefix); #endif /* MEOW_MEDIA_H */