QQ音乐的动效歌词是怎样实践的?

本文由云+社区宣布

作者:QQ音乐手艺团队

一、 背景

1. 近况

歌词浏览已成为音乐app的标配,展现和动画效果也基础上迥然差别,主假如单行的逐字染色的卡拉OK效果和多行的转动效果。固然,我们也不破例。

2. 目的

我们的目的十分明确,一是提拔歌词的基础体验,二是在此基础上,能供应差异化的VIP殊效,来吸收用户开通VIP。

二、探究手艺计划

经由屡次的需求评审和沟通议论,各方在需求的目的和细节上也达成了开端的一致。 产物的愿望 :效果炫酷,能完成逐字动画(位移,翻转,渐隐渐现,隐约,粒子殊效等),可设置等。开辟的思索: 手艺架构计划,机能应战等,接下来我们简朴引见一下肯定手艺计划的历程。

1. 手艺计划选型

这里最初的思绪有两个方向,晋级现有歌词组件和开辟全新歌词组件。所谓知已知彼,百战不殆, 经由过程对挪动端面主流竞品的手艺计划和PC端相似计划的手艺调研与剖析。终究将手艺计划锁定在以下三种:

  • 现有歌词组件晋级
  • Shader序列帧动画
  • ASS序列帧动画

2. 备选手艺计划引见

下面简朴引见一下三种计划的道理和特性,以下表所示:

《QQ音乐的动效歌词是怎样实践的?》

总的来说,就是在原生动画开辟和帧动画计划中举行挑选。

3. 手艺计划对照

以下主假如从是不是完成殊效,开辟的难度,计划的机能,完成的本钱,跨平台等方面对照三种计划,细致细节以下表所示:

《QQ音乐的动效歌词是怎样实践的?》

4. 肯定计划

《QQ音乐的动效歌词是怎样实践的?》

经由过程以上几个维度的综合考量:

  1. 现有歌词组件基础上没法完成逐字动画。
  2. Shader帧动画开辟周期长,完成本钱高,逐字动画支撑不是很好。
  3. ASS完成逐字动画,可经由过程植入动画标签完成庞杂的殊效,有开源支撑,且跨平台。
  4. 综上所述,ASS计划性价比最高。

终究计划也肯定采纳ASS序列帧动画计划。

三、 手艺架构

1. ASS手艺工作道理引见

前面简朴引见了一下什么ASS字幕和帧动画的道理。我们晓得ASS是一种字幕文件花样,属于高等字幕,能够制作出华美的殊效字幕。所以,要想在影戏或许视频上显现ASS效果,起首要做的是编写ASS殊效文件,然后再将ASS殊效文件剖析成序列帧动画的位图,末了将这些位图依据特定的递次和肯定的帧率举行播放,就可以看到种种殊效的动画。以下图所示:

《QQ音乐的动效歌词是怎样实践的?》

2. 怎样接入ASS计划

2.1 合成

以下下图所示:,起首,须要预备展现内容(字幕或许歌词内容),比方一个文本文件,有了最基础的文本文件,怎样转换成ASS剖析器能剖析的ASS文件呢?答案是打K值,打K值是指给字幕文件加上时刻轴属性。而是什么K值呢,就是ASS中K拉OK的效果标签代码,即每行以至每个字的时刻坐标。有了打完K值的ASS文件,我们就可以够在视频播放器中浏览,也就有了最基础的逐字染色动画。假如要开辟更庞杂的殊效,就须要到场更多的殊效标签。而这一部份,就可以够经由过程剧本加上动画模板(动效模板就是具有特定动画效果的ASS文件),将动画标签注入到打完K值ASS文件中,天生终究的ASS殊效文件。至此,一个具有殊效的ASS文件就诞生了。

《QQ音乐的动效歌词是怎样实践的?》

2.2 剖析

