天天看点

Android深入浅出之Audio第一部分AudioTrack分析(2)Android深入浅出之Audio第一部分AudioTrack分析(2)

Android深入浅出之Audio第一部分AudioTrack分析(2)

2013-11-14 16:18 佚名 博客园 字号:T | T

Android深入浅出之Audio第一部分AudioTrack分析(2)Android深入浅出之Audio第一部分AudioTrack分析(2)

本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。

AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验

下面是write。我们写的是short数组,

  1. static jint 
  2. android_media_AudioTrack_native_write_short(JNIEnv *env,  jobject thiz, 
  3.                                                   jshortArray javaAudioData, 
  4.                                                   jint offsetInShorts, 
  5. jint sizeInShorts, 
  6.                                                   jint javaAudioFormat) { 
  7.     return (android_media_AudioTrack_native_write(env, thiz, 
  8.                                                  (jbyteArray) javaAudioData, 
  9.                                                  offsetInShorts*2, sizeInShorts*2, 
  10.                                                  javaAudioFormat) 
  11.             / 2); 
  12. 烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去 
  13. jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data, 
  14.                   jint offsetInBytes, jint sizeInBytes) { 
  15.       ssize_t written = 0; 
  16.     // regular write() or copy the data to the AudioTrack's shared memory? 
  17. if (pTrack->sharedBuffer() == 0) { 
  18. //创建的是流的方式,所以没有共享内存在track中 
  19. //还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建 
  20. //共享内存 
  21.         written = pTrack->write(data + offsetInBytes, sizeInBytes); 
  22.     } else { 
  23.         if (audioFormat == javaAudioTrackFields.PCM16) { 
  24.             // writing to shared memory, check for capacity 
  25.             if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) { 
  26.                 sizeInBytes = pTrack->sharedBuffer()->size(); 
  27.             } 
  28.            //看见没?STATIC模式的,就直接把数据拷贝到共享内存里 
  29.           //当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的 
  30. //共享设进去的 
  31.             memcpy(pTrack->sharedBuffer()->pointer(), 
  32. data + offsetInBytes, sizeInBytes); 
  33.             written = sizeInBytes; 
  34.         } else if (audioFormat == javaAudioTrackFields.PCM8) { 
  35.            PCM8格式的要先转换成PCM16 
  36.     } 
  37.     return written; 

到这里,似乎很简单啊,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。反正JNI这层是再看不出什么有意思的东西了。

四 AudioTrack(C++层)

接上面的内容,我们知道在JNI层,有以下几个步骤:

l         new了一个AudioTrack

l         调用set函数,把AudioTrackJniStorage等信息传进去

l         调用了AudioTrack的start函数

l         调用AudioTrack的write函数

那么,我们就看看真正干活的的C++AudioTrack吧。

AudioTrack.cpp位于framework/base/libmedia/AudioTrack.cpp

4.1 new AudioTrack()和set调用

JNI层调用的是最简单的构造函数:

  1. AudioTrack::AudioTrack() 
  2.     : mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。 

接下来调用set。我们看看JNI那set了什么

  1.  lpTrack->set( 
  2.             atStreamType, //应该是Music吧 
  3.             sampleRateInHertz,//8000 
  4.             format,// 应该是PCM_16吧 
  5.             channels,//立体声=2 
  6.             frameCount,// 
  7.             0,// flags 
  8.             audioCallback, //JNI中的一个回调函数 
  9. &(lpJniStorage->mCallbackData),//回调函数的参数 
  10.             0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上 
  11.             0,//共享buffer地址,stream模式没有 
  12.             true);//回调线程可以调JAVA的东西 
  13. 那我们看看set函数把。 
  14. status_t AudioTrack::set( 
  15.         int streamType, 
  16.         uint32_t sampleRate, 
  17.         int format, 
  18.         int channels, 
  19.         int frameCount, 
  20.         uint32_t flags, 
  21.         callback_t cbf, 
  22.         void* user, 
  23.         int notificationFrames, 
  24.         const sp<IMemory>& sharedBuffer, 
  25.         bool threadCanCallJava) 

...前面一堆的判断,等以后讲AudioSystem再说

  1. audio_io_handle_t output = 
  2. AudioSystem::getOutput((AudioSystem::stream_type)streamType, 
  3.             sampleRate, format, channels, (AudioSystem::output_flags)flags); 
  4.    //createTrack?看来这是真正干活的 
  5.     status_t status = createTrack(streamType, sampleRate, format, channelCount, 
  6.                                   frameCount, flags, sharedBuffer, output); 
  7.   //cbf是JNI传入的回调函数audioCallback 
  8.      if (cbf != 0) { //看来,怎么着也要创建这个线程了! 
  9.         mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); 
  10.        } 
  11.    return NO_ERROR; 

