YYEVA支持动态元素的透明MP4动效播放器 - 客户端渲染篇之Android (七)

2,971 阅读7分钟

大家好,我是苍王。 上一篇给大家介绍了我们近来开发视频特效框架YYEVA项目。这编给大家继续深入介绍一下。

YYEVA(YY Effect Video Animate)是一个开源的支持可插入动态元素的MP4动效播放器解决方案,包含设计资源输出的AE扩展,客户端渲染引擎,在线预览工具。

对比传统的序列帧的动画播放方式,具有更高的压缩率,硬解码效率更高的优点,同时支持插入动态的业务元素;对比SVGA、Lottie等播放器,支持更多的特效支持,如复杂3D效果、描边、粒子效果等,达到所见即所得的效果。

我们发布项目已经有数月,也有很多朋友加入了我们的社群提出一些优化方案,我们陆续在支持。也欢迎大家到 github给我们点个⭐⭐⭐,您的Star是对我们最大的支持。

从这篇开始,我们陆续会发布四篇文章,分别给大家介绍下,YYEVA-SDK是如何解析YYEVA资源,并结合动态元素渲染到屏幕上的。现在,就让我们开始YYEVA渲染之旅吧。

out.gif

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版本,支持多进程播放,但是暂时还没兼容稳定版的一些新功能。

如果想看到Android端对应的源码可以点击这里。

YYEVA-Android 实现之旅

RGB+Alpha混合原理

MP4视频以H264方式编码视频,MPEG-4的颜色采样标准是YUV,YUV是亮度和色度的分量叠加,不支持alpha通道, 因此,如何让MP4视频支持透明度,业界常用的方式是使用两个通道分别进行 存储视频的RGB数据和Alpha数据。

image.png

设计师可以使用我们AE插件生成以上特殊的mp4资源,左边是RGB部分,右边是Alpha部分。然后通过设置不同的拉伸模式,可以在客户端正常展示比例。

为了用户带来更大的全屏特效的震撼感,我们基本选择竖屏使用ASPECT_FIT,横屏

image.png 这个图可以更好的了解我们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空间。

image.png

YYEVA客户端渲染

YYEVA实现动态元素的方案,是在透明MP4的基础上,结合Alpha区域的混合,将需要插入的动态元素信息,提前解析并保存在一个Json数据中,同时经过编码、压缩处理,写入MP4的Metadata段。客户端在解析的时候,分别提取音视频轨信息和描述信息。

yyeva架构.png 以上是整个YYEVA的基本架构 ,在我们的工程里可以分别对应上类名来进行检索学习。

数据解析.png 前面说过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

  1. descript: 描述该资源的整体信息
  2. effect : 描述 该资源下的所有遮罩相关信息
  3. datas : 描述 每一帧遮罩的位置信息

通过这个描述信息,在渲染每一个动态元素的时候,可以知道该动态元素在每一frameIndex的遮罩位置outputFrame,以及在画布上的位置renderFrame

该Json信息,在客户端渲染SDK中,会模态化成对应的Descript, Effect Datas对象,因为数据运用减少反射,我们java和C++层都解析了对象。 C++中使用了parson库,这个比较轻量推荐大家使用。

YYEVA渲染流程

image.png YYEVA整个渲染的流程可以通过上图流程完美呈现出来,渲染的代码都是在Native层。

image.png

以上是整个流程的简单类逻辑。

image.png 这是透明mp4合成的逻辑,通过rgb区域和顶点放大的alpha区域的r值进行合成。渲染出底图。

image.png

fragment shager 元素色值srcRgba 和图形蒙层maskRgba进行合并

image.png

使用opengles使用混合处理进行元素透明度叠加绘制。

结语

代码只是展示了渲染的一小部分,我们还加入了vbo vao等opengles成熟的顶点缓存技术,解析数据JSON收据的优化,支持了列表播放。

我们在2.0的beta版本中已经实现了视频解析器和渲染器、整个音频播放器都迁移到native层中,数据不需要多次解析,调用多进程可以跨进程减少压力。

当然我们遇到了很多问题不同的问题,也是这些问题,让我们有了前进的方向,谢谢使用我们开源sdk的同好们。

希望可以为我们的项目 YYEVA 点上一个⭐⭐⭐,您的支持就是我们一直更新文章下去的动力哈。希望大家多多鼓励