剖析的历程相对照较简朴。剖析一个ASS文件,不仅须要ASS文件自身,还须要晓得ASS文件是用什么字体合成的。这里补充一下,前面合成的时刻,个中的动画模板也是须要指定是运用哪一种字体来合成的。由于这里会涉及到字体的大小,间距等,对动画效果和排版的影响。然后,再回到剖析上来,经由过程ASS文件加上字体库就可以够剖析天生特定序列的帧动画位图。

《QQ音乐的动效歌词是怎样实践的?》

3. 手艺架构

终究计划的手艺架构:功能上离别以下,后担任存储和合成;客户端担任剖析和绘制,显现用户终究的动画效果。

4. 通用性

上面提到了这套计划的通用性和易复用的特性。那除了动效歌词以外,我们还能够做些什么呢?

起首,我们离开营业对架构举行更高一层的笼统,梳理出了更通用的架构方。这里还须要补充一点,“字体库”,从字面上明白应该是一堆字体的容器,所以字体库应该是保留了一大堆的笔墨信息等。但实在不仅是笔墨也能够是图形,所以我们的动画效果能够不只是针对笔墨的,还能够设想一些图形动画效果。所以,这里能够有更多的想像空间。前面剖析的历程我们提到,剖析出一帧帧的图,就拿去直接播放了,如许我们就可以及时看到动画效果。那假如把这些图片保留下来,依据营业需求在须要的时刻再播放呢。这里就可以够拆分出及时衬着和离线衬着两种计划。

这里的衬着供应了两种计划:

1. 及时衬着

将剖析出来的位图马上绘制到屏幕上。

实用场景:及时请求高的场景。

特性: 对体系机能斲丧大,须要注重当前场景的机能开支。

2. 离线衬着

将剖析出来的位图保留到磁盘上,并能够此基础上竖立序列帧动画的资源管理。

实用场景:实用于异步化的场景。

特性: 发起采纳异步线程在背景处置惩罚,削减对主线程斲丧。

人人能够依据各自营业场景和特性天真挑选或许组合运用这两种计划。

以上主假如引见动效歌词手艺计划的完成道理与架构引见。

四、手艺难点与应战

在开辟历程当中,我们遇到了两个重要的题目:一个是在运转庞杂的效果时,动画效果涌现了肉眼可见的卡顿;另一个则是内存的题目,即使是比较简朴的效果播放今后也会占用大批的内存。本文后半部份将重点论述K歌是怎样处理这两个题目的。

1. 卡顿题目形貌

我们选取了一个较为庞杂的效果,包含了大批的烟雾、花瓣等动画元素 及 位移、形变与隐约等效果,它的每一帧画面约由1000个元素组成。

《QQ音乐的动效歌词是怎样实践的?》

在三星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举行衬着。

《QQ音乐的动效歌词是怎样实践的?》

3. 历程耗时剖析

经由过程对各症结历程的办理并运转前述庞杂效果,我们取得了各历程的耗时占比:剖析46%、合成37%、输出与衬着各8%,别的1%。剖析到每一帧并以毫秒盘算则以下表:

《QQ音乐的动效歌词是怎样实践的?》

接下来,我们将会按剖析、合成、输出、衬着如许的递次来逐步优化。

4. 卡顿优化实践

1)过滤通明小图

前面提到,每一行ass文本都邑天生一个或多个的小图,这是由于一个笔墨会被拆解成体裁、边框及背景三个部份,除此以外,libass并不体贴这些组成部份的颜色及通明度。这就致使了如许的一个题目:

Dialogue: 1,0:00:00.00,0:01:00.00,Default,,0,0,0,fx,{\pos(120,100)\1a&HFF&\blur3}全民K歌

以上ass文本所完成的是一个笔墨镂空效果:

《QQ音乐的动效歌词是怎样实践的?》

