H264數(shù)據(jù)中獲取PTS(計(jì)算出PTS)

原文地址: http://70565912.blog.51cto.com/1358202/533736/
只大概說(shuō)明要點(diǎn)肛走。更具體的方法恕不祥敘。
我的開(kāi)源工程和很多開(kāi)源項(xiàng)目都有詳細(xì)完整的實(shí)現(xiàn)代碼潮瓶。
這些要點(diǎn)都是我自己學(xué)習(xí)的總結(jié)痕檬,無(wú)責(zé)任保證正確性碴开。僅做參考哼鬓。
如發(fā)現(xiàn)有問(wèn)題請(qǐng)丟磚頭监右,跪求各方高人指正錯(cuò)誤。Orz
內(nèi)容:
H264的ES原始數(shù)據(jù)一般是以NAL(Network Abstract Layer)的格式存在异希〗『校可以直接用于文件存儲(chǔ)和網(wǎng)絡(luò)傳輸。每一個(gè)NALU(Network Abstract Layer Unit)數(shù)據(jù)称簿,是由數(shù)據(jù)頭+RBSP數(shù)據(jù)組成扣癣。
首先需要將數(shù)據(jù)流,分割成一個(gè)一個(gè)獨(dú)立的NALU數(shù)據(jù)憨降。
接著獲取NALU的nal_type父虑,i_nal_type的值等于0x7表示這個(gè)nalu是個(gè)sps數(shù)據(jù)包。找到并解析這個(gè)sps數(shù)據(jù)包授药,里面包含有非常重要的幀率信息
time_scale/num_units_in_tick=fps
然后根據(jù)nal_type判斷slice(H264中的slice類似一個(gè)視頻幀F(xiàn)RAME的概念)士嚎。其中nal_type值小于0x1,或大于0x5悔叽,表示這個(gè)NALU屬于一個(gè)slice莱衩。
// 檢查是否是slice
if ( i_nal_type < 1/NAL_SLICE/ || i_nal_type > 5/NAL_SLICE_IDR/ )
// 找到slice!!!!!
在找到slice的NALU后,可以逐字節(jié)將NALU的數(shù)據(jù)與0x80進(jìn)行與運(yùn)算娇澎,結(jié)果為真表示這個(gè)slice(視頻幀F(xiàn)RAME)的結(jié)束位置膳殷。
// 判斷是否幀結(jié)束
for (uint32_t i = 3; i < nal_length; i++)
{
if (p_nal[i] & 0x80)
{
// 找到frame_begin!!!!上一幀frame的結(jié)束,下一幀frame的開(kāi)始
}
}
上面的這個(gè)代碼是摘抄自FFMPEG。他實(shí)際作用是判斷slice里面的first_mb_in_slice九火,即第1個(gè)宏塊在slice中的位置,如果是一幀開(kāi)始册招,這個(gè)字段的值肯定是標(biāo)識(shí)第1個(gè)宏塊岔激。因此,也可以完整解析slice的頭部信息是掰,解析出first_mb_in_slice虑鼎,如果是0(注意:這是1個(gè)哥倫布數(shù)值),即這個(gè)NALU是一幀的開(kāi)始键痛。

為什么這里的代碼是逐字節(jié)判斷0x80炫彩?我額外寫點(diǎn)某大神的名言:程序猿不是十萬(wàn)個(gè)為什么,不是維基猿絮短,程序猿是需求猿江兢。如果某程序猿已經(jīng)著手開(kāi)始研究如何解析slice頭部格式,他很自然的不會(huì)有這個(gè)疑問(wèn)丁频。

另外通過(guò)nal_type以及silice_type也可以判斷出幀結(jié)束位置杉允,VLC里面的代碼就是這么干邑贴。

