日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java mp4 视频时间戳_MP4文件中音视频时间戳的计算

發(fā)布時間:2024/1/23 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java mp4 视频时间戳_MP4文件中音视频时间戳的计算 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

MP4文件的組成

MP4文件的格式遵循ISO/IEC 14496-12標準,即ISO base media file format。所有數據都封裝在被稱為Box的數據結構中,一個MP4文件,是由多個Box組成的。

MP4文件的最外層Box

如上圖所示,該MP4文件由ftype、free、mdat和moov四個Box組成。

其中moov Box屬于container box,它又可以包含有其他的Box。它里面保存的數據如下圖所示

moov box

在這里moov box及其子box包含了該MP4文件的元數據,用于指定音視頻數據的存儲位置,數據類型,時間戳之類的信息。

mdat box為長度最大的box,該文件中的音視頻數據都包含在該box中,可以通過解析moov box來獲取每幀音視頻數據具體保存的位置。

moov box包含有每幀音視頻數據在文件中的偏移量信息,所以一般都是位于文件尾,用于方便保存文件時記錄偏移量信息。但是也可以通過其他方式將其移動到文件前面的位置(MP4box和ffmpeg都可以做到),這樣做的好處是播放器在播放網絡上的MP4文件時,可以直接讀取到文件的索引信息,使得開播更快。

Box結構的定義

box

full box

size字段表示該Box的長度,如果size值為1,則表示box的長度超過了32位的表示范圍,需要由type之后的64位用于表示實際的長度。

type字段表示該Box的類型,一般使用4個可打印的字符組合表示,也稱為FOURCC,如ftyp、moov、meta、mdat等。

大部分box除了包含有size和type字段外,還包含有version和flag字段,用于處理在標準升級時產生的box內容定義不一致的問題。

除去以上數據后box剩余的數據為該box的實際數據,根據type不同,表示的含義也各不相同。

moov box

如上圖所示,moov box中會包含有一個mvhd box和一個或多個trak box,每個trak box表示一個音視頻的流。

mvhd box

定義如下:

mvhd box

mvhd box中的duration和timescale字段用來指定該文件的播放時長,duration/timescale的值即為單位為秒的時長。如果文件中多個流的時長不一致,該位置為最大時長。如下圖所示,該文件的播放時長為189167/1000=189.167秒。

mvhdbox

trak box

每個trak box表示一路單獨的流,可能是音頻也可能是視頻。

mdia box下的hdlr box用來指定該流是音頻還是視頻

stsd box的子box用于保存該流的編碼類型

avcC

上圖中avcC box指定了該流的編碼類型為H264,且存儲了解碼所需的SPS、PPS信息。

stsc stsz stco三個box用于保存沒幀視頻或音頻數據在文件中的保存位置。

stts stss ctts三個box用于保存媒體數據和時間戳的對應關系。

Sample(音視頻幀)保存位置的計算

stsz(SampleSizeBox)用于保存每個sample對應的大小

stsz

sample_count字段指明sample的個數;

如果每個sample大小都相等的話,則sample_size字段為sample的大小。否則sample_size設置為0,每個sample的大小由后續(xù)的一個數組來指定。

stsc (SampleToChunkBox)

多個sample組成一個chunk,stsc box保存了sample和chunk之間的對應關系。

stsc

每個chunk可以有一個或多個sample,如果相鄰的chunk含有相同的sample數量,則first_count字段用于指明第一個chunk的索引,sample_per_chunk指明該組chunk中每個chunk中sample的數量。

sample

如上圖

index為1的chunk含有3個sample;

index為2的chunk含有1個sample;

然后下一個first_chunk值為4,則表明index為3的chunk含有和2相同數量的sample,也是1個;

繼續(xù),index為4的chunk含有2個sample;

index為5和6的chunk含有1個sample;7有2個sample;8有1個sample;9含有2個sample;

stco(ChunkOffsetBox)

stco box指明了每個chunk在文件中的存儲位置

stco

entry_count指明了總的chunk的數量

chunk_offset指明了該chunk在文件中的偏移量

以上三個box結合起來,即可計算每個sample在文件中保存的位置和大小

void mp4Parser::GetSamplePosition(Stream* s)

{

int sample_count = s->stsz_count;

int chunk_count = s->stco_count;

if(sample_count > 0)

{

s->sample_position = new uint64_t[sample_count];

}

int remain_chunk_count = chunk_count;

int sample_index = 0;

for(int i=0;istsc_count;i++)

{

int c_count = 0;

if (i != s->stsc_count - 1)

{

c_count = s->stsc_data[i + 1].first_chunk - s->stsc_data[i].first_chunk;

remain_chunk_count -= c_count;

}

else

{

c_count = remain_chunk_count;

}

for (int j = 0; j < c_count; j++)

{

int chunk_index = s->stsc_data[i].first_chunk + j;

uint64_t offset = s->stco_data[chunk_index - 1];

for (int k = 0; k < s->stsc_data[i].samples_per_chunk; k++)

{

s->sample_position[sample_index] = offset;

offset += s->stsz_data[sample_index];

sample_index++;

if (sample_index > sample_count)

return;

}

}

}

}

