摘要:最終方案也確定采用序列幀動畫方案。所以,要想在電影或者視頻上顯示效果,首先要做的是編寫特效文件,然后再將特效文件解析成序列幀動畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進行播放,就能看到各種特效的動畫。
本文由云+社區發表一、 背景 1. 現狀作者:QQ音樂技術團隊
歌詞瀏覽已經成為音樂app的標配,展示和動畫效果也基本上大同小異,主要是單行的逐字染色的卡拉OK效果和多行的滾動效果。當然,我們也不例外。
2. 目標我們的目標十分明確,一是提升歌詞的基礎體驗,二是在此基礎上,能提供差異化的VIP特效,來吸引用戶開通VIP。
二、探索技術方案經過多次的需求評審和溝通討論,各方在需求的目標和細節上也達成了初步的統一。 產品的希望 :效果炫酷,能實現逐字動畫(位移,翻轉,漸隱漸現,模糊,粒子特效等),可配置等。開發的思考: 技術架構方案,性能挑戰等,接下來我們簡單介紹一下確定技術方案的過程。
1. 技術方案選型這里最初的思路有兩個方向,升級現有歌詞組件和開發全新歌詞組件。所謂知已知彼,百戰不殆, 通過對移動端面主流競品的技術方案和PC端類似方案的技術調研與分析。最終將技術方案鎖定在以下三種:
現有歌詞組件升級
Shader序列幀動畫
ASS序列幀動畫
2. 備選技術方案介紹下面簡單介紹一下三種方案的原理和特點,如下表所示:
總的來說,就是在原生動畫開發和幀動畫方案中進行選擇。
3. 技術方案對比以下主要是從是否實現特效,開發的難度,方案的性能,實現的成本,跨平臺等方面對比三種方案,具體細節如下表所示:
4. 確定方案通過以上幾個維度的綜合考量:
現有歌詞組件基本上無法實現逐字動畫。
Shader幀動畫開發周期長,實現成本高,逐字動畫支持不是很好。
ASS實現逐字動畫,可通過植入動畫標簽實現復雜的特效,有開源支持,且跨平臺。
綜上所述,ASS方案性價比最高。
最終方案也確定采用ASS序列幀動畫方案。
三、 技術架構 1. ASS技術工作原理介紹前面簡單介紹了一下什么ASS字幕和幀動畫的原理。我們知道ASS是一種字幕文件格式,屬于高級字幕,可以制作出華麗的特效字幕。所以,要想在電影或者視頻上顯示ASS效果,首先要做的是編寫ASS特效文件,然后再將ASS特效文件解析成序列幀動畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進行播放,就能看到各種特效的動畫。如下圖所示:
2. 如何接入ASS方案 2.1 合成如下下圖所示:,首先,需要準備展示內容(字幕或者歌詞內容),比如一個文本文件,有了最基本的文本文件,怎么轉換成ASS解析器能解析的ASS文件呢?答案是打K值,打K值是指給字幕文件加上時間軸屬性。而是什么K值呢,就是ASS中K拉OK的效果標簽代碼,即每行甚至每個字的時間坐標。有了打完K值的ASS文件,我們就可以在視頻播放器中瀏覽,也就有了最基本的逐字染色動畫。如果要開發更復雜的特效,就需要加入更多的特效標簽。而這一部分,就可以通過腳本加上動畫模板(動效模板就是具有特定動畫效果的ASS文件),將動畫標簽注入到打完K值ASS文件中,生成最終的ASS特效文件。至此,一個具有特效的ASS文件就誕生了。
2.2 解析解析的過程相對比較簡單。解析一個ASS文件,不僅需要ASS文件本身,還需要知道ASS文件是用什么字體合成的。這里補充一下,前面合成的時候,其中的動畫模板也是需要指定是使用哪種字體來合成的。因為這里會涉及到字體的大小,間距等,對動畫效果和排版的影響。然后,再回到解析上來,通過ASS文件加上字體庫就可以解析生成特定序列的幀動畫位圖。
3. 技術架構
最終方案的技術架構:功能上劃分如下,后負責存儲和合成;客戶端負責解析和繪制,呈現用戶最終的動畫效果。
4. 通用性上面提到了這套方案的通用性和易復用的特點。那除了動效歌詞之外,我們還可以做些什么呢?
首先,我們脫離業務對架構進行更高一層的抽象,梳理出了更通用的架構方。這里還需要補充一點,“字體庫”,從字面上理解應該是一堆字體的容器,所以字體庫應該是保存了一大堆的文字信息等。但其實不僅是文字也可以是圖形,所以我們的動畫效果可以不只是針對文字的,還可以設計一些圖形動畫效果。所以,這里可以有更多的想像空間。前面解析的過程我們提到,解析出一幀幀的圖,就拿去直接播放了,這樣我們就能實時看到動畫效果。那如果把這些圖片保存下來,根據業務需求在需要的時候再播放呢。這里就可以拆分出實時渲染和離線渲染兩種方案。
這里的渲染提供了兩種方案:
1. 實時渲染
將解析出來的位圖立即繪制到屏幕上。
適用場景:實時要求高的場景。
特點: 對系統性能消耗大,需要注意當前場景的性能開銷。
2. 離線渲染
將解析出來的位圖保存到磁盤上,并可以此基礎上建立序列幀動畫的資源管理。
適用場景:適用于異步化的場景。
特點: 建議采用異步線程在后臺處理,減少對主線程消耗。
大家可以根據各自業務場景和特點靈活選擇或者組合使用這兩種方案。
以上主要是介紹動效歌詞技術方案的實現原理與架構介紹。
四、技術難點與挑戰在開發過程中,我們遇到了兩個重要的問題:一個是在運行復雜的效果時,動畫效果出現了肉眼可見的卡頓;另一個則是內存的問題,即使是比較簡單的效果播放以后也會占用大量的內存。本文后半部分將重點闡述K歌是如何解決這兩個問題的。
1. 卡頓問題描述我們選取了一個較為復雜的效果,包含了大量的煙霧、花瓣等動畫元素 及 位移、形變與模糊等效果,它的每一幀畫面約由1000個元素構成。
在三星Note 3(Android 5.0,4核,ARMv7)上運行起來平均只能達到7幀的效果。
2. 解碼與渲染的過程為了解決上述問題,我們需要對ASS由文本文件到渲染至屏幕的整個過程有基本的認識。這里以Android為例(Ios在渲染的處理上略有不同,而其它是一致的),先看JNI的接口:
private native int decodeFrame(long time, int[] pixels);
Java層會傳入時間戮time及名為pixels的Int數組,time代表當前需要獲取哪個時間點的動畫效果,libass接著會對與這一時間點有關的每一行文本進行解析,生成一個或多個的小圖,從而得到一系列的圖片,然后合成到一個大圖里面去,最終通過像素拷貝的方式把合成后的結果輸出到pixels,回到Java以后,再把pixels設置至Bitmap,最后交給Canvas進行渲染。
3. 過程耗時分析通過對各關鍵過程的打點并運行前述復雜效果,我們得到了各過程的耗時占比:解析46%、合成37%、輸出與渲染各8%,其它1%。分解到每一幀并以毫秒計算則如下表:
接下來,我們將會按解析、合成、輸出、渲染這樣的順序來逐步優化。
4. 卡頓優化實踐前面提到,每一行ass文本都會生成一個或多個的小圖,這是因為一個文字會被拆解成文體、邊框及背景三個部分,除此之外,libass并不關心這些構成部分的顏色及透明度。這就導致了這樣的一個問題:
Dialogue: 1,0:00:00.00,0:01:00.00,Default,,0,0,0,fx,{pos(120,100)1a&HFF&lur3}全民K歌
以上ass文本所實現的是一個文字鏤空效果:
1a&HFF&表示文字主體是完全透明的,而這樣的一個透明的元素,libass依然會生成一個小圖對它進行各種各樣的處理,但這是完全沒有必要的,于是我們對libass進行了第一點改造:不再生成無效的透明小圖,提高ass解析效率的同時也減少了內存的分配,對后續合成的處理也有正向的影響
在合成的處理中,需要遍歷小圖的每一個像素并拆分為ARGB4個通道進行顏色的運算
dstA = (255 * 255 - (255 - k) * (255 - dstA)) / 255; dstB = (k * b + (255 - k) * dstB) / 255; dstG = (k * g + (255 - k) * dstG) / 255; dstR = (k * r + (255 - k) * dstR) / 255;
與普通的圖片合成不同,在歌詞動效的場景中,小圖由文字或點線之類的圖形構成,往往存在著大量的透明像素及完全不透明像素,可通過判斷來減少這部分的合成運算:
if(k == 0){ // 完全透明,跳過 continue; } if(k == 255){ // 完全不透明,直接使用小圖顏色 dst = color; continue; }
測試了5個在K歌上線的動效,合成時間減少了10%~50%。
雖然通過透明度的判斷減少了一定計算,但無法完全避免。以Alpha通道的計算為例,包含了2次乘法、1次除法和3次的減法,而除法是特別耗時的。所以,對于這些必要的計算,我們進行了簡化,先進行等式變換:
dstA = (255 * 255 - (255 - k) * (255 - dstA)) / 255; = (255 - (255 - k) * (255 - dstA) / 255);
然后利用255 - x = ~x及x / 255 ≈ x >> 8進行替換,得到簡化后的結果:
dstA = ~((~k) * (~dstA)) >> 8);
可見,一次計算變成了1次乘法與4次位運算,測得合成時間減少了26%。
經過上述幾項優化,合成速度快了許多,但這還不夠。在合成的算法中,像素點與像素點間是沒有任何聯系的,所以可以通過并行計算的方式來提高合成的效率。我們采用了NEON的解決方案,利用CPU專用模塊的128位寄存器同時對多個像素點進行計算,因32位色彩中ARGB各占8位,再考慮乘法處理后可能達到的16位,由此,可用128位寄存器同時處理8個像素點的計算,實現約8倍的加速效果,對CPU和幀率可起到明顯的作用。 具體實現如下:
至此,合成的優化告一段落,每一幀的合成耗時由原來的52ms,降到了3ms以內
輸出的過程實際上只是做了一次像素拷貝的操作,把合成后的大圖輸出到JNI傳入的Int數組里面去,除了耗時以后,還會產生額外的一次Native內存分配,于是,我們優化了這個過程,讓合成直接在Int數組進行,這樣就把原來輸出的11ms完全去掉了
前面提到,數據到了Java層,還會調用Bitmap的setPixels方法把像素信息傳給Bitmap,最后才交給Canvas進行繪制,而這里的setPixels做的事跟剛剛輸出的過程一樣,會把像素點全都拷貝一次。所以,我們希望把這一過程的拷貝也給取消掉,但Java并沒有提供接口給我們去獲取Bitmap的Buffer,也就采用了反射的方案,優化后,渲染耗時降低了65%。
我們知道,卡頓的原因在于處理一幀的耗時太久,達不到我們想要的幀率要求,那很容易會想到,我們是否可以使用多線程同時處理多幀數據呢?結果是失敗了,因為libass是單例的模式,同時處理多個時間點的解析合成會導致其內部一些狀態的錯亂,并以crash告終。雖然解碼無法使用多線程,但渲染與libass無關,還是可以拿出來放到一個多帶帶的線程去處理的。這就引入了一個新的問題,解碼與渲染兩個線程都會操作同一塊內存,一邊在寫、一邊在讀,數據容易出錯。于是,我們多申請了一塊內存,一個解碼用,一個渲染用,每次解碼完成時進行交換,我們的雙緩沖異步渲染方案就這樣出現了
這一實現讓libass不需要等待渲染的完成就可以進行下一幀數據的解碼,有效地提高了動效的幀率
經歷上述各項優化后,前述復雜動效在低端機Note 3上由原來的7幀達到15幀
2. 內存問題描述在不干預內存的情況下,在一個3分多鐘的作品上播放了K歌線上的一個普通效果,期間內存的變化見下圖:
內存增量達到了180M,且主要是Native層的內存,這是我們面臨的一個很嚴重的問題,有OOM的風險,系統也有可能因此產生頻繁的GC而引起卡頓
通過對libass源碼的閱讀,我們了解到了更為詳細的ASS解析過程
每一行動效文本在libass中被定義一個事件,先是對事件中的動畫標簽及參數進行解析,得到某一瞬間的所有屬性值后創建文字或圖形的輪廓;接著是對它進行柵格化的處理,后續還有拼接、模糊等處理,最終生成小圖并進行重排,就得到了卡頓問題中所說的一系列小圖。
在這樣的一個過程中,內存分配主要消耗在柵格化和拼接這2個過程中,且libass內部已經實現了一套完整的緩存管理機制,只是其默認緩存較大,分別為128M和64M,總大小達到了192M,再加上些其它的內存分配,最大會占用超過200M的內存才會趨于平穩。除此之外,libass還提供了接口給我們設置緩存的大小,但只能設置總的緩存大小,不能自定義Bitmap和Composite Bitmap分別是多少,其內部會按2:1進行分配。
有了對libass的認識,內存問題也就變成了:如何尋找一個合適的緩存總大小 及 內存的2:1分配是否適合我們的場景。
統計動效在一次播放的過程中查詢緩存的次數M,查詢后命中的次數為N,從而得到緩存命中率N/M。下圖橫軸表示了我們給libass設置的緩存總大小,縱軸則是2類緩存的命中率
通過上面的曲線,我們可以得到2個結論:1. 隨著緩存總大小的增加,新增內存所獲得的收益逐漸變小,對于K歌的場景,設置4M~16M比較合理; 2. Bitmap 與 Composite Bitmap 的分配不合理,可將更多的內存用于Composite Bitmap。
從K歌線上的10幾個動效中,隨機選取了5個,統計各個動效處理1500幀數據對2類緩存的訪求并制成了表格
通過表格的數據可以看到,Composite Bitmap需要更大的緩存,平均約為Bitmap的1.8倍,于是我們把libass內2:1的分配規則調整為了1:1.8,最終使用8M的內存基本上達到了原來16M的效果
設置緩存大小后,內存增長得到了控制且處于穩定狀態;而調整分配比例提高了緩存命中率,減少了CPU在內存分配與柵格化等處理上的耗時。
小結本文主要介紹了動效歌詞開發的關鍵技術和優化策略。技術方案經歷了數次討論和預研,采用了并行計算大幅減少運算時間,優化了編譯策略解決了跨平臺問題。在架構設計上,也充分考慮性能,跨平臺,可擴展,組件化,復用性等各方面的因素。在該方案的落地實現過程中,團隊的John、Harvey、Wing、 Comic,、Jerry、rey等同學通力合作,付出了不懈的努力!
此文已由騰訊云+社區在各渠道發布
獲取更多新鮮技術干貨,可以關注我們騰訊云技術社區-云加社區官方號及知乎機構號
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/53688.html
摘要:最終方案也確定采用序列幀動畫方案。所以,要想在電影或者視頻上顯示效果,首先要做的是編寫特效文件,然后再將特效文件解析成序列幀動畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進行播放,就能看到各種特效的動畫。 本文由云+社區發表作者:QQ音樂技術團隊 一、 背景 1. 現狀 歌詞瀏覽已經成為音樂app的標配,展示和動畫效果也基本上大同小異,主要是單行的逐字染色的卡拉OK效果和多行的...
摘要:非常的龐大,而且它是完全為設計而生的動效庫。它運行于純粹的之上,是目前最強健的動畫資源庫之一。可能是創建滾動特效最好用的工具,它支持大量的瀏覽器,只要它們支持和特性。可以通過安裝吊炸天了,接近現實生活中的物理運動碰撞慣性動畫庫。 收集日期為2019-02-28,★代表當時的該項目在github的star數量 Animate.css 56401 ★ 一個跨瀏覽器的動效基礎庫,是許多基礎動...
摘要:即將立秋的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。課多周刊機器人運營中心是如何玩轉起來的分享課多周刊是如何運營并堅持下來的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大...
摘要:即將立秋的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。課多周刊機器人運營中心是如何玩轉起來的分享課多周刊是如何運營并堅持下來的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大...
閱讀 3619·2021-09-27 14:02
閱讀 1770·2019-08-30 15:56
閱讀 1737·2019-08-29 18:44
閱讀 3269·2019-08-29 17:21
閱讀 477·2019-08-26 17:15
閱讀 1169·2019-08-26 13:57
閱讀 1234·2019-08-26 13:56
閱讀 2874·2019-08-26 11:30