audiotrack播放pcm打印的日志怎么关闭

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
& 今年四月,我到广东从化温泉小住了几天。那里四围是山,环抱着一潭春水。那又浓又翠的景色,简直是一幅青绿山水画。刚去的当晚是个阴天,偶尔倚着楼窗一望,奇怪啊,怎么楼前凭空涌起那么多黑黝黝的小山,一重一重的,起伏不断?记得楼前是一片园林,不是山。这到底是什么幻景呢?赶到天明一看,忍不住笑了。原来是满野的荔枝树,一棵连一棵,每棵的叶子都密得不透缝,黑夜看去,可不就象小山似的!
  荔枝也许是世上最鲜最美的水果。苏东坡写过这样的诗句:“日啖荔枝三百颗,不辞长作岭南人。”可见荔枝的妙处。偏偏我来的不是时候,荔枝刚开花。满树浅黄色的小花,并不出众。新发的嫩叶,颜色淡红,比花倒还中看些。从开花到果子成熟,大约得三个月,看来我是等不及在这儿吃鲜荔枝了。
微笑吧,为了不能忘却的记忆。
近期心愿 微笑吧,为了不能忘却的记忆。
最后登录加载中...
this.p={b:2,ua:19, ub:'http://img.bimg.126.net/photo/pZsC4AwxYVKmow2cVJvFxg==/516777.jpg',us:'她', friendstatus:'none',followstatus:'unFollow',hmcon:'',aShowT:'0',guideId:6};
积分 ${data.totalScore} 分,距离下一等级还有 ${data.nextGradeNeedScore}分
心情随笔列表加载中...
this.p={b:2,n:5,r:'http://./blog/#m=1&c=',mset:'000',mcon:'',srk:-100};
{if defined('fl')&&fl.length>0} {list fl as x}
${x.content|xescape:x.id,x.moveFrom} ${x.publishTime|xtime}
{if x.moveFrom&&x.moveFrom=='wap'} && {/if} {if x.moveFrom&&x.moveFrom=='mobile'} && {/if}
{/list} {else} 暂无心情随笔记录! {/if}
博友列表加载中...
this.p={b:2,m:0};
发现好博客
列表加载中...
this.p={b:2,cn:12,ct:12};
列表加载中...
this.p={b:2,cn:15};
我要留言 & &
& 留言列表加载中...
this.p={b:2,nv:false,cn:5,ct:5};
& & & & & &
网易公司版权所有&&
{list x.l as y}
{/list} {/list}
{if defined('wl')} {list wl as x}{/list} {/if}安卓(21)
1.AudioTrackThread
  threadLoop函数里会调用
   nsecs_t ns = mReceiver.processAudioBuffer();
 mReceiver就是一个AudioTrack,接收者?天知道为嘛这么叫。
 咱们先不说这个很重要的函数 processAudioBuffer,先来看看AudioTrackThread是在哪使用的。
2.AudioTrack类成员变量:
``sp&AudioTrackThread&
mAudioTrackT``
  在AudioTrack::set()函数中:
if (cbf != NULL) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread-&run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
// thread begins in paused state, and will not reference us until start()
  callback存在的话,就new它,然后run之。但是注释都说了,start()之后,这个AudioTrackThread才会真正run起来,
