在分析AudioTrack的时候,第一步会new AudioTrack,并调用他的set方法。在set方法的最后调用了createTrack_l创建音轨。我们现在来分析createTrack_l的流程。

在分析createTrack_l之前,我们先来了解Android音频流的从PCM到输出的路线。首先,我们的PCM音频数据一般会在用户端,而混音会在AudioFlinger端,因此需要把PCM数据传送给AudioFlinger,因此需要开辟出一块内存用于数据传送;数据到了AudioFlinger之后,可以给PCM数据调节音量,增加音效等(即混音),因此还需要一块内存用于音效处理,这块buffer在getOutput内已经开辟;混音完成后即可把PCM数据输出给音频设备进行播放。

creatTrack_l的任务主要是创建音轨,即开辟出数据传送的内存。具体实现是创建出一块share buffer,这块buffer既可以被AudioTrack写入,又可以被AudioFlinger读取进行混音。

createTrack总体可以分为三个步骤:

  1. 从AudioFlinger获取创建sharebuffer所需的参数,如latency,framecount,sampleRate;然后与传入的参数(framecount,sampleRate)做对比,目的是计算出正确的framecount
  2. 从AudioFlinger创建buffer,并创建对sharebuffer进行控制的对象AudioTrackServerProxy
  3. 创建可以对sharebuffer进行控制的对象AudioTrackClientProxy

1. 获取正确framecount

AudioTrack按照如下方式获取framecount

status_t AudioTrack::createTrack_l(

    status = AudioSystem::getLatency(output, streamType, &afLatency);

    status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);

    status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate);

    if (!audio_is_linear_pcm(format)) {
if (sharedBuffer != 0) {
// Same comment as below about ignoring frameCount parameter for set()
frameCount = sharedBuffer->size();
} else if (frameCount == 0) {
frameCount = afFrameCount;
}
if (mNotificationFramesAct != frameCount) {
mNotificationFramesAct = frameCount;
}
} else if (sharedBuffer != 0) {
// user share buffer,we donot neet to allocate
// Ensure that buffer alignment matches channel count
// 8-bit data in shared memory is not currently supported by AudioFlinger
size_t alignment = /* format == AUDIO_FORMAT_PCM_8_BIT ? 1 : */ 2;
if (mChannelCount > 1) {
alignment <<= 1;
}
if (((size_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
return BAD_VALUE;
}
frameCount = sharedBuffer->size()/mChannelCount/sizeof(int16_t); } else if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
// non-fast
uint32_t minBufCount = 2; if (minBufCount <= nBuffering) {
minBufCount = nBuffering;
} // calculate buffer size by param from AudioFlinger
size_t minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; if (frameCount == 0) {
frameCount = minFrameCount;
} else if (frameCount < minFrameCount) {
frameCount = minFrameCount;
}
} else {
// For fast tracks, the frame count calculations and checks are done by server
}

先看一下AudioTrack计算framecount时的式子:

minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate;

afFrameCount与afSampleRate都是从AudioFlinger得到的两个参数。

  1. afFrameCount代表MixerBuffer的大小,单位为Frame。Frame的定义为PCM音频数据的一个“采样 * 音轨个数”。
  2. afSampleRate代表MixerBuffer的默认采样率,即一秒内包含的Frame数目。

因此有如下公式:

$BufferSeconds = \frac{afFrameCount}{afSampleRate} = \frac{frameCount}{sampleRate}$

计算出buffer中包含多少秒音频数据。

下面是一个buffer实例,虽然sample rate一般都会是44100,但是为了方便画图,下面以5代替

AudioFlinger获取AfFrameCount的过程如下:

//AudioFlinger.cpp
size_t AudioFlinger::frameCount(audio_io_handle_t output) const
{
return thread->frameCount();
} //Thread.h
virtual size_t frameCount() const { return mNormalFrameCount; } //Thread.cpp
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
mNormalFrameCount = multiplier * mFrameCount;
} //Audio_hw.c
#define SHORT_PERIOD_SIZE 512 static size_t out_get_buffer_size_low_latency(const struct audio_stream *stream)
{
struct tuna_stream_out *out = (struct tuna_stream_out *)stream; /* take resampling into account and return the closest majoring
multiple of 16 frames, as audioflinger expects audio buffers to
be a multiple of 16 frames. Note: we use the default rate here
from pcm_config_tones.rate. */
size_t size = (SHORT_PERIOD_SIZE * DEFAULT_OUT_SAMPLING_RATE) / pcm_config_tones.rate;
size = ((size + 15) / 16) * 16;
return size * audio_stream_frame_size((struct audio_stream *)stream);
}

获取与AfSampleRate的过程如下:

//AudioFlinger.cpp
uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const
{
return thread->sampleRate();
} //Thread.h
uint32_t sampleRate() const { return mSampleRate; } //Thread.cpp where sample rate be initialized
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
} //Audio_hw.c
#define DEFAULT_OUT_SAMPLING_RATE 44100 // 48000 is possible but interacts poorly with HDMI static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
return DEFAULT_OUT_SAMPLING_RATE;
}

而minFrameCount则包含了minBufferCount,即share buffer有多少个Mixer Buffer的大小

    // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
// n = 1 fast track; nBuffering is ignored
// n = 2 normal track, no sample rate conversion
// n = 3 normal track, with sample rate conversion
// (pessimistic; some non-1:1 conversion ratios don't actually need triple-buffering)
// n > 3 very high latency or very small notification interval; nBuffering is ignored
  1. 如果在调用set方法的的时候,指定了flag为fast track,则表明希望Audio Buffer内的数据被尽快处理,因此Buffer会被创建得比较小,期采用单buffer
  2. 一般情况下,即输入PCM音频数据的采样率与输出音频数据的采样率一样的话,则不用进行采样率转换,采用双buffer
  3. 在需要采样率转换的情况,则采用三buffer
  4. 在碰到高延迟的情况,(如硬件不能及时输出PCM音频),则需要更大的buffer对数据进行缓存

2. AudioFlinger创建share buffer

AudioTrack是通过调用AudioFlinger的createTrack的方法来实现创建share buffer。createTrack的步骤如下:

  1. 获取输出线程PlaybackThread
  2. 调用获取到的PlaybackThread的createTrack_l函数来创建Track对象,在Track对象内部会创建share buffer
  3. 创建Track的binder对象TrackHandle,Track由于需要通过binder返回给AudioTrack,因此是个binder对象,该对象会包含share buffer的信息
sp<IAudioTrack> AudioFlinger::createTrack(...)
{
PlaybackThread *thread = checkPlaybackThread_l(output); track = thread->createTrack_l(client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus); trackHandle = new TrackHandle(track);
return trackHandle;
}

①. 获取输出线程PlaybackThread

还记得getOutput时所创建的PlaybackThread吗?PlaybackThread会在创建MixerThread时一同被创建。在getOutput内,我们把该thread放进了mPlaybackThreads进行维护。现在我们有需要把它取出来。

AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
{
return mPlaybackThreads.valueFor(output).get();
}

②. 调用PlaybackThread的createTrack_l

在createTrack_l内调用了new Track来实现创建share buffer

sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(...)
{
track = new Track(this, client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
}

Track的父类是TrackBase,因此会先构建TrackBase对象

// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::ThreadBase::TrackBase::TrackBase(...)
{
// buffer header
size_t size = sizeof(audio_track_cblk_t); // buffer content size
size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
if (sharedBuffer == 0) {
size += bufferSize;
} if (client != 0) {
//allocate share buffer
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory != 0) {
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
// can't assume mCblk != NULL
} else {
ALOGE("not enough memory for AudioTrack size=%u", size);
client->heap()->dump("AudioTrack");
return;
}
} else {
// this syntax avoids calling the audio_track_cblk_t constructor twice
mCblk = (audio_track_cblk_t *) new uint8_t[size];
// assume mCblk != NULL
} // construct the shared structure in-place.
if (mCblk != NULL) {
// this is header above buffer content
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
} else {
mBuffer = sharedBuffer->pointer();
} }
}

其中,创建出来的buffer需要包含存放Audio PCM data的share buffer,还需要包含audio_track_cblk_t这个buffer头。调用heap->allocate这个函数来创建share buffer,buffer头部调用new(mCblk) audio_track_cblk_t;这种定位new的方式来创建。buffer的结构如下:

new Track在构造函数体内,会创建AudioTrackServerProxy,这个对象会被用作AudioFlinger这边的buffer操作,由于share buffer是跨线程,甚至是跨进程的,而Proxy可以保证buffer访问的线程安全。

AudioFlinger::PlaybackThread::Track::Track(
{
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize);
mServerProxy = mAudioTrackServerProxy;
}

③. 创建TrackHandle

由于share buffer不止会在AudioFlinger这端被读取,还会在AudioTrack这端被写入,因此创建出来的Track需要被传送回AudioTrack。而在binder间传送对象只有binder对象,因此需要构建binder对象TrackHandle,返回给AudioTrack。

sp<IAudioTrack> AudioFlinger::createTrack(...)
{
trackHandle = new TrackHandle(track);
} // TrackHandle is a BnBinder object
class TrackHandle : public android::BnAudioTrack {
...
}

至此,createTrack_l在AudioFlinger这端的工作基本完成了。

3. 创建ClientProxy

有ServerProxy,相应地也会有ClientProxy,AudioTrackClientProxy就是在AudioTrack端可以对Track(share buffer)进行操作的类。

从AudioFlinger的createTrack返回TrackHandle后,就能通过TrackHandle的相关函数获得Track的信息,如buffer的起始地址等。用这些信息构造AudioTrackClientProxy.

