Audio的播放流程

本文详细剖析了Android系统中Audio的播放流程,从AudioTrack服务端的启动准备,包括AudioFlinger、AudioPolicyService和AudioPolicyManager的创建,到客户端如何通过AudioTrack进行音频播放。在服务端,AudioPolicyService充当阀门,AudioFlinger负责混音,PlaybackThread是音频系统的发动机。客户端构建AudioTrack后,通过start()激活播放,数据通过共享内存写入由AudioFlinger的MixerThread混合后输出到音频设备。

流程图

这是基于Android5.1分析的,前几版本好像有些不同,6.0没改太多,不过大体思想是一致的

结构图

播放就像个排水机,AuidoPolicyService是阀门,AudioFlinger是排水池,PlaybackThread是发动机,Track是源,AudioOutput是排水孔。AudioTrack是水桶

排水首先要凿个孔(openOutput),然后添加发动机(建立PlaybackThread),然后将源接到水桶上(建立Track),选择排水孔(selectOutptu),开启相应的发动机(PlaybackThread从睡眠中唤醒),然后就各自排水了。。。。

总体流程图

AudioTrack服务端的启动及准备

服务端的指的是AudioFlinger跟AudioPolicyService等音频相关的服务,这些服务会在系统开机的时候启动
在系统启动完成后,客户端(一般都是app)就能利用这些服务来使用系统提供的功能

Audio相关的服务启动

开机时系统启动各种服务,AudioFlinger跟AudioPolicyService和一些音频相关的服务会在此启动。
各服务的Instantiate()函数在BinderService.h中定义实现,主要是用于抽象出注册服务的操作。
BinderService是个模板类,服务继承该类后可以直接注册到systemserver。

//--->frameworks/av/media/mediaserver/main_mediaserver.cpp
int main(int argc __unused, char** argv)
{
    ...
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    AudioPolicyService::instantiate();
    ...
}

//--->frameworks/native/include/binder/BinderService.h
template<typename SERVICE>
class BinderService
{
public:
    static status_t publish(bool allowIsolated = false) {
        sp<IServiceManager> sm(defaultServiceManager());
        // 这里用模板生成了具体服务的对象
        // new SERVICE()将会调用服务(AudioFlinger,AudioPolicyService等)的构造函数
        return sm->addService(
                String16(SERVICE::getServiceName()),
                new SERVICE(), allowIsolated);
    }
    ...
    static void instantiate() { publish(); }
    ...

};
AudioFlinger的创建

AudioFlinger承担混音工作。(总之很重要啦)
AudioFlinger的构造函数主要是对成员变量和调试工具的初始化。
onFirstRef一般做进一步的初始化工作,AudioFlinger暂时没有在该函数中做重要的工作。

//--->frameworks/av/services/audioflinger.cpp
AudioFlinger::AudioFlinger()
    : BnAudioFlinger(),
      mPrimaryHardwareDev(NULL),
      mAudioHwDevs(NULL),
      mHardwareStatus(AUDIO_HW_IDLE),
      mMasterVolume(1.0f),
      mMasterMute(false),
      mNextUniqueId(1),
      mMode(AUDIO_MODE_INVALID),
      mBtNrecIsOff(false),
      mIsLowRamDevice(true),
      mIsDeviceTypeKnown(false),
      mGlobalEffectEnableTime(0),
      mPrimaryOutputSampleRate(0)
{
...
#ifdef TEE_SINK
    ....
#endif
...
}

void AudioFlinger::onFirstRef()
{
    Mutex::Autolock _l(mLock);
    ...
    mPatchPanel = new PatchPanel(this);
    mMode = AUDIO_MODE_NORMAL;
}
AudioPolicyService的创建

AudioPolicyService用于控制音频播放策略(比如插耳机的时候来电用什么设备去播放音乐)、管理音频设备等

AudioPolicyService的构造函数更简单,只是初始化主要成员。
AudioPolicyService会在onFristRef中做比较多的工作,比如创建command线程,初始化重要成员mAudioPolicyManager。

//--->frameworks/av/services/audiopolicy/AudioPolicyService.cpp
AudioPolicyService::AudioPolicyService()
    : BnAudioPolicyService(),
    mpAudioPolicyDev(NULL),
    mpAudioPolicy(NULL),
    mAudioPolicyManager(NULL),
    mAudioPolicyClient(NULL),
    mPhoneState(AUDIO_MODE_INVALID)
{}

void AudioPolicyService::onFirstRef()
{
    ...
    {
        Mutex::Autolock _l(mLock);
        mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
        mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
        mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);

#ifdef USE_LEGACY_AUDIO_POLICY
    ...(暂时这宏意义不明)
#else
        mAudioPolicyClient = new AudioPolicyClient(this);
        mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
#endif
    }
    ...(效果相关)
}
AudioPolicyManager的创建

AudioPolicyManager作为音频调度策略的实现,在AudioPolicyService关于音频调度的基本都是直接转发给AudioPolicyManager。
(貌似可以重载AudioPolicyManager来改动音频策略的实现,6.0开始可以直接动态选择不同的AudiPolicyManger实现)

在构造函数中,打开了所有能用的音频设备和录音设备,并调用AudioPolicyService创建了相应设备的混音线程。

//--->frameworks/av/services/audiopolicy/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
    :mPrimaryOutput((audio_io_handle_t)0),
    ...

