Android Audio 框架简读 <3>

本文深入探讨了Android Audio框架中的内存分配,特别是AudioTrack和AudioFlinger如何在Binder的帮助下分配和管理内存。虽然一些资料指出AudioTrack可能分配内存,但作者认为实际操作是由AudioFlinger完成。在AudioFlinger的PlaybackThread内部,通过Track对象和TrackBase进行内存分配。IMemory接口在Binder通信中起到关键作用,协调AudioTrack和AudioFlinger的数据同步,确保FIFO机制正确运行,防止播放问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


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
































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值