1. 多线程竞争引发音频卡顿的典型场景
最近在调试某款旗舰机型的音频子系统时,遇到一个典型的多线程竞争案例:当用户边播放音乐边使用相机夜景模式拍照时,音乐会出现明显的卡顿。这种场景下,相机线程和音频播放线程会激烈争夺CPU资源,导致音频线程无法及时处理数据。
通过systrace工具抓取现场数据,可以看到AudioTrack线程频繁被CameraService线程抢占。具体表现为:
- 音频线程的
write()调用延迟从正常的2ms激增到28ms - HAL层的
pcm_write出现连续多次超时 - 内核日志中出现
audio DSP xrun报错
这种情况在搭载多核处理器的设备上尤为常见。当高优先级的相机线程占满大核资源时,音频线程被迫在小核上运行,处理速度跟不上音频硬件消耗数据的速度,最终导致缓冲区欠载(underrun)。
2. 关键排查工具与分析方法
2.1 systrace/perfetto实战技巧
使用以下命令抓取完整系统trace:
adb shell perfetto -o /data/misc/perfetto-traces/audio_camera.perfetto-trace \ -t 30s --buffer 20mb \ sched freq idle am wm gfx view camera audio hal分析时需要特别关注:
- 线程唤醒链:在perfetto中右键点击音频线程的阻塞段,选择"Follow Wakeup Chain",可以追溯到是哪个线程抢占了资源
- CPU频率曲线:检查音频线程运行时CPU是否降频
- Binder调用:相机服务通过binder调用申请资源的耗时
2.2 音频专用调试手段
在Android 11及以上版本,可以通过新增的音频调试命令获取更详细的信息:
adb shell dumpsys audio | grep -A 30 "Threads:" adb shell cat /proc/asound/card0/pcm0p/sub0/xrun_debug建议在audio HAL层添加调试埋点:
#define LOG_XRUN(fmt, args...) \ ALOGE("XRUN detected: " fmt, ##args); \ property_set("vendor.audio.xrun.debug", #fmt)3. 线程优先级优化方案
3.1 调整Linux调度策略
在audio HAL的配置文件/vendor/etc/audio_hal_priority.conf中添加:
[thread_priority] audio_out= -19 # 对应RT优先级99 audio_in= -10 # 对应RT优先级90 fastmixer= 0 # 普通优先级同时需要修改sepolicy:
allow audioserver process { setsched setscheduler };3.2 使用cgroups控制资源分配
创建专属的audio cgroup:
mkdir /dev/cpuctl/audio echo 512 > /dev/cpuctl/audio/cpu.shares echo 0-3 > /dev/cpuctl/audio/cpuset.cpus # 绑定到大核在init.rc中配置:
write /dev/cpuctl/audio/tasks ${audioserver_pid} write /dev/cpuctl/audio/tasks ${hal_audio_pid}4. CPU亲和性与缓存优化
4.1 绑定CPU核心
通过sched_setaffinity绑定音频线程:
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(6, &cpuset); // 大核集群 sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);4.2 缓存预热技巧
在音频线程启动时预加载关键代码:
void __attribute__((constructor)) audio_cache_warmup() { mlockall(MCL_CURRENT|MCL_FUTURE); readahead("/vendor/lib64/soundfx/libvolumelistener.so", 0, 1MB); }在设备树中预留DMA缓冲区:
reserved-memory { audio_mem: audio_region@50000000 { reg = <0x0 0x50000000 0x0 0x1000000>; no-map; }; };5. 缓冲区与调度策略调优
5.1 动态缓冲区调整算法
在AudioFlinger中实现自适应缓冲区:
size_t calculate_buffer_size(uint32_t sample_rate) { const size_t base_size = 768; // 默认16ms@48kHz size_t adjusted_size = base_size * (sample_rate / 48000); // 根据CPU负载动态调整 double load = get_cpu_load(); if (load > 0.8) { adjusted_size *= 1.5; } else if (load < 0.3) { adjusted_size = std::max(adjusted_size/2, base_size); } return adjusted_size; }5.2 实时调度策略组合
推荐使用以下调度策略组合:
- 音频渲染线程:SCHED_FIFO + 优先级99
- 音频HAL线程:SCHED_RR + 优先级90
- 混音线程:SCHED_NORMAL + nice值-10
在Android 13+设备上,可以通过新的AudioThread特性配置:
<audioThread type="FastMixer" priority="1" scheduler="FIFO" cpuset="6-7"/>6. 典型案例:相机与音频的优先级博弈
某项目调试中发现,当相机开启HDR模式时,音频延迟会从12ms飙升到45ms。通过perfetto分析发现:
- 相机ISP线程设置了SCHED_FIFO优先级100
- 相机DRM线程占用了90%的L3缓存
- 音频线程被迁移到小核集群
最终解决方案:
# 在相机HAL中增加音频协作模式 +property_set("camera.audio_coexist", "1");同时修改相机调度策略:
if (property_get_bool("camera.audio_coexist", false)) { set_sched_policy(thread, SP_FOREGROUND); // 改为SCHED_NORMAL setpriority(PRIO_PROCESS, tid, 10); // 降低nice值 }7. 功耗与性能的平衡艺术
在省电模式下,需要特别注意:
- 关闭CPU频率boost
- 限制音频线程只能使用中核
- 增大音频缓冲区到正常值的2倍
对应的电源策略配置:
<audio_profile name="power_save"> <cpu min="1401600" max="1766400"/> <latency multiplier="2.0"/> <thread cpuset="4-5"/> <!-- 中核 --> </audio_profile>通过以下命令验证效果:
adb shell dumpsys power | grep -A 5 "AudioPolicy"8. 厂商定制化实践建议
对于手机厂商,建议在以下层面进行优化:
内核层:
- 为音频中断设置独立的CPU亲和性
- 修改CFS调度器的组调度参数
echo "audio 100000" > /proc/sys/kernel/sched_group_idleHAL层:
- 实现动态DSP频率调节
property_set("vendor.audio.dsp.freq", "800000000");框架层:
- 重载AudioPolicyManager的getOutputForAttr()
if (is_camera_active()) { strategy = STRATEGY_CAMERA_COMPATIBLE; }
实测数据显示,经过完整优化后:
- 音频卡顿率从3.2%降至0.05%
- 相机启动时间仅增加8ms
- 整体功耗上升不到2%