status_t AudioTrack::createTrack_l(...)
{
sp<IAudioTrack> track = audioFlinger->createTrack(...);
sp<IMemory> iMem = track->getCblk();
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer()); mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
}

4. 总结

最后,总结一下各个对象间的关系。

AudioFlinger:

  • 首先会在AudioFlinger端创建Track,Track内包含buffer的创建及buffer指针的维护
  • Track内部有一个AudioTrackServerProxy的成员对象,用于进行buffer的相关操作
  • TrackHandle是Track对象的Binder实例,用于通过Binder返回给AudioTrack

AudioTrack:

  • IAudioTrack是TrackHandle在AudioTrack端相对应的类,该类用于提供buffer的相关信息给AudioTrackClientProxy
  • AudioTrackClientProxy获得buffer的信息后,即可以对buffer进行相关操作

createTrack_l的总体流程如下:

[Android] createTrack_l的更多相关文章

  1. [Android] 混音器AudioMixer

    AudioMixer是Android的混音器,通过混音器可以把各个音轨的音频数据混合在一起,然后输出到音频设备. 创建AudioMixer AudioMixer在MixerThread的构造函数内创建 ...

  2. [Android] AudioTrack::start

    AudioTrack的start方法用于实现Android的音频输出,start究竟做了什么?回顾一下上一小节createTrack_l的最后部分,通过binder返回了一个Track的句柄,并以被保 ...

  3. [Android] AudioTrack实例

    AudioTrack在Android系统中是用于PCM数据的混音.播放,并不涉及到音频的解码.因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播 ...

  4. 深入剖析Android音频之AudioTrack

    播放声音能够用MediaPlayer和AudioTrack,两者都提供了java API供应用开发人员使用.尽管都能够播放声音.但两者还是有非常大的差别的.当中最大的差别是MediaPlayer能够播 ...

  5. Android 音频播放分析笔记

    AudioTrack是Android中比较偏底层的用来播放音频的接口,它主要被用来播放PCM音频数据,和MediaPlayer不同,它不涉及到文件解析和解码等复杂的流程,比较适合通过它来分析Andro ...

  6. [深入理解Android卷一全文-第七章]深入理解Audio系统

    由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  8. 配置android sdk 环境

    1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/

  9. Android SwipeRefreshLayout 下拉刷新——Hi_博客 Android App 开发笔记

    以前写下拉刷新 感觉好费劲,要判断ListView是否滚到顶部,还要加载头布局,还要控制 头布局的状态,等等一大堆.感觉麻烦死了.今天学习了SwipeRefreshLayout 的用法,来分享一下,有 ...

随机推荐

  1. 如何把textfield或者textview中长按出现的(全选,复制,粘贴)显示成中文

    ios在配置文件info.plist 中添加Localiztion native development region (本地化设置) 设置为 china

  2. 系统自带.net版本

    首先我们可以参照下面的图来得到各Windows系统包括server版的自带.NET Framework的信息,下图只列出了.NET Framework 2.0及其之后的版本. Which Versio ...

  3. Android MVC框架模式

    MCV  model view controller  模型-视图-控制写 M层:适合做一些业务逻辑处理,比如数据库存取操作,网络操作,复杂的算法,耗时的任务等都在model层处理. V层:应用层中处 ...

  4. lvchange的available參数

    available參数在man info help中均无此參数,事实上參数为:activate 写此此.值得用的人注意. available 參数实为:  -a, --activate [a|e|l] ...

  5. debian7 安装配置

    最近几天折腾了一下Debian 7 (gnome桌面DVD版,KDE桌面CD版最后会提到),总的来说收获还是挺大的,对比以前使用ubuntu,debian 7给我的感觉像是一个新生婴儿,不带多余的花俏 ...

  6. C++之智能指针

    导读 一直对智能指针有一种神秘的赶脚,虽然平时没怎么用上智能指针,也就看过STL中的其中一种智能指针auto_ptr,但是一直好奇智能指针的设计因此,今天看了一下<C++ Primer Plus ...

  7. uva 11324 The Largest Clique (Tarjan+记忆化)

    /*每个环 要么不选 要么全选 可缩点 就得到一个GAD图 然后搞搞算出最大路径*/ #include<iostream> #include<cstdio> #include& ...

  8. angularjs项目中关于服务的应用

    /** *普通ajax请求公共服务 */ mainModule.factory('myService',function($http,$q){ var service = {}; var baseUr ...

  9. Mysql 列转行group_concat函数,与行转列

    1.正常情况. SELECT JoinEventIds from nt_mainnum 2.使用group_concat函数 select group_concat(JoinEventIds) fro ...

  10. Android项目代码混淆

    http://coolshell.info/blog/2015/03/Android-studio-prefrence.html 什么是Gradle Gradle是一种依赖管理工具,基于Groovy语 ...