1. 程式人生 > 其它 >時間戳 音視訊同步

時間戳 音視訊同步

http://6352513.blog.51cto.com/6342513/1180742

http://blog.csdn.net/happydeer/article/details/206765

http://blog.csdn.net/sidumqz/article/details/53102623

一、time_base
AVStream的time_base的單位是秒。每種格式的time_base的值不一樣,根據取樣來計算,比如mpeg的pts、dts都是以90kHz來取樣的,所以取樣間隔就是1/900000秒。

AVCodecContext的time_base單位同樣為秒,不過精度沒有AVStream->time_base高,大小為1/framerate。

AVPacket下的pts和dts以AVStream->time_base為單位(數值比較大),時間間隔就是AVStream->time_base。

AVFrame裡面的pkt_pts和pkt_dts是拷貝自AVPacket,同樣以AVStream->time_base為單位;而pts是為輸出(顯示)準備的,以AVCodecContex->time_base為單位。

輸入流InputStream下的pts和dts以AV_TIME_BASE為單位(微秒),至於為什麼要轉化為微秒,可能是為了避免使用浮點數。

輸出流OutputStream涉及音視訊同步,結構和InputStream不同,暫時只作記錄,不分析。

二、各個time_base之前的轉換
ffmpeg提供av_rescale_q函式用於time_base之間轉換,av_rescale_q(a,b,c)作用相當於執行a*b/c,通過設定b,c的值,可以很方便的實現time_base之間轉換。

** 例如 **

InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
{
pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
}
AVPacket(AVStream->time_base)到InputStream(AV_TIME_BASE)
static int process_input_packet(InputStream *ist, const AVPacket *pkt)
{
if (pkt->dts != AV_NOPTS_VALUE)
{
ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
}
}
三、其他


AVFrame->pts和AVPacket->pts、AVPacket->dts的值,在解碼/編碼後,會經歷短暫的time_base不匹配的情況:

解碼後,decoded_frame->pts的值使用AVStream->time_base為單位,後在AVFilter裡面轉換成以AVCodecContext->time_base為單位。
編碼後,pkt.pts、pkt.dts使用AVCodecContext->time_base為單位,後通過呼叫"av_packet_rescale_ts"轉換為AVStream->time_base為單位。

對pts、dts、duration的處理主要集中在兩大函式裡面
1、process_input()讀入資料並處理,放到濾鏡裡面
2、reap_filters()從濾鏡讀出資料,處理後寫入檔案

媒體內容在播放時,最令人頭痛的就是音視訊不同步。從技術上來說,解決音視訊同步問題的最佳方案就是時間戳

首先選擇一個參考時鐘(要求參考時鐘上的時間是線性遞增的);

生成資料流時依據參考時鐘上的時間給每個資料塊都打上時間戳(一般包括開始時間和結束時間);

在播放時,讀取資料塊上的時間戳,同時參考當前參考時鐘上的時間來安排播放(如果資料塊的開始時間大於當前參考時鐘上的時間,則不急於播放該資料塊,直到參考時鐘達到資料塊的開始時間;如果資料塊的開始時間小於當前參考時鐘上的時間,則“儘快”播放這塊資料或者索性將這塊資料“丟棄”,以使播放進度追上參考時鐘)。

圖2.8 解決音視訊同步問題的時間戳方案

可見,避免音視訊不同步現象有兩個關鍵——

一是在生成資料流時要打上正確的時間戳。如果資料塊上打的時間戳本身就有問題,那麼播放時再怎麼調整也於事無補。

如圖2.8,視訊流內容是從0s開始的,假設10s時有人開始說話,要求配上音訊流,那麼音訊流的起始時間應該是10s,如果時間戳從0s或其它時間開始打,則這個混合的音視訊流在時間同步上本身就出了問題。

打時間戳時,視訊流和音訊流都是參考參考時鐘的時間,而資料流之間不會發生參考關係;也就是說,視訊流和音訊流是通過一箇中立的第三方(也就是參考時鐘)來實現同步的。

