Android Audio 框架简读 <3>
2016-03-28 18:48
627 查看
上面一篇最后提到Binder,我个人感觉Android系统需要申请内存空间基本上都是Binder完成的,
可以基本认知Binder:Binder在linux层是专门有一个binder驱动层的,这个驱动层可以用于分配设备内存空间.它同时提供接口给其他设备使用,用于分配内存和管理内存,其他设备需要申请内存都需要实现它的接口,通过它提供的接口分配内存和管理内存.
Android Audio 录播都需要分配内存用于存放音频数据,android Audio这个分配是在AudioFlinger核心部分完成的,但是看了很多网上面的说分配内存有两个地方,一个可以是AudioTrack,一个才是AudioFlinger,但是我个人看了很多次这部分源代码,没有发现AudioTrack分配内存的地方,也搜索了关键字 : heap()->allocate ,但是个人觉得AudioTrack应该不可能去申请内存空间,因为数据的操作和使用都是由AudioFlinger完成的---个人观点.
下面看看代码,我个人总是感觉AudioTrack.cpp部分看的不多,因为他最终都是"赖给"AudioFlinger去做事了,所以可以直接从AudioFlinger开始:
从上面开始,这里面不涉及其他的,就看看内存分配,函数里面构造一个Track对象:
进入:这是一个PlaybackThread的内种类:
最终在这里:
无论isTimed是否成立,最终都会使用new Track(...)这个去构建.可以看下:
最后两行.
到Track还没完,因为最终完成是TrackBase,可以看一下Track的构造体:
最后一行出现了TrackBase,然后继续跟踪:
突然出现了一个client,怎么是它分配呢?回过头看看:
初始化:
(这里面要注意函数名:createTrack和createTrack_l,名字非常相近,不知道android是怎么想的哈).
经过上面的一番折腾内存空间就给分配了.
但是可能又会问,分配内存不是Binder去搞定吗,怎么没看出来呀.
我们把下面这一句话继续展开:
heap是谁的?看Client类:
那么MemoryDealer又是什么?allocate又是如何处理的?
终于到IMemory,只要多看一眼前面的createTrack等函数,就会发现它们构造体里面一直传递了一个IMemory的参数,不过这个传递进来的不是用来分配的,主要作用大概有两个:
<1> : 作为AudioTrack.cpp部分的回调;
<2> : 作为AudioTrack和AudioFlinger直接数据生产和消费,再就是协调数据进出有序的,FIFO.
继续allocate:
最后分配:
Allocation::Allocation(
const sp<MemoryDealer>& dealer,
const sp<IMemoryHeap>& heap, ssize_t offset, size_t size)
: MemoryBase(heap, offset, size), mDealer(dealer)
{
#ifndef NDEBUG
void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
memset(start_ptr, 0xda, size);
#endif
}
上面中的IMemoryHeap或者IMemory都是binder的接口,在IMemory.cpp文件中:大致有三点:
<1> : 前面程序会看到IMemory->Pointer:
这个是反馈分配的内存偏移量和内存大小,偏移量比较难理解的话,就可以勉强理解为分配的内存所在的位置.
<2> : Bn***,Bp***中的BpMemory 类:Bp全名:Binder Proxy : Binder代理
其实这东西一两句话说不完,我自己的理解就是binder有货(内存空间,且分配好了的),你要"拿货",你找binder的代理(商),不要直接去找binder,当然binder也没有时间搭理你,另外binder自身分配的时候也不是同步的,所以如果同时直接去操作binder,就不知道有什么后果了.
<3> : BnMemory :这也是个好同志,将数据写入内存中,网上称为服务器端,但个人认为它是最终可以与binder linux节点交互的部分,上面BpMemory最终也是要和BnMemory交互的,至于binder详细,可以阅读:framework/base/libs/binder/下面的源代码.
BnMemory::BnMemory() {
}
BnMemory::~BnMemory() {
}
status_t BnMemory::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case GET_MEMORY: {
CHECK_INTERFACE(IMemory, data, reply);
ssize_t offset;
size_t size;
reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
reply->writeInt32(offset);
reply->writeInt32(size);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
最后还扯一个东西:AudioTrack和AudioFlinger,他们两在使用同一个内存区里面的数据时,怎么协调,AudioTrack把播放的数据缓存到binder分配的内存中,AudioFlinger如何调用的到那个内存中的数据,首先要清楚一点的是:
binder分配的内存区,提供了接口可以让多个设备共同读写同一个内存区,也就是说AudioTrack和AudioFlinger虽然在不同的进程,但是可以通过binder机制共同使用同一片内存数据.但是使用过程中,就会有一个协调的工作,不能出现差错,不然声音要么延迟,缎带,要么重复播放.它主要靠下面的结构体实现数据FIFO的协调工作:
播放状态,播放位置(内存offset)...记录了播放过程中数据使用过程的所有状态和处理.这个地方千万要注意的是,AudioTrack和AudioFlinger两者通过传递这个结构实现数据同步的,读写内存本身是不能够同步进行的,需要人为去实现这个操作同步.
后面还有一个重采样的处理,以及后面的设备选择.
可以基本认知Binder:Binder在linux层是专门有一个binder驱动层的,这个驱动层可以用于分配设备内存空间.它同时提供接口给其他设备使用,用于分配内存和管理内存,其他设备需要申请内存都需要实现它的接口,通过它提供的接口分配内存和管理内存.
Android Audio 录播都需要分配内存用于存放音频数据,android Audio这个分配是在AudioFlinger核心部分完成的,但是看了很多网上面的说分配内存有两个地方,一个可以是AudioTrack,一个才是AudioFlinger,但是我个人看了很多次这部分源代码,没有发现AudioTrack分配内存的地方,也搜索了关键字 : heap()->allocate ,但是个人觉得AudioTrack应该不可能去申请内存空间,因为数据的操作和使用都是由AudioFlinger完成的---个人观点.
下面看看代码,我个人总是感觉AudioTrack.cpp部分看的不多,因为他最终都是"赖给"AudioFlinger去做事了,所以可以直接从AudioFlinger开始:
sp<IAudioTrack> AudioFlinger::createTrack( pid_t pid, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, pid_t tid, int *sessionId, status_t *status) {
从上面开始,这里面不涉及其他的,就看看内存分配,函数里面构造一个Track对象:
track = thread->createTrack_l(client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
进入:这是一个PlaybackThread的内种类:
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l
最终在这里:
if (!isTimed) { track = new Track(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, flags); } else { track = TimedTrack::create(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId); }
无论isTimed是否成立,最终都会使用new Track(...)这个去构建.可以看下:
AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( PlaybackThread *thread, const sp<Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId) : Track(thread, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, IAudioFlinger::TRACK_TIMED),
最后两行.
到Track还没完,因为最终完成是TrackBase,可以看一下Track的构造体:
AudioFlinger::PlaybackThread::Track::Track( PlaybackThread *thread, const sp<Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, IAudioFlinger::track_flags_t flags) : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId),
最后一行出现了TrackBase,然后继续跟踪:
if (client != NULL) { mCblkMemory = client->heap()->allocate(size);
突然出现了一个client,怎么是它分配呢?回过头看看:
sp<IAudioTrack> AudioFlinger::createTrack( pid_t pid, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, pid_t tid, int *sessionId, status_t *status) {
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
sp<Client> client;
初始化:
client = registerPid_l(pid);
(这里面要注意函数名:createTrack和createTrack_l,名字非常相近,不知道android是怎么想的哈).
经过上面的一番折腾内存空间就给分配了.
但是可能又会问,分配内存不是Binder去搞定吗,怎么没看出来呀.
我们把下面这一句话继续展开:
mCblkMemory = client->heap()->allocate(size);
heap是谁的?看Client类:
sp<MemoryDealer> heap() const;
那么MemoryDealer又是什么?allocate又是如何处理的?
virtual sp<IMemory> allocate(size_t size);
终于到IMemory,只要多看一眼前面的createTrack等函数,就会发现它们构造体里面一直传递了一个IMemory的参数,不过这个传递进来的不是用来分配的,主要作用大概有两个:
<1> : 作为AudioTrack.cpp部分的回调;
<2> : 作为AudioTrack和AudioFlinger直接数据生产和消费,再就是协调数据进出有序的,FIFO.
继续allocate:
sp<IMemory> MemoryDealer::allocate(size_t size) { sp<IMemory> memory; const ssize_t offset = allocator()->allocate(size); if (offset >= 0) { memory = new Allocation(this, heap(), offset, size); } return memory; }
最后分配:
Allocation::Allocation(
const sp<MemoryDealer>& dealer,
const sp<IMemoryHeap>& heap, ssize_t offset, size_t size)
: MemoryBase(heap, offset, size), mDealer(dealer)
{
#ifndef NDEBUG
void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
memset(start_ptr, 0xda, size);
#endif
}
上面中的IMemoryHeap或者IMemory都是binder的接口,在IMemory.cpp文件中:大致有三点:
<1> : 前面程序会看到IMemory->Pointer:
void* IMemory::pointer() const { ssize_t offset; sp<IMemoryHeap> heap = getMemory(&offset); void* const base = heap!=0 ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) return 0; return static_cast<char*>(base) + offset; } size_t IMemory::size() const { size_t size; getMemory(NULL, &size); return size; } ssize_t IMemory::offset() const { ssize_t offset; getMemory(&offset); return offset; }
这个是反馈分配的内存偏移量和内存大小,偏移量比较难理解的话,就可以勉强理解为分配的内存所在的位置.
<2> : Bn***,Bp***中的BpMemory 类:Bp全名:Binder Proxy : Binder代理
BpMemory::BpMemory(const sp<IBinder>& impl) : BpInterface<IMemory>(impl), mOffset(0), mSize(0) { } BpMemory::~BpMemory() { } sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const { if (mHeap == 0) { Parcel data, reply; data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp<IBinder> heap = reply.readStrongBinder(); ssize_t o = reply.readInt32(); size_t s = reply.readInt32(); if (heap != 0) { mHeap = interface_cast<IMemoryHeap>(heap); if (mHeap != 0) { mOffset = o; mSize = s; } } } } if (offset) *offset = mOffset; if (size) *size = mSize; return mHeap; }
其实这东西一两句话说不完,我自己的理解就是binder有货(内存空间,且分配好了的),你要"拿货",你找binder的代理(商),不要直接去找binder,当然binder也没有时间搭理你,另外binder自身分配的时候也不是同步的,所以如果同时直接去操作binder,就不知道有什么后果了.
<3> : BnMemory :这也是个好同志,将数据写入内存中,网上称为服务器端,但个人认为它是最终可以与binder linux节点交互的部分,上面BpMemory最终也是要和BnMemory交互的,至于binder详细,可以阅读:framework/base/libs/binder/下面的源代码.
BnMemory::BnMemory() {
}
BnMemory::~BnMemory() {
}
status_t BnMemory::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case GET_MEMORY: {
CHECK_INTERFACE(IMemory, data, reply);
ssize_t offset;
size_t size;
reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
reply->writeInt32(offset);
reply->writeInt32(size);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
最后还扯一个东西:AudioTrack和AudioFlinger,他们两在使用同一个内存区里面的数据时,怎么协调,AudioTrack把播放的数据缓存到binder分配的内存中,AudioFlinger如何调用的到那个内存中的数据,首先要清楚一点的是:
binder分配的内存区,提供了接口可以让多个设备共同读写同一个内存区,也就是说AudioTrack和AudioFlinger虽然在不同的进程,但是可以通过binder机制共同使用同一片内存数据.但是使用过程中,就会有一个协调的工作,不能出现差错,不然声音要么延迟,缎带,要么重复播放.它主要靠下面的结构体实现数据FIFO的协调工作:
// Important: do not add any virtual methods, including ~ struct audio_track_cblk_t { // The data members are grouped so that members accessed frequently and in the same context // are in the same line of data cache. Mutex lock; // sizeof(int) Condition cv; // sizeof(int) // next 4 are offsets within "buffers" volatile uint32_t user; volatile uint32_t server; uint32_t userBase; uint32_t serverBase; // if there is a shared buffer, "buffers" is the value of pointer() for the shared // buffer, otherwise "buffers" points immediately after the control block void* buffers; uint32_t frameCount; // Cache line boundary uint32_t loopStart; uint32_t loopEnd; // read-only for server, read/write for client int loopCount; // read/write for client // Channel volumes are fixed point U4.12, so 0x1000 means 1.0. // Left channel is in [0:15], right channel is in [16:31]. // Always read and write the combined pair atomically. // For AudioTrack only, not used by AudioRecord. private: uint32_t mVolumeLR; public: uint32_t sampleRate; // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for // 8 bit PCM data: in this case, mCblk->frameSize is based on a sample size of // 16 bit because data is converted to 16 bit before being stored in buffer // read-only for client, server writes once at initialization and is then read-only uint8_t frameSize; // would normally be size_t, but 8 bits is plenty uint8_t mName; // normal tracks: track name, fast tracks: track index // used by client only uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger uint16_t waitTimeMs; // Cumulated wait time, used by client only private: // client write-only, server read-only uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0 public: volatile int32_t flags; // Cache line boundary (32 bytes) // Since the control block is always located in shared memory, this constructor // is only used for placement new(). It is never used for regular new() or stack. audio_track_cblk_t(); uint32_t stepUser(uint32_t frameCount); // called by client only, where // client includes regular AudioTrack and AudioFlinger::PlaybackThread::OutputTrack bool stepServer(uint32_t frameCount); // called by server only void* buffer(uint32_t offset) const; uint32_t framesAvailable(); uint32_t framesAvailable_l(); uint32_t framesReady(); // called by server only bool tryLock(); // No barriers on the following operations, so the ordering of loads/stores // with respect to other parameters is UNPREDICTABLE. That's considered safe. // for AudioTrack client only, caller must limit to 0.0 <= sendLevel <= 1.0 void setSendLevel(float sendLevel) { mSendLevel = uint16_t(sendLevel * 0x1000); } // for AudioFlinger only; the return value must be validated by the caller uint16_t getSendLevel_U4_12() const { return mSendLevel; } // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000 void setVolumeLR(uint32_t volumeLR) { mVolumeLR = volumeLR; } // for AudioFlinger only; the return value must be validated by the caller uint32_t getVolumeLR() const { return mVolumeLR; } };
播放状态,播放位置(内存offset)...记录了播放过程中数据使用过程的所有状态和处理.这个地方千万要注意的是,AudioTrack和AudioFlinger两者通过传递这个结构实现数据同步的,读写内存本身是不能够同步进行的,需要人为去实现这个操作同步.
后面还有一个重采样的处理,以及后面的设备选择.
相关文章推荐
- 用Android中的Chronometer实现HH:MM:SS的显示
- 使用RecyclerView替代ListView
- .Net程序猿玩转Android开发---(8)表格布局TableLayout
- Android中管理多个Fragment的最佳实践,完美解决保存状态与重影问题(转)
- Android系统手机为什么卡?
- Android RecyclerView item没有match parent问题
- Android 把XML定义的控件动态实例化
- Android性能优化之渲染优化的8个点
- 【Android Framework】 Android Binder 机制解析
- android 动态加载sd卡的jar文件
- Qt配置Android环境
- ListView嵌套ListView
- Android ImageView深入理解
- Android-动画简介
- AS开发错误整理
- Gradle
- Android从入门到精通pdf+书源代码
- Android布局发现的小问题
- Crosswalk web引擎Android中的使用
- Android Intent的作用,哪些类型的数据可以被传递