git-subtree-dir: sdk git-subtree-mainline:17a972d46fgit-subtree-split:2e7561d74f
159 lines
6.0 KiB
C
159 lines
6.0 KiB
C
#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;
|
||
}
|