第二個關鍵的地方,就是在播放時基於時間戳對資料流的控制,也就是對資料塊早到或晚到採取不同的處理方法。

圖2.8中,參考時鐘時間在0-10s內播放視訊流內容過程中,即使收到了音訊流資料塊也不能立即播放它,而必須等到參考時鐘的時間達到10s之後才可以,否則就會引起音視訊不同步問題。

基於時間戳的播放過程中,僅僅對早到的或晚到的資料塊進行等待或快速處理,有時候是不夠的。

如果想要更加主動並且有效地調節播放效能,需要引入一個反饋機制,也就是要將當前資料流速度太快或太慢的狀態反饋給“源”,讓源去放慢或加快資料流的速度。

熟悉DirectShow的讀者一定知道,DirectShow中的質量控制(Quality Control)就是這麼一個反饋機制。DirectShow對於音視訊同步的解決方案是相當出色的。但WMF SDK在播放時只負責將ASF資料流讀出並解碼,而並不負責音視訊內容的最終呈現,所以它也缺少這樣的一個反饋機制。

為了更好地理解基於時間戳的音視訊同步方案,下面舉一個生活中的例子。假設你和你的一個朋友約好了今天18:00在滬上廣場見面,然後一起吃飯,再去打遊戲。實際上,這個18:00就是你和你朋友保持同步的一個時間點。結果你17:50就到了滬上廣場,那麼你必須等你的朋友。10分鐘過後,你的朋友還沒有到,這時他打來電話說有事耽擱了,要晚一點才能到。你沒辦法,因為你已經在旁邊的餐廳預訂了位置,如果不馬上趕過去,預訂就會被取消,於是你告訴你的朋友直接到餐廳碰頭吧,要他加快點。於是在餐廳將來的某個時間點就成為你和你朋友的又一個同步點。雖然具體時間不定(要看你朋友趕過來的速度),但這樣努力的方向是對的,你和你朋友肯定能在餐廳見到面。結果呢?你朋友終於在18:30趕過來了,你們最終“同步”了。吃完飯19:30了,你臨時有事要處理一下,於是跟你朋友再約好了20:00在附近的一家遊戲廳碰頭。你們又不同步了,但在遊戲廳將來的某個時間點你們還是會再次同步的。

悟出什麼道理了沒有?其實,同步是一個動態的過程,是一個有人等待、有人追趕的過程。同步只是暫時的,而不同步才是常態。人們總是在同步的水平線上振盪波動,但不會偏離這條基線太遠。

====================================http://www.cnblogs.com/NerdWill/p/6744432.html
在對音視訊重新編碼並需要進行同步的場景中,需要遵守幾項基本原則(否則音視訊就會卡頓,不流暢。以音訊aac編碼頻率44.1k,視訊h264編碼25幀幀率為例):

  1. 保證輸入端的音視訊幀到達間隔基本精確。音訊aac每幀時長是23.2ms(1000*1024/44100),視訊每幀時長是40ms(1000/25)。所以,用於編碼的原始音訊samples的到達頻率(或從buffer中獲取的頻率)應該為441 samples/per channel/ per 10ms(每個樣本假設16bits,即882位元組/通道/10ms,如果原始音訊取樣率不是44.1k,編碼前需要重新取樣);原始視訊幀到達頻率(或從buffer中獲取的頻率)應該為1幀/per 40ms(視訊幀率可能需要重新取樣)。如果輸出的音視訊流不流暢,可先檢查輸入端音視訊流的輸入間隔情況。

