1. 音視頻同步的原理
2. 音視頻同步的較正方法
3. 音視頻同步時遇到的問題
聲明:以下所有內容均為轉載
1.原文轉自:http://bbs.chinavideo.org/viewthread.php?tid=1183&extra=page%3D1%26amp%3Bfilter%3Ddigest&page=1
下面貼出部分:
音視頻同步這一塊,我一直不是很了解,很想嘗試一下,下面是在qq群上向諸位前人請教的會話記錄,因為對我這樣的初學者很有幫助,所以粘貼至此,以作備份,再次感謝熱心回答問題的人,尤其是(不做好人!)^_^;
2006-10-25 10:21:06 dophin
有人有實時編碼時對音視頻進行錄制時,音視頻如何同步的相關資料么?有相關網站也可以哈,我實在找不到相關資料了。。。
2006-10-25 10:25:42 dophin
或者具體一點的也行,比如音視頻到底是如何同步的,有書籍介紹也可以啊,先謝了哈。。。。
(人眾人) 2006-10-25 10:21:45
通過時間戳
(人眾人) 2006-10-25 10:21:49
我也在研究這個問題
( 新左派) 2006-10-25 10:21:56
以音頻時間為主
(人眾人) 2006-10-25 10:22:01
對
(不做好人!) 2006-10-25 10:22:08
新左派講對了。
(不做好人!) 2006-10-25 10:22:14
聲音圖象交錯發送。
( 新左派) 2006-10-25 10:22:43
視頻解碼時,按當前播放時間找到對應的視頻幀
(不做好人!) 2006-10-25 10:22:44
可以一個音頻包有N個圖象。就在這一個音頻包完成的過程中按幀率顯示圖象。
(dophin) 2006-10-25 10:28:39
嗯,那么音頻又是以什么為主呢?畢竟,音頻的播放也是和時間有關系的,音頻在播放時就是只管自己播放就行么?
(不做好人!) 2006-10-25 10:24:05
聲卡有時間同步處理機制的。
(不做好人!) 2006-10-25 10:24:32
我以前的電腦主板如果驅動沒有裝好。聲音就非常快。結果圖象也是一閃就過去了。
( 新左派) 2006-10-25 10:24:47
聲音正常解碼與播放啊
(不做好人!) 2006-10-25 10:25:05
是WINDOWS MEDIAPLAYER播放的。只有驅動裝好了。能正確驅動的時候聲卡有時鍾同步功能的。
( 新左派) 2006-10-25 10:25:14
只是視頻的解碼需要參照當前的聲音的時間
(不做好人!) 2006-10-25 10:25:16
所以聲音和圖象交錯,
( 新左派) 2006-10-25 10:25:36
聲音播放時,是不用管什么的,就是單獨的聲音解碼
(dophin) 2006-10-25 10:32:16
哦,我現在大腦中大致有點模型了,就是音頻只管自己播放就行,視頻根據自己本身帶的時間戳與但前系統時間和音頻時間進行比較,然后解碼播放,如此實現同步,這么理解對么?
(不做好人!) 2006-10-25 10:28:00
你是用什么系統?
(dophin) 2006-10-25 10:33:26
聲音和圖像交錯是什么意思?這兩個不是開兩個線程完成的么?不存在交錯問題啊,即使存在,也是操作系統級的吧 ?
(不做好人!) 2006-10-25 10:28:35
AVVVVVVVVVVVVVVVAVVVVVVVVVVVVVVV
(dophin) 2006-10-25 10:33:42
我得,Linux。。。
(不做好人!) 2006-10-25 10:28:52
當你放A的時候,直接交給聲卡。
(不做好人!) 2006-10-25 10:29:30
中間的V就是前后兩個聲音包的相差時間,你就算出平均速度
(dophin) 2006-10-25 10:35:35
算出 平均速度后,得出每sec放幾幀,然后播放v,是這樣么?
(不做好人!) 2006-10-25 10:30:54
對
(不做好人!) 2006-10-25 10:31:01
你聲音有一個采樣率吧。
(dophin) 2006-10-25 10:36:02
對,有,
(不做好人!) 2006-10-25 10:31:23
這就可以算出前后一個聲音的時間了對不?
(不做好人!) 2006-10-25 10:31:36
比如44.1K/S
(dophin) 2006-10-25 10:36:36
對,
(不做好人!) 2006-10-25 10:31:56
那你從DSP中讀取22.05K的數據是不是0.5?
(dophin) 2006-10-25 10:37:05
是,
(不做好人!) 2006-10-25 10:32:29
那在這段0.5秒鍾的時間內你獲取了15幀的數據。那你是不是0.5/15=0.03333秒鍾就刷新一副圖。
(dophin) 2006-10-25 10:37:49
沒錯。
(不做好人!) 2006-10-25 10:32:55
然后你再讀取下一個聲音和圖象包。再這樣搞。就可以了啦。不過前提條件你采集必須是同步的。
(不做好人!) 2006-10-25 10:33:11
對了。你是用嵌入式的嗎?
(不做好人!) 2006-10-25 10:33:25
這個是以聲音為基礎的。
(不做好人!) 2006-10-25 10:34:04
還有一種是設置時鍾,計算你的幀率來設置前后幀的時間,中間有誤差就延時或者是跳躍一下。聲音就另外單獨管理。
(不做好人!) 2006-10-25 10:34:21
看看ffmpeg 的fplay.c,里面的源程序講得很詳細了。
(dophin) 2006-10-25 10:40:12
我的不是embedded
(dophin) 2006-10-25 10:40:24
就是普通pc機,
(不做好人!) 2006-10-25 10:36:08
哦。那你看fplay.c吧。里面很詳細的有聲音和圖象的同步。雖然簡單了一點。但麻雀雖小五臟俱全。
(dophin) 2006-10-25 10:41:54
哦,太好了,采集這一塊,我基本明白了,播放我想也應該差不多,我自己試試看看,太感謝了你了哈。。。^_^
(不做好人!) 2006-10-25 10:37:14
別謝謝我。
(不做好人!) 2006-10-25 10:37:24
不過我做的方法是按照fplay.c里面的做的。
(dophin) 2006-10-25 10:42:23
好的,我馬上就看看fplay。c,以前沒有看是因為感覺他的播放效果不如mplayer,
(不做好人!) 2006-10-25 10:38:06
這個是簡化了的啦。當然比不上mplayer
while(!mpctx->eof){
fill_audio_out_buffers();//音頻stream的讀取,解碼,播放
update_video(&blit_frame);//視頻stream的讀取,解碼,過濾處理
sleep_until_update(&time_frame, &aq_sleep_time);//計算延遲時間並睡眠等待
mpctx->video_out->flip_page();//視頻的播放
adjust_sync_and_print_status(frame_time_remaining, time_frame);//根據音視頻的PTS做同步矯正處理
}
音視頻同步方法為
1)音頻播放playsize = mpctx->audio_out->play(sh_audio->a_out_buffer, playsize, playflags); 后,根據數據大小算出時間並累計
mpctx->delay += playback_speed*playsize/(double)ao_data.bps;
2)視頻解碼前,用累計延遲時間剪掉本禎視頻的時間mpctx->delay -= frame_time;
3)計算聲音延遲時間*time_frame = delay - mpctx->delay / playback_speed;
其中float delay = mpctx->audio_out->get_delay();為距當前聲音OUTPUT BUF里數據被全部播放完為止所需的時間。
4)播放視頻同步完成,所以視頻的播放是完全根據聲卡最后的數據輸出來同步的。
5)計算出當前音視頻PTS差double AV_delay = a_pts - audio_delay - v_pts;再算出矯正值x = (AV_delay + timing_error * playback_speed) * 0.1f;最后把矯正的時間加到延遲累計中mpctx->delay+=x;。
這幾天搞文件回放,視頻格式是H264,音頻是PCM,使用FFMPEG來讀取音視頻,然后用ffmpeg來解碼顯示,所有的一切還算順利,但音視頻同步花了我很多時間,總也搞不清楚為什么會差很多。音視頻同步的原理當然是根據音頻的pts來控制視頻的播放,也就是說在視頻解碼一幀后,是否顯示以及顯示多長時間是通過該幀的PTS與同時正在播放的音頻的PTS比較而來的,如果音頻的PTS較大,則視頻顯示完畢准備下一幀的解碼顯示,否則等待。
具體實現時遇到的問題一:沒辦法得到正在播放的音頻幀的PTS,因為進行音頻播放使用的DirectSound,而對於DirectSound我只能得到當前拷入DirectSound的緩存的幀的PTS,而無法得到正在播放的PTS,如果得不到正在播放的幀的PTS的話,那同步肯定是不可能的了。在網上找資料好象也沒找到有用的,最后突然想到由於音頻幀的大小與時間成正比,那么DirectSound的緩存中的數據所需要的播放時間就可以計算得出,再根據當前正在拷入的音頻幀的PTS,就可以得到正在播放的幀的PTS,再用這個就可以正確同步視頻幀的顯示了。
問題二:根據上面的方法處理后還是出現不同步的現象,為什么這樣我也是百思不得其解,后來才發現是等待機制有問題,原來我是用Sleep()來做等待的,但實際上Sleep()的誤差很大的,網上有說有15MS,做音視頻同步肯定是不行的了,經過不斷的google,找到一份代碼:
void MySleep(int interval)
{
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 獲得計數器的時鍾頻率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 獲得初始值
do
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//獲得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 獲得對應的時間值,單位為秒
}while(dfTim<0.001 * interval);
}
可以達到精度比較高的等待,從效果看,也可以達到音視頻同步。
本以為問題到這就算結束了,但程序運行的時候怎么發現機器這么慢呀,看了下CPU占用率,達到100%。很顯然使用這個做等待是不行的了。
於是繼續google,網上有說timesetevent什么的,我沒有試。感覺麻煩了些。后來想到以前看過的一篇用WaitForSingleObject來做定時讓某段代碼執行的,於是試了試,一試之下立即發現效果明顯,CPU占用率一下子回到了個位數。更改后的代碼如下:
void MySleep(int interval)
{
HANDLE evt;
evt = CreateEvent(NULL, TRUE, FALSE, NULL);
WaitForSingleObject(evt, interval);
CloseHandle(evt);
}