Android音频系统之AudioFlinger(三)

1.1.1 PlaybackThread的循环主体

当一个PlaybackThread进入主循环后(threadLoop),音频事务就正式开启了。仔细观察的话,我们会发现这个循环中会不断地调用以“threadLoop_”开头的若干接口,比如threadLoop_mix、threadLoop_sleepTime、threadLoop_standby等等。以这样的前缀开头,是因为这些函数都是在threadLoop这个主体里被调用的,可以说代表了这个PlaybackThread所需要完成的各个操作步骤。

从上一小节可以了解到,当程序执行到PlaybackThread::onFirstRef时会去真正启动一个线程承载运行threadLoop,接下来我们具体看下这个循环体的处理流程。

bool AudioFlinger::PlaybackThread::threadLoop()

{ …        

    while (!exitPending())/*Step1.*/

    {   …    

        processConfigEvents();/*Step 2. */

        { /*把这段代码框起来的目的是限制自动锁变量_l的生命期,

           从而灵活地实现了自动锁的控制范围*/

          Mutex::Autolock  _l(mLock);

                                 …

          /*Step 3. Standby判断*/

          if(CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime)|| mSuspended> 0)) {                       

                if (!mStandby){

                   threadLoop_standby();//调用设备的

                    mStandby =true;

                   mBytesWritten = 0;

                }

                                                 …

           }

                                  /*Step 4.*/

            mMixerStatus =prepareTracks_l(&tracksToRemove);

                                   …

        }

                                /*Step5.*/

        if(CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {

            threadLoop_mix();

        } else {

           threadLoop_sleepTime();

        }

                   …

        /*Step 6.*/

        if (sleepTime == 0) {

           threadLoop_write(); //不需要休眠,有数据要写

                                   …

            mStandby = false;

        } else {

            usleep(sleepTime);//进入休眠,时间长短是sleepTime

        }

                    /*Step 7.*/

        threadLoop_removeTracks(tracksToRemove);//移除相关Track

       tracksToRemove.clear();…

    }//while (!exitPending())结束

                …

    releaseWakeLock();

    return false;

}

Step1@ PlaybackThread::threadLoop, 循环的条件是!exitPending()为true。这个函数属于Thread类,它主要通过判断内部变量mExitPending的值来得出是否要结束线程。变量mExitPending在Thread初始化时为fasle,如果后面有人通过requestExit()、requestExitAndWait等等来请求退出,这个值就会改变,从而使得PlaybackThread结束循环。

Step2@ PlaybackThread::threadLoop, 处理config事件。当有配置改变的事件发生时,可以通过sendConfigEvent来通知PlaybackThread。这个函数将把事件添加到mConfigEvents全局变量中,以供processConfigEvents进行处理。配置事件包括如下几种:

    enum io_config_event {

        OUTPUT_OPENED,//Output打开

        OUTPUT_CLOSED,//Output关闭

        OUTPUT_CONFIG_CHANGED,//Output配置改变

        INPUT_OPENED, //Input打开

        INPUT_CLOSED, //Input关闭

        INPUT_CONFIG_CHANGED,//Input配置改变

        STREAM_CONFIG_CHANGED,//Stream配置改变

        NUM_CONFIG_EVENTS

    };

Step3@ PlaybackThread::threadLoop,判断当前是否符合Standby的条件,如果是的话就调用threadLoop_standby。这个函数最终还是通过HAL层的接口来实现,如下:

mOutput->stream->common.standby(&mOutput->stream->common);

Step4@ PlaybackThread::threadLoop, 进行数据准备,prepareTracks_l这个函数非常长,我们先用伪代码的形式整理一下它所做的工作,如下所示:

AudioFlinger::PlaybackThread::mixer_stateAudioFlinger::MixerThread::prepareTracks_l(…)

{

                /*Step 1. 当前活跃的Track数量*/

    size_t  count = mActiveTracks.size();

 

    /*Step 2. 循环处理每个Track,这是函数的核心*/

                for (size_t i=0; i<count ; i++) {

        Track* track =mActiveTracks[i];//伪代码没有考虑强指针

 

        /*Step 3. FastTrack下的处理*/

        if(track is FastTrack)

        {

                                                //dosomething;

        }

        /*Step 4. 准备数据,分为以下几个小部来完成*/

                                audio_track_cblk_t*cblk = track->cblk(); //Step 4.1 数据块准备

 

        /*Step 4.2 要回放音频前,至少需要准备多少帧数据?*/

        uint32_t  minFrames = 1;//初始化

          //具体计算minFrames…

   

        /*Step 4.3 如果数据已经准备完毕*/

          //调整音量

          //其它参数设置

    }//for循环结束

   

     /*Step 5. 后续判断*/

       //返回结果,指明当前状态是否已经ready

}