2.保證輸出端的音視訊流時間戳使用同一參考系。比如音視訊流都使用當前系統時間作為同一時間參考系,但音視訊流可以有不同的系統時間起始點。比如音訊流先開始於1492764087000 ms,視訊稍後700ms開始於1492764087700ms。比如rtmp裡面使用32位時間戳,則音視訊流只能使用相對時間戳,接上例,音訊時間戳增長到700ms的時候視訊流才從0開始,表示視訊流是從音訊流的700ms處開始的,這樣才能達到同步的效果。 另外一種時間戳方案是每個音視訊幀按固定間隔增長,比如音視訊時間戳都從0開始,音訊每個aac幀增加23.2ms,每個視訊幀增長40ms。正常情況下,音視訊流是同時從0開始按相應各自間隔傳送幀的,但也有視訊流晚於音訊流或音訊流晚於視訊流的情況。這種情況需要做時間戳同步,稍晚的流起始時間要根據超前的流的時間來設定。

3.保證交叉輸出時音視訊間隔基本精確。這裡的輸出端就完全等同於一個硬體編碼器,只有保證交叉輸出的音視訊幀間隔穩定,才能保證播放端的流暢。比如rtmp,每個aac音訊幀輸出間隔應該在23ms左右,每個視訊幀輸出間隔應該在40ms左右,而且音視訊幀是交叉輸出。換句話說,每23ms要傳送一個aac音訊幀,每40ms傳送一個視訊幀(可以使用兩個單獨的執行緒來分別傳送音視訊流)。如果排除了上面的兩個問題還是不能流暢播放,可以檢查這個環節是否正常。

總之,重新編碼並同步的這個環節,必須建立在數學測量的基礎上。有錯誤或補充的地方,歡迎指出。

================

http://www.cnblogs.com/my_life/articles/6944054.html

視訊中幀就是一個圖片取樣。音訊中一幀一般包含多個樣本,如AAC格式會包含1024個樣本。

http://blog.sina.com.cn/s/blog_6b87c7eb010182hs.html

http://www.jianshu.com/p/030288800a61

取樣頻率是指將模擬聲音波形進行數字化時,每秒鐘抽取聲波幅度樣本的次數。

正常人聽覺的頻率範圍大約在20Hz~20kHz之間,根據奈奎斯特取樣理論,為了保證聲音不失真,取樣頻率應該在40kHz左右。常用的音訊取樣頻率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果採用更高的取樣頻率,還可以達到DVD的音質。

音訊
在數字音訊領域,常用的取樣率有:

8,000 Hz - 電話所用取樣率, 對於人的說話已經足夠
11,025 Hz
22,050 Hz - 無線電廣播所用取樣率
32,000 Hz - miniDV 數碼視訊 camcorder、DAT (LP mode)所用取樣率
44,100 Hz - 音訊CD, 也常用於MPEG-1 音訊(VCD, SVCD, MP3)所用取樣率
47,250 Hz - Nippon Columbia (Denon)開發的世界上第一個商用 PCM 錄音機所用取樣率
48,000 Hz - miniDV、數字電視、DVD、DAT、電影和專業音訊所用的數字聲音所用取樣率
50,000 Hz - 二十世紀七十年代後期出現的3M 和Soundstream 開發的第一款商用數字錄音機所用取樣率
50,400 Hz - 三菱 X-80 數字錄音機所用所用取樣率
96,000 或者 192,000 Hz - DVD-Audio、一些 LPCM DVD 音軌、BD-ROM(藍光碟)音軌、和 HD-DVD (高清晰度 DVD)音軌所用所用取樣率
2.8224 MHz - SACD、索尼 和 飛利浦 聯合開發的稱為Direct Stream Digital的1位sigma-delta modulation 過程所用取樣率。
取樣頻率定義了每秒從連續訊號中提取並組成離散訊號的取樣個數,取樣頻率的倒數是取樣週期或者叫作取樣時間,它是取樣之間的時間間隔

44100已是CD音質, 超過48000或96000的取樣對人耳已經沒有意義。這和電影的每秒 24 幀圖片的道理差不多。

