【尖端干货】H.264编码下直播视频添加水印的优化

2017年4月13日

2016年被称为“中国网络直播元年”,网络直播已成为网络文化领域重要的经营模式和文化市场的重要组成部分。而以“直播造人”事件为节点,直播行业进入了严格规范化的时期,鉴黄、版权保护越来越受到直播行业的关注。在这样的背景下,2016年4月,北京网络文化协会发布了《北京网络直播行业自律公约》,规定网络直播房间必须添加水印。对于直播行业的工程师而言,直播流添加水印成为了必备技能。笔者曾在宝利通,迈能半导体从事avc、hevc编解码开发工作多年,现在任网心科技高级工程师,负责星域CDN转码相关工作,在此分享一些粗浅的经验,希望对大家有所帮助。

传统水印添加技术的4大坑

在直播视频流中添加水印,最简单的方法是推流端直接添加水印并编码推流。然而这需要直播企业规范自己旗下所有的主播在直播时都必须添加带有自家标识的水印logo,而且需要公司投入一定的研发力量去修改推流工具实现加水印技术。从具体操作和研发上存在一定的难度,因此很多直播企业会对接入自家的CDN厂商提出直播流加水印的需求。

在已经编码的视频流中加水印,就需要把压缩视频流解码为原始视频格式,叠加水印图片到视频中的某个固定区域,然后重新编码推送出来,因此要实现直播流加水印就需要用到视频转码技术。而提到视频转码技术就不得不提到两个开源工程:ffmpeg和x264。ffmpeg强大完善的流媒体处理和x264高效高性能的视频压缩能力以及H.264格式在市场中的广泛应用使得它们的组合成为大多数流媒体工程师在转码时的不二选择。添加水印也不过是ffmpeg在命令行的一组命令而已,几个视频滤镜加x264的几个编码参数就可以很方便的在直播流中加入水印并转推出去。

然而对于CDN厂商来说这真的是很好的做法吗?其实不然,用ffmpeg做转码是一种全解全编的方式,即把直播视频流完全解码为原始视频,然后对叠加水印的视频用x264重新编码,这么做存在以下几个缺点:

1、 源流视频的gop结构被改变。如下图所示,直播客户对于直播延迟和视频流质量要求不尽相同,各家采用的视频编码器不同或者编码器采用的参数不同,导致源流的gop结构不尽相同,从CDN厂商的角度,源流的gop结构是难以从程序的角度判断的,对各家客户做差异化处理也存在较大的困难,而统一用相同的编码参数做转码则破坏了源流的gop结构,使得转码之后的水印流在延时数据上发生改变,有可能达不到客户的要求。

转码前的帧序列

转码后的帧序列

2、 码率无法与源流完全保持一致。目前直播推流大多采用rtmp协议或者http协议传输flv的方式,源流码率往往会写到flv的metadata中。然而在实际的应用场景中,由于客户使用的编码器各不相同,flv或者rtmp流的metadata中可能并没有视频码率的数据,或者根本是一个错误数据,由于目前直播流大多数都采用abr的方式,码率是在实时变化的,这种情况下监控源流码率并通知编码器实时作出改变也是非常困难的。即便metadata中的码率是正确的,但是abr码率控制模式还需要峰值码率参数,同时前边提到由于gop结构完全被改变,输入输出视频的同一帧可能使用不同的帧类型,使得转码流与源流码率时刻保持一致也是不可能的。

3、 源流视频质量存在损失。源流质量的损失主要来自于两个方面:

(1)gop结构发生改变。编码器在编码时考虑到不同帧类型的重要性不同,为了提高视频流的整体质量,通常会给I帧分配较小的QP参数以保留较高的图像质量,P帧次之,B帧分配较大的QP参数,图像质量相对来说最差。而经过转码后输入输出视频的同一帧可能使用不同的帧类型,源流的I帧可能变为P帧甚至B帧,原来的P/B帧可能被转码流用作I帧,从而损害视频流的整体质量。

(2)二次编码损失。转码后图像重新进行编码,每个宏块重新进行模式选择、运动估计和量化,使得编码后图像质量下降。

4、 视频转码的计算量太大,对转码集群的投入会非常高。全解全编的视频转码方式所需要的计算量是非常大的,一路1080P30,3mbps的码流可能需要3到4个核进行处理,如果客户所有的流都需要加水印处理,相当于所有的源流都要做转码,需要对转码集群投入大量的服务器做运算,成本大幅增加不说,高负载下直播流的卡顿率也有可能大幅上升。当然有些厂商可能会采用GPU或者FPGA专用硬件的方式来做转码,虽然可以缓解转码服务器负载,然而成本也是非常高的,而且也避免不了之前的三个问题。

仅仅为了叠加一块小小的水印图片,需要用到这么高的计算成本,而且可能只是取得一个差强人意的结果,恐怕再也没有比这更费力不讨好的业务了。难道为了一块小小的水印就必须要把整个视频全部重新编码?就不能只对水印区域重新编码吗?

优化方案:在保证图像质量的前提下大幅提高编码速度

