avformat_seek_file使用
前言
? ? 最近一個播放器項目基于ijkplayer做二次開發(fā),是http-mp4點播,需要做精準seek,用到了avformat_seek_file接口,這里簡要介紹下ffmpeg下的這個接口使用、注意事項,以及http-mp4網絡流精準seek的方案。
接口介紹
? ? int avformat_seek_file(AVFormatContext *s,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int stream_index,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int64_t min_ts,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int64_t ts,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int64_t max_ts,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?int flags);
解釋:
1)AVFormatContext *s,AVFormatContext實例。
2)int stream_index,流索引,但是只有在 flags 包含 AVSEEK_FLAG_FRAME 的時候才是 設置某個流的讀取位置。其他情況都只是把這個流的 time_base (時間基)作為參考。
3)int64_t min_ts,跳轉到的最小的時間,或時間單位,或字節(jié)單位,或幀數(shù)序號(第幾幀)。
4)int64_t ts,目標seek位置,單位同上,通常填INT64_MIN即可。
5)int64_t max_ts,跳轉到的最大的時間,單位同上,通常填 INT64_MAX 即可。
6)int flags,seek方式,如下:
? ? ? AVSEEK_FLAG_BYTE,按字節(jié)大小進行seek。
? ? ? AVSEEK_FLAG_FRAME,按幀數(shù)大小進行seek。
? ? ? AVSEEK_FLAG_ANY,會seek到非IDR,解碼會出現(xiàn)馬賽克、花屏現(xiàn)象。
? ? ? AVSEEK_FLAG_BACKWARD,往 ts 后面找最近的IDR。
本地mp4文件
? ? ? 經測試,avformat_seek_file接口,可通過將flag設置為AVSEEK_FLAG_BACKWARD,可seek到離ts最近的IDR幀(方向是ts的后面),參考示例:
? ? ? avformat_seek_file(s, -1, INT64_MIN, target_ts, INT64_MAX, AVSEEK_FLAG_BACKWARD)
? ? ? 本地mp4文件,如果要做精準seek,則按如上方案實施即可,可找到target_ts后面最近的IDR幀,然后逐幀解碼后,當視頻的pts == target_ts,或者誤差在指定范圍內認為是精準seek即可。
http-mp4
? ? ? 然而,網絡流http-mp4卻不能通過avformat_seek_file接口來找到離target_ts后面最近的IDR幀,可通過服務器來實現(xiàn),讓服務器返回最近的IDR幀。
? ? ? 此處,介紹一個本人自己經實踐可行的方案。
// 此處指http-mp4流 static bool is_http_stream(const char* filename) {assert(filename);char* pos = strstr(filename, "http://");if (pos != NULL) {return true;}pos = strstr(filename, "https://");return pos != NULL; }? ? ? 而后,在ijkplayer的ff_ffplay.c文件read_thread方法的seek_req判斷里,增加如下代碼:
if (is->seek_req) {int64_t seek_target = is->seek_pos;if (is_http_stream(is->filename)) {int gop_size = is->viddec.avctx->gop_size;if (gop_size > 0) {int64_t backward = gop_size * 1000 * 1000;if (seek_target > backward) {seek_target -= backward;}}}int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX; // FIXME the +-2 is due to rounding being not done in the correct direction in generation // of the seek_pos/seek_rel variablesffp_toggle_buffering(ffp, 1);ffp_notify_msg3(ffp, FFP_MSG_BUFFERING_UPDATE, 0, 0);ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);? ? ? 關鍵的代碼段邏輯:
1)即首先判斷是否是http-mp4網絡流(本地文件不走此邏輯)
2)通過VideoState結構體的Decoder類型字段viddec,找到AVCodecContext類型變量avctx
3)再通過avctx拿到本片源的gop_size
4)再將seek_target -= gop_size * 1000 * 1000,再按seek_target此值往回找IDR,可實現(xiàn)http-mp4的精準定位。
if (is_http_stream(is->filename)) {int gop_size = is->viddec.avctx->gop_size;if (gop_size > 0) {int64_t backward = gop_size * 1000 * 1000;if (seek_target > backward) {seek_target -= backward;}}}注意:
1.如果gop較小,比如1s一個關鍵幀,則精準定位按ijkplayer的代碼基本就能滿足需求;
2.但本項目中,遇到了10s甚至更長的gop大小,這對精準seek提出了更高的要求,增加了精準seek的難度?,avformat_seek_file接口無法seek到最近的IDR幀,如果您也是遇到gop較大情形,而又沒有后端服務支持,可參考本精準seek方案。
總結
以上是生活随笔為你收集整理的avformat_seek_file使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何用U盘装系统(图解)
- 下一篇: 图解物联网场景,百度云天工带你玩转物可视