Android音频系统的数据流向

Android音频系统是一个比较复杂的系统,从应用层到框架层、框架层通过JNINative层的运行时库。运行时库通过Binder和系统服务通信(与音频相关的系统服务是AudioFlingerAudioPolicyService),系统服务通过HAL层调用tinyalsa库,最终让驱动播放出声音。可以说音频系统涵盖了Android系统代码的全部层次,本文我们来从应用层开始分析音频系统中音频数据的流向。

因为Android系统的代码非常之庞大复杂,本人在研究Android的代码时一般都用编程里说的抽象,就是从一开始就确定自己要分析的重点,看代码的时候只关注自己要研究的点,其他的只要不影响思路的代码一律忽略。

我们从Android播放PCM码流的应用层例子来分析Android音频系统中音频数据的流向。

在应用层想要播放一个PCM文件,代码比较简单,主要的就三步

mAudioTrack = new AudioTrack()

mAudioTrack.play() //开始播放

mAudioTrack.write() //在播放线程里不停的write需要播放的音频数据

下面我们就来分析应用层的这三个简单的函数,Android系统都做了什么,才让声音播放出来的?

首先得看AudioTrack的构造函数

// frameworks\base\media\java\android\media\AudioTrack.java

整个构造函数最主要的工作就是native_setup()

native_setupJNI接口对应的函数是

// frameworks\base\core\jni\android_media_AudioTrack.cpp

static int android_media_AudioTrack_native_setup()

{

      //创建Native层的AudioTrack对象

      sp<AudioTrack> lpTrack = new AudioTrack();

     

      /*

      * AudioTrack中有MODE_STATICMODE_STREAM两种分类

      * STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack

      * STATIC的意思是一开始创建的时候就把音频数据一次性的放到一个固定的buffer,然后直接传给audiotrack

      * 本文只讲解MODE_STREAM这种模式的情况

      */

      //set就是整个函数最主要做的事情

    lpTrack->set(

        atStreamType,// stream type

        sampleRateInHertz,

        format,// word length, PCM

        nativeChannelMask,

        frameCount,

        AUDIO_OUTPUT_FLAG_NONE,

        audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)

        0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack

        0,// shared mem

        true,// thread can call Java

        sessionId);// audio session ID

}

 

// frameworks\av\media\libmedia\AudioTrack.cpp

status_t AudioTrack::set()

{

      //该函数获取声音的输出设备,就是我们解码后的数据需要往输出设备上写,这样才能播放得出来声音

      //该函数会通过Binder调用到系统服务AudioFlingerAudioPolicyService

      //因为本文的重点不在这里所以不详细讲解这个函数了

      AudioSystem::getOutput()

     

      //mCbf指向audioCallback函数

      mCbf = cbf;

     

      //因为cbf不为NULL,显然这里另起线程AudioTrackThread

      //下面我们来详细分析AudioTrackThread线程

      if (cbf != NULL) {

           mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

           mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);

      }

     

      //因为是MODE_STREAM模式,所以sharedBufferNULL

    status_t status = createTrack_l(streamType,

                                  sampleRate,

                                  format,

                                  frameCount,

                                  flags,

                                  sharedBuffer,

                                  output,

                                  0 /*epoch*/);

     

}

 

//下面来分析AudioTrackThread的主线程函数

bool AudioTrack::AudioTrackThread::threadLoop()

{

      //单从函数的命名来看,AudioTrackThread线程就是要不停的处理AudioBuffer

      //因为代码跑到这里的时候我们还没有开始播放声音,所以显然这个线程只是跑起来了,但是还需要等应用开始调用start后才开始真正干活

      //我们先不急着分析它,先往下看,后面在回来分析这个线程都做了什么。

      nsecs_t ns = mReceiver.processAudioBuffer(this);

}

 

status_t AudioTrack::createTrack_l()

{

      //通过Binder调用了系统服务AudioFlingercreateTrack方法

    sp<IAudioTrack> track = audioFlinger->createTrack(streamType,

                                                      sampleRate,

                                                      // AudioFlinger only sees 16-bit PCM

                                                      format == AUDIO_FORMAT_PCM_8_BIT ?

                                                              AUDIO_FORMAT_PCM_16_BIT : format,

                                                      mChannelMask,

                                                      frameCount,

                                                      &trackFlags,

                                                      sharedBuffer,

                                                      output,

                                                      tid,

                                                      &mSessionId,

                                                      mName,

                                                      mClientUid,

                                                      &status);

}

 