由于目前市场上绝大多数的视频流都是用H.264标准编码的,熟悉H.264的人应该知道,与之前的编码标准如MPEG-2、H.263相比,H.264在很多编码技术上都是高度依赖上下文的,如帧内预测,mv预测,甚至残差的熵编码,前边宏块的编码模式或者残差发生改变,都会影响到后边宏块的编码,这么看来似乎还真的要把整个图像重新编码一次。

当然,H.264也的确有些办法把某些区域隔离开来,比如按照宏块raster scan的顺序,把包含水印的区域部分作为一个slice,不包含水印的区域作为一个slice,不同的slice之间编码是完全独立的,不可以互相参考,这样可以在转码时只把包含水印的slice重新编码。另外H.264可以采用slice group技术,其中一种模式是把图像分为fore ground和back ground两个slice group,不同的slice group之间编码也是完全独立的,水印部分可以放在fore ground这个slice group里。但无论哪种方式,都是要在推流端事先配置好编码参数的,slice group技术很多H.264编码器并不支持,所以需要直播企业开发专门的编码器来支持这种场景,同时也需要跟主播约定好推流时的参数,如果这样,那不如直接在主播推流时直接加上水印再推流更来得方便,因此这种方法并不可行。

在视频转码领域,利用源流解码信息加速编码器决策,在保证图像质量的前提下大幅提高编码速度是一种比较常用的方法,而加水印的业务场景则非常适合使用这一方法。我们可以只对水印相关的宏块重新编码,即只对水印区域覆盖的宏块和参考了水印区域的inter宏块重新做模式选择和运动估计,而其他与水印区域不相关的宏块则可以使用原有的编码模式、运动矢量和量化参数QP等等。由于水印图片通常只占视频中很小的一部分,这样大部分宏块都不必重新做模式选择和运动估计,既可以节省大量无谓的计算,又可以保证加水印流的帧类型与源流完全一致,码率非常接近源流,对图像质量也几乎没有任何损伤,连编码器的码率控制模块(RC)都可以去掉。

当然要实现这样的策略,开发量还是不小的,难点也很多。

首先我们需要重新开发编解码器,使得编解码器之间使用完全一样的数据结构来保存同类的宏块信息,如宏块模式、运动矢量、QP等等,这样才可以方便的在解码器和编码器之间传递和共享编码信息。

需要传递的信息可分为序列级、帧级(或者slice级)和宏块级,如下图所示:

解码器到编码器的信息传递

其中最复杂的一步,是宏块级解码信息的传递,包含前边所说的宏块模式、运动矢量等信息,因为源流的这些信息是经过了推流端编码器决策之后选择的,可以认为对当前图像就是最优的选择,除了水印区域相关的宏块,其他的宏块都可以拿来直接进行编码。

下面三幅图是新旧加水印法对某一帧测试视频加水印前后的模式和mv信息图,第一幅图是原始测试视频,第二幅图是完全转码加水印的视频,第三幅图则是改进后的加水印方法得到的视频。

原始测试视频

完全转码加水印的视频

改进方法加水印的视频

通过对比可以看出改进的方法完全基本使用原有的编码信息来生成水印视频,而原有的完全转码方式得到的视频跟原有视频相比则变化非常大。

然后要进行水印区域相关的宏块的二次编码,这部分宏块由于视频数据发生了改变,需要编码器重新决策以选出最优的模式和mv信息。这部分的工作包括水印图片覆盖区域的宏块和参考了水印区域的inter宏块位置的确定以及编码器宏块模式选择所需要的初始化过程。

此外,实现这种加水印方式的最大难点在于源流的不确定性。由于H.264编码规范支持的编码特性非常多,如果源流中包含自己编码器所不支持的特性,编码时则会在输出流中出现花屏甚至导致程序崩溃,因此一方面需要编码器尽可能支持更多的编码特性,另一方面在遇到不支持的编码参数时能够识别出来并回退到普通的完全转码加水印的方式来更完善和稳定的支持线上的业务。

实际应用:

通过对三种类型游戏的流:英雄联盟,穿越火线,守望先锋分别进行测试,测试机器环境为24核cpu:E5-2630 v2 @ 2.60GHz。在解码器编码器都只开一个线程的情况下性能数据如下表所示:

优化后转码的性能基本提升了一倍左右,其中英雄联盟性能提升最小,守望先锋提升最多。究其原因是因为英雄联盟画面变化和运动剧烈程度相对是最小的,而优化完的水印程序对运动剧烈码率较高的流改善最为明显,所以穿越火线和守望先锋这种fps游戏改善最为明显。

此外对不同帧率的流进行测试,发现随着视频帧率增加,视频获得的性能增益逐渐下降,因为帧率越高的情况下一方面本身需要更多的计算,另一方面相邻帧之间的相关性增加,使得帧间的变化越来越小,所能取得的增益自然会下降。

在测试机器环境下,线上绝大多数流用两个编码线程即可满足要求,少数高码率高帧率的流,三个线程可以确保满足要求。

再来看输出水印流的图像质量对比,测试用例还是上述的三个游戏视频,结果如下表所示:

可以看到优化后图像质量有了非常明显地提升,结果符合算法的初衷。