Files
MeowOS-SDK/sdk/test_ui_audio_host.c
T
MeowOS f44e3bc6d0 Add 'sdk/' from commit '2e7561d74f75de05dd7fb362f203f600afb451d7'
git-subtree-dir: sdk
git-subtree-mainline: 17a972d46f
git-subtree-split: 2e7561d74f
2026-09-01 21:08:40 +08:00

159 lines
6.0 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.
#define _GNU_SOURCE
/* test_ui_audio_host.c — meow_ui_audio SDK 客户端宿主测试。
*
* 验证:
* - meow_audio_open/write/query/pause/resume/close 全链(走 audiod FILE sink)
* - on_preempt 回调:第二客户端(raw)抢先 → 本 SDK 客户端收到 PREEMPTED,write 返回负值
*
* 启动被测试 audiod(宿主二进制,FILE sink)。
* 编译:cc -I../meow_launcherd -I. -Os -Wall -Wextra test_ui_audio_host.c meow_ui_audio.c -lpthread -o test_ui_audio_host
*/
#include "meow_ui.h"
#include "meow_ipc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#define TSOCK "/tmp/meow_audio_ui.sock"
#define TWAV "/tmp/meow_audio_ui.wav"
static int g_pass = 0, g_fail = 0;
static void check(int c, const char *n) { if (c) { printf(" PASS %s\n", n); g_pass++; } else { printf(" FAIL %s\n", n); g_fail++; } }
static int g_preempted = 0;
static char g_by[64];
static void preempt_cb(const char *by_app, void *ctx)
{
(void)ctx;
g_preempted = 1;
snprintf(g_by, sizeof g_by, "%s", by_app ? by_app : "?");
}
/* 小工具:raw 客户端连接 */
static int raw_connect(void)
{
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;
snprintf(a.sun_path, sizeof a.sun_path, "%s", TSOCK);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
return fd;
}
static int w_all(int fd, const void *p, size_t n)
{
const uint8_t *b = p; size_t o = 0;
while (o < n) { ssize_t r = write(fd, b + o, n - o); if (r < 0) { if (errno == EINTR) continue; return -1; } o += (size_t)r; }
return 0;
}
static int r_all(int fd, void *p, size_t n)
{
uint8_t *b = p; size_t o = 0;
while (o < n) { ssize_t r = read(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; }
return 0;
}
static int sf(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
memcpy(hdr, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &type, 2); memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &len, 4);
if (w_all(fd, hdr, sizeof hdr)) return -1;
if (len && pl && w_all(fd, pl, len)) return -1;
return 0;
}
static int rf(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (r_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 && r_all(fd, rbuf, len)) return -1;
return (int)len;
}
static int raw_open(int fd, const char *app, int rate)
{
m_audio_open_t o; memset(&o, 0, sizeof o);
snprintf(o.app_id, sizeof o.app_id, "%s", app);
o.rate = (uint32_t)rate; o.ch = 2; o.fmt = AUDIO_FMT_S16;
if (sf(fd, MSG_AUDIO_OPEN, 9, &o, sizeof o)) return -1;
uint8_t rbuf[64]; uint16_t rt;
if (rf(fd, &rt, rbuf, sizeof rbuf) < 0 || rt != MSG_AUDIO_OPEN_ACK) return -1;
m_audio_open_ack_t a; memcpy(&a, rbuf, sizeof a);
return (a.err == MEOW_OK) ? (int)a.stream_id : -1;
}
static int g_audiod_pid = -1;
static void stop_audiod(void) { if (g_audiod_pid > 0) { kill(g_audiod_pid, SIGTERM); waitpid(g_audiod_pid, NULL, 0); g_audiod_pid = -1; } }
int main(void)
{
unlink(TSOCK); unlink(TWAV);
setenv("MEOW_AUDIO_SOCK", TSOCK, 1); /* SDK 客户端连接测试 audiod 的 socket */
g_audiod_pid = fork();
if (g_audiod_pid == 0) {
setenv("MEOW_AUDIO_SINK", "file", 1);
setenv("MEOW_AUDIO_OUT", TWAV, 1);
setenv("MEOW_AUDIO_SOCK", TSOCK, 1);
setenv("MEOW_AUDIO_FORCE", "1", 1);
execl("../meow_audiod/meow_audiod_host", "meow_audiod_host", (char *)0); /* SDK 测试运行于 sdk/ 目录 */
_exit(127);
}
usleep(300000);
printf("=== meow_ui_audio SDK host test ===\n");
/* 注册回调(读线程懒启动) */
meow_audio_on_preempt(preempt_cb, NULL);
/* 无 audiod 场景:断开后 open 应返回 -ENOENT(先测一次) */
int sid = meow_audio_open(44100, 2, AUDIO_FMT_S16);
check(sid > 0, "open (audiod present)");
/* 写正弦(stereo:L/R 同值交织) */
static int16_t pcm[4096];
for (int i = 0; i < 2048; i++) {
int16_t s = (int16_t)(sin(2.0 * M_PI * 440 * i / 44100) * 0.3 * 32767);
pcm[i * 2] = s; pcm[i * 2 + 1] = s;
}
int wr = meow_audio_write(pcm, 2048);
check(wr == 2048, "write 2048 frames");
meow_audio_state st;
check(meow_audio_query(&st) == 0 && st.active == 1, "query active");
printf(" state: active=%d app=%s rate=%u written=%llu\n", st.active, st.app_id, (unsigned)st.rate, (unsigned long long)st.written_frames);
check(meow_audio_pause(sid, 1) == 0, "pause");
meow_audio_query(&st);
check(st.paused == 1, "query paused");
check(meow_audio_pause(sid, 0) == 0, "resume");
/* 第二客户端(raw)抢先 → 本 SDK 收 PREEMPTED,write 返回负值 */
int rfd = raw_connect();
check(rfd >= 0, "raw connect");
int rsid = raw_open(rfd, "raw-app", 96000);
check(rsid > 0, "raw open (preempt)");
usleep(200000);
check(g_preempted == 1, "on_preempt fired");
printf(" preempted by %s\n", g_by);
check(meow_audio_write(pcm, 2048) < 0, "write after preempt -> <0 (dead)");
check(meow_audio_query(&st) == 0, "query still works");
/* close 旧 sid(已死,应无副作用) */
check(meow_audio_close(sid) == 0, "close dead sid");
check(meow_audio_pause(sid, 1) < 0 || meow_audio_pause(sid, 1) == -MEOW_ERR_NOENT, "pause dead sid -> error");
close(rfd);
stop_audiod();
printf("=== SUMMARY: pass=%d fail=%d ===\n", g_pass, g_fail);
return g_fail ? 1 : 0;
}