1a&HFF&示意笔墨主体是完全通明的,而如许的一个通明的元素,libass依旧会天生一个小图对它举行林林总总的处置惩罚,但这是完全没有必要的,因而我们对libass举行了第一点革新:不再天生无效的通明小图,进步ass剖析效力的同时也削减了内存的分派,对后续合成的处置惩罚也有正向的影响

《QQ音乐的动效歌词是怎样实践的?》

2)像素通明度推断

在合成的处置惩罚中,须要遍历小图的每个像素并拆分为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%。

3)简化盘算

虽然经由过程通明度的推断削减了肯定盘算,但没法完全避免。以Alpha通道的盘算为例,包含了2次乘法、1次除法和3次的减法,而除法是迥殊耗时的。所以,关于这些必要的盘算,我们举行了简化,先举行等式变更:

dstA = (255 * 255 - (255 - k) * (255 - dstA)) / 255; 
     = (255 - (255 - k) * (255 - dstA) / 255);

然后应用255 - x = ~xx / 255 ≈ x >> 8举行替代,取得简化后的效果:

dstA = ~((~k) * (~dstA)) >> 8);

可见,一次盘算变成了1次乘法与4次位运算,测得合成时刻削减了26%。

4)并行盘算

经由上述几项优化,合成速率快了很多,但这还不够。在合成的算法中,像素点与像素点间是没有任何联络的,所以能够经由过程并行盘算的体式格局来进步合成的效力。我们采纳了NEON的处理计划,应用CPU专用模块的128位寄存器同时对多个像素点举行盘算,因32位颜色中ARGB各占8位,再斟酌乘法处置惩罚后能够到达的16位,由此,可用128位寄存器同时处置惩罚8个像素点的盘算,完成约8倍的加快效果,对CPU和帧率可起到显著的作用。 细致完成以下:

《QQ音乐的动效歌词是怎样实践的?》

5)合成优化前后对照

至此,合成的优化告一段落,每一帧的合成耗时由本来的52ms,降到了3ms之内

《QQ音乐的动效歌词是怎样实践的?》

6)作废像素拷贝

输出的历程实际上只是做了一次像素拷贝的操纵,把合成后的大图输出到JNI传入的Int数组内里去,除了耗时今后,还会发生分外的一次Native内存分派,因而,我们优化了这个历程,让合成直接在Int数组举行,如许就把本来输出的11ms完全去掉了

《QQ音乐的动效歌词是怎样实践的?》

前面提到,数据到了Java层,还会挪用Bitmap的setPixels要领把像素信息传给Bitmap,末了才交给Canvas举行绘制,而这里的setPixels做的事跟方才输出的历程一样,会把像素点全都拷贝一次。所以,我们愿望把这一历程的拷贝也给作废掉,但Java并没有供应接口给我们去猎取Bitmap的Buffer,也就采纳了反射的计划,优化后,衬着耗时降低了65%。

《QQ音乐的动效歌词是怎样实践的?》

7)双缓冲异步衬着

我们晓得,卡顿的缘由在于处置惩罚一帧的耗时太久,达不到我们想要的帧率请求,那很轻易会想到,我们是不是能够运用多线程同时处置惩罚多帧数据呢?效果是失利了,由于libass是单例的形式,同时处置惩罚多个时刻点的剖析合成会致使其内部一些状况的紊乱,并以crash了结。虽然解码没法运用多线程,但衬着与libass无关,照样能够拿出来放到一个零丁的线程去处置惩罚的。这就引入了一个新的题目,解码与衬着两个线程都邑操纵统一块内存,一边在写、一边在读,数据轻易失足。因而,我们多申请了一块内存,一个解码用,一个衬着用,每次解码完成时举行交流,我们的双缓冲异步衬着计划就如许涌现了

《QQ音乐的动效歌词是怎样实践的?》

这一完成让libass不须要守候衬着的完成就可以够举行下一帧数据的解码,有效地进步了动效的帧率

8)卡顿优化效果汇总

阅历上述各项优化后,前述庞杂动效在低端机Note 3上由本来的7帧到达15帧

