這個用例很簡單,但其中會有一些重要概念,應注意理解。
注意:要了解AudioTrack Java API的具體信息,需要仔細閱讀Android API中的相關文檔。閱讀API文檔,是一個能快速掌握相關知識的好方法。
**AudioTrackAPI使用例子(Java層)**
~~~
//① 根據音頻數據的特性來確定所要分配的緩沖區的最小size
int bufsize =
AudioTrack.getMinBufferSize(8000,//采樣率:每秒8K個點
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//聲道數:雙聲道
AudioFormat.ENCODING_PCM_16BIT//采樣精度:一個采樣點16比特,相當于2個字節
);
//② 創建AudioTrack
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC,//音頻流類型
8000,AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufsize,
AudioTrack.MODE_STREAM//數據加載模式);
//③ 開始播放
trackplayer.play() ;
......
//④ 調用write寫數據
trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ;//往track中寫數據
......
//⑤ 停止播放和釋放資源
trackplayer.stop();//停止播放
trackplayer.release();//釋放底層資源
~~~
上面的用例引入了兩個新的概念,一個是數據加載模式,另一個是音頻流類型。下面進行詳細介紹。
1. AudioTrack的數據加載模式
AudioTrack有兩種數據加載模式:MODE_STREAM和MODE_STATIC,它們對應著兩種完全不同的使用場景。
- MODE_STREAM:在這種模式下,通過write一次次把音頻數據寫到AudioTrack中。這和平時通過write系統調用往文件中寫數據類似,但這種工作方式每次都需要把數據從用戶提供的Buffer中拷貝到AudioTrack內部的Buffer中,這在一定程度上會使引入延時。為解決這一問題,AudioTrack就引入了第二種模式。
* MODE_STATIC:這種模式下,在play之前只需要把所有數據通過一次write調用傳遞到AudioTrack中的內部緩沖區,后續就不必再傳遞數據了。這種模式適用于像鈴聲這種內存占用量較小,延時要求較高的文件。但它也有一個缺點,就是一次write的數據不能太多,否則系統無法分配足夠的內存來存儲全部數據。
這兩種模式中以MODE_STREAM模式相對常見和復雜,我們的分析將以它為主。
>[info]**注意**:如果采用STATIC模式,須先調用write寫數據,然后再調用play。
2. 音頻流的類型
在AudioTrack構造函數中,會接觸到AudioManager.STREAM_MUSIC這個參數。它的含義與Android系統對音頻流的管理和分類有關。
Android將系統的聲音分為好幾種流類型,下面是幾個常見的:
- STREAM_ALARM:警告聲
- STREAM_MUSIC:音樂聲,例如music等
- STREAM_RING:鈴聲
- STREAM_SYSTEM:系統聲音,例如低電提示音,鎖屏音等
- STREAM_VOCIE_CALL:通話聲
* * * * *
**注意**:上面這些類型的劃分和音頻數據本身并沒有關系。例如MUSIC和RING類型都可以是某首MP3歌曲。另外,聲音流類型的選擇沒有固定的標準,例如,鈴聲預覽中的鈴聲可以設置為MUSIC類型。
* * * * *
音頻流類型的劃分和Audio系統對音頻的管理策略有關。其具體作用,在以后的分析中再做詳細介紹。在目前的用例中,把它當做一個普通數值即可。
3. Buffer分配和Frame的概念
在用例中碰到的第一個重要函數就是getMinBufferSize。這個函數對于確定應用層分配多大的數據Buffer具有重要指導意義。先回顧一下它的調用方式:
**AudioTrackAPI使用例子(Java層)**
~~~
//注意這些參數的值。想象我們正在一步步的Trace,這些參數都會派上用場
AudioTrack.getMinBufferSize(8000,//每秒8K個點
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//雙聲道
AudioFormat.ENCODING_PCM_16BIT);
~~~
來看這個函數的實現:
**AudioTrack.java**
~~~
static public int getMinBufferSize(intsampleRateInHz, int channelConfig,
intaudioFormat) {
int channelCount = 0;
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
caseAudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;//目前最多支持雙聲道
break;
default:
return AudioTrack.ERROR_BAD_VALUE;
}
//目前只支持PCM8和PCM16精度的音頻數據
if((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
return AudioTrack.ERROR_BAD_VALUE;
}
//對采樣頻率也有要求,太低或太高都不行。
if( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) )
return AudioTrack.ERROR_BAD_VALUE;
/*
調用Native函數,先想想為什么,如果是簡單計算,那么Java層做不到嗎?
原來,還需要確認硬件是否支持這些參數,當然得進入Native層查詢了
*/
int size = native_get_min_buff_size(sampleRateInHz,
channelCount,audioFormat);
if((size == -1) || (size == 0)) {
return AudioTrack.ERROR;
}
else {
return size;
}
}
~~~
Native的函數將查詢Audio系統中音頻輸出硬件HAL對象的一些信息,并確認它們是否支持這些采樣率和采樣精度。
說明:HAL對象的具體實現和硬件廠商有關系,如果沒有特殊說明,我們則把硬件和HAL作為一種東西討論。
來看Native的native_get_min_buff_size函數。它在android_media_track.cpp中。
**android_media_track.cpp**
~~~
/*
注意我們傳入的參數是:
sampleRateInHertz = 8000,nbChannels = 2
audioFormat = AudioFormat.ENCODING_PCM_16BIT
*/
static jintandroid_media_AudioTrack_get_min_buff_size(
JNIEnv*env, jobject thiz,
jintsampleRateInHertz, jint nbChannels, jint audioFormat)
{
intafSamplingRate;
intafFrameCount;
uint32_t afLatency;
/*
下面這些調用涉及了AudioSystem,這個和AudioPolicy有關系。這里僅把它們看成是
信息查詢即可
*/
//查詢采樣率,一般返回的是所支持的最高采樣率,例如44100
if(AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
return -1;
}
//① 查詢硬件內部緩沖的大小,以Frame為單位。什么是Frame?
if(AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
return -1;
}
//查詢硬件的延時時間
if(AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
return -1;
}
......
~~~
這里有必要插入內容,因為代碼中出現了音頻系統中的一個重要概念:Frame(幀)。
說明:Frame是一個單位,經多方查尋,最終在ALSA的wiki中找到了對它的解釋。Frame直觀上用來描述數據量的多少,例如,一幀等于多少字節。1單位的Frame等于1個采樣點的字節數×聲道數(比如PCM16,雙聲道的1個Frame等于2×2=4字節)。
我們知道,1個采樣點只針對一個聲道,而實際上可能會有一或多個聲道。由于不能用一個獨立的單位來表示全部聲道一次采樣的數據量,也就引出了Frame的概念。Frame的大小,就是一個采樣點的字節數×聲道數。另外,在目前的聲卡驅動程序中,其內部緩沖區也是采用Frame作為單位來分配和管理的。
OK,繼續native_get_min_buff_size函數。
~~~
......
// minBufCount表示緩沖區的最少個數,它以Frame作為單位
uint32_t minBufCount = afLatency / ((1000 *afFrameCount)/afSamplingRate);
if(minBufCount < 2) minBufCount = 2;//至少要兩個緩沖
//計算最小幀個數
uint32_tminFrameCount =
(afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
//下面根據最小的FrameCount計算最小的緩沖大小
intminBuffSize = minFrameCount //計算方法完全符合我們前面關于Frame的介紹
* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
* nbChannels;
returnminBuffSize;
}
~~~
getMinBufSize會綜合考慮硬件的情況(諸如是否支持采樣率,硬件本身的延遲情況等)后,得出一個最小緩沖區的大小。一般我們分配的緩沖大小會是它的整數倍。
好了,介紹完一些基本概念后,開始要分析AudioTrack了。
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結