看看真正干活的createTrack

  1. status_t AudioTrack::createTrack( 
  2.         int streamType, 
  3.         uint32_t sampleRate, 
  4.         int format, 
  5.         int channelCount, 
  6.         int frameCount, 
  7.         uint32_t flags, 
  8.         const sp<IMemory>& sharedBuffer, 
  9.         audio_io_handle_t output) 
  10. status_t status; 
  11. //啊,看来和audioFlinger挂上关系了呀。 
  12.     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); 
  13.   //下面这个调用最终会在AudioFlinger中出现。暂时不管它。 
  14.     sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), 
  15.                                                       streamType, 
  16.                                                       sampleRate, 
  17.                                                       format, 
  18.                                                       channelCount, 
  19.                                                       frameCount, 
  20.                                                       ((uint16_t)flags) << 16, 
  21.                                                       sharedBuffer, 
  22.                                                       output, 
  23.                                                       &status); 
  24.    //看见没,从track也就是AudioFlinger那边得到一个IMemory接口 
  25. //这个看来就是最终write写入的地方 
  26.     sp<IMemory> cblk = track->getCblk(); 
  27.     mAudioTrack.clear(); 
  28.     mAudioTrack = track; 
  29.     mCblkMemory.clear();//sp<XXX>的clear,就看着做是delete XXX吧 
  30.     mCblkMemory = cblk; 
  31.     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); 
  32.     mCblk->out = 1; 
  33.     mFrameCount = mCblk->frameCount; 
  34. if (sharedBuffer == 0) { 
  35. //终于看到buffer相关的了。注意我们这里的情况 
  36. //STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。 
  37. //反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到 
  38. //的buffer了。 
  39.         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); 
  40.     } 
  41.     return NO_ERROR; 

还记得我们说MemoryXXX没有同步机制,所以这里应该有一个东西能体现同步的,

那么我告诉大家,就在audio_track_cblk_t结构中。它的头文件在

framework/base/include/private/media/AudioTrackShared.h

实现文件就在AudioTrack.cpp中

  1. audio_track_cblk_t::audio_track_cblk_t() 
  2. //看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了 
  3. //等以后介绍同步方面的知识时,再细说 
  4.     : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), 
  5.     userBase(0), serverBase(0), buffers(0), frameCount(0), 
  6.     loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), 
  7.     flowControlFlag(1), forceReady(0) 

到这里,大家应该都有个大概的全景了。

l         AudioTrack得到AudioFlinger中的一个IAudioTrack对象,这里边有一个很重要的数据结构audio_track_cblk_t,它包括一块缓冲区地址,包括一些进程间同步的内容,可能还有数据位置等内容

l         AudioTrack启动了一个线程,叫AudioTrackThread,这个线程干嘛的呢?还不知道

l         AudioTrack调用write函数,肯定是把数据写到那块共享缓冲了,然后IAudioTrack在另外一个进程AudioFlinger中(其实AudioFlinger是一个服务,在mediaservice中运行)接收数据,并最终写到音频设备中。

那我们先看看AudioTrackThread干什么了。

调用的语句是:

mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

AudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。

反正最终会调用AudioTrackAThread的threadLoop函数。

