ffmpeg開(kāi)發(fā)播放器學(xué)習(xí)筆記 - 解碼音頻,使用AudioQueue 播放

該節(jié)是ffmpeg開(kāi)發(fā)播放器學(xué)習(xí)筆記的第六節(jié)《ffmpeg解碼音頻,使用AudioQueue 播放》

ffmpeg音頻解碼后的數(shù)據(jù)是PCM(Pulse Code Modulation山憨,脈沖編碼調(diào)制)音頻數(shù)據(jù)是未經(jīng)壓縮的音頻采樣數(shù)據(jù)裸流弥喉,它是由模擬信號(hào)經(jīng)過(guò)采樣、量化由境、編碼轉(zhuǎn)換成的標(biāo)準(zhǔn)數(shù)字音頻數(shù)據(jù)。對(duì)于我們最常說(shuō)的“無(wú)損音頻”來(lái)說(shuō)虏杰,一般都是指?jìng)鹘y(tǒng)CD格式中的16bit/44.1kHz采樣率的文件格式,而知所以稱(chēng)為無(wú)損壓縮瘸彤,也是因?yàn)槠浒?0Hz-22.05kHz這個(gè)完全覆蓋人耳可聞范圍的頻響頻率而得名笛钝,當(dāng)然現(xiàn)在的各種PCM格式編碼高碼率文件已經(jīng)層出不窮非常常見(jiàn)质况,但是就像上文中所說(shuō)的,高碼率并不能有效地提升PCM編碼采樣率的頻響范圍中贝,而只能增加其采樣點(diǎn)來(lái)得到更加類(lèi)似模擬錄音的平滑波形臼朗。PCM音頻格式的播放在macOS/iOS平臺(tái)可以使用AudioQueueu與AudioUint來(lái)播放,本節(jié)采用了AudioQueue。

image

? 第一節(jié) - Hello FFmpeg
? 第二節(jié) - 軟解視頻流,渲染 RGB24
? 第三節(jié) - 認(rèn)識(shí)YUV
? 第四節(jié) - 硬解碼,OpenGL渲染YUV
? 第五節(jié) - Metal 渲染YUV
?? 第六節(jié) - 解碼音頻,使用AudioQueue 播放
?? 第七節(jié) - 音視頻同步
?? 第八節(jié) - 完善播放控制
?? 第九節(jié) - 倍速播放
?? 第十節(jié) - 增加視頻過(guò)濾效果
?? 第十一節(jié) - 音頻變聲

該節(jié) Demo 地址: https://github.com/czqasngit/ffmpeg-player/releases/tag/ffmpeg-audio-video
實(shí)例代碼提供了Objective-CSwift兩種實(shí)現(xiàn),為了方便說(shuō)明,文章引用的是Objective-C代碼,因?yàn)?code>Swift代碼指針看著不簡(jiǎn)潔绣否。
該節(jié)最終效果如下圖:

image

目標(biāo)

  • 了解PCM
  • 了解ffmpeg解碼同時(shí)播放音頻與視頻的流程
  • 了解AudioQueue播放流程
  • 完成ffmpeg解碼后的音頻與視頻同時(shí)播放

了解PCM

采樣率和采樣大小