sp&AudioTrackThread& t = mAudioTrackT
if (t != 0) {
if (previousState == STATE_STOPPING) {
mProxy-&interrupt();
t-&resume();
3.综上,也就是说AudioTrack::start的时候会有 mAudioTrackThread运行起来,不断调用 processAudioBuffer()方法。
4. processAudioBuffer()方法:
  里头循环调用了
   status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
   mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
  即获得buffer,然后,谁创建了AudioTrack,并且设置了回调(在AudioTrack构造函数中传入参数,构造函数会调用set进行赋值),就和谁要数据。比如说nuplayer。要数据的时候就把经过解码的pcm数据传下来。
5. obtainBuffer()
  真正干活的也不是这。干活的是:
    status = proxy-&obtainBuffer(&buffer, requested, elapsed);
  实现在AudioTrackShare.cpp里头。
  (某一种情况)这个地方取到buffer的地址,然后通过
   mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
  把数据取回来,由于取到的这个buffer,audioFlinger那边也能访问,所以。。。
6.还是说说set函数吧(上面说过了,在构造函数中会调用)
  这里头有一个很重要的调用:createTrack_l(),这个函数在凡平老师的书里也说到了。AudioTrack和 AudioFlinger的联系啊简直是。
  这里头又调用了以前经常见到的一个函数AudioSystem::getOutputForAttr,这个函数一路下调到apm里头,现在暂且略过不表。
  createTrack_l()函数中经过了很复杂的流程,以及获取,设置了好多参数之后(其实是没看懂,一笔带过),调用了
sp&IAudioTrack& track = audioFlinger-&createTrack(streamType,
mSampleRate,mFormat,mChannelMask,&temp,&trackFlags,mSharedBuffer,output,tid,&mSessionId,(mClientPid && 16) |mClientUid,&status);
  通过AudioFlinger的createTrack函数,返回了一个 sp&IAudioTrack&,我记得这个东西对应
  的就是TrackHandle类(AudioFlinger里头一个小小的内部类)。
再之后,贯穿AudioTrack类其中的mAudioTrack变量就是从这来的。
mAudioTrack =
很多AudioTrack的操作,都是调用TrackHandle类。最终由playbackThread的track类完成实际工作。
这还涉及到了共享内存,共享buffer等内容。略过先。
还是先继续瞅瞅createTrack函数吧。
7.AudioFlinger::createTrack()
track = thread-&createTrack_l(client, streamType, sampleRate, format,channelMask,
     frameCount,sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
sp&PlaybackThread::Track&
trackHandle = new TrackHandle(track);
  最终AudioFlinger的createTrack函数是这么返回的:
  用PlaybackThread::createTrack_l()函数返回一个PlaybackThread::Track对象,然后用这个对象作为参数,构造一个TrackHandle对象,然后返回。
  言归正传,接下来
if (!isTimed) {
  track = new Track(this, client, streamType, sampleRate, format,
    channelMask, frameCount, NULL, sharedBuffer,sessionId, uid, *flags,
    TrackBase::TYPE_DEFAULT);
  track = TimedTrack::create(this, client, streamType, sampleRate, format,
  channelMask, frameCount, sharedBuffer, sessionId, uid);
  TimedTrack是什么鬼,说普通的吧:
8.AudioFlinger::PlaybackThread::Track::Track()
  PlaybackThread内部类Track又是什么鬼?
  这他妈应该是真正干活的了吧,再往下好像还有proxy.我要吐了。看到这边audioflinger,才发现,audiotrack的内容实在是太少了。
9.现在咱们又绕回去说说AudioTrackThread调用 processAudioBuffer()的那个地方
  nsecs_t ns = mReceiver.processAudioBuffer();
  这个函数返回纳秒,而函数内部return的地方,多是调用了
  framesToNanoseconds()
  static inline nsecs_t framesToNanoseconds(ssize_t frames, uint32_t sampleRate, float speed){
  return ((double)frames * ) / ((double)sampleRate * speed);
  函数的实现也很简单。虽然不是很懂,到应该是s转换成了ns(纳秒)。难道是每次拿到的数据所持续的播放时间?
10.现在回去说说obtainBuffer吧。上面说到,它走到了AudioTrackShare.cpp里。这个文件里头有这么几个类:
  //基类:Proxy
  //Client系列:
   - ClientProxy
   - AudioTrackClientProxy
   - StaticAudioTrackClientProxy
   - AudioRecordClientProxy
  //Server系列:
   - AudioTrackServerProxy
   - StaticAudioTrackServerProxy
   - AudioRecordServerProxy
  继承关系一目了然,从名字大概能猜出来。
  代码的文件名中Share基本能说明,这些类应该和AudioFlinger,或者说给AudioTrack提供数据的一方关系很大。当然AudioTrack肯定是有关系的了。AudioTrack对应Client,AudioFlinger对应server。这两者之间还有一个生产者,消费者的关系(别人说的)。
  话说AudioTrack类的obtainBuffer调用了
   status_t ClientProxy::obtainBuffer
  这里边有个函数
   StaticAudioTrackServerProxy::pollPosition()
  也同样被调用到,然后呢?没看懂呢,先不继续了。。。
  // Proxy used by AudioFlinger server
   class ServerProxy : public Proxy {
  看着没?头文件都说了,AudioFlinger用的!
  费了半天劲,在AudioFlinger找到了一个变量mServerProxy,sourceinsight无法跳转,grep搜索发现,定义在了TrackBase.h里了
  void AudioFlinger::ThreadBase::TrackBase,子类的子类呀!
让我们看看真正的证据吧:
AudioFlinger::PlaybackThread::Track::Track()构造函数里调用了
if (sharedBuffer == 0) {
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize);
  没错,和我们想的一样,就是用的ServerProxy.
  上面的参数,mCblk,mBuffer,等参数在sourceInsight上是黑色的,没法跳转,根据经验,定义在基类里,即TrackBase.h文件里。比如说mBuffers(Track类的)从 StaticAudioTrackServerProxy()构造函数一路传了上去,最终,传到了前面说的Proxy类的mBuffers。
  而这个mBuffers基本上就是obtainBuffer取数据的源泉啊。
  mBuffers在AudioFlinger构造serverProxy的时候一步步传进来。然后在AudioTrack调用ClientProxy的obtainBuffer(write方式或者是processAudioBuffer方式都会调用)的时候通过参数返回。AudioTrack拿到共有缓冲区的地址之后,就可以往里放数据了也就是说,AudioFlinger和AudioTrack在通过这个mBuffers传递数据呀!这个地方还有一点需要说一下。就是
  这个地方为什么有两个类,有一个前面多了一个static.使用场景不一样!static的那个是用在ShareBuffer给定(AudioTrack()那个参数不为空)的情况下,一直去里头取数据即可。所以说是static的。
11.AudioTrack::write函数:
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
上层应用调用这个函数,然后不断往下写经过解码的数据。这里边的buffer就是数据了。
while (userSize &= mFrameSize) {
status_t err = obtainBuffer(&audioBuffer,
blocking ? &ClientProxy : &ClientProxy);
memcpy(audioBuffer.i8, buffer, toWrite);
releaseBuffer(&audioBuffer);
  其中的这个while循环不断做的事情就是通过obtainBuffer获得和AudioFlinger共享的buffer。然后往里头memcpy上层write的数据。最后releaseBuffer。
12.回调方式
  如果构造AudioTrack的时候,选用的构造函数是带callback_t cbf的那个,并且传入了存在的一个值,在AudioTrack的构造函数里就会调用set函数,把这个cbf传递给AudioTrack的mCbf这个成员变量。只要
  ``mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);``
类似这样的调用方式,就会回调到在构造函数里传入的那个地址。比如SoundPool就把process函数作为了回调函数传给了AudioTrack。
  从字面意思看就是,我需要数据,快给我数据!不过soundPool一般是不会回调 EVENT_MORE_DATA的。因为一般数据量都很少,一次就可以搞定。soundPool一般都是回调EVENT_BUFFER_END。通知上层,数据没了。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:112626次
积分:1377
积分:1377
排名:千里之外
原创:30篇
转载:36篇
评论:33条
(1)(3)(3)(2)(1)(9)(5)(1)(1)(3)(6)(1)(1)(1)(2)(3)(1)(8)(3)(2)(1)(5)(3)18245人阅读
Android开发系列(106)
Android深入浅出之Audio
第一部分 AudioTrack分析
AudioAndroidAndroidThreadMemoryBase
1.1 分析工具
sourceinsightandroidAPI docandroidfroyo
froyosourceinsightframwork
二 Audio系统
AudioAndroidSDK
AudioManagerAudioTrack
三 AudioTrack(JAVA层)
AudioTrack
3.1 AudioTrack API的使用例子
AudioTrackAPI
//根据采样率,采样精度,单双声道来得到frame的大小。
int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点
  AudioFormat.CHANNEL_CONFIGURATION_,//双声道
AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。
//创建AudioTrack
AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
  AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
  AudioFormat.ENCODING_PCM_16BIT,
  bufsize,
AudioTrack.MODE_STREAM);//
&trackplayer.play() ;//开始
trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据
trackplayer.stop();//停止播放
trackplayer.release();//释放底层资源。
1 AudioTrack.MODE_STREAM的意思:
MODE_STATICMODE_STREAMSTREAMwriteaudiotracksocketPCMwriteaudiotrack
JAVANative
STATICbufferaudiotrackwriteAudioTrackbuffer
2 StreamType
AudioTrackAndroidAudioManager
musicmusicmusic
AudioTrack
3.2 分析之getMinBufferSize
AudioTrack.getMinBufferSize(8000,//每秒8K个点
  AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT);
-----&AudioTrack.JAVA
//注意,这是个static函数
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
&&&&&&& int channelCount = 0;
&&&&&&& switch(channelConfig) {
&&& &&&&case AudioFormat.CHANNEL_OUT_MONO:
&&&&&&& case AudioFormat.CHANNEL_CONFIGURATION_MONO:
&&&&&&&&&&& channelCount = 1;
&&&&&&&&&&&
&&&&&&& case AudioFormat.CHANNEL_OUT_STEREO:
&&&&&&& case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
&&&&&&&&&&& channelCount = 2;---&看到了吧,外面名字搞得这么酷,其实就是指声道数
&&&&&&&&&&&
&&&&&&& default:
&&&&&&&&&&& loge("getMinBufferSize(): Invalid channel configuration.");
&&&&&&&&&&& return AudioTrack.ERROR_BAD_VALUE;
&&& //目前只支持PCM8和PCM16精度的音频&&&
&&&&&&& if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&&&&&&&&&&& && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
&&&&&&&&&&& loge("getMinBufferSize(): Invalid audio format.");
&&&&&&&&&&& return AudioTrack.ERROR_BAD_VALUE;
&&&&& //ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间
&&&&&&& if ( (sampleRateInHz & 4000) || (sampleRateInHz & 48000) ) {
&&&&&&&&&&& loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
&&&&&&&&&&& return AudioTrack.ERROR_BAD_VALUE;
&&&&&& //调用native函数,够烦的,什么事情都搞到JNI层去。
&&&&&&& int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
&&&&&&& if ((size == -1) || (size == 0)) {
&&&&&&&&&&& loge("getMinBufferSize(): error querying hardware");
&&&&&&&&&&& return AudioTrack.ERROR;
&& &&&&&else {
&&&&&&&&&&&
native_get_min_buff_size---&在framework/base/core/jni/android_media_track.cpp中实现。(不了解JNI的一定要学习下,否则只能在JAVA层搞,太狭隘了。)最终对应到函数
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,& jobject thiz,
jint sampleRateInHertz, jint nbChannels, jint audioFormat)
{//注意我们传入的参数是:
//sampleRateInHertz = 8000
//nbChannels = 2;
//audioFormat = AudioFormat.ENCODING_PCM_16BIT
&&& int afSamplingR
&&& int afFrameC
&&& uint32_t afL
//下面涉及到AudioSystem,这里先不解释了,
//反正知道从AudioSystem那查询了一些信息
&&& if (AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
&&&&&&& return -1;
&&& if (AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
&&&&&&& return -1;
&&& if (AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
&&&&&&& return -1;
//音频中最常见的是frame这个单位,什么意思?经过多方查找,最后还是在ALSA的wiki中
//找到解释了。一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。
&&& // Ensure that buffer depth covers at least audio hardware latency
&&& uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSamplingRate);
&&& if (minBufCount & 2) minBufCount = 2;
uint32_t minFrameCount =
&(afFrameCount*sampleRateInHertz*minBufCount)/afSamplingR
//下面根据最小的framecount计算最小的buffersize&&&
int minBuffSize = minFrameCount
&&&&&&&&&&& * (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
&&&&&&&&&&& * nbC
&&& return minBuffS
AudioTrack
3.3 分析之new AudioTrack
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC,
  AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
  AudioFormat.ENCODING_PCM_16BIT,
  bufsize,
AudioTrack.MODE_STREAM);//
其实现代码在AudioTrack.java中。
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
&&&&&&&&&&& int bufferSizeInBytes, int mode)
&&& throws IllegalArgumentException {
&&&&&&& mState = STATE_UNINITIALIZED;
&&&&&&& // 获得主线程的Looper,这个在MediaScanner分析中已经讲过了
&&&&&&& if ((mInitializationLooper = Looper.myLooper()) == null) {
&&&&&&&&&&& mInitializationLooper = Looper.getMainLooper();
&&& //检查参数是否合法之类的,可以不管它
&&&&&&& audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
&& //我是用getMinBufsize得到的大小,总不会出错吧?
&&&&&&& audioBuffSizeCheck(bufferSizeInBytes);
&&&&&&& // 调用native层的native_setup,把自己的WeakReference传进去了
&&&& //不了解JAVA WeakReference的可以上网自己查一下,很简单的
&&&&&&& int initResult = native_setup(new WeakReference&AudioTrack&(this),
&&&&&&&&&&&&&&& mStreamType, 这个值是AudioManager.STREAM_MUSIC
&mSampleRate, 这个值是8000
mChannels, 这个值是2
mAudioFormat,这个值是AudioFormat.ENCODING_PCM_16BIT
&&&&&&&&&&&&&&& mNativeBufferSizeInBytes, //这个是刚才getMinBufSize得到的
mDataLoadMode);DataLoadMode是MODE_STREAM
&&&&&&&& ....
JNIandroid_media_AudioTrack.cpp
static int
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
&&&&&&& jint streamType, jint sampleRateInHertz, jint channels,
&&&&&&& jint audioFormat, jint buffSizeInBytes, jint memoryMode)
&&& int afSampleR
&&& int afFrameC
&& 下面又要调用一堆东西,烦不烦呐?具体干什么用的,以后分析到AudioSystem再说。
&&& AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
&& AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
&& AudioSystem::isOutputChannel(channels);
&&& popCount是统计一个整数中有多少位为1的算法
int nbChannels = AudioSystem::popCount(channels);
&&& if (streamType == javaAudioTrackFields.STREAM_MUSIC) {
&&&&&&& atStreamType = AudioSystem::MUSIC;
&& int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
&&& int format = audioFormat == javaAudioTrackFields.PCM16 ?
&&&&&&&&&&& AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
&&& int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
//上面是根据Buffer大小和一个Frame大小来计算帧数的。
// AudioTrackJniStorage,就是一个保存一些数据的地方,这
//里边有一些有用的知识,下面再详细解释
&&& AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
&&&& jclass clazz = env-&GetObjectClass(thiz);
&&&&& lpJniStorage-&mCallbackData.audioTrack_class = (jclass)env-&NewGlobalRef(clazz);
&&&& lpJniStorage-&mCallbackData.audioTrack_ref = env-&NewGlobalRef(weak_this);
&&&& lpJniStorage-&mStreamType = atStreamT
//创建真正的AudioTrack对象
&&& AudioTrack* lpTrack = new AudioTrack();
&&&&&& if (memoryMode == javaAudioTrackFields.MODE_STREAM) {
& //如果是STREAM流方式的话,把刚才那些参数设进去
&&&&&& lpTrack-&set(
&&&&&&&&&&& atStreamType,// stream type
&&&&&&&&&&& sampleRateInHertz,
&&&&&&&&&&& format,// word length, PCM
&&&&&&&&&&& channels,
&&&&&&&&&&& frameCount,
&&&&&&&&&&& 0,// flags
&&&&&&&&&&& audioCallback,
&(lpJniStorage-&mCallbackData),//callback, callback data (user)
&&&&&&&&&&& 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
&&&&&&&&&&& 0,// 共享内存,STREAM模式需要用户一次次写,所以就不用共享内存了
&&&&&&&&&&& true);// thread can call Java
&&&&&&&&&&&
&&& } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {
&&&&&&& &//如果是static模式,需要用户一次性把数据写进去,然后
&&&&&& //再由audioTrack自己去把数据读出来,所以需要一个共享内存
//这里的共享内存是指C++AudioTrack和AudioFlinger之间共享的内容
&//因为真正播放的工作是由AudioFlinger来完成的。
&&&&&&&&& lpJniStorage-&allocSharedMem(buffSizeInBytes);
&&&&&&&&& lpTrack-&set(
&&&&&&&&&&& atStreamType,// stream type
&&&&&&&&&&& sampleRateInHertz,
&&&&&&&&&&& format,// word length, PCM
&&&&&&&&&&& channels,
&&&&&&&&&&& frameCount,
&&&&&&&&&&& 0,// flags
&&&&&&&&&&& audioCallback,
&(lpJniStorage-&mCallbackData),//callback, callback data (user));
&&&&&&&&&&& 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
&&&&&&&&&&& lpJniStorage-&mMemBase,// shared mem
&&&&&&&&&&& true);// thread can call Java
&&& if (lpTrack-&initCheck() != NO_ERROR) {
&&&&&&& LOGE("Error initializing AudioTrack");
&&&&&&& goto native_init_
//又来这一招,把C++AudioTrack对象指针保存到JAVA对象的一个变量中
//这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了。
&&& env-&SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack);
&&& env-&SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage);
1 AudioTrackJniStorage详解
struct audiotrack_callback_cookie {
&&& jclass&&&&& audioTrack_
&&& jobject&&&& audioTrack_
&};& cookie其实就是把JAVA中的一些东西保存了下,没什么特别的意义
class AudioTrackJniStorage {
&&& public:
&&&&&&& sp&MemoryHeapBase&&&&&&&&& mMemH//这两个Memory很重要
&&&&&&& sp&MemoryBase&&&&&&&&&&&&& mMemB
&&&&&&& audiotrack_callback_cookie mCallbackD
&&&&&&& int&&&&&&&&&&&&&&&&&&& &&&&mStreamT
&&&&& bool allocSharedMem(int sizeInBytes) {
&&&&&&& mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
&&&&&&& mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
//注意用法,先弄一个HeapBase,再把HeapBase传入到MemoryBase中去。
2 MemoryHeapBase
AndroidBinderBinderBnxxxBpxxxMemoryHeapBase
class MemoryHeapBase : public virtual BnMemoryHeap
& 果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了
//Bp端调用的函数最终都会调到Bn这来
对Binder机制不了解的,可以参考:
http://blog.csdn.net/Innost/archive//6124685.aspx
& 有好几个构造函数,我们看看我们使用的:
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
&&& : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
&&&& &mDevice(0), mNeedUnmap(false)
&&& const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它
//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存
//不明白得去man mmap看看
mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是
ashmem_create_region返回的文件描述符
MemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。
getBaseID()---&返回mFd,如果为负数,表明刚才创建共享内存失败了
getBase()-&返回mBase,内存位置
& getSize()-&返回mSize,内存大小
MemoryHeapBaseMemoryBaseBinder
唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset
那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类
这样就不用用户到处去计算读写位置了。
class MemoryBase : public BnMemory
&&& MemoryBase(const sp&IMemoryHeap&& heap, ssize_t offset, size_t size);
&&& virtual sp&IMemoryHeap& getMemory(ssize_t* offset, size_t* size)
protected:
&&& size_t getSize() const { return mS }
&&& ssize_t getOffset() const { return mO }
&&& const sp&IMemoryHeap&& getHeap() const { return mH }
lBnMemoryHeapBaseBnMemoryBase
lBnMemoryBaseBpXXX
lBpMemoryBaseBnXXX
BpmemcpyAndroid
SharedBufferBpAudioFlinger
3.4 分析之play和write
playwriteJAVAnative
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
//看见没,从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针
//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改!
&&& AudioTrack *lpTrack = (AudioTrack *)env-&GetIntField(
&&&&&&& thiz, javaAudioTrackFields.nativeTrackInJavaObj);
&&& lpTrack-&start(); //这个以后再说
writeshort
static jint
android_media_AudioTrack_native_write_short(JNIEnv *env,& jobject thiz,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& jshortArray javaAudioData,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& jint offsetInShorts,
jint sizeInShorts,
&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&jint javaAudioFormat) {
&&& return (android_media_AudioTrack_native_write(env, thiz,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& (jbyteArray) javaAudioData,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& offsetInShorts*2, sizeInShorts*2,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& javaAudioFormat)
&&&&&&&&&&& / 2);
烦人,又根据Byte还是Short封装了下,最终会调到重要函数writeToTrack去
jint writeToTrack(AudioTrack* pTrack, jint audioFormat, jbyte* data,
&&&&&&&&&&&&&&&&& jint offsetInBytes, jint sizeInBytes) {
&&&&& ssize_t written = 0;
&&& // regular write() or copy the data to the AudioTrack's shared memory?
if (pTrack-&sharedBuffer() == 0) {
//创建的是流的方式,所以没有共享内存在track中
//还记得我们在native_setup中调用的set吗?流模式下AudioTrackJniStorage可没创建
//共享内存
&&&&&&& written = pTrack-&write(data + offsetInBytes, sizeInBytes);
&&& } else {
&&&&&&& if (audioFormat == javaAudioTrackFields.PCM16) {
&&&&&&&&&&& // writing to shared memory, check for capacity
&&&&&&&&&&& if ((size_t)sizeInBytes & pTrack-&sharedBuffer()-&size()) {
&&&&&&&&&&&&&&& sizeInBytes = pTrack-&sharedBuffer()-&size();
&&&&&&&&&&& }
&&&&&&&&&& //看见没?STATIC模式的,就直接把数据拷贝到共享内存里
&&&&&&&&& //当然,这个共享内存是pTrack的,是我们在set时候把AudioTrackJniStorage的
//共享设进去的
&&&&&&&&&&& memcpy(pTrack-&sharedBuffer()-&pointer(),
data + offsetInBytes, sizeInBytes);
&&&&&&&&&&& written = sizeInB
&&&&&&& } else if (audioFormat == javaAudioTrackFields.PCM8) {
&&&&&&&&&& PCM8格式的要先转换成PCM16
&&&&&&&&&&&
JAVAAudioTrackwriteJNIC++ AudioTrack writeJNI
四 AudioTrack(C++层)
lAudioTrack
lsetAudioTrackJniStorage
lAudioTrackstart
lAudioTrackwrite
C++AudioTrack
framework/base/libmedia/AudioTrack.cpp
4.1 new AudioTrack()和set调用
JNI层调用的是最简单的构造函数:
AudioTrack::AudioTrack()
&&& : mStatus(NO_INIT) //把状态初始化成NO_INIT。Android大量使用了设计模式中的state。
接下来调用set。我们看看JNI那set了什么
& lpTrack-&set(
&&&&&&&&&&& atStreamType, //应该是Music吧
&&&&&&&&&&& sampleRateInHertz,//8000
&&&&&&&&&&& format,// 应该是PCM_16吧
&&&&&&&&&&& channels,//立体声=2
&&&&&&&&&&& frameCount,//
&&&&&&&&&&& 0,// flags
&&&&&&&&&&& audioCallback, //JNI中的一个回调函数
&(lpJniStorage-&mCallbackData),//回调函数的参数
&&&&&&&&&&& 0,// 通知回调函数,表示AudioTrack需要数据,不过暂时没用上
&&&&&&&&&&& 0,//共享buffer地址,stream模式没有
&&&&&&&&&&& true);//回调线程可以调JAVA的东西
那我们看看set函数把。
status_t AudioTrack::set(
&&&&&&& int streamType,
&&&&&&& uint32_t sampleRate,
&&&&&&& int format,
&&&&&&& int channels,
&&&&&&& int frameCount,
&&&&&&& uint32_t flags,
&&&&&&& callback_t cbf,
&&&&&&& void* user,
&&&&&&& int notificationFrames,
&&&&&&& const sp&IMemory&& sharedBuffer,
&&&&&&& bool threadCanCallJava)
&& ...前面一堆的判断,等以后讲AudioSystem再说
audio_io_handle_t output =
AudioSystem::getOutput((AudioSystem::stream_type)streamType,
&&&&&&&&&&& sampleRate, format, channels, (AudioSystem::output_flags)flags);
& &//createTrack?看来这是真正干活的
&&& status_t status = createTrack(streamType, sampleRate, format, channelCount,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& frameCount, flags, sharedBuffer, output);
& //cbf是JNI传入的回调函数audioCallback
&&&& if (cbf != 0) { //看来,怎么着也要创建这个线程了!
&&&&&&& mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
&& return NO_ERROR;
看看真正干活的createTrack
status_t AudioTrack::createTrack(
& &&&&&&int streamType,
&&&&&&& uint32_t sampleRate,
&&&&&&& int format,
&&&&&&& int channelCount,
&&&&&&& int frameCount,
&&&&&&& uint32_t flags,
&&&&&&& const sp&IMemory&& sharedBuffer,
&&&&&&& audio_io_handle_t output)
//啊,看来和audioFlinger挂上关系了呀。
&&& const sp&IAudioFlinger&& audioFlinger = AudioSystem::get_audio_flinger();
& //下面这个调用最终会在AudioFlinger中出现。暂时不管它。
&&& sp&IAudioTrack& track = audioFlinger-&createTrack(getpid(),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& streamType,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& sampleRate,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& format,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& channelCount,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& frameCount,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ((uint16_t)flags) && 16,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& sharedBuffer,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& output,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&status);
&& //看见没,从track也就是AudioFlinger那边得到一个IMemory接口
//这个看来就是最终write写入的地方
&&& sp&IMemory& cblk = track-&getCblk();
&&& mAudioTrack.clear();
&&& mAudioTrack =
&&& mCblkMemory.clear();//sp&XXX&的clear,就看着做是delete XXX吧
&&& mCblkMemory =
&&& mCblk = static_cast&audio_track_cblk_t*&(cblk-&pointer());
&&& mCblk-&out = 1;
&&& mFrameCount = mCblk-&frameC
if (sharedBuffer == 0) {
//终于看到buffer相关的了。注意我们这里的情况
//STREAM模式没有传入共享buffer,但是数据确实又需要buffer承载。
//反正AudioTrack是没有创建buffer,那只能是刚才从AudioFlinger中得到
//的buffer了。
&&&&&&& mCblk-&buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
&&& return NO_ERROR;
audio_track_cblk_t
AudioTrack.cpp
audio_track_cblk_t::audio_track_cblk_t()
//看见下面的SHARED没?都是表示跨进程共享的意思。这个我就不跟进去说了
//等以后介绍同步方面的知识时,再细说
&&& : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
&&& userBase(0), serverBase(0), buffers(0), frameCount(0),
&&& loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
&&& flowControlFlag(1), forceReady(0)
lAudioFlingerIAudioTrackaudio_track_cblk_t
lAudioTrackThread
lwriteIAudioTrackAudioFlingerAudioFlingermediaservice
AudioTrackThread
调用的语句是:
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
AudioTrackThread从Thread中派生,这个内容在深入浅出Binder机制讲过了。
反正最终会调用AudioTrackAThread的threadLoop函数。
先看看构造函数
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
&&& : Thread(bCanCallJava), mReceiver(receiver)
{& //mReceiver就是AudioTrack对象
& // bCanCallJava为TRUE
AudioTrackstart
void AudioTrack::start()
& //start函数调用AudioTrackThread函数触发产生一个新的线程,执行mAudioTrackThread的
threadLoop
&&& sp&AudioTrackThread& t = mAudioTrackT
t-&run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
//让AudioFlinger中的track也start
&&& status_t status = mAudioTrack-&start();
bool AudioTrack::AudioTrackThread::threadLoop()
& //太恶心了,又调用AudioTrack的processAudioBuffer函数
return mReceiver.processAudioBuffer(this);
bool AudioTrack::processAudioBuffer(const sp&AudioTrackThread&& thread)
Buffer audioB
&&& uint32_
&&& size_t writtenS
&&&&& ...回调1
&&&&&&&& mCbf(EVENT_UNDERRUN, mUserData, 0);
...回调2 都是传递一些信息到JNI里边
&&&&&&&& mCbf(EVENT_BUFFER_END, mUserData, 0);
&&&& &&&&// Manage loop end callback
&&& while (mLoopCount & mCblk-&loopCount) {
&&&&&&& mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
& //下面好像有写数据的东西
&&&&& do {
&&&&&& audioBuffer.frameCount =
//获得buffer,
&&&&&& status_t err = obtainBuffer(&audioBuffer, 1);
&&&&&&& size_t reqSize = audioBuffer.
//把buffer回调到JNI那去,这是单独一个线程,而我们还有上层用户在那不停
//地write呢,怎么会这样?
&&&&&&& mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
&&&&&&&& audioBuffer.size = writtenS
&&&&&&&& frames -= audioBuffer.frameC
&&&&&& releaseBuffer(&audioBuffer); //释放buffer,和obtain相对应,看来是LOCK和UNLOCK
&&& while (frames);
writemCbfEVENT_MORE_DATA
setC++AudioTrack
static void audioCallback(int event, void* user, void *info) {
&&& if (event == AudioTrack::EVENT_MORE_DATA) {
&&&&&&& &//哈哈,太好了,这个函数没往里边写数据
&&&&&&& AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)
&&&&&&& pBuff-&size = 0;&
googleJAVA AudioTrack
writeAudioTrackThread
&4.2 write
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
& 够简单,就是obtainBuffer,memcpy数据,然后releasBuffer
眯着眼睛都能想到,obtainBuffer一定是Lock住内存了,releaseBuffer一定是unlock内存了
&&&&&&& audioBuffer.frameCount = userSize/frameSize();
&&&&&&& status_t err = obtainBuffer(&audioBuffer, -1);
&&&&&&&& size_t toW
&&&&&&&& toWrite = audioBuffer.
&&&&&&&& memcpy(audioBuffer.i8, src, toWrite);
&&&&&&&& src += toW
&&&&&&& userSize -= toW
&&&&&&& written += toW
&&&&&&& releaseBuffer(&audioBuffer);
&&& } while (userSize);
obtainBuffer太复杂了,不过大家知道其大概工作方式就可以了
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
&& //恕我中间省略太多,大部分都是和当前数据位置相关,
&uint32_t framesAvail = cblk-&framesAvailable();
&&&& cblk-&lock.lock();//看见没,lock了
&&&& result = cblk-&cv.waitRelative(cblk-&lock, milliseconds(waitTimeMs));
//我发现很多地方都要判断远端的AudioFlinger的状态,比如是否退出了之类的,难道
//没有一个好的方法来集中处理这种事情吗?
&&&&& if (result == DEAD_OBJECT) {
&&&&&&& result = createTrack(mStreamType, cblk-&sampleRate, mFormat, mChannelCount,
&&&&&&&&& mFrameCount, mFlags, mSharedBuffer,getOutput());
//得到buffer
&&& audioBuffer-&raw = (int8_t *)cblk-&buffer(u);
& return active ? status_t(NO_ERROR) : status_t(STOPPED);
在看看releaseBuffer
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
&&& audio_track_cblk_t* cblk = mC
cblk-&stepUser(audioBuffer-&frameCount);
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
&&& uint32_t u = this-&
&&& u += frameC
&&&& if (out) {
&&&&&&&&& if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
&&&&&&&&&&& bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
&&& } else if (u & this-&server) {
&&&&&&&& u = this-&
&&& if (u &= userBase + this-&frameCount) {
&&&&&&& userBase += this-&frameC
&& this-&user =
& flowControlFlag = 0;
奇怪了,releaseBuffer没有unlock操作啊?难道我失误了?
再去看看obtainBuffer?为何写得这么晦涩难懂?
原来在obtainBuffer中会某一次进去lock,再某一次进去可能就是unlock了。没看到obtainBuffer中到处有lock,unlock,wait等同步操作吗。一定是这个道理。难怪写这么复杂。还使用了少用的goto语句。
唉,有必要这样吗!
五 AudioTrack总结
lAudioFlingerAudioTrackAudioFlinger
lnewsetBinderAudioFlingerIAudioTrackAudioFlinger
lstartJNIwrite
lwriteAudioTrackmemcpybuffer
lAudioFlingermemcpy
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1908877次
积分:17109
积分:17109
排名:第368名
原创:151篇
评论:2123条
好兄弟的新书《深入理解Android 卷3》即将上市,感谢大家对《深入理解Android》系列书籍的支持。
文章:81篇
阅读:1419006
(1)(1)(1)(1)(1)(28)(1)(3)(1)(1)(1)(1)(1)(1)(2)(12)(3)(1)(2)(2)(2)(1)(1)(1)(3)(1)(2)(1)(4)(7)(3)(1)(2)(1)(1)(5)(6)(4)(4)(5)(2)(1)(6)(2)(3)(15)(6)(2)

我要回帖

更多关于 m audio fast track 的文章

 

随机推荐