现在我们针对上面的步骤来做“填空”。

Step1@ MixerThread::prepareTracks_l, mActiveTracks的数据类型是SortedVector,用于记录当前活跃的Track。它会随着新的AudioTrack的加入而扩大,也会在必要的情况下(AudioTrack工作结束、或者出错等等)remove相应的Track。

Step2&3@MixerThread::prepareTracks_l, 循环的条件就是要逐个处理该PlaybackThread中包含的Track。假如当前是一个FastTrack,我们还要做一些其它准备,这里就暂时不去涉及具体细节了。

Step4@ MixerThread::prepareTracks_l, 这一步是准备工作中最重要的,那就是缓冲数据。在学习代码细节前,我们先来了解数据传输时容易出现的underrun情况。

什么是BufferUnderrun呢?

当两个设备或进程间形成“生产者-消费者”关系时,如果生产的速度不及消费者消耗的速度,就会出现Underrun。以音频回放为例,此时用户听到的声音就可能是断断续续的,或者是重复播放当前buffer中的数据(取决于具体的实现)。

如何避免这种异常的发生?这也是Step4所要解决的问题,以下分为几个小步骤来看AudioFlinger是如何做到的。

Ø  Step4.1,取得数据块

  audio_track_cblk_t*cblk = track->cblk();

  关于audio_track_cblk_t的更多描述,可以参见后面数据流小节。