先看看构造函数

  1. AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava) 
  2.     : Thread(bCanCallJava), mReceiver(receiver) 
  3. {  //mReceiver就是AudioTrack对象 
  4.   // bCanCallJava为TRUE 

这个线程的启动由AudioTrack的start函数触发。

  1. void AudioTrack::start() 
  2.   //start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的 
  3. threadLoop 
  4.     sp<AudioTrackThread> t = mAudioTrackThread; 
  5. t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); 
  6. //让AudioFlinger中的track也start 
  7.     status_t status = mAudioTrack->start(); 
  8. bool AudioTrack::AudioTrackThread::threadLoop() 
  9.   //太恶心了,又调用AudioTrack的processAudioBuffer函数 
  10. return mReceiver.processAudioBuffer(this); 
  11. bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) 
  12. Buffer audioBuffer; 
  13.     uint32_t frames; 
  14.     size_t writtenSize; 
  15.       ...回调1 
  16.          mCbf(EVENT_UNDERRUN, mUserData, 0); 
  17. ...回调2 都是传递一些信息到JNI里边 
  18.          mCbf(EVENT_BUFFER_END, mUserData, 0); 
  19.          // Manage loop end callback 
  20.     while (mLoopCount > mCblk->loopCount) { 
  21.         mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); 
  22.     } 
  23.   //下面好像有写数据的东西 
  24.       do { 
  25.        audioBuffer.frameCount = frames; 
  26. //获得buffer, 
  27.        status_t err = obtainBuffer(&audioBuffer, 1); 
  28.         size_t reqSize = audioBuffer.size; 
  29. //把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停 
  30. //地write呢,怎么会这样? 
  31.         mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); 
  32.          audioBuffer.size = writtenSize; 
  33.          frames -= audioBuffer.frameCount; 
  34.        releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK 
  35. 操作了 
  36.     } 
  37.     while (frames); 
  38.    return true; 
  39. 难道真的有两处在write数据?看来必须得到mCbf去看看了,传的是EVENT_MORE_DATA标志。 
  40. mCbf由set的时候传入C++的AudioTrack,实际函数是: 
  41. static void audioCallback(int event, void* user, void *info) { 
  42.     if (event == AudioTrack::EVENT_MORE_DATA) { 
  43.          //哈哈,太好了,这个函数没往里边写数据 
  44.         AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; 
  45.         pBuff->size = 0;  
  46.      } 

从代码上看,本来google考虑是异步的回调方式来写数据,可惜发现这种方式会比较复杂,尤其是对用户开放的JAVA AudioTrack会很不好处理,所以嘛,偷偷摸摸得给绕过去了。

太好了,看来就只有用户的write会真正的写数据了,这个AudioTrackThread除了通知一下,也没什么实际有意义的操作了。

让我们看看write吧。

4.2 write

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

{

够简单,就是obtainBuffer,memcpy数据,然后releasBuffer

眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了

  1. do { 
  2.        audioBuffer.frameCount = userSize/frameSize(); 
  3.        status_t err = obtainBuffer(&audioBuffer, -1); 
  4.         size_t toWrite; 
  5.         toWrite = audioBuffer.size; 
  6.         memcpy(audioBuffer.i8, src, toWrite); 
  7.         src += toWrite; 
  8.        } 
  9.        userSize -= toWrite; 
  10.        written += toWrite; 
  11.        releaseBuffer(&audioBuffer); 
  12.    } while (userSize); 
  13.    return written; 

obtainBuffer太复杂了,不过大家知道其大概工作方式就可以了

  1. status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) 
  2.    //恕我中间省略太多,大部分都是和当前数据位置相关, 
  3.  uint32_t framesAvail = cblk->framesAvailable(); 
  4.      cblk->lock.lock();//看见没,lock了 
  5.      result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); 
  6. //我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道 
  7. //没有一个好的方法来集中处理这种事情吗? 
  8.       if (result == DEAD_OBJECT) { 
  9.         result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount, 
  10.           mFrameCount, mFlags, mSharedBuffer,getOutput()); 
  11.         } 
  12. //得到buffer 
  13.     audioBuffer->raw = (int8_t *)cblk->buffer(u); 
  14.   return active ? status_t(NO_ERROR) : status_t(STOPPED); 
  15. 在看看releaseBuffer 
  16. void AudioTrack::releaseBuffer(Buffer* audioBuffer) 
  17.     audio_track_cblk_t* cblk = mCblk; 
  18. cblk->stepUser(audioBuffer->frameCount); 
  19. uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) 
  20.     uint32_t u = this->user; 
  21.     u += frameCount; 
  22.      if (out) { 
  23.           if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { 
  24.             bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; 
  25.         } 
  26.     } else if (u > this->server) { 
  27.          u = this->server; 
  28.     } 
  29.     if (u >= userBase + this->frameCount) { 
  30.         userBase += this->frameCount; 
  31.     } 
  32.    this->user = u; 
  33.   flowControlFlag = 0; 
  34.   return u; 

奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?

再去看看obtainBuffer?为何写得这么晦涩难懂?

原来在obtainBuffer中会某一次进去lock,再某一次进去可能就是unlock了。没看到obtainBuffer中到处有lock,unlock,wait等同步操作吗。一定是这个道理。难怪写这么复杂。还使用了少用的goto语句。

唉,有必要这样吗!

五 AudioTrack总结

通过这一次的分析,我自己觉得有以下几个点:

l         AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。

l         看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点

工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。

l         AudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。

l         调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了

l         用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯

l         可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。

【编辑推荐】

  1. MTP in Android详解
  2. Android Audio系统变化说明
  3. Android rom移植知识
  4. Android深入浅出之Audio第三部分Audio Policy
  5. Android深入浅出之AudioFlinger分析

【责任编辑:张叶青 TEL:(010)68476606】