聲音其實(shí)是一種能量波黎炉,因此也有頻率和振幅的特征,頻率對(duì)應(yīng)于時(shí)間軸線慷嗜,振幅對(duì)應(yīng)于電平軸線。波是無(wú)限光滑的庆械,弦線可以看成由無(wú)數(shù)點(diǎn)組成,由于存儲(chǔ)空間是相對(duì)有限的沐序,數(shù)字編碼過(guò)程中堕绩,必須對(duì)弦線的點(diǎn)進(jìn)行采樣。采樣的過(guò)程就是抽取某點(diǎn)的頻率值奴紧,很顯然,在一秒中內(nèi)抽取的點(diǎn)越多黍氮,獲取得頻率信息更豐富,為了復(fù)原波形捷枯,一次振動(dòng)中专执,必須有2個(gè)點(diǎn)的采樣淮捆,人耳能夠感覺(jué)到的最高頻率為20kHz,因此要滿足人耳的聽(tīng)覺(jué)要求已艰,則需要至少每秒進(jìn)行40k次采樣蚕苇,用40kHz表達(dá),這個(gè)40kHz就是采樣率涩笤。我們常見(jiàn)的CD,采樣率為44.1kHz蹬碧。光有頻率信息是不夠的,我們還必須獲得該頻率的能量值并量化誊稚,用于表示信號(hào)強(qiáng)度罗心。量化電平數(shù)為2的整數(shù)次冪,我們常見(jiàn)的CD位16bit的采樣大小渤闷,即2的16次方。采樣大小相對(duì)采樣率更難理解飒箭,因?yàn)橐@得抽象點(diǎn),舉個(gè)簡(jiǎn)單例子:假設(shè)對(duì)一個(gè)波進(jìn)行8次采樣肩碟,采樣點(diǎn)分別對(duì)應(yīng)的能量值分別為A1-A8凸椿,但我們只使用2bit的采樣大小,結(jié)果我們只能保留A1-A8中4個(gè)點(diǎn)的值而舍棄另外4個(gè)削饵。如果我們進(jìn)行3bit的采樣大小未巫,則剛好記錄下8個(gè)點(diǎn)的所有信息。采樣率和采樣大小的值越大叙凡,記錄的波形更接近原始信號(hào)。

有損和無(wú)損

根據(jù)采樣率和采樣大小可以得知跛璧,相對(duì)自然界的信號(hào)严里,音頻編碼最多只能做到無(wú)限接近追城,至少目前的技術(shù)只能這樣了,相對(duì)自然界的信號(hào)迷帜,任何數(shù)字音頻編碼方案都是有損的色洞,因?yàn)闊o(wú)法完全還原戏锹。在計(jì)算機(jī)應(yīng)用中火诸,能夠達(dá)到最高保真水平的就是PCM編碼,被廣泛用于素材保存及音樂(lè)欣賞奈搜,CD盾碗、DVD以及我們常見(jiàn)的WAV文件中均有應(yīng)用媚污。因此廷雅,PCM約定俗成了無(wú)損編碼,因?yàn)镻CM代表了數(shù)字音頻中最佳的保真水準(zhǔn)商架,并不意味著PCM就能夠確保信號(hào)絕對(duì)保真芥玉,PCM也只能做到最大程度的無(wú)限接近蛇摸。我們而習(xí)慣性的把MP3列入有損音頻編碼范疇灿巧,是相對(duì)PCM編碼的。強(qiáng)調(diào)編碼的相對(duì)性的有損和無(wú)損饿肺,是為了告訴大家盾似,要做到真正的無(wú)損是困難的敬辣,就像用數(shù)字去表達(dá)圓周率,不管精度多高村刨,也只是無(wú)限接近撰茎,而不是真正等于圓周率的值。

使用音頻壓縮技術(shù)的原因

要算一個(gè)PCM音頻流的碼率是一件很輕松的事情乾吻,采樣率值×采樣大小值×聲道數(shù) bps髓梅。一個(gè)采樣率為44.1KHz绎签,采樣大小為16bit,雙聲道的PCM編碼的WAV文件奢方,它的數(shù)據(jù)速率則為 44.1K×16×2 =1411.2 Kbps爸舒。我們常說(shuō)128K的MP3,對(duì)應(yīng)的WAV的參數(shù)扭勉,就是這個(gè)1411.2 Kbps,這個(gè)參數(shù)也被稱(chēng)為數(shù)據(jù)帶寬涂炎,它和ADSL中的帶寬是一個(gè)概念。將碼率除以8,就可以得到這個(gè)WAV的數(shù)據(jù)速率两蟀,即176.4KB/s震缭。這表示存儲(chǔ)一秒鐘采樣率為44.1KHz,采樣大小為16bit拣宰,雙聲道的PCM編碼的音頻信號(hào),需要176.4KB的空間巡社,1分鐘則約為10.34M,這對(duì)大部分用戶是不可接受的,尤其是喜歡在電腦上聽(tīng)音樂(lè)的朋友,要降低磁盤(pán)占用次企,只有2種方法潜圃,降低采樣指標(biāo)或者壓縮缸棵。降低指標(biāo)是不可取的谭期,因此專(zhuān)家們研發(fā)了各種壓縮方案。由于用途和針對(duì)的目標(biāo)市場(chǎng)不一樣踏志,各種音頻壓縮編碼所達(dá)到的音質(zhì)和壓縮比都不一樣胀瞪,在后面的文章中我們都會(huì)一一提到。有一點(diǎn)是可以肯定的凄诞,他們都?jí)嚎s過(guò)。