//从这里开始代码已经跑到AudioFlinger进程了,和原来的进程不是同一个进程了

sp<IAudioTrack> AudioFlinger::createTrack()

{

      //这个threadAudioFlinger进程里的PlaybackThread线程,专门用来播放声音的线程

      //本文重点不在研究PlaybackThread是怎么来的,只需要知道系统服务AudioFlinger进程里有好多线程在做不一样的事情

      //除了PlaybackThread播放线程外,还有录音线程等

      track = thread->createTrack_l(client, streamType, sampleRate, format,

                 channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);

}

 

sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l()

{

      //根据是否限时构造track对象

      if (!isTimed) {

           track = new Track(this, client, streamType, sampleRate, format,

                      channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);

      }

}

 

//我们接着分析Track的构造函数

AudioFlinger::PlaybackThread::Track::Track(……)

      :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,

            sessionId, uid, true /*isOut*/),

      ……

{

      //我们先来分析TrackBase的构造函数

      AudioFlinger::ThreadBase::TrackBase::TrackBase()

      {

           //如果播放模式是MODE_STATICsharedBuffer不为NULL;如果是MODE_STREAM,则sharedBufferNULL

           mCblkMemory = client->heap()->allocate(size);

           mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());

           mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);

          

           //原来存放音频的共享内存是在这里分配的,audio_track_cblk_t就是存放音频数据的结构体

           //在该构造函数中分配完内存后,Track中创建AudioTrackServerProxy对象时传递的参数MBuffer就不会为NULL了。

      }

 

      //因为是MODE_STREAM模式,sharedBuffer等于0 同时mCblk也不等于NULL

      mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, mFrameSize);

      //到这里已经基本清楚了,以申请到的audio_track_cblk_t结构当参数new出来的AudioTrackServerProxy是专门管理内存的

      //就是说AudioFlinger就是通过AudioTrackServerProxy对象来管理audio_track_cblk_t

}

 

//到这里为止应用层的mAudioTrack = new AudioTrack()代码就已经基本跑完了

//下面我们继续分析mAudioTrack.play()

// frameworks\base\media\java\android\media\AudioTrack.java

public void play() throws IllegalStateException {

      //调用JNI接口android_media_AudioTrack.cppandroid_media_AudioTrack_start

      native_start();

}

 

static void android_media_AudioTrack_start(JNIEnv *env, jobject thiz)

{

      sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);

     

      //调用AudioTrackstart方法

      lpTrack->start();

}

 

status_t AudioTrack::start()

{

      sp<AudioTrackThread> t = mAudioTrackThread;

     

      //显然之前暂停的AudioTrackThread线程开始启动了,运行mReceiver.processAudioBuffer(this)代码了

      t->resume();

     

      //mAudioTrack是之前在AudioTrack::createTrack_l()得到的IAudioTrack对象

      //显然这里通过Binder调用了AudioFlinger::PlaybackThread::Trackstart方法

      status = mAudioTrack->start();

}

 

status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event,

                                                    int triggerSession)

{

      PlaybackThread *playbackThread = (PlaybackThread *)thread.get();

      status = playbackThread->addTrack_l(this);

}

 

status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)

{

       broadcast_l();

}

 

void AudioFlinger::PlaybackThread::broadcast_l()

{

    mSignalPending = true;

    mWaitWorkCV.broadcast();

}

 

//显然mAudioTrack->start就只是给AudioFlingerPlaybackThread线程发送消息,让PlaybackThread线程开始播放

//我们分析PlaybackThread线程的主函数AudioFlinger::PlaybackThread::threadLoop就明白了

 

bool AudioFlinger::PlaybackThread::threadLoop()

{

      //因为受到mWaitWorkCV.broadcast,加上mSignalPending标志已经被志为true,所以线程进入播放循环

      threadLoop_mix();

      ssize_t ret = threadLoop_write();

}

 

//先看threadLoop_mix,从名字上看应该是混音处理

