
導(dǎo)讀:如今,B 站已經(jīng)成為了國內(nèi)最大的視頻彈幕網(wǎng)站,其他視頻平臺、漫畫、閱 讀等內(nèi)容平臺也都增加了彈幕功能。彈幕已經(jīng)成為一種重要的內(nèi)容互動的手段, 因此研發(fā)一套接入靈活、玩法豐富的彈幕組件就顯得非常重要 。
全文3979字,預(yù)計閱讀時間9分鐘。
引言:
如今B 站已經(jīng)成為了國內(nèi)最大的視頻彈幕網(wǎng)站,其他視頻平臺、漫畫、閱讀等內(nèi)容平臺也都增加了彈幕功能。彈幕已經(jīng)成為一種重要的內(nèi)容互動的手段。
廠內(nèi)在短視頻,長視頻,直播等媒體類產(chǎn)品線較多,面臨著同樣的彈幕問題,諸如彈幕量大影響視頻體驗(yàn),彈幕互動(點(diǎn)贊),角色彈幕,彩色彈幕等諸多業(yè)務(wù)需求。
目前市面上開源的彈幕實(shí)現(xiàn)主要是基于DanmakuFlame, 該庫代碼已經(jīng)停止維護(hù),并且支持的功能比較單一,接入維護(hù)困難,擴(kuò)展困難?;谝陨显蛞虼搜邪l(fā)一套接入靈活、玩法豐富的彈幕組件就顯得非常重要。BDDMBarrage即是基于滿足以上需求開發(fā)的彈幕sdk,該sdk支持自定義彈幕樣式,支持彈幕數(shù)據(jù)源注入,彈幕穿人等功能。
該文會主旨講述彈幕穿人的解決方案,為方便理解,我們會從宏觀的彈幕架構(gòu)出發(fā),深入到人像分離技術(shù),算法,服務(wù)端部署,彈幕遮罩管理,遮罩緩存以及開發(fā)中遇到的棘手問題包括解決該問題的技術(shù)策略,還有端側(cè)的性能優(yōu)化的諸多方面展開討論,最后會分享一些對未來技術(shù)暢享和規(guī)劃。
先體驗(yàn)一下最終的實(shí)現(xiàn)效果圖:

一、彈幕架構(gòu)圖
- 彈幕數(shù)據(jù)管理模塊:其主要功能是為彈幕渲染模塊提供數(shù)據(jù)。首先,會在彈幕緩存模塊內(nèi)查找是否有該時間點(diǎn)的彈幕數(shù)據(jù),如果有數(shù)據(jù),則直接提供給彈幕渲染模塊;如果沒有數(shù)據(jù),則觸發(fā)彈幕數(shù)據(jù)的網(wǎng)絡(luò)請求,獲得彈幕數(shù)據(jù)后再提供給彈幕渲染模塊。因?yàn)閺椖粚r效性要求比較高,所以該模塊設(shè)計了一個預(yù)取策略,這樣保證彈幕渲染模塊在獲取數(shù)據(jù)的時候,盡可能地命中緩存模塊,減少由于網(wǎng)絡(luò)請求而產(chǎn)生的時間延遲。
- 彈幕渲染模塊:其中彈幕時間引擎可以方便地統(tǒng)一調(diào)控彈幕整體速度,比如倍速、慢速、暫停等;彈幕調(diào)度模塊提供一套嚴(yán)密的軌道算法:可以根據(jù)彈幕自身的長度設(shè)定合理的速度,并且能夠保證同一軌道的任何兩條彈幕不會碰撞。針對彈幕的樣式以及彈幕自身交互,提供一套自定義方案。接入方可以按照自己的APP形態(tài)去任意設(shè)計彈幕的UI,也能夠發(fā)掘出新的玩法從而提升整個APP的互動氛圍,比如番樂APP設(shè)計了依據(jù)劇情的角色彈幕,運(yùn)營彈幕,也可以增加VIP彈幕等。
- 彈幕穿人模塊:該模塊是利用AI的圖像分割技術(shù)產(chǎn)生一系列的蒙版文件,然后根據(jù)視頻播放器對視頻的裁剪方式,對蒙版進(jìn)行處理生成合適的遮罩,最后按照視頻的時間軸把相應(yīng)的遮罩渲染到彈幕View上。具體方案會在后面介紹。
二、彈幕穿人
在幾個產(chǎn)品線接入彈幕組件以后,發(fā)現(xiàn)大家都不約而同把彈幕軌道數(shù)設(shè)置為3,具體原因是擔(dān)心彈幕過多而影響用戶的視頻消費(fèi)體驗(yàn)。這樣雖然保證了視頻觀看的體驗(yàn),但是大大弱化了彈幕營造的氛圍,為此我們彈幕組件的rd們開始暗戳戳地準(zhǔn)備一個黑科技——彈幕穿人技術(shù),希望以此能解決問題。下面一段視頻給大家看下我們當(dāng)前的進(jìn)展。整個流程自下而上,分成算法側(cè)、服務(wù)端、客戶端三層:首先,算法側(cè)按每秒 32 幀的頻率進(jìn)行視頻抽幀,對每一幀進(jìn)行人臉識別,配合人臉跟蹤和平滑處理,生成每一幀的人臉元數(shù)據(jù);其次,服務(wù)端將多個幀的人臉元數(shù)據(jù)進(jìn)行相似度濾重,然后根據(jù)每3分鐘一個元數(shù)據(jù)包。在客戶端sdk側(cè)會根據(jù)播放進(jìn)度預(yù)拉取服務(wù)側(cè)的對應(yīng)時間段的壓縮包,播放到相應(yīng)幀將彈幕試圖與人臉元數(shù)據(jù)做一個混合渲染。1、算法側(cè)
1)視頻抽幀模塊:將視頻流按每秒 32 幀(可配置)的頻率抽幀。抽幀頻率越高,遮罩越平滑,遮罩顯示畫質(zhì)會更細(xì)膩,但后面人臉識別算法耗時也隨之增加,手機(jī)的性能損耗也會隨之增大,如內(nèi)存耗電等。2)模型訓(xùn)練模塊:提供多張多角度劇中出現(xiàn)的人物圖像,給模型訓(xùn)練模塊來訓(xùn)練,生成對應(yīng)人臉庫,再配合已訓(xùn)練完成的明星庫,這兩個庫可以大大提高人臉檢測的準(zhǔn)確度;3)人臉檢測:識別每一幀圖像中的人臉,并給出人臉輪廓數(shù)據(jù);4)人臉相似度:為減少網(wǎng)絡(luò)數(shù)據(jù)傳輸壓力,會對相似度大于95%的兩幀,丟棄一幀,或者數(shù)幀。2、服務(wù)端
1)視頻抽幀元數(shù)據(jù)管理:管理算法側(cè)提供的幀數(shù)據(jù),以視頻維度,視頻內(nèi)時間段維度將大量的視頻元數(shù)據(jù)進(jìn)行分包,建立映射索引,提供到SDK的可以是某個時間段內(nèi)視頻的元數(shù)據(jù)組2)合并:算法側(cè)吐出的都是每一幀的元數(shù)據(jù),但客戶端關(guān)心的是一張人臉的變化過程,服務(wù)端會把元數(shù)據(jù)合并,去重組成人臉組數(shù)據(jù);3)彈幕服務(wù):提供基礎(chǔ)的彈幕數(shù)據(jù)3、客戶端sdk
1)渲染模塊:渲染模塊 有兩套方案:
▌其一是直接通過Canvas的混合模式繪制setXfermode模式繪制。該模式會對canvas上的兩個圖層進(jìn)行選擇性疊加,這樣在頭像部分的圖層上,我們選擇只繪制遮罩層,而不繪制彈幕層即可實(shí)現(xiàn)遮罩效果;
▌其二使用OpenGL,根據(jù)傳入的遮罩圖,在Fragment Shader 處,輸出對應(yīng)的繪制顏色即可。最初使用的方案是OpenGL的繪制,通過源碼閱讀發(fā)現(xiàn)兩種實(shí)現(xiàn)方案在底層實(shí)現(xiàn)上是一致的,Canvas也是Surface提供出來的可繪制api,選擇第一種既可以,方便簡潔;
2)人臉數(shù)據(jù)緩存:緩存整個視頻的索引表,根據(jù)索引表定位到具體的遮罩包, 根據(jù)當(dāng)前的播放進(jìn)度在遮罩包內(nèi)便宜取處對應(yīng)的遮罩;3)彈幕基礎(chǔ)控制API,以及配置API。三、服務(wù)部署
1: 環(huán)境:環(huán)境依賴:FFmpeg、Python2.7、OpenCV、numpy2: 離線數(shù)據(jù)存儲結(jié)構(gòu)離線處理過程中的文件存放目錄及文件后綴:
目錄名:{vid}_{media_id}根據(jù)視頻vid及media_id生成對應(yīng)文件夾,包含如下子文件:- humanseg(人像分割處理后的base64圖片信息 .json)
- contour_png(圖像處理過程中生成的輪廓圖 .png)
- contour_svg(轉(zhuǎn)存為svg格式的圖片 .svg)
四、SDK內(nèi)置人臉模型碰到的問題
廠內(nèi)也嘗試了使用端內(nèi)置人臉模型的方案。碰到如下問題:1、視頻的播放每16s一幀,會產(chǎn)生大量的幀數(shù)據(jù),模型識別速度在性能上碰到了瓶頸,會存在丟幀的情況,導(dǎo)致遮罩效果不夠細(xì)膩。尤其頭像邊緣處理較為嚴(yán)重。2、端側(cè)識別時手機(jī) cup 消耗增大,即耗電量會增大,同時可能也影響到播放器卡頓率,整個內(nèi)存壓力也很大。
五、棘手的問題
一個2分鐘視頻按照每秒32幀進(jìn)行抽針、圖像分割的話,將會得到3840張蒙版文件,目前從圖像分割算子獲取的蒙版文件是一張二值圖(PNG格式),大約在100多K,那么一個2分鐘視頻生成的蒙版文件總計375M。按照這樣的規(guī)格去設(shè)計,彈幕蒙版文件有可能比視頻本身還大,占用更多的帶寬,這樣肯定是不能落地的。另外,由于彈幕蒙版文件過大,下載也需要花費(fèi)比較長的時間,勢必會造成視頻已經(jīng)播放了很長一段時間,但彈幕蒙版還處于下載中,用戶體驗(yàn)也會非常糟糕。針對這個問題,我們主要從兩方面入手:第一、把二值圖轉(zhuǎn)為svg文件,因?yàn)閟vg文件就是一個純粹的XML,并且可壓縮性非常強(qiáng)。只需要把二值圖中人形輪廓記錄到svg文件里面即可(也就是一些點(diǎn)的集合)。另外還可以靈活調(diào)整記錄人形輪廓的粒度,從而進(jìn)一步調(diào)整svg文件的大小。最終我們把一張二值圖大小從100多K壓縮為幾百字節(jié)。第二、蒙版文件集合采用分段壓縮存儲,這樣可以達(dá)到邊播變下載的效果。而且,在第一段下載完成以后就可以渲染,提升用戶體驗(yàn);另外,視頻播到哪里,彈幕蒙版文件下載到哪里,這樣也節(jié)省了帶寬。{vid}_{interval} _{index}.zip示例:4752528107223374247_10_0.zip{index}.svg 示例:0000001.svg▎索引文件結(jié)構(gòu):index.json不同手機(jī)內(nèi)存是有限制的,尤其一些低端機(jī)對內(nèi)存的消耗更是捉襟見肘,而視頻類app相對來說更耗內(nèi)存,所以彈幕sdk的耗內(nèi)存程度,直接決定了其可用程度。由于每個遮罩文件大約在100~200kb, 我們的遮罩是1分鐘會產(chǎn)生32幀,即使經(jīng)過合并處理也還是很大。按照這個計算1分鐘的內(nèi)存占用:Memory Total = 32 * 100kb = 3.125MB在ios側(cè)還算可以,在Android側(cè)每一分鐘產(chǎn)生3mb的內(nèi)存占用即使進(jìn)行內(nèi)存回收也會產(chǎn)生很差的性能體驗(yàn),平凡的內(nèi)存回收會很耗性能,導(dǎo)致app卡頓明顯。根據(jù)視頻時長分配固定的本地內(nèi)存,該本地內(nèi)存循環(huán)利用,這樣減少了內(nèi)存的頻繁回收,限制了內(nèi)存的無止盡使用。六、未來展望
人臉數(shù)據(jù)的產(chǎn)生過程是很耗成本的,單服務(wù)跑一個腳本5分鐘視頻,大約需要2小時,但是產(chǎn)生的數(shù)據(jù)是復(fù)雜的,不僅可以產(chǎn)產(chǎn)生人臉二值圖,還可以產(chǎn)生人體的其他數(shù)據(jù), 如人的運(yùn)動軌跡等,下一步我們準(zhǔn)備把帶有人臉數(shù)據(jù)和人體數(shù)據(jù)的腳本,人像運(yùn)動軌跡做為基本腳本,基于這些基本腳本,可以做很多創(chuàng)新的案例,比人彈幕互動,彈幕跟隨的玩法,也可以對視頻中不同人物的頭像進(jìn)行變臉等等。技術(shù)交流,歡迎加我微信:ezglumes ,拉你入技術(shù)交流群。
推薦閱讀:
音視頻開發(fā)工作經(jīng)驗(yàn)分享 || 視頻版
OpenGL ES 學(xué)習(xí)資源分享
開通專輯 | 細(xì)數(shù)那些年寫過的技術(shù)文章專輯
NDK 學(xué)習(xí)進(jìn)階免費(fèi)視頻來了
你想要的音視頻開發(fā)資料庫來了
推薦幾個堪稱教科書級別的 Android 音視頻入門項(xiàng)目