存档

文章标签 ‘x264’

[笔记]低码率H.264视频心得

2010年2月8日

86那接了点私活,大体上就是帮某些有需求的客户做些适合于streaming的视频。由于Target platform是flash v10搭载的电脑,于是那个老掉牙恶心死人的VP6/7就被我直接pass了,由于有文件大小的要求这个问题最终演化成了一个低码率H.264视频的实践问题。由于组里的480p双兼容mp4也算是典型的低码率应用,所以略作些整理吧。对我而言,perferred的toolchain是avisynth+x264+ffmpeg,当然我不用ffmpeg编码,只是拿来mux flv而已。

首先来看Video部分,avisynth+x264的黄金搭档也正是我最熟悉的两个工具。考虑到处理的视频大多都是实写(PAL),因此avisynth中只需要稍微做点降噪,再来点锐化就好了。我选择的方案是dfttest+LSFmod,dfttest用默认参数轻轻一抹,LSFmod的参数上也尽量做到柔和即可。此处值得注意的是由于最终目标码率很低(<250k@CIF),因此任何锐化(High Emphasis)的操作都可能会导致最终产品的画面布满玻璃渣,所以锐化滤镜的选择和参数的使用需要特别注意。我的LSFmod里strength只用了55。
源滤镜是处理这种千奇百怪视频的重要绊脚石,好在有ffmpegsource2这种通吃滤镜,什么VFR我才不管呢,反正都是PAL的实写,统统喂给它就能搞定了。只是要注意有时候源是yuv444的,所以出现奇数分辨率也一点都不奇怪了。

下面来看x264的参数,我的理念是在bitrate budgets下获得最好的(主观)质量,因此基本的rc方案毫无疑问的使用了1pass CRF+2pass Bitrate的方法。这样既能控制最终码率,又不至于被码率限死而影响质量。好在这个任务没有对播放端的解码延迟有什么要求,于是可以尽情的用些容易导致latency的参数了。
在确定rc方案之后,下面就是挑出一些参数来做优化了。我把参数的优化分成五个部分:ME、降码率、除块、帧类型、兼容性。
//顺便一说,我用的--crf 20的1pass。

首先是me,由于低码率的限制,精确的me(与之相对是更小的误差值)是必不可少的,相反在码率很充足的情况下me的精确性到来的不是那么重要了。我使用的是--me tesa --subme 10 --trellis 2 --merange 48的方案,tesa保证了尽可能精确的ME,subme 10保证了子块预测/细化时的准确性,merange 48相对于CIF来说也足够大了。

其次是码率的控制,开大mbtree的lookahead和降低qcomp数值是一个好方法,我是用的是--rc-lookahead 100 --qcomp 0.5的配置。有人可能会提到aq-mode 2,但我实际的感觉,aq-mode 2在这种极低的码率环境下会出现恼人的mosaic effect,所以aq-mode还是坚持使用mode 1,strength=1.0的默认设置吧。

然后是除块的参数,在前面avisynth的参数选择上我就说了锐化参数必须小心,x264里关于锐化/除块的参数有二,psy-rd和deblock。psy-rd的strength越高,玻璃渣就越明显(低码率下),deblock的负值则被认为有助于恢复锐利的线条和texture。所以对我这种环境,两者的效果都应该降低。我选择的是--psy-rd 0.2:0 --deblock 1:1的配置。

最后是帧类型和兼容性,这两个分类间相互的依赖性比较大,因此联合起来考虑。对流媒体而言,考虑到分辨率的大小,--profile main --level 3.0的设置足矣。ref 3和bframes 8则完全是个人喜好,如果要ep的话bframes 16也是可以的。--partitions b8x8,p8x8,i4x4,i8x8也是个人喜好,而且在level=3.0时这也就等于all了,我没有考虑把i8x8排除在外,因为target platform是标准终端因此没有必要。
vbv-bufsize和maxrate完全是为了限制1pass crf可能的飚码率问题,我定在了1600和1200。keyint和min-keyint则是一如既往的fps*10和1的配置。

1pass完整参数如下:
x264 –profile main –level 3.0 –pass 1 –crf 20 –stats “proc.stats” –min-keyint 1 –keyint 250 –ref 3 –bframes 8 –b-adapt 2 –direct auto –partitions none –no-fast-pskip –no-dct-decimate –vbv-bufsize 1600 –vbv-maxrate 1200 –rc-lookahead 100 –qcomp 0.5 –psy-rd 0.2:0 –deblock 1:1 –me tesa –merange 48 –subme 10 –trellis 2 –sar 1:1 –thread-input –threads 6 -o NUL proc.avs

2pass的完整参数如下:
x264 --profile main --level 3.0 --pass 2 --bitrate 340 --stats "proc.stats" --min-keyint 1 --keyint 250 --ref 3 --bframes 8 --b-adapt 2 --direct auto --partitions b8x8,p8x8,i8x8,i4x4 --no-fast-pskip --no-dct-decimate --vbv-bufsize 1600 --vbv-maxrate 1200 --rc-lookahead 100 --qcomp 0.5 --psy-rd 0.2:0 --deblock 1:1 --me tesa --merange 48 --subme 10 --trellis 2 --sar 1:1 --thread-input --threads 6 -o proc.264 proc.avs

