RK Android平台音频调试指南:从基础到实战,解决多设备输出、声卡异常等核心问题 电子说
在 Android 开发中,音频模块的调试往往是 “老大难”—— 多声卡无法区分、多设备同时输出没声音、HDMI 录音崩溃… 这些问题不仅影响用户体验,还会消耗大量开发时间。
瑞芯微(Rockchip)针对 RK 平台推出的 MultiAudio 方案,专门解决 Android 原生音频框架的局限,同时提供了一套清晰的调试方法论。今天这篇文章,就从实战角度出发,带大家搞定 RK Android 平台的音频调试,覆盖基础操作、常见 Bug 解决、多设备控制等核心场景。

在聊调试前,我们得先明白“为什么需要调试”——Android 原生音频框架存在不少局限,这也是 RK MultiAudio 方案的出发点:
1.多声卡“认不出、用不了”:即使设备支持多个声卡(比如双 HDMI、SPDIF),原生系统会按 AudioPolicy 优先级选最高的,无法让不同声卡同时输出不同声音;多个同类型声卡(如双 HDMI)更是无法区分。
2.多设备录音需适配:Android 12 虽支持多设备同时录音,但原生代码默认不支持,需要厂商针对性开发。
3.多 APP 音频冲突:系统音频焦点机制会导致多屏同时播放视频时出现暂停,影响多场景使用(如会议投屏 + 本地播放)。
而 RK MultiAudio 方案正好补上这些短板,核心能力包括:
•多 HDMI/DP 插拔识别 + 声音分离(HDMI_0/HDMI_1、DP_0/DP_1 独立输出);
•第三方 APP 按包名指定声卡(如 MX Player 走扬声器、RockVideoPlayer 走 HDMI);
•JAVA 层通过接口直接控制声卡;
•多 HDMI IN 同时录音(HDMIIN_0/HDMIIN_1 独立录音);
•多设备音量同步调节与保存。
调试的第一步,是确认“硬件通路是否正常”—— 也就是声卡是否被正确识别、驱动是否能工作。这两步操作简单,但却是排查问题的基础。
通过 adb 执行命令,查看当前系统已注册的声卡:
cat /proc/asound/cards
以 RK3588 为例,正常输出会类似这样(能看到扬声器、HDMI IN、HDMI 0/1 等声卡):
0 [rockchipes8388 ]: rockchip-es8388 - rockchip-es8388rockchip-es83881 [rockchiphdmiin ]: rockchip_hdmiin - rockchip,hdmiinrockchip,hdmiin2 [rockchiphdmi0 ]: rockchip-hdmi0 - rockchip-hdmi0rockchip-hdmi03 [rockchiphdmi1 ]: rockchip-hdmi1 - rockchip-hdmi1rockchip-hdmi1
如果某块声卡没出现(比如 HDMI 0 缺失),先排查硬件连接(如 HDMI 线是否插好),再检查 DTS 配置是否正确。
确认声卡识别后,用tinyplay 工具测试驱动是否正常(需系统集成 tinyalsa 工具集)。
以测试 HDMI 0 声卡(对应上述输出中的索引 “2”)为例:
1.准备一个 WAV 格式的音频文件(如 test.wav),推到设备的 /data 目录;
2.执行命令播放:
tinyplay /data/test.wav -D 2 -d 0
•-D:指定声卡索引(这里“2” 对应 rockchiphdmi0);
•-d:指定设备编号(通常为 0)。
如果能正常听到声音,说明 HDMI 0 声卡驱动没问题;若没声音,优先排查驱动配置(如 DTS 中声卡节点是否启用)。
调试中最头疼的是“明明配置了,却出问题”—— 我们整理了 RK 平台最常遇到的 5 类音频 Bug,附详细解决步骤。
•现象:代码同步到最新后,HDMI 无音频输出,查看声卡却能识别到。
•原因:新框架支持多 HDMI 识别,若系统只有 1 路 HDMI,上层会默认调用 “hdmi0” 声卡,但 DTS 中声卡名可能配置成了 “hdmi1”,导致匹配失败。
•解决:修改 DTS 文件,将声卡名改为 “rockchip-hdmi0”:
# 找到 DTS 中 HDMI 声卡节点rockchiphdmi1: rockchip-hdmi1 {- rockchip,card-name = "rockchip-hdmi1"+ rockchip,card-name = "rockchip-hdmi0"};
•现象:开启 UBoot Logo 显示后,HDMI 开机无声音,手动插拔一次 HDMI 线才正常。
•原因:HDMI 的 extcon(外部连接状态控制器)状态更新不及时,系统误以为 HDMI 未连接。
•解决:修改 DRM 驱动代码,强制同步 extcon 状态:
在drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c 中添加状态同步逻辑:
mutex_lock(&hdmi->mutex);if (result != hdmi->last_connector_result) {dev_dbg(hdmi->dev, "read_hpd result: %d", result);// 同步 extcon 状态extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, result == connector_status_connected);handle_plugged_change(hdmi, result == connector_status_connected);hdmi->last_connector_result = result;}mutex_unlock(&hdmi->mutex);
•现象:SPDIF(光纤 / 同轴)与扬声器 / HDMI 同时播放时,音频卡顿、断连。
•原因:1)DMA 音频流位置计算错误;2)声卡 MCLK 配置不合理。
•解决:两步操作:
a.集成 3 个关键 Kernel 补丁(修复 DMA 与 PM 问题):
修复 DMA 流位置计算:ALSA: pcm_dmaengine: always get stream position from DMA driver
修复 DMA runtime PM 失衡:dmaengine: pl330: Fix unbalanced runtime PM
提升 DMA 循环传输效率:dmaengine: pl330: Improve dma cyclic transfers efficiency
a.修改 DTS 中 SPDIF 声卡节点,添加 MCLK 配置:
spdif_tx0_sound: spdif-tx0-sound {status = "okay";compatible = "simple-audio-card";+ simple-audio-card,mclk-fs = <128>; // 添加这行simple-audio-card,name = "rockchip,spdif-tx0";// 其他配置...};
•现象:用 HDMI IN 录制外部音频(如机顶盒信号)时,Audio 服务突然崩溃,日志提示 “TimeCheck 超时”。
•原因:HDMI IN 录音的等待时间过短,导致超时触发服务重启。
•解决:修改 I2S-TDM 驱动,延长录音等待时间:
在sound/soc/rockchip/rockchip_i2s_tdm.c 中添加超时配置:
static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, int cmd) {// 其他逻辑...break;+ // 针对录音流,延长等待时间到 500ms+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {+ substream->wait_time = msecs_to_jiffies(500);+ }return ret;}
•现象:同步代码后,所有声卡都没声音,查看proc/asound/cards 能识别到声卡。
•原因:RK 默认代码基于多声卡设备(如 RK3588 EVB1 支持 4 路声卡),优先级为 HDMI0 > HDMI1 > DP0 > DP1;若实际设备只有 1 路 HDMI1,系统仍优先调用 HDMI0(但无对应声卡),导致所有声音无输出。
•解决:修改WiredAccessoryManager.java,屏蔽 HDMI0 的优先级判断:
# 文件路径:frameworks/base/services/core/java/com/android/server/WiredAccessoryManager.javaelse if (headset == BIT_HDMI_AUDIO) {- Slog.d(TAG, "hdmi_0 plug");- outDevice = AudioManager.DEVICE_OUT_HDMI;+ // 屏蔽 HDMI0 识别,避免优先级冲突+ // Slog.d(TAG, "hdmi_0 plug");+ // outDevice = AudioManager.DEVICE_OUT_HDMI;} else if (headset == BIT_HDMI_AUDIO_1) {Slog.d(TAG, "hdmi_1 plug");outDevice = AudioManager.DEVICE_OUT_HDMI_1;
除了调试,掌握 MultiAudio 的核心调用方式,能让开发更高效。
在MediaPlayer 初始化时,通过setAudioSessionId 指定声卡,必须在 setDataSource 前调用:
MediaPlayer mp = new MediaPlayer();// 指定声卡:PRIMARY(57)=扬声器,HDMI0(65),DP0(73),HDMI1(81),DP1(89)mp.setAudioSessionId(65); // 声音从 HDMI0 输出mp.setDataSource("/data/test.mp4");mp.prepare();mp.start();
适合需要固定 APP 输出设备的场景(如会议软件固定走 HDMI),修改 AudioTrack.cpp:
String8 appPackage = String8(mPackageName);// RockVideoPlayer 走 HDMI0,gallery3d 走 HDMI1,MX Player 走扬声器if (strstr(appPackage.string(), "RockVideoPlayer")) {sessionid = (audio_session_t)65;} else if (strstr(appPackage.string(), "gallery3d")) {sessionid = (audio_session_t)81;} else if (strstr(appPackage.string(), "mxtech")) { // MX Player 包名含 mxtechsessionid = (audio_session_t)57;}
同时需要:
•打开MultiAudioTest 宏(在frameworks/av/media/libaudioclient/include/media/AudioTrack.h中设为1);•解决音频焦点冲突:在MediaFocusControl.java 中添加focusChangeHint = 3,避免多APP 同时播放时暂停。
通过AudioRecord 初始化时指定MediaRecorder.AudioSource.HDMIIN,即可录制 HDMI 外部输入的声音:
// 参数:音频源(HDMIIN)、采样率、声道配置、格式、缓冲区大小AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.HDMIIN,44100, // 采样率 44.1kHzAudioFormat.CHANNEL_IN_STEREO, // 立体声AudioFormat.ENCODING_PCM_16BIT, // 16bit PCM4096 // 缓冲区大小);// 开始录音audioRecord.startRecording();
如果开发 TvInput 相关功能(如电视信号输入),可以用 AudioPatch 替代传统的AudioStream,进一步降低音频延时(但录屏无法捕获 TvInput 预览声音,需根据需求选择)。
核心修改:在TvInputHardwareManager.java 中屏蔽AudioStream 调用,启用AudioPatch:
// 屏蔽原 AudioStream 逻辑// mAudioStream.start(6);// mAudioStream.stop();// 启用 AudioPatchif (mAudioPatch != null) {mAudioManager.releaseAudioPatch(mAudioPatch);}// 创建 AudioPatch 连接 HDMI IN 音频源mAudioManager.createAudioPatch(audioPatchArray,new AudioPortConfig[] { sourceConfig },new AudioPortConfig[] { sinkConfig });
RK Android 平台的音频调试,核心是 “先确认基础通路(声卡 + 驱动),再定位上层逻辑(优先级、状态同步)”。MultiAudio 方案不仅解决了原生框架的局限,其配套的调试方法也能覆盖大部分场景。
希望这篇指南能帮你少踩坑,高效搞定音频调试!如果觉得有用,欢迎点赞、转发,也欢迎在评论区分享你的调试经验~
全部0条评论
快来发表一下你的评论吧 !