來在研究HLS(HTTP Live Streaming),以實現android上播放m3u8文件。由於TS段的切分不統一,每個視頻網站給出的m3u8 playlists總有差別,在時間戳顯示上有差異,所以對DTS和PTS進行了研究。
DTS和PTS是音視頻同步的關鍵技術,同時也是丟幀策略密切相關。
dts/pts定義
dts: decoding time stamp pts: present time stamp
在ISO/IEC13818-1中制定90k
Hz 的時鐘,如果編碼幀頻是30,那麼時間戳間隔就該是90000 / 30 = 3000。 在FFMPEG中有三種時間單位:秒、微秒和dts/pts。從dts/pts轉化爲微秒公式:
|
其中AV_TIME_BASE爲1,000,000,denominator爲90,000。 拿到m3u8播放列表後,首先進行解析。HTTP Live Streaming標準草案可以從這裏http://tools.ietf.org/html/draft-pantos-http-live-streaming-08查看。 解析代碼在ffmpeg/libavformat/hls.c中 |
|
parse_playlist函數 |
|
解析播放列表的問題 |
|
|
|
當解析到#EXT-X-TARGETDURATION標籤時,後面緊跟着的是TS段的最大時長,當前沒有什麼用。#EXTINF標籤後緊跟的是當前TS段的時長,當EXT-X-VERSION標籤大於等於3時,TS段的時長可以爲小數,當前(2012-07-26)的FFMPEG代碼還不支持EXT-X-VERSION標籤的判斷,TS段的時長也爲整數。保存了當前段的時長,單位爲秒。 |
|
|
|
當前草案中還有EXT-X-DISCONTINUITY標籤,它表徵其後面的視頻段文件和之前的不連續,這意味着文件格式、時間戳順序、編碼參數等的變化。但是很遺憾,當前FFMPEG仍然不支持,這意味着該標籤出現後,後續的PES中攜帶的dts和pts將重新從零開始計數。
|
|
seek_timestamp爲標誌位,它表徵當前視頻發生了SEEK事件,當發生SEEK事件後首先調用hls_read_seek()函數定位到應該讀取的TS段,更新HLS上下文中的段序號。當讀取到該段的packet,有兩種判斷。 在ffplay中,當外界發起seek請求後,將執行以下操作。 |
|
|
|
|
|
調用avformat_seek_file(),完成文件的seek定位 |
|
|
|
清空解碼前packet隊列(音頻、視頻、字幕) |
|
|
|
調用avcodec_flush_buffers(),清空解碼buffer和相關狀態 |
|
|
|
在第一個步驟中,將在HLS層進行seek操作,seek流程圖如下圖所示: |
|
http://image55.360doc.com/DownloadImg/2012/09/1315/26808238_1.png http://image55.360doc.com/DownloadImg/2012/09/1315/26808238_1.png |
|
首先讀取packet,判斷是否有seek操作,沒有則直接將該packet返回,送人後續的解碼操作。如果是seek情況,則讀取dts時間戳,如果dts沒有值,則直接清除seek標誌並返回packet(問題一)。如果dts時間戳有值,則將該值轉化爲微秒並與seek傳入的時間進行比較,看是否大於seek時間,如果大於則表明讀取的packet達到了seek要求(問題二),否則繼續讀packet。如果seek時間已經滿足,則看該packet的flags是否是關鍵幀,如果是則返回該packet(問題三),否則繼續讀packet。 |
|
該流程很簡單,但是帶來了三個問題。分別解釋 |
|
|
|
問題一,如果dts沒有值,返回回去後,解碼狀態全部進行了reset,則送入的第一幀信息應該爲關鍵幀,否則該幀需要參考其他幀,產生花屏。 |
|
|
|
問題二,如果dts時間戳有誤,將出現dts轉化爲微秒後永遠小於seek傳入時間問題,則永遠無法返回packet,導致seek僵死。 |
|
|
|
問題三,判斷packet是否爲關鍵幀,忽略了該packet是否爲視頻,如果該packet爲音頻並且flag AV_PKT_FLAG_KEY的結果爲真,則將返回該packet並清空seek標準。後續讀到的視頻也有非關鍵幀的可能,從而導致花屏。 |
|