在x264 rev.1376下实际跑的结果,crf 20的1pass在上述参数下基本码率不会超过400k,于是2pass的bitrate模式限制就比较宽松了。基本*0.75就能得到适合的2pass码率。

音频:
音频部分用ffmpeg处理,通用格式就是
ffmpeg -i somefile.ext -acodec libfaac -ab 64k oputfile.aac
有时ffmpeg的编译没有开启外部libfaac.dll的支持,解决方法要不就自己configure然后make要不就去找其他人编译的带libfaac支持的binary咯。
另外此处用的是average bitrate模式,也可以用-q这种质量模式,目标64kbps的音频q的数大约是0.25。

Mux:
继续ffmpeg:
ffmpeg -i somefile.mp4 -vcodec copy -acodec copy oputfile.flv

于是就大功告成了。

admin 技术宅区 , , ,

x264 ratecontrol小记+upconv的陷阱

2009年12月13日

任何编码器在工作过程中都隐含着这样一个优化问题,即在没有明显观测区别(给定质量要求)的情况下,优化最终文件的大小(Rate-Distortion)。RD常常被细化成两个优化子问题,其一是模式选择,其二是ratecontrol也就是码率控制。在给定标准的前提下,模式选择从性能上来说没有太多可优化的,因此码率控制就变成了最能体现编码器设计者的智慧的部分,任何优秀的编码器之所以优秀的原因,很大程度上是因为它内部的码率控制算法先进,优化得当。为了便于理解,我们用数学的语言重新描述下RD这个大框架:

已知一种质量损失算子J,选择合适的预测模式,在给定目标文件大小的情况下,使得J最小。
(Given a distortion metric J, find appropriate estimation method, such that the J is minimized under the condition of assigned bitrate.)

x264是遵循H.264/AVC标准的性能最优的开源编码器,在x264 source的doc/ratecontrol.txt中有一篇关于x264中使用的各种码率控制的粗略介绍。说是粗略只是因为他没有涉及具体的算法细节,而是从理论框架的角度讨论了x264中各种码率模式下完成的工作,下面我就用自己的语言稍微概括下这个文档大体的内容:

码率控制的理论基础(或者说公理吧):
1. 相同的qp不一定代表着相同的主观质量、同样相同的PSNR也不代表着相同的主观质量。前者表明的是qp与质量之间不能互相指代,后者则说明了psnr这种质量算子与主观质量之间的偏差。
2. 给运动估计做得好的帧分配更多的码率有助于提高整体质量。这是因为GOP和IBBBP这种链式结构的存在而产生的,一个显而易见的推论是如果想改善整体质量,对I和IDR的预测下手是更有效的。
3. 已知一帧的编码模式(预测模式),可以预测出不同qp取值下所需的码率,当然这种预测会随着qp分布的离散而变得不精确。
4. 帧的重要性与它被参照的次数成正比。基于此,我们能看到的一个常见qp分布就是Iqp>Pqp>Bqp。

码率控制的框架:
x264下码率控制就是三步走,根据不同的模式每步具体干的活不一样,归纳起来这三步就是:
1、根据复杂度分配qp
2、根据模式特性重调qp
3、码率溢出控制

x264中使用的码率控制模式:
1. 2pass mode
对应于–pass 2 –bitrate xxx –stats “xxx.stats” stats文件由ABR的1pass生成(crf亦可)
此时的优化问题可以归结成选择qp使得质量最优同时达到指定的码率。具体实施时分成三步进行:
在2pass开始前,用经验公式(复杂度^0.6)决定一些帧的码率,复杂度是由1pass的预测结果得到的(所谓恒定qp时的码率)。
之后根据这个结果,进行调整以达到指定的最终文件大小。如果有vbv选项,则vbv选项中的指定值也会成为调整的一个依据。
最后开始编码,此时有两种补偿机制作用,防止码率溢出:
远期补偿机制检查的是以当前qp编码全部帧时最终大小与指定大小的差,并以此更新之后编码帧的qp。如果发现一直偏离指定大小,则存在一个强制调整机制以纠正这个偏离(乘上偏差值的倒数)。
近期补偿机制则是防止在编码开始和结束时码率偏离指定值太多而存在的。

2. 1pass ABR
1pass与2pass最本质的差别在于2pass时编码器已经对全片的复杂度走势了然于心,但1pass则一切未知,因此1pass时复杂度是由预测得到的。
所谓复杂度的预测,本质上还是一次运动估计过程,只不过我们不需要非常精确的估计,x264里使用的是downsample后的“缩略图”进行估计,算法使用的是SATD。
另外,对I帧qp分配的奖励机制,也是基于之前已编码I帧获得的信息来实现的。
再者,对qp的重调也只能通过之前已编码帧获取的信息来进行。
防止码率溢出的机制与2pass相同。

3. CRF
crf的全名是constant rate factor,这里说的rate factor,就是前面两种模式下第二部重调qp所用的factor。知道了这点CRF也就不难理解了:
CRF的第一步与1pass ABR一样,同时没有防止码率溢出的机制(没有第三步)。CRF的第二部也就是重调qp时使用的是使用者指定的rate factor而不是编码器自己完成的。

