最近碰到一個問題,流媒體服務器寫出來的TS文件用ffmpeg、potplayer播放時長是正確的,可是使用mediainfo和windows自帶的系統播放器播放,就會出現播放時長偏小的問題。而mediainfo中音視頻播放時長倒是正確值。以下圖所示:windows
如上圖所示,該MPEG-TS文件的播放總時長爲9.268秒,視頻播放時長爲10.865秒,音頻播放時長爲10.755秒。而該視頻在ffmpeg中顯示播放時長爲10.865秒。服務器
針對以上問題,筆者下載了mediainfo的源碼編譯調試發現,mediainfo解析MPEG-TS的播放時長來自於TS包中的PCR(即program_clock_reference/節目時鐘參考)。而音視頻播放時長來自於PES中時間戳。說到這裏,我估計有些不熟悉MPEG-TS格式的小夥伴已經開始暈了。ui
MPEG-TS文件結構分三個層面:編碼
使用固定包大小對數據進行打包,TS包大小固定爲188字節(M2TS/藍光高清爲192字節)。它包括TS頭數據、有效載荷數據和填充數據三部分。spa
TS包頭爲4字節固定大小包頭標識,定義以下
typedef struct TS_packet_header
{調試
unsigned sync_byte : 8; //同步字節, 固定爲0x47,表示後面的是一個TS分組 unsigned transport_error_indicator : 1; //傳輸誤碼指示符 unsigned payload_unit_start_indicator : 1; //有效荷載單元起始指示符 unsigned transport_priority : 1; //傳輸優先, 1表示高優先級,傳輸機制可能用到,解碼用不着 unsigned PID : 13; //PID unsigned transport_scrambling_control : 2; //傳輸加擾控制 unsigned adaption_field_control : 2; //自適應控制 01僅含有效負載,10僅含調整字段,11含有調整字段和有效負載。爲00解碼器不進行處理 unsigned continuity_counter : 4; //連續計數器 一個4bit的計數器,範圍0-15
} TS_packet_header; //總共32位,4個字節
當adaption_field_control=10或11時:
包頭後面緊跟者調整字段,PCR則是在這個字段中,以下
...]code
有效載荷數據通常爲音視頻數據包的PES數據包,或是節目映射表PMT和節目訪問表PAT等TS節目信息。視頻
當TS包頭和有效載荷數據不總188字節時,將會使用0xff填充,直到TS包知足188字節爲止。blog
通過打包的基本碼流,通常以數據幀爲單位對音視頻數據進行打包,同時會攜帶有該數據幀的時間戳和一些其餘標誌位信息。ip
視頻基本碼流數據。
PCR(Program Clock Reference/節目時鐘參考)用於恢復出與編碼端一致的系統時序時鐘(STC/System Time Clock)。MPEG2系統層標準規定,PTS的時間間隔不能超過0.7S,出如今TS包頭的PCR間隔不能超過0.1S,PCR最大值位26:30:43。在TS傳輸過程當中,通常DTS和PCR差值會在一個合適的範圍,這個差值就是要設置的音視頻buffer的大小。通常狀況下視頻DTS和PCR的差值在700ms-1200ms之間,音頻差值在200ms-700ms之間。
如今終於明白了,mediainfo和系統播放器獲取時間是根據PCR/節目時鐘參考來計算的,而ffmpeg和potplayer之類的播放器時使用PTS來計算時間戳的。而PCR的時間間隔最大不能超過0.1s,即100ms,而這個TS文件的PCR時間間隔明顯就大於1秒,纔會致使這個問題的出現。想要解決這個問題,在每間隔100ms插入一個PCR就OK了。