Ø  Step4.2 计算正确回放音频所需的最少帧数,初始值为1。

                                uint32_tminFrames = 1;

                                if((track->sharedBuffer() == 0) && !track->isStopped() &&!track->isPausing() &&

               (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {

            if(t->sampleRate() == (int)mSampleRate) {

                minFrames = mNormalFrameCount;

            } else {

                minFrames =(mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;               

                minFrames +=mAudioMixer->getUnreleasedFrames(track->name());                

               ALOG_ASSERT(minFrames <= cblk->frameCount);

            }

         }

当track->sharedBuffer()为0时,说明AudioTrack不是STATIC模式的,否则数据就是一次性传送的,可以参见AudioTrack小节的描述。全局变量mSampleRate 是通过mOutput->stream->common.get_sample_rate获得的,它是由HAL提供的,代表的是设备的Sampling rate。

如果两者一致的话,就采用mNormalFrameCount,这个值在readOutputParameters函数中进行初始化。如果两者不一致的话,就要预留多余的量做rounding(+1)和interpolation(+1)。另外,还需要考虑未释放的空间大小,也就是getUnreleasedFrames得到的。得出的minFrames必需小于数据块的总大小,因而最后有个ASSERT。通常情况下frameCount分配的是一个buffer的两倍,可以参见AudioTrack小节的例子。

Ø  Step4.3 数据是否准备就绪了?

上一步我们计算出了数据的最小帧值,即minFrames,接下来就该判断目前的情况是否符合这一指标了,代码如下所示:

                    if ((track->framesReady() >=minFrames) && track->isReady() &&!track->isPaused()&& !track->isTerminated())

        {//数据准备就绪,并处于ready状态

            mixedTracks++; //需要mix的Track数量增加1

            …

            /*计算音量值*/

            uint32_t vl, vr,va; //三个变量分别表示左、右声道、Aux level音量

            if(track->isMuted() || track->isPausing()||mStreamTypes[track->streamType()].mute) {

                vl = vr = va =0; //当静音时,变量直接赋0

                if (track->isPausing()) {

                   track->setPaused();

                }

            } else {               

                /*这里获得的是针对每个stream类型设置的音量值,也就是后面“音量调节”小节里最

                  后执行到的地方,在这里就起到作用了*/

                                                  float typeVolume =mStreamTypes[track->streamType()].volume;               

                float v =masterVolume * typeVolume; //主音量和类型音量的乘积

                uint32_t  vlr = cblk->getVolumeLR(); //这里得到的vlr必须经过验证是否在合理范围内

                vl = vlr &0xFFFF; //vlr的高低位分别表示vr和vl

                vr = vlr>> 16;

                if (vl >MAX_GAIN_INT) { //对vl进行合理值判断

                   ALOGV("Track left volume out of range: %04X", vl);

                    vl =MAX_GAIN_INT;

                }

                if (vr >MAX_GAIN_INT) {//对vr进行合理值判断

                   ALOGV("Track right volume out of range: %04X", vr);

                    vr =MAX_GAIN_INT;

                }

                // now applythe master volume and stream type volume

                vl =(uint32_t)(v * vl) << 12;

                vr =(uint32_t)(v * vr) << 12;

                uint16_tsendLevel = cblk->getSendLevel_U4_12();

                // send levelcomes from shared memory and so may be corrupt

                if (sendLevel> MAX_GAIN_INT) {

                    ALOGV("Track send level out of range:%04X", sendLevel);

                    sendLevel= MAX_GAIN_INT;

                }

                va =(uint32_t)(v * sendLevel);

            } …          

           mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void*)vl);

           mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void*)vr);

           mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void*)va);

            …           

        } else {//数据未准备就绪,略过。。。

  对于音量的设置还有很多细节,大家有兴趣的可以深入研究下。在得到vl、vr和va的值后,还需要把它们应用到AudioMixer中去,不过在prepareTracks_l中还只是调用mAudioMixer->setParameter设置了这些参数,真正的实现是在threadLoop_mix中,我们后面会讲到这个函数。

Step5@ MixerThread::prepareTracks_l, 通过对每个Track执行上述的处理后,最后要返回一个结果,这通常取决于:

①是否有activetrack

②active track的数据是否已经准备就绪

返回的最终值将影响到threadLoop的下一步操作。

 

完成了prepareTracks_l的分析,我们再回到前面的threadLoop。

Step5@ PlaybackThread::threadLoop, 如果上一步的数据准备工作已经完成(即返回值是MIXER_TRACKS_READY),就开始进行真正的混音操作,即threadLoop_mix,否则会休眠一定的时间——如此循环往复直到退出循环体。

void AudioFlinger::MixerThread::threadLoop_mix()

{

    int64_t pts;

                …

   mAudioMixer->process(pts);

    …

}

这样就进入AudioMixer的处理了,我们放在下一小节做统一分析。

假如数据还没有准备就绪,那么AudioFlinger将调用threadLoop_sleepTime来计算需要休眠多长时间(变量sleepTime),并在threadLoop主循环的末尾(在remove track之前)执行usleep进入休眠。

Step6@ PlaybackThread::threadLoop, 将数据写到HAL中,从而逐步写入到硬件设备中。

void AudioFlinger::PlaybackThread::threadLoop_write()

{

    mLastWriteTime =systemTime();

    mInWrite = true;

    int bytesWritten;

    if (mNormalSink != 0) {

                   …

        ssize_t framesWritten= mNormalSink->write(mMixBuffer, count);

        …

    } else {

        bytesWritten =(int)mOutput->stream->write(mOutput->stream, mMixBuffer,mixBufferSize);

    }

    if (bytesWritten > 0)mBytesWritten += mixBufferSize;

    mNumWrites++;

    mInWrite = false;

}

分为两种情况:

Ø  如果是采用了NBAIO(Non-blocking AudioI/O),即mNormalSink不为空,则通过它写入HAL

Ø  否则使用普通的AudioStreamOut(即mOutput变量)将数据输出

 

Step7@ PlaybackThread::threadLoop, 移除tracksToRemove中指示的Tracks。是否移除一个Track是在prepareTracks_l中判断中,可以概括为以下几种情况:

Ø  对于Fast Track,如果它的状态(mState)是STOPPING_2、PAUSED、TERMINATED、STOPPED、FLUSHED,或者状态是ACTIVE但underrun的次数超过限额(mRetryCount),则会被加入tracksToRemove列表中

Ø  当前的Track数据未准备就绪的情况下,且是STATICTRACK或者已经停止/暂停,也会被加入tracksToRemove列表中

在tracksToRemove列表中的Track,与其相关的output将收到stop请求(由AudioSystem::stopOutput发起)。

关于AudioFlinger中与AudioTrack、AudioPolicyService有交互的部分,我们还将在后续小节进行阐述。

发布了90 篇原创文章 · 获赞 29 · 访问量 74万+
展开阅读全文

Android 程序异常弹出消息:很抱歉,程序出现异常即将关闭,但是程序正常运行

07-26

I/art ( 314): Explicit concurrent mark sweep GC freed 5(160B) AllocSpace objects, 0(0B) LOS objects, 89% free, 126KB/1150KB, paused 144us total 19.486ms E/fpcdclient( 6015): failed to connect socket -2 E/fingerprints_service( 6015): fpcd_connect() failed D/AndroidRuntime( 6015): Shutting down VM E/AndroidRuntime( 6015): FATAL EXCEPTION: main E/AndroidRuntime( 6015): Process: com.fingerprints.service, PID: 6015 E/AndroidRuntime( 6015): java.lang.RuntimeException: Unable to create application com.fingerprints.service.FingerprintService: java.lang.RuntimeException: openNative() failed E/AndroidRuntime( 6015): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4590) E/AndroidRuntime( 6015): at android.app.ActivityThread.access$1500(ActivityThread.java:151) E/AndroidRuntime( 6015): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1367) E/AndroidRuntime( 6015): at android.os.Handler.dispatchMessage(Handler.java:102) E/AndroidRuntime( 6015): at android.os.Looper.loop(Looper.java:135) E/AndroidRuntime( 6015): at android.app.ActivityThread.main(ActivityThread.java:5288) E/AndroidRuntime( 6015): at java.lang.reflect.Method.invoke(Native Method) E/AndroidRuntime( 6015): at java.lang.reflect.Method.invoke(Method.java:372) E/AndroidRuntime( 6015): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:915) E/AndroidRuntime( 6015): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:710) E/AndroidRuntime( 6015): Caused by: java.lang.RuntimeException: openNative() failed E/AndroidRuntime( 6015): at com.fingerprints.system.FpcD.<init>(FpcD.java:245) E/AndroidRuntime( 6015): at com.fingerprints.system.FpcD.open(FpcD.java:230) E/AndroidRuntime( 6015): at com.fingerprints.service.ServiceImpl.userEvent(ServiceImpl.java:105) E/AndroidRuntime( 6015): at com.fingerprints.service.ServiceImpl.<init>(ServiceImpl.java:135) E/AndroidRuntime( 6015): at com.fingerprints.service.FingerprintService.onCreate(FingerprintService.java:32) E/AndroidRuntime( 6015): at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012) E/AndroidRuntime( 6015): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4587) E/AndroidRuntime( 6015): ... 9 more W/ActivityManager( 2215): Process com.fingerprints.service has crashed too many times: killing! W/Adreno-ES20( 278): <process_gl_state_enables:519>: GL_INVALID_ENUM W/GLConsumer( 278): [com.android.systemui.ImageWallpaper] bindTextureImage: clearing GL error: 0x500 I/Process ( 6015): Sending signal. PID: 6015 SIG: 9 W/Adreno-ES20( 278): <process_gl_state_enables:519>: GL_INVALID_ENUM W/GLConsumer( 278): [com.android.systemui.ImageWallpaper] bindTextureImage: clearing GL error: 0x500 W/Adreno-ES20( 278): <process_gl_state_enables:519>: GL_INVALID_ENUM W/GLConsumer( 278): [com.android.systemui.ImageWallpaper] bindTextureImage: clearing GL error: 0x500 I/ActivityManager( 2215): Process com.fingerprints.service (pid 6015) has died W/Adreno-ES20( 278): <process_gl_state_enables:519>: GL_INVALID_ENUM W/GLConsumer( 278): [com.android.systemui.ImageWallpaper] bindTextureImage: clearing GL error: 0x500 W/Adreno-ES20( 278): <process_gl_state_enables:519>: GL_INVALID_ENUM W/GLConsumer( 278): [com.android.systemui.ImageWallpaper] bindTextureImage: clearing GL error: 0x500 问答

