feat(launcher): 媒体会话+Now Playing (agent-204) — MSG_META(0x72)/MSG_PLAYCTL(0x73)+meow_media.c(最近活跃源=active_seq净序,30s超时回收); 通知中心顶部媒体卡片(封面缓存+进度seek+控制键); 主界面中部/右缘左滑进 Now Playing 大页(封面+元数据+进度+控制+歌词占位), 左缘返回; 空态完备

launcher a42171a1→ddfe616e; 宿主 test_media ALL PASS + qemu 端到端(META注册→PLAYCTL下发闭环)
出入: 卡片以媒体会话为权威非通知; PLAYCTL 走会话fd(断连防御留后续)
This commit is contained in:
MeowOS committed 2026-08-31 06:51:04 +08:00
1 parent 8012948b26
commit 32543ac616
15 files changed
+1429 -34

No files matched your search

+47
View File
@@ -862,6 +862,14 @@ static size_t ipc_send(int fd, uint16_t type, uint32_t req_id,
}
/* 公开的 ipc_send 包装(media 模块经 ops 注入用;签名与 meow_media_ops.send 一致)。 */
size_t meow_ipc_send_pub(int fd, uint16_t type, uint32_t req_id,
const void *payload, uint32_t len)
{
return ipc_send(fd, type, req_id, payload, len);
}
/* 组装完的帧入队(SPSC 无锁,先写槽再推进 tail,__sync 发布)。 */
static void ipc_enqueue_frame(struct meow_ipc_conn *c)
{
@@ -1014,6 +1022,12 @@ static void *ipc_thread(void *arg)
* (bind/listen 失败记 ipc_thread_ok=0,随后 meow_ipc_process 仍安全轮询空队列。) */
int meow_ipc_init(void)
{
/* 注入媒体会话 ops:send=ipc_send 包装、log=logmsg(阶段D附录B)。 */
static meow_media_ops g_media_ops;
g_media_ops.send = meow_ipc_send_pub;
g_media_ops.log = logmsg;
meow_media_init(&g_media_ops);
ipc_event_fd = eventfd(0, EFD_NONBLOCK);
if (ipc_event_fd < 0)
logmsg("ipc: eventfd fail %s (10ms poll drains anyway)", strerror(errno));
@@ -1246,6 +1260,39 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
logmsg("ipc: NOTIFY_LIST (downlink) not dispatched here");
break;
/* 阶段D附录B:媒体会话(FRAMEWORK_DESIGN 附录 B)。META 声明 MF_PLAYBACK 的 App 上报
* 元数据/进度/状态 → 归并到 meow_media 会话(最近活跃=当前源);回 ACK。 */
case MSG_META: {
m_meta_t mm;
memset(&mm, 0, sizeof mm);
memcpy(&mm, m->payload, m->plen < (uint16_t)sizeof mm ? m->plen : (uint16_t)sizeof mm);
mm.app_id[MEOW_APP_ID_MAX - 1] = 0;
mm.title[127] = 0; mm.artist[127] = 0; mm.album[127] = 0;
mm.artwork[191] = 0; mm.lyric[191] = 0;
int rc = meow_media_meta(&mm, m->fd);
m_ack_t a;
a.code = MEOW_OK; /* 会话策略:槽满降级最旧,不视为错误 */
a.status = (uint32_t)rc;
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
logmsg("ipc: META app=%s state=%u pos=%u/%u rc=%d -> ACK",
mm.app_id, mm.state, mm.pos_ms, mm.dur_ms, rc);
break;
}
/* MSG_PLAYCTL(↑ 控制器→launcher 转发):把 cmd/seek 下发到「最近活跃媒体会话」fd。
* 物理媒体键 / 宿主验证经此外发;通知中心卡片/播放页按钮走 meow_media_playctl 直发。 */
case MSG_PLAYCTL: {
m_playctl_t c;
memset(&c, 0, sizeof c);
memcpy(&c, m->payload, m->plen < (uint16_t)sizeof c ? m->plen : (uint16_t)sizeof c);
int rc = meow_media_playctl(c.cmd, c.seek_ms);
m_ack_t a;
a.code = MEOW_OK;
a.status = (uint32_t)rc; /* 0=已下发;-1=无源/发送失败(status=0xFFFFFFFF) */
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
logmsg("ipc: PLAYCTL cmd=%u seek=%lld rc=%d -> ACK", c.cmd,
(long long)c.seek_ms, rc);
break;
}
default:
logmsg("ipc: unsupported frame type 0x%04x (req %u)", m->type, m->req_id);
{