頻率與采樣率的關(guān)系

采樣率表示了每秒對(duì)原始信號(hào)采樣的次數(shù)伪朽,我們常見(jiàn)到的音頻文件采樣率多為44.1KHz汛蝙,這意味著什么呢?假設(shè)我們有2段正弦波信號(hào)患雇,分別為20Hz和20KHz,長(zhǎng)度均為一秒鐘苛吱,以對(duì)應(yīng)我們能聽(tīng)到的最低頻和最高頻,分別對(duì)這兩段信號(hào)進(jìn)行40KHz的采樣绘雁,我們可以得到一個(gè)什么樣的結(jié)果呢援所?結(jié)果是:20Hz的信號(hào)每次振動(dòng)被采樣了40K/20=2000次,而20K的信號(hào)每次振動(dòng)只有2次采樣住拭。顯然历帚,在相同的采樣率下杠娱,記錄低頻的信息遠(yuǎn)比高頻的詳細(xì)。這也是為什么有些音響發(fā)燒友指責(zé)CD有數(shù)碼聲不夠真實(shí)的原因禽拔,CD的44.1KHz采樣也無(wú)法保證高頻信號(hào)被較好記錄室叉。要較好的記錄高頻信號(hào),看來(lái)需要更高的采樣率茧痕,于是有些朋友在捕捉CD音軌的時(shí)候使用48KHz的采樣率,這是不可取的凿渊!這其實(shí)對(duì)音質(zhì)沒(méi)有任何好處,對(duì)抓軌軟件來(lái)說(shuō)搪锣,保持和CD提供的44.1KHz一樣的采樣率才是最佳音質(zhì)的保證之一彩掐,而不是去提高它。較高的采樣率只有相對(duì)模擬信號(hào)的時(shí)候才有用狗超,如果被采樣的信號(hào)是數(shù)字的朴下,請(qǐng)不要去嘗試提高采樣率努咐。
流特征
隨著網(wǎng)絡(luò)的發(fā)展殴胧,人們對(duì)在線收聽(tīng)音樂(lè)提出了要求团滥,因此也要求音頻文件能夠一邊讀一邊播放,而不需要把這個(gè)文件全部讀出后然后回放灸姊,這樣就可以做到不用下載就可以實(shí)現(xiàn)收聽(tīng)了;也可以做到一邊編碼一邊播放碗誉,正是這種特征,可以實(shí)現(xiàn)在線的直播哮缺,架設(shè)自己的數(shù)字廣播電臺(tái)成為了現(xiàn)實(shí)。

編碼格式

PCM編碼

PCM 脈沖編碼調(diào)制是Pulse Code Modulation的縮寫(xiě)。前面的文字我們提到了PCM大致的工作流程俺猿,我們不需要關(guān)心PCM最終編碼采用的是什么計(jì)算方式,我們只需要知道PCM編碼的音頻流的優(yōu)點(diǎn)和缺點(diǎn)就可以了诵冒。PCM編碼的最大的優(yōu)點(diǎn)就是音質(zhì)好谊惭,最大的缺點(diǎn)就是體積大。我們常見(jiàn)的Audio CD就采用了PCM編碼圈盔,一張光盤(pán)的容量只能容納72分鐘的音樂(lè)信息。

WAV格式

