zoukankan      html  css  js  c++  java
  • 转---Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据

    引子

    Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放,目前Android的Froyo版本设定了同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数 据流。

    如何使用AudioTrack

    AudioTrack的主要代码位于 frameworks/base/media/libmedia/audiotrack.cpp中。现在先通过一个例子来了解一下如何使用 AudioTrack,ToneGenerator是android中产生电话拨号音和其他音调波形的一个实现,我们就以它为例子:

    ToneGenerator的初始化函数:

    [c-sharp] view plaincopy
    1. bool ToneGenerator::initAudioTrack() {  
    2.    // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size  
    3.     mpAudioTrack = new AudioTrack();  
    4.     mpAudioTrack->set(mStreamType,  
    5.                       0,  
    6.                       AudioSystem::PCM_16_BIT,  
    7.                       AudioSystem::CHANNEL_OUT_MONO,  
    8.                       0,  
    9.                       0,  
    10.                       audioCallback,  
    11.                       this,  
    12.                       0,  
    13.                       0,  
    14.                       mThreadCanCallJava);  
    15.     if (mpAudioTrack->initCheck() != NO_ERROR) {  
    16.         LOGE("AudioTrack->initCheck failed");  
    17.         goto initAudioTrack_exit;  
    18.     }  
    19.     mpAudioTrack->setVolume(mVolume, mVolume);  
    20.     mState = TONE_INIT;  
    21.     ......  
    22.  }  

    可见,创建步骤很简单,先new一个AudioTrack的实例,然后调用set成员函数完成参数的设置并注册到AudioFlinger中,然后 可以调用其他诸如设置音量等函数进一步设置音频参数。其中,一个重要的参数是audioCallback,audioCallback是一个回调函数,负 责响应AudioTrack的通知,例如填充数据、循环播放、播放位置触发等等。回调函数的写法通常像这样:

    [c-sharp] view plaincopy
    1. void ToneGenerator::audioCallback(int event, void* user, void *info) {  
    2.     if (event != AudioTrack::EVENT_MORE_DATA) return;  
    3.     AudioTrack::Buffer *buffer = static_cast<AudioTrack::Buffer *>(info);  
    4.     ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user);  
    5.     short *lpOut = buffer->i16;  
    6.     unsigned int lNumSmp = buffer->size/sizeof(short);  
    7.     const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc;  
    8.     if (buffer->size == 0) return;  
    9.   
    10.     // Clear output buffer: WaveGenerator accumulates into lpOut buffer  
    11.     memset(lpOut, 0, buffer->size);  
    12.     ......  
    13.     // 以下是产生音调数据的代码,略....  
    14. }  

    该函数首先判断事件的类型是否是EVENT_MORE_DATA,如果是,则后续的代码会填充相应的音频数据后返回,当然你可以处理其他事件,以下是可用的事件类型:

    [c-sharp] view plaincopy
    1. enum event_type {  
    2.         EVENT_MORE_DATA = 0,        // Request to write more data to PCM buffer.  
    3.         EVENT_UNDERRUN = 1,         // PCM buffer underrun occured.  
    4.         EVENT_LOOP_END = 2,         // Sample loop end was reached; playback restarted from loop start if loop count was not 0.  
    5.         EVENT_MARKER = 3,           // Playback head is at the specified marker position (See setMarkerPosition()).  
    6.         EVENT_NEW_POS = 4,          // Playback head is at a new position (See setPositionUpdatePeriod()).  
    7.         EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.  
    8.     };  

    开始播放:

    [c-sharp] view plaincopy
    1. mpAudioTrack->start();  

    停止播放:

    [c-sharp] view plaincopy
    1. mpAudioTrack->stop();  

    只要简单地调用成员函数start()和stop()即可。

    AudioTrack和AudioFlinger的通信机制

    通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。

    AudioFlinger是android中的一个service,在android启动时就已经被加载。下面这张图展示了他们两个的关系:

                                                                                  图一 AudioTrack和AudioFlinger的关系

    我们可以这样理解这张图的含义:

    • audio_track_cblk_t实现了一个环形FIFO;
    • AudioTrack是FIFO的数据生产者;
    • AudioFlinger是FIFO的数据消费者。

    建立联系的过程

    下面的序列图展示了AudioTrack和AudioFlinger建立联系的过程:

                                                                  图二 AudioTrack和AudioFlinger建立联系

    解释一下过程:

    • Framework或者Java层通过JNI,new AudioTrack();
    • 根据StreamType等参数,通过一系列的调用getOutput();
    • 如有必要,AudioFlinger根据StreamType打开不同硬件设备;
    • AudioFlinger为该输出设备创建混音线程: MixerThread(),并把该线程的id作为getOutput()的返回值返回给AudioTrack;
    • AudioTrack通过binder机制调用AudioFlinger的createTrack();
    • AudioFlinger注册该AudioTrack到MixerThread中;
    • AudioFlinger创建一个用于控制的TrackHandle,并以IAudioTrack这一接口作为createTrack()的返回值;
    • AudioTrack通过IAudioTrack接口,得到在AudioFlinger中创建的FIFO(audio_track_cblk_t);
    • AudioTrack创建自己的监控线程:AudioTrackThread;

    自此,AudioTrack建立了和AudioFlinger的全部联系工作,接下来,AudioTrack可以:

    • 通过IAudioTrack接口控制该音轨的状态,例如start,stop,pause等等;
    • 通过对FIFO的写入,实现连续的音频播放;
    • 监控线程监控事件的发生,并通过audioCallback回调函数与用户程序进行交互;

    FIFO的管理

     audio_track_cblk_t

    audio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相 应的内存,然后通过IMemory接口返回AudioTrack的,这样AudioTrack和AudioFlinger管理着同一个 audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO 中读取音频数据,经Mixer后送给AudioHardware进行播放。

    audio_track_cblk_t的主要数据成员:

        user             -- AudioTrack当前的写位置的偏移
        userBase     -- AudioTrack写偏移的基准位置,结合user的值方可确定真实的FIFO地址指针
        server          -- AudioFlinger当前的读位置的偏移
        serverBase  -- AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针

        frameCount -- FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节

        buffers         -- 指向FIFO的起始地址

        out               -- 音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0

    audio_track_cblk_t的主要成员函数:

    framesAvailable_l()和framesAvailable()用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别。

    [c-sharp] view plaincopy
    1. uint32_t audio_track_cblk_t::framesAvailable_l()  
    2. {  
    3.     uint32_t u = this->user;  
    4.     uint32_t s = this->server;  
    5.     if (out) {  
    6.         uint32_t limit = (s < loopStart) ? s : loopStart;  
    7.         return limit + frameCount - u;  
    8.     } else {  
    9.         return frameCount + u - s;  
    10.     }  
    11. }  

        

    framesReady()用于获取FIFO中可读取的空间大小。

    [c-sharp] view plaincopy
    1. uint32_t audio_track_cblk_t::framesReady()  
    2. {  
    3.     uint32_t u = this->user;  
    4.     uint32_t s = this->server;  
    5.     if (out) {  
    6.         if (u < loopEnd) {  
    7.             return u - s;  
    8.         } else {  
    9.             Mutex::Autolock _l(lock);  
    10.             if (loopCount >= 0) {  
    11.                 return (loopEnd - loopStart)*loopCount + u - s;  
    12.             } else {  
    13.                 return UINT_MAX;  
    14.             }  
    15.         }  
    16.     } else {  
    17.         return s - u;  
    18.     }  
    19. }  

    我们看看下面的示意图:

                   _____________________________________________

                   ^                          ^                             ^                           ^

            buffer_start              server(s)                 user(u)                  buffer_end

     很明显,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s

     可能有人会问,应为这是一个环形的buffer,一旦user越过了buffer_end以后,应该会发生下面的情况:

                    _____________________________________________

                   ^                ^             ^                                                     ^

            buffer_start     user(u)     server(s)                                   buffer_end

    这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。我们先看完下面三个函数的代码再分析:

    [c-sharp] view plaincopy
    1. uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)  
    2. {  
    3.     uint32_t u = this->user;  
    4.     u += frameCount;  
    5.     ......  
    6.     if (u >= userBase + this->frameCount) {  
    7.         userBase += this->frameCount;  
    8.     }  
    9.     this->user = u;  
    10.     ......  
    11.     return u;  
    12. }  
    [c-sharp] view plaincopy
    1. bool audio_track_cblk_t::stepServer(uint32_t frameCount)  
    2. {  
    3.     // the code below simulates lock-with-timeout  
    4.     // we MUST do this to protect the AudioFlinger server  
    5.     // as this lock is shared with the client.  
    6.     status_t err;  
    7.     err = lock.tryLock();  
    8.     if (err == -EBUSY) { // just wait a bit  
    9.         usleep(1000);  
    10.         err = lock.tryLock();  
    11.     }  
    12.     if (err != NO_ERROR) {  
    13.         // probably, the client just died.  
    14.         return false;  
    15.     }  
    16.     uint32_t s = this->server;  
    17.     s += frameCount;  
    18.     // 省略部分代码  
    19.      // ......  
    20.     if (s >= serverBase + this->frameCount) {  
    21.         serverBase += this->frameCount;  
    22.     }  
    23.     this->server = s;  
    24.     cv.signal();  
    25.     lock.unlock();  
    26.     return true;  
    27. }  
    [c-sharp] view plaincopy
    1. void* audio_track_cblk_t::buffer(uint32_t offset) const  
    2. {  
    3.     return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;  
    4. }  

    stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动 的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得 当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。根据这种算 法,user和server的值都可能大于FIFO的大小:framCount,那么,如何确定真正的写指针的位置呢?这里需要用到userBase这一 成员变量,在stepUser()中,每当user的值越过(userBase+frameCount),userBase就会增加 frameCount,这样,映射到FIFO中的偏移总是可以通过(user-userBase)获得。因此,获得当前FIFO的写地址指针可以通过成员 函数buffer()返回:

    p = mClbk->buffer(mclbk->user);

    在AudioTrack中,封装了两个函数:obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()获得当前可写的数量和写指针的位置,releaseBuffer()则在写入数据后被调用,它其实就是简单地调用 stepUser()来调整偏移的位置。

    IMemory接口

    在createTrack的过程中,AudioFlinger会根据传入的frameCount参数,申请一块内存,AudioTrack可以通过 IAudioTrack接口的getCblk()函数获得指向该内存块的IMemory接口,然后AudioTrack通过该IMemory接口的 pointer()函数获得指向该内存块的指针,这块内存的开始部分就是audio_track_cblk_t结构,紧接着是大小为frameSize的 FIFO内存。

    IMemory->pointer() ---->|_______________________________________________________

                                         |__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|

    看看AudioTrack的createTrack()的代码就明白了:

    [c-sharp] view plaincopy
    1. sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),  
    2.                                                       streamType,  
    3.                                                       sampleRate,  
    4.                                                       format,  
    5.                                                       channelCount,  
    6.                                                       frameCount,  
    7.                                                       ((uint16_t)flags) << 16,  
    8.                                                       sharedBuffer,  
    9.                                                       output,  
    10.                                                       &status);  
    11.     // 得到IMemory接口  
    12.     sp<IMemory> cblk = track->getCblk();                         
    13.     mAudioTrack.clear();  
    14.     mAudioTrack = track;  
    15.     mCblkMemory.clear();  
    16.     mCblkMemory = cblk;  
    17.     // 得到audio_track_cblk_t结构  
    18.     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());   
    19.     // 该FIFO用于输出      
    20.     mCblk->out = 1;                                              
    21.     // Update buffer size in case it has been limited by AudioFlinger during track creation  
    22.     mFrameCount = mCblk->frameCount;  
    23.     if (sharedBuffer == 0) {  
    24.        // 给FIFO的起始地址赋值  
    25.         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);  
    26.     } else {  
    27.         ..........          
    28.     }  
  • 相关阅读:
    自定义组件要加@click方法
    绑定样式
    647. Palindromic Substrings
    215. Kth Largest Element in an Array
    448. Find All Numbers Disappeared in an Array
    287. Find the Duplicate Number
    283. Move Zeroes
    234. Palindrome Linked List
    202. Happy Number
    217. Contains Duplicate
  • 原文地址:https://www.cnblogs.com/deman/p/4155051.html
Copyright © 2011-2022 走看看