Android的音視頻開發是我暫定的一個職業發展的一個方向,通過自學記錄一些記了又忘記的知識。
音頻基礎知識
采樣率(samplerate)
藍色代表模擬音頻信號,紅色的點代表采樣得到的量化數值。
采用就是把模擬信號數字化的過程,不僅僅是音頻需要采樣,所有的模擬信號都需要通過采樣轉換為可以用0101來表示的數字信號。常用的音頻采樣頻率有:8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz、96kHz、192kHz等。
人耳可以聽到的聲波頻率范圍?
20-20000Hz。為了保證聲音不失真,采樣頻率應在40kHz以上。
AudioRecord構造參數中的sampleRateInHz。
量化精度(位寬)
上圖中,每一個紅色的采樣點,都需要用一個數值來表示大小,這個數值的數據類型大小可以是:4bit、8bit、16bit、32bit等等,位數越多,表示得就越精細,聲音質量自然就越好,當然,數據量也會成倍增大。
AudioRecord構造參數中的audioFormat。
聲道數(channels)
由于音頻的采集和播放是可以疊加的,因此,可以同時從多個音頻源采集聲音,并分別輸出到不同的揚聲器,故聲道數一般表示聲音錄制時的音源數量或回放時相應的揚聲器數量。
AudioRecord 構造參數中的channelConfig。
音頻幀(frame)
視頻我們知道每一幀是一張圖片,音頻數據是流式的,沒有明確的一幀幀的概念,但是在音頻處理的時候,一般取2.5ms-60ms為單位數據量的一幀音頻。
假設某通道音頻信號采樣率為8kHz,位寬位16bit,20ms一幀,雙通道,那么一幀音頻的大小為:
8000*16bit*0.02*2=5120bit=640byte
音頻編碼
是將音頻采樣數據(PCM等)壓縮成為音頻碼流,從而降低音頻的數據量。模擬的音頻信號轉換為數字信號需要經過采樣和量化,量化的過程被稱之為編碼,根據不同的量化策略,產生了許多不同的編碼方式,常見的編碼方式有:PCM 和 ADPCM
為什么要音頻編碼
存儲一秒鐘采樣率為44.1KHz,位深為16bit,雙聲道的PCM編碼的音頻信號,需要
44100*16bit*2 / 8/1024 = 172.2KB的空間,那么1分鐘則約為10.09M。
這對大部分用戶是不可接受的。
只有2種方法,降低采樣指標或者壓縮。
音頻壓縮
降低采樣是不可取的,因此就有了各種各樣的壓縮方式。
有兩類主要的音頻文件格式:
有損文件格式: 是基于聲學心理學的模型,除去人類很難或根本聽不到的聲音。
無損格式,例如PCM,WAV,ALS,ALAC,TAK,FLAC,APE,WavPack(WV)
有損格式,例如MP3,AAC,WMA,Ogg。
根據采樣率和采樣大小可以得知,相對自然界的信號,音頻編碼最多只能做到無限接近,至少目前的技術只能這樣了,相對自然界的信號,任何數字音頻編碼方案都是有損的,因為無法完全還原。在計算機應用中,能夠達到最高保真水平的就是PCM編碼,被廣泛用于素材保存及音樂欣賞,CD、DVD以及我們常見的WAV文件中均有應用。因此,PCM約定俗成了無損編碼,因為PCM代表了數字音頻中最佳的保真水準,并不意味著PCM就能夠確保信號絕對保真,PCM也只能做到最大程度的無限接近。
我們而習慣性的把MP3列入有損音頻編碼范疇,是相對PCM編碼的。
Android如何采集音頻
Android SDK對于音頻采集提供兩套API:MediaRecorder和AudioRecorder,前者是偏上層的一個API,可以直接把手機麥克風錄入的音頻數據進行編碼壓縮為AMR,MP3等并保存文件。后者接近底層,可以靈活控制,得到原始的一幀幀PCM音頻數據。
當要簡單的把數據采集為音頻文件,就使用MediaRecorder,如果要對音頻做進一步的算法處理就使用AudioRecorder。
什么是PCM音頻數據?
PCM(Pulse Code Modulation)也被稱為脈沖編碼調制。PCM音頻數據是未經壓縮的音頻采樣數據裸流,它是由模擬信號經過采樣、量化、編碼轉換成的標準的數字音頻數據。(詳細參考)
AudioRecord
簡介
AudioRecord輸出是PCM語音數據,如果保存成音頻文件,是不能夠被播放器播放的,所以必須先寫代碼實現數據編碼以及壓縮。
AudioRecord適用于對采集的音頻數據進行二次處理的,我們首先看到代碼AudioRecord類的構造函數。
/**
* Class constructor.
* Though some invalid parameters will result in an {@link IllegalArgumentException} exception,
* other errors do not. Thus you should call {@link #getState()} immediately after construction
* to confirm that the object is usable.
* @param audioSource the recording source.
* See {@link MediaRecorder.AudioSource} for the recording source definitions.
* @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
* rate that is guaranteed to work on all devices, but other rates such as 22050,
* 16000, and 11025 may work on some devices.
* {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
* which is usually the sample rate of the source.
* {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_IN_MONO} and
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
* to work on all devices.
* @param audioFormat the format in which the audio data is to be returned.
* See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
* and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
* to during the recording. New audio data can be read from this buffer in smaller chunks
* than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
* required buffer size for the successful creation of an AudioRecord instance. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
* @throws java.lang.IllegalArgumentException
*/
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
看這個構造函數的參數的解釋可以明白個七七八八。具體的介紹如下:
audioSource
音頻采集的輸入源,可選的值以常量的形式定義在 MediaRecorder.AudioSource 類中,常用的值包括:DEFAULT(默認),VOICE_RECOGNITION(用于語音識別,等同于DEFAULT),MIC(由手機麥克風輸入),VOICE_COMMUNICATION(用于VoIP應用)等等。
sampleRateInHz
采樣率,注意,目前44100Hz是唯一可以保證兼容所有Android手機的采樣率。
channelConfig
通道數的配置,可選的值以常量的形式定義在 AudioFormat 類中,常用的是 CHANNEL_IN_MONO(單通道),CHANNEL_IN_STEREO(雙通道)
audioFormat
這個參數是用來配置“數據位寬”的,可選的值也是以常量的形式定義在 AudioFormat 類中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保證兼容所有Android手機的。
bufferSizeInBytes
配置的是 AudioRecord 內部的音頻緩沖區的大小,該緩沖區的值不能低于一幀“音頻幀”(Frame)的大小,而前一篇文章介紹過,一幀音頻幀的大小計算如下:
int size = 采樣率 x 位寬 x 采樣時間 x 通道數
在Android開發中,AudioRecord 類提供了一個幫助你確定這個 bufferSizeInBytes 的函數,原型如下:
int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);
不同的廠商的底層實現是不一樣的,但無外乎就是根據上面的計算公式得到一幀的大小,音頻緩沖區的大小則必須是一幀大小的2~N倍。
使用AudioRecord
操作AudioRecord步驟如下:
配置參數,初始化AudioRecord構造函數
開始采集
開啟一個子線程,不斷從AudioRecord的緩沖區將音頻數據讀出來。注意,這個過程一定要及時,否則就會出現“overrun”的錯誤,該錯誤在音頻開發中比較常見,意味著應用層沒有及時地取走音頻數據,導致內部的音頻緩沖區溢出。
停止采集,釋放資源。
示例代碼:
注意添加RECORD_AUDIO權限。
點擊查看
MediaRecorder
MediaRecorder可以直接把手機麥克風錄入的音頻數據進行編碼壓縮為AMR,MP3等并保存文件。
使用MediaRecorder
簡單操作使用MediaRecorder類步驟如下:
實例化MediaRecorder對象
使用函數SetAudioSource設置硬件設備為采集音頻輸入數據
使用函數SetOutputFormat設置輸出音頻格式。使用類OutputFormat列出支持的輸出格式
調用方法SetAudioEncoder方法設置音頻編碼格式
調用SetOutputFile方法保存輸出文件數據的絕對完整路徑
調用Prepare方法初始化錄制
調用Start方法開始錄制
示例代碼:
注意添加權限:
WRITE_EXTERNAL_STORAGE;
RECORD_AUDIO;
點擊查看
總結
MediaRecorder和AudioRecord都可以錄制音頻,區別是MediaRecorder錄制的音頻文件是經過壓縮后的,需要設置編碼器。并且錄制的音頻文件可以用系統自帶的Music播放器播放。
而AudioRecord錄制的是PCM格式的音頻文件,需要用AudioTrack來播放,AudioTrack更接近底層。
在用MediaRecorder進行錄制音視頻時,最終還是會創建AudioRecord用來與AudioFlinger進行交互。
C++層MediaRecorder創建AudioRecord類的代碼位于AudioSource類構造函數中.
MediaRecorder錄制的數據是 amr MP3 格式;AudioRecorder錄制出來的是比較原始的PCM音頻格式;PCM經過編碼壓縮可以為 amr MP3 AAC。
優缺點:
AudioRecord
主要是實現邊錄邊播以及對音頻的實時處理,這個特性讓他更適合在語音方面有優勢;
優點:語音的實時處理,可以用代碼實現各種音頻的封裝
缺點:輸出是PCM格式文件,如果保存成音頻文件,是不能夠被播放器播放的,所以必須先寫代碼實現數據編碼以及壓縮
MediaRecorder
已經集成了錄音、編碼、壓縮等,支持少量的錄音音頻格式,大概有,aac,amr,3gp等
優點:集成,直接調用相關接口即可,代碼量小
缺點:無法實時處理音頻;輸出的音頻格式不是很多,例如沒有輸出mp3格式文件