使用AVFoundation處理視頻
使用AVAssetReader温兼、AVAssetWriter編解碼視頻
之前的兩篇文章淺略講了iOS音視頻開發(fā)相關(guān)代碼實(shí)現(xiàn)丰辣;
在編碼時(shí)關(guān)于音視頻的相關(guān)參數(shù)比較多,這些參數(shù)不是隨便什么數(shù)值就能行的隙姿;如果不理解緣由娜汁,而填寫了不合適的參數(shù)都弹,容易導(dǎo)致音視頻處理過程中出現(xiàn)各種奇怪的問題仁锯;
只有明白了音視頻相關(guān)的原理仙蚜,才能理解各種參數(shù)的含義此洲,才能更好的實(shí)現(xiàn)開發(fā);
現(xiàn)在委粉,就從音頻入手呜师,總結(jié)下音頻相關(guān)的理論知識;
聲音的本質(zhì)
聲音是如何產(chǎn)生的贾节?
聲音是由物體振動而產(chǎn)生的汁汗,一切正在發(fā)聲的物體都在振動
當(dāng)小球撞擊到音叉的時(shí)候,音叉會發(fā)生振動栗涂,對周圍的空氣產(chǎn)生擠壓知牌,然后導(dǎo)致更大范圍的空氣跟著一起振動,最后我們耳朵旁邊的空氣也開始振動斤程;這是因?yàn)榭諝猱a(chǎn)生了疏密變化角寸,形成疏密相間的縱波,由此就產(chǎn)生了聲波暖释,聲波一直延續(xù)到振動消失為止袭厂; 聲波一直傳入我們耳朵,就聽到了聲音球匕;
我們說話時(shí)的聲音纹磺,也是聲帶振動的結(jié)果;
聲音的本質(zhì)就是聲波亮曹;
我們聽到聲音的過程:
聲波 --> 耳廓(收集聲波)--> 外耳道(傳遞聲波) --> 鼓膜(將聲波轉(zhuǎn)換成振動) --> 聽小骨(放大振動) --> 耳蝸(將振動轉(zhuǎn)換成電信號) --> 聽覺神經(jīng)(傳遞電信號) --> 大腦(形成聽覺)
聲波的三要素
聲波的三要素是頻率橄杨、振幅和波形;頻率代表音階的高低照卦,振幅代表響度式矫,波形代表音色。
橫坐標(biāo)為時(shí)間役耕,縱坐標(biāo)為受振動的物體分子來回振動產(chǎn)生的位移采转;隨著時(shí)間推移,分子的來回振動的軌跡,就是一個(gè)正弦或余弦函數(shù)的波形圖故慈;
頻率
受振動的物體分子每秒來回振動的次數(shù)板熊,叫做頻率
;單位是秒分之一(1/s)察绷,也稱為赫茲
(Hz)干签;頻率用來表示振動的快慢
(如441Hz代表每秒來回振動441次)
頻率越高,波長就越短拆撼。反之頻率越低波長則越長容劳,低頻率可以更容易地繞過障礙物,因此能量衰減就小闸度,聲音就會傳得更遠(yuǎn)竭贩。
人類耳朵的聽力有一個(gè)頻率范圍,大約是20Hz~20kHz筋岛,不過娶视,即使是在這個(gè)頻率范圍內(nèi)晒哄,不同的頻率睁宰,聽力的感覺也會不一樣
振幅
物體未受到振動影響時(shí)的位置(橫軸上)稱為平衡位置;
從平衡位置到最大位移位置之間的距離寝凌,就叫做振幅
柒傻;
振幅代表響度,振幅越大表示響度越大能量越大较木,我們聽到的聲音就越大红符;
波形
上面我們說聲波是正弦或余弦函數(shù)的圖;但這是在單一頻率的聲波的情況下的伐债;
事實(shí)上预侯,聲源的振動產(chǎn)生的并不是單一頻率的聲波,而是由基音和不同頻率的泛音組成的復(fù)合聲音峰锁;當(dāng)聲源的主體振動時(shí)會發(fā)出一個(gè)基音萎馅;同時(shí)其余各部分也有復(fù)合的聲源,這些聲源組合產(chǎn)生泛音(其實(shí)就是物理學(xué)上的諧波)
泛音決定了不同的音色
虹蒋,不同的聲源由于其材料糜芳、結(jié)構(gòu)不同,泛音不同魄衅,則發(fā)出聲音的音色也不同峭竣;
最后用一張圖總結(jié)三要素
音頻數(shù)字化
上面講到聲音的本質(zhì)是聲波的形式,聲音屬于模擬信號皆撩,但便于計(jì)算機(jī)處理和存儲的是數(shù)字信號沮榜;所以需要將模擬信號(轉(zhuǎn)成數(shù)字信號后進(jìn)行存儲守呜。這一過程,即為音頻數(shù)字化由境。
我們在互聯(lián)網(wǎng)上聽到的聲音,都是先經(jīng)過錄制后轉(zhuǎn)為了數(shù)字音頻修然,再傳輸?shù)交ヂ?lián)網(wǎng)上的;
音頻數(shù)字化的常見技術(shù)方案是脈沖編碼調(diào)制(PCM囤捻,Pulse Code Modulation)瘟则;
主要過程:采樣 、量化 、編碼。
采樣
所謂采樣就是 在時(shí)間軸上對信號進(jìn)行數(shù)字化
模擬信號的波形是無限光滑的,可以看成由無數(shù)個(gè)點(diǎn)組成,由于存儲空間是相對有限的浅浮,數(shù)字編碼過程中铜靶,必須要對波形的點(diǎn)進(jìn)行采樣。采樣就是每隔一段時(shí)間采集一次模擬信號的樣本涩笤,在時(shí)間上將模擬信號離散化的過程恩沽。
根據(jù)采樣定理(奈奎斯特–香農(nóng)采樣定理),只有當(dāng)采樣率高于聲音信號最高頻率的2倍時(shí)脖镀,才能把采集的聲音信號唯一地還原成原來的聲音;因此要按比聲音最高頻率高2倍以上的頻率對聲音進(jìn)行采樣狼电;人耳能夠聽到的頻率范圍是20Hz~20kHz认然,所以采樣頻率一般為 44.1kHz,這樣就可以保證采樣聲音達(dá)到20kHz也能被數(shù)字化漫萄,從而使得經(jīng)過數(shù)字化處理之后卷员,人耳聽到的聲音質(zhì)量不會被降低。采樣率
表示每秒采集的樣本數(shù)量腾务,44.1kHz就是代表1秒會采樣44100次;
量化
量化是指在幅度軸上對信號進(jìn)行數(shù)字化毕骡,將每一個(gè)采樣點(diǎn)的樣本值數(shù)字化
比如用16比特的二進(jìn)制信號來表示聲音的一個(gè)采樣,而16比特所表示的 范圍是[-32768岩瘦,32767]未巫,共有2^16=625536個(gè)可能取值,因此最終模擬的音頻信號在幅度上也分為了65536層
這里的16bit即為位深度
(采樣精度/采樣大衅裘痢):使用多少個(gè)二進(jìn)制位來存儲一個(gè)采樣點(diǎn)的樣本值叙凡;位深度越高,表示的振幅越精確密末;
編碼
所謂編碼握爷,就是按照一定的格式記錄采樣和量化后的數(shù)字?jǐn)?shù)據(jù),比如順序存儲或壓縮存儲严里,等等新啼。
編碼涉及了很多種格式,通常說的音頻裸數(shù)據(jù)格式就是PCM(脈沖編碼調(diào)制)數(shù)據(jù)刹碾。PCM需要以下幾個(gè)概念:采樣格式(sampleFormat)
燥撞、采樣率 (sampleRate)
、聲道數(shù)(channel)
迷帜。
采樣格式:包含采樣位深度物舒、大小端模式、數(shù)據(jù)排列方式等戏锹;
以FFmpeg定義的sampleFormat為例:
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
};?????????????????
其中U,S,F,D表示存儲的類型冠胯,對應(yīng)unsigned、signed景用、float和double類型涵叮;
數(shù)值表示位深度惭蹂;
P表示聲道數(shù)據(jù)排列方式為Planar
,還有排列方式為Packed
:
對于雙聲道音頻來說割粮,Packed表示兩個(gè)聲道的數(shù)據(jù)交錯(cuò)存儲盾碗,交織在一起,即:
LRLRLRLR 的存儲方式舀瓢;
Planar 表示兩個(gè)聲道分開存儲廷雅,也就是平鋪分開,即:
LLLLRRRR 的存儲方式京髓;
以Packed存儲方式為例 大端模式不同位深度數(shù)據(jù)存儲如下:
聲道:單聲道產(chǎn)生一組聲波數(shù)據(jù)航缀,雙聲道(立體聲)產(chǎn)生兩組聲波數(shù)據(jù)。
對于聲音格式堰怨,還有一個(gè)概念用來描述它的大小芥玉,稱為比特率(byteRate)
,即指單位時(shí)間內(nèi)傳輸或處理的比特?cái)?shù)量备图;單位是:比特每秒(bps)灿巧,還有:千比特每秒(Kbps)、兆比特每秒(Mbps)等等揽涮;
以CD的音質(zhì)為例:位深度為16比特(2字節(jié))抠藕,采樣率為44.1kHZ,聲道數(shù)為2蒋困,這些信息就描述了CD的音質(zhì)盾似。對于1分鐘CD音質(zhì)的數(shù)據(jù),比特率為:
44100 * 16 * 2 = 1378.125 Kbps
存儲空間為:
1378.125 * 60 / 8 / 1024 = 10.09MB
通常雪标,采樣率零院、位深度越高,數(shù)字化音頻的質(zhì)量就越好汰聋。從比特率的特性可以看得出來:比特率越高门粪,數(shù)字化音頻的質(zhì)量也越好;我們所說的無損音樂烹困,就是采樣率、位深度都很高的乾吻,沒有進(jìn)行壓縮處理的數(shù)字化聲音髓梅;
最后還是用一張圖總結(jié)數(shù)字化過程:
音頻編解碼
編碼
前面計(jì)算了每分鐘CD音質(zhì)的數(shù)據(jù)采樣格式,需要的存儲空間約為10.1MB绎签;在網(wǎng)絡(luò)中傳播的話枯饿,數(shù)據(jù)量太大了;
為了更便于存儲和傳輸诡必,一般都會使用某種音頻編碼對它進(jìn)行編碼壓縮奢方,然后再存成某種音頻文件格式搔扁。
壓縮分為無損壓縮和有損壓縮。
無損壓縮:解壓后可以完全還原出原始數(shù)據(jù)蟋字;
有損壓縮:解壓后不能完全還原出原始數(shù)據(jù)稿蹲,會丟失一部分信息;一般是壓縮掉冗余信號(冗余信號是指不能被人耳感知到的信號鹊奖,包含人耳聽覺范圍之外的音頻信號以及被掩蔽掉的音頻信號等)苛聘,不進(jìn)行編碼處理。
解碼
當(dāng)需要播放音頻時(shí)忠聚,得先解碼(解壓縮)出PCM數(shù)據(jù)设哗,然后再進(jìn)行播放。
常見的編碼格式:
WAV編碼
WAV(Waveform Audio File Format)两蟀,是由IBM和Microsoft開發(fā)的音頻文件格式网梢,擴(kuò)展名是.wav,通常采用PCM編碼赂毯,常用于Windows系統(tǒng)中澎粟;編碼的一種實(shí)現(xiàn)就是在PCM數(shù)據(jù)格式的前面加上44字節(jié),分別用來描述PCM的采樣率欢瞪、聲道數(shù)活烙、數(shù)據(jù)格式等信息。
- 特點(diǎn):音質(zhì)非常好遣鼓,大量軟件都支持啸盏。
- 適用場合:多媒體開發(fā)的中間文件、保存音樂和音效素材骑祟。
MP3編碼
MP3(MPEG Audio Layer III)具有不錯(cuò)的壓縮比回懦,使用LAME編碼的中高碼率的MP3文件,聽感上非常接近源WAV文件次企。
- 特點(diǎn): 壓縮比比較高怯晕,大量軟件和硬件都支持,兼容性好缸棵。
- 適用場合:高比特率下對兼容性有要求的音樂欣賞舟茶。
AAC編碼
AAC(Advanced Audio Coding)是新一代的音頻有損壓縮技術(shù);
AAC編碼的文件擴(kuò)展名主要有3種:
- .acc:傳統(tǒng)的AAC編碼堵第,使用MPEG-2 Audio Transport Stream(ADTS)容器
- .mp4:使用了MPEG-4 Part 14的簡化版即3GPP Media Release 6 Basic(3gp6)進(jìn)行封裝的AAC編碼
- .m4a:為了區(qū)別純音頻MP4文件和包含視頻的MP4文件而由Apple公司使用的擴(kuò)展名吧凉;
- 特點(diǎn):在小于128Kbit/s的碼率下表現(xiàn)優(yōu)異,并且多用于視頻中的音頻編碼踏志。
- 適用場合:128Kbit/s以下的音頻編碼阀捅,多用于視頻中音頻軌的編碼。
代碼實(shí)現(xiàn)
- AVFoundation 音頻編碼
// -----解碼----
// AVAssetReader
do {
reader = try AVAssetReader(asset: composition)
} catch let e {
callback(false, e)
return
}
reader.timeRange = CMTimeRange(start: .zero, duration: composition.duration)
// AVAssetReaderOutput
audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil)
audioOutput.alwaysCopiesSampleData = false
audioOutput.audioMix = audioMix
if reader.canAdd(audioOutput) {
reader.add(audioOutput)
}
if !reader.startReading() {
callback(false, reader.error)
return
}
// -----編碼----
// AVAssetWriter
do {
writer = try AVAssetWriter(outputURL: outputUrl, fileType: .mp3)
} catch let e {
callback(false, e)
return
}
writer.shouldOptimizeForNetworkUse = true
let audioOutputSettings: [String : Any] = [
AVFormatIDKey: NSNumber(value: kAudioFormatMPEGLayer3),
AVNumberOfChannelsKey: NSNumber(value: 2),
AVSampleRateKey: NSNumber(value: 44100),
AVEncoderBitRateKey: NSNumber(value: 128000)
]
// AVAssetWriterInput
audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioOutputSettings)
if writer.canAdd(audioInput) {
writer.add(audioInput)
}
writer.startWriting()
writer.startSession(atSourceTime: .zero)
// 準(zhǔn)備寫入數(shù)據(jù)
videoInput.requestMediaDataWhenReady(on: inputQueue) { [weak self] in
...
}
其中audioOutputSettings的4項(xiàng)就對應(yīng)上面分析過的: 編碼格式针余,聲道數(shù)饲鄙,采樣率凄诞,比特率;這些設(shè)置最終決定了編碼后音頻的格式忍级、音頻的存儲空間及音質(zhì)帆谍;
而且這些設(shè)置都是固定組合的,不同編碼格式Format所需的Key有所不一樣颤练;
wav/pcm 格式設(shè)置(需要pcm相關(guān)設(shè)置):
let audioOutputSettings: [String : Any] = [
AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),
AVNumberOfChannelsKey: NSNumber(value: 2),
AVSampleRateKey: NSNumber(value: 44100),
AVLinearPCMBitDepthKey: NSNumber(value: 16),
AVLinearPCMIsBigEndianKey: NSNumber(value: false),
AVLinearPCMIsFloatKey: NSNumber(value: false),
AVLinearPCMIsNonInterleaved: NSNumber(value: false)
]
- FFmpeg
// 解碼 mp3-->pcm
ffmpeg -i test.mp3 -acodec pcm_s16le -f s16le -ac 2 -ar 44100 test.pcm
// 編碼 pcm-->mp3
ffmpeg -f s16le -ac 2 -ar 44100 -acodec pcm_s16le -i test test_new.mp3
播放pcm
ffplay -i crop.pcm -ar 44100 -ac 2 -f s16le
續(xù)篇:
音視頻開發(fā)基礎(chǔ)理論-視頻篇