欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > Android audio(10)-AudioFlinger的track数据管理

Android audio(10)-AudioFlinger的track数据管理

2025/4/1 6:59:16 来源:https://blog.csdn.net/weixin_52370850/article/details/143586831  浏览:    关键词:Android audio(10)-AudioFlinger的track数据管理

从前面 AudioTrack、PlaybackThread、ouputstream三者的关系中,我们看到 AudioTrack 把音频流数据送入到对应的 PlaybackThread 中,那么应用进程是如何控制音频流的开始播放 start()、停止播放 stop()、暂停播放 pause()。

一、音频流管理

应用进程与 AudioFlinger 并不在一个进程上,这就需要 AudioFlinger 提供一套接口可以让应用程序跨进程调用来实现track管理功能。
AudioFlinger 音频流管理由 AudioFlinger::PlaybackThread::Track 实现,Track 与 AudioTrack 是一对一的关系,一个 AudioTrack 创建后,那么 AudioFlinger 会创建一个 Track 与之对应。PlaybackThread 与 AudioTrack/Track 是一对多的关系,一个 PlaybackThread 可以挂着多个 Track。
具体来说,AudioTrack 创建时,AudioPolicyManager 根据 AudioTrack 的输出标识和流类型,找到对应的output和 PlaybackThread(如果没有找到的话,则系统会打开对应的output并新建一个 PlaybackThread),然后让PlaybackThread创建一个 Track 。

PlaybackThread 有两个私有成员与此相关:
mTracks:该 PlaybackThread 创建的所有 Track 均添加保存到这个向量中。
mActiveTracks:只有需要播放(设置了 ACTIVE 状态)的 Track 会添加到这个集合中。PlaybackThread 会从该集合上找到所有设置了 ACTIVE 状态的 Track,把这些 Track 数据混音后写到输出流设备。

音频流控制最常用的三个接口:
AudioFlinger::PlaybackThread::Track::start:开始播放:把该 Track 置 ACTIVE 状态,然后添加到 mActiveTracks 向量中,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 通知 PlaybackThread 。

AudioFlinger::PlaybackThread::Track::stop:停止播放:把该 Track 置 STOPPED 状态,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 通知 PlaybackThread 。

AudioFlinger::PlaybackThread::Track::pause:暂停播放:把该 Track 置 PAUSING 状态,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 通知 PlaybackThread 。

AudioFlinger::PlaybackThread::threadLoop() 在循环中,会调用 prepareTracks_l() 处理音频track和混音器:ACTIVE 状态的 Track 会添加到 mActiveTracks,此外的 Track 会从 mActiveTracks 上移除出来,然后根据track的属性配置 AudioMixer。audiomixer里面针对每一路audiotrack都有一路track对应。
这三个音频流控制接口是非常简单的,主要是设置一下 Track 的状态,然后发个事件通知 PlaybackThread 就行,复杂的处理都在 AudioFlinger::PlaybackThread::threadLoop() 中了。

二、混音器处理

现在我们开始分析 AudioTrack 的创建过程,特别注意 AudioTrack 与 AudioFlinger 如何建立联系、用于 AudioTrack 与 AudioFlinger 交换数据的匿名共享内存如何分配。

1、相关类介绍

AudioFlinger::PlaybackThread
源码位置:/frameworks/av/services/audioflinger/Threads.h

class PlaybackThread : public ThreadBase, public StreamOutHalInterfaceCallback,public VolumeInterface, public StreamOutHalInterfaceEventCallback {……
}

回放线程基类,不同输出flag的音频流对应不同类型的 PlaybackThread 实例(分为四种:MixerThread、DirectOutputThread、DuplicatingThread、OffloadThread),所有的 PlaybackThread 实例都会添加到 AudioFlinger.mPlaybackThreads 向量中。

源码位置:/frameworks/av/services/audioflinger/AudioFlinger.h

DefaultKeyedVector<audio_io_handle_t, sp<PlaybackThread>>  mPlaybackThreads;

可见 audio_io_handle_t 是与 PlaybackThread 是一一对应的,由已知的 audio_io_handle_t 就能找到对应的 PlaybackThread,audio_io_handle_t 在创建 PlaybackThread 时由系统分配(openoutput),这个值是全局唯一的。

AudioFlinger::PlaybackThread::Track
源码位置:/frameworks/av/services/audioflinger/Tracks.cpp

