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
@@ -0,0 +1,154 @@
|
||||
/* test_ipc_harness.c — 宿主 IPC 服务端模拟(等价 launcher ipc_dispatch 的媒体子集).
|
||||
*
|
||||
* 用 meow_media.c + meow_ipc.h 复刻 launcher 的 IPC 服务器对媒体消息的处理,
|
||||
* 供「qemu-mipsel meow_ipc listen/meta/playctl」端到端验证:
|
||||
* listen → HELLO(MF_PLAYBACK)+META 注册源并等待下发;
|
||||
* meta → 上报 META(作为新源/更新);
|
||||
* playctl → 上传控制,服务端转发给「最近活跃会话」fd。
|
||||
* 运行:gcc test_ipc_harness.c meow_media.c -o /tmp/meow_srv ; /tmp/meow_srv /tmp/e2e.sock
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <poll.h>
|
||||
#include "meow_ipc.h"
|
||||
#include "meow_media.h"
|
||||
|
||||
#define MAXCLI 8
|
||||
typedef struct {
|
||||
int fd;
|
||||
uint8_t hdr[MEOW_IPC_HDR_LEN]; int hdr_n;
|
||||
uint8_t pay[MEOW_IPC_MAX]; uint32_t pay_tot, pay_n;
|
||||
char app_id[MEOW_APP_ID_MAX];
|
||||
} conn;
|
||||
|
||||
static conn g_c[MAXCLI];
|
||||
|
||||
static size_t srv_send(int fd, uint16_t type, uint32_t req, const void *payload, uint32_t len)
|
||||
{
|
||||
if (fd < 0) return 0;
|
||||
uint8_t h[MEOW_IPC_HDR_LEN];
|
||||
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
|
||||
memcpy(h+0,&magic,4); memcpy(h+4,&ver,2); memcpy(h+6,&type,2); memcpy(h+8,&req,4); memcpy(h+12,&len,4);
|
||||
size_t off=0;
|
||||
while (off<sizeof h){ ssize_t w=write(fd,h+off,sizeof h-off); if(w<=0)return off; off+=(size_t)w; }
|
||||
if(len&&payload){ size_t p=0; while(p<len){ ssize_t w=write(fd,(const uint8_t*)payload+p,len-p); if(w<=0)break; p+=(size_t)w; } off+=p; }
|
||||
return off;
|
||||
}
|
||||
|
||||
static conn *find_slot(int fd) { for (int i=0;i<MAXCLI;i++) if (g_c[i].fd==fd) return &g_c[i]; return NULL; }
|
||||
static conn *alloc_slot(int fd){ for (int i=0;i<MAXCLI;i++) if (g_c[i].fd==-1){ g_c[i].fd=fd; g_c[i].hdr_n=0; g_c[i].pay_tot=g_c[i].pay_n=0; g_c[i].app_id[0]=0; return &g_c[i]; } return NULL; }
|
||||
|
||||
static void handle(conn *c)
|
||||
{
|
||||
uint16_t type; uint32_t req;
|
||||
memcpy(&type, c->hdr+6, 2); memcpy(&req, c->hdr+8, 4);
|
||||
switch (type) {
|
||||
case MSG_HELLO: {
|
||||
m_hello_t h; memset(&h,0,sizeof h); memcpy(&h,c->pay,c->pay_n<sizeof h?c->pay_n:sizeof h);
|
||||
snprintf(c->app_id, sizeof c->app_id, "%s", h.app_id);
|
||||
m_ack_t a; a.code=MEOW_OK; a.status=1; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||||
printf("SRV: HELLO app=%s flags=%u -> ACK\n", c->app_id, h.flags); break;
|
||||
}
|
||||
case MSG_META: {
|
||||
m_meta_t m; memset(&m,0,sizeof m); memcpy(&m,c->pay,c->pay_n<sizeof m?c->pay_n:sizeof m);
|
||||
m.app_id[MEOW_APP_ID_MAX-1]=0; m.title[127]=0; m.artist[127]=0; m.album[127]=0; m.artwork[191]=0; m.lyric[191]=0;
|
||||
int rc = meow_media_meta(&m, c->fd);
|
||||
m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||||
const meow_media_session_t *cur = meow_media_current();
|
||||
printf("SRV: META app=%s state=%u rc=%d | current=%s\n", m.app_id, m.state, rc,
|
||||
cur?cur->app_id:"(none)");
|
||||
break;
|
||||
}
|
||||
case MSG_PLAYCTL: {
|
||||
m_playctl_t p; memset(&p,0,sizeof p); memcpy(&p,c->pay,c->pay_n<sizeof p?c->pay_n:sizeof p);
|
||||
int rc = meow_media_playctl(p.cmd, p.seek_ms);
|
||||
m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a);
|
||||
printf("SRV: PLAYCTL cmd=%u seek=%lld rc=%d (target current)\n", p.cmd, (long long)p.seek_ms, rc);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
m_err_t e; memset(&e,0,sizeof e); e.code=MEOW_ERR_INVAL; e.msg_len=5; memcpy(e.msg,"nope",5);
|
||||
srv_send(c->fd,MSG_ERR,req,&e,sizeof e);
|
||||
printf("SRV: unsupported type 0x%04x\n", type); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 读一个连接可用字节并组装帧(>=1 完整帧即 dispatch)。返回 0=ok,-1=连接关闭。 */
|
||||
static int conn_read(conn *c)
|
||||
{
|
||||
for (;;) {
|
||||
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) {
|
||||
ssize_t r = read(c->fd, c->hdr + c->hdr_n, MEOW_IPC_HDR_LEN - c->hdr_n);
|
||||
if (r < 0) { if (errno==EAGAIN) return 0; return -1; }
|
||||
if (r == 0) return -1;
|
||||
c->hdr_n += (int)r;
|
||||
if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) return 0;
|
||||
uint32_t magic, len; uint16_t ver;
|
||||
memcpy(&magic,c->hdr,4); memcpy(&ver,c->hdr+4,2); memcpy(&len,c->hdr+12,4);
|
||||
if (magic!=MEOW_IPC_MAGIC||ver!=MEOW_IPC_VERSION||len>MEOW_IPC_MAX) return -1;
|
||||
c->pay_tot=len; c->pay_n=0;
|
||||
if (len==0){ handle(c); c->hdr_n=0; continue; }
|
||||
}
|
||||
if (c->pay_n < c->pay_tot) {
|
||||
ssize_t r = read(c->fd, c->pay + c->pay_n, c->pay_tot - c->pay_n);
|
||||
if (r < 0){ if (errno==EAGAIN) return 0; return -1; }
|
||||
if (r==0) return -1;
|
||||
c->pay_n += (uint32_t)r;
|
||||
if (c->pay_n < c->pay_tot) return 0;
|
||||
}
|
||||
handle(c);
|
||||
c->hdr_n=0; c->pay_n=0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path = (argc>1)?argv[1]:"/tmp/meow_ipc_e2e.sock";
|
||||
for (int i=0;i<MAXCLI;i++) g_c[i].fd=-1;
|
||||
meow_media_ops ops;
|
||||
ops.send=srv_send; ops.log=NULL; meow_media_init(&ops);
|
||||
|
||||
int lfd=socket(AF_UNIX,SOCK_STREAM,0);
|
||||
unlink(path);
|
||||
struct sockaddr_un a; memset(&a,0,sizeof a); a.sun_family=AF_UNIX; strncpy(a.sun_path,path,sizeof a.sun_path-1);
|
||||
if (bind(lfd,(struct sockaddr*)&a,sizeof a)<0){ perror("bind"); return 1; }
|
||||
chmod(path,0666);
|
||||
if (listen(lfd,8)<0){ perror("listen"); return 1; }
|
||||
fcntl(lfd,F_SETFL,O_NONBLOCK);
|
||||
printf("SRV: listening %s\n", path); fflush(stdout);
|
||||
|
||||
struct pollfd fds[MAXCLI+1]; int nfds=1;
|
||||
fds[0].fd=lfd; fds[0].events=POLLIN; fds[0].revents=0;
|
||||
while (1) {
|
||||
fds[0].fd=lfd; fds[0].events=POLLIN;
|
||||
int n=1;
|
||||
for (int i=0;i<MAXCLI;i++) if (g_c[i].fd>=0){ fds[n].fd=g_c[i].fd; fds[n].events=POLLIN; fds[n].revents=0; n++; }
|
||||
nfds=n;
|
||||
int r=poll(fds,nfds,400);
|
||||
if (r<0){ if(errno==EINTR)continue; break; }
|
||||
if (fds[0].revents&POLLIN){
|
||||
for(;;){ int c=accept(lfd,NULL,NULL); if(c<0)break; fcntl(c,F_SETFL,O_NONBLOCK);
|
||||
if(!alloc_slot(c)){ close(c); printf("SRV: reject conn (full)\n"); } else printf("SRV: accept fd=%d\n", c); }
|
||||
}
|
||||
for (int i=1;i<nfds;i++){
|
||||
int ev=fds[i].revents;
|
||||
if (ev&(POLLIN|POLLHUP|POLLERR|POLLNVAL)){
|
||||
conn *c=find_slot(fds[i].fd);
|
||||
if (!c) continue;
|
||||
if (conn_read(c)<0){ printf("SRV: conn %d closed\n", c->fd); close(c->fd); c->fd=-1; }
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
close(lfd); unlink(path);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user