国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Android音頻系統(tǒng)之AudioTrack(一)

1.1 AudioTrack

1.1.1 AudioTrack應(yīng)用實(shí)例

對(duì)于Android應(yīng)用開(kāi)發(fā)人員來(lái)講,音頻回放最熟悉的莫過(guò)于MediaPlayer,而AudioTrack相信用的人相對(duì)會(huì)少很多。這是因?yàn)镸ediaPlayer提供了更完整的封裝和狀態(tài)控制,使得我們用很少的代碼就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的音樂(lè)播放器。而相比MediaPlayer,AudioTrack更為精練、高效,實(shí)際上MediaPlayerService的內(nèi)部實(shí)現(xiàn)就是使用了AudioTrack。

AudioTrack被用于PCM音頻流的回放,在數(shù)據(jù)傳送上它有兩種方式:

?  調(diào)用write(byte[],int,int)或write(short[],int,int)把音頻數(shù)據(jù)“push”到AudioTrack中。

?  與之相對(duì)的,當(dāng)然就是“pull”形式的數(shù)據(jù)獲取,即數(shù)據(jù)接收方主動(dòng)索取的過(guò)程,如下圖所示:

除此之外,AudioTrack還同時(shí)支持static和streaming兩種模式:

§  static

靜態(tài)的言下之意就是數(shù)據(jù)一次性交付給接收方。好處是簡(jiǎn)單高效,只需要進(jìn)行一次操作就完成了數(shù)據(jù)的傳遞;缺點(diǎn)當(dāng)然也很明顯,對(duì)于數(shù)據(jù)量較大的音頻回放,顯然它是無(wú)法勝任的,因而通常只用于播放鈴聲、系統(tǒng)提醒等對(duì)內(nèi)存小的操作

§  streaming

流模式和網(wǎng)絡(luò)上播放視頻是類(lèi)似的,即數(shù)據(jù)是按照一定規(guī)律不斷地傳遞給接收方的。理論上它可用于任何音頻播放的場(chǎng)景,不過(guò)我們一般在以下情況下采用:

?  音頻文件過(guò)大

?  音頻屬性要求高,比如采樣率高、深度大的數(shù)據(jù)

?  音頻數(shù)據(jù)是實(shí)時(shí)產(chǎn)生的,這種情況就只能用流模式了

下面我們選取AudioTrackTest.java為例來(lái)講解,先從使用者的角度來(lái)了解下AudioTrack。

/*cts/tests/tests/media/src/android/media/cts*/

    public voidtestSetStereoVolumeMax()  throwsException {

        final String TEST_NAME= "testSetStereoVolumeMax";

        final int TEST_SR =22050;

        final int TEST_CONF =AudioFormat.CHANNEL_CONFIGURATION_STEREO;

        final int TEST_FORMAT= AudioFormat.ENCODING_PCM_16BIT;

        final int TEST_MODE =AudioTrack.MODE_STREAM;

        final intTEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;

        // --------initialization --------------

                                /*Step1.*/

        int  minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);

         /*Step 2.*/

        AudioTrack  track = newAudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF,

                                                                                                                                                 TEST_FORMAT, 2 * minBuffSize,TEST_MODE);

        byte data[] = newbyte[minBuffSize];

        // -------- test--------------

        track.write(data, OFFSET_DEFAULT, data.length);

        track.write(data, OFFSET_DEFAULT, data.length);

        track.play();

        float maxVol =AudioTrack.getMaxVolume();

        assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS);

        // -------- tear down--------------

        track.release();

    }

這個(gè)TestCase是測(cè)試立體聲左右聲道最大音量的。順便提一下,關(guān)于自動(dòng)化測(cè)試的更多描述,可以參照本書(shū)最后一個(gè)篇章。用例中涉及到AudioTrack的常規(guī)操作都用高顯標(biāo)示出來(lái)了。可見(jiàn)大概是這么幾個(gè)步驟:

Step1@ testSetStereoVolumeMax,getMinBufferSize

字面意思就是獲取最小的buffer大小,這個(gè)buffer將用于后面AudioTrack的構(gòu)造函數(shù)。它是AudioTrack可以正常使用的一個(gè)最低保障,根據(jù)官方的建議如果音頻文件本身要求較高的話,最好可以采用比MinBufferSize更大的數(shù)值。這個(gè)函數(shù)的實(shí)現(xiàn)相對(duì)簡(jiǎn)單:

