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 实体按压留用户终验。
220 lines
7.3 KiB
C
220 lines
7.3 KiB
C
/* play/ipc_media.c — 媒体会话 IPC 最小客户端(SDK 尚无媒体封装,App 内用 meow_ipc.h 帧格式直连)。
|
||
*
|
||
* 连接:AF_UNIX SOCK_STREAM 到 MEOW_IPC_SOCK(缺省 /usr/data/meow_ipc.sock)。HELLO(app_id,
|
||
* MF_PLAYBACK) 注册为媒体源;之后**同一连接**周期发 MSG_META(~1s,进度随播放推进)并阻塞等
|
||
* launcher 下发 MSG_PLAYCTL(播放/暂停/上下曲/seek/toggle)。launcher 经该连接 fd 回发(meow_media.c
|
||
* send_playctl 用最近活跃会话的 fd)。无 socket/launcher 不跑 → 优雅跳过(打日志不崩)。
|
||
* 线程模型:独立线程(main 起了 meow_ui_run 阻塞;播放线程归 player_manager,本线程只做 IPC 收发)。
|
||
*/
|
||
#include "../ncm_app_internal.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <poll.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/un.h>
|
||
#include "meow_ipc.h"
|
||
|
||
static pthread_t g_thr;
|
||
static int g_thr_on;
|
||
static int g_quit;
|
||
|
||
static const char *sock_path(void)
|
||
{
|
||
const char *e = getenv("MEOW_IPC_SOCK");
|
||
return (e && e[0]) ? e : MEOW_IPC_PATH;
|
||
}
|
||
|
||
static int connect_path(const char *path)
|
||
{
|
||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
if (fd < 0) return -1;
|
||
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 (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
|
||
return fd;
|
||
}
|
||
|
||
/* 发送一帧(头+负载)。 */
|
||
static int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len)
|
||
{
|
||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||
uint32_t magic = MEOW_IPC_MAGIC;
|
||
uint16_t ver = MEOW_IPC_VERSION;
|
||
memcpy(hdr + 0, &magic, 4);
|
||
memcpy(hdr + 4, &ver, 2);
|
||
memcpy(hdr + 6, &type, 2);
|
||
memcpy(hdr + 8, &req_id, 4);
|
||
memcpy(hdr + 12, &len, 4);
|
||
size_t off = 0;
|
||
while (off < sizeof hdr) {
|
||
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
|
||
if (w <= 0) return -1;
|
||
off += (size_t)w;
|
||
}
|
||
if (len && payload) {
|
||
off = 0;
|
||
while (off < len) {
|
||
ssize_t w = write(fd, (const uint8_t *)payload + off, len - off);
|
||
if (w <= 0) return -1;
|
||
off += (size_t)w;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
|
||
{
|
||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||
size_t off = 0;
|
||
while (off < sizeof hdr) {
|
||
ssize_t r = read(fd, hdr + off, sizeof hdr - off);
|
||
if (r <= 0) return -1;
|
||
off += (size_t)r;
|
||
}
|
||
uint32_t magic, len; uint16_t ver;
|
||
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2);
|
||
memcpy(&len, hdr + 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;
|
||
}
|
||
|
||
/* 组并发送一条 META。 */
|
||
static void send_meta(int fd, uint32_t req_id)
|
||
{
|
||
m_meta_t mm;
|
||
memset(&mm, 0, sizeof mm);
|
||
ncm_strlcpy(mm.app_id, "dev.player.ncm", sizeof mm.app_id);
|
||
|
||
int idx = player_manager_playing_song();
|
||
pthread_mutex_lock(&g_play.mu);
|
||
int pos = (int)g_play.pos_ms;
|
||
int dur = (int)g_play.dur_ms;
|
||
int state = g_play.state;
|
||
int err = g_play.err;
|
||
pthread_mutex_unlock(&g_play.mu);
|
||
|
||
static char title[128], artist[128], album[128];
|
||
title[0] = artist[0] = album[0] = 0;
|
||
ncm_song_t sm;
|
||
if (idx >= 0 && player_manager_current_song(&sm) == 0) { /* 播放快照(窗口无关,§17) */
|
||
snprintf(title, sizeof title, "%s", sm.name);
|
||
snprintf(artist, sizeof artist, "%s", sm.art_name);
|
||
snprintf(album, sizeof album, "%s", sm.album_name);
|
||
}
|
||
snprintf(mm.title, sizeof mm.title, "%s", title);
|
||
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
|
||
snprintf(mm.album, sizeof mm.album, "%s", album);
|
||
mm.pos_ms = (uint32_t)(pos < 0 ? 0 : pos);
|
||
mm.dur_ms = (uint32_t)(dur < 0 ? 0 : dur);
|
||
uint32_t st = 0;
|
||
if (err != 0) st = 0;
|
||
else if (state == NCM_PC_PLAYING) st = 1;
|
||
else if (state == NCM_PC_PAUSED) st = 2;
|
||
else if (state == NCM_PC_BUFFER) st = 3;
|
||
else st = 0;
|
||
mm.state = st;
|
||
/* 封面:若已落盘(cover 缓存文件)则提供路径 */
|
||
mm.flags = 0;
|
||
const char *cp = cover_current_path();
|
||
if (cp && cp[0]) {
|
||
snprintf(mm.artwork, sizeof mm.artwork, "%s", cp);
|
||
mm.flags |= MF_ART | MF_COVER;
|
||
}
|
||
if (lyric_count() > 0) mm.flags |= MF_LYRIC;
|
||
send_frame(fd, MSG_META, req_id, &mm, sizeof mm);
|
||
}
|
||
|
||
static void apply_playctl(const m_playctl_t *c)
|
||
{
|
||
switch (c->cmd) {
|
||
case NCM_CTL_PLAY: player_manager_pause(0); break;
|
||
case NCM_CTL_PAUSE: player_manager_pause(1); break;
|
||
case NCM_CTL_PREV: player_manager_next(-1); break;
|
||
case NCM_CTL_NEXT: player_manager_next(1); break;
|
||
case NCM_CTL_SEEK: player_manager_seek(c->seek_ms); break;
|
||
case NCM_CTL_TOGGLE:
|
||
/* 物理播放/暂停键(SDK 164 恒发 toggle,不再本地判 state):源是状态权威,
|
||
* 按当前态翻转(与播放页按钮 ui/player.c 同款判定)。非 PLAYING/PAUSED
|
||
* (停止/缓冲)时 pause() 内部按态过滤,天然 no-op 安全。 */
|
||
player_manager_pause(player_manager_state() == NCM_PC_PAUSED ? 0 : 1);
|
||
break;
|
||
default: break;
|
||
}
|
||
}
|
||
|
||
static void *ipc_thread(void *arg)
|
||
{
|
||
(void)arg;
|
||
int fd = connect_path(sock_path());
|
||
if (fd < 0) { meow_log(MEOW_LOG_WARN, "ipc: no launcher socket"); return NULL; }
|
||
|
||
m_hello_t h;
|
||
memset(&h, 0, sizeof h);
|
||
ncm_strlcpy(h.app_id, "dev.player.ncm", sizeof h.app_id);
|
||
h.version = 1;
|
||
h.flags = MF_PLAYBACK;
|
||
if (send_frame(fd, MSG_HELLO, 1, &h, sizeof h) != 0) { close(fd); return NULL; }
|
||
uint8_t hb[256];
|
||
uint16_t htype;
|
||
if (read_frame(fd, &htype, hb, sizeof hb) < 0) { close(fd); return NULL; }
|
||
|
||
send_meta(fd, 2); /* 首条注册媒体源(state=0 未播也登记,start 播后自动成源) */
|
||
|
||
uint64_t last_meta = 0;
|
||
while (!g_quit && !g_app_quit) {
|
||
struct pollfd p;
|
||
p.fd = fd; p.events = POLLIN;
|
||
int pr = poll(&p, 1, 200);
|
||
if (pr > 0 && (p.revents & POLLIN)) {
|
||
uint8_t rbuf[MEOW_IPC_MAX];
|
||
uint16_t type;
|
||
int n = read_frame(fd, &type, rbuf, sizeof rbuf);
|
||
if (n < 0) break;
|
||
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
|
||
m_playctl_t c;
|
||
memcpy(&c, rbuf, sizeof c);
|
||
apply_playctl(&c);
|
||
}
|
||
} else if (pr < 0 && errno != EINTR) {
|
||
break;
|
||
}
|
||
uint64_t now = meow_ui_time_ms();
|
||
if (now - last_meta >= 1000) {
|
||
send_meta(fd, (uint32_t)(now & 0x7FFFFFFF));
|
||
last_meta = now;
|
||
}
|
||
}
|
||
close(fd);
|
||
return NULL;
|
||
}
|
||
|
||
int ipc_media_start(void)
|
||
{
|
||
if (g_thr_on) return 0;
|
||
g_quit = 0;
|
||
if (pthread_create(&g_thr, NULL, ipc_thread, NULL) != 0) return -1;
|
||
pthread_detach(g_thr);
|
||
g_thr_on = 1;
|
||
return 0;
|
||
}
|
||
|
||
void ipc_media_stop(void)
|
||
{
|
||
g_quit = 1;
|
||
/* 线程在 200ms poll 内退出(detach,不 join)*/
|
||
g_thr_on = 0;
|
||
}
|