Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_media.c
T
MeowOS 820d1e52bf fix(media): 物理播放/暂停键(164)改发 PLAYCTL cmd=6 toggle + App 前台状态栏音量%实时刷新
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 实体按压留用户终验。
2026-09-01 14:44:55 +08:00

176 lines
7.1 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.
/* test_media.c — 宿主单测:meow_media 媒体会话(阶段D附录B)。
*
* 验证:
* 1) META 上报 → 注册会话,current()=最近上报者;
* 2) 最近活跃切换(两个源,后上报者胜);
* 3) has_source()(state=0 停止 → 0);
* 4) PLAYCTL 下发字节格式(socketpair 对端可读回 MSG_PLAYCTL 帧,cmd/seek 正确);
* 5) meow_media_playctl 无源 → -1;
* 6) clear(app) 清会话 → current()=NULL。
* 只依赖 meow_media.c + meow_ipc.h(纯 C),宿主 gcc 直编。
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include "meow_media.h"
#include "meow_ipc.h"
static int g_fails = 0;
#define CHECK(cond, msg) do { \
if (cond) printf("PASS: %s\n", msg); \
else { printf("FAIL: %s\n", msg); g_fails++; } } while (0)
/* ops.send:把帧写到 fd(socketpair 一端)。 */
static size_t test_send(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 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 void fill_meta(m_meta_t *m, const char *app, uint32_t state, uint32_t pos, uint32_t dur,
const char *title, const char *artist)
{
memset(m, 0, sizeof *m);
snprintf(m->app_id, sizeof m->app_id, "%s", app);
m->state = state; m->pos_ms = pos; m->dur_ms = dur;
snprintf(m->title, sizeof m->title, "%s", title);
snprintf(m->artist, sizeof m->artist, "%s", artist);
}
/* 从 fd 读一帧(头+负载),校验 magic/ver,返回负载类型;不足返回 -1。 */
static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, uint32_t rcap)
{
uint8_t rhdr[MEOW_IPC_HDR_LEN];
size_t off = 0;
while (off < sizeof rhdr) {
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
if (r <= 0) return -1;
off += (size_t)r;
}
uint32_t magic, len; uint16_t ver;
memcpy(&magic, rhdr, 4); memcpy(&ver, rhdr + 4, 2); memcpy(type, rhdr + 6, 2);
memcpy(req_id, rhdr + 8, 4); memcpy(&len, rhdr + 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;
}
int main(void)
{
meow_media_ops ops;
ops.send = test_send;
ops.log = NULL; /* 静默 */
meow_media_init(&ops);
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) { perror("socketpair"); return 1; }
/* sv[0]=会话 fd(launcher 下发端);sv[1]=测试读回端(模拟 App)。 */
/* 1) 单源注册 */
m_meta_t m;
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 120000, 240000, "Song A", "Artist A");
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers source");
const meow_media_session_t *cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm");
CHECK(cur && cur->state == MEOW_MEDIA_PLAYING, "state == PLAYING");
CHECK(meow_media_has_source() == 1, "has_source == 1 (playing)");
/* 2) 多源最近活跃切换 */
fill_meta(&m, "com.player2", MEOW_MEDIA_PLAYING, 0, 180000, "Song B", "Artist B");
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers second source");
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.player2"), "current() == com.player2 (newest)");
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 30000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm after re-report (newest)");
/* 3) state=0 停止 → has_source=0(但会话仍在,current() 仍指向它) */
fill_meta(&m, "com.ncm", MEOW_MEDIA_STOPPED, 0, 0, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
cur = meow_media_current();
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() still == com.ncm (stopped)");
CHECK(meow_media_has_source() == 0, "has_source == 0 (stopped)");
/* 恢复播放,准备 PLAYCTL 下发验证 */
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 1000, 240000, "Song A", "Artist A");
meow_media_meta(&m, sv[0]);
/* 4) PLAYCTL 下发:cmd=1(播放),读回端应收到 MSG_PLAYCTL/第11号 req */
CHECK(meow_media_playctl(1, 0) == 0, "playctl sends (cmd=play)");
uint8_t rbuf[512];
uint16_t type; uint32_t req;
int n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
CHECK(n == (int)sizeof(m_playctl_t), "downlink frame payload == m_playctl_t size");
CHECK(type == MSG_PLAYCTL, "downlink type == MSG_PLAYCTL");
m_playctl_t c;
memcpy(&c, rbuf, sizeof c);
CHECK(c.cmd == 1 && c.seek_ms == 0, "downlink cmd=1 seek=0");
/* 5) seek:cmd=5 seek=12345 */
CHECK(meow_media_playctl(5, 12345) == 0, "playctl sends (cmd=seek)");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 5 && c.seek_ms == 12345, "downlink seek cmd=5 seek=12345");
/* 5b) toggle 透传:cmd=6(物理播放/暂停键)原样下发,launcher 不改写 cmd */
CHECK(meow_media_playctl(6, 0) == 0, "playctl sends (cmd=toggle)");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 6 && c.seek_ms == 0, "downlink cmd=6 passthrough");
/* playctl_to 目标 app(另一会话) */
CHECK(meow_media_playctl_to("com.player2", 3, 0) == 0, "playctl_to player2 sends");
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
memcpy(&c, rbuf, sizeof c);
CHECK(type == MSG_PLAYCTL && c.cmd == 3, "downlink to player2 cmd=3");
CHECK(meow_media_playctl_to("no.such.app", 1, 0) == -1, "playctl_to unknown app == -1");
/* 6) 无源:清全部 → playctl -1 / has_source 0 */
meow_media_clear(NULL);
CHECK(meow_media_current() == NULL, "current() == NULL after clear all");
CHECK(meow_media_playctl(1, 0) == -1, "playctl no source == -1");
CHECK(meow_media_has_source() == 0, "has_source == 0 after clear");
close(sv[0]); close(sv[1]);
/* 7) 空 app_id 拒绝 */
fill_meta(&m, "", MEOW_MEDIA_PLAYING, 0, 0, "x", "y");
CHECK(meow_media_meta(&m, sv[0]) == -1, "meta empty app_id == -1");
printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails);
return g_fails ? 1 : 0;
}