這是一種古老的音頻文件格式铁蹈,由微軟開(kāi)發(fā)众眨。WAV是一種文件格式,符合RIFF (Resource Interchange File Format) 規(guī)范娩梨。所有的WAV都有一個(gè)文件頭,這個(gè)文件頭包含了音頻流的編碼參數(shù)颂龙。WAV對(duì)音頻流的編碼沒(méi)有硬性規(guī)定掸冤,除了PCM之外,還有幾乎所有支持ACM規(guī)范的編碼都可以為WAV的音頻流進(jìn)行編碼稿湿。很多朋友沒(méi)有這個(gè)概念,我們拿AVI做個(gè)示范包斑,因?yàn)锳VI和WAV在文件結(jié)構(gòu)上是非常相似的,不過(guò)AVI多了一個(gè)視頻流而已神帅。我們接觸到的AVI有很多種萌抵,因此我們經(jīng)常需要安裝一些Decode才能觀看一些AVI,我們接觸到比較多的DivX就是一種視頻編碼绍填,AVI可以采用DivX編碼來(lái)壓縮視頻流,當(dāng)然也可以使用其他的編碼壓縮滔驶。同樣卿闹,WAV也可以使用多種音頻編碼來(lái)壓縮其音頻流,不過(guò)我們常見(jiàn)的都是音頻流被PCM編碼處理的WAV锻霎,但這不表示W(wǎng)AV只能使用PCM編碼,MP3編碼同樣也可以運(yùn)用在WAV中雇寇,和AVI一樣蚌铜,只要安裝好了相應(yīng)的Decode,就可以欣賞這些WAV了冬殃。
在Windows平臺(tái)下,基于PCM編碼的WAV是被支持得最好的音頻格式深滚,所有音頻軟件都能完美支持涣觉,由于本身可以達(dá)到較高的音質(zhì)的要求,因此官册,WAV也是音樂(lè)編輯創(chuàng)作的首選格式,適合保存音樂(lè)素材鸦难。因此,基于PCM編碼的WAV被作為了一種中介的格式合蔽,常常使用在其他編碼的相互轉(zhuǎn)換之中拴事,例如MP3轉(zhuǎn)換成WMA。

MP3編碼

MP3作為目前最為普及的音頻壓縮格式刃宵,為大家所大量接受,各種與MP3相關(guān)的軟件產(chǎn)品層出不窮,而且更多的硬件產(chǎn)品也開(kāi)始支持MP3步淹,我們能夠買(mǎi)到的VCD/DVD播放機(jī)都很多都能夠支持MP3,還有更多的便攜的MP3播放器等等键闺,雖然幾大音樂(lè)商極其反感這種開(kāi)放的格式澈驼,但也無(wú)法阻止這種音頻壓縮的格式的生存與流傳。MP3發(fā)展已經(jīng)有10個(gè)年頭了缝其,他是MPEG(MPEG:Moving Picture Experts Group) Audio Layer-3的簡(jiǎn)稱(chēng),是MPEG1的衍生編碼方案榴都,1993年由德國(guó)Fraunhofer IIS研究院和湯姆生公司合作發(fā)展成功漠其。MP3可以做到12:1的驚人壓縮比并保持基本可聽(tīng)的音質(zhì),在當(dāng)年硬盤(pán)天價(jià)的日子里拴驮,MP3迅速被用戶接受柴信,隨著網(wǎng)絡(luò)的普及,MP3被數(shù)以億計(jì)的用戶接受颠印。MP3編碼技術(shù)的發(fā)布之初其實(shí)是非常不完善的抹竹,由于缺乏對(duì)聲音和人耳聽(tīng)覺(jué)的研究止潮,早期的mp3編碼器幾乎全是以粗暴方式來(lái)編碼,音質(zhì)破壞嚴(yán)重袄琳。隨著新技術(shù)的不斷導(dǎo)入燃乍,mp3編碼技術(shù)一次一次的被改良,其中有2次重大技術(shù)上的改進(jìn)刻蟹。
除了以上編碼,還有其它的有興趣可以自己去google一下。

了解ffmpeg解碼同時(shí)播放音頻與視頻的流程

