目錄
- 什么是PCM?
- PCM數(shù)據(jù)格式
- FFmpeg支持的PCM數(shù)據(jù)格式
- FFmpeg中Packed和Planar的PCM數(shù)據(jù)區(qū)別
- 字節(jié)序
- PCM音頻數(shù)據(jù)的處理
- 參考
1. 什么是PCM?
PCM(Pulse Code Modulation,脈沖編碼調(diào)制)音頻數(shù)據(jù)是未經(jīng)壓縮的音頻采樣數(shù)據(jù)裸流,它是由模擬信號經(jīng)過采樣、量化、編碼轉(zhuǎn)換成的標(biāo)準(zhǔn)數(shù)字音頻數(shù)據(jù)。
描述PCM數(shù)據(jù)的6個參數(shù):
- Sample Rate : 采樣頻率。8kHz(電話)、44.1kHz(CD)、48kHz(DVD)。
- Sample Size : 量化位數(shù)。通常該值為16-bit。
- Number of Channels : 通道個數(shù)。常見的音頻有立體聲(stereo)和單聲道(mono)兩種類型,立體聲包含左聲道和右聲道。另外還有環(huán)繞立體聲等其它不太常用的類型。
- Sign : 表示樣本數(shù)據(jù)是否是有符號位,比如用一字節(jié)表示的樣本數(shù)據(jù),有符號的話表示范圍為-128 ~ 127,無符號是0 ~ 255。
- Byte Ordering : 字節(jié)序。字節(jié)序是little-endian還是big-endian。通常均為little-endian。字節(jié)序說明見第4節(jié)。
- Integer Or Floating Point : 整形或浮點型。大多數(shù)格式的PCM樣本數(shù)據(jù)使用整形表示,而在一些對精度要求高的應(yīng)用方面,使用浮點類型表示PCM樣本數(shù)據(jù)。
推薦的PCM數(shù)據(jù)播放工具:
- ffplay, 使用示例如下:
//播放格式為f32le,單聲道,采樣頻率48000Hz的PCM數(shù)據(jù)
ffplay -f f32le -ac 1 -ar 48000 pcm_audio
- Audacity:一款免費開源的跨平臺音頻處理軟件。
- Adobe Auditon。導(dǎo)入原始數(shù)據(jù),打開的時候需要選擇采樣率、格式和字節(jié)序。
2. PCM數(shù)據(jù)格式
如果是單聲道的音頻文件,采樣數(shù)據(jù)按時間的先后順序依次存入(有的時候也會采用LRLRLR方式存儲,只是另一個聲道的數(shù)據(jù)為0),如果是雙聲道的話就按照LRLRLR的方式存儲,存儲的時候與字節(jié)序有關(guān)。big-endian模式如下圖所示:

3. FFmpeg支持的PCM數(shù)據(jù)格式
使用ffmpeg -formats命令,獲取ffmpeg支持的音視頻格式,其中我們可以找到支持的PCM格式。
DE alaw PCM A-law
DE f32be PCM 32-bit floating-point big-endian
DE f32le PCM 32-bit floating-point little-endian
DE f64be PCM 64-bit floating-point big-endian
DE f64le PCM 64-bit floating-point little-endian
DE mulaw PCM mu-law
DE s16be PCM signed 16-bit big-endian
DE s16le PCM signed 16-bit little-endian
DE s24be PCM signed 24-bit big-endian
DE s24le PCM signed 24-bit little-endian
DE s32be PCM signed 32-bit big-endian
DE s32le PCM signed 32-bit little-endian
DE s8 PCM signed 8-bit
DE u16be PCM unsigned 16-bit big-endian
DE u16le PCM unsigned 16-bit little-endian
DE u24be PCM unsigned 24-bit big-endian
DE u24le PCM unsigned 24-bit little-endian
DE u32be PCM unsigned 32-bit big-endian
DE u32le PCM unsigned 32-bit little-endian
DE u8 PCM unsigned 8-bit
s是有符號,u是無符號,f是浮點數(shù)。
be是大端,le是小端。
4. FFmpeg中Packed和Planar的PCM數(shù)據(jù)區(qū)別
FFmpeg中音視頻數(shù)據(jù)基本上都有Packed和Planar兩種存儲方式,對于雙聲道音頻來說,Packed方式為兩個聲道的數(shù)據(jù)交錯存儲;Planar方式為兩個聲道分開存儲。假設(shè)一個L/R為一個采樣點,數(shù)據(jù)存儲的方式如下所示:
- Packed: L R L R L R L R
- Planar: L L L L R R R R
FFmpeg音頻解碼后的數(shù)據(jù)是存放在AVFrame結(jié)構(gòu)中的。
- Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音頻數(shù)據(jù)中。
- Planar格式,frame.data[i]或者frame.extended_data[i]表示第i個聲道的數(shù)據(jù)(假設(shè)聲道0是第一個), AVFrame.data數(shù)組大小固定為8,如果聲道數(shù)超過8,需要從frame.extended_data獲取聲道數(shù)據(jù)。
下面為FFmpeg內(nèi)部存儲音頻使用的采樣格式,所有的Planar格式后面都有字母P標(biāo)識。
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
說明:
- Planar模式是ffmpeg內(nèi)部存儲模式,我們實際使用的音頻文件都是Packed模式的。
- FFmpeg解碼不同格式的音頻輸出的音頻采樣格式不是一樣。測試發(fā)現(xiàn),其中AAC解碼輸出的數(shù)據(jù)為浮點型的 AV_SAMPLE_FMT_FLTP 格式,MP3解碼輸出的數(shù)據(jù)為 AV_SAMPLE_FMT_S16P 格式(使用的mp3文件為16位深)。具體采樣格式可以查看解碼后的AVFrame中的format成員或解碼器的AVCodecContext中的sample_fmt成員。
- Planar或者Packed模式直接影響到保存文件時寫文件的操作,操作數(shù)據(jù)的時候一定要先檢測音頻采樣格式。
5. 字節(jié)序
談到字節(jié)序的問題,必然牽涉到兩大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存儲數(shù)據(jù),而x86系列則采用little endian方式存儲數(shù)據(jù)。那么究竟什么是big endian,什么又是little endian?
big endian是指低地址存放最高有效字節(jié)(MSB,Most Significant Bit),而little endian則是低地址存放最低有效字節(jié)(LSB,Least Significant Bit)。
下面用圖像加以說明。比如數(shù)字0x12345678在兩種不同字節(jié)序CPU中的存儲順序如下所示:
Big Endian
低地址 高地址
----------------------------------------------------------------------------->
| 12 | 34 | 56 | 78 |
Little Endian
低地址 高地址
----------------------------------------------------------------------------->
| 78 | 56 | 34 | 12 |
所有網(wǎng)絡(luò)協(xié)議都是采用big endian的方式來傳輸數(shù)據(jù)的。所以也把big endian方式稱之為網(wǎng)絡(luò)字節(jié)序。當(dāng)兩臺采用不同字節(jié)序的主機(jī)通信時,在發(fā)送數(shù)據(jù)之前都必須經(jīng)過字節(jié)序的轉(zhuǎn)換成為網(wǎng)絡(luò)字節(jié)序后再進(jìn)行傳輸。
6. PCM音頻數(shù)據(jù)的處理
6.1 分離雙聲道PCM音頻數(shù)據(jù)左右聲道的數(shù)據(jù)
按照雙聲道的LRLRLR的PCM音頻數(shù)據(jù)可以通過將它們交叉的讀出來的方式來分離左右聲道的數(shù)據(jù)。
int pcm_s16le_split(const char* file, const char* out_lfile, const char* out_rfile) {
FILE *fp = fopen(file, "rb+");
if (fp == NULL) {
printf("open %s failed\n", file);
return -1;
}
FILE *fp1 = fopen(out_lfile, "wb+");
if (fp1 == NULL) {
printf("open %s failed\n", out_lfile);
return -1;
}
FILE *fp2 = fopen(out_rfile, "wb+");
if (fp2 == NULL) {
printf("open %s failed\n", out_rfile);
return -1;
}
char * sample = (char *)malloc(4);
while(!feof(fp)) {
fread(sample, 1, 4, fp);
//L
fwrite(sample, 1, 2, fp1);
//R
fwrite(sample + 2, 1, 2, fp2);
}
free(sample);
fclose(fp);
fclose(fp1);
fclose(fp2);
return 0;
}