您的位置:首页 > 移动开发 > Android开发

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开始:

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两者通过传递这个结构实现数据同步的,读写内存本身是不能够同步进行的,需要人为去实现这个操作同步.

后面还有一个重采样的处理,以及后面的设备选择.


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: