日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Android音频(6)——音频系统分析

發布時間:2023/12/19 综合教程 32 生活家
生活随笔 收集整理的這篇文章主要介紹了 Android音频(6)——音频系统分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、AudioPolicyService啟動過程分析

1. 播放聲音,聲音從哪個設備播放出來是由audio policy決定的。

2.在每一個聲卡,對應一個output,一個output對應系統中都有一個線程與其對應。

3.對硬件的訪問操作是由AudioFlinger來完成的

4.AudioPolicyService在啟動時會去讀取解析配置文件/system/etc/audio_policy.conf 根據配置文件來操作AudioFlinger來打開output,創建線程。

5.tiny4412上的配置文件

# cat /system/etc/audio_policy.conf

global_configuration {
  attached_output_devices AUDIO_DEVICE_OUT_SPEAKER
  default_output_device AUDIO_DEVICE_OUT_SPEAKER
  attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC
}

audio_hw_modules { //注意這里是modules,里面的每一項都表示一個module
  primary {    //這是一個module,一個module對一個廠家提供的一個.so文件
    outputs { //注意這是outputs,里面的每一項都是一個output
      primary { //這是一個output,里面的參數是對這個output的配置,格式“name value”
        sampling_rates 44100
        channel_masks AUDIO_CHANNEL_OUT_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL
        flags AUDIO_OUTPUT_FLAG_PRIMARY
      }
    }
    inputs {
      primary {
        sampling_rates 8000|11025|12000|16000|22050|24000|32000|44100|48000
        channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_WIRED_HEADSET|AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET|AUDIO_DEVICE_IN_AUX_DIGITAL|AUDIO_DEVICE_IN_VOICE_CALL
      }
    }
  }
}

6.AudioPolicyService啟動過程分析

a. 加載解析/vendor/etc/audio_policy.conf或/system/etc/audio_policy.conf
   對于配置文件里的每一個module項, new HwModule(name), 放入mHwModules數組
   對于module里的每一個output, new IOProfile, 放入module的mOutputProfiles
   對于module里的每一個input, new IOProfile, 放入module的mInputProfiles
b. 根據module的name加載廠家提供的so文件 (通過AudioFlinger來加載)    這里很重要!!!
c. 打開對應的output                     (通過AudioFlinger來open output)

7.HwModule是來自配置文件中的audio_hw_modules中的成員primary

8.instantiate()用于注冊服務

main() //framework/main_mediaserver.cpp
    AudioPolicyService::instantiate(); //framework/main_mediaserver.cpp
這個instantiate()實現在BinderService類中,用于add Service.

二、AudioFlinger啟動過程分析

參考002 UML

1.加載的.so文件在/system/lib/hw下

2 AudioFlinger啟動過程分析

a. 注冊AudioFlinger服務
b. 被AudioPolicyService調用以打開廠家提供的so文件
b.1 加載哪個so文件? 文件名是什么? 文件名從何而來? 
    名字從/system/etc/audio_policy.conf得到module的名字為: primary,所以so文件就是 : audio.primary.XXX.so, eg. audio.primary.tiny4412.so(來自audio_hw_hal.cpp和AudioHardware.cpp)

b.2 該so文件由什么源文件組成? 查看Android.mk
    audio.primary.$(TARGET_DEVICE) : device/friendly-arm/common/libaudio/AudioHardware.cpp
                                     libhardware_legacy
    libhardware_legacy :     hardware/libhardware_legacy/audio/audio_hw_hal.cpp                                 

    結論:主要是由AudioHardware.cpp和audio_hw_hal.cpp構成。

b.3 對硬件的封裝:
    AudioFlinger.cpp   : 把硬件封裝成AudioHwDevice (放入mAudioHwDevs數組中)
    audio_hw_hal.cpp   : 把硬件封裝成audio_hw_device
    廠家               : 把硬件封裝成AudioHardware (派生自: AudioHardwareInterface)
    
    AudioHwDevice是對audio_hw_device的封裝,
    audio_hw_device中函數的實現要通過AudioHardware類對象

c. 被AudioPolicyService調用來open output、創建playback thread 

三、AudioTrack創建過程

1.AudioTrack::AudioTrack()調用的set()中創建一個AudioTrack并與硬件掛鉤

2.Android中使用一個output來描述聲卡的輸出通道。一個output對應一個playbackThread,這些信息由AudioFilnger決定

3.AudioTrack創建過程概述

a.1 C++實現測試程序: 
    frameworks/base/media/tests/audiotests/shared_mem_test.cpp
    內容為自己構造一個正弦波音頻,一直播放知道Ctrl+C結束。

a.2 java實現的測試程序:(目前還沒測試)
    frameworks/base/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java

播放聲音時都要創建AudioTrack對象(這個AudioTrack對象可以是C++實現的,也可以是Java實現的),
java的AudioTrack對象創建時會導致C++的AudioTrack對象被創建; 所以分析的核心是c++的AudioTrack類, 創建AudioTrack時涉及一個重要函數: set

b. 猜測創建過程的主要工作
b.1 set()使用AudioTrack的屬性, 根據AudioPolicy找到對應的output、playbackThread(一個output就對應一個playbackThread)
b.2 在playbackThread中創建對應的track
b.3 APP的AudioTrack 和 playbackThread的mTracks中的track之間建立共享內存

c. 源碼時序圖 003 UML

四、AudioTrack創建過程_選擇output

1. AudioTrack創建過程_選擇output

a. APP構造AudioTrack時指定了 stream type
b. AudioTrack::setAttributesFromStreamType 根據stream type設置屬性
c. AudioPolicyManager::getStrategyForAttr  根據屬性獲得聲音的strategy(類別)
d. AudioPolicyManager::getDeviceForStrategy 根據類別選擇聲音從哪個device里面播放
e. AudioPolicyManager::getOutputForDevice    根據device獲得output.
    //可能有多個output支持同一類別的一個device
       e.1 AudioPolicyManager::getOutputsForDevice //獲取這個device的所有output
       e.2 output = selectOutput(outputs, flags, format);//從這個device的多個output中選擇某一個output

2.AudioPolicyManager.mOutputs對應于已打開的output,每一項都含有mProfile,這個mProfile是來自/system/etc/audio_policy.conf,mProfile中指明該output支持哪些device。

DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mOutputs;

3.有可能有多個output都支持某個device,怎么從多個output中取出最合適的output?

AudioPolicyManager::selectOutput中
首先匹配flag,找出最吻合的output:
a.APP創建AudioTrack時會傳入flag
b.output對應的profile中也有flag(來自/system/etc/audio_policy.conf中指定的flag),
c.使用上述a,b的flag進行比較,取出吻合度最高的output.
如果吻合度相同:
如果primary output支持該設備,選它,否則取出第一個output。

五、AudioTrack創建過程_Track和共享內存

1.一個output對應一個播放設備(聲卡),也對應一個播放線程。音頻數據流向playbackThread ---> output ---> 聲卡設備

2.應用程序中的AudioTrack和播放線程中的mTrack中的成員是一一對應的。它們之間通過共享內存來傳遞數據。

3. AudioTrack創建過程_Track和共享內存
回顧:
a. APP創建AudioTrack <-----------------> AudioFlinger中PlaybackThread創建對應的Track
b. APP給AudioTrack提供音頻數據有2種方式: 一次性提供(MODE_STATIC)、邊播放邊提供(MODE_STREAM)

問:
a. 音頻數據存在buffer中, 這個buffer由誰提供? APP 還是 PlaybackThread ?
(1)MODE_STATIC:若是App一次性提供音頻數據,那么buffer是由App創建的,因為App更方便知道buffer的大小屬性。
(2)MODE_STREAM:若App是邊播放邊提供音頻數據,那么就由playbackThread創建,這樣App實現起來比較方便。

b. APP提供數據, PlaybackThread消耗數據, 如何同步?
(1)MODE_STATIC:一次性提供,就不需要同步,這是一前一后的事情。
(2)MODE_STREAM:邊播放邊提供音頻數據的時候,這是典型的生產者消費者問題,使用環形緩沖區來同步。

4.playbackThread中的mTracks是個鏈表,上面的每一個Track都對應應用程序中的一個AudioTrack. 也正是由于App創建了
AudioTrack才導致mTracks鏈表上的track被創建。

5.App和playbackThread是處于不同的進程中的,音頻數據可以通過binder通信,但是效率不是很高,所以使用共享內存來傳遞數據。

6.測試程序shared_mem_test.cpp中使用的就是一次性提供:

AudioTrackTest::Test01() 
    iMem = heap->allocate(BUF_SZ*sizeof(short));
    memcpy(p, smpBuf, BUF_SZ*sizeof(short));
     sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
               rate,
               AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
               AUDIO_CHANNEL_OUT_MONO,
               iMem); /*這里一次性把音頻數據傳下去了*/

  對于C++的實現,參數iMem若是不為NULL就表示這個共享內存是由App創建的,之后就會使用MODE_STATIC這個方式傳輸數據;若為NULL,表明App并沒有去創建這個buffer,將由playbackThread來創建這個共享內存,之后將通過MODE_STREAM方式傳輸數據;

AudioTrack它并沒有一個模式來分辨是使用MODE_STATIC還是MODE_STREAM,它通過參數iMem是NULL還是非NULL來分辨使用哪種方式。

C++的AudioTrack類不需要指定模式,但是Java的AudioTrack需要:

MediaAudioTrackTest.java
testSetPlaybackHeadPositionTooFar()
    int TEST_MODE = AudioTrack.MODE_STREAM;
    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 2*minBuffSize, TEST_MODE);

testSetPlaybackRateUninit()
    int TEST_MODE = AudioTrack.MODE_STATIC;
    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);

7.AudioTrack構造函數:

AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId) //AudioTrack.java
    //本地初始化
    native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                mSampleRate, mChannels, mAudioFormat,
                mNativeBufferSizeInBytes, mDataLoadMode, session);
        //對應這個JNI函數
        android_media_AudioTrack_setup //android_media_AudioTrack.cpp
            //本地的AudioTrack類對象,沒有傳入任何參數
            sp<AudioTrack> lpTrack = new AudioTrack();
            case MODE_STREAM:
                //對于MODE_STREAM模式,直接調用set(),沒有在App進程中分配內存
                lpTrack->set() 
            case MODE_STATIC:
                //對于MODE_STATIC模式,會在App進程中分配buffer
                lpJniStorage->allocSharedMem(buffSizeInBytes)
                lpTrack->set()

六、音頻數據的傳遞

1.App這邊相關的代碼在AudioTrack.cpp中,playbackTread的處理函數在Track.cpp中。

2.mProxy用于App端管理共享內存

// frameworksavmedialibmediaAudioTrack.cpp
AudioTrack::createTrack_l()
    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        mProxy = mStaticProxy;
    }

mServerProxy類是用于給播放線程管理內存的

// frameworksavservicesaudioflingerTracks.cpp
AudioFlinger::PlaybackThread::Track::Track
    
    if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize);
    }
    mServerProxy = mAudioTrackServerProxy;

3.音頻數據的傳遞總結

a. APP創建AudioTrack, playbackThread創建對應的Track,它們之間通過共享內存傳遞音頻數據
b. APP有2種使用共享內存的方式:
b.1 MODE_STATIC: APP創建共享內存, APP一次性填充數據
b.2 MODE_STREAM: APP使用obtainBuffer獲得空白內存, 填充數據后使用releaseBuffer釋放內存
c. playbackThread使用obtainBuffer獲得含有數據的內存, 使用數據后使用releaseBuffer釋放內存
d. App端的AudioTrack中含有mProxy,它被用來管理共享內存,里面含有obtainBuffer,releaseBuffer函數;
   播放線程端的Track中含有mServerProxy,它被用來管理共享內存,里面含有obtainBuffer, releaseBuffer函數
   對于不同的MODE, 這些Proxy指向不同的對象。
e. 對于MODE_STREAM, APP和playbackThread使用環型緩沖區的方式傳遞數據。

5.環形緩沖區講解

a.初始R=0,W=0,buff的長度為LEN
b.寫入一個數據
	w = W % LEN;
	buff[w] = data;
	W++;
c.讀出一個數據
	r = R % LEN;
	data = buff[r];
	R++;
判斷緩沖區滿:R + LEN == W
判斷緩沖區空:R == W

由上可以看出R和W是一直遞增的。

判斷溢出時,R和W同時都減少LEN的整數倍大小,將不影響結果

改進: 將LEN圓整到2的n次方,此時W%LEN這個可能比較慢的操作就可以轉換為w=W&(LEN-1)

七、PlaybackThread處理流程

1.聲卡往往只支持一種格式的音頻數據(如2-Channel,44K-Sample, 16bit-Deep)。但是App可能會傳遞下來不同格式的音頻數據,
這些數據在在playbackThread中進行重采樣,采樣到聲卡支持的格式。mAudioMixer對象負責這一工作。重采樣之后還要把各個App
的音頻數據混合起來,稱為混音,也是由mAudioMixer負責的。

2.hook成員指向不同的處理函數

//frameworksavservicesaudioflingerAudioMixer.cpp
process__nop():手機靜音了,不做任何處理
process__genericNoResampling(): 不需要重采樣
process__genericResampling(): 應該就是進行重采樣

3. PlaybackThread 處理流程

AudioFlinger::PlaybackThread::threadLoop()
frameworksavservicesaudioflingerThreads.cpp
a. prepareTracks_l : 
   確定enabled track, disabled track。對于enabled track, 設置mState.tracks[x]中的參數
b. threadLoop_mix : 處理數據(比如重采樣)、混音
   確定hook: 逐個分析mState.tracks[x]的數據, 根據它的格式確定tracks[x].hook。再確定總的mState.hook

   調用hook: 調用總的mState.hook即可, 它會再去調用每一個mState.tracks[x].hook
   
   混音后的數據會放在mState.outputTemp臨時BUFFER中然后轉換格式后存入 thread.mMixerBuffer
c. memcpy_by_audio_format : 把數據從thread.mMixerBuffer或thread.mEffectBuffer復制到thread.mSinkBuffer
d. threadLoop_write: 把thread.mSinkBuffer寫到聲卡上
e. threadLoop_exit

4.總結
  原始數據會保存在共享內存中,經過重采樣等處理后存放在一個臨時的outputTemp buffer中,這個臨時buffer中的數據保存的是重采樣后的數據,數據來源是多個Track。最終的可播放的數據經過混音后存放在mMixerBuffer這個buffer中。如果需要進行音效處理(比如加強低音),還有一部分數據會被放在mEffectBuffer中。之后會把mMixerBuffer和mEffectBuffer中的音頻數據都輸出到mSinkBuffer中,然后從mSinkBuffer中將數據發給聲卡硬件。

總結

以上是生活随笔為你收集整理的Android音频(6)——音频系统分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。