本節(jié)采用了解碼片效、播放音頻英古、渲染視頻各自己在單獨(dú)的線程中進(jìn)行,共享數(shù)據(jù)緩沖區(qū)。解碼線程利用ffmpeg從數(shù)據(jù)流中讀取數(shù)據(jù),并將音頻與視頻數(shù)據(jù)幀進(jìn)行解碼轉(zhuǎn)碼后分別存儲(chǔ)到音頻與視頻緩沖區(qū)膨桥。
音頻播放與視頻渲染線程從音頻解碼線程緩沖區(qū)請(qǐng)求數(shù)據(jù)唠叛,流程圖如下:


image
  • 解碼線程維護(hù)兩個(gè)緩沖區(qū): 音頻與視頻緩沖區(qū)
  • 音頻或視頻緩沖區(qū)緩沖數(shù)據(jù)未填滿時(shí)通知解碼線程開(kāi)始進(jìn)行解碼
  • 緩沖區(qū)數(shù)據(jù)填滿后解碼數(shù)據(jù)暫停,并通知音頻和視頻可以繼續(xù)播放(如果此時(shí)音頻或視頻緩沖區(qū)線程暫停)
  • 音頻播放線程從音頻緩沖區(qū)獲取數(shù)據(jù),如果緩沖區(qū)數(shù)據(jù)不夠則暫停音頻緩沖區(qū)并通知解碼線程繼續(xù)解碼(如果解碼線程暫停)
  • 視頻渲染線程從視頻緩沖區(qū)獲取數(shù)據(jù),如果緩沖區(qū)數(shù)據(jù)不夠則暫停視頻緩沖區(qū)并通知解碼線程繼續(xù)解碼(如果解碼線程暫停)

了解AudioQueue播放流程

AudioQueue是macOS/iOS平臺(tái)的可直接播放PCM數(shù)據(jù)的音頻播放庫(kù)之后,提供了C語(yǔ)言接口,利用AudioQueu可以很方便的播放PCM數(shù)據(jù)。

1.初始化AudioQueue

初始化AudioQueue時(shí)需要提供一個(gè)目標(biāo)PCM數(shù)據(jù)的描述,這個(gè)描述的結(jié)體構(gòu)是這樣的:

AudioStreamBasicDescription asbd;
asbd.mSampleRate = audioInformation.rate;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mChannelsPerFrame = 2;
asbd.mFramesPerPacket = 1;
asbd.mBitsPerChannel = 16;
asbd.mBytesPerFrame = 4;
asbd.mBytesPerPacket = 4;
/// kLinearPCMFormatFlagIsSignedInteger: 存儲(chǔ)的數(shù)據(jù)類(lèi)型
/// kAudioFormatFlagIsPacked: 數(shù)據(jù)交叉排列
asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mReserved = NO;

mSampleRate: 采樣率,描述了音頻采樣時(shí)的頻率(一秒時(shí)間對(duì)音頻波進(jìn)行多少次數(shù)據(jù)采集)
mFormatID: 播放的數(shù)據(jù)格式
mChannelsPerFrame: 一幀音頻有多少個(gè)通道數(shù)(單聲道介牙、雙聲道)
mFramesPerPacket: 一個(gè)數(shù)據(jù)包包含多少個(gè)音頻幀,PCM數(shù)據(jù)這個(gè)值是1
mBitsPerChannel: 一個(gè)數(shù)據(jù)通道占多少位數(shù)據(jù)
mBytesPerFrame: 一幀音頻數(shù)據(jù)占多少個(gè)字節(jié)數(shù)
mBytesPerPacket: 一個(gè)數(shù)據(jù)久占多少個(gè)字節(jié)數(shù)
mFormatFlags: 數(shù)據(jù)格式的具體描述
mReserved: 強(qiáng)制8位對(duì)齊,這里必須設(shè)置成0
這個(gè)結(jié)構(gòu)體具體描述的PCM數(shù)據(jù)的讀取方式,這很重要,數(shù)據(jù)讀取失敗播放出來(lái)的效果會(huì)很奇怪环础。
有了這個(gè)數(shù)據(jù)結(jié)構(gòu),就可以調(diào)用如下接口完成AudioQueue的初始化:

