• 数据、循环Android音频系统之AudioFlinger(三)by小雨


    在本文中,我们要主介绍数据、循环-的内容,自我感觉有个不错的建议和大家分享下

        

    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有交互的部分,我们还将在后续节小停止阐述。

    文章结束给大家分享下程序员的一些笑话语录: 开发时间
      项目经理: 如果我再给你一个人,那可以什么时候可以完工?程序员: 3个月吧!项目经理: 那给两个呢?程序员: 1个月吧!
    项目经理: 那100呢?程序员: 1年吧!
    项目经理: 那10000呢?程序员: 那我将永远无法完成任务.

  • 相关阅读:
    如何查看操作系统的具体版本号
    Review of Image Super-resolution Reconstruction Based on Deep Learning
    opencv imshow图片显示不全
    Javaweb文件下载异常
    Microsoft Edge出现错误代码:STATUS_INVALID_IMAGE_HASH
    Javaweb导入excel数据
    Java读取execl数据
    浏览器网页左上角小图标实现方式
    Java LDAP验证
    Java JPA新增数据生成UUID
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3022850.html
Copyright © 2020-2023  润新知