在線(xiàn)直播網(wǎng)站源碼開(kāi)發(fā),音視頻同步的處理方案及選擇
共 3821字,需瀏覽 8分鐘
·
2022-02-09 17:34
如果在直播場(chǎng)景中,音視頻不同步就會(huì)給觀眾造成不好的觀感,不利于在線(xiàn)直播網(wǎng)站源碼的長(zhǎng)期發(fā)展,所以今天我們就一起來(lái)了解一下,在在線(xiàn)直播網(wǎng)站源碼開(kāi)發(fā)時(shí),音視頻同步的三種處理方案。
通常有以下三種:
1.視頻時(shí)鐘同步到音頻時(shí)鐘
以在線(xiàn)直播網(wǎng)站源碼音頻時(shí)鐘為標(biāo)準(zhǔn)時(shí)鐘,音頻自然播放。視頻幀播放時(shí)判斷當(dāng)前視頻幀播放結(jié)束后的時(shí)間與當(dāng)前的音頻時(shí)鐘時(shí)間對(duì)比,如果視頻當(dāng)前幀播放完時(shí)間比音頻時(shí)鐘時(shí)間早,則讓當(dāng)前視頻播放線(xiàn)程暫時(shí)時(shí)間差,以保證播放完后與音頻時(shí)鐘同步。如果在線(xiàn)直播網(wǎng)站源碼當(dāng)前視頻幀播放完時(shí)間比音頻時(shí)間晚,則丟棄當(dāng)前視頻幀讀取下一幀再判斷,以保證播放完后與音頻時(shí)鐘同步。
2.音頻時(shí)鐘同步到視頻時(shí)鐘
以在線(xiàn)直播網(wǎng)站源碼視頻時(shí)鐘為標(biāo)準(zhǔn),視頻自然播放。同步邏輯則與第1點(diǎn)的同步邏輯一致:即音頻快了就暫停音頻播放線(xiàn)程等待時(shí)間差,慢了則丟棄當(dāng)前音頻幀。以保證在線(xiàn)直播網(wǎng)站源碼當(dāng)前音頻幀播放完與視頻幀時(shí)鐘同步。
3.以外部時(shí)鐘為準(zhǔn),音頻與視頻時(shí)鐘同時(shí)同步到外部時(shí)鐘。
同步邏輯與1、2點(diǎn)一致。需要注意的是外部時(shí)鐘應(yīng)盡量使用毫秒時(shí)鐘以確保在線(xiàn)直播網(wǎng)站源碼中音視頻同步的精準(zhǔn)。
同步方案選擇
以上3種方案都可以實(shí)現(xiàn)在線(xiàn)直播網(wǎng)站源碼音頻與視頻的同步處理,但怎么選擇更適合的方案呢?
人的眼睛與耳朵對(duì)圖像與聲音的敏感程度不一樣,當(dāng)畫(huà)面偶爾缺少一幀或者幾幀時(shí)人的眼睛可能不太容易察覺(jué)。這是因?yàn)楫?huà)面的連貫性比較強(qiáng),兩幀畫(huà)面之前的差異有時(shí)候很小,眼睛比耳機(jī)敏感度更低。當(dāng)聲音發(fā)生一變化,比如缺失了一點(diǎn)聲音或者聲音異常的,人的耳朵馬上就察覺(jué)到了。
在大多數(shù)在線(xiàn)直播網(wǎng)站源碼上聲音的播放開(kāi)銷(xiāo)都比渲染畫(huà)面小。聲音的數(shù)據(jù)處理過(guò)程更簡(jiǎn)單,數(shù)量量也更小。聲音線(xiàn)程播放聲音卡頓的概率很小。
在線(xiàn)直播網(wǎng)站源碼聲音的播放緩存對(duì)象是重復(fù)利用的,而這個(gè)利用則是由實(shí)際播放聲音的具體線(xiàn)程來(lái)回調(diào)的。不同于視頻每一幀的渲染,聲音的暫停與丟棄相比視頻實(shí)現(xiàn)成本更高。
綜合上以的三點(diǎn),本文選擇第1點(diǎn)同步方案視頻時(shí)鐘同步到音頻時(shí)鐘
編碼實(shí)現(xiàn)音視頻同步
在線(xiàn)直播網(wǎng)站源碼音頻視頻同步基礎(chǔ)
FFmpeg里有兩種時(shí)間戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顧名思義,前者是解碼的時(shí)間,后者是顯示的時(shí)間。要仔細(xì)理解這兩個(gè)概念,需要先了解FFmpeg中的packet和frame的概念。
FFmpeg中用AVPacket結(jié)構(gòu)體來(lái)描述解碼前或編碼后的壓縮包,用AVFrame結(jié)構(gòu)體來(lái)描述解碼后或編碼前的信號(hào)幀。 對(duì)于在線(xiàn)直播網(wǎng)站源碼的視頻來(lái)說(shuō),AVFrame就是視頻的一幀圖像。這幀圖像什么時(shí)候顯示給用戶(hù),就取決于它的PTS。DTS是AVPacket里的一個(gè)成員,表示這個(gè)壓縮包應(yīng)該什么時(shí)候被解碼。
如果在線(xiàn)直播網(wǎng)站源碼視頻里各幀的編碼是按輸入順序(也就是顯示順序)依次進(jìn)行的,那么解碼和顯示時(shí)間應(yīng)該是一致的。可事實(shí)上,在大多數(shù)編解碼標(biāo)準(zhǔn)(如H.264或HEVC)中,編碼順序和輸入順序并不一致。 于是才會(huì)需要PTS和DTS這兩種不同的時(shí)間戳。
基本概念:
- I幀 :幀內(nèi)編碼幀 又稱(chēng)intra picture,I 幀通常是每個(gè) GOP(MPEG
所使用的一種視頻壓縮技術(shù))的第一個(gè)幀,經(jīng)過(guò)適度地壓縮,做為隨機(jī)訪問(wèn)的參考點(diǎn),可以當(dāng)成圖象。I幀可以看成是一個(gè)圖像經(jīng)過(guò)壓縮后的產(chǎn)物。 - P幀: 前向預(yù)測(cè)編碼幀,又稱(chēng)predictive-frame,通過(guò)充分將低于圖像序列中前面已編碼幀的時(shí)間冗余信息來(lái)壓縮傳輸數(shù)據(jù)量的編碼圖像,也叫預(yù)測(cè)幀;
- B幀: 雙向預(yù)測(cè)內(nèi)插編碼幀 又稱(chēng)bi-directional interpolated prediction
frame,既考慮與源圖像序列前面已編碼幀,也顧及源圖像序列后面已編碼幀之間的時(shí)間冗余信息來(lái)壓縮傳輸數(shù)據(jù)量的編碼圖像,也叫雙向預(yù)測(cè)幀; - PTS:Presentation Time Stamp。PTS主要用于度量解碼后的視頻幀什么時(shí)候被顯示出來(lái)
- DTS:Decode Time Stamp。DTS主要是標(biāo)識(shí)讀入內(nèi)存中的bit流在什么時(shí)候開(kāi)始送入在線(xiàn)直播網(wǎng)站源碼解碼器中進(jìn)行解碼。
在沒(méi)有B幀存在的情況下DTS的順序和PTS的順序應(yīng)該是一樣的。
IPB幀的不同:
- I幀:自身可以通過(guò)在線(xiàn)直播網(wǎng)站源碼視頻解壓算法解壓成一張單獨(dú)的完整的圖片。
- P幀:需要參考其前面的一個(gè)I frame 或者B frame來(lái)生成一張完整的圖片。
- B幀:則要參考其前一個(gè)I或者P幀及其后面的一個(gè)P幀來(lái)生成一張完整的圖片。
兩個(gè)I幀之間形成一個(gè)GOP,在x264中同時(shí)可以通過(guò)參數(shù)來(lái)設(shè)定bf的大小,即:I 和p或者兩個(gè)P之間B的數(shù)量。
通過(guò)上述基本可以說(shuō)明如果有B frame 存在的情況下一個(gè)GOP的最后一個(gè)frame一定是P。
DTS和PTS的不同:
DTS主要用于在線(xiàn)直播網(wǎng)站源碼視頻的解碼,在解碼階段使用.PTS主要用于視頻的同步和輸出.在display的時(shí)候使用.在沒(méi)有B frame的情況下.DTS和PTS的輸出順序是一樣的.
音頻與視頻數(shù)據(jù)緩沖對(duì)象增加時(shí)鐘數(shù)據(jù)
@interface FFQueueAudioObject : NSObject@property (nonatomic, assign, readonly)float pts;@property (nonatomic, assign, readonly)float duration;- (instancetype)initWithLength:(int64_t)length pts:(float)pts duration:(float)duration;- (uint8_t *)data;- (int64_t)length;- (void)updateLength:(int64_t)length;@end
@interface FFQueueVideoObject : NSObject@property (nonatomic, assign)double unit;@property (nonatomic, assign)double pts;@property (nonatomic, assign)double duration;- (instancetype)init;- (AVFrame *)frame;@end
分別在上面的音頻與視頻緩沖對(duì)象上增加變量pts與duration。
- pts: 當(dāng)前幀播放或顯示的時(shí)間。
- duration: 當(dāng)前幀播放或顯示持續(xù)的時(shí)長(zhǎng)。在線(xiàn)直播網(wǎng)站源碼音頻幀內(nèi)包括多個(gè)音頻數(shù)據(jù)包,而視頻則可以通過(guò)FPS計(jì)算得到每一幀的顯示持續(xù)時(shí)長(zhǎng)。
視頻時(shí)鐘同步到音頻時(shí)鐘
pthread_mutex_lock(&(self->mutex));/// 讀取當(dāng)前的音頻時(shí)鐘時(shí)間double ac = self->audio_clock;pthread_mutex_unlock(&(self->mutex));FFQueueVideoObject *obj = NULL;/// 統(tǒng)計(jì)路過(guò)的視頻幀數(shù)量int readCount = 0;/// 首先讀取一幀視頻數(shù)據(jù)obj = [self.videoFrameCacheQueue dequeue];readCount ++;/// 計(jì)算當(dāng)前視頻帖播放結(jié)束時(shí)的時(shí)間點(diǎn)double vc = obj.pts + obj.duration;if(ac - vc > self->tolerance_scope) {
/// 視頻太慢,丟棄當(dāng)前幀繼續(xù)讀取下一幀
/// 這里認(rèn)為讀取下一幀或者更下一幀不會(huì)造成視頻緩沖隊(duì)列枯竭,所以未做等待處理
/// 因?yàn)闀r(shí)時(shí)同步能形成的時(shí)間差比較有限
while (ac - vc > self->tolerance_scope) {
FFQueueVideoObject *_nextObj = [self.videoFrameCacheQueue dequeue];
if(!_nextObj) break;
obj = _nextObj;
vc = obj.pts + obj.duration;
readCount ++;
}} else if (vc - ac > self->tolerance_scope) {
/// 視頻太快,暫停一下再接著渲染顯示當(dāng)前視頻幀
float sleep_time = vc - ac;
usleep(sleep_time * 1000 * 1000);} else {
/// 在誤差范圍之后, 不需要處理}
tolerance_scope為可允許的誤差值,即在線(xiàn)直播網(wǎng)站源碼音頻與視頻時(shí)間差小于這個(gè)數(shù)據(jù)則認(rèn)為是同步的。這是因?yàn)橐_(dá)到絕對(duì)的時(shí)間一致性是不可能的,在計(jì)算時(shí)間的過(guò)程中有精度的丟失。
獲取當(dāng)前在線(xiàn)直播網(wǎng)站源碼音頻時(shí)鐘的時(shí)間(該時(shí)間為當(dāng)前音頻幀播放結(jié)束后的時(shí)間)
讀取一幀視頻幀,計(jì)算出該視頻幀播放完之后的時(shí)間
判斷在線(xiàn)直播網(wǎng)站源碼音頻時(shí)間與視頻時(shí)間的差值,進(jìn)行同步處理
到此,在線(xiàn)直播網(wǎng)站源碼音視頻同步的完成了,希望對(duì)大家的在線(xiàn)直播網(wǎng)站源碼開(kāi)發(fā)有幫助。
本文轉(zhuǎn)載自網(wǎng)絡(luò),轉(zhuǎn)載僅為分享干貨知識(shí),如有侵權(quán)歡迎聯(lián)系云豹科技進(jìn)行刪除處理
?
