Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_audio.c
T
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

516 lines
20 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.
/* meow_ui_audio.c — App 侧独占音频客户端(SDK 公共件;见 meow_ui.h §11)。
*
* 定位:App 只经本 API 喂 PCM 给 meow_audiod(独占 hw:0,0 守护)。audiod 不在一律失败
* (open 返回 MEOW_ERR_NOENT 等负错误码),不做隐式直开回退;调试可 env MEOW_AUDIO_DIRECT=1 走自带
* 直接 ALSA 直开(非正式能力)。
*
* 传输:AF_UNIX SOCK_STREAM 到 audiod socket(缺省 /usr/data/meow_audio.sock;env
* MEOW_AUDIO_SOCK 覆盖,宿主/qemu)。帧格式复用 meow_ipc.h 16B 头 + m_audio_* 负载(同一份)。
*
* 线程模型(懒连 + 内部读线程):
* - 首次 open/query 时 connect → 懒启动读线程(poll 读 audiod 下发帧)。
* - 读线程为唯一读方:PREEMPTED → 调 on_preempt 回调 + 标写死;OPEN_ACK/STATE/ACK(匹配
* 当前挂起请求)→ 拷贝到 g_resp、唤醒调用方(条件变量)。
* - 写路径(write/pause/resume/close)由 App 线程在另一方向写同一 fd(全双工,互不干扰)。
*
* 格式归一:open 返回前记录 grant_rate/grant_ch/grant_fmt;meow_audio_write 把 native(open 参数)
* 样本逐帧转成 grant 格式(mono→stereo 复制 / S16↔S32 定值升降位),分批 ≤4096B 发 MSG_AUDIO_WRITE。
* 无 SRC:grant_rate == native rate(audiod 把 DAC 原生率设为流率)。
*/
#include "meow_ui.h"
#include "meow_ipc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <time.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifdef __linux__
#include <sound/asound.h>
#include <sys/ioctl.h>
#define UI_AUDIO_HAVE_ALSA 1
#endif
#define AUD_SOCK_DEFAULT "/usr/data/meow_audio.sock"
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;
static int g_fd = -1;
static pthread_t g_thr;
static int g_thr_on = 0;
static volatile int g_quit = 0;
/* 当前流状态 */
static int g_has_stream = 0;
static uint32_t g_sid = 0;
static int g_dead = 0; /* 收到 PREEMPTED 后置 1:write 返回负值 */
static int g_own_rate = 0, g_own_ch = 0, g_own_fmt = 0; /* native(open 参数) */
static int g_grant_rate = 0, g_grant_ch = 0, g_grant_fmt = 0;
static meow_audio_preempt_fn g_preempt_cb = NULL;
static void *g_preempt_ctx = NULL;
/* 挂起请求(读线程分发;一次只有一个) */
static uint32_t g_pending = 0; /* req_id,0=无挂起 */
static uint16_t g_resp_type = 0;
static uint8_t g_resp[MEOW_IPC_MAX];
static uint32_t g_resp_len = 0;
/* 调试直开(env MEOW_AUDIO_DIRECT=1) */
static int g_direct = 0;
static int g_dir_fd = -1;
static const char *sock_path(void)
{
const char *e = getenv("MEOW_AUDIO_SOCK");
return (e && e[0]) ? e : AUD_SOCK_DEFAULT;
}
static int connect_path(const char *path)
{
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;
strncpy(a.sun_path, path, sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
return fd;
}
static int write_all(int fd, const uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t w = write(fd, p + off, n - off);
if (w < 0) { if (errno == EINTR) continue; return -1; }
if (w == 0) return -1;
off += (size_t)w;
}
return 0;
}
static int read_all(int fd, uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t r = read(fd, p + off, n - off);
if (r < 0) { if (errno == EINTR) continue; return -1; }
if (r == 0) return -1;
off += (size_t)r;
}
return 0;
}
static int send_frame(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);
if (write_all(fd, hdr, sizeof hdr) != 0) return -1;
if (len && payload && write_all(fd, payload, len) != 0) return -1;
return 0;
}
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (read_all(fd, hdr, sizeof hdr) != 0) 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 > 0 && read_all(fd, rbuf, len) != 0) return -1;
return (int)len;
}
static void *audio_thread_main(void *arg);
/* (调用方已持 mu)标记断线并关闭。 */
static void mark_dead_locked(void)
{
if (g_fd >= 0) { close(g_fd); g_fd = -1; }
g_dead = 1;
}
/* (调用方已持 mu)懒连接 + 懒起读线程。成功 0;audiod 不在 -1。 */
static int conn_locked(void)
{
if (g_fd >= 0) return g_fd;
if (g_direct) return -2; /* 调试直开:不连 socket */
const char *sp = sock_path();
int fd = connect_path(sp);
if (fd < 0) return -1;
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
g_fd = fd;
if (!g_thr_on) { g_quit = 0; g_thr_on = 1; pthread_create(&g_thr, NULL, audio_thread_main, NULL); pthread_detach(g_thr); }
return fd;
}
/* ---- 调试直开(MEOW_AUDIO_DIRECT=1,仅开发) ---- */
#ifdef UI_AUDIO_HAVE_ALSA
static void pmask(struct snd_mask *m, unsigned bit) { m->bits[bit / 32] |= 1u << (bit % 32); }
static void pivset(struct snd_interval *iv, unsigned val)
{
iv->min = iv->max = val; iv->integer = 1; iv->openmin = iv->openmax = 0; iv->empty = 0;
}
static int direct_alsa_open(int rate)
{
int fd = open("/dev/snd/pcmC0D0p", O_RDWR);
if (fd < 0) return -1;
struct snd_pcm_hw_params p;
/* 「全放行」初始化(snd_pcm_hw_params_any 语义):memset 0 的 interval 会被内核当成
* 「只能取 0」约束 → refine 必空 → HW_PARAMS 恒 EINVAL(audiod 同款坑,勿回退)。 */
memset(&p, 0, sizeof p);
unsigned nm = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1;
for (unsigned i = 0; i < nm; i++)
for (unsigned b = 0; b < sizeof p.masks[i].bits / sizeof p.masks[i].bits[0]; b++)
p.masks[i].bits[b] = ~0u;
unsigned ni = SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1;
for (unsigned i = 0; i < ni; i++) { p.intervals[i].min = 0; p.intervals[i].max = ~0u; }
p.flags = 0;
/* mask 先清零再单点(pmask 只 OR):全 1 的 access 会被内核 choose 成 MMAP_INTERLEAVED
* → write() EINVAL(audiod 同款第二坑,勿回退)。 */
struct snd_mask *am = &p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(am, 0, sizeof *am); pmask(am, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
struct snd_mask *fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(fm, 0, sizeof *fm); pmask(fm, SNDRV_PCM_FORMAT_S16_LE);
struct snd_mask *sm = &p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(sm, 0, sizeof *sm); pmask(sm, SNDRV_PCM_SUBFORMAT_STD);
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], 2);
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], (unsigned)rate);
p.rmask = ~0u; p.info = 0; p.msbits = 0; p.rate_num = 0; p.rate_den = 0;
if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p) != 0) { close(fd); return -1; }
ioctl(fd, SNDRV_PCM_IOCTL_PREPARE, 0);
return fd;
}
#endif
/* ---- 格式转换 ---- */
static int fmt_bps(int fmt)
{
switch (fmt) {
case AUDIO_FMT_S16: return 2;
case AUDIO_FMT_S24:
case AUDIO_FMT_S32:
case AUDIO_FMT_FLOAT: return 4;
default: return 2;
}
}
static int32_t sample_from_native(const uint8_t *p, int fmt)
{
if (fmt == AUDIO_FMT_S16) { int16_t v; memcpy(&v, p, 2); return v; }
int32_t v; memcpy(&v, p, 4); return v; /* S32/S24(32-in-32)/FLOAT 按 32 位采样 */
}
static void sample_to_grant(uint8_t *p, int fmt, int32_t v)
{
if (fmt == AUDIO_FMT_S16) { int16_t x = (int16_t)v; memcpy(p, &x, 2); return; }
memcpy(p, &v, 4);
}
static int32_t convert_sample(int32_t v, int nfmt, int gfmt)
{
int nb = (nfmt == AUDIO_FMT_S16) ? 16 : 32;
int gb = (gfmt == AUDIO_FMT_S16) ? 16 : 32;
if (gb > nb) return v << (gb - nb);
if (gb < nb) return v >> (nb - gb);
return v;
}
/* 发一批 WRITE(≤MEOW_IPC_MAX,帧对齐;调用方已持 mu) */
static int send_write_chunk(const uint8_t *p, uint32_t len)
{
if (g_fd < 0) { g_dead = 1; return -1; }
if (send_frame(g_fd, MSG_AUDIO_WRITE, 0, p, len) != 0) { mark_dead_locked(); return -1; }
return 0;
}
/* ---- 公开 API ---- */
int meow_audio_open(int rate, int ch, int fmt)
{
/* env 检查:调试直开 */
const char *de = getenv("MEOW_AUDIO_DIRECT");
g_direct = (de && de[0] == '1') ? 1 : 0;
pthread_mutex_lock(&g_mu);
if (g_has_stream) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_BUSY; }
if (ch > 2 || ch < 1 || rate <= 0 || fmt < AUDIO_FMT_S16 || fmt > AUDIO_FMT_FLOAT || fmt == AUDIO_FMT_FLOAT) {
pthread_mutex_unlock(&g_mu);
return MEOW_ERR_INVAL;
}
g_own_rate = rate; g_own_ch = ch; g_own_fmt = fmt;
if (g_direct) {
#ifdef UI_AUDIO_HAVE_ALSA
int fd = direct_alsa_open(rate);
if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NODEV; }
g_dir_fd = fd;
g_grant_rate = rate; g_grant_ch = 2; g_grant_fmt = AUDIO_FMT_S16;
g_sid = 1; g_has_stream = 1; g_dead = 0;
pthread_mutex_unlock(&g_mu);
return 1;
#else
pthread_mutex_unlock(&g_mu);
return MEOW_ERR_NODEV;
#endif
}
int fd = conn_locked();
if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; } /* audiod 不在 */
m_audio_open_t o;
memset(&o, 0, sizeof o);
snprintf(o.app_id, sizeof o.app_id, "%s", getenv("MEOW_LOG_APP") ? getenv("MEOW_LOG_APP") : "app");
o.rate = (uint32_t)rate; o.ch = (uint16_t)ch; o.fmt = (uint16_t)fmt;
uint32_t req = (g_pending == 0xFFFFFFFF) ? 1 : (g_pending + 1); /* 单调递增(单个挂起) */
g_pending = req;
g_resp_len = 0;
if (send_frame(g_fd, MSG_AUDIO_OPEN, req, &o, sizeof o) != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; }
/* 等响应(3s 超时) */
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3;
while (g_pending == req) {
int r = pthread_cond_timedwait(&g_cond, &g_mu, &ts);
if (r != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; }
}
if (g_resp_type != MSG_AUDIO_OPEN_ACK || g_resp_len < sizeof(m_audio_open_ack_t)) {
pthread_mutex_unlock(&g_mu);
return MEOW_ERR_IO;
}
m_audio_open_ack_t a;
memcpy(&a, g_resp, sizeof a);
if (a.err != MEOW_OK) { pthread_mutex_unlock(&g_mu); return (int)a.err; }
g_sid = a.stream_id;
g_grant_rate = (int)a.grant_rate; g_grant_ch = (int)a.grant_ch; g_grant_fmt = (int)a.grant_fmt;
g_has_stream = 1; g_dead = 0;
pthread_mutex_unlock(&g_mu);
return (int)g_sid;
}
int meow_audio_write(const void *pcm, int frames)
{
if (!pcm || frames <= 0) return 0;
pthread_mutex_lock(&g_mu);
if (g_direct) {
size_t nb = (size_t)frames * (size_t)g_grant_ch * (size_t)fmt_bps(g_grant_fmt);
const uint8_t *p = (const uint8_t *)pcm;
size_t off = 0;
while (off < nb) {
ssize_t w = write(g_dir_fd, p + off, nb - off);
if (w < 0) { if (errno == EINTR) continue; pthread_mutex_unlock(&g_mu); return -1; }
off += (size_t)w;
}
pthread_mutex_unlock(&g_mu);
return frames;
}
if (!g_has_stream || g_dead || g_fd < 0) { pthread_mutex_unlock(&g_mu); return -1; }
/* 归一:native → grant */
int ownfs = g_own_ch * fmt_bps(g_own_fmt);
int gfs = g_grant_ch * fmt_bps(g_grant_fmt);
static uint8_t out[MEOW_IPC_MAX];
size_t ofs = 0;
int done_frames = 0;
const uint8_t *np = (const uint8_t *)pcm;
for (int i = 0; i < frames; i++) {
/* 取 native 帧样本 */
int32_t s[2];
for (int c = 0; c < g_own_ch && c < 2; c++)
s[c] = sample_from_native(np + (size_t)i * ownfs + (size_t)c * fmt_bps(g_own_fmt), g_own_fmt);
/* 整帧才能进 payload(每帧不得跨两个 WRITE 分片) */
if (ofs + (size_t)gfs > sizeof out) {
if (send_write_chunk(out, (uint32_t)ofs) != 0) { pthread_mutex_unlock(&g_mu); return -1; }
ofs = 0;
}
/* 生成 grant 帧:own_ch==1 → 复制;own_ch==2 → 直通 */
for (int c = 0; c < g_grant_ch && c < 2; c++) {
int32_t v = (g_own_ch == 1) ? s[0] : s[c];
v = convert_sample(v, g_own_fmt, g_grant_fmt);
sample_to_grant(out + ofs, g_grant_fmt, v);
ofs += (size_t)fmt_bps(g_grant_fmt);
}
done_frames++;
}
if (ofs && send_write_chunk(out, (uint32_t)ofs) != 0) { pthread_mutex_unlock(&g_mu); return -1; }
pthread_mutex_unlock(&g_mu);
return done_frames;
}
int meow_audio_pause(int sid, int paused)
{
pthread_mutex_lock(&g_mu);
if (g_direct) { pthread_mutex_unlock(&g_mu); return 0; }
if (!g_has_stream || g_sid != (uint32_t)sid) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; }
m_audio_stream_t s; s.stream_id = (uint32_t)sid;
uint32_t req = g_pending + 1; g_pending = req;
if (send_frame(g_fd, paused ? MSG_AUDIO_PAUSE : MSG_AUDIO_RESUME, req, &s, sizeof s) != 0) {
mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO;
}
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3;
while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; } }
pthread_mutex_unlock(&g_mu);
return 0;
}
int meow_audio_close(int sid)
{
pthread_mutex_lock(&g_mu);
if (g_direct) { g_has_stream = 0; if (g_dir_fd >= 0) close(g_dir_fd); g_dir_fd = -1; pthread_mutex_unlock(&g_mu); return 0; }
if (g_has_stream && g_sid == (uint32_t)sid) {
m_audio_stream_t s; s.stream_id = (uint32_t)sid;
uint32_t req = g_pending + 1; g_pending = req;
if (send_frame(g_fd, MSG_AUDIO_STOP, req, &s, sizeof s) == 0) {
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3;
while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) break; }
}
g_has_stream = 0;
g_sid = 0;
g_dead = 0;
}
pthread_mutex_unlock(&g_mu);
return 0;
}
/* 从 audiod STATE 文本解析关键字段 */
static int state_text_parse(const char *t, meow_audio_state *o)
{
memset(o, 0, sizeof *o);
o->route_port = -1;
const char *p;
if ((p = strstr(t, "active="))) o->active = atoi(p + 7);
if ((p = strstr(t, "app="))) {
/* app_id 只取到下一个空白(状态行是空格分隔 k=v,整体拷贝会吞掉后续字段) */
p += 4;
size_t k = 0;
while (p[k] && p[k] != ' ' && p[k] != '\n' && p[k] != '\t' && k < sizeof o->app_id - 1) {
o->app_id[k] = p[k]; k++;
}
o->app_id[k] = 0;
}
if ((p = strstr(t, "sid="))) o->stream_id = (uint32_t)atoi(p + 4);
if ((p = strstr(t, "rate="))) o->rate = (uint32_t)atoi(p + 5);
if ((p = strstr(t, "ch="))) o->ch = (uint16_t)atoi(p + 3);
if ((p = strstr(t, "fmt="))) {
int f = 0;
if (strncmp(p + 4, "S16", 3) == 0) f = AUDIO_FMT_S16;
else if (strncmp(p + 4, "S24", 3) == 0) f = AUDIO_FMT_S24;
else if (strncmp(p + 4, "S32", 3) == 0) f = AUDIO_FMT_S32;
o->fmt = (uint16_t)f;
}
if ((p = strstr(t, "written="))) o->written_frames = (uint64_t)strtoull(p + 8, NULL, 10);
if ((p = strstr(t, "played="))) o->played_frames = (uint64_t)strtoull(p + 7, NULL, 10);
if ((p = strstr(t, "underrun="))) o->underrun = (uint32_t)atoi(p + 9);
if ((p = strstr(t, "paused="))) o->paused = atoi(p + 7);
if ((p = strstr(t, "port="))) o->route_port = atoi(p + 5);
if ((p = strstr(t, "vol="))) o->vol = atoi(p + 4);
if (o->app_id[0] == '\0') snprintf(o->app_id, sizeof o->app_id, "-");
return 0;
}
int meow_audio_query(meow_audio_state *out)
{
if (!out) return MEOW_ERR_INVAL;
pthread_mutex_lock(&g_mu);
if (g_direct) {
memset(out, 0, sizeof *out);
out->active = g_has_stream;
out->stream_id = g_sid;
out->rate = (uint32_t)g_grant_rate;
out->ch = (uint16_t)g_grant_ch;
out->fmt = (uint16_t)g_grant_fmt;
out->route_port = -1;
pthread_mutex_unlock(&g_mu);
return 0;
}
int fd = conn_locked();
if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; }
uint32_t req = g_pending + 1; g_pending = req;
if (send_frame(g_fd, MSG_AUDIO_QUERY, req, NULL, 0) != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; }
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3;
while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; } }
if (g_resp_type != MSG_AUDIO_STATE) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; }
g_resp[g_resp_len < MEOW_IPC_MAX - 1 ? g_resp_len : MEOW_IPC_MAX - 1] = 0;
state_text_parse((const char *)g_resp, out);
pthread_mutex_unlock(&g_mu);
return 0;
}
void meow_audio_on_preempt(meow_audio_preempt_fn cb, void *ctx)
{
pthread_mutex_lock(&g_mu);
g_preempt_cb = cb;
g_preempt_ctx = ctx;
int need = !g_thr_on && !g_direct;
if (need) { g_quit = 0; g_thr_on = 1; }
pthread_mutex_unlock(&g_mu);
if (need) { pthread_create(&g_thr, NULL, audio_thread_main, NULL); pthread_detach(g_thr); }
}
/* 读线程主体:poll 读 audiod 下发帧 → 分发 PREEMPTED / 挂起响应。
* **不**自行 connect——连接由调用方(open/query 经 conn_locked)建立并启动本线程;
* 这里只等 g_fd 就绪后 poll(避免与调用方 conn_locked 竞争出新 socket)。 */
static void *audio_thread_main(void *arg)
{
(void)arg;
while (!g_quit) {
pthread_mutex_lock(&g_mu);
int fd = g_fd;
pthread_mutex_unlock(&g_mu);
if (fd < 0) { usleep(300000); continue; }
struct pollfd p;
p.fd = fd; p.events = POLLIN; p.revents = 0;
int pr = poll(&p, 1, 200);
if (pr < 0 && errno != EINTR) {
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
continue;
}
if (pr > 0 && (p.revents & (POLLHUP | POLLERR | POLLNVAL))) {
pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu);
continue;
}
if (pr > 0 && (p.revents & POLLIN)) {
uint8_t rbuf[MEOW_IPC_MAX];
uint16_t type;
int n = read_frame(fd, &type, rbuf, sizeof rbuf);
if (n < 0) { pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu); continue; }
if (type == MSG_AUDIO_PREEMPTED && n >= (int)sizeof(m_audio_preempted_t)) {
m_audio_preempted_t pm;
memcpy(&pm, rbuf, sizeof pm);
meow_audio_preempt_fn cb;
void *ctx;
pthread_mutex_lock(&g_mu);
cb = g_preempt_cb; ctx = g_preempt_ctx;
if (pm.stream_id == g_sid) { g_has_stream = 0; g_dead = 1; g_sid = 0; }
pthread_mutex_unlock(&g_mu);
if (cb) cb(pm.by_app, ctx);
} else if (type == MSG_AUDIO_OPEN_ACK || type == MSG_ACK || type == MSG_AUDIO_STATE) {
pthread_mutex_lock(&g_mu);
if (g_pending) {
g_resp_type = type;
if ((uint32_t)n > MEOW_IPC_MAX) n = (int)MEOW_IPC_MAX;
memcpy(g_resp, rbuf, (size_t)n);
g_resp_len = (uint32_t)n;
g_pending = 0;
pthread_cond_signal(&g_cond);
}
pthread_mutex_unlock(&g_mu);
}
/* 其它类型丢弃 */
}
}
pthread_mutex_lock(&g_mu);
mark_dead_locked();
pthread_mutex_unlock(&g_mu);
return NULL;
}