static public int getMinBufferSize(int sampleRateInHz, int channelConfig,int audioFormat) {

        int channelCount = 0;

        switch(channelConfig){

        caseAudioFormat.CHANNEL_OUT_MONO:

        caseAudioFormat.CHANNEL_CONFIGURATION_MONO:

            channelCount = 1;

            break;

        case AudioFormat.CHANNEL_OUT_STEREO:

        caseAudioFormat.CHANNEL_CONFIGURATION_STEREO:

            channelCount = 2;

            break;

        default:

            …

        }

首先得出聲道數(shù),目前最多只支持雙聲道。

        if ((audioFormat !=AudioFormat.ENCODING_PCM_16BIT)

            && (audioFormat !=AudioFormat.ENCODING_PCM_8BIT)) {

            returnAudioTrack.ERROR_BAD_VALUE;

        }

得出音頻采樣深度,只支持8bit和16bit兩種。

        // sample rate, notethese values are subject to change

        if ( (sampleRateInHz< 4000) || (sampleRateInHz > 48000) ) {

           loge("getMinBufferSize(): " + sampleRateInHz +"Hz is nota supported sample rate.");

            returnAudioTrack.ERROR_BAD_VALUE;

        }

得出采樣頻率,支持的范圍是4k-48kHZ.

        int size =native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);

        …

    }

也就是說(shuō)最小buffer大小取決于采樣率、聲道數(shù)和采樣深度三個(gè)屬性。那么具體是如何計(jì)算的呢?我們接著看下native層代碼實(shí)現(xiàn):

frameworks/base/core/jni/android_media_AudioTrack.cpp

static jint android_media_AudioTrack_get_min_buff_size(JNIEnv*env,  jobject thiz,

    jint sampleRateInHertz,jint nbChannels, jint audioFormat) {

    int frameCount = 0;

    if(AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,

                                                                                                 sampleRateInHertz) != NO_ERROR) {

        return -1;

    }

    return  frameCount * nbChannels * (audioFormat ==javaAudioTrackFields.PCM16 ? 2 : 1);

}

這里又調(diào)用了getMinFrameCount,這個(gè)函數(shù)用于確定至少需要多少Frame才能保證音頻正常播放。那么Frame代表了什么意思呢?可以想象一下視頻中幀的概念,它代表了某個(gè)時(shí)間點(diǎn)的一幅圖像。這里的Frame也是類(lèi)似的,它應(yīng)該是指某個(gè)特定時(shí)間點(diǎn)時(shí)的音頻數(shù)據(jù)量,所以android_media_AudioTrack_get_min_buff_size中最后采用的計(jì)算公式就是:

至少需要多少幀*每幀數(shù)據(jù)量

= frameCount * nbChannels * (audioFormat ==javaAudioTrackFields.PCM16 ? 2 : 1);

公式中frameCount就是需要的幀數(shù),每一幀的數(shù)據(jù)量又等于:

Channel數(shù)*每個(gè)Channel數(shù)據(jù)量= nbChannels * (audioFormat ==javaAudioTrackFields.PCM16 ? 2 : 1)

層層返回getMinBufferSize就得到了保障AudioTrack正常工作的最小緩沖區(qū)大小了。

Step2@ testSetStereoVolumeMax,創(chuàng)建AudioTrack實(shí)例

有了minBuffSize后,我們就可以創(chuàng)建一個(gè)AudioTrack對(duì)象了。它的構(gòu)造函數(shù)原型是:

public AudioTrack (int streamType, int sampleRateInHz, intchannelConfig, int audioFormat, int bufferSizeInBytes, int mode)

除了倒數(shù)第二個(gè)參數(shù)是計(jì)算出來(lái)的,其它入?yún)⒃谶@個(gè)TestCase中都是直接指定的。比如streamType是STREAM_MUSIC,sampleRateInHz是22050等等。如果是編寫(xiě)一個(gè)音樂(lè)播放器,這些參數(shù)自然都是需要通過(guò)分析音頻文件得出來(lái)的,所幸的是Android提供了更加易用的MediaPlayer,使得我們不需要理會(huì)這些瑣碎細(xì)節(jié)。

