Seek 策略以及在有 B 帧情况下的处理
在知識星球分享的文章,順便也在公眾號發(fā)表一下,不足之處,歡迎指正。
一個關(guān)于音視頻領(lǐng)域?qū)I(yè)問答的小圈子!!
最近在做 Seek 相關(guān)功能時遇到的問題排查,順便也學(xué)到了一些新的東西,和大家分享下。
在視頻播放時執(zhí)行 Seek 到任意點的操作,一般都是 Seek 到任意點往前最近的 I 幀,然后再逐幀解碼到指定時間點。
這里可以優(yōu)化,假設(shè)當(dāng)前時間和指定時間在一個 GOP 內(nèi),就可以不用 seek ,直接順序向下解碼就好。
而正是這個優(yōu)化出現(xiàn)了一點問題,現(xiàn)象如下:
已經(jīng)判斷播放點 A 和 Seek 點 B 不在一個 GOP 內(nèi),然后執(zhí)行 av_seek_frame 方法還是把時間點 A 所在 GOP 全部解碼了,導(dǎo)致播放上出現(xiàn)了卡頓。
這里就很奇怪了,明明判斷不在一個 GOP ,那 Seek 時就應(yīng)該從時間點 B 所在 GOP 的 I 幀開始解碼, 但執(zhí)行時還是解碼了上一個 GOP 的內(nèi)容。
到底是判斷是否同一個 GOP 的函數(shù)出問題了還是 Seek 方法有問題呢?
帶著疑問開始深入源碼探索。
FFmpeg 沒有直接提供判斷兩幀是否同一個 GOP 的方法,所以通過 av_index_search_timestamp 方法得到傳入時間點最近的 I 幀的 index 索引,如果兩個時間點的索引相同則表示為同一個 GOP 內(nèi),因為最近的 I 幀相同。
然而 av_index_search_timestamp 方法是通過 AVIndexEntry 中的 timestamp 來判斷的,它是一個 DTS 值,通過二分查找得到最近的索引。
在沒有 B 幀的情況下,I 幀的 PTS 等于 DTS ,所以判斷不會出問題。然而正是有了 B 幀,如果 I 幀的 PTS 和 DTS 不相等的話,那么上面的判斷相當(dāng)于是拿一個 PTS 值和 I 幀的 DTS 比較是否同一個 GOP 了。
如果將錯就錯,判斷 GOP 時得到結(jié)論是非同一個 GOP ,那么 Seek 也應(yīng)該是非同一個 GOP ,但現(xiàn)實恰恰相反,Seek 當(dāng)做了同一個 GOP ,這里面肯定有計算出問題了,繼續(xù)深入源碼。
通過在 mov.c 源碼中看到了如下的操作:
static?int?mov_seek_stream(AVFormatContext?*s,?AVStream?*st,?int64_t?timestamp,?int?flags) {MOVStreamContext?*sc?=?st->priv_data;FFStream?*const?sti?=?ffstream(st);int?sample,?time_sample,?ret;unsigned?int?i;//?Here?we?consider?timestamp?to?be?PTS,?hence?try?to?offset?it?so?that?we//?can?search?over?the?DTS?timeline.timestamp?-=?(sc->min_corrected_pts?+?sc->dts_shift);ret?=?mov_seek_fragment(s,?st,?timestamp);if?(ret?<?0)return?ret;sample?=?av_index_search_timestamp(st,?timestamp,?flags);av_log(s,?AV_LOG_TRACE,?"stream?%d,?timestamp?%"PRId64",?sample?%d\n",?st->index,?timestamp,?sample);//?省略部分代碼注意到如下一行代碼:
timestamp?-=?(sc->min_corrected_pts?+?sc->dts_shift);也就是說我們傳入的時間都會被減上一個值,然后再執(zhí)行 av_index_search_timestamp 方法,而這個值導(dǎo)致判斷 GOP 和 Seek 之間的關(guān)鍵幀索引出問題了。
正如代碼中的注釋所示,假設(shè)傳入的時間是 PTS 值,然后給它減去偏移以得到 DTS 值,因為 av_index_search_timestamp 方法就通過 DTS 進行比較的嘛。
出現(xiàn)問題的原因就是 seek 的時間點正好在 I 幀的 PTS 和 DTS 范圍之間了,執(zhí)行 seek 時減去偏差值就小于 DTS 了,所以變成了同一個 GOP 。
現(xiàn)在要解決問題就是如何得到 sc->min_corrected_pts + sc->dts_shif 之和,然后判斷 GOP 時減去它以修正得到 DTS 值。
還好通過遍歷源碼發(fā)現(xiàn)它的值是不會運行時改變的,一旦決定了就定下來了。另外我們可以用第一個 I 幀的 DTS 值作為偏移值。
auto?indexEntry?=?avStream->index_entries;auto?nbIndexEntry?=?avStream->nb_index_entries;for?(int?i?=?0;?i?<?nbIndexEntry;?++i)?{if?(indexEntry[i].flags?==?AVINDEX_KEYFRAME)?{DTSOffset?=?indexEntry[i].timestamp;return;}}如果沒有 B 幀,DTS 值為 0 ,有 B 幀,那么首幀的 DTS 值就可以用來做偏差值進行計算了。
一個音視頻領(lǐng)域?qū)I(yè)問答的小圈子!
推薦閱讀:
音視頻開發(fā)工作經(jīng)驗分享 || 視頻版
OpenGL ES 學(xué)習(xí)資源分享
開通專輯 | 細(xì)數(shù)那些年寫過的技術(shù)文章專輯
Android NDK 免費視頻在線學(xué)習(xí)!!!
你想要的音視頻開發(fā)資料庫來了
推薦幾個堪稱教科書級別的 Android 音視頻入門項目
覺得不錯,點個在看唄~
總結(jié)
以上是生活随笔為你收集整理的Seek 策略以及在有 B 帧情况下的处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 年入200万的华人程序员,过了30岁就成
- 下一篇: Mac macOs Big Sur 版本