186 lines
8.9 KiB
C
186 lines
8.9 KiB
C
/* test_media_lyric.c — 宿主单测:批3 live 歌词数据源框架(meow_media 会话携带 lrc/tlrc → meow_lyric 解析 → 随播放位置命中当前行)。
|
||
*
|
||
* 链路:App 上报歌词(分片 m_lyric → meow_media_lyric)→ 会话累积 lrc/tlrc(有界、seq 代际)
|
||
* → launcher 读取(meow_media_lyric_valid/lrc/tlrc)→ meow_lyric_parse → meow_lyric_line_at(meow_media_display_pos)。
|
||
*
|
||
* 覆盖矩阵:
|
||
* L1 App 上报 lrc/tlrc → 会话携带歌词(valid/lrc/tlrc 正确)
|
||
* L2 launcher 读取 → meow_lyric_parse 成功(count/行文本/翻译合并正确)
|
||
* L3 播放位置推进(display_pos 随 PLAYING 单调增)→ line_at 命中当前行(pos 增大 → 行号不降)
|
||
* L4 分片重组:>MEOW_LYRIC_CHUNK 的 lrc 拆多帧上报,会话按 off 累积成完整串
|
||
* L5 seq 代际切换(换歌)→ 清旧词;同 seq 重报(幂等)不破坏
|
||
* L6 会话不存在 → meow_media_lyric 返回 -1;翻译无匹配自动忽略
|
||
* L7 多 App 独立:另一源上报自己的歌词,互不串
|
||
*
|
||
* 编译(宿主):meow_media.c(纯 libc)+ meow_lyric.c(纯 libc+pthread)。
|
||
* gcc -Wall -Wextra -pthread -o test_media_lyric test_media_lyric.c meow_media.c ../sdk/meow_lyric.c -I. -I../sdk
|
||
* ./test_media_lyric
|
||
*/
|
||
#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"
|
||
#include "meow_lyric.h"
|
||
|
||
static int g_fails = 0;
|
||
#define CHECK(cond, msg) do { \
|
||
if (cond) printf("PASS: %s\n", msg); \
|
||
else { printf("FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); g_fails++; } } while (0)
|
||
|
||
static size_t test_send(int fd, uint16_t type, uint32_t req_id,
|
||
const void *payload, uint32_t len)
|
||
{
|
||
/* 用不到(本链路无下行),占位满足 meow_media_ops.send 签名。 */
|
||
(void)fd; (void)type; (void)req_id; (void)payload; (void)len;
|
||
return 0;
|
||
}
|
||
|
||
static void fill_meta(m_meta_t *m, const char *app, uint32_t state, uint32_t pos, uint32_t dur)
|
||
{
|
||
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, "Song");
|
||
snprintf(m->artist, sizeof m->artist, "Artist");
|
||
}
|
||
|
||
/* 模拟 ncm 的 send_lyric_kind:把 text 按 ≤MEOW_LYRIC_CHUNK 分片,逐片调 meow_media_lyric。 */
|
||
static void report_lyric_chunked(const char *app, uint32_t seq, uint16_t kind,
|
||
const char *text, size_t cmax)
|
||
{
|
||
if (!text) text = "";
|
||
size_t total = strlen(text);
|
||
if (total > cmax) total = cmax;
|
||
size_t off = 0;
|
||
do {
|
||
size_t n = total - off;
|
||
if (n > MEOW_LYRIC_CHUNK) n = MEOW_LYRIC_CHUNK;
|
||
m_lyric_t ly;
|
||
memset(&ly, 0, sizeof ly);
|
||
snprintf(ly.app_id, sizeof ly.app_id, "%s", app);
|
||
ly.seq = seq;
|
||
ly.kind = kind;
|
||
ly.total = (uint16_t)total;
|
||
ly.off = (uint16_t)off;
|
||
ly.len = (uint16_t)n;
|
||
if (n) memcpy(ly.data, text + off, n);
|
||
ly.data[n] = 0;
|
||
int rc = meow_media_lyric(&ly);
|
||
if (rc != 0) { printf(" (report chunk off=%zu rc=%d)\n", off, rc); }
|
||
off += n;
|
||
} while (off < total);
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
meow_media_ops ops;
|
||
ops.send = test_send;
|
||
ops.log = NULL;
|
||
meow_media_init(&ops);
|
||
|
||
const char *lrc = "[00:01.00]Hello\n[00:02.00]World\n[00:03.00]Bye\n";
|
||
const char *tlrc = "[00:01.00]你好\n[00:03.00]再见\n[99:99.99]ignore me\n";
|
||
|
||
/* ---- L1+L4 大片(跨分片)---- */
|
||
char big[4096];
|
||
int bi = 0;
|
||
bi += snprintf(big + bi, sizeof big - bi, "[00:00.10]p0\n");
|
||
for (int i = 1; i <= 40; i++) /* 约 40 行 → >MEOW_LYRIC_CHUNK(980B) */
|
||
bi += snprintf(big + bi, sizeof big - bi, "[00:%02d.%02d]line%d\n", i / 60, i % 60, i);
|
||
|
||
/* 注册会话(META 先行,否则 meow_media_lyric 丢弃) */
|
||
m_meta_t m;
|
||
fill_meta(&m, "dev.player.ncm", MEOW_MEDIA_PLAYING, 1000, 120000);
|
||
CHECK(meow_media_meta(&m, -1) == 0, "META registers session");
|
||
|
||
/* 上报歌词:seq=1(新代际),lrc 与 tlrc 各分片 */
|
||
report_lyric_chunked("dev.player.ncm", 1, 0, lrc, MEOW_MEDIA_LRC_MAX);
|
||
report_lyric_chunked("dev.player.ncm", 1, 1, tlrc, MEOW_MEDIA_TLRC_MAX);
|
||
|
||
const meow_media_session_t *s = meow_media_current();
|
||
CHECK(s && !strcmp(s->app_id, "dev.player.ncm"), "current() == dev.player.ncm");
|
||
CHECK(meow_media_lyric_valid(s) == 1, "L1: lyric_valid == 1");
|
||
CHECK(strcmp(meow_media_lyric_lrc(s), lrc) == 0, "L1: lrc matches");
|
||
CHECK(strcmp(meow_media_lyric_tlrc(s), tlrc) == 0, "L1: tlrc matches");
|
||
|
||
/* ---- L2 launcher 读取 → 解析 ---- */
|
||
int n = meow_lyric_parse(meow_media_lyric_lrc(s), meow_media_lyric_tlrc(s));
|
||
CHECK(n == 3, "L2: meow_lyric_parse count == 3");
|
||
CHECK(meow_lyric_count() == 3, "L2: count() == 3");
|
||
CHECK(strcmp(meow_lyric_line_text(0), "Hello") == 0, "L2: line0 text Hello");
|
||
CHECK(strcmp(meow_lyric_line_tl(0), "你好") == 0, "L2: line0 tl 你好 (2000ms 合并)");
|
||
CHECK(strcmp(meow_lyric_line_text(2), "Bye") == 0, "L2: line2 text Bye");
|
||
CHECK(strcmp(meow_lyric_line_tl(2), "再见") == 0, "L2: line2 tl 再见");
|
||
CHECK(meow_lyric_count() == 3, "L2: 无匹配翻译行被忽略(仍 3 行)");
|
||
|
||
/* ---- L3 播放位置推进 → 命中当前行 ---- */
|
||
uint32_t pA = meow_media_display_pos(s);
|
||
int lineA = meow_lyric_line_at(pA);
|
||
CHECK(lineA == 0, "L3: display_pos ≈ 1000ms → line 0 (Hello)");
|
||
usleep(600 * 1000); /* 播放中:display_pos 随墙钟推进 */
|
||
uint32_t pB = meow_media_display_pos(s);
|
||
int lineB = meow_lyric_line_at(pB);
|
||
CHECK(pB >= pA, "L3: display_pos advances (playing)");
|
||
CHECK(lineB >= lineA, "L3: line_at pos 增大 → 行号不降(随播放命中当前行)");
|
||
/* 再推进到 ≥3000ms 应到末行 */
|
||
usleep(1500 * 1000);
|
||
uint32_t pC = meow_media_display_pos(s);
|
||
CHECK(pC >= 3000u || lineB >= 2, "L3: pos 推进后到位(或已末行)");
|
||
CHECK(meow_lyric_line_at(pC) >= meow_lyric_line_at(pB), "L3: 行号单调不降");
|
||
|
||
/* ---- L4 大片分片重组(big lrc)---- */
|
||
meow_media_clear(NULL);
|
||
fill_meta(&m, "dev.player.ncm", MEOW_MEDIA_PLAYING, 0, 120000);
|
||
meow_media_meta(&m, -1);
|
||
report_lyric_chunked("dev.player.ncm", 2, 0, big, MEOW_MEDIA_LRC_MAX);
|
||
s = meow_media_current();
|
||
CHECK(s && meow_media_lyric_valid(s) == 1, "L4: big lyric_valid == 1");
|
||
int bn = meow_lyric_parse(meow_media_lyric_lrc(s), NULL);
|
||
CHECK(bn > 40, "L4: 大片跨分片重组后解析行数 > 40");
|
||
CHECK(meow_lyric_line_at(0) == 0 && meow_lyric_line_at(10) == 0, "L4: 低 pos 命中第0行");
|
||
CHECK(meow_lyric_line_at(meow_lyric_line_time(bn - 1)) == bn - 1, "L4: 末行命中");
|
||
|
||
/* ---- L5 seq 代际切换(换歌)清旧词 ---- */
|
||
report_lyric_chunked("dev.player.ncm", 3, 0, "[00:05.00]New song line\n", MEOW_MEDIA_LRC_MAX);
|
||
s = meow_media_current();
|
||
CHECK(meow_media_lyric_valid(s) == 1, "L5: 新代际仍 valid");
|
||
CHECK(strcmp(meow_media_lyric_lrc(s), "[00:05.00]New song line\n") == 0, "L5: 新词覆盖旧词");
|
||
CHECK(meow_media_lyric_tlrc(s)[0] == 0, "L5: 新代际清空旧翻译");
|
||
/* seq 不变重报:幂等(不清) */
|
||
report_lyric_chunked("dev.player.ncm", 3, 1, "[00:05.00]新歌\n", MEOW_MEDIA_TLRC_MAX);
|
||
s = meow_media_current();
|
||
CHECK(strcmp(meow_media_lyric_tlrc(s), "[00:05.00]新歌\n") == 0, "L5: 同 seq 补翻译不清");
|
||
|
||
/* ---- L6 会话不存在 → -1;空 app_id → -1 ---- */
|
||
m_lyric_t empty;
|
||
memset(&empty, 0, sizeof empty);
|
||
snprintf(empty.app_id, sizeof empty.app_id, "no.session.app");
|
||
empty.seq = 9; empty.kind = 0; empty.total = 0; empty.off = 0; empty.len = 0;
|
||
CHECK(meow_media_lyric(&empty) == -1, "L6: 未注册会话 → -1");
|
||
memset(&empty, 0, sizeof empty);
|
||
CHECK(meow_media_lyric(&empty) == -1, "L6: 空 app_id → -1");
|
||
|
||
/* ---- L7 多 App 独立 ---- */
|
||
fill_meta(&m, "com.player2", MEOW_MEDIA_PLAYING, 0, 180000);
|
||
meow_media_meta(&m, -1);
|
||
meow_media_clear("dev.player.ncm"); /* 只清 ncm */
|
||
report_lyric_chunked("com.player2", 1, 0, "[00:01.00]Second app\n", MEOW_MEDIA_LRC_MAX);
|
||
s = meow_media_current();
|
||
CHECK(s && !strcmp(s->app_id, "com.player2"), "L7: current == com.player2");
|
||
CHECK(meow_media_lyric_valid(s) == 1, "L7: player2 歌词有效");
|
||
CHECK(strcmp(meow_media_lyric_lrc(s), "[00:01.00]Second app\n") == 0, "L7: player2 歌词独立");
|
||
|
||
/* ---- 空歌词(无词歌曲):上报空串 → valid=0 ---- */
|
||
report_lyric_chunked("com.player2", 2, 0, "", MEOW_MEDIA_LRC_MAX);
|
||
report_lyric_chunked("com.player2", 2, 1, "", MEOW_MEDIA_TLRC_MAX);
|
||
s = meow_media_current();
|
||
CHECK(meow_media_lyric_valid(s) == 0, "空词上报 → lyric_valid == 0");
|
||
|
||
printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails);
|
||
return g_fails ? 1 : 0;
|
||
}
|