OSStatus status = AudioQueueNewOutput(&asbd, _AudioQueueOutputCallback, (__bridge void *)self, NULL, NULL, 0, &audioQueue);
NSAssert(status == errSecSuccess, @"Initialize audioQueue Failed");
  • 第一個(gè)參數(shù)是PCM數(shù)據(jù)描述的結(jié)構(gòu)體指針
  • 第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),在實(shí)際播放過(guò)程中需要重復(fù)利用AudioQueueBuffer以降低重復(fù)初始化AudioQueueBuffer的開(kāi)銷(xiāo)
  • 第三個(gè)參數(shù)是回調(diào)函數(shù)中的上下文參數(shù),在C語(yǔ)言的函數(shù)指針中這樣的設(shè)計(jì)很常見(jiàn),回調(diào)函數(shù)需要知道上下文并正確關(guān)聯(lián)對(duì)應(yīng)的對(duì)象
  • 最后一個(gè)參數(shù)則是初始化完成的AudioQueue對(duì)象
    其中第二個(gè)參數(shù)的函數(shù)簽名是這樣的:
static void _AudioQueueOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    FFAudioQueuePlayer *player = (__bridge FFAudioQueuePlayer *)inUserData;
    [player reuseAudioQueueBuffer:inBuffer];
}

2.初始化AudioQueueBuffer

AudioQueueBuffer是具體需要播放的音頻數(shù)據(jù)的載體,AudioQueueBuffer可以攜帶了多個(gè)音頻幀,完成AudioQueue的數(shù)據(jù)化之后就可以利用如下函數(shù)初始化足夠可重復(fù)利用的AudioQueueBuffer,這里MAX_BUFFER_COUNT設(shè)置為3剩拢。

for(NSInteger i = 0; i < MAX_BUFFER_COUNT; i ++) {
    AudioQueueBufferRef audioQueueBuffer = NULL;
    status = AudioQueueAllocateBuffer(self->audioQueue, audioInformation.buffer_size, &audioQueueBuffer);
    NSAssert(status == errSecSuccess, @"Initialize AudioQueueBuffer Failed");
    CFArrayAppendValue(buffers, audioQueueBuffer);
}

AudioQueueBuffer對(duì)象需要保存并引用,防止被釋放徐伐。其次在后期的播放控制中也需要重新利用這些對(duì)象。

3.啟動(dòng)并播放數(shù)據(jù)

AudioQueueStart(audioQueue, NULL);

首先調(diào)用上面的代碼啟動(dòng)播放器

AudioQueueBufferRef aqBuffer;
aqBuffer->mAudioDataByteSize = (int)length;
memcpy(aqBuffer->mAudioData, data, length);
AudioQueueEnqueueBuffer(self->audioQueue, aqBuffer, 0, NULL);
AudioQueueEnqueueBuffer(self->audioQueue, aqBuffer, 0, NULL);

設(shè)置好需要播放的數(shù)據(jù)大小與具體的數(shù)據(jù)之后,將AudioQueueBuffer對(duì)象放AudioQueue播放隊(duì)列即可播放出聲音。

完成ffmpeg解碼后的音頻與視頻同時(shí)播放

通過(guò)以上三步即可完成音頻數(shù)據(jù)的播放,但是在結(jié)合了ffmpeg之后除了音頻還需要播放視頻祸穷。

1.完善解碼

解碼在單獨(dú)的線程中進(jìn)行,這里為了方便說(shuō)明流程邏輯使用的是偽代碼(完整的代碼請(qǐng)看示例工程),大致邏輯是這樣的:

