Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/meow_media.c
T

203 lines
6.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_media.c — MeowOS 媒体会话注册表(阶段 D · 附录 B / D1)。
*
* 实现说明见 meow_media.h。自包含:仅依赖 meow_ipc.h 结构 + <time.h>(monotonic)+
* <string.h>/<stdarg.h>;发送/日志经 meow_media_ops 注入,宿主可单测。
*/
#include "meow_media.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
static const meow_media_ops *g_ops = NULL;
static meow_media_session_t g_sess[MEOW_MEDIA_MAX];
static uint64_t g_seq = 0; /* 全局活跃净序(每次 META +1,严格区分先后) */
/* 单调毫秒(self-contained;launcher/main_menu 的 now_ms 与此同源 CLOCK_MONOTONIC)。 */
static uint64_t media_now_ms(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) ts.tv_sec = ts.tv_nsec = 0;
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)(ts.tv_nsec / 1000000u);
}
static void media_log(const char *fmt, ...)
{
if (!g_ops || !g_ops->log) return;
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
g_ops->log("%s", buf);
}
static char *media_cp(char *dst, size_t dstsz, const char *src)
{
if (!dst || dstsz == 0) return dst;
if (!src) src = "";
snprintf(dst, dstsz, "%s", src);
dst[dstsz - 1] = 0;
return dst;
}
void meow_media_init(const meow_media_ops *ops)
{
g_ops = ops;
}
static meow_media_session_t *sess_find(const char *app_id)
{
if (!app_id || !app_id[0]) return NULL;
for (int i = 0; i < MEOW_MEDIA_MAX; i++)
if (g_sess[i].used && strcmp(g_sess[i].app_id, app_id) == 0)
return &g_sess[i];
return NULL;
}
/* 找空槽;满时覆盖最旧槽(LRU 式,防单源漂移占满)。 */
static meow_media_session_t *sess_alloc(void)
{
int slot = -1;
uint64_t oldest = UINT64_MAX;
for (int i = 0; i < MEOW_MEDIA_MAX; i++) {
if (!g_sess[i].used) return &g_sess[i];
if (g_sess[i].last_active_ms < oldest) { oldest = g_sess[i].last_active_ms; slot = i; }
}
if (slot >= 0) {
media_log("media: sessions full, evict app=%s", g_sess[slot].app_id);
memset(&g_sess[slot], 0, sizeof g_sess[slot]);
return &g_sess[slot];
}
return NULL;
}
int meow_media_meta(const m_meta_t *m, int fd)
{
if (!m || !m->app_id[0]) return -1;
meow_media_session_t *s = sess_find(m->app_id);
if (!s) {
s = sess_alloc();
if (!s) { media_log("media: meta drop app=%s (no slot)", m->app_id); return -1; }
s->used = 1;
media_cp(s->app_id, sizeof s->app_id, m->app_id);
}
s->fd = fd;
s->last_active_ms = media_now_ms();
s->active_seq = ++g_seq; /* 严格递增:跨会话先后唯一,不受时钟粒度影响 */
s->state = m->state;
s->pos_ms = m->pos_ms;
s->dur_ms = m->dur_ms;
s->flags = m->flags;
media_cp(s->title, sizeof s->title, m->title);
media_cp(s->artist, sizeof s->artist, m->artist);
media_cp(s->album, sizeof s->album, m->album);
media_cp(s->artwork, sizeof s->artwork, m->artwork);
media_cp(s->lyric, sizeof s->lyric, m->lyric);
media_log("media: META app=%s fd=%d state=%u pos=%u/%u title=\"%s\"",
s->app_id, fd, s->state, s->pos_ms, s->dur_ms, s->title);
return 0;
}
const meow_media_session_t *meow_media_current(void)
{
/* 最近活跃 = active_seq 最大者(严格先后;last_active_ms 仅作进度推进用)。 */
const meow_media_session_t *best = NULL;
uint64_t t = 0;
for (int i = 0; i < MEOW_MEDIA_MAX; i++) {
if (g_sess[i].used && g_sess[i].active_seq >= t) {
t = g_sess[i].active_seq;
best = &g_sess[i];
}
}
return best;
}
int meow_media_has_source(void)
{
const meow_media_session_t *s = meow_media_current();
return (s && s->state != MEOW_MEDIA_STOPPED) ? 1 : 0;
}
static int send_playctl(const meow_media_session_t *s, uint32_t cmd, int64_t seek_ms)
{
if (!s || !s->used || !g_ops || !g_ops->send) return -1;
m_playctl_t c;
memset(&c, 0, sizeof c);
c.cmd = cmd;
c.seek_ms = seek_ms;
size_t n = g_ops->send(s->fd, MSG_PLAYCTL, 0, &c, sizeof c);
media_log("media: PLAYCTL -> %s cmd=%u seek=%lld n=%zu",
s->app_id, cmd, (long long)seek_ms, n);
/* ops->send 返回写入总字节(16B 头 + 负载);成功 = 完整帧已写出。 */
return (n >= MEOW_IPC_HDR_LEN + sizeof c) ? 0 : -1;
}
int meow_media_playctl(uint32_t cmd, int64_t seek_ms)
{
return send_playctl(meow_media_current(), cmd, seek_ms);
}
int meow_media_playctl_to(const char *app_id, uint32_t cmd, int64_t seek_ms)
{
meow_media_session_t *s = sess_find(app_id);
return s ? send_playctl(s, cmd, seek_ms) : -1;
}
void meow_media_clear(const char *app_id)
{
int n = 0;
for (int i = 0; i < MEOW_MEDIA_MAX; i++) {
if (g_sess[i].used &&
(!app_id || !app_id[0] || strcmp(g_sess[i].app_id, app_id) == 0)) {
media_log("media: clear app=%s", g_sess[i].app_id);
memset(&g_sess[i], 0, sizeof g_sess[i]);
n++;
}
}
if (app_id && app_id[0]) media_log("media: cleared %d sess for %s", n, app_id);
else if (n) media_log("media: cleared all %d sess", n);
}
void meow_media_tick(void)
{
uint64_t now = media_now_ms();
for (int i = 0; i < MEOW_MEDIA_MAX; i++) {
if (!g_sess[i].used) continue;
if (g_sess[i].state == MEOW_MEDIA_PLAYING || g_sess[i].state == MEOW_MEDIA_BUFFER) {
if (now - g_sess[i].last_active_ms > MEOW_MEDIA_STALE_MS) {
media_log("media: stale source %s (no META %llu ms) -> clear",
g_sess[i].app_id,
(unsigned long long)(now - g_sess[i].last_active_ms));
memset(&g_sess[i], 0, sizeof g_sess[i]);
}
}
}
}
uint32_t meow_media_display_pos(const meow_media_session_t *s)
{
if (!s) return 0;
uint32_t p = s->pos_ms;
if (s->state == MEOW_MEDIA_PLAYING) {
uint64_t now = media_now_ms();
uint64_t el = (now > s->last_active_ms) ? (now - s->last_active_ms) : 0;
uint32_t adv = (el > 0xFFFFFFFFull) ? 0xFFFFFFFFu : (uint32_t)el;
p = s->pos_ms + adv;
if (s->dur_ms && p > s->dur_ms) p = s->dur_ms;
}
return p;
}
void meow_media_dump(const char *prefix)
{
for (int i = 0; i < MEOW_MEDIA_MAX; i++) {
if (!g_sess[i].used) continue;
media_log("%s sess%d app=%s state=%u fd=%d last=%llu pos=%u/%u title=\"%s\"",
prefix ? prefix : "media", i, g_sess[i].app_id, g_sess[i].state,
g_sess[i].fd, (unsigned long long)g_sess[i].last_active_ms,
g_sess[i].pos_ms, g_sess[i].dur_ms, g_sess[i].title);
}
}