AudioFlinger::PlaybackThread::Track::Track(……) : TrackBase(……
{
}

音频track类,会创建一块匿名共享内存用于 AudioTrack 与 AudioFlinger 之间的数据交换(方便起见,这块匿名共享内存,以后均简单称为 FIFO),同时实现 start()、stop()、pause() 等音频流常用控制手段。注意,多个 Track 对象可能都注册到同一个 PlaybackThread 中(尤其对于 MixerThread 而言,一个 MixerThread 往往挂着多个 Track 对象),这多个 Track 对象都会添加到 PlaybackThread.mTracks 向量中统一管理。

AudioFlinger::TrackHandle
源码位置:/frameworks/av/services/audioflinger/AudioFlinger.h

// 客户端IAudioTrack的服务器端
class TrackHandle : public android::BnAudioTrack {……
}

Track 对象只负责音频流管理业务,对外并没有提供跨进程的 Binder 调用接口,而应用进程又需要对音频流进行控制,所以需要一个对象来代理 Track 的跨进程通讯,这个角色就是 TrackHandle,AudioTrack 通过它与 Track 交互。这里利用到代理模式。track负责具体的业务实现。trackhandle负责接受应用客户端的调用,并调用track来实现具体的业务。

AudioTrack
源码位置:/frameworks/av/media/libaudioclient/AudioTrack.cpp

Android 音频系统对外提供的一个 API 类,负责音频流数据输出。每个音频流对应着一个 AudioTrack 实例,不同输出标识的 AudioTrack 会匹配到不同的 AudioFlinger::PlaybackThread。AudioTrack 与 AudioFlinger::PlaybackThread 之间通过 FIFO 来交换音频数据,AudioTrack 是 FIFO 生产者,AudioFlinger::PlaybackThread 是 FIFO 消费者。

AudioTrack::AudioTrackThread
源码位置:/frameworks/av/media/libaudioclient/AudioTrack.cpp

AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver): Thread(true /* bCanCallJava */)  // binder recursion on restoreTrack_l() may call Java., mReceiver(receiver), mPaused(true), mPausedInt(false), mPausedNs(0LL),mIgnoreNextPausedInt(false)
{
}

数据传输模式为 TRANSFER_CALLBACK 时,需要创建该线程,它通过调用 audioCallback 回调函数主动从用户进程处索取数据并填充到 FIFO 上。数据传输模式为 TRANSFER_SYNC 时,则不需要创建这个线程,因为用户进程会持续调用 AudioTrack.write() 填充数据到 FIFO。数据传输模式为 TRANSFER_SHARED 时,也不需要创建这个线程,因为用户进程会创建一块匿名共享内存,并把要播放的音频数据一次性拷贝到这块匿名共享内存上了。

IAudioTrack
源码位置:/frameworks/av/media/libaudioclient/IAudioTrack.cpp

IAudioTrack 是链结 AudioTrack 与 AudioFlinger 的桥梁。它在 AudioTrack 端的对象是 BpAudioTrack,在 AudioFlinger 端的对象是 BnAudioTrack,从图中不难看出,AudioFlinger::TrackHandle 继承自 BnAudioTrack,而 AudioFlinger::TrackHandle 恰恰是AudioFlinger::PlaybackThread::Track 的代理对象,所以 AudioTrack 得到 IAudioTrack 实例后,就可以调用 IAudioTrack 的接口与 AudioFlinger::PlaybackThread::Track 交互。

2、audio_io_handle_t

audio_io_handle_t 是 AudioTrack/AudioRecord/AudioSystem、AudioFlinger、AudioPolicyManager 之间一个重要的连接点。
在前面 AudioFlinger::openOutput_l() 注释中大致说明了它的来历及其作用:

当打开输出流设备及创建 PlaybackThread 时,系统会分配一个全局唯一的值作为 audio_io_handle_t,并把 audio_io_handle_t 和 PlaybackThread 添加到键值对向量 mPlaybackThreads 中,由于 audio_io_handle_t 和 PlaybackThread 是一一对应的关系,因此拿到一个 audio_io_handle_t,就能遍历键值对向量 mPlaybackThreads 找到它对应的 PlaybackThread,可以简单理解 audio_io_handle_t 为 PlaybackThread 的索引号或线程 id。由于 audio_io_handle_t 具有 PlaybackThread 索引特性,所以应用进程想获取 PlaybackThread 某些信息的话,只需要传入对应的 audio_io_handle_t 即可。
例如 AudioFlinger::format(audio_io_handle_t output),这是 AudioFlinger 的一个服务接口,用户进程可以通过该接口获取某个 PlaybackThread 配置的音频格式。

AudioFlinger::format

audio_format_t AudioFlinger::format(audio_io_handle_t output) const
{Mutex::Autolock _l(mLock);// 根据传入的audio_io_handle_t从键值对mPlaybackThreads中找到他对应的PlaybackThreadsPlaybackThread *thread = checkPlaybackThread_l(output);if (thread == NULL) {ALOGW("format() unknown thread %d", output);return AUDIO_FORMAT_INVALID;}return thread->format();
}AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
{return mPlaybackThreads.valueFor(output).get();
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com