ARTICLE · 1044580
安卓驱动[8]AN14音频链路与音视频同步链路
安卓驱动[8]AN14音频链路与音视频同步链路
音频链路 (Audio Pipeline) — 从上到下
1. 应用层 (Java)
Java: MediaPlayer / AudioTrack / AudioRecord2. 框架层 (frameworks/av)
- AudioTrack (音频输出客户端): frameworks/av/media/libaudioclient/AudioTrack.cpp- AudioTrack::AudioTrack (line 243/260) 构造- AudioTrack::getPosition (line 1676) 播放位置- AudioTrack::getTimestamp (line 3169, 3211) 时间戳(A/V同步用)- AudioTrack::getTimestamp_l (line 3178, 3217) 时间戳实现- AudioTrack::write (line 243,260) 构造- AudioFlinger (音频服务): frameworks/av/services/audioflinger/Threads.cpp- AudioFlinger::ThreadBase::ThreadBase (line 549) 线程构造- AudioFlinger::MixerThread::threadLoop_write (line 5272) 写数据到 HAL- AudioFlinger::MixerThread::threadLoop_mix (line 5399) 混音- AudioFlinger::PlaybackThread::getTimestamp_l (line 4764) 线程时间戳- threadloop_getHalTimestamp_l (line 4604) 取 HAL 时间戳
3. HAL层 (hardware/rockchip/audio/primary_hal/audio_hw.c)
- out_write (line 2916) 播放写数据- in_read (line 3653) 录音读数据- out_get_presentation_position (line 3164) 呈现时间戳(A/V同步关键)- out_get_latency (line 2519) 时延- start_output_stream (line 1330) 打开播放 PCM- start_input_stream (line 1753) 打开录音 PCM- adev_open_output_stream (line 4081) 打开播放流- adev_open_input_stream (line 4710) 打开录音流- adev_set_parameters (line 4375) 参数(路由)- ALSA 混音器: alsa_mixer.c, alsa_route.c
4. 内核层 (kernel-6.1/sound/soc)
- I2S 控制器: sound/soc/rockchip/rockchip_i2s_tdm.c- rockchip_i2s_tdm_hw_params (line 2008) 配置采样率/位宽/通道- rockchip_i2s_tdm_trigger (line 2095) 启动/停止传输- rockchip_i2s_tdm_startup (line 2500) 打开 substream- rockchip_i2s_tdm_dai_probe DAI 注册- codec:自定义- HDMI: sound/soc/rockchip/rockchip_hdmi.c- ALSA core: sound/core/pcm_native.c (snd_pcm_*)- sound/core/pcm_native.c — snd_pcm_pointer / snd_pcm_status(时间戳源头)- sound/soc/soc-pcm.c — DAI 启停
二、音视频同步链路 (A/V sync)
核心原理:以音频时钟为主时钟,视频按音频时钟调度。
1. 音频时间戳的产生(芯片 → 框架)
内核 snd_pcm_pointer (sound/core/pcm_native.c)↓HAL out_get_presentation_position (audio_hw.c:3164)↓AudioFlinger threadloop_getHalTimestamp_l (Threads.cpp:4604)↓AudioTrack::getTimestamp_l(AudioTrack.cpp:3217)
1. AudioFlinger 提供时间戳 (HAL → 内核 → 回传)
- HAL: out_get_presentation_position (audio_hw.c:3164)- 内核 ALSA: snd_pcm_pointer / snd_pcm_status- AudioFlinger: threadloop_getHalTimestamp_l (Threads.cpp)- AudioTrack: getTimestamp_l (AudioTrack.cpp)
2. MediaClock (A/V同步的时间基准)
frameworks/av/media/libstagefright/MediaClock.cpp- MediaClock::updateAnchor (line 89) 更新锚点(音视频对齐)- MediaClock::getMediaTime (line 160) 取当前媒体时间- MediaClock::getRealTimeFor (line 191) 媒体时间→真实时间- MediaClock::setPlaybackRate (line 133) 倍速- MediaClock::getMediaTime_l (line 170) 内部实现
3. NuPlayerRenderer (A/V同步的核心逻辑)
frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp- onDrainAudioQueue (line 1063) — 音频出队(推进 clock)- onQueueBuffer / queueBuffer (line 1537) -入队- onDrainAudioQueue (the A/V sync decision)(line 1063) 音频出队(推进主时钟)- onDrainVideoQueue (line 1397) — 视频出队(按 clock 调度)- getCurrentPosition (line 384) 当前位置- setAudioFirstAnchorTimeIfNeeded_l (line 422) 设置音频锚点- clearAnchorTime (line 430) 清锚点
4. Video 渲染按 MediaClock 调度
- MediaCodec (frameworks/av/media/libstagefright/MediaCodec.cpp)- ACodec (frameworks/av/media/libstagefright/ACodec.cpp)
三、调试入口对照表
现象 | 先看哪一层 | 具体位置 |
没声音/爆音 | HAL + 内核 | audio_hw.c:out_write → rockchip_i2s_tdm.c:trigger |
音视频不同步 | 框架 | NuPlayerRenderer.cpp:onDrainAudioQueue/onDrainVideoQueue |
时间戳不对 | HAL | audio_hw.c:out_get_presentation_position |
路由错(扬声器/耳机) | HAL | alsa_route.c / adev_set_parameters |
I2S 没时钟 | 内核 | rockchip_i2s_tdm.c:hw_params |
codec 没声音 | 内核 | sound/soc/codecs/es8323.c |
视频花屏/解码失败 | MPP | drivers/video/rockchip/mpp/mpp_rkvdec2.c |
抓日志的正确定位顺序
1.应用层logcat -s NuPlayer NuPlayerRenderer MediaClock AudioTrack2.框架/HALlogcat -s audio_hw_primary AudioFlinger3.内核dmesg | grep -E 'i2s|es8323|codec|dai|mpp|rkvdec'