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:
1 parent
8012948b26
commit
32543ac616
15 files changed
+1429
-34
No files matched your search
@@ -342,6 +342,126 @@ static int cmd_dismissall(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 阶段 D 附录 B:媒体会话上报 → MSG_META,期望 MSG_ACK。
|
||||
* meta <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||||
static int cmd_meta(const char *app_id, uint32_t state, uint32_t pos, uint32_t dur,
|
||||
const char *title, const char *artist, const char *album, const char *artwork)
|
||||
{
|
||||
int fd = connect_path(g_sock);
|
||||
if (fd < 0) return 1;
|
||||
m_meta_t mm;
|
||||
memset(&mm, 0, sizeof mm);
|
||||
strncpy(mm.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||||
mm.state = state; mm.pos_ms = pos; mm.dur_ms = dur;
|
||||
snprintf(mm.title, sizeof mm.title, "%s", title);
|
||||
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||||
snprintf(mm.album, sizeof mm.album, "%s", album);
|
||||
if (artwork) snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork);
|
||||
uint8_t rbuf[256];
|
||||
int rt = xchg(fd, MSG_META, 11, &mm, sizeof mm, rbuf, sizeof rbuf);
|
||||
close(fd);
|
||||
if (rt == MSG_ACK) {
|
||||
m_ack_t a;
|
||||
memcpy(&a, rbuf, sizeof a);
|
||||
printf("META ok: app=%s state=%u code=%d\n", app_id, state, a.code);
|
||||
return a.code == MEOW_OK ? 0 : 1;
|
||||
}
|
||||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||||
fprintf(stderr, "META: unexpected resp type 0x%04x\n", rt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 控制转发 → MSG_PLAYCTL(launcher 转发到最近活跃媒体会话),期望 MSG_ACK。
|
||||
* playctl <cmd> [seek_ms] (cmd 1=播放 2=暂停 3=上一首 4=下一首 5=seek) */
|
||||
static int cmd_playctl(uint32_t cmd, int64_t seek_ms)
|
||||
{
|
||||
int fd = connect_path(g_sock);
|
||||
if (fd < 0) return 1;
|
||||
m_playctl_t c;
|
||||
memset(&c, 0, sizeof c);
|
||||
c.cmd = cmd; c.seek_ms = seek_ms;
|
||||
uint8_t rbuf[256];
|
||||
int rt = xchg(fd, MSG_PLAYCTL, 12, &c, sizeof c, rbuf, sizeof rbuf);
|
||||
close(fd);
|
||||
if (rt == MSG_ACK) {
|
||||
m_ack_t a;
|
||||
memcpy(&a, rbuf, sizeof a);
|
||||
printf("PLAYCTL ok: cmd=%u seek=%lld code=%d (status=%d)\n", cmd,
|
||||
(long long)seek_ms, a.code, (int)a.status);
|
||||
return a.code == MEOW_OK ? 0 : 1;
|
||||
}
|
||||
if (rt == MSG_ERR) { print_err((const m_err_t *)rbuf); return 1; }
|
||||
fprintf(stderr, "PLAYCTL: unexpected resp type 0x%04x\n", rt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 读一帧(头+负载)→ 返回负载长;-1=连接断/坏帧。type/req_id 输出。 */
|
||||
static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, uint32_t rcap)
|
||||
{
|
||||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||||
size_t off = 0;
|
||||
while (off < sizeof rhdr) {
|
||||
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
|
||||
if (r <= 0) return -1;
|
||||
off += (size_t)r;
|
||||
}
|
||||
uint32_t magic, len; uint16_t ver;
|
||||
memcpy(&magic, rhdr, 4); memcpy(&ver, rhdr + 4, 2); memcpy(type, rhdr + 6, 2);
|
||||
memcpy(req_id, rhdr + 8, 4); memcpy(&len, rhdr + 12, 4);
|
||||
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
|
||||
if (len > rcap) len = rcap;
|
||||
off = 0;
|
||||
while (off < len) {
|
||||
ssize_t r = read(fd, rbuf + off, len - off);
|
||||
if (r <= 0) return -1;
|
||||
off += (size_t)r;
|
||||
}
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
/* 媒体源监听:HELLO(MF_PLAYBACK) + META → 保持连接等待 launcher 下发 MSG_PLAYCTL。
|
||||
* listen <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||||
static int cmd_listen(const char *app_id, uint32_t state, uint32_t pos, uint32_t dur,
|
||||
const char *title, const char *artist, const char *album,
|
||||
const char *artwork)
|
||||
{
|
||||
int fd = connect_path(g_sock);
|
||||
if (fd < 0) return 1;
|
||||
m_hello_t h;
|
||||
memset(&h, 0, sizeof h);
|
||||
strncpy(h.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||||
h.version = 1; h.flags = MF_PLAYBACK;
|
||||
uint8_t rbuf[256];
|
||||
int rt = xchg(fd, MSG_HELLO, 1, &h, sizeof h, rbuf, sizeof rbuf);
|
||||
if (rt != MSG_ACK) { fprintf(stderr, "listen: HELLO resp 0x%04x\n", rt); close(fd); return 1; }
|
||||
m_meta_t mm;
|
||||
memset(&mm, 0, sizeof mm);
|
||||
strncpy(mm.app_id, app_id, MEOW_APP_ID_MAX - 1);
|
||||
mm.state = state; mm.pos_ms = pos; mm.dur_ms = dur;
|
||||
snprintf(mm.title, sizeof mm.title, "%s", title);
|
||||
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||||
snprintf(mm.album, sizeof mm.album, "%s", album);
|
||||
if (artwork) snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork);
|
||||
rt = xchg(fd, MSG_META, 2, &mm, sizeof mm, rbuf, sizeof rbuf);
|
||||
if (rt != MSG_ACK) { fprintf(stderr, "listen: META resp 0x%04x\n", rt); close(fd); return 1; }
|
||||
printf("listen: source=%s registered (state=%u) — awaiting PLAYCTL...\n", app_id, state);
|
||||
fflush(stdout);
|
||||
/* 阻塞读:等待 launcher 下发 MSG_PLAYCTL(同一连接)。 */
|
||||
for (;;) {
|
||||
uint16_t type; uint32_t req_id;
|
||||
int n = read_frame(fd, &type, &req_id, rbuf, sizeof rbuf);
|
||||
if (n < 0) { fprintf(stderr, "listen: conn closed\n"); close(fd); return 0; }
|
||||
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
|
||||
m_playctl_t c;
|
||||
memcpy(&c, rbuf, sizeof c);
|
||||
printf("RECV PLAYCTL: cmd=%u seek=%lld\n", c.cmd, (long long)c.seek_ms);
|
||||
fflush(stdout);
|
||||
} else {
|
||||
printf("RECV frame type=0x%04x len=%d\n", type, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *env = getenv("MEOW_IPC_SOCK");
|
||||
@@ -408,6 +528,30 @@ int main(int argc, char **argv)
|
||||
if (!strcmp(cmd, "dismissall")) {
|
||||
return cmd_dismissall();
|
||||
}
|
||||
if (!strcmp(cmd, "meta")) {
|
||||
/* meta <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||||
if (i + 7 >= argc) { fprintf(stderr, "meta needs <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album>\n"); return 1; }
|
||||
uint32_t st = (uint32_t)strtoul(argv[i + 2], NULL, 0);
|
||||
uint32_t pos = (uint32_t)strtoul(argv[i + 3], NULL, 0);
|
||||
uint32_t dur = (uint32_t)strtoul(argv[i + 4], NULL, 0);
|
||||
const char *aw = (i + 8 < argc) ? argv[i + 8] : NULL;
|
||||
return cmd_meta(argv[i + 1], st, pos, dur, argv[i + 5], argv[i + 6], argv[i + 7], aw);
|
||||
}
|
||||
if (!strcmp(cmd, "listen")) {
|
||||
/* listen <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album> [artwork] */
|
||||
if (i + 7 >= argc) { fprintf(stderr, "listen needs <app_id> <state> <pos_ms> <dur_ms> <title> <artist> <album>\n"); return 1; }
|
||||
uint32_t st = (uint32_t)strtoul(argv[i + 2], NULL, 0);
|
||||
uint32_t pos = (uint32_t)strtoul(argv[i + 3], NULL, 0);
|
||||
uint32_t dur = (uint32_t)strtoul(argv[i + 4], NULL, 0);
|
||||
const char *aw = (i + 8 < argc) ? argv[i + 8] : NULL;
|
||||
return cmd_listen(argv[i + 1], st, pos, dur, argv[i + 5], argv[i + 6], argv[i + 7], aw);
|
||||
}
|
||||
if (!strcmp(cmd, "playctl")) {
|
||||
if (i + 1 >= argc) { fprintf(stderr, "playctl needs <cmd> [seek_ms]\n"); return 1; }
|
||||
uint32_t cmd2 = (uint32_t)strtoul(argv[i + 1], NULL, 0);
|
||||
int64_t seek = (i + 2 < argc) ? (int64_t)strtoll(argv[i + 2], NULL, 0) : 0;
|
||||
return cmd_playctl(cmd2, seek);
|
||||
}
|
||||
if (!strcmp(cmd, "nav")) {
|
||||
/* nav report <app_id> <depth> <top> | nav exit <app_id> */
|
||||
if (i + 1 >= argc) { fprintf(stderr, "nav needs <app_id> and report|exit\n"); return 1; }
|
||||
|
||||
Reference in new issue
Block a user