void AudioFlinger::MixerThread::threadLoop_mix()

{

      mAudioMixer->process(pts);

}

 

//mAudioMixer到底是什么鬼?

//我们到AudioFlingerMixerThread构造函数里一看就明白了

AudioFlinger::MixerThread::MixerThread()

{

      mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);

}

 

void AudioMixer::process()

{

      //hook是一个指针,我们暂时可以不去理会这个指针指向的函数是什么

      //先继续分析threadLoop_write

      mState.hook(&mState, pts);

}

 

ssize_t AudioFlinger::PlaybackThread::threadLoop_write()

{

      bytesWritten = mOutput->stream->write(mOutput->stream,

                                                                 mMixBuffer + offset, mBytesRemaining);

     

      //在明显不过了,该函数就是从mMixBuffer里将音频数据写给驱动,这样驱动就可以播放出声音来了

      //那么mMixBuffer是从哪来的?

}

 

//AudioFlinger::PlaybackThread的构造函数里发现有调用了readOutputParameters方法

void AudioFlinger::PlaybackThread::readOutputParameters()

{

      delete[] mAllocMixBuffer;

      size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize;

      mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1];

      mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align);

     

      //原来在PlaybackThread的构造函数里申请了混音使用的Buffer

      //显然混音后的数据就放在这个Buffer里,那么混音器又是去哪里拿数据来混音的呢?

      //我们在回去看AudioMixer::process里调用的mState.hook

}

 

//显然hook在正常状态下是指向process__OneTrack16BitsStereoNoResampling函数的

void AudioMixer::process__OneTrack16BitsStereoNoResampling()

{

      //t.bufferProviderAudioBufferProvider,而AudioBufferProvider是基类。

      //t.bufferProvider实际的对象是Track,就是说这里真正调用的是AudioFlinger::PlaybackThread::Track::getNextBuffer()

      t.bufferProvider->getNextBuffer()

}

 

AudioFlinger::PlaybackThread::Track::getNextBuffer()

{

      //看见mServerProxy了吧?Track构造出来的时候对mServerProxy进行赋值了

      //刚好验证了我们刚才的想法,PlaybackThread就是从audio_track_cblk_t里拿的数据来混音的

      //所以该函数其实是调用了AudioTrackServerProxy的父类ServerProxyobtainBuffer方法

      mServerProxy->obtainBuffer()

}

 

// frameworks/av/media/libmedia/AudioTrackShared.cpp

ServerProxy::obtainBuffer()

{

      //看见mCblk了吧,mCblk就是上面提到的内存

      //也就是说PlaybackThread其实就是从AudioFlinger申请的共享内存了拿音频数据来进行混音处理的

      audio_track_cblk_t* cblk = mCblk;

}

 

//代码分析到这里我们已经基本搞清楚了服务AudioFlingerPlaybackThread线程就是从AudioFlinger申请的共享内存里拿的数据进行混音处理

//混音处理后的数据放在mAllocMixBuffer里,接着从mAllocMixBuffer里拿数据写到驱动去播放声音

//那么用户端是怎么把音频数据写到共享内存的呢?下面分析应用层的mAudioTrack.write()

//之前说了应用层的工程师想要播放PCM码流的话,需要自己new一个线程不停的执行mAudioTrack.write()

//AudioTrack.java里看见write函数有两个,这个和音频数据需要字节对齐有关,我们可以不要理会,挑一个分析就可以了

//照之前的分析,代码自然会通过JNI接口调用到Native层,我们直接到Native层看看就好了

static jint android_media_AudioTrack_native_write_byte()

{

      //lpTrack参数就是AudioTrack对象

      jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);

}

 

jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,

                  jint offsetInBytes, jint sizeInBytes) {

      //因为是MODE_STREAM模式,sharedBuffer等于0

      written = track->write(data + offsetInBytes, sizeInBytes);

}

 

ssize_t AudioTrack::write(const void* buffer, size_t userSize)