AAC一幀可以解析出的音訊時長
一個AAC原始幀包含一段時間內1024個取樣及相關資料
音訊幀的播放時間=一個AAC幀對應的取樣樣本的個數/取樣頻率(單位為s)
一幀 1024個 sample。取樣率 Samplerate 44100KHz,每秒44100個sample, 所以 根據公式 音訊幀的播放時間=一個AAC幀對應的取樣樣本的個數/取樣頻率
當前AAC一幀的播放時間是= 10241000000/44100= 22.32ms(單位為ms)
(一個aac幀是由1024個樣本組成的,一秒內aac的取樣次數是44.1k次,所以一個aac幀的時長是1
1024/44.1k, 單位為秒)
對取樣率為44.1kHz的AAC音訊進行解碼時,一幀的解碼時間須控制在23.22毫秒內。

MP3一幀可以解析出的音訊時長
mp3 每幀均為1152個位元組 (樣本), 則:
frame_duration = 1152 * 1000000 / sample_rate
sample_rate = 44100HZ時, 計算出的時長為26.122ms,這就是經常聽到的mp3每幀播放時間固定為26ms的由來。
H264 視訊
視訊的一個幀的播放時間跟幀率有關:

frame_duration = 1000/幀率(fps)

例如:fps = 25.00 ,計算出來的時常為40ms,這就是同行所說的40ms一幀視訊資料。

================

http://blog.csdn.net/aoshilang2249/article/details/38469051

http://blog.csdn.net/ownWell/article/details/8114121

【取樣位數】

即取樣值或取樣值(就是將取樣樣本幅度量化)。它是用來衡量聲音波動變化的一個引數,也可以說是音效卡的解析度。它的數值越大,解析度也就越高,所發出聲音的能力越強。

