liwen01 2023.12.10
音訊是聲音的一種數位化表示方式,它的應用領域非常多,很多領域的應用技術已經很成熟,比如常見的:通訊、娛樂、醫療(超聲)、人機互動等等。就我目前接觸到的消費類嵌入式裝置而言,比較多的應用場景是:
涉及到的開發技術主要有:
雖然音訊的應用技術大部分都已經比較成熟了,但是在嵌入式開發中,受限於硬體資源的匱乏,還是會遇到不少的問題。其中涉及到很多的知識和概念,如果不是專業做音視訊的同學,估計也容易弄迷糊。
下面內容是將我自己在實際開發工作中接觸到的音訊相關的知識進行了一個簡單整理歸納,僅供參考。
比較理想的音訊應用處理流程,大概入下圖所示:
在嵌入式應用中,考慮到系統資源限制、應用場景的不同,實際使用會比較的複雜,主要的受限是:既要支援本地音訊儲存、又要支援網路傳輸。
PCM是原始音訊資料,一般嵌入式晶片的音訊編碼是可以將PCM資料編碼成G711、G726等格式,但基本上不會支援AAC編碼,主要可能是涉及到版權問題。君正和海思系列的SOC都不能直接支援AAC編碼。
但是從編碼壓縮比例來看,ACC編碼的壓縮比例是比G711、G726的要高的,也就是說在相同條件下,AAC編碼可以儲存時間更加長的音訊資訊。另外,很多視訊封裝庫,對AAC的支援都是比較友好。
基於上面這些情況,就會導致在同一個體統中,可能會存在幾種格式的音訊格式資料。比如下圖:
上圖中,主要的應用場景,一個是音訊網路傳輸,一個是音訊本地儲存。
路線1:
路線2:
路線3:
路線4:
PCM:
下圖開啟的是一個:2聲道,48KHz 取樣頻率,16位元深度的PCM檔案
G711A與G711U
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: 音訊通道數 -ar:音訊取樣率 -f:檔案格式
G711與PCM之間的轉換先對來說是比較簡單的,上面我是將一個 48K 16bit 2通道PCM 與G711 格式相互轉換的簡單工程
AAC 相比於G711 要複雜很多,AAC它有很多的版本,編碼器也有很多種,使用比較多的是FAAC(Freeware Advanced Audio Coder),因為它是免費的。
AAC的檔案格式有:
AAC的流格式:
比較常用的是ADTS格式,因為它在音訊資料檔案儲存和流傳輸中都可以使用
我們看fdk-aac中對ADTS結構的定義
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;
這裡只是把結構頭部的項列出來了,這裡列出來的有15項,整個結構頭的長度有17個位元組。
實際ADTS頭結構有兩種長度,包含CRC校驗的是9個位元組的長度,沒有CRC校驗的是7個位元組,每項的作用與實際長度可以看wiki上的一個定義:https://wiki.multimedia.cx/index.php/ADTS
我們使用Elecard Stream Analyzer 工具開啟一個ADTS格式的AAC檔案進行檢視會更加的清晰:
如果是需要自己手動解析AAC的ADTS格式檔案,也可以通過上面方式進行解析,先找到幀頭標籤,再逐項的解析各個引數,最後在根據幀長度跳轉到下一幀進行資料解析。
主要的AAC編碼器有:FhG、Nero AAC、QuickTime/iTunes、FAAC、DivX AAC ,在嵌入式中比較常用的是FAAC。
基於FAAC的編碼工具和庫,比較常用的有:
上面介紹的幾種AAC封裝庫,都可以在github上下載到原始碼:
https://github.com/mstorsjo/fdk-aac https://github.com/knik0/faac https://github.com/knik0/faad2
github 上下載原始碼https://github.com/mstorsjo/fdk-aac
可以通過tag選擇不同版本進行下載,tag中的一般都是比較穩定的釋出版本
如果要將fdk-aac移植到君正的T31裝置上,可以按下面命令進行交叉編譯:
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上編譯測試,可以使用下面命令:
mkdir _install_linux_x86
./autogen.sh
./configure --prefix=$PWD/_install_linux_x86
make -j4
make install
這裡簡單介紹如何使用fdk-aac將PCM檔案編碼成AAC格式檔案,然後再通過fdk-aac將AAC解碼成PCM格式資料。
fdk-aac
原始碼下有個 test-encode-decode.c
檔案,它是以wav格式的檔案為基礎的一個demo,如果PCM和AAC資料是以wav的格式儲存的,可以直接參考官方demo。
我這裡使用的是上面有介紹的PCM裸流進行編碼和解碼。
(a) PCM編碼成AAC
因為我們使用的是PCM裸流,從檔案中是無法讀取出流的任何資訊,所以PCM流的資訊是需要我們自己填寫的:
int aot, afterburner, eld_sbr, vbr, bitrate, adts, sample_rate, channels,mode;
/**引數設定**/
aot = 2; /**Audio object type 2 MPEG-4 AAC Low Complexity.**/
afterburner = 0; /**是否啟用分析合成演演算法,可提高編碼質量,但是會耗資源**/
eld_sbr = 0 ; /**Spectral Band Replication 頻譜顯示**/
vbr = 0; /**變動位元速率設定**/
bitrate = 48000; /**編碼位元速率**/
adts = 1; /**是否可傳輸**/
sample_rate = 48000; /**取樣率**/
channels = 2; /**通道**/
通過aacEncoder_SetParam(encoder, AACENC_TRANSMUX, 2)
可以設定需要編碼成的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檔案,首先需要能夠檢測到AAC檔案中音訊幀的位置及長度,所以我們首先需要解析AAC 的ADTS頭資訊,頭結構定義如下:
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);
解碼的時候,還需要注意需要使用aacDecoder_ConfigRaw 設定PCM的資訊,demo 是通過info.confBuf來獲取,這個值是在編碼的時候才會有,所以這個值需要根據實際引數來設定:
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\n");
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$
如需上面介紹的工程,測試檔案,以及檢視工具,可以在公眾號中回覆 資源
獲取,內容在音視訊
連線中。
嵌入式音訊開發涉及到的內容很多,每個功能單獨拉出來都會涉及到很多的知識點。
上面只是簡單的介紹了一下它們的概念,以簡單使用。如有錯誤,歡迎批評指正。