Files
MeowOS ade0a57353 A1: meow_audiod 独占音频服务(agent-244 骨架 + agent-246 真机收尾)
- meow_audiod:启动门控(/tmp/meow_boot_mode 非 meow exit 0)、IO/播放/路由三线程、
  ring ~200ms、ALSA UAPI 独占直开、切率 mute-重配-恢复、抢占(后开赢+PREEMPTED)、
  socket EOF 回收、xrun PREPARE 恢复;meow_route 轮询逻辑并入路由线程
- meow_audio CLI:status/tone/route/vol;services.d/audiod.conf(trigger=manual,on-fail)
- SDK meow_ui_audio.c 编进 meow_ui.a:懒连接/读线程分发 PREEMPTED 回调/EPIPE 标死/
  无 audiod 返回 MEOW_ERR_NOENT 不隐式直开(MEOW_AUDIO_DIRECT=1 仅调试)
- launcher:确认 MeowOS 模式后 meow_svcd start audiod(double-fork best-effort 不阻塞)
- meow_route 退役:S89 预置换 audiod.conf + 清理旧 meow_route.conf(防拉起已删二进制
  重启循环);fw_content.conf 删 meow_route、加 meow_audiod+meow_audio(md5 打包时填)
- 真机定位并修复 ALSA UAPI 两大坑(即 ncm 播放无声根因):
  ① hw_params 须全放行初始化(mask 全 1/interval [0,UINT_MAX]),memset 0 interval
     等价只能取 0 → refine 必空 → HW_PARAMS 恒 EINVAL
  ② access/format mask 先清零再单点(pmask 只 OR),全 1 被 choose 成 MMAP_INTERLEAVED
     → write() 恒 EINVAL
- 附带修复:dev_write fd<0 防日志风暴丢 PCM 限频日志;SDK state 解析 app_id 截空白;
  meow_ui.h 文档负错误码表述修正
- 验证:宿主 FILE sink 全 IPC 链+抢占+tone WAV 20/20,SDK 客户端 13/13;
  qemu-mipsel 同过;真机 tone 44.1k/96k 切率 written=played、underrun=0、日志完整
- meow_ui.a 53a3edd2 → ncm 重链 d2031829(已热更到卡);launcher a2f932a1
2026-08-31 22:40:08 +08:00

159 lines
6.0 KiB
C
Raw Permalink 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;
}