創(chuàng)建AudioTrack的一個(gè)重要任務(wù)就是和AudioFlinger建立聯(lián)系,它是由native層的代碼來(lái)實(shí)現(xiàn)的:

    public AudioTrack(intstreamType, int sampleRateInHz, int channelConfig, int audioFormat,

            intbufferSizeInBytes, int mode, int sessionId)

    throwsIllegalArgumentException {

        …

        int initResult = native_setup(new WeakReference<AudioTrack>(this),

                mStreamType,mSampleRate, mChannels, mAudioFormat,

               mNativeBufferSizeInBytes, mDataLoadMode, session);

                                …

    }

這里調(diào)用了native_setup來(lái)創(chuàng)建一個(gè)本地AudioTrack對(duì)象,如下:

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

static int  android_media_AudioTrack_native_setup(JNIEnv*env, jobject thiz, jobject weak_this,

        jint streamType, jintsampleRateInHertz, jint javaChannelMask,

        jint audioFormat, jintbuffSizeInBytes, jint memoryMode, jintArray jSession)

{   

    …

    sp<AudioTrack>lpTrack = new AudioTrack();

    …

AudioTrackJniStorage* lpJniStorage =new AudioTrackJniStorage();

創(chuàng)建一個(gè)Storage對(duì)象,直覺(jué)告訴我們這可能是存儲(chǔ)音頻數(shù)據(jù)的地方,后面我們?cè)僭敿?xì)分析。

    …

                if (memoryMode== javaAudioTrackFields.MODE_STREAM) {

        lpTrack->set(…

                                   audioCallback, //回調(diào)函數(shù)

                                   &(lpJniStorage->mCallbackData),//回調(diào)數(shù)據(jù)

            0,

            0,//shared mem

            true,// thread cancall Java

            sessionId);//audio session ID

    } else if (memoryMode ==javaAudioTrackFields.MODE_STATIC) {

        …

        lpTrack->set(…    

                                   audioCallback, &(lpJniStorage->mCallbackData),0,      

            lpJniStorage->mMemBase,// shared mem

            true,// thread cancall Java

            sessionId);//audio session ID

    }

…// native_setup結(jié)束

函數(shù)native_setup首先新建了一個(gè)AudioTrack(native)對(duì)象,然后進(jìn)行各種屬性的計(jì)算,最后調(diào)用set函數(shù)為AudioTrack設(shè)置這些屬性——我們只保留兩種內(nèi)存模式(STATIC和STREAM)有差異的地方,便于大家比對(duì)理解。對(duì)于靜態(tài)數(shù)據(jù),入?yún)⒅械牡箶?shù)第三個(gè)是lpJniStorage->mMemBase,而STREAM類(lèi)型時(shí)為null(0)。

到目前為止我們還沒(méi)有看到它與AudioFlinger有交互的地方,看來(lái)謎底應(yīng)該就在這個(gè)set函數(shù)中了。

status_t  AudioTrack::set(…

                   callback_t  cbf, void* user, int notificationFrames, constsp<IMemory>& sharedBuffer,

        boolthreadCanCallJava, int sessionId)

