Asterisk現(xiàn)有版本不支持播放視頻文件(支持視頻通話),無法滿足發(fā)送視頻通知燕刻、視頻IVR等場景脸爱。本系列文章,通過學(xué)習(xí)音視頻的相關(guān)知識(shí)和工具昼丑,嘗試實(shí)現(xiàn)一個(gè)通過Asterisk播放mp4視頻文件的應(yīng)用呻逆。
- Asterisk播放mp4(1)——音頻和PCM編碼
- Asterisk播放mp4(2)——音頻封裝
- Asterisk播放mp4(3)——搭建開發(fā)環(huán)境
- Asterisk播放mp4(4)——H264&AAC
- Asterisk播放mp4(5)——MP4文件解析
- Asterisk播放mp4(6)——音視頻同步
- Asterisk播放mp4(7)——DTMF
本文分為四個(gè)部分:音頻基本概念,PCM編碼格式介紹菩帝,PCM A-law編碼格式介紹咖城,制作測試樣本。
音頻基本概念
我們聽到的聲音是由頻率(是什么)和強(qiáng)度(有多響)決定的呼奢。上圖是一個(gè)正弦函數(shù)生成的音頻宜雀,頻率是441Hz,采樣率是44.1kHz控妻,采樣(sample)的最大值是4095州袒。聲音的頻率就是一個(gè)完整的波形沒秒鐘重復(fù)的次數(shù),采樣率(sample_rate)是每秒鐘收集多少次聲音的強(qiáng)度數(shù)據(jù)弓候,這樣每個(gè)完整的波形包含100個(gè)采樣郎哭。
上面這個(gè)圖是個(gè)單聲道(channel)的音頻他匪,實(shí)際的音頻很多都是多聲道的。當(dāng)有多個(gè)聲道時(shí)夸研,需要定義每個(gè)聲道中的采樣用什么方式進(jìn)行排列邦蜜,例如:雙聲道的時(shí),可以LRLR...
亥至,也可以LL...,RR...
悼沈。(這個(gè)問題會(huì)在后續(xù)講容器的文章中在詳細(xì)說明)
每個(gè)采樣(sample)代表了聲音的強(qiáng)度,但它這并不是個(gè)絕對(duì)的聲音大小姐扮,只是相對(duì)的絮供。因?yàn)椋瑯拥穆曇粑募ㄟ^不同的設(shè)備播放茶敏,聲音的大小顯然不同壤靶,這個(gè)和播放設(shè)備提供的能量有關(guān)。聲音的絕對(duì)大小用分貝(dB)度量惊搏,采樣只是和分貝的相對(duì)關(guān)系贮乳。
PCM編碼
PCM(Pulse Code Modulation,脈沖編碼調(diào)制)是對(duì)聲音的振幅(音量的大刑窆摺)進(jìn)行編碼向拆,位深度為14bit,20bit酪耳,24bit等(用多少個(gè)二進(jìn)制位表示)浓恳。通過每秒鐘記錄若干次振幅的值(采樣率,8k碗暗,44.1k奖蔓,48k等),把聲音的波形記錄下來(頻率)讹堤。
計(jì)算機(jī)系統(tǒng)中8個(gè)二進(jìn)制位對(duì)應(yīng)1個(gè)字節(jié)吆鹤,所以實(shí)際表示一個(gè)采樣時(shí),需要用1個(gè)洲守,2個(gè)或4個(gè)字節(jié)進(jìn)行表示疑务;每個(gè)字節(jié)又可采用有符號(hào)或無符號(hào)的方式。當(dāng)用多個(gè)字節(jié)表示一個(gè)采樣時(shí)梗醇,又存在字節(jié)序的問題知允,就是前面的字節(jié)表示的高位還是低位,分為大字節(jié)序(big-endian叙谨,將高序字節(jié)存儲(chǔ)在起始地址)和小字節(jié)序(little-endian温鸽,將低序字節(jié)存儲(chǔ)在起始地址)。這樣就會(huì)產(chǎn)生 很多編碼的組合,下面是ffmpeg
支持的pcm
采樣格式(也可以通過命令行ffmpeg -formats | grep PCM
查看)涤垫。
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
};
PCM A-law編碼
PCM本身是一種無損(lossless)編碼格式(我理解無損的含義是收集到什么就記錄什么姑尺,不同格式間的差別在于記錄的精度,并不因?yàn)榉绞疆a(chǎn)生的損失)蝠猬。但是在電話系統(tǒng)中為了更有效地傳遞數(shù)據(jù)切蟋,提出了PCM A-alaw
和PCM mu-law
兩種編碼格式,其思路是將原始的PCM編碼榆芦, 壓縮成8位的編碼(具體的算法不展開了柄粹,可以參考g711原理pcm轉(zhuǎn)alaw,pcm轉(zhuǎn)ulaw匆绣,alaw轉(zhuǎn)pcm驻右,ulaw轉(zhuǎn)pcm)。
alaw
是將13位的有符號(hào)數(shù)壓縮為8位的有符號(hào)數(shù)崎淳,是有損編碼(壓縮)旺入,編碼后的數(shù)據(jù)通過解碼無法恢復(fù)到原狀,數(shù)值越大的采樣損失的數(shù)據(jù)就越多凯力。(這里有個(gè)疑問,在系統(tǒng)中是用16位保存一個(gè)樣本的礼华,那是否又有3位數(shù)據(jù)丟掉了咐鹤?)
alaw
只定義了采樣的編解碼方法,但是實(shí)際使用中還要注意采樣率的問題圣絮,通常mp3祈惶,mp4這些音視頻文件中的采樣率為44.1k或48k,而電話系統(tǒng)中的采樣率為8k扮匠,那么從高采樣率到低采樣率捧请,必然又會(huì)產(chǎn)生損失。
制作樣本數(shù)據(jù)
上圖是實(shí)現(xiàn)Asterisk播放mp4文件的簡要流程棒搜,中間涉及到音頻的編解碼過程疹蛉,其中任何一個(gè)環(huán)節(jié)(出現(xiàn)錯(cuò)誤或者因?yàn)榫幗獯a帶來損失)都可能導(dǎo)致最終聽到的聲音有問題(失真,噪音等)力麸。我們必須找到一種方法驗(yàn)證在各個(gè)環(huán)節(jié)是否運(yùn)行正確可款,例如;采樣率克蚂,采樣位深度等等闺鲸。我采用的方法是通過ffmpeg
這個(gè)工具生成測試數(shù)據(jù),并按照各個(gè)環(huán)節(jié)產(chǎn)生的編碼格式生成對(duì)應(yīng)的參照數(shù)據(jù)埃叭,然后比較在各個(gè)環(huán)節(jié)實(shí)際產(chǎn)生的數(shù)據(jù)是否正確摸恍。
pcm_s16le
通常mp4文件中的音頻解碼出的裸流是pcm_s16le
(有符號(hào),2字節(jié)赤屋,小字節(jié)序)立镶,那么我們先生成一個(gè)該格式的10秒鐘的裸流壁袄。
ffmpeg -lavfi sine -t 10 -f s16le -c:a pcm_s16le sine-10s.s16le
ffmpeg
告訴我們生成的文件的編碼格式pcm_s16le
,采樣率是44100
谜慌,單聲道mono
然想。通過查看源碼(libavfilter/asrc_sine.c)可以知道:1、正弦波的頻率是441Hz欣范,那么每周期包含100個(gè)采樣(44100/441)变泄,這個(gè)有助于我們理解生成的數(shù)據(jù)的變化規(guī)律;2恼琼、采樣的最大值等于4095(0xfff)妨蛹,并不是有符號(hào)16位數(shù)的最大值32767(0x8000),這個(gè)最大值正好是12bit晴竞,加上1位符號(hào)位是13蛙卤,正好和alaw
將13位有符號(hào)數(shù)轉(zhuǎn)為8位對(duì)應(yīng)上了。
生成文件的大小是882000
字節(jié)噩死,44100個(gè)采樣/秒 * 10秒 * 2字節(jié)/采樣颤难。
讀取生成文件的前200個(gè)采樣(400個(gè)字節(jié))生成圖形如下:
按16進(jìn)制格式打開文件(vi sine-10s.s16le,打開后:%!xxd
)已维,查看數(shù)據(jù):
可以看到在第26個(gè)采樣達(dá)到最大值ff0f
行嗤,第76個(gè)采樣達(dá)到最小值01f0
。因?yàn)槭切∽止?jié)序垛耳,要調(diào)換字節(jié)順序栅屏,所以最大值對(duì)應(yīng)的是0x0fff(4095)
,最小值對(duì)應(yīng)的是0xf001(-4095)
堂鲜。
用上面的ffmpeg
命令生成一段10秒鐘的pcm_s16le
格式音頻文件(裸流栈雳,沒有進(jìn)行封裝)。我們既可以用耳朵聽缔莲,也可以用眼睛看這個(gè)音頻哥纫。
alaw
下面我們把同樣的聲音做alaw
編碼。
ffmpeg -lavfi sine -t 10 -f alaw -c:a pcm_alaw sine-10s.alaw
生成文件的大小441000
痴奏,44100個(gè)采樣/秒 * 10秒 * 2字節(jié)/采樣磺箕。
讀取生成文件的前200個(gè)采樣(200個(gè))字節(jié),生成圖形:
alaw
采樣的符號(hào)位和原始pcm數(shù)據(jù)符號(hào)位是相反的抛虫,所以波峰和波谷和s16le
是相反的松靡;因?yàn)閿?shù)據(jù)進(jìn)行了編碼壓縮,所以編碼后的數(shù)據(jù)并不能體現(xiàn)出原始波形建椰,但是頻率并沒有發(fā)生改變雕欺。
直接打開文件查看數(shù)據(jù):
可以看到在波峰波谷位置數(shù)據(jù)的差異變小了。
如果需要設(shè)置采樣率(默認(rèn)是44.1k),例如:8k屠列,可以通過ar
參數(shù)實(shí)現(xiàn)啦逆。
ffmpeg -lavfi sine -t 10 -ar 8000 -f alaw -c:a pcm_alaw sine-8k-10s.alaw
alaw 轉(zhuǎn) s16le
alaw
是一種壓縮格式,需要解壓縮才能播放笛洛,按alaw
算法解碼出來的采樣時(shí)13位有符號(hào)數(shù)夏志,對(duì)應(yīng)的存儲(chǔ)格式就是pcm_s16le
,所以我們看看alaw轉(zhuǎn)s16le會(huì)是什么樣苛让?
ffmpeg -f alaw -i sine-10s.alaw -f s16le -c:a pcm_s16le sine-alaw2s16le-10s.s16le
這次是用之前生成好的alaw
文件作為輸入沟蔑。
取前200個(gè)樣本生成圖形。
從圖形上可以看到整體波形并沒有發(fā)生改變狱杰,但是在波峰波谷的位置存在失真瘦材,這表明alaw
的編解碼過程帶來了損失。
查看原始數(shù)據(jù)我們也可以看到已經(jīng)和原始s16le
數(shù)據(jù)不同仿畸,數(shù)據(jù)精度下降了食棕。
其他
其他ffmpeg命令
生成一段空白10秒裸流,采樣全是0错沽。
ffmpeg -lavfi anullsrc=r=44100:cl=mono -t 10 -f s16le -c:a pcm_s16le null-10s.raw
生成一段指定內(nèi)容的裸流(并不能設(shè)定為特定值簿晓,如果指定的是0,輸出的是0千埃,否則是最大值)憔儿。
ffmpeg -lavfi aevalsrc=1 -t 1 -f s16le -c:a pcm_s16le eval-1s.raw
采樣的數(shù)值含義
執(zhí)行如下命令,可以查看音頻文件的音量:
ffmpeg -i sine-10s.wav -filter:a volumedetect -f null /dev/null
獲得輸出內(nèi)容:
n_samples: 441000
mean_volume: -21.1 dB
max_volume: -18.1 dB
histogram_18db: 128000
看這個(gè)數(shù)據(jù)仍然不太明白音量到底是什么镰禾,通過看源碼,形成大體上的理解唱逢,0dB被當(dāng)作音量的極大值吴侦,對(duì)應(yīng)16位有符號(hào)數(shù)的最大值就是32767(0x8000),等于91dB的音量坞古。(目前并不確切知道為什么選91分貝這個(gè)值备韧,似乎是再高的值人就受不了。)
#define AMPLITUDE 4095 // 4095 0x0fff 是12bit的最大值(無符號(hào))
#define MAX_DB 91
// 0x0fff 4095
// 0x8000 32767
4095/32767 = 0.1249733
db = 20 * log10(0.1249733) = -18.1
參考
參考:http://ffmpeg.org/ffmpeg-filters.html#anullsrc
參考:http://ffmpeg.org/ffmpeg-filters.html#sine
參考:http://ffmpeg.org/ffmpeg-filters.html#aevalsrc
參考:https://trac.ffmpeg.org/wiki/AudioVolume
參考:http://ffmpeg.org/ffmpeg-filters.html#volumedetect
參考:https://stackoverflow.com/questions/2445756/how-can-i-calculate-audio-db-level