這個(gè)公眾號(hào)會(huì)路線(xiàn)圖 式的遍歷分享音視頻技術(shù):音視頻基礎(chǔ) → 音視頻工具 → 音視頻工程示例 → 音視頻工業(yè)實(shí)戰(zhàn)篇亭。關(guān)注一下成本不高,錯(cuò)過(guò)干貨損失不小 ↓↓↓
在視頻編輯場(chǎng)景中炸站,涉及到的模塊很多城瞎,比如:抽幀模塊若皱、預(yù)覽播放模塊贸人、視頻編輯模塊洽洁、特效合成模塊跌穗、視頻轉(zhuǎn)碼模塊等等订晌。這些模塊各自都有對(duì)應(yīng)的性能指標(biāo),這些指標(biāo)影響著編輯場(chǎng)景的用戶(hù)體驗(yàn)蚌吸。這里我們先介紹一下抽幀模塊和預(yù)覽播放模塊相關(guān)的優(yōu)化锈拨。
在抽幀模塊和預(yù)覽播放模塊我們關(guān)注的指標(biāo)主要有:
- 視頻抽幀相關(guān):
- 視頻抽幀成功率,對(duì)視頻進(jìn)行抽幀時(shí)的成功率羹唠。
- 視頻抽幀平均時(shí)長(zhǎng)奕枢,對(duì)視頻進(jìn)行抽幀時(shí)的獲取每幀截圖的平均耗時(shí)。
- 視頻播放相關(guān):
- 視頻 Seek 平均時(shí)長(zhǎng)佩微,從拖動(dòng)視頻進(jìn)度到對(duì)應(yīng)時(shí)間點(diǎn)到圖像顯示出來(lái)的平均耗時(shí)缝彬。
1、視頻抽幀優(yōu)化
抽幀模塊主要用于提取和展示視頻畫(huà)面縮略圖的場(chǎng)景哺眯。
視頻縮略圖展示
1.1谷浅、抽幀接口異步逐幀回調(diào)
通常展示視頻畫(huà)面縮略圖是需要一定數(shù)量的縮略圖,這時(shí)候可能有兩種做法:一種是等成功獲取到所有縮略圖后,再一起展示出來(lái)一疯;另一種是每獲取到一幀縮略圖就先展示出來(lái)撼玄。
從體驗(yàn)上來(lái)講,通常處理完一幀后立即展示出來(lái)的體驗(yàn)優(yōu)于等所有幀都處理完成后才展示的體驗(yàn)墩邀,前者的效果能更快的給用戶(hù)反饋掌猛,告訴用戶(hù)事情正在發(fā)生,而不是要等很久眉睹。所以荔茬,在設(shè)計(jì)抽幀模塊的接口時(shí),就需要將其設(shè)計(jì)為異步調(diào)用且逐幀回調(diào)的方式辣往。
1.2兔院、精準(zhǔn)抽幀與非精準(zhǔn)抽幀
由于編碼采用的參數(shù)不同,不同視頻的關(guān)鍵幀數(shù)量和關(guān)鍵幀間隔差別很大站削,目前很多短視頻產(chǎn)品為了提高壓縮率坊萝,轉(zhuǎn)碼時(shí)設(shè)置的關(guān)鍵幀數(shù)量都比較少。抽幀模塊在抽取視頻幀時(shí)许起,如果僅解碼關(guān)鍵幀十偶,處理是最快的,但是當(dāng)關(guān)鍵幀數(shù)量少于需要的抽幀數(shù)量時(shí)又不能滿(mǎn)足顯示視頻縮略圖的需求园细,這時(shí)候就需要解碼其他非關(guān)鍵幀惦积。所以,應(yīng)對(duì)不同的視頻猛频,抽幀的具體處理方式也不同狮崩。
- 精準(zhǔn)抽幀:要按照給定的時(shí)間點(diǎn)列表,抽取并返回對(duì)應(yīng)時(shí)間點(diǎn)的圖像鹿寻。
- 采用跳躍的方式進(jìn)行解碼睦柴。 首先計(jì)算待解碼各幀的時(shí)間戳位于哪個(gè) GOP,從對(duì)應(yīng)的 GOP 的 IDR 幀開(kāi)始解碼毡熏,直到解碼到準(zhǔn)確的位置坦敌。如果待解碼的幀中有兩幀或多幀在一個(gè) GOP 內(nèi),則這兩幀或多幀在一次 GOP 順序解碼中完成痢法,不要重復(fù)多次從頭開(kāi)始解碼該 GOP狱窘。這樣可以提升抽幀的速度。
- 非精準(zhǔn)抽幀:抽取并返回給定數(shù)量的圖像财搁,但是可以不設(shè)置各幀的時(shí)間點(diǎn)蘸炸,或者允許抽取幀的時(shí)間點(diǎn)和給定的時(shí)間點(diǎn)存在一定的誤差。 -僅解碼關(guān)鍵幀尖奔,并可重復(fù)使用幻馁。 僅解碼關(guān)鍵幀的好處是速度最快洗鸵,但如果需要的抽幀數(shù)量比視頻的關(guān)鍵幀數(shù)量多,那就要根據(jù)時(shí)間點(diǎn)靠近的原則來(lái)返回最近的關(guān)鍵幀仗嗦,這樣會(huì)出現(xiàn)重復(fù)的關(guān)鍵幀作為返回值膘滨。比如,現(xiàn)在需要抽取 10 幅縮略圖稀拐,但視頻中僅 2 個(gè) I 幀火邓,則返回的前 5 幅為第 1 個(gè) I 幀,后 5 幅為第 2 個(gè) I 幀德撬。
- 設(shè)置非精準(zhǔn)誤差范圍铲咨。 比如接受誤差范圍為給定時(shí)間點(diǎn) 100ms 左右,則可以先查找給點(diǎn)時(shí)間點(diǎn)前后 100ms 左右是否存在關(guān)鍵幀蜓洪,如果存在纤勒,則解碼該關(guān)鍵幀返回即可;如果不存在則繼續(xù)向左查找最近的 IDR 幀開(kāi)始解碼隆檀,解碼至進(jìn)入給定時(shí)間點(diǎn)左邊 100ms 的范圍即可停止解碼摇天,并返回最近的圖像。
1.3恐仑、數(shù)據(jù)轉(zhuǎn)換和縮放優(yōu)化
視頻幀解碼后的 YUV 數(shù)據(jù)通常是非常大的泉坐,在抽幀時(shí)往往需要將 YUV 數(shù)據(jù)轉(zhuǎn)換為 RGB 進(jìn)行處理,并且常常還需要進(jìn)行裁剪裳仆、縮放腕让、旋轉(zhuǎn)。在通過(guò)數(shù)據(jù)格式判斷是否需要數(shù)據(jù)轉(zhuǎn)換或者縮放等操作至指定分辨率時(shí)歧斟,使用指令加速的 libyuv 替換手寫(xiě)的內(nèi)存拷貝移動(dòng)方法能縮短轉(zhuǎn)換時(shí)間纯丸。
1.4、解碼丟棄非參考幀
非參考幀就是其他幀在解碼過(guò)程中不需要參考此幀静袖。在解碼目標(biāo)幀時(shí)液南,可以丟棄掉關(guān)鍵幀和目標(biāo)幀之間的非參考幀不進(jìn)行解碼,從而節(jié)省解碼時(shí)間勾徽,提升抽幀速度。
1.5统扳、解碼性能測(cè)試和適配
不同設(shè)備的軟解喘帚、硬解性能有較大的差異,在 Android 設(shè)備上硬解還包括 ByteBuffer 和 Surface 方式咒钟,它們的解碼的性能也表現(xiàn)不同吹由,解碼方式有同步也有異步,對(duì)于 H.264 和 H.265 以及不同分辨率的支持情況也不同朱嘴,最好對(duì)機(jī)型進(jìn)行 Benchmark 后選擇最優(yōu)解碼方式來(lái)提高解碼速度倾鲫。
1.6粗合、解碼器復(fù)用池
在整個(gè)視頻編輯的工作流中,抽幀模塊乌昔、預(yù)覽播放和轉(zhuǎn)碼模塊都有可能需要使用解碼器隙疚,由于操作對(duì)象大多情況下是同一個(gè)視頻,所以解碼器的參數(shù)幾乎都是一致的磕道。為了能夠更快的獲取解碼器供屉,可以實(shí)現(xiàn)一個(gè)解碼器復(fù)用池來(lái)優(yōu)化解碼器的使用性能。
當(dāng)外界請(qǐng)求解碼器池的時(shí)候溺蕉,解碼器池會(huì)在池中尋找屬性匹配(寬伶丐、高、H.264/H.265疯特、硬解/軟解等)并且處于空閑狀態(tài)的解碼器哗魂。如果當(dāng)前沒(méi)有符合條件的解碼器實(shí)例,解碼器池會(huì)創(chuàng)建解碼器并設(shè)置解碼器為非空閑狀態(tài)漓雅。解碼器池也會(huì)定時(shí)清理空閑的解碼器實(shí)例录别,優(yōu)化內(nèi)存。
1.7故硅、抽幀縮略圖緩存
可以存儲(chǔ)解碼后的 BitMap 作為縮略圖緩存庶灿,通過(guò)包含視頻內(nèi)容的 hash 值、抽幀尺寸吃衅、抽幀位置等參數(shù)的信息作為緩存縮略圖的 key往踢。當(dāng)用戶(hù)對(duì)同一個(gè)視頻進(jìn)行操作,在進(jìn)入不同頁(yè)面需要抽幀時(shí)徘层,則可直接從緩存中獲取數(shù)據(jù)來(lái)展示峻呕,不過(guò)這里需要注意 控制緩存的總大小和及時(shí)清理緩存。
1.8趣效、多線(xiàn)程并發(fā)
可將多個(gè)抽幀目標(biāo)時(shí)間戳劃分到多個(gè) GOP瘦癌,由于 GOP 是可以獨(dú)立解碼的單元,所以可以對(duì)這些 GOP 進(jìn)行并發(fā)解碼抽幀跷敬,每組用一個(gè)解碼器解碼讯私,這樣可以時(shí)限并行解碼。
需要注意硬解碼器是有限制的西傀,所以一般使用 2-3 個(gè)線(xiàn)程并發(fā)即可斤寇。這里可以結(jié)合上面提到的解碼器復(fù)用池復(fù)用解碼器,避免解碼器的頻繁創(chuàng)建和超過(guò)數(shù)量限制拥褂。
1.9娘锁、解封裝層優(yōu)化
可以在解封裝層就過(guò)濾出目標(biāo)解碼幀所在的數(shù)據(jù)包(AVPacket) ,而不是等到解碼時(shí)做 Seek饺鹃,因?yàn)?Seek 是需要 flush 解碼器莫秆,這樣會(huì)有耗時(shí)间雀。
比如,當(dāng)待抽幀的視頻中關(guān)鍵幀的數(shù)量大于或等于目標(biāo)抽幀數(shù)镊屎,直接在 Demuxer 中就準(zhǔn)備好對(duì)應(yīng)的關(guān)鍵幀包給解碼器解碼出幀即可惹挟;當(dāng)待抽幀的視頻中關(guān)鍵幀的數(shù)量小于目標(biāo)抽幀數(shù),則可以在 Demuxer 中找到所有待解碼幀及其依賴(lài)幀送給解碼器解碼出幀杯道。這樣就可以避免在解碼時(shí)還需要做 Seek 操作耗時(shí)匪煌。
2、視頻 Seek 優(yōu)化
在視頻編輯的場(chǎng)景中党巾,用戶(hù)有大部分時(shí)間會(huì)停留在編輯頁(yè)面萎庭,在這個(gè)頁(yè)面對(duì)視頻進(jìn)度進(jìn)行拖動(dòng)來(lái)預(yù)覽視頻是一個(gè)高頻的操作,這樣依賴(lài)對(duì)視頻 Seek 體驗(yàn)的優(yōu)化就顯得尤為重要了齿拂。
視頻 Seek 的流程一般分為:解封裝器(Demuxer)Seek驳规、解碼器解碼、音視頻丟棄署海、渲染這四個(gè)步驟吗购。首先播放器根據(jù)用戶(hù)操作拿到目標(biāo)的 Seek 位置,利用解封裝器跳到視頻文件距離目標(biāo)位置左邊最近的 IDR 幀開(kāi)始讀取數(shù)據(jù)砸狞,將之后的視頻 AVPacket 數(shù)據(jù)送給解碼器解碼得到幀(AVFrame)數(shù)據(jù)捻勉,將音頻 AVPacket 直接丟棄到目標(biāo)位置。解碼出來(lái)的視頻幀(AVFrame)數(shù)據(jù)是從 IDR 幀開(kāi)始的刀森,所以需要丟棄目標(biāo)位置之前的幀數(shù)據(jù)踱启,從而渲染從目標(biāo)位置開(kāi)始之后的幀。
2.1研底、精準(zhǔn) Seek 和非精準(zhǔn) Seek
Seek 分為精準(zhǔn)和非精準(zhǔn)埠偿。精準(zhǔn) Seek 是指 Seek 到給定時(shí)間點(diǎn)的位置;非精準(zhǔn) Seek 是指允許 Seek 到給定時(shí)間點(diǎn)附近一定誤差范圍內(nèi)的位置榜晦。
如果不做優(yōu)化冠蒋,精準(zhǔn) Seek 的速度可能會(huì)比較慢的,原因主要包括:
- Demuxer 解封裝耗時(shí):部分格式導(dǎo)致 Demuxer Seek 的過(guò)程很慢乾胶。比如抖剿,MP4 可以從 moov box 的關(guān)鍵幀索引信息中快速精準(zhǔn)查到各 IDR 幀的位置,但是 HLS 就需要先找到 ts 切片下載下來(lái)识窿,然后只能從這個(gè)切片開(kāi)始讀取斩郎。
- 解碼耗時(shí):解碼一幀的速度,以及由于幀之間的解碼依賴(lài)關(guān)系導(dǎo)致要解碼多幀才能得到目標(biāo)幀腕扶,是影響 Seek 速度最主要的因素。
- 渲染邏輯耗時(shí):需要丟棄一個(gè) GOP 中的 IDR 幀到目標(biāo)幀前的其他幀來(lái)直接渲染目標(biāo)幀吨掌。需要注意一些線(xiàn)程和鎖的等待耗時(shí)半抱。
非精準(zhǔn) Seek 可以 Seek 到目標(biāo)幀左側(cè)最近 IDR 幀的位置脓恕,解碼器可以直接解碼這一幀而不需要依賴(lài)其他幀,并隨即完成渲染窿侈,所以非精準(zhǔn) Seek 的速度可以相對(duì)比較快炼幔。
2.2、多線(xiàn)程并發(fā)
將解封裝和解碼拆分成兩個(gè)模塊放到不同線(xiàn)程處理史简,并設(shè)置緩沖區(qū)乃秀。讀取數(shù)據(jù)完成解封裝后將數(shù)據(jù)存儲(chǔ)到緩沖區(qū),解碼線(xiàn)程從緩沖區(qū)取數(shù)據(jù)解碼圆兵,形成一個(gè)生產(chǎn)者消費(fèi)者模式跺讯。
2.3、減少解碼不必要的幀
減少解碼不必要的幀包括下面幾種情況:
- 解碼丟棄目標(biāo)幀之前的音頻幀:由于渲染視頻幀的時(shí)候殉农,需要丟棄一個(gè) GOP 中的 IDR 幀到目標(biāo)幀前一幀的數(shù)據(jù)來(lái)直接渲染目標(biāo)幀刀脏。所以解碼時(shí)可以直接丟棄與這段視頻幀對(duì)應(yīng)的音頻幀,不必解碼超凳。
-
解碼丟棄非參考幀:解碼可以丟棄非參考幀愈污。準(zhǔn)確判斷參考幀是方式是:H.264 通過(guò)判斷 NALU Header 結(jié)構(gòu)中的
nal_ref_idc
字段,該字段為 0 表示非參考幀轮傍;H.265 直接判斷 NALU Header 的type
字段來(lái)確定是否為參考幀茎匠。 - 根據(jù)最優(yōu)解碼序列解碼:最優(yōu)解碼序列是指在當(dāng)前 GOP 內(nèi)解碼目標(biāo)幀及目標(biāo)幀之后所有幀的最小需要依賴(lài)幀的集合譬胎。基于這個(gè)最優(yōu)解碼序列來(lái)解碼,可以少解碼一些幀悄雅,對(duì)于包含 B 幀較多的視頻以及 GOP 長(zhǎng)度較大的視頻,效果很好汰具。
2.4讽膏、向右 Seek 充分利用緩存數(shù)據(jù)
在做向右 Seek 時(shí),如果當(dāng)前解封裝后的 AVPacket 緩沖區(qū)中有目標(biāo)幀棚瘟,則不必調(diào)用 Demuxer Seek 操作和解碼现斋,直接繼續(xù)解碼后面的幀直到目標(biāo)幀即可。如果目標(biāo)幀跟當(dāng)前幀不在一個(gè) GOP偎蘸,則直接跳到目標(biāo)幀所在的 GOP 的 IDR 幀開(kāi)始解碼庄蹋。
2.5、解碼性能測(cè)試和適配
同『視頻抽幀優(yōu)化』一節(jié)中的解碼性能測(cè)試和適配
講到的一樣迷雪,不同設(shè)備上的解碼性能受到多種因素的影響限书,最好能做好 Benchmark 再根據(jù)性能情況選擇最后的編解碼配置。
2.6章咧、交互體驗(yàn)優(yōu)化
用戶(hù) Seek 時(shí)交互體驗(yàn)優(yōu)化需要注意以下幾點(diǎn):
- 將影響用戶(hù)交互的代碼移到異步線(xiàn)程:如果 Seek 操作涉及的代碼性能影響到了主線(xiàn)程的用戶(hù)交互倦西,需要將 Seek 操作拆分,將耗時(shí)較大的代碼放到異步線(xiàn)程赁严,不能影響主線(xiàn)程用戶(hù)的 UI 操作扰柠。
- 用戶(hù)連續(xù)滑動(dòng)時(shí)體驗(yàn)優(yōu)化:如果用戶(hù)連續(xù)滑動(dòng)粉铐,可以展示滑動(dòng)中已解碼好的幀,即使與當(dāng)前手指的位置不一致卤档,等滑動(dòng)停止后再展示停止時(shí)刻的幀蝙泼。連續(xù)滑動(dòng)會(huì)觸發(fā)連續(xù)的 Seek,新的 Seek 來(lái)了劝枣,但是老的 Seek 的幀這時(shí)候已經(jīng)解碼完成或者已解碼到的幀在上一次目標(biāo)幀和新的目標(biāo)幀之間汤踏,可以展示當(dāng)前已解碼到的幀,這樣可以給用戶(hù)連續(xù)滑動(dòng)的效果舔腾,而不是畫(huà)面卡住跳動(dòng)的感覺(jué)溪胶。
- 完 -
推薦閱讀