每個取樣資料記錄的是振幅, 取樣精度取決於取樣位數的大小:
1 位元組(也就是8bit) 只能記錄 256 個數, 也就是隻能將振幅劃分成 256 個等級;
2 位元組(也就是16bit) 可以細到 65536 個數, 這已是 CD 標準了;
4 位元組(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.

【交錯模式】
http://www.cnblogs.com/my_life/articles/6841859.html
數字音訊訊號儲存的方式。資料以連續幀的方式存放,即首先記錄幀1的左聲道樣本和右聲道樣本,再開始幀2的記錄…
【非交錯模式】
首先記錄的是一個週期內所有幀的左聲道樣本,再記錄所有右聲道樣本。

==================
http://blog.sina.com.cn/s/blog_864ca0b60101p7zn.html
音訊中經常會提到兩個概念:取樣率、位元率(位元速率)。實際上當我們在播放MP3時,這兩個資訊有些播放器會顯示:例如:取樣率44.1KHz,位元率192Kbps。
取樣率是指每秒從連續訊號中提取並組成離散訊號的取樣個數,用赫茲(Hz)來表示。
1、編碼前位元率
位元率是指經過編碼後的音訊資料每秒鐘需要用多少個位元來表示,但是這裡的編碼需要注意不是指MP3,WMA等編碼概念,是指模擬訊號到數字訊號的編碼即(PCM資料),與它們密切相關的是表示每個PCM樣點的bit位數。比如8bit,16bit
以網上的例子來說:
以電話為例,每秒3000次取樣,每個取樣是7位元,那麼電話的位元率是21000。而CD是每秒44100次取樣,兩個聲道,每個取樣是13位PCM編碼,所以CD的位元率是44100213=1146600,也就是說CD每秒的資料量大約是144KB,而一張CD的容量是74分等於4440秒,就是639360KB=640MB。

通過上面的原理說明,就很容易解釋,為什麼有些音訊檔案取樣率一樣,但是位元率不一樣,或者是位元率一樣,取樣率不一樣的問題。當然,在實際的應用中,為了同一輸出,可能會將解碼後的PCM資料進行上取樣或者下采樣統一到48K,最後輸出。由於這個限制,對於取樣檔案本身為96K的高保真檔案就無法有效播放。需要更加高階的播放硬體。
2、編碼後位元率
位元率是指經過編碼後的音訊資料每秒鐘需要用多少個位元來表示,但是這裡的編碼需要注意是指MP3,WMA等編碼概念,不是指模擬訊號到數字訊號的編碼即(PCM資料),與它們密切相關的是表示每個PCM樣點的bit位數。比如8bit,16bit
編碼的時候設定位元率(wav —>aac—>wav),例如為64Kbps,也就是說音樂播放時每秒讀取的編碼檔案的大小是64KBytes,因此假如一個12m的音樂片段,它的檔案大小應該是64*12 /8=96Kb,以上資料都是指AAC檔案的大小。至於aac編碼得到的檔案wav大小,它與aac壓縮說法的壓縮比有關。
aac—>wav的原理,aac 檔案中需要包含通道個數資訊,取樣率資訊,幀大小。解碼的時候是一幀一幀的解碼得到480或者是512PCM資料。做模擬時存放到wav檔案中。如果是實時播放,則需要考慮DAC的取樣率問題,一般它的取樣率是一定的,我們的平臺是按48K輸出。即每秒鐘48000個PCM資料輸入到 DAC處處理。這裡需要注意區分位元率概念。如果是做模擬儲存到wav檔案,則可以看到相應的資訊.

=====================================
http://developer.t-firefly.com/thread-5731-1-1.html
http://www.cnblogs.com/my_life/articles/6274692.html

PCR(Program Clock
Reference)同步在非硬體精確時鐘源的情況還是謹慎使用,我起初採用PCR同步就會出現,隨著時間的推移,軟體模擬的時鐘不精確會導致視訊出現延時或者音視訊不同步。
最近研究了FFMPEG的同步技巧,覺得其精妙絕倫,完全不用擔心隨著時間的推移會發生如上問題!下面簡述下FFMPEG下如何進行音視訊同步的吧!
FFMPEG有三種同步方式,視訊同步音訊,音訊同步視訊,同步到外部時鐘!
第三種,同步到外部始終也就是PCR同步和我原來說的那中同步方式,一樣! 用的最多的還是,視訊同步音訊,為什麼呢?
音訊的取樣率是固定的,若音訊稍有卡頓,都會很明顯的聽出來,反則視訊則不如此,雖然表面上說的是30P(每秒30幀),不一定每一幀的間隔就必須精確到33.33ms(所以每幀間隔大約33.33ms),
因為人肉眼是觀察不出來的,所以視訊的幀率可以是動態的,並不是嚴格標準的! 廢話少說了!用視訊同步音訊,做法很簡單!
首先,音訊執行緒只管自己獨立解碼播放。視訊執行緒在顯示之前只需要檢測視訊PTS是否大於音訊PTS,若大,則等待音訊PTS>=視訊PTS,若小,則直接播放,小太多則可以直接丟棄(跳幀)。做法就如此簡單!但前提是你編碼器一定要打好正確的PTS,若沒有則只有自己偽造PTS了!

音視訊同步和幀率控制其實是一個東西。我們先不管音視訊同步是什麼,我們先來看看如何進行幀率控制。明白了幀率控制,音視訊同步那些都一通百通。一些基本的音視訊術語我就不介紹了,大家自己百度吧!
1、幀率控制
幀率控制的方法有千萬種,最2的方法無非是每解碼/顯示一幀就進行延時,為了方便我們在進行幀率控制的同時能夠理解音視訊同步,我在此採用PCR同步的方式來進行幀率控制。網上關於PCR同步的原理講了一大堆,有些很是難懂,一點兒也不通俗,我這裡來給大家把晦澀的理論以最通俗的方式表達出來。還希望大家多多指教!

拿1280x720@30p的視訊源來做理解。30P也就是說每秒鐘30幀,也就是每一幀需要1/30*1000ms大概也就是每隔33.33ms就必須顯示一幀。

要想知道如何正確的進行解碼,就必須先了解編碼端是如何工作的!
一般編碼器會以27MHZ的時鐘來進行編碼,這些都不重要,重要的就是,編碼器一般預設會每隔30ms會發送一次PCR資訊,這裡的PCR資訊就很重要了,
他是我們在解碼端解碼進行幀率控制的時間基點,同時也是我們以後在進行同步校準的校準基點。

說了這麼多,那麼我就那個PCR資訊來給大家分析分析。PCR資訊是33bit組成的一個INT64_T的資料,從解複用器裡面出來我們可以得到一個很龐大的數字,看這個我們當然看不太懂!但是如果知道這個數字如何生成的那就好理解多了!

PCR資訊說白了就是給視訊的時間戳資訊,比如一部電影是從 (00:01:23:033)時 : 分 : 秒 : 毫秒 開始,那麼這個時間基點生成的PCR資訊就是 (((00*60+1)*60+23.033)90K)%2^33。90K為27M,300分頻的結果。剛剛說了PCR會每30ms更新一次,那麼PCR每次遞增的數值就為0.03090K=2700,這和PTS的值原理是相同的,這裡先提一下,其實這個增量也不重要。我們需要的知識第一個PCR值就OK,但是如果考慮到後期校準,還是要用到以後的PCR值的。這裡先不管校準的問題!

說了PCR,還有個值是我們需要的,那就是PTS。其實對於硬解碼器來說DTS資訊我們根本就不需要管他,我們只需要一幀一幀的把資料送進去,順便把每一幀的PTS資訊送進去,解碼器送出來的就是排列好了PTS資訊的幀了,其他解碼器不知道至少RK3288是這樣的。大家可以試著把解複用後的每一幀的PTS打印出來,你會發現在解複用後一般是這樣排列的9000 3000 6000 18000 12000 15000…這種是AVC編碼的使用的是預測編碼決定的,先不管他,你只管這樣把沒一幀依次送入解碼器,解碼器解碼輸出後自然就排列成3000 6000 9000 12000 15000 18000這才是我們需要的PTS!至於什麼是PTS,實際上和PCR原理差不多,但是有個關鍵的地方PTS的增量值可不是預設的30ms了,他是由視訊的幀率來決定的!說道重要的地方了哈!根據上面PCR的原理,如果是30p的視訊那麼每一幀就是1/30這麼多的增量,再乘90K=3000。

說透了,我們這裡就是利用這個PTS值來進行同步順便進行幀率控制!
關鍵的地方來了!
如果視訊流現在來了,我們先獲取到第一個PCR值為1230000,我們現在馬上在解碼器端重建一個90K的時鐘!這就是關鍵所在,至於如何重建90K的時鐘,說白了就是開一個定時器,定時時間為1/90K(11.11us),每隔11.11us我們就把PCR計數值+1,同時這時候解碼器也在工作,試想一下,如果是30P的視訊,也就是33.33ms顯示一次,那麼當過了33.33ms後,PCR的數值加到好多了呢?沒錯就是33.33ms/11.11us=3000,這個增量不是和PTS的增量一摸一樣!這時候你只需要在解碼執行緒裡判斷當前幀的PTS是不是和這個PCR相等,如果相等就顯示,如果PCR大可以丟棄當前幀,也就是說的跳幀,如果PCR小說明解碼快了,這個時候就可以等待定時器執行緒到PCR==PTS。
這樣就很巧妙的解決了幀率控制的問題了!同理,音視訊同步也可以這樣!你可以讓音訊的PTS去和PCR對比!其實大多數情況下都是以視訊同步音訊,音訊解碼不用管它,直接解碼播放就OK了,你只需要進行幀率控制就OK了!同時注意隨著時間的推移有可能出現延時,那麼這個時候就需要重新來獲取PCR來更新定時器執行緒裡面的PCR基值了!

http://www.cnblogs.com/NerdWill/p/6744432.html

在對音視訊重新編碼並需要進行同步的場景中,需要遵守幾項基本原則(否則音視訊就會卡頓,不流暢。以音訊aac編碼頻率44.1k,視訊h264編碼25幀幀率為例):

  1. 保證輸入端的音視訊幀到達間隔基本精確。音訊aac每幀時長是23.2ms(1000*1024/44100),視訊每幀時長是40ms(1000/25)。所以,用於編碼的原始音訊samples的到達頻率(或從buffer中獲取的頻率)應該為441 samples/per channel/ per 10ms(每個樣本假設16bits,即882位元組/通道/10ms,如果原始音訊取樣率不是44.1k,編碼前需要重新取樣);原始視訊幀到達頻率(或從buffer中獲取的頻率)應該為1幀/per 40ms(視訊幀率可能需要重新取樣)。如果輸出的音視訊流不流暢,可先檢查輸入端音視訊流的輸入間隔情況。

2.保證輸出端的音視訊流時間戳使用同一參考系。比如音視訊流都使用當前系統時間作為同一時間參考系,但音視訊流可以有不同的系統時間起始點。比如音訊流先開始於1492764087000 ms,視訊稍後700ms開始於1492764087700ms。比如rtmp裡面使用32位時間戳,則音視訊流只能使用相對時間戳,接上例,音訊時間戳增長到700ms的時候視訊流才從0開始,表示視訊流是從音訊流的700ms處開始的,這樣才能達到同步的效果。 另外一種時間戳方案是每個音視訊幀按固定間隔增長,比如音視訊時間戳都從0開始,音訊每個aac幀增加23.2ms,每個視訊幀增長40ms。正常情況下,音視訊流是同時從0開始按相應各自間隔傳送幀的,但也有視訊流晚於音訊流或音訊流晚於視訊流的情況。這種情況需要做時間戳同步,稍晚的流起始時間要根據超前的流的時間來設定。

3.保證交叉輸出時音視訊間隔基本精確。這裡的輸出端就完全等同於一個硬體編碼器,只有保證交叉輸出的音視訊幀間隔穩定,才能保證播放端的流暢。比如rtmp,每個aac音訊幀輸出間隔應該在23ms左右,每個視訊幀輸出間隔應該在40ms左右,而且音視訊幀是交叉輸出。換句話說,每23ms要傳送一個aac音訊幀,每40ms傳送一個視訊幀(可以使用兩個單獨的執行緒來分別傳送音視訊流)。如果排除了上面的兩個問題還是不能流暢播放,可以檢查這個環節是否正常。

總之,重新編碼並同步的這個環節,必須建立在數學測量的基礎上。有錯誤或補充的地方,歡迎指出。

======================

http://www.xuebuyuan.com/1400936.html

視訊、音訊打時間戳的方法

http://blog.csdn.net/wfqxx/article/details/5497138

AVCodecContext *m_VCtx;

一 固定幀率

  1. 視訊時間戳

    pts = inc++ *(1000/fps); 其中inc是一個靜態的,初始值為0,每次打完時間戳inc加1.

    在ffmpeg,中的程式碼為

    pkt.pts= m_nVideoTimeStamp++ * (m_VCtx->time_base.num * 1000 / m_VCtx->time_base.den);

注:見AVCodecContext 的程式碼註釋:

對於固定幀率, timebase == 1/framerate

framerate = fps

  1. 音訊時間戳

    pts = inc++ * (frame_size * 1000 / sample_rate)

    在ffmpeg中的程式碼為

    pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

注: frame_size: Number of samples per channel in an audio frame. 每個音訊幀的 sample 個數.

取樣頻率是指將模擬聲音波形進行數字化時,每秒鐘抽取聲波幅度樣本的次數。

正常人聽覺的頻率範圍大約在20Hz~20kHz之間,根據奈奎斯特取樣理論,為了保證聲音不失真,取樣頻率應該在40kHz左右。常用的音訊取樣頻率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果採用更高的取樣頻率,還可以達到DVD的音質

對取樣率為44.1kHz的AAC音訊進行解碼時,一幀的解碼時間須控制在23.22毫秒內。

背景知識:

(一個AAC原始幀包含一段時間內1024個取樣及相關資料)

(一個MP3原始幀包含一段時間內1152個取樣及相關資料)

分析:

1 AAC

音訊幀的播放時間=一個AAC幀對應的取樣樣本的個數/取樣頻率(單位為s)

一幀 1024個 sample。取樣率 Samplerate 44100KHz,每秒44100個sample, 所以 根據公式 音訊幀的播放時間=一個AAC幀對應的取樣樣本的個數/取樣頻率

當前AAC一幀的播放時間是= 1024*1000000/44100= 22.32ms(單位為ms)

2 MP3

mp3 每幀均為1152個位元組, 則:

frame_duration = 1152 * 1000000 / sample_rate

例如:sample_rate = 44100HZ時, 計算出的時長為26.122ms,這就是經常聽到的mp3每幀播放時間固定為26ms的由來。

二 可變幀率

有很多的採集卡,攝像頭,在做採集的時候,明明設定的25FPS,但實際採集資料回撥過來,發現並不是40毫秒(1s=1000ms; 1000ms / 25 = 40 ms)的間隔,而是50,60,甚至100不等的時間間隔。這就給編碼後打時間戳帶來很大的困難。

在libav裡,我們的預設編碼引數都是:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps; //s32Fps是幀率

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1;

這樣在編碼後的時間戳以1遞增,只適合於固定幀率。

我們來改一下:

ptAvEncoder->ptAvStreamVideo->codec->time_base.den = s32Fps * 1000;

ptAvEncoder->ptAvStreamVideo->codec->time_base.num = 1* 1000;

這樣就把時間戳的scale變成了毫秒,就可以以毫秒為單位進行計算了,如下:

tAvPacket.pts = ((s64)u32TimeStamp * (s64)s32Fps);

u32TimeStamp是從開始記錄的時間差值,以毫秒為單位;s32Fps是幀率。

對於音訊,mp4檔案預設是取樣率為tick的,時間戳計算為:

tAvPacket.pts = (AvEncoderAudioInSizeGet(hHandle) * ( (s64)(u32TimeStamp)) / (AvEncoderAudioInSizeGet(hHandle) * 1000 / ptAvEncoder->ptAvStreamAudio->codec->sample_rate);

AvEncoderAudioInSizeGet(hHandle) 每次編碼器需要的PCM資料長度。

u32TimeStamp是從開始記錄的時間差值,以毫秒為單位。

ptAvEncoder->ptAvStreamAudio->codec->sample_rate PCM取樣率,代表一秒的資料量。

因為乘以了1000,所以也化成了毫秒單位。

===示例AVInputFormat mio=

音訊基本資訊初始化(read_header):

st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
st->codecpar->sample_rate = 48000;
st->codecpar->channels = 2;
avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE); /* 64 bits pts in us */
// to assume AAC encode
md->a_frame_duration = AV_TIME_BASE * 1024 / st->codecpar->sample_rate;   //每個音訊幀的時長

 
給音訊幀打時間戳(read_packet):

int64_t ats = 0;

av_init_packet(&pkt);
pkt.pts = (ats += a_frame_duration);    //計算方式其實跟上面    pts = inc++ * (frame_size * 1000 / sample_rate)一樣
pkt.dts = pkt.pts;
pkt.data = (uint8_t *)abuf;
pkt.size = rtval;
pkt.stream_index = audio_index;
 

====視訊基本資訊的初始化==

st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->width = vhcnt;
st->codecpar->height = vvcnt;
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
st->codecpar->format = AV_PIX_FMT_UYVY422;
st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y');
st->time_base = av_make_q(1, 25);                //25: 幀率
st->avg_frame_rate = av_inv_q(st->time_base);
md->v_frame_duration = av_q2d(st->time_base)  * AV_TIME_BASE;   //每個視訊幀的時長
avpriv_set_pts_info(st, 64, 1, AV_TIME_BASE); /* 64 bits pts in use */
 

給視訊幀打時間戳(read_packet):

int64_t vts = 0;

av_init_packet(&pkt);
pkt.pts = (vts += v_frame_duration);
pkt.dts = pkt.pts;
pkt.data = (uint8_t *)vbuf;
pkt.size = rtval;
pkt.stream_index = video_index;