Android运行出现Fatal signal 6 (SIGABRT)的错误

12-04

在运行自己的项目时,出现以下错误 ``` heap corruption detected by dlfree 12-04 15:32:01.671 3093 3231 F libc : Fatal signal 6 (SIGABRT), code -6 in tid 3231 (Thread-287) 12-04 15:32:01.726 3232 3232 I AEE/AED : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 12-04 15:32:01.726 3232 3232 I AEE/AED : Build fingerprint: 'alps/full_q5c/q5c:6.0/MRA58K/1510916390:user/test-keys' 12-04 15:32:01.726 3232 3232 I AEE/AED : Revision: '0' 12-04 15:32:01.727 3232 3232 I AEE/AED : ABI: 'arm' 12-04 15:32:01.727 3232 3232 I AEE/AED : pid: 3093, tid: 3231, name: Thread-287 >>> com.face.arcface <<< 12-04 15:32:01.727 3232 3232 I AEE/AED : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- 12-04 15:32:01.753 3232 3232 I AEE/AED : Abort message: 'heap corruption detected by dlfree' 12-04 15:32:01.754 3232 3232 I AEE/AED : r0 00000000 r1 00000c9f r2 00000006 r3 a05f8978 12-04 15:32:01.754 3232 3232 I AEE/AED : r4 a05f8980 r5 a05f8930 r6 00000002 r7 0000010c 12-04 15:32:01.754 3232 3232 I AEE/AED : r8 b6d0bed8 r9 b8ac25a8 sl 71272718 fp a05f86bc 12-04 15:32:01.754 3232 3232 I AEE/AED : ip 00000006 sp a05f8490 lr b6ce8b3d pc b6ce9af4 cpsr 400f0010 12-04 15:32:01.787 356 1091 D FrameworkListener: dispatchCommand data = (getaddrinfo googleads.g.doubleclick.net ^ 1024 0 1 0 0) 12-04 15:32:01.789 356 3236 D SocketClient: SocketClient sendData done: 222 12-04 15:32:01.789 356 3236 D SocketClient: SocketClient sendData done: 12-04 15:32:01.789 356 3236 D SocketClient: SocketClient sendData done: 12-04 15:32:01.790 3232 3232 I AEE/AED : 12-04 15:32:01.790 3232 3232 I AEE/AED : backtrace: 12-04 15:32:01.790 3232 3232 I AEE/AED : #00 pc 00043af4 /system/lib/libc.so (tgkill+12) 12-04 15:32:01.790 3232 3232 I AEE/AED : #01 pc 00042b39 /system/lib/libc.so (pthread_kill+32) 12-04 15:32:01.790 3232 3232 I AEE/AED : #02 pc 0001babb /system/lib/libc.so (raise+10) 12-04 15:32:01.790 3232 3232 I AEE/AED : #03 pc 00018c6d /system/lib/libc.so (__libc_android_abort+34) 12-04 15:32:01.790 3232 3232 I AEE/AED : #04 pc 00016814 /system/lib/libc.so (abort+4) 12-04 15:32:01.790 3232 3232 I AEE/AED : #05 pc 0001a6cf /system/lib/libc.so (__libc_fatal+16) 12-04 15:32:01.790 3232 3232 I AEE/AED : #06 pc 0002ef69 /system/lib/libc.so (__bionic_heap_corruption_error+8) 12-04 15:32:01.790 3232 3232 I AEE/AED : #07 pc 000311b9 /system/lib/libc.so (dlfree+308) 12-04 15:32:01.790 3232 3232 I AEE/AED : #08 pc 00000e9f /data/app/com.syxrobot.arcface-1/lib/arm/libimage.so 12-04 15:32:01.790 3232 3232 I AEE/AED : #09 pc 0027ed89 /data/app/com.syxrobot.arcface-1/oat/arm/base.odex (offset 0x1ca000) (int com.guo.android_extend.image.ImageConverter.image_uninit(int)+84) 12-04 15:32:01.790 3232 3232 I AEE/AED : #10 pc 0027eee7 /data/app/com.syxrobot.arcface-1/oat/arm/base.odex (offset 0x1ca000) (void com.guo.android_extend.image.ImageConverter.destroy()+106) 12-04 15:32:01.790 3232 3232 I AEE/AED : #11 pc 002acfa9 /data/app/com.syxrobot.arcface-1/oat/arm/base.odex (offset 0x1ca000) (void com.face.arcface.RegisterActivity$2.run()+1100) 12-04 15:32:01.791 3232 3232 I AEE/AED : #12 pc 0243e141 /system/framework/arm/boot.oat (offset 0x2420000) ``` 补充: 运行我自己的项目时,这个错误并不是一定会出现,但是出现后,会连续很多次都会崩溃,而且每次都是同一张图片,之后换图片可以继续工作,再点击会崩溃的图片时,有可能会没问题,如果没问题,之后就不会出现问题,运行官方DEMO这张图片,是一定会出问题,不知道是图片问题,还是什么,百度谷歌都没找到答案 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览