{

    AutoMutex lock(mLock);

    …

    if (streamType ==AUDIO_STREAM_DEFAULT) {

        streamType =AUDIO_STREAM_MUSIC;

    }

當(dāng)不設(shè)置streamType時(shí),會(huì)被改為默認(rèn)的AUDIO_STREAM_MUSIC。

    …

    if (format ==AUDIO_FORMAT_DEFAULT) {

        format =AUDIO_FORMAT_PCM_16_BIT;

    }

    if (channelMask == 0) {

        channelMask =AUDIO_CHANNEL_OUT_STEREO;

    }

采樣深度和聲道數(shù)默認(rèn)為16bit和立體聲

    …

    if (format ==AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) {

        ALOGE("8-bit datain shared memory is not supported");

        return BAD_VALUE;

    }

當(dāng)sharedBuffer!=0時(shí)表明是STATIC模式,也就是說(shuō)靜態(tài)數(shù)據(jù)模式下只支持16bit深度否則就報(bào)錯(cuò)直接返回,這點(diǎn)要特別注意。

    …

audio_io_handle_t output = AudioSystem::getOutput(streamType,sampleRate, format, channelMask, flags);

通過(guò)上述的有效性檢查后,AudioTrack接著就可以使用底層的音頻服務(wù)了。那么是直接調(diào)用AudioFlinger服務(wù)提供的接口嗎?理論上這樣子做也是可以的,但Android系統(tǒng)考慮得更細(xì),它在AudioTrack與底層服務(wù)間又提供了AudioSystem和AudioService。其中前者同時(shí)提供了Java和native兩層的實(shí)現(xiàn),而AudioService則只有native層的實(shí)現(xiàn)。這樣子就降低了使用者(AudioTrack)與底層實(shí)現(xiàn)(AudioPolicyService、AudioFlinger等等)間的藕合。換句話說(shuō),不同版本的Android音頻系統(tǒng)通常改動(dòng)很大,但只要AudioSystem和AudioService向上的接口不變,那么AudioTrack就不需要做修改。

所以上面的getOutput是由AudioSystem來(lái)提供的,可以猜測(cè)到其內(nèi)部只是做了些簡(jiǎn)單的中轉(zhuǎn)功能,最終還是得由AudioPolicyService/AudioFlinger來(lái)實(shí)現(xiàn)。這個(gè)getOutput尋找最適合當(dāng)前AudioTrack的audio interface以及Output輸出(也就是前面通過(guò)openOutput打開(kāi)的通道),然后AudioTrack會(huì)向這個(gè)Output申請(qǐng)一個(gè)Track。

    …

    mVolume[LEFT] = 1.0f;

mVolume[RIGHT] = 1.0f; /*左右聲道的初始值都是最大,另外還有其它成員變量都會(huì)在這里賦值,

                      我們直接省略掉了*/

    if (cbf != NULL) {

        mAudioTrackThread =new AudioTrackThread(*this, threadCanCallJava);

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

}

    status_t  status = createTrack_l(…sharedBuffer, output);

    …

} //AudioTrack::set函數(shù)結(jié)束

因?yàn)閏bf是audioCallback不為空,所以這里會(huì)啟動(dòng)一個(gè)AudioTrack線程。這個(gè)線程是用于AudioTrack(native)與AudioTrack(java)間的數(shù)據(jù)事件通知的,這就為上層應(yīng)用處理事件提供了一個(gè)入口,包括:

EVENT_MORE_DATA = 0,  /*請(qǐng)求寫(xiě)入更多數(shù)據(jù)*/

EVENT_UNDERRUN = 1,  /*PCM 緩沖發(fā)生了underrun*/

EVENT_LOOP_END = 2,   /*到達(dá)loop end,如果loop count不為空的話

                         將從loop start重新開(kāi)始回放*/

EVENT_MARKER = 3,     /*Playback head在指定的位置,參考setMarkerPosition*/

EVENT_NEW_POS = 4,   /*Playback head在一個(gè)新的位置,參考setPositionUpdatePeriod */

EVENT_BUFFER_END = 5 /*Playback head在buffer末尾*/

AudioTrack在AudioFlinger中是以Track來(lái)管理的。不過(guò)因?yàn)樗鼈冎g是跨進(jìn)程的關(guān)系,自然需要一個(gè)“橋梁”來(lái)維護(hù),這個(gè)溝通的媒介是IAudioTrack(這有點(diǎn)類(lèi)似于顯示系統(tǒng)中的IWindow)。函數(shù)createTrack_l除了為AudioTrack在AudioFlinger中申請(qǐng)一個(gè)Track外,還會(huì)建立兩者間IAudioTrack橋梁:

status_t  AudioTrack::createTrack_l(

        audio_stream_type_t  streamType,uint32_t sampleRate, audio_format_tformat, uint32_t channelMask,

        int frameCount,audio_output_flags_t flags, const sp<IMemory>& sharedBuffer,

        audio_io_handle_t  output)