dispatch_async(decode_dispatch_queue, ^{
    while (true) {
        /// Video與Audio緩沖幀都超過(guò)最大數(shù)時(shí)暫停解碼線程,等待喚醒
        if((不需要解碼音頻 && 不需要解碼視頻) {
             sleep_for_wait();
        }
        AVFrame *frame = decode();
        if(is_audio(frame)) {
          audio_enqueue(frame);
          notify_audio_play_thread_play_if_wait();
        } else if(is_video(frame)) {
          video_enqueue(frame);
          notify_video_render_thread_render_if_wait();
        } else if(decode_complete) {
          /// 跳出while循環(huán)
          break;
        }
    }
    NSLog(@"Decode completed, read end of file.");
});

2.完善視頻渲染

dispatch_async(video_render_dispatch_queue, ^{
    if(!has_enough_video_frame() && !isDecodeComplete) {
        notify_decode_thread_keep_decode();
        sleep_video_render_thread_for_wait();
    }
    AVFrame* frame = video_dequeue();
    if(obj) {
        video_render(frame);
        if(low_max_cache_video_frame()) {
          notify_decode_thread_keep_decode();
        }
    } else {
        if(isDecodeComplete) {
            stop_video_render();
        }
    }
});

3.完善音頻播放

dispatch_async(audio_play_dispatch_queue, ^{
    if(!has_enough_audio_frame() && !isDecodeComplete) {
        notify_decode_thread_keep_decode();
        sleep_audio_play_thread_for_wait();
    }
    AVFrame* frame = audio_dequeue();
    if(obj) {
        audio_play(frame);
        if(low_max_cache_audio_frame()) {
          notify_decode_thread_keep_decode();
        }
    } else {
        if(isDecodeComplete) {
            stop_audio_play();
        }
    }
});

解碼線程扮演了生產(chǎn)者的角色,在數(shù)據(jù)不夠的時(shí)候生產(chǎn)數(shù)據(jù)雷滚。音頻播放與視頻渲染扮演了消費(fèi)者的角色,有足夠的數(shù)據(jù)后消費(fèi)數(shù)據(jù)吗坚。數(shù)據(jù)緩沖區(qū)保持一個(gè)合理的動(dòng)態(tài)平衡。

到此,播放器已經(jīng)完成了視頻的渲染與音頻的播放。但是這還不夠,音頻與視頻在單獨(dú)的線程中不會(huì)是一種非常理想的同步播放的狀態(tài),下一節(jié)將解決播放同步的問(wèn)題垫言。

總結(jié)

  • 了解PCM是什么以及模擬音頻信號(hào)轉(zhuǎn)換成數(shù)字信號(hào)的過(guò)程與常用的編碼格式
  • 了解ffmpeg解碼同時(shí)播放音頻與視頻的流程,對(duì)播放器有了一個(gè)大致的認(rèn)識(shí)
  • 了解AudioQueue播放流程
  • 完成ffmpeg解碼后的音頻與視頻同時(shí)播放
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霸褒,老刑警劉巖盈蛮,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抖誉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡袒炉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)孽文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夺艰,“玉大人,你說(shuō)我怎么就攤上這事郁副。” “怎么了拔疚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)稚失。 經(jīng)常有香客問(wèn)我,道長(zhǎng)嘱巾,這世上最難降的妖魔是什么诫钓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮问拘,結(jié)果婚禮上惧所,老公的妹妹穿的比我還像新娘骤坐。我一直安慰自己下愈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布拌夏。 她就那樣靜靜地躺著履因,像睡著了一般。 火紅的嫁衣襯著肌膚如雪栅迄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天西篓,我揣著相機(jī)與錄音憋活,去河邊找鬼。 笑死寸爆,一個(gè)胖子當(dāng)著我的面吹牛礁鲁,可吹牛的內(nèi)容都是我干的赁豆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼析二,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼节预!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起安拟,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糠赦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拙泽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泼疑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年荷荤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梅猿。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袱蚓,死狀恐怖几蜻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梭稚,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布忱屑,位于F島的核電站,受9級(jí)特大地震影響莺戒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜从铲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一名段、第九天 我趴在偏房一處隱蔽的房頂上張望阱扬。 院中可真熱鬧伸辟,春花似錦、人聲如沸用踩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)姊扔。三九已至,卻和暖如春恰梢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗅回。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工摧茴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苛白。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像懂版,于是被迫代替她去往敵國(guó)和親躏率。 傳聞我的和親對(duì)象是個(gè)殘疾皇子民鼓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353