PTS和DTS的計算

I P B 幀的概念

在視頻壓縮中,為了提高壓縮率,會將每幀畫面壓縮為不同類型的視頻幀數據。

I幀表示關鍵幀,包含有一幀畫面的完整信息,解碼時只需要本幀數據就可以解碼出完整的一幀畫面。

P幀表示前向參考幀,它保存了本幀與上一幀的差異信息,它不能單獨解碼,需要根據上一幀的畫面加上本幀保存的差值來獲取本幀的完整畫面。

B幀為雙向參考幀,它解碼時需要依賴它之前和之后的幀來獲取最終的畫面

因為B幀需要依賴它后面的幀來進行解碼,所以它的解碼順序就必然和顯示順序不能保持一致,這是就需要解碼時間戳(DTS)和顯示時間戳(PTS)來共同決定一幀視頻數據何時解碼,然后何時顯示了。

stts(TimeToSampleBox)

stts

根據stts box可以計算出每個sample的dts,其中sample_delta為該sample的dts相對于上一個smaple的差值,比如entry_count=1,sample_count=5,sample_delta=1024時,5個sample的dts將依次為0 1024 2048 3072 4096。

ctts(CompositionOffsetBox)

ctts

cttsbox保存了每個sample的composition time和decode time之間的差值,這里CompositionTime就直接理解成PTS吧。

如果不存在ctts box,則代表該流不存在B幀,那么PTS就直接等于DTS,例如音頻數據就不存在ctts box。

根據stts和ctts兩個box可以計算出sample的DTS和PTS

stss(SyncSampleBox)

stss

stss box保存了哪些幀是關鍵幀(即I幀),做seek跳轉時,視頻需要從關鍵幀開始解碼,否則解碼會出現異常。

示例

這里我們選擇一個只有5幀畫面的MP4文件進行分析

stsz內容:

sample_count = 5

index = 1, size = 919

index = 2, size = 39

index = 3, size = 36

index = 4, size = 36

index = 5, size = 36

stsc內容:

entry_count = 2

first_chunk = 1, samples_per_chunk = 3, sample_description_index = 1

first_chunk = 2, samples_per_chunk = 1, sample_description_index = 1

stco內容

entry_count = 3

index = 1, chunk_offset = 48

index = 2, chunk_offset = 1051

index = 3, chunk_offset = 1096

index為1、2、3的三幀組成為chunk1

chunk1的起始地址為48,則sample1的起始地址為48,sample2的起始地址為48+919=967(919為sample1的大小),sample3的起始地址為967+39=1006(39為sample2的大小)。

chunk2和chunk3只包含有1個sample,分別為sample4和sample5

chunk2的起始地址為1051,則sample4的起始地址為1051

chunk3的起始地址為1096,則sample5的起始地址為1096

stts內容:

stts_count = 1

count:5, delte:512

ctts內容:

ctts_count = 5

count:1, offset:1024

count:1, offset:2560

count:1, offset:1024

count:1, offset:0

count:1, offset:512

根據stts可知,5個sample的DTS分別為 0、512、1024、1536、2048

與ctts內容相加,可得PTS分別為1024、3072、2048、1536、2560

即實際顯示的順序應該是按照PTS從小到大的順序(1、4、3、5、2)

DTS和PTS值轉換為時間

以上計算出來的DTS和PTS為一個整形的數值,但是他們如何轉換為以秒為單位的實際時間呢?

參看上面第二幅圖,moov/trak/mdia/mdhd這個順序下的mdhd box

mdhd

此box中有和mvhd中同樣的timesacle和duration字段,兩處并不一定一致,mdhd box中的timescale和duration表示當前流的時長,duration/timescale的值即為當前流的時長。

同樣,PTS和DTS除以timescale即為相應的以秒為單位的時間

上面那個例子中,視頻流的timescale=15360,則相應的DTS和PTS應該為(0、0.033、0.067、0.1、0.133)(0.067、0.2、0.133、0.1、0.167)。

elst(EditListBox)

moov/trak/edts/elst box同樣對PTS會產生影響,它可以是實際時間戳產生偏移

elst

segment_duration:表示該edit段的時長,以Movie Header Box(mvhd)中的timescale為單位。

media_time:表示該edit段的起始時間,以track中Media Header Box(mdhd)中的timescale為單位。如果值為-1,表示是空edit,一個track中最后一個edit不能為空。

media_rate:edit段的速率為0的話,edit段相當于一個”dwell”,即畫面停止。畫面會在media_time點上停止segment_duration時間。否則這個值始終為1。

為使PTS從0開始,media_time字段一般設置為第一個CTTS的值,計算PTS和DTS的時候,他們分別都減去media_time字段的值就可以將PTS調整為從0開始的值

如果media_time是從一個比較大的值,則表示要求PTS值大于該值時畫面才進行顯示,這時應該將第一個大于或等于該值的PTS設置為0,其他的PTS和DTS也相應做調整

如果elst box中有多個設置,表示會有多段的顯示,具體用法這里不再說明,可以查詢elst box用法。

總結

以上是生活随笔為你收集整理的java mp4 视频时间戳_MP4文件中音视频时间戳的计算的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。