解析到位于幀結(jié)束位置的NALU,就可以判斷出每一幀(slice)的開(kāi)始和結(jié)尾叔磷。解析slice的slice_type拢驾,根據(jù)slice_type,可以判斷出這個(gè)slice的IPB類型改基。
// 根據(jù)slice類型判斷幀類型
switch(slice.i_slice_type)
{
case 2: case 7:
case 4: case 9:
p_flags = 0x0002/BLOCK_FLAG_TYPE_I/;
break;
case 0: case 5:
case 3: case 8:
p_flags = 0x0004/BLOCK_FLAG_TYPE_P
/;
break;
case 1:
case 6:
p_flags = 0x0008/BLOCK_FLAG_TYPE_B/;
break;
default:
p_flags = 0;
break;
}
從現(xiàn)在開(kāi)始繁疤,就有兩種辦法來(lái)計(jì)算PTS了。
方法一秕狰、根據(jù)前后幀的IPB類型稠腊,可以得知幀的實(shí)際顯示順序,使用前面獲取的sps信息中的幀率封恰,以及幀計(jì)數(shù)frame_count即可計(jì)算出PTS麻养。此方法需要做幾幀緩存(一般緩存一個(gè)group的長(zhǎng)度)。
I P B B I P B B I P B ... 幀類型
1 2 3 4 5 6 7 8 9 10 11 ... 第幾幀
1 4 2 3 5 8 6 7 9 12 10 ... 幀顯示順序
一個(gè)I幀與下一個(gè)I幀之間诺舔,是一個(gè)group鳖昌。
從上圖可見(jiàn),P類型的幀的顯示順序低飒,是排在后面最后一個(gè)B幀之后许昨。
所以要獲取第7幀的pts,起碼要知道他下一幀的類型褥赊,才能得知他的顯示順序糕档。
第8幀的pts=1000(毫秒)
7(幀顯示順序)
幀率
方法二、每一個(gè)slice的信息里面拌喉,都記錄有pic_order_cnt_lsb速那,當(dāng)前幀在這個(gè)group中的顯示順序。通過(guò)這個(gè)pic_order_cnt_lsb尿背,可以直接計(jì)算出當(dāng)前幀的PTS端仰。此方法不需要做幀緩存。
計(jì)算公式:
pts=1000(i_frame_counter + pic_order_cnt_lsb)(time_scale/num_units_in_tick)
i_frame_counter是最近一次I幀位置的幀序田藐,通過(guò)I幀計(jì)數(shù)+當(dāng)前group中的幀序荔烧,得到幀實(shí)際顯示序列位置,乘上幀率汽久,再乘上1000(毫秒)的base_clock(基本時(shí)鐘頻率)鹤竭,得到PTS。
I P B B I P B B I P B ... 幀類型
1 2 3 4 5 6 7 8 9 10 11 ... 第幾幀
1 4 2 3 5 8 6 7 9 12 10 ... 幀顯示順序
0 6 2 4 0 6 2 4 0 6 2 ... pic_order_cnt_lsb

細(xì)心一點(diǎn)可以注意到景醇,在上圖臀稚,slice里面的pic_order_cnt_lsb是以2進(jìn)行遞增。
通常H264里面的sps中記錄的幀率啡直,也是實(shí)際幀率的2倍time_scale/num_units_in_tick=fps*2

因此烁涌,實(shí)際的計(jì)算公式應(yīng)該是這樣
pts=1000(i_frame_counter2+pic_order_cnt_lsb)* (time_scale/num_units_in_tick)
或者是
pts=1000(i_frame_counter+pic_order_cnt_lsb/2) (time_scale/num_units_in_tick/2)
所以苍碟,第11幀的pts應(yīng)該是這么計(jì)算
1000(92+2)*(time_scale/num_units_in_tick)
結(jié)束語(yǔ):
這里pts的base_clock都是按照1000(毫秒)計(jì)算,如果復(fù)用到ts里撮执,base_clock是90k微峰,所以還應(yīng)該再乘以90。

題外話:關(guān)于H264中sps里面記錄的幀率是實(shí)際幀率的2倍抒钱,包括slice里面的pic_order_cnt_lsb也是2倍遞增蜓肆,我推測(cè)可能是編碼按照分場(chǎng)(頂場(chǎng)、底場(chǎng))編碼所致谋币。另外我注意到sps信息中的offset_for_top_to_bottom_field字段仗扬,從命名上,貌似是可以用來(lái)標(biāo)記是否逐場(chǎng)蕾额,還是分奇偶場(chǎng)編碼早芭。以上都屬猜測(cè),有請(qǐng)高人解惑诅蝶。 Orz

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末退个,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子调炬,更是在濱河造成了極大的恐慌语盈,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缰泡,死亡現(xiàn)場(chǎng)離奇詭異刀荒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)棘钞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門缠借,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宜猜,你說(shuō)我怎么就攤上這事烈炭。” “怎么了宝恶?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)趴捅。 經(jīng)常有香客問(wèn)我垫毙,道長(zhǎng),這世上最難降的妖魔是什么拱绑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任综芥,我火速辦了婚禮,結(jié)果婚禮上猎拨,老公的妹妹穿的比我還像新娘膀藐。我一直安慰自己屠阻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布额各。 她就那樣靜靜地躺著国觉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虾啦。 梳的紋絲不亂的頭發(fā)上麻诀,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音傲醉,去河邊找鬼蝇闭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛硬毕,可吹牛的內(nèi)容都是我干的呻引。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼吐咳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逻悠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起挪丢,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蹂风,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后乾蓬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體惠啄,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年任内,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撵渡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡死嗦,死狀恐怖趋距,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情越除,我是刑警寧澤节腐,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站摘盆,受9級(jí)特大地震影響翼雀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孩擂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一狼渊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类垦,春花似錦狈邑、人聲如沸城须。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)糕伐。三九已至,卻和暖如春驱入,著一層夾襖步出監(jiān)牢的瞬間赤炒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工亏较, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莺褒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓雪情,卻偏偏與公主長(zhǎng)得像遵岩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巡通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容