大家好,我是苍王。 上一篇给大家介绍了我们近来开发视频特效框架YYEVA项目。这编给大家继续深入介绍一下。
YYEVA(YY Effect Video Animate)是一个开源的支持可插入动态元素的MP4动效播放器解决方案,包含设计资源输出的AE扩展,客户端渲染引擎,在线预览工具。
对比传统的序列帧的动画播放方式,具有更高的压缩率,硬解码效率更高的优点,同时支持插入动态的业务元素;对比SVGA、Lottie等播放器,支持更多的特效支持,如复杂3D效果、描边、粒子效果等,达到所见即所得的效果。
我们发布项目已经有数月,也有很多朋友加入了我们的社群提出一些优化方案,我们陆续在支持。也欢迎大家到 github给我们点个⭐⭐⭐,您的Star是对我们最大的支持。
从这篇开始,我们陆续会发布四篇文章,分别给大家介绍下,YYEVA-SDK是如何解析YYEVA资源,并结合动态元素渲染到屏幕上的。现在,就让我们开始YYEVA渲染之旅吧。
YYEVA-Android 接入
- 使用Gradle安装依赖:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
- 对应的Module添加:
dependencies {
implementation 'com.github.yylive.YYEVA-Android:yyeva:1.0.17'
}
// 2.0.0-beta版本
dependencies {
implementation 'com.github.yylive.YYEVA-Android:yyeva:2.0.0-beta'
}
稳定版最新版本是1.0.17,新加入了一些如加速播放,列表播放示例。
beta版本还是2.0.0-beta版本,支持多进程播放,但是暂时还没兼容稳定版的一些新功能。
YYEVA-Android 实现之旅
RGB+Alpha混合原理
MP4视频以H264方式编码视频,MPEG-4的颜色采样标准是YUV,YUV是亮度和色度的分量叠加,不支持alpha通道, 因此,如何让MP4视频支持透明度,业界常用的方式是使用两个通道分别进行 存储视频的RGB数据和Alpha数据。
设计师可以使用我们AE插件生成以上特殊的mp4资源,左边是RGB部分,右边是Alpha部分。然后通过设置不同的拉伸模式,可以在客户端正常展示比例。
为了用户带来更大的全屏特效的震撼感,我们基本选择竖屏使用ASPECT_FIT,横屏
这个图可以更好的了解我们RGB和Alpha区域的排布和转换。
vec3 rgb = texture2D(texture,vec2(vUx.x/2,vUv.y))).rgb
vec alpha = texture2D(texture,vec2(0.5 + vUx.x/2,vUv.y))).r
gl_FragColor=vec4(rgb,alpha)
Android里面opengles的扩展库已经帮助我们将解析器上yuv的数据直接转换为rgba通道,我们只需要将纹理顶点区域指定好。
rgb区域是在左边二分之一的位置,alpha的数据实际只是占rgb区域右边挨着的八分之一大小区域。
意思就是alpha区域实际是rgb区域的四分之一。这里这样处理是人眼对alpha区域并不是非常敏感,可以缩小来压缩mp4空间。
YYEVA客户端渲染
YYEVA实现动态元素的方案,是在透明MP4的基础上,结合Alpha区域的混合,将需要插入的动态元素信息,提前解析并保存在一个Json数据中,同时经过编码、压缩处理,写入MP4的Metadata段。客户端在解析的时候,分别提取音视频轨信息和描述信息。
以上是整个YYEVA的基本架构 ,在我们的工程里可以分别对应上类名来进行检索学习。
前面说过YYEVA会将json数据封装到mp4的metadata当中,然而metada是通过ffmpeg进行封装的,Android端并没有封装好可以直接读取metadata数据的工具。这里只能用比较笨的方法,对整个mp4文件进行遍历读取,读取带有yyeffectmp4josn[[]]yyeffectmp4json的字符串。然后再进行base64,zip解密,那么才能得我们需要的整个数据。
Android的MediaMetaData类和ffmpeg解封并不等同,所以然并卵。IOS端却是能够通过遍历的方法获取到ffmepg封装的metadata box。
如果有大佬能做个相关内容,能够不引用ffmpeg,获取到metadata的box的能力,也是私聊小弟,让我们的框架更加高效完善。
描述信息
通过上面几个图,我们发现,有一个比较关键的信息,就是Json描述信息,这个描述信息我们在使用插件解析设计师图层的时候,已经通过编码压缩,写入到了Metadata类。客户端拿到这个Json类,可以知道每一个动态元素,每一帧的渲染大小、位置以及形状。
{
"descript": { //视频的描述信息
"width": 1808, //输出视频的宽
"height": 1008, //输出视频的高
"isEffect": 1, //是否为动态元素视频
"version": 1, //插件的版本号
"rgbFrame": [0, 0, 900, 1000], //rgb位置信息
"alphaFrame": [900, 0, 450, 500] //alpha位置信息
},
"effect": [
{ //动态元素的遮罩描述信息 : 文字类型
"effectWidth": 700, //动态元素宽
"effectHeight": 1049, //动态元素高
"effectId": 1, //动态元素索引id
"effectTag": "nickName", //动态元素的tag,业务使用的时候,表示的key
"effectType": "txt", //动态元素类型 有 txt和img 2种
"fontColor":"#ffffff", //当为txt类型的时候才存在 如果设计侧未指定,由渲染端自行指定默认值
"fontSize":13, //当为txt类型的时候才存在 如果设计侧未指定,由渲染端自行指定默认值
},{ //动态元素的遮罩描述信息 : 图片类型
"effectWidth": 300, //同上
"effectHeight": 400,//同上
"effectId": 2, //同上
"effectTag": "user_avatar", //同上
"effectType": "img", //同上
"scaleMode":"aspectFill", //当为img类型的时候才存在 如果设计侧未指定,由渲染端自行指定默认值
}...],
"datas": [{ //每一帧的动态元素位置信息
"frameIndex": 0, //帧索引
"data": [{
"renderFrame": [x1, y1, w1, h1], //在画布上的位置
"effectId": 1, //标志是哪个动态元素
"outputFrame": [x1`, y1`, w1`, h1`] //在视频区域的位置
},{
"renderFrame": [x2, y2, w2, h2], //在画布上的位置
"effectId": 2, //标志是哪个动态元素
"outputFrame": [x2`, y2`, w2`, h2`] //在视频区域的位置
} ... ]
}
复制代码
Json数据 包含三层:descript/effect/datas
- descript: 描述该资源的整体信息
- effect : 描述 该资源下的所有遮罩相关信息
- datas : 描述 每一帧遮罩的位置信息
通过这个描述信息,在渲染每一个动态元素的时候,可以知道该动态元素在每一frameIndex
的遮罩位置outputFrame
,以及在画布上的位置renderFrame
该Json信息,在客户端渲染SDK中,会模态化成对应的Descript, Effect Datas对象,因为数据运用减少反射,我们java和C++层都解析了对象。 C++中使用了parson库,这个比较轻量推荐大家使用。
YYEVA渲染流程
YYEVA整个渲染的流程可以通过上图流程完美呈现出来,渲染的代码都是在Native层。
以上是整个流程的简单类逻辑。
这是透明mp4合成的逻辑,通过rgb区域和顶点放大的alpha区域的r值进行合成。渲染出底图。
fragment shager 元素色值srcRgba 和图形蒙层maskRgba进行合并
使用opengles使用混合处理进行元素透明度叠加绘制。
结语
代码只是展示了渲染的一小部分,我们还加入了vbo vao等opengles成熟的顶点缓存技术,解析数据JSON收据的优化,支持了列表播放。
我们在2.0的beta版本中已经实现了视频解析器和渲染器、整个音频播放器都迁移到native层中,数据不需要多次解析,调用多进程可以跨进程减少压力。
当然我们遇到了很多问题不同的问题,也是这些问题,让我们有了前进的方向,谢谢使用我们开源sdk的同好们。