{

const sp<IAudioFlinger>&  audioFlinger = AudioSystem::get_audio_flinger();

獲得AudioFlinger服務(wù),還記得上一小節(jié)的介紹嗎?AudioFlinger在ServiceManager中注冊(cè),以“media.audio_flinger”為服務(wù)名。

      …

   IAudioFlinger::track_flags_t  trackFlags = IAudioFlinger::TRACK_DEFAULT;

    …

sp<IAudioTrack> track =audioFlinger->createTrack(getpid(),streamType, sampleRate,format,

     channelMask, frameCount, trackFlags, sharedBuffer, output, tid,&mSessionId, &status);

//未完待續(xù)…

利用AudioFlinger創(chuàng)建一個(gè)IAudioTrack,這是它與AudioTrack之間的跨進(jìn)程通道。除此之處,AudioFlinger還做了什么?我們深入AudioFlinger分析下。

sp<IAudioTrack> AudioFlinger::createTrack(…

        constsp<IMemory>& sharedBuffer, audio_io_handle_t output,

        pid_t tid, int*sessionId, status_t *status)

{

   sp<PlaybackThread::Track>  track;

    sp<TrackHandle>trackHandle;

                …

    PlaybackThread *thread = checkPlaybackThread_l(output);

    PlaybackThread*effectThread = NULL;

    …

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

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

                …

    if (lStatus == NO_ERROR) {

        trackHandle = new  TrackHandle(track);

    } else {

        client.clear();

        track.clear();

    }

    return trackHandle;

}

我們只留下createTrack中最重要的幾個(gè)步驟,即:

·          AudioFlinger::checkPlaybackThread_l

在AudioFlinger::openOutput時(shí),產(chǎn)生了全局唯一的audio_io_handle_t值,這個(gè)值是與PlaybackThread相對(duì)應(yīng)的,它作為mPlaybackThreads鍵值對(duì)的key值存在。

當(dāng)AudioTrack調(diào)用createTrack時(shí),需要傳入這個(gè)全局標(biāo)記值,checkPlaybackThread_l借此找到匹配的PlaybackThread

·          PlaybackThread::createTrack_l

找到匹配的PlaybackThread后,還需要在其內(nèi)部創(chuàng)建一個(gè)PlaybackThread::Track對(duì)象(所有Track都由PlaybackThread::mTracks全局變量管理),這些工作由PlaybackThread::createTrack_l完成。我們以下圖表示它們之間的關(guān)系:

·          new TrackHandle

TrackHandle實(shí)際上就是IAudioTrack,后續(xù)AudioTrack將利用這個(gè)binder服務(wù)來(lái)調(diào)用getCblk等接口

我們?cè)俳又懊鍭udioTrack::createTrack_l“未完待續(xù)”的部分往下看。

    …

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

    …

    mAudioTrack = track;

    mCblkMemory = cblk;

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

    …

    if (sharedBuffer == 0) {

        mCblk->buffers =(char*)mCblk + sizeof(audio_track_cblk_t);

    } else {

        mCblk->buffers =sharedBuffer->pointer();

       mCblk->stepUser(mCblk->frameCount);

    }

    …

    return NO_ERROR;

}

事實(shí)上當(dāng)PlaybackThread創(chuàng)建一個(gè)PlaybackThread::Track對(duì)象時(shí),所需的緩沖區(qū)空間就已經(jīng)分配了。這塊空間是可以跨進(jìn)程共享的,所以AudioTrack可以通過(guò)track->getCblk()來(lái)獲取。看起來(lái)很簡(jiǎn)單的一句話,但其中涉及到很多的細(xì)節(jié),我們會(huì)在后面的數(shù)據(jù)流小節(jié)做集中分析。

到目前為止,AudioTrack已經(jīng)可以通過(guò)IAudioTrack(即上面代碼段中的track變量)來(lái)調(diào)用AudioFlinger提供的服務(wù)了。我們以序列圖來(lái)總結(jié)這一小節(jié):

  

圖 13?22 AudioTrack的創(chuàng)建流程

創(chuàng)建了AudioTrack后,應(yīng)用實(shí)例通過(guò)不斷寫(xiě)入(AudioTrack::write)數(shù)據(jù)來(lái)回放音頻,這部分代碼與音頻數(shù)據(jù)流有關(guān),我們也放在后面小節(jié)中分析。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Android 音頻系統(tǒng):從 AudioTrack 到 AudioFlinger
Android Audio audio
Android深入淺出之Audio 第一部分 AudioTrack分析
Android音頻系統(tǒng)之AudioFlinger(三)
看到的一篇比較好的AudioFlinger分析(2)
android MediaPlayer Stagefright架構(gòu)(音頻)圖解
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服