Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_media.c
T

254 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* meow_ui_media.c — App 侧媒体会话封装(SDK 公共件;见 meow_ui_media.h)。
*
* 传输:AF_UNIX SOCK_STREAM 到 MEOW_IPC_SOCK(缺省 /usr/data/meow_ipc.sock)。后台线程持有
* 持久连接:HELLO(app_id, MF_PLAYBACK) 注册为媒体源 + poll 读 launcher 下发 MSG_PLAYCTL(经
* 该连接 fd 回发,见 launcher meow_media.c send_playctl)。meow_ui_media_meta() 在调用线程
* 直接经同一连接发 MSG_META(全双工:写 META、读 PLAYCTL 互不干扰)。
*
* 连接生命周期(best-effort):懒连(首次 meta / 注册回调时 connect+HELLO+ACK);
* 断线(读失败/写失败)→ close+置 -1,下次 meta 重连;launcher 不在 → connect 失败 → 静默。
* 线程安全:g_fd 读写经互斥;后台线程只在 poll/读失败时改 fd,meta 线程只在 connect 时改 fd。
*/
#include "meow_ui_media.h"
#include "meow_ipc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
static char g_app[MEOW_APP_ID_MAX] = "";
static int g_app_set = 0;
static int g_fd = -1;
static pthread_t g_thr;
static int g_thr_on = 0;
static volatile int g_quit = 0;
static meow_media_playctl_fn g_cb = NULL;
/* 后台线程主体(定义在文件后段;on_playctl 懒起时引用)。 */
static void *media_thread_main(void *arg);
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;
}
/* 写满 n 字节(EINTR 重试);失败返回 0。 */
static int write_all(int fd, const uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t w = write(fd, p + off, n - off);
if (w < 0) { if (errno == EINTR) continue; return 0; }
if (w == 0) return 0;
off += (size_t)w;
}
return 1;
}
static int read_all(int fd, uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t r = read(fd, p + off, n - off);
if (r < 0) { if (errno == EINTR) continue; return 0; }
if (r == 0) return 0;
off += (size_t)r;
}
return 1;
}
/* 组装并写出 16B(LE)帧头 + 负载。 */
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);
if (!write_all(fd, hdr, sizeof hdr)) return -1;
if (len && payload && !write_all(fd, payload, len)) return -1;
return 0;
}
/* 读一帧头+负载;返回负载长(≥0),-1=断/坏帧。 */
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (!read_all(fd, hdr, sizeof hdr)) return -1;
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;
if (len > 0 && !read_all(fd, rbuf, len)) return -1;
return (int)len;
}
static void media_app_id(void)
{
if (g_app_set) return;
const char *s = getenv("MEOW_LOG_APP");
snprintf(g_app, sizeof g_app, "%s", (s && s[0]) ? s : "app");
g_app_set = 1;
}
/* (调用方已持 mu)连接 + HELLO(MF_PLAYBACK) + 读 ACK。成功存 g_fd 返回 fd;失败 -1。 */
static int conn_locked(void)
{
if (g_fd >= 0) return g_fd;
int fd = connect_path(sock_path());
if (fd < 0) return -1;
media_app_id();
m_hello_t h;
memset(&h, 0, sizeof h);
snprintf(h.app_id, sizeof h.app_id, "%s", g_app);
h.version = 1;
h.flags = MF_PLAYBACK;
if (send_frame(fd, MSG_HELLO, 1, &h, sizeof h) != 0) { close(fd); return -1; }
uint8_t rbuf[256];
uint16_t rt;
if (read_frame(fd, &rt, rbuf, sizeof rbuf) < 0 ||
(rt != MSG_ACK && rt != MSG_ERR)) { close(fd); return -1; }
g_fd = fd;
return fd;
}
/* (调用方已持 mu)标记断线并关闭。 */
static void mark_dead_locked(void)
{
if (g_fd >= 0) { close(g_fd); g_fd = -1; }
}
void meow_ui_media_init(const char *app_id)
{
pthread_mutex_lock(&g_mu);
if (app_id && app_id[0]) {
snprintf(g_app, sizeof g_app, "%s", app_id);
g_app_set = 1;
} else if (!g_app_set) {
const char *s = getenv("MEOW_LOG_APP");
snprintf(g_app, sizeof g_app, "%s", (s && s[0]) ? s : "app");
g_app_set = 1;
}
pthread_mutex_unlock(&g_mu);
}
void meow_ui_media_on_playctl(meow_media_playctl_fn cb)
{
pthread_mutex_lock(&g_mu);
g_cb = cb;
int need_start = !g_thr_on;
if (need_start) { g_quit = 0; g_thr_on = 1; }
pthread_mutex_unlock(&g_mu);
if (need_start) pthread_create(&g_thr, NULL, media_thread_main, NULL);
/* detach:App 退出经 meow_ui_media_stop 设 g_quit 后线程自身退出,无需 join */
if (need_start) pthread_detach(g_thr);
}
void meow_ui_media_meta(uint32_t state, uint32_t pos_ms, uint32_t dur_ms,
const char *title, const char *artist, const char *album,
const char *artwork, const char *lyric)
{
m_meta_t mm;
memset(&mm, 0, sizeof mm);
pthread_mutex_lock(&g_mu);
media_app_id();
snprintf(mm.app_id, sizeof mm.app_id, "%s", g_app);
mm.state = state;
mm.pos_ms = pos_ms;
mm.dur_ms = dur_ms;
if (title) snprintf(mm.title, sizeof mm.title, "%s", title);
if (artist) snprintf(mm.artist, sizeof mm.artist, "%s", artist);
if (album) snprintf(mm.album, sizeof mm.album, "%s", album);
if (artwork && artwork[0]) { snprintf(mm.artwork, sizeof mm.artwork, "%s", artwork); mm.flags |= MF_ART | MF_COVER; }
if (lyric && lyric[0]) { snprintf(mm.lyric, sizeof mm.lyric, "%s", lyric); mm.flags |= MF_LYRIC; }
int fd = conn_locked();
if (fd >= 0) {
if (send_frame(fd, MSG_META, (uint32_t)((uint64_t)pos_ms & 0x7FFFFFFF), &mm, sizeof mm) != 0) {
mark_dead_locked(); /* 断线:下次 meta 重连 */
fd = conn_locked();
if (fd >= 0) (void)send_frame(fd, MSG_META, (uint32_t)((uint64_t)pos_ms & 0x7FFFFFFF), &mm, sizeof mm);
}
}
pthread_mutex_unlock(&g_mu);
}
void meow_ui_media_stop(void)
{
pthread_mutex_lock(&g_mu);
g_quit = 1;
mark_dead_locked();
g_thr_on = 0;
pthread_mutex_unlock(&g_mu);
}
/* 后台线程主体:懒连 + poll 读 launcher 下发 MSG_PLAYCTL → 调 cb。 */
static void *media_thread_main(void *arg)
{
(void)arg;
while (!g_quit) {
pthread_mutex_lock(&g_mu);
int fd = g_fd;
if (fd < 0) fd = conn_locked(); /* 懒连/重连(best-effort;失败则回到循环再试) */
pthread_mutex_unlock(&g_mu);
if (fd < 0) { usleep(500000); continue; }
struct pollfd p;
p.fd = fd; p.events = POLLIN; p.revents = 0;
int pr = poll(&p, 1, 200);
if (pr < 0 && errno != EINTR) {
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
continue;
}
if (pr > 0 && (p.revents & (POLLHUP | POLLERR | POLLNVAL))) {
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
continue;
}
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) {
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
continue;
}
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
m_playctl_t c;
memcpy(&c, rbuf, sizeof c);
/* g_cb 经互斥读快照后调用(App 回调可能在 UI 线程,简化为直接调) */
pthread_mutex_lock(&g_mu);
meow_media_playctl_fn cb = g_cb;
pthread_mutex_unlock(&g_mu);
if (cb) cb((int)c.cmd, (int64_t)c.seek_ms);
}
}
}
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
return NULL;
}