《QQ音乐的动效歌词是怎样实践的?》

2. 内存题目形貌

在不干涉干与内存的情况下,在一个3分多钟的作品上播放了K歌线上的一个一般效果,时期内存的变化见下图:

《QQ音乐的动效歌词是怎样实践的?》

内存增量到达了180M,且主假如Native层的内存,这是我们面对的一个很严重的题目,有OOM的风险,体系也有能够因而发生频仍的GC而引发卡顿

1)深切内存分派

经由过程对libass源码的浏览,我们相识到了更加细致的ASS剖析历程

《QQ音乐的动效歌词是怎样实践的?》

每一行为效文本在libass中被定义一个事宜,先是对事宜中的动画标签及参数举行剖析,取得某一霎时的一切属性值后建立笔墨或图形的表面;接着是对它举行栅格化的处置惩罚,后续另有拼接、隐约等处置惩罚,终究天生小图并举行重排,就取得了卡顿题目中所说的一系列小图。

在如许的一个历程当中,内存分派重要斲丧在栅格化和拼接这2个历程当中,且libass内部已完成了一套完全的缓存管理机制,只是其默许缓存较大,离别为128M和64M,总大小到达了192M,再加上些别的的内存分派,最大会占用凌驾200M的内存才会趋于平稳。除此以外,libass还供应了接口给我们设置缓存的大小,但只能设置总的缓存大小,不能自定义Bitmap和Composite Bitmap离别是多少,其内部会按2:1举行分派。

有了对libass的熟悉,内存题目也就变成了:怎样寻觅一个适宜的缓存总大小 及 内存的2:1分派是不是合适我们的场景。

2)寻觅适宜的缓存总大小

统计动效在一次播放的历程当中查询缓存的次数M,查询后掷中的次数为N,从而取得缓存掷中率N/M。下图横轴示意了我们给libass设置的缓存总大小,纵轴则是2类缓存的掷中率

《QQ音乐的动效歌词是怎样实践的?》

经由过程上面的曲线,我们能够取得2个结论:1. 跟着缓存总大小的增添,新增内存所取得的收益逐步变小,关于K歌的场景,设置4M~16M比较合理; 2. Bitmap 与 Composite Bitmap 的分派不合理,可将更多的内存用于Composite Bitmap。

2)寻觅适宜的缓存比例

从K歌线上的10几个动效中,随机选取了5个,统计各个动效处置惩罚1500帧数据对2类缓存的访求并制成了表格

《QQ音乐的动效歌词是怎样实践的?》

经由过程表格的数据能够看到,Composite Bitmap须要更大的缓存,均匀约为Bitmap的1.8倍,因而我们把libass内2:1的分派划定规矩调解为了1:1.8,终究运用8M的内存基础上到达了本来16M的效果

《QQ音乐的动效歌词是怎样实践的?》

3)内存优化效果

设置缓存大小后,内存增进取得了掌握且处于稳固状况;而调解分派比例进步了缓存掷中率,削减了CPU在内存分派与栅格化等处置惩罚上的耗时。

《QQ音乐的动效歌词是怎样实践的?》

小结

本文重要引见了动效歌词开辟的症结手艺和优化战略。手艺计划阅历了数次议论和预研,采纳了并行盘算大幅削减运算时刻,优化了编译战略处理了跨平台题目。在架构设想上,也充分斟酌机能,跨平台,可扩大,组件化,复用性等各方面的要素。在该计划的落地完成历程当中,团队的John、Harvey、Wing、 Comic,、Jerry、rey等同砚同心协力,付出了不懈的勤奋!

此文已由腾讯云+社区在各渠道宣布

猎取更多新颖手艺干货,能够关注我们腾讯云手艺社区-云加社区官方号及知乎机构号

    原文作者:腾讯云加社区
    原文地址: https://segmentfault.com/a/1190000018208713
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