前言
音頻是聲音的一種數(shù)字化表示方式,它的應(yīng)用領(lǐng)域非常多,很多領(lǐng)域的應(yīng)用技術(shù)已經(jīng)很成熟,比如常見的:通信、娛樂、醫(yī)療(超聲)、人機(jī)交互等等。就我目前接觸到的消費(fèi)類嵌入式設(shè)備而言,比較多的應(yīng)用場(chǎng)景是:
語音對(duì)講,
音視頻錄像
語音檢測(cè),識(shí)別
涉及到的開發(fā)技術(shù)主要有:
音頻的編碼、解碼
音頻格式封裝、格式轉(zhuǎn)換
回聲消除
聲音檢測(cè)、識(shí)別
雖然音頻的應(yīng)用技術(shù)大部分都已經(jīng)比較成熟了,但是在嵌入式開發(fā)中,受限于硬件資源的匱乏,還是會(huì)遇到不少的問題。其中涉及到很多的知識(shí)和概念,如果不是專業(yè)做音視頻的同學(xué),估計(jì)也容易弄迷糊。
下面內(nèi)容是將我自己在實(shí)際開發(fā)工作中接觸到的音頻相關(guān)的知識(shí)進(jìn)行了一個(gè)簡(jiǎn)單整理歸納,僅供參考。
(一)音頻處理流程介紹
(1)理想處理流程
比較理想的音頻應(yīng)用處理流程,大概入下圖所示:
MIC 將聲音震動(dòng)信號(hào)轉(zhuǎn)換為電(數(shù)字/模擬)信號(hào),將其輸入到SOC的AI(音頻輸入模塊)
AI模塊對(duì)輸入的信號(hào)進(jìn)行轉(zhuǎn)換(ADC轉(zhuǎn)換采樣),輸出為PCM格式的音頻數(shù)據(jù)
將PCM音頻數(shù)據(jù)進(jìn)行壓縮、轉(zhuǎn)換、封裝成各種格式,比如常見的AAC、MP3等
將壓縮過的音頻文件,與視頻文件一起封裝成音視頻文件,比如MP4文件
(2)實(shí)際處理流程
在嵌入式應(yīng)用中,考慮到系統(tǒng)資源限制、應(yīng)用場(chǎng)景的不同,實(shí)際使用會(huì)比較的復(fù)雜,主要的受限是:既要支持本地音頻存儲(chǔ)、又要支持網(wǎng)絡(luò)傳輸。
PCM是原始音頻數(shù)據(jù),一般嵌入式芯片的音頻編碼是可以將PCM數(shù)據(jù)編碼成G711、G726等格式,但基本上不會(huì)支持AAC編碼,主要可能是涉及到版權(quán)問題。君正和海思系列的SOC都不能直接支持AAC編碼。
但是從編碼壓縮比例來看,ACC編碼的壓縮比例是比G711、G726的要高的,也就是說在相同條件下,AAC編碼可以存儲(chǔ)時(shí)間更加長(zhǎng)的音頻信息。另外,很多視頻封裝庫(kù),對(duì)AAC的支持都是比較友好。
基于上面這些情況,就會(huì)導(dǎo)致在同一個(gè)體統(tǒng)中,可能會(huì)存在幾種格式的音頻格式數(shù)據(jù)。比如下圖:
上圖中,主要的應(yīng)用場(chǎng)景,一個(gè)是音頻網(wǎng)絡(luò)傳輸,一個(gè)是音頻本地存儲(chǔ)。
路線1:
將AI模塊采集到的PCM直接通過網(wǎng)絡(luò)傳輸給IOT平臺(tái)
這種方式耗費(fèi)資源少,但是占用網(wǎng)絡(luò)帶寬大
適用于沒有音頻編碼模塊的SOC
路線2:
將PCM格式數(shù)據(jù),編碼成G711、G726等格式之后再通過網(wǎng)絡(luò)傳輸出去
耗費(fèi)資源少,網(wǎng)絡(luò)帶寬占用的也少,是最優(yōu)的一個(gè)選項(xiàng)
適用于帶音頻編碼的SOC
路線3:
將PCM格式數(shù)據(jù),通過軟件編碼的方式編碼成AAC格式,然后再封裝成MP4、AVI等格式
這種方式會(huì)占用CPU資源,運(yùn)行內(nèi)存RAM,以及Flash空間(AAC編碼庫(kù)比較大)
適用于一定需要AAC編碼的場(chǎng)景
路線4:
出現(xiàn)這種使用方式的主要原因是,SOC同一時(shí)間只支持一個(gè)音頻格式輸出,比如如果要輸出PCM格式,就不能再編碼輸出G711、G726等格式
將編碼輸出的G711、G726格式,進(jìn)行軟件解碼成PCM格式,在通過軟件壓縮成AAC格式,最后才封裝成mp4格式
這種方式適用于一定要使用AAC格式,但是SOC又不能同時(shí)輸出兩種類型音頻格式的場(chǎng)景
耗費(fèi)的各種資源都是最多的
(二)音頻格式轉(zhuǎn)換
(1)PCM 與 G711A、G711U
PCM:
設(shè)備通過MIC采集音頻信號(hào),MIC分為兩大類,數(shù)字MIC和模擬MIC,數(shù)字MIC輸出的是已經(jīng)轉(zhuǎn)換過的數(shù)字信號(hào),但消費(fèi)類設(shè)備中比較常用的是模擬MIC。
PCM數(shù)據(jù)?是將模擬MIC輸入的模擬音頻信號(hào)通過ADC轉(zhuǎn)換為數(shù)字信號(hào)的二進(jìn)制序列,它沒有文件頭也沒有結(jié)束標(biāo)志,是一種未壓縮的數(shù)據(jù)格式。
PCM文件?可以通過Audacity Beta (Unicode) 以文件->導(dǎo)入->裸數(shù)據(jù) 的方式打開,可以進(jìn)行播放,剪輯,查看等操作
主要的參數(shù)有:聲道,采樣頻率,采樣位數(shù)
下圖打開的是一個(gè):2聲道,48KHz 采樣頻率,16位深度的PCM文件
G711A與G711U
G711 分為a-law和u-law,通過查表的方式將16位的PCM數(shù)據(jù)壓縮成8位
G711 它的壓縮率為1:2,1個(gè)1M 的PCM文件轉(zhuǎn)換為G711格式后只有0.5M
G711 中的u-law 即g711u,主要使用在北美和日本
G711 中的a-law 即g711a,主要使用在歐洲及其它地區(qū)
如果要直接播放G711 文件音頻,在Linux系統(tǒng)中可以直接使用 ffplay 命令來播放
ffplay -i test.pcm ?-f s16le ?-ac 2 ?-ar 48000 ffplay -i test.g711a ?-f alaw ?-ac 2 ?-ar 48000 ffplay -i test.g711u ?-f mulaw ?-ac 2 ?-ar 48000
-ac: 音頻通道數(shù) -ar:音頻采樣率 -f:文件格式
G711與PCM之間的轉(zhuǎn)換先對(duì)來說是比較簡(jiǎn)單的,上面我是將一個(gè) 48K 16bit 2通道PCM 與G711 格式相互轉(zhuǎn)換的簡(jiǎn)單工程
(三) AAC格式與編碼
AAC 相比于G711 要復(fù)雜很多,AAC它有很多的版本,編碼器也有很多種,使用比較多的是FAAC(Freeware Advanced Audio Coder),因?yàn)樗敲赓M(fèi)的。
(1)AAC的各種格式
AAC的文件格式有:
ADIF?(Audio Data Interchange Format) 只有在文件開頭的位置才有音頻的頭部信息
ADTS?(Audio Data Transport Stream) 主要特點(diǎn)是每一幀都帶有頭部信息
文件格式是指主要以文件類型來保存的音頻數(shù)據(jù)
AAC的流格式:
流格式主要是指用于流媒體傳輸?shù)母袷剑饕校?/p>
AAC_RAW?是指未經(jīng)過封裝AAC裸數(shù)據(jù)
AAC_ADTS?與文件格式中的ADTS格式相同
AAC_LATM (Low-Overhead Audio Transport Multiplex)AAC音頻的一種傳輸協(xié)議。
比較常用的是ADTS格式,因?yàn)樗谝纛l數(shù)據(jù)文件存儲(chǔ)和流傳輸中都可以使用
(2)ATDS格式介紹
我們看fdk-aac中對(duì)ADTS結(jié)構(gòu)的定義
typedefstruct { ?/* ADTS header fields */ ?UCHAR mpeg_id; ?UCHAR layer; ?UCHAR protection_absent; ?UCHAR profile; ?UCHAR sample_freq_index; ?UCHAR private_bit; ?UCHAR channel_config; ?UCHAR original; ?UCHAR home; ?UCHAR copyright_id; ?UCHAR copyright_start; ?USHORT frame_length; ?USHORT adts_fullness; ?UCHAR num_raw_blocks; ?UCHAR num_pce_bits; } STRUCT_ADTS_BS;
這里只是把結(jié)構(gòu)頭部的項(xiàng)列出來了,這里列出來的有15項(xiàng),整個(gè)結(jié)構(gòu)頭的長(zhǎng)度有17個(gè)字節(jié)。
實(shí)際ADTS頭結(jié)構(gòu)有兩種長(zhǎng)度,包含CRC校驗(yàn)的是9個(gè)字節(jié)的長(zhǎng)度,沒有CRC校驗(yàn)的是7個(gè)字節(jié),每項(xiàng)的作用與實(shí)際長(zhǎng)度可以看wiki上的一個(gè)定義:https://wiki.multimedia.cx/index.php/ADTS
我們使用Elecard Stream Analyzer 工具打開一個(gè)ADTS格式的AAC文件進(jìn)行查看會(huì)更加的清晰:
標(biāo)簽1隨意點(diǎn)的第四幀,它的偏移地址是0x54a
標(biāo)簽2處是ADTS 的同步字Syncword,12位,0xFFF
右上的方框,是ADTS各項(xiàng)參數(shù)的解析
標(biāo)簽3處是單前幀(第4幀)的長(zhǎng)度,403
標(biāo)簽4是下一幀的偏移地址0x6dd,正好是上一幀的偏移地址+上一幀的長(zhǎng)度 = 0x54a + 403 = 0x6dd
如果是需要自己手動(dòng)解析AAC的ADTS格式文件,也可以通過上面方式進(jìn)行解析,先找到幀頭標(biāo)簽,再逐項(xiàng)的解析各個(gè)參數(shù),最后在根據(jù)幀長(zhǎng)度跳轉(zhuǎn)到下一幀進(jìn)行數(shù)據(jù)解析。
(3)AAC格式編碼
主要的AAC編碼器有:FhG、Nero AAC、QuickTime/iTunes、FAAC、DivX AAC?,在嵌入式中比較常用的是FAAC。
基于FAAC的編碼工具和庫(kù),比較常用的有:
FFMPEG:?它可以集成多種編碼器
fdk-aac:?同時(shí)集成了faac編解碼
faac:??aac 編碼庫(kù)
faad:??aac 解碼庫(kù)
上面介紹的幾種AAC封裝庫(kù),都可以在github上下載到源碼:
https://github.com/mstorsjo/fdk-aac https://github.com/knik0/faac https://github.com/knik0/faad2
(4) fdk-aac移植
github 上下載源碼https://github.com/mstorsjo/fdk-aac
可以通過tag選擇不同版本進(jìn)行下載,tag中的一般都是比較穩(wěn)定的發(fā)布版本
如果要將fdk-aac移植到君正的T31設(shè)備上,可以按下面命令進(jìn)行交叉編譯:
mkdir _install_uclibc ./autogen.sh CFLAGS+=-muclibc LDFLAGS+=-muclibc CPPFLAGS+=-muclibc CXXFLAGS+=-muclibc ./configure --prefix=$PWD/_install_uclibc --host=mips-linux-gnu make -j4 make install
交叉編譯的文件放置在_install_uclibc文件夾下,可以通過下面命令確定編譯使用的編譯工具鏈:file libfdk-aac.so.2.0.2
biao@ubuntu:~/test/fdk-aac-master/_install_uclibc/lib$ file libfdk-aac.so.2.0.2 libfdk-aac.so.2.0.2: ELF 32-bit LSB shared object, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, not stripped
如果要直接在PC上編譯測(cè)試,可以使用下面命令:
mkdir _install_linux_x86 ./autogen.sh ./configure --prefix=$PWD/_install_linux_x86 make -j4 make install
(5) fdk-aac應(yīng)用
這里簡(jiǎn)單介紹如何使用fdk-aac將PCM文件編碼成AAC格式文件,然后再通過fdk-aac將AAC解碼成PCM格式數(shù)據(jù)。
fdk-aac?源碼下有個(gè)?test-encode-decode.c?文件,它是以wav格式的文件為基礎(chǔ)的一個(gè)demo,如果PCM和AAC數(shù)據(jù)是以wav的格式存儲(chǔ)的,可以直接參考官方demo。
我這里使用的是上面有介紹的PCM裸流進(jìn)行編碼和解碼。
(a) PCM編碼成AAC
因?yàn)槲覀兪褂玫氖荘CM裸流,從文件中是無法讀取出流的任何信息,所以PCM流的信息是需要我們自己填寫的:
int aot, afterburner, eld_sbr, vbr, bitrate, adts, sample_rate, channels,mode; ? ?/**參數(shù)設(shè)置**/ ? ?aot ? ? ? ? ? ? = 2; ? ?/**Audio object type 2 ?MPEG-4 AAC Low Complexity.**/ ? ?afterburner ? ? = 0; ? ?/**是否啟用分析合成算法,可提高編碼質(zhì)量,但是會(huì)耗資源**/ ? ?eld_sbr ? ? ? ? = 0 ; ? ?/**Spectral Band Replication 頻譜顯示**/ ? ?vbr ? ? ? ? ? ? = 0; ? ? /**可變碼率配置**/ ? ?bitrate ? ? ? ? = 48000; /**編碼碼率**/ ? ?adts ? ? ? ? ? ?= 1; ? ? /**是否可傳輸**/ ? ?sample_rate ? ? = 48000; /**采樣率**/ ? ?channels ? ? ? ?= 2; ? ? /**通道**/
通過aacEncoder_SetParam(encoder, AACENC_TRANSMUX, 2)?可以設(shè)定需要編碼成的AAC格式,它支持的格式有:
- 0: raw access units - 1: ADIF bitstream format - 2: ADTS bitstream format - 6: Audio Mux Elements (LATM) withmuxConfigPresent = 1 - 7: Audio Mux Elements (LATM) withmuxConfigPresent = 0, out of band StreamMuxConfig - 10: Audio Sync Stream (LOAS) */
(b) AAC解碼成PCM
我們這里介紹將ADTS格式編碼的AAC文件解壓成PCM
要解碼AAC文件,首先需要能夠檢測(cè)到AAC文件中音頻幀的位置及長(zhǎng)度,所以我們首先需要解析AAC 的ADTS頭信息,頭結(jié)構(gòu)定義如下:
typedefstruct adts_fixed_header { ? ?unsigned short syncword:12; ? ?unsignedchar id: ? ? ? ? ? ? ? ? ? ? ? 1; ? ?unsignedchar layer:2; ? ?unsignedchar protection_absent: ? ? ? ?1; ? ?unsignedchar profile: ? ? ? ? ? ? ? ? ?2; ? ?unsignedchar sampling_frequency_index: 4; ? ?unsignedchar private_bit: ? ? ? ? ? ? ?1; ? ?unsignedchar channel_configuration:3; ? ?unsignedchar original_copy:1; ? ?unsignedchar home: ? ? ? ? ? ? ? ? ? ? 1; } adts_fixed_header; // length : 28 bits typedefstruct adts_variable_header { ? ?unsignedchar copyright_identification_bit:1; ? ?unsignedchar copyright_identification_start:1; ? ?unsigned short aac_frame_length:13; ? ?unsigned short adts_buffer_fullness:11; ? ?unsignedchar number_of_raw_data_blocks_in_frame:2; } adts_variable_header; // length : 28 bits
解析方法如下:
memset(&fixed_header, 0, sizeof(adts_fixed_header)); ?memset(&variable_header, 0, sizeof(adts_variable_header)); ?get_fixed_header(headerBuff, &fixed_header); ?get_variable_header(headerBuff, &variable_header);
解碼的時(shí)候,還需要注意需要使用aacDecoder_ConfigRaw 配置PCM的信息,demo 是通過info.confBuf來獲取,這個(gè)值是在編碼的時(shí)候才會(huì)有,所以這個(gè)值需要根據(jù)實(shí)際參數(shù)來設(shè)置:
unsignedchar ?conf[] = {0x11, 0x90}; ?//AAL-LC 48kHz 2 channle ? ? ?unsignedchar* conf_array[1] = { conf }; ? ? ?unsignedint length = 2; ? ? ?if (AAC_DEC_OK != aacDecoder_ConfigRaw(decoder, conf_array, &length)) ? ? ?{ ? ? ? ? ?printf("error: aac config fail "); ? ? ? ? ?exit(1); ? ? ?}
完整工程文件如下:
biao@ubuntu:~/test/faac/fdk-aac-x86$ tree . ├── 48000_16bits_2ch.pcm ├── adts.c ├── adts.h ├── decode_48000_16bits_2ch.pcm ├── include │?? └── fdk-aac │?? ? ? ├── aacdecoder_lib.h │?? ? ? ├── aacenc_lib.h │?? ? ? ├── FDK_audio.h │?? ? ? ├── genericStds.h │?? ? ? ├── machine_type.h │?? ? ? └── syslib_channelMapDescr.h ├── lib │?? ├── libfdk-aac.a │?? ├── libfdk-aac.la │?? ├── libfdk-aac.so -> libfdk-aac.so.2.0.2 │?? ├── libfdk-aac.so.2 -> libfdk-aac.so.2.0.2 │?? ├── libfdk-aac.so.2.0.2 │?? └── pkgconfig │?? ? ? └── fdk-aac.pc ├── Makefile ├── out.aac ├── out_ADIF.aac ├── out_adts.aac ├── out_RAW.aac └── test_faac.c 4 directories, 22 files biao@ubuntu:~/test/faac/fdk-aac-x86$
結(jié)尾
嵌入式音頻開發(fā)涉及到的內(nèi)容很多,每個(gè)功能單獨(dú)拉出來都會(huì)涉及到很多的知識(shí)點(diǎn)。
上面只是簡(jiǎn)單的介紹了一下它們的概念,以簡(jiǎn)單使用。如有錯(cuò)誤,歡迎批評(píng)指正。
審核編輯:黃飛
評(píng)論
查看更多