Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/meow_media.c
T
MeowOS ad9778476a fix(§10.5): 通知中心叠加媒体保活——播放中 App 不 SIGSTOP(修播放中滑通知中心音乐停)
- launcher: run_app_notif_overlay 入口 meow_media_app_playing() 判定(当前源+PLAYING+app_id 匹配)
  → 不 SIGSTOP/不 SIGCONT,App 续跑保 PCM;非播放中维持 SIGSTOP 模板;关闭时 unlink 标记挪到
  恢复帧上屏后(防未冻结 App 抢 present 一帧回闪)。
- SDK gfx: present() 见 /tmp/meow_notif_active(env MEOW_NOTIF_ACTIVE_FILE 覆写)自抑上屏,
  仅 App 进程(系统 UI/launcher 不抑制),200ms 节流 stat,脏区累积解除后一次上屏;
  meow_gfx_overlay_suppressed_count 供单测断言。
- SDK input: 叠加期排空丢弃本进程 evdev 副本 + EVIOCGKEY 位图静默对齐(防面板点击被 App
  双重消费/解除后边沿误判),进入沿复位触摸状态机。
- 宿主:新 test_overlay_suppress T1-T4 + test_media §8(8 用例)全 PASS;test_notif_*/
  test_sdk_*/test_vol_ramp_jack 等回归全过;zig-mipsel -Wall -Wextra 全 TU 0 warning。
- meow_launcherd fbb82036(/usr/bin 只读待固件,fw_content.conf 已更)/ meow_ui.a d6242fc2 /
  hello a073535a / files 2b799d07(已热更到卡);ncm 未重链(ncm_app 他 agent 在改)。
2026-09-01 15:23:32 +08:00

210 lines
6.9 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;
}
int meow_media_app_playing(const char *app_id)
{
const meow_media_session_t *s = meow_media_current();
if (!s || !app_id || !app_id[0]) return 0;
return (s->state == MEOW_MEDIA_PLAYING && strcmp(s->app_id, app_id) == 0) ? 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);
}
}