在Android开发中,音频模块的调试往往是“老大难”——多声卡无法区分、多设备同时输出没声音、HDMI录音崩溃…这些问题不仅影响用户体验,还会消耗大量开发时间。
瑞芯微(Rockchip)针对RK平台推出的MultiAudio方案,专门解决Android原生音频框架的局限,同时提供了一套清晰的调试方法论。今天这篇文章,就从实战角度出发,带大家搞定RK Android平台的音频调试,覆盖基础操作、常见Bug解决、多设备控制等核心场景。
一、先搞懂:Android原生音频的“痛点”与MultiAudio解决方案
在聊调试前,我们得先明白“为什么需要调试”——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独立录音);
•多设备音量同步调节与保存。
二、基础调试:先搞定“声卡识别”与“驱动验证”
调试的第一步,是确认“硬件通路是否正常”——也就是声卡是否被正确识别、驱动是否能工作。这两步操作简单,但却是排查问题的基础。
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配置是否正确。
2.测试驱动:验证声卡能否正常出声
确认声卡识别后,用tinyplay工具测试驱动是否正常(需系统集成tinyalsa工具集)。
以测试HDMI 0声卡(对应上述输出中的索引“2”)为例:
1.准备一个WAV格式的音频文件(如test.wav),推到设备的/data目录;
2.执行命令播放:
tinyplay/data/test.wav -D2-d0
•-D:指定声卡索引(这里“2”对应rockchiphdmi0);
•-d:指定设备编号(通常为0)。
如果能正常听到声音,说明HDMI 0声卡驱动没问题;若没声音,优先排查驱动配置(如DTS中声卡节点是否启用)。
三、实战:5大常见音频Bug及解决方案
调试中最头疼的是“明明配置了,却出问题”——我们整理了RK平台最常遇到的5类音频Bug,附详细解决步骤。
1.问题1:更新代码后,HDMI突然没声音(之前正常)
•现象:代码同步到最新后,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"};
2.问题2:UBoot开Logo后,HDMI没声音(插拔线后恢复)
•现象:开启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);
3.问题3:SPDIF与其他声卡同时出声,声音断断续续
•现象: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";// 其他配置...};
4.问题4:HDMI IN录音时,偶尔出现Audio服务崩溃
•现象:用HDMI IN录制外部音频(如机顶盒信号)时,Audio服务突然崩溃,日志提示“TimeCheck超时”。
•原因:HDMI IN录音的等待时间过短,导致超时触发服务重启。
•解决:修改I2S-TDM驱动,延长录音等待时间:
在sound/soc/rockchip/rockchip_i2s_tdm.c中添加超时配置:
staticintrockchip_i2s_tdm_trigger(structsnd_pcm_substream *substream,intcmd){// 其他逻辑...break;+ // 针对录音流,延长等待时间到 500ms+ if(substream->stream == SNDRV_PCM_STREAM_CAPTURE) {+ substream->wait_time = msecs_to_jiffies(500);+ }returnret;}
5.问题5:更新代码后,声卡完全没声音(优先级冲突)
•现象:同步代码后,所有声卡都没声音,查看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.javaelseif(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;}elseif(headset==BIT_HDMI_AUDIO_1) {Slog.d(TAG,"hdmi_1 plug");outDevice=AudioManager.DEVICE_OUT_HDMI_1;
四、实用技巧:多设备输出与HDMI IN录音的调用方法
除了调试,掌握MultiAudio的核心调用方式,能让开发更高效。
1.多设备输出:指定声卡的2种方式
(1)JAVA层通过AudioSessionId控制
在MediaPlayer初始化时,通过setAudioSessionId指定声卡,必须在setDataSource前调用:
MediaPlayermp=newMediaPlayer();// 指定声卡:PRIMARY(57)=扬声器,HDMI0(65),DP0(73),HDMI1(81),DP1(89)mp.setAudioSessionId(65);// 声音从 HDMI0 输出mp.setDataSource("/data/test.mp4");mp.prepare();mp.start();
(2)按APP包名绑定声卡
适合需要固定APP输出设备的场景(如会议软件固定走HDMI),修改AudioTrack.cpp:
String8 appPackage =String8(mPackageName);// RockVideoPlayer 走 HDMI0,gallery3d 走 HDMI1,MX Player 走扬声器if(strstr(appPackage.string(),"RockVideoPlayer")) {sessionid = (audio_session_t)65;}elseif(strstr(appPackage.string(),"gallery3d")) {sessionid = (audio_session_t)81;}elseif(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同时播放时暂停。
2. HDMI IN录音:调用HDMIIN音频源
通过AudioRecord初始化时指定MediaRecorder.AudioSource.HDMIIN,即可录制HDMI外部输入的声音:
// 参数:音频源(HDMIIN)、采样率、声道配置、格式、缓冲区大小AudioRecordaudioRecord=newAudioRecord(MediaRecorder.AudioSource.HDMIIN,44100,// 采样率 44.1kHzAudioFormat.CHANNEL_IN_STEREO,// 立体声AudioFormat.ENCODING_PCM_16BIT,// 16bit PCM4096// 缓冲区大小);// 开始录音audioRecord.startRecording();
五、扩展:用AudioPatch优化TvInput音频延时
如果开发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,newAudioPortConfig[] { sourceConfig },newAudioPortConfig[] { sinkConfig });
六、总结与支持
RK Android平台的音频调试,核心是“先确认基础通路(声卡+驱动),再定位上层逻辑(优先级、状态同步)”。MultiAudio方案不仅解决了原生框架的局限,其配套的调试方法也能覆盖大部分场景。
希望这篇指南能帮你少踩坑,高效搞定音频调试!如果觉得有用,欢迎点赞、转发,也欢迎在评论区分享你的调试经验~