{
    mpClientInterface = clientInterface;
    ...
    // 加载音频模块
    defaultAudioPolicyConfig();
    ...
    for (size_t i = 0; i < mHwModules.size(); i++) {
        ...
        for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
            ...
            status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,  // 打开音频设备
                &output,
                &config,
                &outputDesc->mDevice,
                String8(""),
                &outputDesc->mLatency,
                outputDesc->mFlags);
        }
    }

    ...(打开录音设备)
}

mpClientInterface就是AudioPolicyService,AudioPolicyService最终会调用AudioFlinger的openOutput函数。

//--->frameworks/av/services/audiopolicy/AudioPolicyClientImpl.cpp
status_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module,
    audio_io_handle_t *output,
    audio_config_t *config,
    audio_devices_t *devices,
    const String8& address,
    uint32_t *latencyMs,
    audio_output_flags_t flags)
{
    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
    if (af == 0) {
        ALOGW("%s: could not get AudioFlinger", __func__);
        return PERMISSION_DENIED;
    }
    return af->openOutput(module, output, config, devices, address, latencyMs, flags);
}

AudioFlinger的openOutput中会针对输出设备的类型创建了一个PlaybackThread。
PlaybackThread在AudioFlinger相当重要,相当于音频系统的发动机

PlaybackThread有几种,比较常见有MixerThread,蓝牙耳机设备需要外放(比如ring类型的流需要同时从耳机与喇叭出来)的时候使用DuplicatingThread。

//--->frameworks/av/services/audioflinger/AudioFlinger.cpp
status_t AudioFlin
### ExoPlayer 播放视频时的音频处理流程 ExoPlayer 在播放视频时,音频处理流程涉及多个关键组件和阶段,包括媒体格式解析、音频解码、音频渲染以及最终的音频输出。整个流程通过高度模块化的设计实现,确保灵活性和兼容性,同时支持多种音频编码格式和设备特性。 #### 媒体格式解析与轨道选择 ExoPlayer 首先通过 `MediaSource` 解析媒体容器格式,提取音频和视频轨道信息。在播放过程中,`TrackSelector` 被用来选择合适的音频轨道。音频轨道的选择依据包括用户偏好、语言设置以及设备支持的音频编码格式。例如,某些设备可能不支持 `audio/ac3` 或 `audio/eac3`,此时 ExoPlayer 会根据 `MediaCodecUtil` 的检测结果选择备用轨道或切换至软件解码器[^1]。 #### 音频解码 音频解码是音频处理流程中的核心环节。ExoPlayer 默认使用 `MediaCodecAudioRenderer`,该组件通过 Android 系统提供的 `MediaCodec` 接口进行硬件解码。硬件解码的优势在于性能高、功耗低,但受限于设备对特定音频编码格式的支持情况。例如,某些设备可能不支持 AC3(Dolby Digital)格式的硬件解码,此时 ExoPlayer 可能抛出 `IllegalArgumentException` 并导致音频无法播放[^1]。 为解决兼容性问题,ExoPlayer 提供了基于 FFmpeg 的 `FfmpegAudioRenderer`,通过软件方式解码不被硬件支持的音频格式。FFmpeg 集成了广泛的音频编码库,能够解码包括 AC3、DTS、FLAC 在内的多种音频格式。通过自定义 `RenderersFactory`,可以将 `FfmpegAudioRenderer` 注册为音频解码器以替代默认的 `MediaCodecAudioRenderer`: ```java public class CustomRenderersFactory extends DefaultRenderersFactory { public CustomRenderersFactory(Context context) { super(context); } @Override protected void buildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector, List<Renderer> out) { out.add(new FfmpegAudioRenderer()); } } ``` 初始化 ExoPlayer 时使用该自定义工厂: ```java ExoPlayer player = new ExoPlayer.Builder(context, new CustomRenderersFactory(context)).build(); ``` #### 音频渲染与输出 音频渲染阶段由 `AudioSink` 负责,该组件将解码后的音频数据写入音频输出设备。Android 系统提供了多种音频输出路径,包括扬声器、蓝牙耳机、HDMI 等。ExoPlayer 通过 `AudioAttributes` 和 `AudioStreamType` 控制音频流类型和播放行为。例如,对于视频通话场景,ExoPlayer 可能使用本地录音和本地播放路径,而非 VOIP 通路[^4]。 此外,音频输出的延迟和采样率等参数也会影响播放体验。某些音频系统(如 PipeWire)支持动态延迟调整,以适应不同场景的需求。虽然 PipeWire 主要用于 Linux 系统,但其设计理念反映了现代音频系统对延迟和功耗的优化趋势[^2]。 #### 音频同步与缓冲管理 ExoPlayer 通过时间戳同步机制确保音频与视频的同步播放。音频和视频数据分别通过各自的解码器处理,并在渲染阶段根据时间戳对齐。音频缓冲区的管理由 `AudioSink` 负责,确保音频数据的连续播放,避免因缓冲不足导致卡顿。 在某些情况下,音频轨道的切换或参数变化可能不会立即生效,因为管道中已存在的音频数据需要完全处理完毕后,新的音频流才能进入播放阶段[^3]。这种延迟现象在动态调整音频轨道或切换语言时尤为明显。 --- ###
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值