4. 1pass CBR和constant quantizer
前者的第二步中重调qp使用的factor是由vbv缓存中存储的所有帧均值获得的,前者的第三步中使用了某些硬阈值条件。
后者则非常简单,由于x264是基于帧做码率控制的,因此只是对每种类型的帧指定一样的qp罢了。

————
upconv的陷阱:

源起是蛇妹妹告诉咱的一个地方,看到fancy的频谱图不禁心情大好,在matlab下依样画葫芦的实现了一番,matlab的代码如下:


src2 = imread('sample2.bmp');
g = rgb2gray(src2);
dst = imresize(g,[1080 1920],'lanczos3');
dst_spec = fft2(dst);
imshow(log10(abs(fftshift(dst_spec))+1),[]);

于是拿soraoto试了下,果然如同anibin所言,480制作确定
soraoto

至于喷不喷,咱文笔不好,有请蛇妹妹吧XDD

admin ACG, 不是A片的那个A, 技术宅区 , , ,

X264 rev1198 MB-Tree Ratecontrol加入

2009年8月8日

The rest, as they say, is the history.

用这个形容倒也不算过分了。
git repository的链接在此http://git.videolan.org/gitweb.cgi?p=x264.git;a=commit;h=bb66c482242a0747823661b212114c1a2f015fe3

DS大人还兴奋的写了篇ep感想文
A Tree of Thought
嘛 当然我就不吐槽你这装文青的心态如何了…

不过我感兴趣的是MB-Tree的实现原理,来看下ds的原文

About a year and a half ago, I had an idea: what if we made a graph of how each block of the video referenced other blocks temporally and used this graph to increase quality on blocks which are referenced a lot and lower it on those which are referenced less?

此处是说如果能记录并整理各帧内MB被参考的频繁程度的话,或许能通过这个频繁程度反馈的信息,优化编码各MB时bit allocation的策略,从而达到改善质量的效果。
这点不难理解,如果一个有偏差的(比如承载着错误的图像信息)编码MB,被频繁的作为参考帧使用,那么这个参考帧的错误会传递到所有使用该块做估计的MB上,于是形成了累加恶化效应,这也是后面ds所说的propagation的来由。

My guess was it had a simple 2-pass heuristic for I-frame quantizers; low-motion scenes would get higher quality I-frames and high-motion scenes would get lower quality I-frames; it seemed pretty straightforward. The way I decided to do it was via a concept I called “propagation.”

这里说的是Mainconcept中对I帧用的一种渐进式量化算法,低运动场景会得到高质量的I帧,而相反,高运动场景会得到低质量的I帧。
这个策略其优势也是不难理解,低运动场景大量的MB会被重复运用到后续编码块的prediction中,这种方式保证了这些块的误差相对较小,从而降低了错误累加恶化的速度。对大动态场景,运动的部分往往本身就是帧内预测块。(当然这由MSE决定)
另外此策略的优势来自于人眼,人眼对运动场景的辨识能力是随着运动频率递减的,而受视觉暂留的影响这种递减的速率会随着运动场景的逐渐加快而加速,因此人眼是很难辨认大动态场景中错误,选择较低的码率来编码大动态场景,这也是符合人主观质量的做法。

Clearly, the propagation method is just another way of implementing the basic concept of qcomp: frames whose data doesn’t propagate far are basically high complexity, and frames whose data does propagate far are basically low complexity. As a result, I disabled qcomp when testing this idea. And the tests bore me out: there was a significant improvement across most test clips! But on a few clips, especially anime, there was a very significant loss of quality.

As one might expect, in anime, the vast majority of complexity is usually confined to a small portion of the frame–for example, a character walking across an otherwise-static frame. Furthermore, the sharp lines making up the character are much more “complex” than the static background. Thus, it can appear to the propagation algorithm that a series of frames is complex, when in reality only the character’s motion is complex, while the rest of the frame is static. The algorithm then lowers the quality of all the frames, including the background, greatly decreasing quality, despite the fact that it should only have lowered quality on the moving character instead. If only we could apply this propagation algorithm to individual blocks instead of the whole frame…

嗯,用于选择quantization程度来获得优化质量的qcomp参数大家应该都熟知。它和上述mainconcept的精神是一致的,即给低动态更高的码率。但如果只是根据这个原理,将某些帧粗暴的设置成qcomp=1,对于前景后景复杂度迥异的anime来说,是很糟糕的。
于是MB-Tree的想法重现了。
怎么样,就是兜了个圈,看似很简单的原理,但就没人之前想到过么-.-或许去年11月出现在mailist上的这个讨论,给了ds很多启示呢
http://mailman.videolan.org/pipermail/x264-devel/2008-November/005116.html

DS最后给了个地灵殿的例子,弹幕游戏前后复杂度的差别实在太大了,当然MB-Tree很好的完成了这点,SSIM提高了60%。
至于主观的测试,今天太晚了,周末找时间试试吧。

admin 技术宅区 ,

Switch to our mobile site