Android深入浅出之Audio
第一部分 AudioTrack分析
一 目的
本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。
分析的流程是:
l 先从API层对应的某个类开始,用户层先要有一个简单的使用流程。
l 根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释。也就是深度优先的方法。
分析工具很简单,就是sourceinsight和android的API doc文档。当然还得有android的源代码。我这里是基于froyo的源码。
注意,froyo源码太多了,不要一股脑的加入到sourceinsight中,只要把framwork目录下的源码加进去就可以了,后续如要用的话,再加别的目录。
二 Audio系统
先看看Audio里边有哪些东西?通过Android的SDK文档,发现主要有三个:
l AudioManager:这个主要是用来管理Audio系统的
l AudioTrack:这个主要是用来播放声音的
l AudioRecord:这个主要是用来录音的
其中AudioManager的理解需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。一般看来,最简单的就是播放声音了。所以我们打算从AudioTrack开始分析。
三 AudioTrack(JAVA层)
JAVA的AudioTrack类的代码在:
framework/base/media/java/android/media/AudioTrack.java中。
先看看使用例子,然后跟进去分析。至于AudioTrack的其他使用方法和说明,需要大家自己去看API文档了。
//根据采样率,采样精度,单双声道来得到frame的大小。
int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。
//创建AudioTrack
AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
AudioFormat.ENCODING_PCM_16BIT,
bufsize,
AudioTrack.MODE_STREAM);//
trackplayer.play() ;//开始
trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据
….
trackplayer.stop();//停止播放
trackplayer.release();//释放底层资源。
这里需要解释下两个东西:
1 AudioTrack.MODE_STREAM的意思:
AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
2 StreamType
这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。
Android将系统的声音分为以下几类常见的(未写全):
l STREAM_ALARM:警告声
l STREAM_MUSCI:音乐声,例如music等
l STREAM_RING:铃声
l STREAM_SYSTEM:系统声音
l STREAM_VOCIE_CALL:电话声音
为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。
其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
AudioTrack的例子就几个函数。先看看第一个函数:
AudioTrack.getMinBufferSize(8000,//每秒8K个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT);
----->AudioTrack.JAVA
//注意,这是个static函数
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;--->看到了吧,外面名字搞得这么酷,其实就是指声道数
break;
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return AudioTrack.ERROR_BAD_VALUE;
}
//目前只支持PCM8和PCM16精度的音频
if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
loge("getMinBufferSize(): Invalid audio format.");
return AudioTrack.ERROR_BAD_VALUE;
}
//ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间
if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
return AudioTrack.ERROR_BAD_VALUE;
}
//调用native函数,够烦的,什么事情都搞到JNI层去。
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if ((size == -1) || (size == 0)) {
loge("getMinBufferSize(): error querying hardware");
return AudioTrack.ERROR;
}
else {
return size;
}
native_get_min_buff_size--->在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz,
jint sampleRateInHertz, jint nbChannels, jint audioFormat)
{//注意我们传入的参数是:
//sampleRateInHertz = 8000
//nbChannels = 2;
//audioFormat = AudioFormat.ENCODING_PCM_16BIT
int afSamplingRate;
int afFrameCount;
uint32_t afLatency;
//下面涉及到AudioSystem,这里先不解释了,
//反正知道从AudioSystem那查询了一些信息
if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
return -1;
}
if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
return -1;
}
if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
return -1;
}
//音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中
//找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
if (minBufCount < 2) minBufCount = 2;
uint32_t minFrameCount =
(afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
//下面根据最小的framecount计算最小的buffersize
int minBuffSize = minFrameCount
* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
* nbChannels;
return minBuffSize;
}
getMinBufSize函数完了后,我们得到一个满足最小要求的缓冲区大小。这样用户分配缓冲区就有了依据。下面就需要创建AudioTrack对象了
先看看调用函数:
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC,
8000,
AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
AudioFormat.ENCODING_PCM_16BIT,
bufsize,
AudioTrack.MODE_STREAM);//
其实现代码在AudioTrack.java中。
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
mState = STATE_UNINITIALIZED;
// 获得主线程的Looper,这个在MediaScanner分析中已经讲过了
if ((mInitializationLooper = Looper.myLooper()) == null) {
mInitializationLooper = Looper.getMainLooper();
}
//检查参数是否合法之类的,可以不管它
audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
//我是用getMinBufsize得到的大小,总不会出错吧?
audioBuffSizeCheck(bufferSizeInBytes);
// 调用native层的native_setup,把自己的WeakReference传进去了
//不了解JAVA WeakReference的可以上网自己查一下,很简单的
int initResult = native_setup(new WeakReference
mStreamType, 这个值是AudioManager.STREAM_MUSIC
mSampleRate, 这个值是8000
mChannels, 这个值是2
mAudioFormat,这个值是AudioFormat.ENCODING_PCM_16BIT
mNativeBufferSizeInBytes, //这个是刚才getMinBufSize得到的
mDataLoadMode);DataLoadMode是MODE_STREAM
....
}
上面函数调用最终进入了JNI层android_media_AudioTrack.cpp下面的函数
static int
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jint streamType, jint sampleRateInHertz, jint channels,
jint audioFormat, jint buffSizeInBytes, jint memoryMode)
{
int afSampleRate;
int afFrameCount;
下面又要调用一堆东西,烦不烦呐?具体干什么用的,以后分析到AudioSystem再说。
AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
AudioSystem::isOutputChannel(channels);
popCount是统计一个整数中有多少位为1的算法
int nbChannels = AudioSystem::popCount(channels);
if (streamType == javaAudioTrackFields.STREAM_MUSIC) {
atStreamType = AudioSystem::MUSIC;
}
int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
int format = audioFormat == javaAudioTrackFields.PCM16 ?
AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
//上面是根据Buffer大小和一个Frame大小来计算帧数的。
// AudioTrackJniStorage,就是一个保存一些数据的地方,这
//里边有一些有用的知识,下面再详细解释
AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
jclass clazz = env->GetObjectClass(thiz);
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
lpJniStorage->mStreamType = atStreamType;
//创建真正的AudioTrack对象
AudioTrack* lpTrack = new AudioTrack();
if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
//如果是STREAM流方式的话,把刚才那些参数设进去
lpTrack->set(
atStreamType,// stream type
sampleRateInHertz,
format,// word length, PCM
channels,
frameCount,
0,// flags
audioCallback,
&(lpJniStorage->mCallbackData),//callback, callback data (user)
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
0,// 共享内存,STREAM模式需要用户一次次写,所以就不用共享内存了
true);// thread can call Java
} else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
//如果是static模式,需要用户一次性把数据写进去,然后
//再由audioTrack自己去把数据读出来,所以需要一个共享内存
//这里的共享内存是指C++AudioTrack和AudioFlinger之间共享的内容
//因为真正播放的工作是由AudioFlinger来完成的。
lpJniStorage->allocSharedMem(buffSizeInBytes);
lpTrack->set(
atStreamType,// stream type
sampleRateInHertz,
format,// word length, PCM
channels,
frameCount,
0,// flags
audioCallback,
&(lpJniStorage->mCallbackData),//callback, callback data (user));
0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
lpJniStorage->mMemBase,// shared mem
true);// thread can call Java
}
if (lpTrack->initCheck() != NO_ERROR) {
LOGE("Error initializing AudioTrack");
goto native_init_failure;
}
//又来这一招,把C++AudioTrack对象指针保存到JAVA对象的一个变量中
//这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了。
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
}
1 AudioTrackJniStorage详解
这个类其实就是一个辅助类,但是里边有一些知识很重要,尤其是Android封装的一套共享内存的机制。这里一并讲解,把这块搞清楚了,我们就能轻松得在两个进程间进行内存的拷贝。
AudioTrackJniStorage的代码很简单。
struct audiotrack_callback_cookie {
jclass audioTrack_class;
jobject audioTrack_ref;
}; cookie其实就是把JAVA中的一些东西保存了下,没什么特别的意义
class AudioTrackJniStorage {
public:
sp
sp
audiotrack_callback_cookie mCallbackData;
int mStreamType;
bool allocSharedMem(int sizeInBytes) {
mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
//注意用法,先弄一个HeapBase,再把HeapBase传入到MemoryBase中去。
return true;
}
};
2 MemoryHeapBase
MemroyHeapBase也是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端(Bnxxx),一个代理端Bpxxx。看看MemoryHeapBase定义:
class MemoryHeapBase : public virtual BnMemoryHeap
{
果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了
//Bp端调用的函数最终都会调到Bn这来
对Binder机制不了解的,可以参考:
http://blog.csdn.net/Innost/archive/2011/01/08/6124685.aspx
有好几个构造函数,我们看看我们使用的:
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(0), mNeedUnmap(false)
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它
//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存
//不明白得去man mmap看看
mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是
ashmem_create_region返回的文件描述符
}
MemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。
getBaseID()--->返回mFd,如果为负数,表明刚才创建共享内存失败了
getBase()->返回mBase,内存位置
getSize()->返回mSize,内存大小
有了MemoryHeapBase,又搞了一个MemoryBase,这又是一个和Binder机制挂钩的类。
唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset
那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类
这样就不用用户到处去计算读写位置了。
class MemoryBase : public BnMemory
{
public:
MemoryBase(const sp
virtual sp
protected:
size_t getSize() const { return mSize; }
ssize_t getOffset() const { return mOffset; }
const sp
};
好了,明白上面两个MemoryXXX,我们可以猜测下大概的使用方法了。
l BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,
l 然后把BnMemoryBase传递到BpXXX
l BpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。
注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且Android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。
另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。
JAVA层到这一步后就是调用play和write了。JAVA层这两个函数没什么内容,都是直接转到native层干活了。
先看看play函数对应的JNI函数
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
//看见没,从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针
//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改!
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
lpTrack->start(); //这个以后再说
}
下面是write。我们写的是short数组,
static jint
android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz,
jshortArray javaAudioData,
jint offsetInShorts,
jint sizeInShorts,
jint javaAudioFormat) {
return (android_media_AudioTrack_native_write(env, thiz,
(jbyteArray) javaAudioData,
offsetInShorts*2, sizeInShorts*2,
javaAudioFormat)
/ 2);
}
烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去
jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
jint offsetInBytes, jint sizeInBytes) {
ssize_t written = 0;
// regular write() or copy the data to the AudioTrack's shared memory?
if (pTrack->sharedBuffer() == 0) {
//创建的是流的方式,所以没有共享内存在track中
//还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建
//共享内存
written = pTrack->write(data + offsetInBytes, sizeInBytes);
} else {
if (audioFormat == javaAudioTrackFields.PCM16) {
// writing to shared memory, check for capacity
if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {
sizeInBytes = pTrack->sharedBuffer()->size();
}
//看见没?STATIC模式的,就直接把数据拷贝到共享内存里
//当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的
//共享设进去的
memcpy(pTrack->sharedBuffer()->pointer(),
data + offsetInBytes, sizeInBytes);
written = sizeInBytes;
} else if (audioFormat == javaAudioTrackFields.PCM8) {
PCM8格式的要先转换成PCM16
}
return written;
}
到这里,似乎很简单啊,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。反正JNI这层是再看不出什么有意思的东西了。
四 AudioTrack(C++层)
接上面的内容,我们知道在JNI层,有以下几个步骤:
l new了一个AudioTrack
l 调用set函数,把AudioTrackJniStorage等信息传进去
l 调用了AudioTrack的start函数
l 调用AudioTrack的write函数
那么,我们就看看真正干活的的C++AudioTrack吧。
AudioTrack.cpp位于framework/base/libmedia/AudioTrack.cpp
JNI层调用的是最简单的构造函数:
AudioTrack::AudioTrack()
: mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。
{
}
接下来调用set。我们看看JNI那set了什么
lpTrack->set(
atStreamType, //应该是Music吧
sampleRateInHertz,//8000
format,// 应该是PCM_16吧
channels,//立体声=2
frameCount,//
0,// flags
audioCallback, //JNI中的一个回调函数
&(lpJniStorage->mCallbackData),//回调函数的参数
0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上
0,//共享buffer地址,stream模式没有
true);//回调线程可以调JAVA的东西
那我们看看set函数把。
status_t AudioTrack::set(
int streamType,
uint32_t sampleRate,
int format,
int channels,
int frameCount,
uint32_t flags,
callback_t cbf,
void* user,
int notificationFrames,
const sp
bool threadCanCallJava)
{
...前面一堆的判断,等以后讲AudioSystem再说
audio_io_handle_t output =
AudioSystem::getOutput((AudioSystem::stream_type)streamType,
sampleRate, format, channels, (AudioSystem::output_flags)flags);
//createTrack?看来这是真正干活的
status_t status = createTrack(streamType, sampleRate, format, channelCount,
frameCount, flags, sharedBuffer, output);
//cbf是JNI传入的回调函数audioCallback
if (cbf != 0) { //看来,怎么着也要创建这个线程了!
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
}
return NO_ERROR;
}
看看真正干活的createTrack
status_t AudioTrack::createTrack(
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
const sp
audio_io_handle_t output)
{
status_t status;
//啊,看来和audioFlinger挂上关系了呀。
const sp
//下面这个调用最终会在AudioFlinger中出现。暂时不管它。
sp
streamType,
sampleRate,
format,
channelCount,
frameCount,
((uint16_t)flags) << 16,
sharedBuffer,
output,
&status);
//看见没,从track也就是AudioFlinger那边得到一个IMemory接口
//这个看来就是最终write写入的地方
sp
mAudioTrack.clear();
mAudioTrack = track;
mCblkMemory.clear();//sp
mCblkMemory = cblk;
mCblk = static_cast
mCblk->out = 1;
mFrameCount = mCblk->frameCount;
if (sharedBuffer == 0) {
//终于看到buffer相关的了。注意我们这里的情况
//STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。
//反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到
//的buffer了。
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
}
return NO_ERROR;
}
还记得我们说MemoryXXX没有同步机制,所以这里应该有一个东西能体现同步的,
那么我告诉大家,就在audio_track_cblk_t结构中。它的头文件在
framework/base/include/private/media/AudioTrackShared.h
实现文件就在AudioTrack.cpp中
audio_track_cblk_t::audio_track_cblk_t()
//看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了
//等以后介绍同步方面的知识时,再细说
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
userBase(0), serverBase(0), buffers(0), frameCount(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
flowControlFlag(1), forceReady(0)
{
}
到这里,大家应该都有个大概的全景了。
l AudioTrack得到AudioFlinger中的一个IAudioTrack对象,这里边有一个很重要的数据结构audio_track_cblk_t,它包括一块缓冲区地址,包括一些进程间同步的内容,可能还有数据位置等内容
l AudioTrack启动了一个线程,叫AudioTrackThread,这个线程干嘛的呢?还不知道
l AudioTrack调用write函数,肯定是把数据写到那块共享缓冲了,然后IAudioTrack在另外一个进程AudioFlinger中(其实AudioFlinger是一个服务,在mediaservice中运行)接收数据,并最终写到音频设备中。
那我们先看看AudioTrackThread干什么了。
调用的语句是:
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
AudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。
反正最终会调用AudioTrackAThread的threadLoop函数。
先看看构造函数
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
: Thread(bCanCallJava), mReceiver(receiver)
{ //mReceiver就是AudioTrack对象
// bCanCallJava为TRUE
}
这个线程的启动由AudioTrack的start函数触发。
void AudioTrack::start()
{
//start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的
threadLoop
sp
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
//让AudioFlinger中的track也start
status_t status = mAudioTrack->start();
}
bool AudioTrack::AudioTrackThread::threadLoop()
{
//太恶心了,又调用AudioTrack的processAudioBuffer函数
return mReceiver.processAudioBuffer(this);
}
bool AudioTrack::processAudioBuffer(const sp
{
Buffer audioBuffer;
uint32_t frames;
size_t writtenSize;
...回调1
mCbf(EVENT_UNDERRUN, mUserData, 0);
...回调2 都是传递一些信息到JNI里边
mCbf(EVENT_BUFFER_END, mUserData, 0);
// Manage loop end callback
while (mLoopCount > mCblk->loopCount) {
mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
}
//下面好像有写数据的东西
do {
audioBuffer.frameCount = frames;
//获得buffer,
status_t err = obtainBuffer(&audioBuffer, 1);
size_t reqSize = audioBuffer.size;
//把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停
//地write呢,怎么会这样?
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
audioBuffer.size = writtenSize;
frames -= audioBuffer.frameCount;
releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK
操作了
}
while (frames);
return true;
}
难道真的有两处在write数据?看来必须得到mCbf去看看了,传的是EVENT_MORE_DATA标志。
mCbf由set的时候传入C++的AudioTrack,实际函数是:
static void audioCallback(int event, void* user, void *info) {
if (event == AudioTrack::EVENT_MORE_DATA) {
//哈哈,太好了,这个函数没往里边写数据
AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
pBuff->size = 0;
}
从代码上看,本来google考虑是异步的回调方式来写数据,可惜发现这种方式会比较复杂,尤其是对用户开放的JAVA AudioTrack会很不好处理,所以嘛,偷偷摸摸得给绕过去了。
太好了,看来就只有用户的write会真正的写数据了,这个AudioTrackThread除了通知一下,也没什么实际有意义的操作了。
让我们看看write吧。
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{
够简单,就是obtainBuffer,memcpy数据,然后releasBuffer
眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了
do {
audioBuffer.frameCount = userSize/frameSize();
status_t err = obtainBuffer(&audioBuffer, -1);
size_t toWrite;
toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, src, toWrite);
src += toWrite;
}
userSize -= toWrite;
written += toWrite;
releaseBuffer(&audioBuffer);
} while (userSize);
return written;
}
obtainBuffer太复杂了,不过大家知道其大概工作方式就可以了
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
//恕我中间省略太多,大部分都是和当前数据位置相关,
uint32_t framesAvail = cblk->framesAvailable();
cblk->lock.lock();//看见没,lock了
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
//我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道
//没有一个好的方法来集中处理这种事情吗?
if (result == DEAD_OBJECT) {
result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, mSharedBuffer,getOutput());
}
//得到buffer
audioBuffer->raw = (int8_t *)cblk->buffer(u);
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
在看看releaseBuffer
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
audio_track_cblk_t* cblk = mCblk;
cblk->stepUser(audioBuffer->frameCount);
}
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
{
uint32_t u = this->user;
u += frameCount;
if (out) {
if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
}
} else if (u > this->server) {
u = this->server;
}
if (u >= userBase + this->frameCount) {
userBase += this->frameCount;
}
this->user = u;
flowControlFlag = 0;
return u;
}
奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?
再去看看obtainBuffer?为何写得这么晦涩难懂?
原来在obtainBuffer中会某一次进去lock,再某一次进去可能就是unlock了。没看到obtainBuffer中到处有lock,unlock,wait等同步操作吗。一定是这个道理。难怪写这么复杂。还使用了少用的goto语句。
唉,有必要这样吗!
五 AudioTrack总结
通过这一次的分析,我自己觉得有以下几个点:
l AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。
l 看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点
工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。
l AudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。
l 调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了
l 用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯
l 可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。
一 目的
本文承接Audio第一部分的AudioTrack,通过AudioTrack作为AF(AudioFlinger)的客户端,来看看AF是如何完成工作的。
在AT(AudioTrack)中,我们涉及到的都是流程方面的事务,而不是系统Audio策略上的内容。WHY?因为AT是AF的客户端,而AF是Android系统中Audio管理的中枢。AT我们分析的是按流程方法,那么以AT为切入点的话,AF的分析也应该是流程分析了。
对于分析AT来说,只要能把它的调用顺序(也就是流程说清楚就可以了),但是对于AF的话,简单的分析调用流程 我自己感觉是不够的。因为我发现手机上的声音交互和管理是一件比较复杂的事情。举个简单例子,当听music的时候来电话了,声音处理会怎样?
虽然在Android中,还有一个叫AudioPolicyService的(APS)东西,但是它最终都会调用到AF中去,因为AF实际创建并管理了硬件设备。所以,针对Android声音策略上的分析,我会单独在以后来分析。
二 从AT切入到AF
直接从头看代码是没法掌握AF的主干的,必须要有一个切入点,也就是用一个正常的调用流程来分析AF的处理流程。先看看AF的产生吧,这个C/S架构的服务者是如何产生的呢?
AF是一个服务,这个就不用我多说了吧?代码在
framework/base/media/mediaserver/Main_mediaServer.cpp中。
int main(int argc, char** argv)
{
sp
sp
....
AudioFlinger::instantiate();--->AF的实例化
AudioPolicyService::instantiate();--->APS的实例化
....
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
哇塞,看来这个程序的负担很重啊。没想到。为何AF,APS要和MediaService和CameraService都放到一个篮子里?
看看AF的实例化静态函数,在framework/base/libs/audioFlinger/audioFlinger.cpp中
void AudioFlinger::instantiate() {
defaultServiceManager()->addService( //把AF实例加入系统服务
String16("media.audio_flinger"), new AudioFlinger());
}
再来看看它的构造函数是什么做的。
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),//初始化基类
mAudioHardware(0), //audio硬件的HAL对象
mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
{
mHardwareStatus = AUDIO_HW_IDLE;
//创建代表Audio硬件的HAL对象
mAudioHardware = AudioHardwareInterface::create();
mHardwareStatus = AUDIO_HW_INIT;
if (mAudioHardware->initCheck() == NO_ERROR) {
setMode(AudioSystem::MODE_NORMAL);
//设置系统的声音模式等,其实就是设置硬件的模式
setMasterVolume(1.0f);
setMasterMute(false);
}
}
AF中经常有setXXX的函数,到底是干什么的呢?我们看看setMode函数。
status_t AudioFlinger::setMode(int mode)
{
mHardwareStatus = AUDIO_HW_SET_MODE;
status_t ret = mAudioHardware->setMode(mode);//设置硬件的模式
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
当然,setXXX还有些别的东西,但基本上都会涉及到硬件对象。我们暂且不管它。等分析到Audio策略再说。
好了,Android系统启动的时候,看来AF也准备好硬件了。不过,创建硬件对象就代表我们可以播放了吗?
我这里简单的把AT调用AF的流程列一下,待会按这个顺序分析AF的工作方式。
--参加AudioTrack分析的4.1节
1. 创建
AudioTrack* lpTrack = new AudioTrack();
lpTrack->set(...);
这个就进入到C++的AT了。下面是AT的set函数
audio_io_handle_t output =
AudioSystem::getOutput((AudioSystem::stream_type)streamType,
sampleRate, format, channels, (AudioSystem::output_flags)flags);
status_t status = createTrack(streamType, sampleRate, format, channelCount,
frameCount, flags, sharedBuffer, output);
----->creatTrack会和AF打交道。我们看看createTrack重要语句
const sp
//下面很重要,调用AF的createTrack获得一个IAudioTrack对象
sp
sp
总结一下创建的流程,AT调用AF的createTrack获得一个IAudioTrack对象,然后从这个对象中获得共享内存的对象。
2. start和write
看看AT的start,估计就是调用IAudioTrack的start吧?
void AudioTrack::start()
{
//果然啊...
status_t status = mAudioTrack->start();
}
那write呢?我们之前讲了,AT就是从共享buffer中:
l Lock缓存
l 写缓存
l Unlock缓存
注意,这里的Lock和Unlock是有问题的,什么问题呢?待会我们再说
按这种方式的话,那么AF一定是有一个线程在那也是:
l Lock,
l 读缓存,写硬件
l Unlock
总之,我们知道了AT的调用AF的流程了。下面一个一个看。
1 createTrack
sp
pid_t pid,//AT的pid号
int streamType,//MUSIC,流类型
uint32_t sampleRate,//8000 采样率
int format,//PCM_16类型
int channelCount,//2,双声道
int frameCount,//需要创建的buffer可包含的帧数
uint32_t flags,
const sp
int output,//这个是从AuidoSystem获得的对应MUSIC流类型的索引
status_t *status)
{
sp
sp
sp
wp
status_t lStatus;
{
Mutex::Autolock _l(mLock);
//根据output句柄,获得线程?
PlaybackThread *thread = checkPlaybackThread_l(output);
//看看这个进程是不是已经是AF的客户了
//这里说明一下,由于是C/S架构,那么作为服务端的AF肯定有地方保存作为C的AT的信息
//那么,AF是根据pid作为客户端的唯一标示的
//mClients是一个类似map的数据组织结构
wclient = mClients.valueFor(pid);
if (wclient != NULL) {
} else {
//如果还没有这个客户信息,就创建一个,并加入到map中去
client = new Client(this, pid);
mClients.add(pid, client);
}
//从刚才找到的那个线程对象中创建一个track
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, &lStatus);
}
//喔,还有一个trackHandle,而且返回到AF端的是这个trackHandle对象
trackHandle = new TrackHandle(track);
return trackHandle;
}
这个AF函数中,突然冒出来了很多新类型的数据结构。说实话,我刚开始接触的时候,大脑因为常接触到这些眼生的东西而死机!大家先不要拘泥于这些东西,我会一一分析到的。
先进入到checkPlaybackThread_l看看。
AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
{
PlaybackThread *thread = NULL;
//看到这种indexOfKey的东西,应该立即能想到:
//喔,这可能是一个map之类的东西,根据key能找到实际的value
if (mPlaybackThreads.indexOfKey(output) >= 0) {
thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get();
}
//这个函数的意思是根据output值,从一堆线程中找到对应的那个线程
return thread;
}
看到这里很疑惑啊:
l AF的构造函数中没有创建线程,只创建了一个audio的HAL对象
l 如果AT是AF的第一个客户的话,我们刚才的调用流程里边,也没看到哪有创建线程的地方呀。
l output是个什么玩意儿?为什么会根据它作为key来找线程呢?
看来,我们得去Output的来源那看看了。
我们知道,output的来源是由AT的set函数得到的:如下:
audio_io_handle_t output = AudioSystem::getOutput(
(AudioSystem::stream_type)streamType, //MUSIC类型
sampleRate, //8000
format, //PCM_16
channels, //2两个声道
(AudioSystem::output_flags)flags//0
);
上面这几个参数后续不再提示了,大家知道这些值都是由AT做为切入点传进去的
然后它在调用AT自己的createTrack,最终把这个output值传递到AF了。其中audio_io_handle_t类型就是一个int类型。
//叫handle啊?好像linux下这种叫法的很少,难道又是受MS的影响吗?
我们进到AudioSystem::getOutput看看。注意,大家想想这是系统的第一次调用,而且发生在AudioTrack那个进程里边。AudioSystem的位置在framework/base/media/libmedia/AudioSystem.cpp中
audio_io_handle_t AudioSystem::getOutput(stream_type stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
output_flags flags)
{
audio_io_handle_t output = 0;
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0 &&
((stream != AudioSystem::VOICE_CALL && stream != AudioSystem::BLUETOOTH_SCO) ||
channels != AudioSystem::CHANNEL_OUT_MONO ||
(samplingRate != 8000 && samplingRate != 16000))) {
Mutex::Autolock _l(gLock);
//根据我们的参数,我们会走到这个里边来
//喔,又是从map中找到stream=music的output。可惜啊,我们是第一次进来
//output一定是0
output = AudioSystem::gStreamOutputMap.valueFor(stream);
}
if (output == 0) {
//我晕,又到AudioPolicyService(APS)
//由它去getOutput
const sp
output = aps->getOutput(stream, samplingRate, format, channels, flags);
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) {
Mutex::Autolock _l(gLock);
//如果取到output了,再把output加入到AudioSystem维护的这个map中去
//说白了,就是保存一些信息吗。免得下次又这么麻烦去骚扰APS!
AudioSystem::gStreamOutputMap.add(stream, output);
}
}
return output;
}
怎么办?需要到APS中才能找到output的信息?
没办法,硬着头皮进去吧。那先得看看APS是如何创建的。不过这个刚才已经说了,是和AF一块在那个Main_mediaService.cpp中实例化的。
位置在framework/base/lib/libaudioflinger/ AudioPolicyService.cpp中
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpPolicyManager(NULL)
{
// 下面两个线程以后再说
mTonePlaybackThread = new AudioCommandThread(String8(""));
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
//喔,使用普适的AudioPolicyManager,把自己this做为参数
//我们这里先使用普适的看看吧
mpPolicyManager = new AudioPolicyManagerBase(this);
//使用硬件厂商提供的特殊的AudioPolicyManager
//mpPolicyManager = createAudioPolicyManager(this);
}
}
我们看看AudioManagerBase的构造函数吧,在framework/base/lib/audioFlinger/
AudioPolicyManagerBase.cpp中。
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
: mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false)
{
mpClientInterface = clientInterface;这个client就是APS,刚才通过this传进来了
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
openOutput又交给APS的openOutput来完成了,真绕....
}
唉,看来我们还是得回到APS,
audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
AudioSystem::output_flags flags)
{
sp
//FT,FT,FT,FT,FT,FT,FT
//绕了这么一个大圈子,竟然回到AudioFlinger中了啊??
return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels,
pLatencyMs, flags);
}
在我们再次被绕晕之后,我们回眸看看足迹吧:
l 在AudioTrack中,调用set函数
l 这个函数会通过AudioSystem::getOutput来得到一个output的句柄
l AS的getOutput会调用AudioPolicyService的getOutput
l 然后我们就没继续讲APS的getOutPut了,而是去看看APS创建的东西
l 发现APS创建的时候会创建一个AudioManagerBase,这个AMB的创建又会调用APS的openOutput。
l APS的openOutput又会调用AudioFlinger的openOutput
有一个疑问,AT中set参数会和APS构造时候最终传入到AF的openOutput一样吗?如果不一样,那么构造时候openOutput的又是什么参数呢?
先放下这个悬念,我们继续从APS的getOutPut看看。
audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
AudioSystem::output_flags flags)
{
Mutex::Autolock _l(mLock);
//自己又不干活,由AudioManagerBase干活
return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
}
进去看看吧
audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
uint32_t samplingRate,
uint32_t format,
uint32_t channels,
AudioSystem::output_flags flags)
{
audio_io_handle_t output = 0;
uint32_t latency = 0;
// open a non direct output
output = mHardwareOutput; //这个是在哪里创建的?在AMB构造的时候..
return output;
}
具体AMB的分析待以后Audio系统策略的时候我们再说吧。反正,到这里,我们知道了,在APS构造的时候会open一个Output,而这个Output又会调用AF的openOutput。
int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
{
status_t status;
PlaybackThread *thread = NULL;
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
uint32_t format = pFormat ? *pFormat : 0;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
Mutex::Autolock _l(mLock);
//由Audio硬件HAL对象创建一个AudioStreamOut对象
AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
(int *)&format,
&channels,
&samplingRate,
&status);
mHardwareStatus = AUDIO_HW_IDLE;
if (output != 0) {
//创建一个Mixer线程
thread = new MixerThread(this, output, ++mNextThreadId);
}
//终于找到了,把这个线程加入线程管理组织中
mPlaybackThreads.add(mNextThreadId, thread);
return mNextThreadId;
}
}
明白了,看来AT在调用AF的createTrack的之前,AF已经在某个时候把线程创建好了,而且是一个Mixer类型的线程,看来和混音有关系呀。这个似乎和我们开始设想的AF工作有点联系喔。Lock,读缓存,写Audio硬件,Unlock。可能都是在这个线程里边做的。
2 继续createTrack
AudioFlinger::createTrack(
pid_t pid,
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
const sp
int output,
status_t *status)
{
sp
sp
sp
wp
status_t lStatus;
{
//假设我们找到了对应的线程
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
//晕,调用这个线程对象的createTrack_l
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, &lStatus);
}
trackHandle = new TrackHandle(track);
return trackHandle;----》注意,这个对象是最终返回到AT进程中的。
实在是....太绕了。再进去看看thread->createTrack_l吧。_l的意思是这个函数进入之前已经获得同步锁了。
跟着sourceinsight ctrl+鼠标左键就进入到下面这个函数。
下面这个函数的签名好长啊。这是为何?
原来Android的C++类中大量定义了内部类。说实话,我之前几年的C++的经验中基本没接触过这么频繁使用内部类的东东。--->当然,你可以说STL也大量使用了呀。
我们就把C++的内部类当做普通的类一样看待吧,其实我感觉也没什么特殊的含义,和外部类是一样的,包括函数调用,public/private之类的东西。这个和JAVA的内部类是大不一样的。
sp
const sp
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
const sp
status_t *status)
{
sp
status_t lStatus;
{ // scope for mLock
Mutex::Autolock _l(mLock);
//new 一个track对象
//我有点愤怒了,Android真是层层封装啊,名字取得也非常相似。
//看看这个参数吧,注意sharedBuffer这个,此时的值应是0
track = new Track(this, client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer);
mTracks.add(track); //把这个track加入到数组中,是为了管理用的。
}
lStatus = NO_ERROR;
return track;
}
看到这个数组的存在,我们应该能想到什么吗?这时已经有:
l 一个MixerThread,内部有一个数组保存track的
看来,不管有多少个AudioTrack,最终在AF端都有一个track对象对应,而且这些所有的track对象都会由一个线程对象来处理。----难怪是Mixer啊
再去看看new Track,我们一直还没找到共享内存在哪里创建的!!!
AudioFlinger::PlaybackThread::Track::Track(
const wp
const sp
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
const sp
: TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
{
// mCblk !=NULL?什么时候创建的??
//只能看基类TrackBase,还是很愤怒,太多继承了。
if (mCblk != NULL) {
mVolume[0] = 1.0f;
mVolume[1] = 1.0f;
mStreamType = streamType;
mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount *
sizeof(int16_t) : sizeof(int8_t);
}
}
看看基类TrackBase干嘛了
AudioFlinger::ThreadBase::TrackBase::TrackBase(
const wp
const sp
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
const sp
: RefBase(),
mThread(thread),
mClient(client),
mCblk(0),
mFrameCount(0),
mState(IDLE),
mClientTid(-1),
mFormat(format),
mFlags(flags & ~SYSTEM_FLAGS_MASK)
{
size_t size = sizeof(audio_track_cblk_t);
size_t bufferSize = frameCount*channelCount*sizeof(int16_t);
if (sharedBuffer == 0) {
size += bufferSize;
}
//调用client的allocate函数。这个client是什么?就是我们在CreateTrack中创建的
那个Client,我不想再说了。反正这里会创建一块共享内存
mCblkMemory = client->heap()->allocate(size);
有了共享内存,但是还没有里边有同步锁的那个对象audio_track_cblk_t
mCblk = static_cast
下面这个语法好怪啊。什么意思???
new(mCblk) audio_track_cblk_t();
//各位,这就是C++语法中的placement new。干啥用的啊?new后面的括号中是一块buffer,再
后面是一个类的构造函数。对了,这个placement new的意思就是在这块buffer中构造一个对象。
我们之前的普通new是没法让一个对象在某块指定的内存中创建的。而placement new却可以。
这样不就达到我们的目的了吗?搞一块共享内存,再在这块内存上创建一个对象。这样,这个对象不也就能在两个内存中共享了吗?太牛牛牛牛牛了。怎么想到的?
// clear all buffers
mCblk->frameCount = frameCount;
mCblk->sampleRate = sampleRate;
mCblk->channels = (uint8_t)channelCount;
}
好了,解决一个重大疑惑,跨进程数据共享的重要数据结构audio_track_cblk_t是通过placement new在一块共享内存上来创建的。
回到AF的CreateTrack,有这么一句话:
trackHandle = new TrackHandle(track);
return trackHandle;----》注意,这个对象是最终返回到AT进程中的。
trackHandle的构造使用了thread->createTrack_l的返回值。
读到这里的人,一定会被异常多的class类型,内部类,继承关系搞疯掉。说实话,这里废点心血整个或者paste一个大的UML图未尝不可。但是我是不太习惯用图说话,因为图我实在是记不住。那好吧。我们就用最简单的话语争取把目前出现的对象说清楚。
1 AudioFlinger
class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient
AudioFlinger类是代表整个AudioFlinger服务的类,其余所有的工作类都是通过内部类的方式在其中定义的。你把它当做一个壳子也行吧。
2 Client
Client是描述C/S结构的C端的代表,也就算是一个AT在AF端的对等物吧。不过可不是Binder机制中的BpXXX喔。因为AF是用不到AT的功能的。
class Client : public RefBase {
public:
sp
sp
pid_t mPid;//C端的进程id
};
3 TrackHandle
Trackhandle是AT端调用AF的CreateTrack得到的一个基于Binder机制的Track。
这个TrackHandle实际上是对真正干活的PlaybackThread::Track的一个跨进程支持的封装。
什么意思?本来PlaybackThread::Track是真正在AF中干活的东西,不过为了支持跨进程的话,我们用TrackHandle对其进行了一下包转。这样在AudioTrack调用TrackHandle的功能,实际都由TrackHandle调用PlaybackThread::Track来完成了。可以认为是一种Proxy模式吧。
这个就是AudioFlinger异常复杂的一个原因!!!
class TrackHandle : public android::BnAudioTrack {
public:
TrackHandle(const sp
virtual ~TrackHandle();
virtual status_t start();
virtual void stop();
virtual void flush();
virtual void mute(bool);
virtual void pause();
virtual void setVolume(float left, float right);
virtual sp
sp
};
4 线程类
AF中有好几种不同类型的线程,分别有对应的线程类型:
l RecordThread:
RecordThread : public ThreadBase, public AudioBufferProvider
用于录音的线程。
l PlaybackThread:
class PlaybackThread : public ThreadBase
用于播放的线程
l MixerThread
MixerThread : public PlaybackThread
用于混音的线程,注意他是从PlaybackThread派生下来的。
l DirectoutputThread
DirectOutputThread : public PlaybackThread
直接输出线程,我们之前在代码里老看到DIRECT_OUTPUT之类的判断,看来最终和这个线程有关。
l DuplicatingThread:
DuplicatingThread : public MixerThread
复制线程?而且从混音线程中派生?暂时不知道有什么用
这么多线程,都有一个共同的父类ThreadBase,这个是AF对Audio系统单独定义的一个以Thread为基类的类。------》FT,真的很麻烦。
ThreadBase我们不说了,反正里边封装了一些有用的函数。
我们看看PlayingThread吧,里边由定义了内部类:
5 PlayingThread的内部类Track
我们知道,TrackHandle构造用的那个Track是PlayingThread的createTrack_l得到的。
class Track : public TrackBase
晕喔,又来一个TrackBase。
TrackBase是ThreadBase定义的内部类
class TrackBase : public AudioBufferProvider, public RefBase
基类AudioBufferProvider是一个对Buffer的封装,以后在AF读共享缓冲,写数据到硬件HAL中用得到。
个人感觉:上面这些东西,其实完完全全可以独立到不同的文件中,然后加一些注释说明。
写这样的代码,要是我是BOSS的话,一定会很不爽。有什么意义吗?有什么好处吗?
好了,这里终于在AF中的createTrack返回了TrackHandle。这个时候系统处于什么状态?
l AF中的几个Thread我们之前说了,在AF启动的某个时间就已经起来了。我们就假设AT调用AF服务前,这个线程就已经启动了。
这个可以看代码就知道了:
void AudioFlinger::PlaybackThread::onFirstRef()
{
const size_t SIZE = 256;
char buffer[SIZE];
snprintf(buffer, SIZE, "Playback Thread %p", this);
//onFirstRef,实际是RefBase的一个方法,在构造sp的时候就会被调用
//下面的run就真正创建了线程并开始执行threadLoop了
run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
}
到底执行哪个线程的threadLoop?我记得我们是根据output句柄来查找线程的。
看看openOutput的实行,真正的线程对象创建是在那儿。
nt AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
{
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, ++mNextThreadId);
//如果flags没有设置直接输出标准,或者format不是16bit,或者声道数不是2立体声
//则创建DirectOutputThread。
} else {
//可惜啊,我们创建的是最复杂的MixerThread
thread = new MixerThread(this, output, ++mNextThreadId);
1. MixerThread
非常重要的工作线程,我们看看它的构造函数。
AudioFlinger::MixerThread::MixerThread(const sp
: PlaybackThread(audioFlinger, output, id),
mAudioMixer(0)
{
mType = PlaybackThread::MIXER;
//混音器对象,传进去的两个参数时基类ThreadBase的,都为0
//这个对象巨复杂,最终混音的数据都由它生成,以后再说...
mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
}
2. AT调用start
此时,AT得到IAudioTrack对象后,调用start函数。
status_t AudioFlinger::TrackHandle::start() {
return mTrack->start();
} //果然,自己又不干活,交给mTrack了,这个是PlayintThread createTrack_l得到的Track对象
status_t AudioFlinger::PlaybackThread::Track::start()
{
status_t status = NO_ERROR;
sp
//这个Thread就是调用createTrack_l的那个thread对象,这里是MixerThread
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
int state = mState;
if (mState == PAUSED) {
mState = TrackBase::RESUMING;
} else {
mState = TrackBase::ACTIVE;
}
//把自己由加到addTrack_l了
//奇怪,我们之前在看createTrack_l的时候,不是已经有个map保存创建的track了
//这里怎么又出现了一个类似的操作?
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
playbackThread->addTrack_l(this);
return status;
}
看看这个addTrack_l函数
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp
{
status_t status = ALREADY_EXISTS;
// set retry count for buffer fill
track->mRetryCount = kMaxTrackStartupRetries;
if (mActiveTracks.indexOf(track) < 0) {
mActiveTracks.add(track);//啊,原来是加入到活跃Track的数组啊
status = NO_ERROR;
}
//我靠,有戏啊!看到这个broadcast,一定要想到:恩,在不远处有那么一个线程正
//等着这个CV呢。
mWaitWorkCV.broadcast();
return status;
}
让我们想想吧。start是把某个track加入到PlayingThread的活跃Track队列,然后触发一个信号事件。由于这个事件是PlayingThread的内部成员变量,而PlayingThread又创建了一个线程,那么难道是那个线程在等待这个事件吗?这时候有一个活跃track,那个线程应该可以干活了吧?
这个线程是MixerThread。我们去看看它的线程函数threadLoop吧。
bool AudioFlinger::MixerThread::threadLoop()
{
int16_t* curBuf = mMixBuffer;
Vector< sp
while (!exitPending())
{
processConfigEvents();
//Mixer进到这个循环中来
mixerStatus = MIXER_IDLE;
{ // scope for mLock
Mutex::Autolock _l(mLock);
const SortedVector< wp
//每次都取当前最新的活跃Track数组
//下面是预备操作,返回状态看看是否有数据需要获取
mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
}
//LIKELY,是GCC的一个东西,可以优化编译后的代码
//就当做是TRUE吧
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
//调用混音器,把buf传进去,估计得到了混音后的数据了
//curBuf是mMixBuffer,PlayingThread的内部buffer,在某个地方已经创建好了,
//缓存足够大
mAudioMixer->process(curBuf);
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
}
有数据要写到硬件中,肯定不能sleep了呀
if (sleepTime == 0) {
//把缓存的数据写到outPut中。这个mOutput是AudioStreamOut
//由Audio HAL的那个对象创建得到。等我们以后分析再说
int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
mStandby = false;
} else {
usleep(sleepTime);//如果没有数据,那就休息吧..
}
3. MixerThread核心
到这里,大家是不是有种焕然一新的感觉?恩,对了,AF的工作就是如此的精密,每个部分都配合得丝丝入扣。不过对于我们看代码的人来说,实在搞不懂这么做的好处----哈哈 有点扯远了。
MixerThread的线程循环中,最重要的两个函数:
prepare_l和mAudioMixer->process,我们一一来看看。
uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp
{
uint32_t mixerStatus = MIXER_IDLE;
//得到活跃track个数,这里假设就是我们创建的那个AT吧,那么count=1
size_t count = activeTracks.size();
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
for (size_t i=0 ; i sp Track* const track = t.get(); //得到placement new分配的那个跨进程共享的对象 audio_track_cblk_t* cblk = track->cblk(); //设置混音器,当前活跃的track。 mAudioMixer->setActiveTrack(track->name()); if (cblk->framesReady() && (track->isReady() || track->isStopped()) && !track->isPaused() && !track->isTerminated()) { // compute volume for this track //AT已经write数据了。所以肯定会进到这来。 int16_t left, right; if (track->isMuted() || masterMute || track->isPausing() || mStreamTypes[track->type()].mute) { left = right = 0; if (track->isPausing()) { track->setPaused(); } //AT设置的音量假设不为零,我们需要聆听声音! //所以走else流程 } else { // read original volumes with volume control float typeVolume = mStreamTypes[track->type()].volume; float v = masterVolume * typeVolume; float v_clamped = v * cblk->volume[0]; if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; left = int16_t(v_clamped); v_clamped = v * cblk->volume[1]; if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; right = int16_t(v_clamped); //计算音量 } //注意,这里对混音器设置了数据提供来源,是一个track,还记得我们前面说的吗?Track从 AudioBufferProvider派生 mAudioMixer->setBufferProvider(track); mAudioMixer->enable(AudioMixer::MIXING); int param = AudioMixer::VOLUME; //为这个track设置左右音量等 mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); mAudioMixer->setParameter( AudioMixer::TRACK, AudioMixer::FORMAT, track->format()); mAudioMixer->setParameter( AudioMixer::TRACK, AudioMixer::CHANNEL_COUNT, track->channelCount()); mAudioMixer->setParameter( AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, int(cblk->sampleRate)); } else { if (track->isStopped()) { track->reset(); } //如果这个track已经停止了,那么把它加到需要移除的track队列tracksToRemove中去 //同时停止它在AudioMixer中的混音 if (track->isTerminated() || track->isStopped() || track->isPaused()) { tracksToRemove->add(track); mAudioMixer->disable(AudioMixer::MIXING); } else { mAudioMixer->disable(AudioMixer::MIXING); } } } // remove all the tracks that need to be... count = tracksToRemove->size(); return mixerStatus; }
看明白了吗?prepare_l的功能是什么?根据当前活跃的track队列,来为混音器设置信息。可想而知,一个track必然在混音器中有一个对应的东西。我们待会分析AudioMixer的时候再详述。
为混音器准备好后,下面调用它的process函数
void AudioMixer::process(void* output)
{
mState.hook(&mState, output);//hook?难道是钩子函数?
}
晕乎,就这么简单的函数???
CTRL+左键,hook是一个函数指针啊,在哪里赋值的?具体实现函数又是哪个?
没办法了,只能分析AudioMixer类了。
4. AudioMixer
AudioMixer实现在framework/base/libs/audioflinger/AudioMixer.cpp中
AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
: mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate)
{
mState.enabledTracks= 0;
mState.needsChanged = 0;
mState.frameCount = frameCount;
mState.outputTemp = 0;
mState.resampleTemp = 0;
mState.hook = process__nop;//process__nop,是该类的静态函数
track_t* t = mState.tracks;
//支持32路混音。牛死了
for (int i=0 ; i<32 ; i++) {
t->needs = 0;
t->volume[0] = UNITY_GAIN;
t->volume[1] = UNITY_GAIN;
t->volumeInc[0] = 0;
t->volumeInc[1] = 0;
t->channelCount = 2;
t->enabled = 0;
t->format = 16;
t->buffer.raw = 0;
t->bufferProvider = 0;
t->hook = 0;
t->resampler = 0;
t->sampleRate = mSampleRate;
t->in = 0;
t++;
}
}
//其中,mState是在AudioMixer.h中定义的一个数据结构
//注意,source insight没办法解析这个mState,因为....见下面的注释。
struct state_t {
uint32_t enabledTracks;
uint32_t needsChanged;
size_t frameCount;
mix_t hook;
int32_t *outputTemp;
int32_t *resampleTemp;
int32_t reserved[2];
track_t tracks[32];// __attribute__((aligned(32)));《--把这里注释掉
//否则source insight会解析不了这个state_t类型
};
int mActiveTrack;
uint32_t mTrackNames;//names?搞得像字符串,实际是一个int
const uint32_t mSampleRate;
state_t mState
好了,没什么吗。hook对应的可选函数实现有:
process__validate
process__nop
process__genericNoResampling
process__genericResampling
process__OneTrack16BitsStereoNoResampling
process__TwoTracks16BitsStereoNoResampling
AudioMixer构造的时候,hook是process__nop,有几个地方会改变这个函数指针的指向。
这部分涉及到数字音频技术,我就无力讲解了。我们看看最接近的函数
process__OneTrack16BitsStereoNoResampling
void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
{
单track,16bit双声道,不需要重采样,大部分是这种情况了
const int i = 31 - __builtin_clz(state->enabledTracks);
const track_t& t = state->tracks[i];
AudioBufferProvider::Buffer& b(t.buffer);
int32_t* out = static_cast
size_t numFrames = state->frameCount;
const int16_t vl = t.volume[0];
const int16_t vr = t.volume[1];
const uint32_t vrl = t.volumeRL;
while (numFrames) {
b.frameCount = numFrames;
//获得buffer
t.bufferProvider->getNextBuffer(&b);
int16_t const *in = b.i16;
size_t outFrames = b.frameCount;
if UNLIKELY--->不走这.
else {
do {
//计算音量等数据,和数字音频技术有关。这里不说了
uint32_t rl = *reinterpret_cast
in += 2;
int32_t l = mulRL(1, rl, vrl) >> 12;
int32_t r = mulRL(0, rl, vrl) >> 12;
*out++ = (r<<16) | (l & 0xFFFF);
} while (--outFrames);
}
numFrames -= b.frameCount;
//释放buffer。
t.bufferProvider->releaseBuffer(&b);
}
}
好像挺简单的啊,不就是把数据处理下嘛。这里注意下buffer。到现在,我们还没看到取共享内存里AT端write的数据呐。
那只能到bufferProvider去看了。
注意,这里用的是AudioBufferProvider基类,实际的对象是Track。它从AudioBufferProvider派生。
我们用得是PlaybackThread的这个Track
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
//一阵暗喜吧。千呼万唤始出来,终于见到cblk了
audio_track_cblk_t* cblk = this->cblk();
uint32_t framesReady;
uint32_t framesReq = buffer->frameCount;
//哈哈,看看数据准备好了没,
framesReady = cblk->framesReady();
if (LIKELY(framesReady)) {
uint32_t s = cblk->server;
uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
if (framesReq > framesReady) {
framesReq = framesReady;
}
if (s + framesReq > bufferEnd) {
framesReq = bufferEnd - s;
}
获得真实的数据地址
buffer->raw = getBuffer(s, framesReq);
if (buffer->raw == 0) goto getNextBuffer_exit;
buffer->frameCount = framesReq;
return NO_ERROR;
}
getNextBuffer_exit:
buffer->raw = 0;
buffer->frameCount = 0;
return NOT_ENOUGH_DATA;
}
再看看释放缓冲的地方:releaseBuffer,这个直接在ThreadBase中实现了
void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
buffer->raw = 0;
mFrameCount = buffer->frameCount;
step();
buffer->frameCount = 0;
}
看看step吧。mFrameCount表示我已经用完了这么多帧。
bool AudioFlinger::ThreadBase::TrackBase::step() {
bool result;
audio_track_cblk_t* cblk = this->cblk();
result = cblk->stepServer(mFrameCount);//哼哼,调用cblk的stepServer,更新
服务端的使用位置
return result;
}
到这里,大伙应该都明白了吧。原来AudioTrack中write的数据,最终是这么被使用的呀!!!
恩,看一个process__OneTrack16BitsStereoNoResampling不过瘾,再看看
process__TwoTracks16BitsStereoNoResampling。
void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void*
output)
int i;
uint32_t en = state->enabledTracks;
i = 31 - __builtin_clz(en);
const track_t& t0 = state->tracks[i];
AudioBufferProvider::Buffer& b0(t0.buffer);
en &= ~(1<
i = 31 - __builtin_clz(en);
const track_t& t1 = state->tracks[i];
AudioBufferProvider::Buffer& b1(t1.buffer);
int16_t const *in0;
const int16_t vl0 = t0.volume[0];
const int16_t vr0 = t0.volume[1];
size_t frameCount0 = 0;
int16_t const *in1;
const int16_t vl1 = t1.volume[0];
const int16_t vr1 = t1.volume[1];
size_t frameCount1 = 0;
int32_t* out = static_cast
size_t numFrames = state->frameCount;
int16_t const *buff = NULL;
while (numFrames) {
if (frameCount0 == 0) {
b0.frameCount = numFrames;
t0.bufferProvider->getNextBuffer(&b0);
if (b0.i16 == NULL) {
if (buff == NULL) {
buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
}
in0 = buff;
b0.frameCount = numFrames;
} else {
in0 = b0.i16;
}
frameCount0 = b0.frameCount;
}
if (frameCount1 == 0) {
b1.frameCount = numFrames;
t1.bufferProvider->getNextBuffer(&b1);
if (b1.i16 == NULL) {
if (buff == NULL) {
buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
}
in1 = buff;
b1.frameCount = numFrames;
} else {
in1 = b1.i16;
}
frameCount1 = b1.frameCount;
}
size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
numFrames -= outFrames;
frameCount0 -= outFrames;
frameCount1 -= outFrames;
do {
int32_t l0 = *in0++;
int32_t r0 = *in0++;
l0 = mul(l0, vl0);
r0 = mul(r0, vr0);
int32_t l = *in1++;
int32_t r = *in1++;
l = mulAdd(l, vl1, l0) >> 12;
r = mulAdd(r, vr1, r0) >> 12;
// clamping...
l = clamp16(l);
r = clamp16(r);
*out++ = (r<<16) | (l & 0xFFFF);
} while (--outFrames);
if (frameCount0 == 0) {
t0.bufferProvider->releaseBuffer(&b0);
}
if (frameCount1 == 0) {
t1.bufferProvider->releaseBuffer(&b1);
}
}
if (buff != NULL) {
delete [] buff;
}
}
看不懂了吧??哈哈,知道有这回事就行了,专门搞数字音频的需要好好研究下了!
三 再论共享audio_track_cblk_t
为什么要再论这个?因为我在网上找了下,有人说audio_track_cblk_t是一个环形buffer,环形buffer是什么意思?自己查查!
这个吗,和我之前的工作经历有关系,某BOSS费尽心机想搞一个牛掰掰的环形buffer,搞得我累死了。现在audio_track_cblk_t是环形buffer?我倒是想看看它是怎么实现的。
顺便我们要解释下,audio_track_cblk_t的使用和我之前说的Lock,读/写,Unlock不太一样。为何?
l 第一因为我们没在AF代码中看到有缓冲buffer方面的wait,MixThread只有当没有数据的时候会usleep一下。
l 第二,如果有多个track,多个audio_track_cblk_t的话,假如又是采用wait信号的办法,那么由于pthread库缺乏WaitForMultiObjects的机制,那么到底该等哪一个?这个问题是我们之前在做跨平台同步库的一个重要难题。
1. 写者的使用
我们集中到audio_track_cblk_t这个类,来看看写者是如何使用的。写者就是AudioTrack端,在这个类中,叫user
l framesAvailable,看看是否有空余空间
l buffer,获得写空间起始地址
l stepUser,更新user的位置。
2. 读者的使用
读者是AF端,在这个类中加server。
l framesReady,获得可读的位置
l stepServer,更新读者的位置
看看这个类的定义:
struct audio_track_cblk_t
{
Mutex lock; //同步锁
Condition cv;//CV
volatile uint32_t user;//写者
volatile uint32_t server;//读者
uint32_t userBase;//写者起始位置
uint32_t serverBase;//读者起始位置
void* buffers;
uint32_t frameCount;
// Cache line boundary
uint32_t loopStart; //循环起始
uint32_t loopEnd; //循环结束
int loopCount;
uint8_t out; //如果是Track的话,out就是1,表示输出。
}
注意这是volatile,跨进程的对象,看来这个volatile也是可以跨进程的嘛。
l 唉,又要发挥下了。volatile只是告诉编译器,这个单元的地址不要cache到CPU的缓冲中。也就是每次取值的时候都要到实际内存中去读,而且可能读内存的时候先要锁一下总线。防止其他CPU核执行的时候同时去修改。由于是跨进程共享的内存,这块内存在两个进程都是能见到的,又锁总线了,又是同一块内存,volatile当然保证了同步一致性。
l loopStart和loopEnd这两个值是表示循环播放的起点和终点的,下面还有一个loopCount吗,表示循环播放次数的
那就分析下吧。
先看写者的那几个函数
4 写者分析
先用frameavail看看当前剩余多少空间,我们可以假设是第一次进来嘛。读者还在那sleep呢。
uint32_t audio_track_cblk_t::framesAvailable()
{
Mutex::Autolock _l(lock);
return framesAvailable_l();
}
int32_t audio_track_cblk_t::framesAvailable_l()
{
uint32_t u = this->user; 当前写者位置,此时也为0
uint32_t s = this->server; //当前读者位置,此时为0
if (out) { out为1
uint32_t limit = (s < loopStart) ? s : loopStart;
我们不设循环播放时间吗。所以loopStart是初始值INT_MAX,所以limit=0
return limit + frameCount - u;
//返回0+frameCount-0,也就是全缓冲最大的空间。假设frameCount=1024帧
}
}
然后调用buffer获得其实位置,buffer就是得到一个地址位置。
void* audio_track_cblk_t::buffer(uint32_t offset) const
{
return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
}
完了,我们更新写者,调用stepUser
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
{
//framecount,表示我写了多少,假设这一次写了512帧
uint32_t u = this->user;//user位置还没更新呢,此时u=0;
u += frameCount;//u更新了,u=512
// Ensure that user is never ahead of server for AudioRecord
if (out) {
//没甚,计算下等待时间
}
//userBase还是初始值为0,可惜啊,我们只写了1024的一半
//所以userBase加不了
if (u >= userBase + this->frameCount) {
userBase += this->frameCount;
//但是这句话很重要,userBase也更新了。根据buffer函数的实现来看,似乎把这个
//环形缓冲铺直了....连绵不绝。
}
this->user = u;//喔,user位置也更新为512了,但是useBase还是0
return u;
}
好了,假设写者这个时候sleep了,而读者起来了。
5 读者分析
uint32_t audio_track_cblk_t::framesReady()
{
uint32_t u = this->user; //u为512
uint32_t s = this->server;//还没读呢,s为零
if (out) {
if (u < loopEnd) {
return u - s;//loopEnd也是INT_MAX,所以这里返回512,表示有512帧可读了
} else {
Mutex::Autolock _l(lock);
if (loopCount >= 0) {
return (loopEnd - loopStart)*loopCount + u - s;
} else {
return UINT_MAX;
}
}
} else {
return s - u;
}
}
使用完了,然后stepServer
bool audio_track_cblk_t::stepServer(uint32_t frameCount)
{
status_t err;
err = lock.tryLock();
uint32_t s = this->server;
s += frameCount; //读了512帧了,所以s=512
if (out) {
}
没有设置循环播放嘛,所以不走这个
if (s >= loopEnd) {
s = loopStart;
if (--loopCount == 0) {
loopEnd = UINT_MAX;
loopStart = UINT_MAX;
}
}
//一样啊,把环形缓冲铺直了
if (s >= serverBase + this->frameCount) {
serverBase += this->frameCount;
}
this->server = s; //server为512了
cv.signal(); //读者读完了。触发下写者吧。
lock.unlock();
return true;
}
6 真的是环形缓冲吗?
环形缓冲是这样一个场景,现在buffer共1024帧。
假设:
l 写者先写到1024帧
l 读者读到512帧
l 那么,写者还可以从头写512帧。
所以,我们得回头看看frameavail是不是把这512帧算进来了。
uint32_t audio_track_cblk_t::framesAvailable_l()
{
uint32_t u = this->user; //1024
uint32_t s = this->server;//512
if (out) {
uint32_t limit = (s < loopStart) ? s : loopStart;
return limit + frameCount - u;返回512,用上了!
}
}
再看看stepUser这句话
if (u >= userBase + this->frameCount) {u为1024,userBase为0,frameCount为1024
userBase += this->frameCount;//好,userBase也为1024了
}
看看buffer
return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
//offset是外界传入的基于user的一个偏移量。offset-userBase,得到的正式从头开始的那段数据空间。太牛了!
农历新年快来到了,我希望能把android这个系列写完。
今天这篇帖子是农历年最后一篇了。大家如果有什么想了解的,可以回帖或者站内短消息,我们共同来分析Android。
一目的
上回我们说了AudioFlinger(AF),总感觉代码里边有好多东西没说清楚,心里发毛。就看了看AF的流程,我们敢说自己深入了解了Android系统吗?AudioPolicyService(APS)是个什么东西?为什么要有它的存在?下层的Audio HAL层又是怎么结合到Android中来的?更有甚者,问个实在问题:插入耳机后,声音又怎么从最开始的外放变成从耳机输出了?调节音量的时候到底是调节Music的还是调节来电音量呢?这些东西,我们在AF的流程中统统都没讲到。但是这些他们又是至关重要的。从我个人理解来看,策略(Policy)比流程更复杂和难懂。
当然,遵循我们的传统分析习惯,得有一个切入点,否则我们都不知道从何入手了。
这里的切入点将是:
l AF和APS系统第一次起来后,到底干了什么。
l 检测到耳机插入事件后,AF和APS的处理。
大家跟着我一步步来看,很快就发现,啊哈,APS也不是那么难嘛。
另外,这次代码分析的格式将参考《Linux内核情景分析》的样子,函数调用的解析将采用深度优先的办法,即先解释所调用的函数,然后再出来继续讲。
我曾经数度放弃分析APS,关键原因是我没找到切入点,只知道代码从头看到尾!
二 AF和APS的诞生
这个东西,已经说得太多了。在framework\base\media\MediaServer\Main_MediaServer中。
我们看看。
int main(int argc, char** argv)
{
sp
sp
//先创建AF
AudioFlinger::instantiate();
//再创建APS
AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
前面说过,instantiate内部会实例化一个对象,那直接看AF的构造函数。
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),//基类构造函数
mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
{
注意mAudioHardware和mNextThreadId
mHardwareStatus = AUDIO_HW_IDLE;
//创建audio的HAL代表
mAudioHardware = AudioHardwareInterface::create();
mHardwareStatus = AUDIO_HW_INIT;
//下面这些不至于会使用APS吧?APS还没创建呢!
if (mAudioHardware->initCheck() == NO_ERROR) {
setMode(AudioSystem::MODE_NORMAL);
setMasterVolume(1.0f);
setMasterMute(false);
}
}
感觉上,AF的构造函数就是创建了一个最重要的AudioHardWare的HAL代表。
其他好像是没干什么策略上的事情。
不过:AF创建了一个AudioHardware的HAL对象。注意整个系统就这一个AudioHardware了。也就是说,不管是线控耳机,蓝牙耳机,麦克,外放等等,最后都会由这一个HAL统一管理。
再看APS吧。
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpPolicyManager(NULL)
{
// mpPolicyManager?策略管理器?可能很重要
char value[PROPERTY_VALUE_MAX];
// TonePlayback?播放铃声的?为什么放在这里?以后来看看
mTonePlaybackThread = new AudioCommandThread(String8(""));
// Audio Command?音频命令?看到Command,我就想到设计模式中的Command模式了
//Android尤其是MediaPlayerService中大量使用了这种模式。
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
//注意AudioPolicyManagerBase的构造函数,把this传进去了。
mpPolicyManager = new AudioPolicyManagerBase(this);
//先假设我们使用Generic的Audio设备吧。
#else
...
#endif
// 根据系统属性来判断摄像机是否强制使用声音。这个...为什么会放在这里?
//手机带摄像机好像刚出来的时候,为了防止偷拍,强制按快门的时候必须发出声音
//就是这个目的吧?
property_get("ro.camera.sound.forced", value, "0");
mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
}
so easy!,不至于吧?我们不应该放过任何一个疑问!这么多疑问,先看哪个呢?这里分析的是Audio Policy,而构造函数中又创建了一个AudioPolicyManagerBase,而且不同厂商还可以实现自己的AudioPolicyManager,看来这个对于音频策略有至关重要的作用了。
不得不说的是,Android代码中的这些命名在关键地方上还是比较慎重和准确的。
另外,AudioPolicyManagerBase的构造函数可是把APS传进去了,看来又会有一些回调靠APS了。真绕。
代码位置在framework\base\libs\audioflinger\AudioPolicyManagerBase.cpp中
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
:
mPhoneState(AudioSystem::MODE_NORMAL), ---->这里有电话的状态?
mRingerMode(0),
mMusicStopTime(0),
mLimitRingtoneVolume(false)
{
[--->mPhoneState(AudioSystem::MODE_NORMAL)]
AudioSystem其实是窥视Android如何管理音频系统的好地方。位置在
framework\base\include\media\AudioSystem.h中,定义了大量的枚举之类的东西来表达Google对音频系统的看法。我们只能见招拆招了。
下面是audio_mode的定义。这里要注意一个地方:
这些定义都和SDK中的JAVA层定义类似。实际上应该说先有C++层的定义,然后再反映到JAVA层中。但是C++层的定义一般没有解释说明,而SDK中有。所以我们不能不面对的一个痛苦现实就是:常常需要参考SDK的说明才能搞明白到底是什么。
关于C++的AudioSystem这块,SDK的说明在AudioManager中。
enum audio_mode {
//解释参考SDK说明,以下不再说明
MODE_INVALID = -2, //无效mode
MODE_CURRENT = -1,//当前mode,和音频设备的切换(路由)有关
MODE_NORMAL = 0,//正常mode,没有电话和铃声
MODE_RINGTONE,//收到来电信号了,此时会有铃声
MODE_IN_CALL,//电话mode,这里表示已经建立通话了
NUM_MODES // Android大量采用这种技巧来表示枚举结束了。
};
好,继续:
...
mPhoneState(AudioSystem::MODE_NORMAL), ---->这里有电话的状态?
mRingerMode(0),
mMusicStopTime(0),
mLimitRingtoneVolume(false)
{
mpClientInterface = clientInterface;//BT,保存APS对象。
//forceUse?这是个什么玩意儿?
for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
mForceUse[i] = AudioSystem::FORCE_NONE;
}
[---->AudioSystem::FORCE_NONE和AudioSystem::NUM_FORCE_USE]
注意,这里有两个枚举,太无耻了。先看看FORCE_NONE这个
enum forced_config {强制_配置,看名字好像是强制使用设备吧,比如外放,耳机,蓝牙等
FORCE_NONE,
FORCE_SPEAKER,
FORCE_HEADPHONES,
FORCE_BT_SCO,
FORCE_BT_A2DP,
FORCE_WIRED_ACCESSORY,
FORCE_BT_CAR_DOCK,
FORCE_BT_DESK_DOCK,
NUM_FORCE_CONFIG,
FORCE_DEFAULT = FORCE_NONE //这个,太无聊了。
};
再看看AudioSystem::NUM_FORCE_USE这个
enum force_use {
FOR_COMMUNICATION,//这里是for_xxx,不是force_xxx。
FOR_MEDIA,
FOR_RECORD,
FOR_DOCK,
NUM_FORCE_USE
};
不懂,两个都不懂。为何?能猜出来什么吗?也不行。因为我们没找到合适的场景!那好吧,我们去SDK找找。恩
我看到AudioManager这个函数setSpeakerphoneOn (boolean on)。好吧,我
这么调用
setSpeakerphoneOn(true),看看实现。
这次我没再浪费时间了,我用一个新的工具coolfind,把搜索framework目录,寻找*.java文件,匹配字符串setSpeakerphone。终于,我在
framework/base/media/java/android/media/AudioService.java中找到了。
public void setSpeakerphoneOn(boolean on){
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
if (on) {
//看到这里,是不是明白十之八九了?下面这个调用是:
//强制通话使用speaker!原来是这么个意思!
AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION,
AudioSystem.FORCE_SPEAKER);
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
} else {
AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION,
AudioSystem.FORCE_NONE);
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
}
好了,说点题外话,既然Android源码都放开给我们了,有什么理由我们不去多搜搜呢?上网google也是搜,查源代码也是一样吗。不过我们要有目的:就是找到一个合适的使用场景。
force_use和force_config就不用我再解释了吧?
[--->AudioPolicyManagerBase::AudioPolicyManagerBase]
...
//下面这个意思就是把几种for_use的情况使用的设备全部置为NONE。
//比如设置FOR_MEDIA的场景,使用的设备就是FORCE_NONE
for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
mForceUse[i] = AudioSystem::FORCE_NONE;
}
// 目前可以的输出设备,耳机和外放
mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
AudioSystem::DEVICE_OUT_SPEAKER;
//目前可用的输入设备,内置MIC
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
又得来看看AudioSystem是怎么定义输入输出设备的了。
[--->mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE]
enum audio_devices {
// output devices
DEVICE_OUT_EARPIECE = 0x1,
DEVICE_OUT_SPEAKER = 0x2,
DEVICE_OUT_WIRED_HEADSET = 0x4,
DEVICE_OUT_WIRED_HEADPHONE = 0x8,
DEVICE_OUT_BLUETOOTH_SCO = 0x10,
DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
DEVICE_OUT_AUX_DIGITAL = 0x400,
DEVICE_OUT_DEFAULT = 0x8000,
DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER |
DEVICE_OUT_WIRED_HEADSET | DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),
DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
// input devices
DEVICE_IN_COMMUNICATION = 0x10000,
DEVICE_IN_AMBIENT = 0x20000,
DEVICE_IN_BUILTIN_MIC = 0x40000,
DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,
DEVICE_IN_WIRED_HEADSET = 0x100000,
DEVICE_IN_AUX_DIGITAL = 0x200000,
DEVICE_IN_VOICE_CALL = 0x400000,
DEVICE_IN_BACK_MIC = 0x800000,
DEVICE_IN_DEFAULT = 0x80000000,
DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT |
DEVICE_IN_BUILTIN_MIC |DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET |
DEVICE_IN_AUX_DIGITAL | DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC |
DEVICE_IN_DEFAULT)
};
一些比较容易眼花的东西我标成红色的了。这么多东西,不过没什么我们不明白的了。
得嘞,继续走。
[--->AudioPolicyManagerBase::AudioPolicyManagerBase]
// 目前可以的输出设备,又有耳机又有外放,配置很强悍啊。
//注意这里是OR操作符,最终mAvailableOutputDevices = 0X3
mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
AudioSystem::DEVICE_OUT_SPEAKER;
//目前可用的输入设备,内置MIC,mAvailableInputDevices为0x4000,不过我们不关注input
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
...
下面东西就很少了,我们一气呵成。
//创建一个AudioOutputDescriptor,并设置它的device为外设0x2
AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
//调用APS的openOutput,得到一个mHardwareOutput东东。这是个int型
//不过保不准是一个指针也不一定喔。
//而且,下面的参数都是指针类型(flags除外),难道?有人会改value吗?
mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
//这个...估计是把int和指针加入到一个map了,方便管理。
addOutput(mHardwareOutput, outputDesc);
//不知道干嘛,待会看。
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
//不知道干嘛,待会看。
updateDeviceForStrategy();
好了,上面还有一系列函数,等着我们调用呢。我们一个一个看。
提前说一下,这块可是AudioManagerBase的核心喔。
[---->AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor()]
AudioOutputDescriptor是个什么?我不是神,我也得看注释。
// descriptor for audio outputs. Used to maintain current configuration of each opened audio output
// and keep track of the usage of this output by each audio stream type.
明白了么?大概意思就是它,是这么一个东西:
l 描述audio输出的,可以用来保存一些配置信息。
l 跟踪音频stream类型使用这个output的一些情况。
没明白吧?以后碰到场景就明白了。
它的构造函数干了如下勾当:
AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
: mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
{}
//很好,统统都置零了。上面这些东西不用我解释了吧?命名规则也可以看出来。
OK,go on.
[--->mHardwareOutput = mpClientInterface->openOutput()]:
这里调用的是APS的openOutput,看看去:
[--->AudioPolicyService::openOutput]
audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
AudioSystem::output_flags flags)
{
sp
//娘希匹,搞到AF去了
return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels,
pLatencyMs, flags);
}
[----->AudioFlinger::openOutput]
int AudioFlinger::openOutput(uint32_t *pDevices,
uint32_t *pSamplingRate,
uint32_t *pFormat,
uint32_t *pChannels,
uint32_t *pLatencyMs,
uint32_t flags)
{
//我们思考下传进来的值吧
//*pDevices=0x2,代表外放
//其他都是0。 嘿嘿,有了值,这不就知道下面该怎么走了吗?
status_t status;
PlaybackThread *thread = NULL;
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
uint32_t format = pFormat ? *pFormat : 0;
uint32_t channels = pChannels ? *pChannels : 0;
uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
Mutex::Autolock _l(mLock);
//HAL对象得到一个AudioStreamOut,传进去的值会改吗?
AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
(int *)&format,
&channels,
&samplingRate,
&status);
mHardwareStatus = AUDIO_HW_IDLE;
if (output != 0) {
//走哪个分支?我把答案告诉大家吧。
//刚才那个mAudioHardware->openOutputStream确实会更改指针对应的value。
//当然,我们说了,AF使用的是GENERIC的Audio硬件。大家有兴趣可以去看看它的实现。
//我待会再贴出它的内容。反正到这里。
//那几个值变成:format为PCM_16_BIT,channels为2,samplingRate为44100
//这样的话,那只能走else分支了。
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, ++mNextThreadId);
} else {
//还记得前两节分析的同学,看到这里是不是明白了?恩,原来
//open一个Output,就会在AF中创建一个混音线程。设计得真好。
//想象下,所有设置为外放的程序,它的输出都是这个外放stream混音线程来工作
//所有设置为耳机的程序,它的输出都是这个耳机stream混音线程来完成。
//为什么对stream特加强调呢,没看见
//我们调用的是mAudioHardware->openOutputStream(0x2,,,)嘛。返回的
//是一个AudioStreamOut,可不是设备喔。Android把这些个东西都交给HAL层去实现了。
//不用自己来管理系统上有什么耳机,外设,蓝牙真实设备之类的东东,它反正用AudioStreamOut来表示它想要的就可以了。例如Generic的Audio Hal只支持一个OutputStream。--> only my opinion
thread = new MixerThread(this, output, ++mNextThreadId);
}
//好了,又多得了一个线程,
mPlaybackThreads.add(mNextThreadId, thread);
if (pSamplingRate) *pSamplingRate = samplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = channels;
if (pLatencyMs) *pLatencyMs = thread->latency();
//从这里返回的是混音线程的索引。
return mNextThreadId;
}
return 0;//如果没创建成功线程,则返回零。
}
好,我们回到AudioManagerBase中。
[--->AudioPolicyManagerBase::AudioPolicyManagerBase]
mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
//上面实际就返回一个线程index。我有点疑惑,难道APS就只这么一个实际是线程index的东西就就行了吗?虽然它把这个index当成hardware的标识了。
//这个...估计是把int和指针加入到一个map了,方便管理。不看了。
addOutput(mHardwareOutput, outputDesc);
//不知道干嘛,待会看。
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
[--->setOutputDevice(mHardwareOutput,...)]
这个函数,很重要!另外,再传点技巧。不要老在source insight中后退后退了,直接找到window菜单,里边列出了最近打开的文件,找到我们的AudioManagerBase.cpp,不就行了吗?
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
{
//注意我们的参数:
// output = 1,
//device为AudioSystem::DEVICE_OUT_SPEAKER
// force为true,delayMs用默认值0
//map吧?刚才通过addOutput已经加进去了
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
if (outputDesc->isDuplicated()) {
setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
return;
}
//还记得addOutput前设置的device吗?对了,为0X3,外放|耳机
uint32_t prevDevice = (uint32_t)outputDesc->device();
现在设置的是外设,
if ((device == 0 || device == prevDevice) && !force) {
return;
}
//喔,设置这个outputDesc为外放
outputDesc->mDevice = device;
popCount为2,因为device=0x2=0010
//另外,我对下面这个output== mHardwareOutput尤其感兴趣。还记得我们刚才的疑问吗?
// mHardwareOutput实际上是AF返回的一个线程索引,那AMB怎么根据这样一个东西来
//管理所有的线程呢?果然,这里就比较了output是不是等于最初创建的线程索引
//这就表明。虽然只有这么一个mHardwareOutput,但实际上还是能够操作其他output的!
if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
setStrategyMute(STRATEGY_MEDIA, true, output);
usleep(outputDesc->mLatency*2*1000);
}
// 晕,又冒出来一个AudioParameter,不过意思却很明白
//说我们要设置路由,新的输出设备为外放
//等我们以后讲由外放切换到耳机,再来看这个问题。
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)device);
mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
// update stream volumes according to new device
applyStreamVolumes(output, device, delayMs);
// if changing from a combined headset + speaker route, unmute media streams
if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
//这里说,把media的音量置为0。以后再说。
setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
}
}
好了,返回了。
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
这个调研,更新了mHardwareOutput对应的输出路由设备,而且还发了一个命令给APS,说你给我更新对应混音线程的输出路由设备。
[--->AudioPolicyManagerBase::AudioPolicyManagerBase]
.....
addOutput(mHardwareOutput, outputDesc);
setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER,
true);
//只剩下最后一个函数了
updateDeviceForStrategy();
[----->updateDeviceForStrategy()]
void AudioPolicyManagerBase::updateDeviceForStrategy()
{
for (int i = 0; i < NUM_STRATEGIES; i++) {
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
}
}
晕,又出来一个枚举。我们看看
[---->for (int i = 0; i < NUM_STRATEGIES; i++)]
NUM_STRATEGIES在hardware/libhardware_legacy/include/hardware_legacy/
AudioPolicyManagerBase.h中定义。
enum routing_strategy {
//好像很好理解
STRATEGY_MEDIA,
STRATEGY_PHONE,//通话音吗?
STRATEGY_SONIFICATION,//除了其他三个外的,可以是铃声,提醒声等。
STRATEGY_DTMF,//好像是拨号音
NUM_STRATEGIES
};
这个,反正我在SDK上没找到对应说明,我们待到以后看看会不会柳暗花明呢?
[----->getDeviceForStrategy((routing_strategy)i, false)]
看这个函数名的意思是,为各种策略找到它对应的设备。
uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
{
// fromCache为false
//放眼望去,这个函数好像涉及到很对策略方面的事情。
//我们大概讲解下,至于系统为什么要这么做,问Google吧。
uint32_t device = 0;
switch (strategy) {
case STRATEGY_DTMF:
if (mPhoneState != AudioSystem::MODE_IN_CALL) {
//如果在打电话过程中,你再按按键,则和MEDIA走一个设备
device = getDeviceForStrategy(STRATEGY_MEDIA, false);
break;
}
//注意这里没有break,所以在其他mode下,DTMF和PHONE用一个策略
case STRATEGY_PHONE:
//还得判断用户是不是强制使用了输出设备。
switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
case AudioSystem::FORCE_BT_SCO:
if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
device = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
if (device) break;
// if SCO device is requested but no SCO device is available, fall back to default
// case
// FALL THROUGH
//我们还记得强制设置那里吗?对了,此时都是FORCE_NONE
//而且,mAvailableOutputDevices是0X3 (外放|耳机)
default: // FORCE_NONE
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
if (device) break;
//看,下面这句会成立。啥意思?如果有耳机的话,那么输出设备就是耳机
//太正确了。实际手机是不是就是这样的呢?
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
break;
//再验证下我们刚才说的,如果强制使用外放的话,
case AudioSystem::FORCE_SPEAKER:
if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
//果然,会强制使用外放。
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
break;
}
break;
case STRATEGY_SONIFICATION://分析方法同上,我不说了。
if (mPhoneState == AudioSystem::MODE_IN_CALL) {
device = getDeviceForStrategy(STRATEGY_PHONE, false);
break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
// 同样没有break,说明SONIFICATION受MEDIA策略影响。
case STRATEGY_MEDIA: {
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
//可惜,上面那些高级设备我们都没有
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
}
//假设我们没有从SONIFICATION下来,那么device最终会= DEVICE_OUT_SPEAKER。
//假设我们从SONIFICATION下来,那么device还是等于DEVICE_OUT_SPEAKER
//奇怪,如果有耳机的话为何会走外放呢?普通耳机和线控耳机还能区分?
device |= device2;
} break;
default:
break;
}
return device;
}
好了,回到
[---->AudioPolicyManagerBase::updateDeviceForStrategy()]
void AudioPolicyManagerBase::updateDeviceForStrategy()
{
for (int i = 0; i < NUM_STRATEGIES; i++) {
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
}
}
这个函数完了,表明各种策略下使用的对应设备也准备好了。
真爽,一路回去,APS的构造就完了。
留个纪念:
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
{
....
updateDeviceForStrategy();
}
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpPolicyManager(NULL)
{
#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
mpPolicyManager = new AudioPolicyManagerBase(this);
LOGV("build for GENERIC_AUDIO - using generic audio policy");
...
#endif
property_get("ro.camera.sound.forced", value, "0");
mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
}
总结下吧,AF,APS都创建完了,得到什么了吗?下面按先后顺序说说。
l AF创建了一个代表HAL对象的东西
l APS创建了两个AudioCommandThread,一个用来处理命令,一个用来播放tone。我们还没看。
l APS同时会创建AudioManagerBase,做为系统默认的音频管理
l AMB集中管理了策略上面的事情,同时会在AF的openOutput中创建一个混音线程。同时,AMB会更新一些策略上的安排。
另外,我们分析的AMB是Generic的,但不同厂商可以实现自己的策略。例如我可以设置只要有耳机,所有类型声音都从耳机出。
上面关于AMB方面,我们还只是看了看它的代码,还没有一个实际例子来体会。