fix(bt): 蓝牙P2 mipsel libasound链接 — 改glibc-dynamic(设备实为glibc2.22硬浮点,pcm.bluealsa=独立glibc插件需dlopen,musl-static死路)直链设备libasound.so.2,BT层脱离dlopen(内联9个libasound原型);qemu装载动态库验证+宿主67pass/0warning;audiod改glibc-dynamic(57KB)M6.30后需重验DAC出声
This commit is contained in:
1 parent
71b2dafecc
commit
4ecddb5ea3
2 files changed
+73
-75
No files matched your search
@@ -1,29 +1,41 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# build_audiod.sh — meow_audiod 交叉编译(mipsel 静态)+ 宿主多目标可选。
|
# build_audiod.sh — meow_audiod 交叉编译(mipsel glibc-dynamic)+ 宿主多目标可选。
|
||||||
#
|
#
|
||||||
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。audiod 复用 meow_ipc.h(共享协议头,
|
# 目标:R3 Pro II / MeowOS(MIPS32 o32,真实 ABI)。audiod 复用 meow_ipc.h(共享协议头,
|
||||||
# -I../meow_launcherd)。audiod 为 svcd 托管的独立守护;meow_audio 为用户侧 CLI。
|
# -I../meow_launcherd)。audiod 为 svcd 托管的独立守护;meow_audio 为用户侧 CLI。
|
||||||
#
|
#
|
||||||
|
# 【P2 蓝牙链接定案 2026-09-03】设备为 glibc-2.22(ld-2.22.so / libc-2.22.so,非 musl),且
|
||||||
|
# libasound 会在运行时 dlopen 单独的 bluealsa pcm 插件(/usr/lib/alsa-lib/libasound_module_pcm_bluealsa.so)
|
||||||
|
# ——故 audiod 必须以**动态 glibc** 形态链接设备现有 libasound.so.2:
|
||||||
|
# - 用 zig --target=mipsel-linux-gnueabihf.2.22(硬浮点 o32,glibc-2.22 版本引用,设备 libs 齐);
|
||||||
|
# libasound 原型在 meow_audiod.c 内联声明(不依赖 asoundlib.h)。
|
||||||
|
# - 删 -static:musl-static 无 dlopen(实测 "Dynamic loading not supported",无法加载 bluealsa 插件);
|
||||||
|
# musl-dynamic 需设备不存在的 musl ld.so。libasound 链接库来自 libbt/(设备 libasound.so.2 副本)。
|
||||||
|
# - meow_audio CLI 无 libasound 依赖,保持 musl-static(zig-mipsel-cc)。
|
||||||
|
#
|
||||||
# 用法:
|
# 用法:
|
||||||
# ./build_audiod.sh # 交叉编译 → meow_audiod(mipsel 静态)
|
# ./build_audiod.sh # 交叉编译 → meow_audiod(mipsel glibc-dynamic,BT 直链 libasound)
|
||||||
# ./build_audiod.sh cli # 交叉编译 meow_audio CLI → meow_audio(mipsel 静态)
|
# ./build_audiod.sh cli # 交叉编译 meow_audio CLI → meow_audio(mipsel musl-static)
|
||||||
# ./build_audiod.sh host # 宿主编译 meow_audiod_host + meow_audio_host(集成测试)
|
# ./build_audiod.sh host # 宿主编译 *_host 并跑 test_audiod_host 回归(FILE sink)
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
ROOT="$HOME/Project/R3ProII"
|
ROOT="$HOME/Project/R3ProII"
|
||||||
CC="$ROOT/.tools/zig-mipsel-cc"
|
CC="$ROOT/.tools/zig-mipsel-cc"
|
||||||
|
ZIG="$ROOT/.tools/zig-env/bin/zig"
|
||||||
INC="-I$ROOT/patch_v2/meow_launcherd"
|
INC="-I$ROOT/patch_v2/meow_launcherd"
|
||||||
|
|
||||||
if [ "$1" = "host" ]; then
|
if [ "$1" = "host" ]; then
|
||||||
HOST_CC="${CC_HOST:-cc}"
|
HOST_CC="${CC_HOST:-cc}"
|
||||||
# -ldl:BT 输出目标用 dlopen 运行时解析 libasound(pcm.bluealsa 是 alsa-lib 插件,非内核节点;
|
# BT 目标直链宿主 libasound.so.2(无 asoundlib.h,原型内联于 meow_audiod.c;-l: 精确到版本,
|
||||||
# 主机/交叉工具链无 asoundlib.h/libasound,故不静态链接)。mipsel 静态 musl 不支持 dlopen,见
|
# 不依赖 unversioned dev 链接)。删 -ldl:BT 不再用 dlopen。
|
||||||
# meow_audiod.c bt 段注释(真机 mipsel 需主会话提供 musl-libasound 或改动态,本任务以源码+宿主为准)。
|
"$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread -l:libasound.so.2
|
||||||
"$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread -ldl
|
|
||||||
"$HOST_CC" $INC -Os -Wall -Wextra meow_audio.c -o meow_audio_host -lm
|
"$HOST_CC" $INC -Os -Wall -Wextra meow_audio.c -o meow_audio_host -lm
|
||||||
|
"$HOST_CC" $INC -Os -Wall -Wextra test_audiod_host.c -o test_audiod_host -lm
|
||||||
echo "== host builds =="
|
echo "== host builds =="
|
||||||
ls -la meow_audiod_host meow_audio_host
|
ls -la meow_audiod_host meow_audio_host test_audiod_host
|
||||||
file meow_audiod_host meow_audio_host
|
file meow_audiod_host meow_audio_host test_audiod_host
|
||||||
|
echo "== host regression (test_audiod_host) =="
|
||||||
|
./test_audiod_host || { echo "!! host regression FAILED"; exit 1; }
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -36,8 +48,21 @@ if [ "$1" = "cli" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
"$CC" $INC -Os -Wall -Wextra -static meow_audiod.c -o meow_audiod -lpthread
|
# meow_audiod:mipsel glibc-dynamic,BT 直链 libasound(-Llibbt -lasound)。-lpthread 供播放线程。
|
||||||
echo "== meow_audiod (mipsel static) =="
|
# 链接源 libbt/libasound.so.2.0.0(设备固件 libasound.so.2 副本);缺失时从固件镜像自拷贝(idempotent)。
|
||||||
|
LIBBT="libbt"
|
||||||
|
ASOUND_SRC="$ROOT/hiby_os_crack/r3proii/squashfs-root-example/usr/lib/libasound.so.2.0.0"
|
||||||
|
mkdir -p "$LIBBT"
|
||||||
|
if [ ! -f "$LIBBT/libasound.so.2.0.0" ]; then cp "$ASOUND_SRC" "$LIBBT/libasound.so.2.0.0"; fi
|
||||||
|
ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so"
|
||||||
|
ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so.2"
|
||||||
|
"$ZIG" cc --target=mipsel-linux-gnueabihf.2.22 $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod \
|
||||||
|
-lpthread -Llibbt -lasound
|
||||||
|
echo "== meow_audiod (mipsel glibc-dynamic, BT links libasound.so.2) =="
|
||||||
ls -la meow_audiod
|
ls -la meow_audiod
|
||||||
md5sum meow_audiod
|
md5sum meow_audiod
|
||||||
file meow_audiod
|
file meow_audiod
|
||||||
|
echo "-- dynamic NEEDED --"
|
||||||
|
readelf -d meow_audiod 2>/dev/null | grep -iE "needed" || true
|
||||||
|
echo "-- glibc version refs (max must be <= 2.22) --"
|
||||||
|
readelf --version-info meow_audiod 2>/dev/null | grep -oE "GLIBC_2\.[0-9]+" | sort -uV | tail -3 || true
|
||||||
@@ -57,7 +57,6 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include "meow_ipc.h"
|
#include "meow_ipc.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@@ -78,7 +77,6 @@
|
|||||||
/* ---- 蓝牙 A2DP source(P2 输出目标 OUT_BT) ---- */
|
/* ---- 蓝牙 A2DP source(P2 输出目标 OUT_BT) ---- */
|
||||||
#define BT_PCM_DEFAULT "pcm.bluealsa" /* alsa.conf `pcm.bluealsa`(plug→bluealsa);aplay -D bluealsa 等价 */
|
#define BT_PCM_DEFAULT "pcm.bluealsa" /* alsa.conf `pcm.bluealsa`(plug→bluealsa);aplay -D bluealsa 等价 */
|
||||||
#define BT_PCM_FALLBACK "bluealsa" /* 部分配置只定义别名节点,防打开失败再试一次 */
|
#define BT_PCM_FALLBACK "bluealsa" /* 部分配置只定义别名节点,防打开失败再试一次 */
|
||||||
#define BT_SO "libasound.so.2" /* 运行时 dlopen(不静态链接:设备为 musl 静态勿 dlopen,见 main 注释) */
|
|
||||||
#define BT_CODEC_AUTO "auto"
|
#define BT_CODEC_AUTO "auto"
|
||||||
#define BT_VOL_FULL_PCM "/usr/bin/bluealsa-cli" /* 一键设 AVRCP 100%(best-effort,不存在则跳过) */
|
#define BT_VOL_FULL_PCM "/usr/bin/bluealsa-cli" /* 一键设 AVRCP 100%(best-effort,不存在则跳过) */
|
||||||
|
|
||||||
@@ -187,12 +185,10 @@ static uint32_t g_dev_rate = 0, g_dev_ch = 0, g_dev_fmt = 0;
|
|||||||
static int g_reconfig_pending = 0;
|
static int g_reconfig_pending = 0;
|
||||||
|
|
||||||
/* ---- 蓝牙 A2DP source 运行时状态(g_mu 保护;仅 g_target==OUT_BT && g_sink==SINK_ALSA 时用) ----
|
/* ---- 蓝牙 A2DP source 运行时状态(g_mu 保护;仅 g_target==OUT_BT && g_sink==SINK_ALSA 时用) ----
|
||||||
* 直接写 libasound 插件 PCM(pcm.bluealsa). 不静态链接 libasound:主机/工具链均无 asoundlib,
|
* 直接调用 libasound 插件 PCM(pcm.bluealsa)。audiod 以动态 glibc 链接设备 libasound.so.2
|
||||||
* 用 dlopen/dlsym 运行时解析(设备 libasound.so.2 在 /usr/lib;见 dl 注释)。 */
|
* (见 bt_* 段注释),此处仅存 snd_pcm_t 不透明句柄。 */
|
||||||
static void *g_bt_hdl = NULL; /* snd_pcm_t *(libasound 不透明句柄) */
|
static void *g_bt_hdl = NULL; /* snd_pcm_t *(libasound 不透明句柄) */
|
||||||
static int g_bt_open = 0; /* BT PCM 已打开 */
|
static int g_bt_open = 0; /* BT PCM 已打开 */
|
||||||
static void *g_bt_so = NULL; /* dlopen 句柄 */
|
|
||||||
static int g_bt_loaded = 0; /* dlopen 尝试过(失败不再重试风暴) */
|
|
||||||
static int g_bt_vol_full_done = 0; /* 已对 AVRCP 置 100%(幂等,一次即可) */
|
static int g_bt_vol_full_done = 0; /* 已对 AVRCP 置 100%(幂等,一次即可) */
|
||||||
|
|
||||||
/* ---- 数字域电平表(DAC 实时监控;CS43198 无模拟电平回读,播放线程看 PCM 样本) ----
|
/* ---- 数字域电平表(DAC 实时监控;CS43198 无模拟电平回读,播放线程看 PCM 样本) ----
|
||||||
@@ -824,61 +820,39 @@ static int settings_get_volume(void)
|
|||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* 蓝牙 A2DP source(输出目标 OUT_BT,PLAN §三/§五)
|
* 蓝牙 A2DP source(输出目标 OUT_BT,PLAN §三/§五)
|
||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
* audiod 用 libasound **运行时 dlopen** 直连 `pcm.bluealsa`(plug→bluealsa,播放向,A2DP),
|
* audiod **直接调用 libasound** 连 `pcm.bluealsa`(plug→bluealsa,播放向,A2DP),
|
||||||
* 把播放器解码输出透传过去。BT 是**独立目标**:
|
* 把播放器解码输出透传过去。BT 是**独立目标**:
|
||||||
* - 不开 hw:0,0(不 period=1024×4、不 FPGA numid=9 武装、不 DAC 音量反转);
|
* - 不开 hw:0,0(不 period=1024×4、不 FPGA numid=9 武装、不 DAC 音量反转);
|
||||||
* - 保留音频流透传 + 格式归一(grant_fmt=S16,plug 层转 codec 协商率);
|
* - 保留音频流透传 + 格式归一(grant_fmt=S16,plug 层转 codec 协商率);
|
||||||
* - 音量走 AVRCP/耳机侧(音量隔离:绝不下发 DAC numid=1/2 反转,见 vol_apply)。
|
* - 音量走 AVRCP/耳机侧(音量隔离:绝不下发 DAC numid=1/2 反转,见 vol_apply)。
|
||||||
* dlopen 说明:主机/交叉工具链均无 asoundlib.h/libasound,静态链接不可行;且设备 musl-静态
|
* 链接说明(2026-09-03 真机定案):`pcm.bluealsa` 的 bluealsa 是**独立 pcm 插件**,libasound
|
||||||
* 二进制本身不支持 dlopen("Dynamic loading not supported")——因此 mipsel 真机若要生效,
|
* 在运行时以 dlopen 加载 /usr/lib/alsa-lib/libasound_module_pcm_bluealsa.so(glibc,依赖
|
||||||
* 需主会话提供 musl-mipsel libasound 并用 `-lasound` 直接链接(或改动态 musl-PIE)。本源码以
|
* libdbus-1.so.3)——故 audiod 必须以**动态 glibc** 形态链接设备现有 libasound.so.2
|
||||||
* dlopen 路径编译到宿主(0 warning),真机 mipsel 链接此点列为本任务风险清单。禁止旁路:不跑
|
* (build_audiod.sh:zig --target=mipsel-linux-gnueabihf.2.22,删 -static)。musl-static 不可行
|
||||||
* bluealsa-aplay/bluealsa-cli open(sink/D-Bus 或新建 daemon),audiod 内换目标 PCM 是唯一最简。
|
* (实测 "Dynamic loading not supported",无 dlopen 无法加载插件);musl-dynamic 亦不可行(设备
|
||||||
|
* 无 musl ld.so)。libasound 原型在此内联声明(避免依赖 asoundlib.h;工具链不提供)。
|
||||||
|
* 禁止旁路:不跑 bluealsa-aplay/bluealsa-cli open(D-Bus/新建 daemon),audiod 内换目标 PCM 唯一最简。
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
#define SND_PCM_STREAM_PLAYBACK 0
|
#define SND_PCM_STREAM_PLAYBACK 0
|
||||||
#define SND_PCM_ACCESS_RW_INTERLEAVED 3
|
#define SND_PCM_ACCESS_RW_INTERLEAVED 3
|
||||||
#define SND_PCM_FORMAT_S16_LE 2
|
#define SND_PCM_FORMAT_S16_LE 2
|
||||||
#define SND_PCM_NONBLOCK 1
|
#define SND_PCM_NONBLOCK 1
|
||||||
|
|
||||||
static int (*g_fn_open)(void **, const char *, int, int);
|
/* libasound 相关函数原型(内联声明,避免依赖 asoundlib.h;snd_pcm_t 不透明 → void*) */
|
||||||
static int (*g_fn_set_params)(void *, int, int, unsigned, unsigned, int, unsigned);
|
int snd_pcm_open(void **pcm, const char *name, int stream, int mode);
|
||||||
static int (*g_fn_nonblock)(void *, int);
|
int snd_pcm_set_params(void *pcm, int format, int access, unsigned channels,
|
||||||
static long (*g_fn_writei)(void *, const void *, unsigned long);
|
unsigned rate, int soft_resample, unsigned latency_us);
|
||||||
static int (*g_fn_wait)(void *, int);
|
int snd_pcm_nonblock(void *pcm, int nonblock);
|
||||||
static int (*g_fn_recover)(void *, int, int);
|
long snd_pcm_writei(void *pcm, const void *buffer, unsigned long size);
|
||||||
static int (*g_fn_drain)(void *);
|
int snd_pcm_wait(void *pcm, int timeout);
|
||||||
static int (*g_fn_close)(void *);
|
int snd_pcm_recover(void *pcm, int err, int silent);
|
||||||
static const char *(*g_fn_strerror)(int);
|
int snd_pcm_drain(void *pcm);
|
||||||
/* dlsym→函数指针:用 memcpy 转(POSIX 可移植惯例;直接 `(void*)&fn` 或 union 会触发
|
int snd_pcm_close(void *pcm);
|
||||||
* -Wstrict-aliasing / 函数指针类型告警,memcpy 无此问题)。 */
|
const char *snd_strerror(int errnum);
|
||||||
#define BT_LOAD_SYM(field, name) do { \
|
|
||||||
void *p_ = dlsym(g_bt_so, name); \
|
|
||||||
if (!p_) { logmsg("bt: dlsym %s failed", name); dlclose(g_bt_so); g_bt_so = NULL; return -1; } \
|
|
||||||
memcpy(&g_##field, &p_, sizeof g_##field); \
|
|
||||||
} while (0)
|
|
||||||
static void bt_close(void); /* 前向:bt_write 判死路径要用 */
|
static void bt_close(void); /* 前向:bt_write 判死路径要用 */
|
||||||
static void bt_vol_full(void); /* 前向:bt_open 成功时促成 AVRCP 100% */
|
static void bt_vol_full(void); /* 前向:bt_open 成功时促成 AVRCP 100% */
|
||||||
|
|
||||||
static int bt_load(void)
|
|
||||||
{
|
|
||||||
if (g_bt_so) return 0;
|
|
||||||
if (g_bt_loaded) return -1; /* 已试过且失败:不再重试(防 dlopen 风暴) */
|
|
||||||
g_bt_loaded = 1;
|
|
||||||
g_bt_so = dlopen(BT_SO, RTLD_NOW);
|
|
||||||
if (!g_bt_so) { logmsg("bt: dlopen %s failed: %s", BT_SO, dlerror()); return -1; }
|
|
||||||
BT_LOAD_SYM(fn_open, "snd_pcm_open");
|
|
||||||
BT_LOAD_SYM(fn_set_params,"snd_pcm_set_params");
|
|
||||||
BT_LOAD_SYM(fn_nonblock, "snd_pcm_nonblock");
|
|
||||||
BT_LOAD_SYM(fn_writei, "snd_pcm_writei");
|
|
||||||
BT_LOAD_SYM(fn_wait, "snd_pcm_wait");
|
|
||||||
BT_LOAD_SYM(fn_recover, "snd_pcm_recover");
|
|
||||||
BT_LOAD_SYM(fn_drain, "snd_pcm_drain");
|
|
||||||
BT_LOAD_SYM(fn_close, "snd_pcm_close");
|
|
||||||
BT_LOAD_SYM(fn_strerror, "snd_strerror");
|
|
||||||
logmsg("bt: libasound loaded via dlopen");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bt.codec 生效:bluealsa 的 codec 是 config-time 参数(/usr/data/alsa.conf `pcm.bluealsa` 块的
|
/* bt.codec 生效:bluealsa 的 codec 是 config-time 参数(/usr/data/alsa.conf `pcm.bluealsa` 块的
|
||||||
* `codec "X"`)。audiod 在 BT 打开前做一次**幂等且外科式**的 token 替换:只把 bluealsa 块内的
|
* `codec "X"`)。audiod 在 BT 打开前做一次**幂等且外科式**的 token 替换:只把 bluealsa 块内的
|
||||||
* `codec "..."` 改成 `codec "<g_bt_codec>"`(其余字符不动)。仅真机且文件可写时执行;
|
* `codec "..."` 改成 `codec "<g_bt_codec>"`(其余字符不动)。仅真机且文件可写时执行;
|
||||||
@@ -936,30 +910,29 @@ static void bt_apply_codec_conf(void)
|
|||||||
free(out);
|
free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BT 打开:dlopen libasound → snd_pcm_open(pcm.bluealsa/bluealsa, PLAYBACK, NONBLOCK) →
|
/* BT 打开:libasound → snd_pcm_open(pcm.bluealsa/bluealsa, PLAYBACK, NONBLOCK) →
|
||||||
* set_params(S16_LE, RW, ch, rate, soft_resample, 100ms)。返回 0=ok。失败置 g_bt_open=0。 */
|
* set_params(S16_LE, RW, ch, rate, soft_resample, 100ms)。返回 0=ok。失败置 g_bt_open=0。 */
|
||||||
static int bt_open(void)
|
static int bt_open(void)
|
||||||
{
|
{
|
||||||
if (bt_load() != 0) return -1;
|
|
||||||
void *pcm = NULL;
|
void *pcm = NULL;
|
||||||
const char *names[2] = { BT_PCM_DEFAULT, BT_PCM_FALLBACK };
|
const char *names[2] = { BT_PCM_DEFAULT, BT_PCM_FALLBACK };
|
||||||
int r = -1;
|
int r = -1;
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
r = g_fn_open(&pcm, names[i], SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
r = snd_pcm_open(&pcm, names[i], SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||||
if (r == 0) { logmsg("bt: opened %s", names[i]); break; }
|
if (r == 0) { logmsg("bt: opened %s", names[i]); break; }
|
||||||
logmsg("bt: snd_pcm_open %s: %s", names[i], g_fn_strerror ? g_fn_strerror(r) : "?");
|
logmsg("bt: snd_pcm_open %s: %s", names[i], snd_strerror(r));
|
||||||
pcm = NULL;
|
pcm = NULL;
|
||||||
}
|
}
|
||||||
if (r != 0) return -1;
|
if (r != 0) return -1;
|
||||||
g_bt_hdl = pcm;
|
g_bt_hdl = pcm;
|
||||||
g_fn_nonblock(pcm, 1);
|
snd_pcm_nonblock(pcm, 1);
|
||||||
unsigned rate = g_st.rate ? g_st.rate : 44100;
|
unsigned rate = g_st.rate ? g_st.rate : 44100;
|
||||||
unsigned ch = g_st.ch ? g_st.ch : 2;
|
unsigned ch = g_st.ch ? g_st.ch : 2;
|
||||||
r = g_fn_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED,
|
r = snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
ch, rate, 1, 100000);
|
ch, rate, 1, 100000);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
logmsg("bt: set_params %u/%u/S16: %s", rate, ch, g_fn_strerror ? g_fn_strerror(r) : "?");
|
logmsg("bt: set_params %u/%u/S16: %s", rate, ch, snd_strerror(r));
|
||||||
g_fn_close(pcm); g_bt_hdl = NULL; return -1;
|
snd_pcm_close(pcm); g_bt_hdl = NULL; return -1;
|
||||||
}
|
}
|
||||||
g_bt_open = 1;
|
g_bt_open = 1;
|
||||||
logmsg("bt: PCM ready rate=%u ch=%u fmt=S16 codec=%s", rate, ch, g_bt_codec);
|
logmsg("bt: PCM ready rate=%u ch=%u fmt=S16 codec=%s", rate, ch, g_bt_codec);
|
||||||
@@ -985,12 +958,12 @@ static void bt_write(uint8_t *buf, size_t n)
|
|||||||
const uint8_t *off = buf;
|
const uint8_t *off = buf;
|
||||||
int stalls = 0;
|
int stalls = 0;
|
||||||
while (left > 0 && !g_stop) {
|
while (left > 0 && !g_stop) {
|
||||||
long w = g_fn_writei(g_bt_hdl, off, left);
|
long w = snd_pcm_writei(g_bt_hdl, off, left);
|
||||||
if (w > 0) { off += (size_t)w * fs; left -= (unsigned long)w; stalls = 0; continue; }
|
if (w > 0) { off += (size_t)w * fs; left -= (unsigned long)w; stalls = 0; continue; }
|
||||||
if (w < 0) {
|
if (w < 0) {
|
||||||
int err = (int)(-w);
|
int err = (int)(-w);
|
||||||
if (err == EAGAIN || err == EWOULDBLOCK) {
|
if (err == EAGAIN || err == EWOULDBLOCK) {
|
||||||
int pr = g_fn_wait(g_bt_hdl, ALSA_POLL_MS);
|
int pr = snd_pcm_wait(g_bt_hdl, ALSA_POLL_MS);
|
||||||
if (pr == 0) { /* 无空间超时 → 停摆计数(镜像 DAC stall) */
|
if (pr == 0) { /* 无空间超时 → 停摆计数(镜像 DAC stall) */
|
||||||
g_stall++; stalls++;
|
g_stall++; stalls++;
|
||||||
if (stalls >= ALSA_STALL_MAX) {
|
if (stalls >= ALSA_STALL_MAX) {
|
||||||
@@ -1006,10 +979,10 @@ static void bt_write(uint8_t *buf, size_t n)
|
|||||||
if (err == EPIPE || err == ESTRPIPE) { /* xrun:recover 后继续 */
|
if (err == EPIPE || err == ESTRPIPE) { /* xrun:recover 后继续 */
|
||||||
g_underrun++;
|
g_underrun++;
|
||||||
logmsg("bt xrun #%u -> recover", (unsigned)g_underrun);
|
logmsg("bt xrun #%u -> recover", (unsigned)g_underrun);
|
||||||
g_fn_recover(g_bt_hdl, err, 1);
|
snd_pcm_recover(g_bt_hdl, err, 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
logmsg("bt writei err: %s", g_fn_strerror ? g_fn_strerror(err) : "?");
|
logmsg("bt writei err: %s", snd_strerror(err));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (w == 0) break;
|
if (w == 0) break;
|
||||||
@@ -1021,8 +994,8 @@ static void bt_write(uint8_t *buf, size_t n)
|
|||||||
static void bt_close(void)
|
static void bt_close(void)
|
||||||
{
|
{
|
||||||
if (g_bt_hdl) {
|
if (g_bt_hdl) {
|
||||||
g_fn_drain(g_bt_hdl);
|
snd_pcm_drain(g_bt_hdl);
|
||||||
g_fn_close(g_bt_hdl);
|
snd_pcm_close(g_bt_hdl);
|
||||||
g_bt_hdl = NULL;
|
g_bt_hdl = NULL;
|
||||||
}
|
}
|
||||||
g_bt_open = 0;
|
g_bt_open = 0;
|
||||||
|
|||||||
Reference in new issue
Block a user