{

      status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);

     

      if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {

           // Divide capacity by 2 to take expansion into account

           toWrite = audioBuffer.size >> 1;

           memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite);

      } else {

           toWrite = audioBuffer.size;

           memcpy(audioBuffer.i8, buffer, toWrite);

      }

     

      //代码比较明显了,就是obtainBuffer获取内存,然后将从应用层传下来的数据buffer拷贝到内存audioBuffer

      //audioBuffer到底是什么鬼?我们接着分析obtainBuffer函数

}

 

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,

        struct timespec *elapsed, size_t *nonContig)

{

      sp<AudioTrackClientProxy> proxy;

      sp<IMemory> iMem;

     

      proxy = mProxy;

      iMem = mCblkMemory;

     

      status = proxy->obtainBuffer(&buffer, requested, elapsed);

     

      //真相大白!AudioTrackClientProxy和上面提到的AudioTrackServerProxy对应

      //服务端通过AudioTrackServerProxy来管理内存,即从内存里取出音频数据

      //客户端通过AudioTrackClientProxy来管理内存,即往内存里放入音频数据

      //那么mProxyiMemmCblkMemory到底都是什么东西呢?

}

 

//我们回到AudioTrack::createTrack_l里一看,一切都一目了然了

status_t AudioTrack::createTrack_l(

        audio_stream_type_t streamType,

        uint32_t sampleRate,

        audio_format_t format,

        size_t frameCount,

        audio_output_flags_t flags,

        const sp<IMemory>& sharedBuffer,

        audio_io_handle_t output,

        size_t epoch)

{

      sp<IAudioTrack> track = audioFlinger->createTrack()

      //我们在通过BinderAudioFlinger给我们返回一个track时,AudioFlinger已经申请了内存mCblkMemorymCblk

      //我们通过返回的track当然可以拿到接口去管理内存咯!

     

      //通过IMemory获取mCblk的接口

      sp<IMemory> iMem = track->getCblk();

     

    mAudioTrack = track;

    mCblkMemory = iMem;

    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());

    mCblk = cblk;

     

      //cblk当参数new出来的AudioRecordClientProxy对象就可以和AudioFlinger端的AudioRecordServerProxy对象一起管理着同一个audio_track_cblk_t

      mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);

}

 

//不知道大伙还记不记得上面讲AudioTrack::set()时我们曾new出一个AudioTrackThread线程

//而在AudioTrack::start()时我们让线程运行mReceiver.processAudioBuffer(this)

//下面我来分析一下AudioTrackThread的主函数处理的processAudioBuffer都做了什么

nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)

{

      //这里和AudioTrack::write时一样也获取内存

      status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);

     

      if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {

           // 8 to 16 bit conversion, note that source and destination are the same address

           memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize);

           audioBuffer.size <<= 1;

      }

     

      //这个线程所做的事情就是把audioBuffer.i8的数据拷贝到audioBuffer.i16

      //也就是说不管AudioTrack::write时将数据拷贝到哪里,经该线程数据最终都会被拷贝到audioBuffer.i16

      //至于为什么要这么做本人也还没有搞明白

}

 

总结:

经上面的分析,我们可以将Android播放PCM码流的流程总结一下

就是当应用层new出一个AudioTrack时,代码从框架层通过JNI接口调到Native层的运行时库,代码运行在运行时库时,newAudioTrackThread线程。

接着由运行时库通过Binder和音频服务通信,而音频服务AudioFlinger会申请出共享内存,同时newPlaybackThread线程。

而应用层在调用AudioTrack::start()时,AudioTrackThread线程主函数进行processAudioBuffer处理,即该线程通过AudioTrackClientProxy对象获取共享内存,对共享内存里的数据进行处理。

同时AudioTrack::start()会通过Binder和音频服务AudioFlinger通信,给AudioFlingerPlaybackThread线程发送广播,让PlaybackThread线程开始进入播放循环,即从混音器的缓冲区里拿出音频数据给驱动写,让声音播放出来。

最后在应用层会new一个线程不停的AudioTrack.write(),这个相当于该线程会通过AudioTrackClientProxy对象获取共享内存,将应用层的音频数据拷贝到共享内存里。

 

整个PCM码流播放的流程就是这样,你们会发现本文没有提到音频的另外一个服务AudioPolicyService,其实这个服务很重要,但是因为这个服务负责的事情,对我们对音频数据的流向的理解没有多大的影响,所以为了大家好理解文章里没有提到。

 

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值