x264 教程 HTML 完整版

欢迎阅读!本教程仅算盲人摸象的业余分析,仅具备业余参考价值。若有什么不会的可以加群691892901

地图

  1. x264 教程网页版(你在这里)
  2. x265 教程网页版
  3. 附录 α: QAAC 音频压缩教程Github 副本
    • 下载安装,用例,Pipe 导入,流格式,封装格式,多声道编辑,压缩强度与占用,章节处理,缓入缓出,音频标识,音频编辑,滤镜,已知报错,多参数储存和调用,采样率的选择,采样率的验证
  4. 附录 β:x264,libx264,x265,libx265 压制教程急用版
  5. 附录 γ:ffprobe 教程Github 副本
  6. 附录 δ:ffprobe + Excel 绘制码率曲线,帧类型饼图
  7. 附录 ε:交错转逐行与 IVTC 处理
    • 逐行与交错扫描,帧率与场率,仅交错,A:B Pulldown / telecine,2:2:2:4,3:2:3:2,Euro-pulldown,0.5:0.5,特殊情况,去交错的算法与滤镜,IVTC 滤镜用例,p/c/n/u/b,AviSynth 例,TFM-TDecimate 例,VapourSynth 例
  8. 附录 ζ:图像序列的处理
  9. 相关:mpv 播放器的安装与设置

打印支持

推荐设置:彩色模式、不打印背景、0.04 英寸边距、打印页眉页脚

点击图片即可轮询三种图片大小。窗口宽度大于窗口高度则切换到宽屏/桌面端/平板电脑排版,否则切换到窄屏/移动端排版。

安卓 12 或更高版本中,如 Edge,Brave,Kiwi,Arc,Dragon,Opera 的 Chromium 内核网页浏览器有着中文字体过细的显示问题(谷歌早已知晓却从未修复)建议使用 Firefox 以确保舒适的观看体验。

必备常识

封装与解封装 - Multiplex/De-multiplex

元数据 Metadata

描述数据的数据,例如食品包装上的标题、成分表、图片、营养信息、教你如何正确打开包装的附注就是一种元数据。视音频封装文件中的元数据并不是视音频本身,但播放器需要其中的一些信息才能以正确的帧率、音视频对齐、色彩标准,使用正确的视频解码器,音频解码器,以正确的打开“包装”。

编解码

数据大小单位

基本单位
二进制千进位
十进制千进位
image1

图:比特,字节与体积时间比的转换

操作系统(处理器、内存)的“字节”是以二进制换算的,而储存容量(闪存、硬盘)的“字节”是十进制换算的。一开始储存容量不大,而 1000 进和 1024 进差 2.4%,不如多卖电脑重要,就有了现在不统一的容量称呼

码率/比特率 Bitrate

文件体积每秒,单位 bps,Bps,Kbps,KBps,Mbps,MBps,Gbps,GBps 等。“ps”即 per second,也可写作“Kb/s,Mb/s”等。

色彩

人眼感光细胞捕获特定强度的电磁波(可见光),然后通过大脑处理,产生了我们所看到的幻觉。实际上,色彩只有一个标量属性,即频率。

image2

图:色彩的波形频率变化,见维基百科

亮度

YUV / YCbCr 色彩空间图像的亮度平面 Y,或者 RGB 图像分离出的亮度/灰度平面,有时也被称为伽马平面或 Y 平面。

灰度/灰阶 Grayscale

代表将色彩信息去除,只保留黑、灰、白色的单通道图像。

硬件的色深 Depth 与范围 Range

数字电路中软硬件亮度从黑到白之间过渡的细分密度。由显示器的驱动板/主控板接收 GPU 输出的数字信号,并转义为像素点的电压得到。因此使用了 CRT 显像管的电视可能是模拟电视,也可能是数字电视,取决于有没有如 HDMI,DP 等数字信号接口。模拟电视使用 \( 0 \text{mV ~ } 700 \text{mV} \) 的输入电压表示色深。在当时考虑到电视台调制偏差,以及干扰可能会映射出 \( >700 \text{mV} \) 的烧屏强光,而通过限制了色深密度的范围来解决问题,得到了有限范围。

硬件层兼容有限/模拟/电视范围 Limited/Analog/TV

计算机擅长使用二进制,因此数字色深的分阶用位/bit 表示。如 4bit 16 深 \( 2^4 \)、8bit 256 深 \( 2^8 \)、10bit 1024 深 \( 2^{10} \) 的形式呈现:

硬件层完整范围 Full/PC

软件的色深 Depth 与范围 Range

数字电视的 Bt.601 色彩标准考虑了数字与模拟电视颜色的一致性,而定义了兼容有限 Limited-compatible 的色深密度,导致现在使用 Bt.709,Bt.2020 色彩标准的媒体(支持完整色深)中,有限范围仍是绝对主流。

软件层兼容有限/模拟/电视范围 Limited/Analog/TV

软件层完整范围 Full/PC

色深的错误

输出设置为有限,未经 Y/C 转换,连接到设置为输入完整范围的显示器,显示器缺失最暗和最亮的范围,呈现灰蒙蒙的画面,例如:

输出设置为完整,未经 Y/C 转换,连接到设置为输入有限区间的显示器,显示器忽略 8bit 下 0~15 和 235~255 的亮度范围,出现画面过亮过暗(饱和度过高)的画面,例如:

yuvj 色彩空间中的 j 被误解,而使用了无效或错误的转换处理导致

heic 格式的图片被错误转换到 jpeg 或错误显示导致,一般出现在手机上,以及 Windows 11 的图片浏览器中

HDR 视频在设备没有正确配置的情况下直接显示导致

image3

图:完整色深 Fullrange 与有限/TV 色深 Limited,包括上述说明的更优版本见硬件茶谈

色深的转换

ffmpeg 转换到完整:

ffmpeg.exe -i "limited.mp4" -vf "scale=in_range=limited:out_range=full" -color_range 2 -pix_fmt yuv420p -movflags +write_colr "full.mp4"

ffmpeg 转换到有限:

ffmpeg.exe -i "limited.mp4" -vf "scale=in_range=full:out_range=limited" -color_range 1 -pix_fmt yuv420p -movflags +write_colr "full.mp4"

伽马曲线/伽马映射

伽马矫正

两种方式:向上提亮缩放“白化” \( s = cr^\frac{1}{\gamma} \) 和向下变暗缩放“黑化” \( s = cr^\gamma \)

image4

图:伽马矫正的指数算式,此处c = 1代表 0~1 的亮度区间,8bit 256 深则c = 256,见desmos 互动例

对比度 Contrast

通常情况下,表示对音量、冷热、远近等两种程度的差异大小缩放。在图像/显示器中,对比度则代表最亮和最暗的显示亮度差异。图片的对比度低会显得发灰,因为当前的亮度设置位于这个灰度水平上。

人眼在黑暗中观看同样的视频时,有些场景会显得过亮,有些场景则会显得太暗。这种情况下,降低对比度即可解决该问题;而在阳光下观看同样的视频时,即使将手机电脑调到最大亮度,也无法清晰看到暗部场景。这种情况下,降低对比度并增加亮度即可解决该问题。

售卖手机的厂商可以考虑让所有用手机看视频的人都想要换手机:只需在下拉状态栏的亮度滑块条 Brightness 上增加一个“暗度 Darkness”滑块:

饱和度 Saturation

一般指液体在特定压力和温度下溶解了某种成分的程度,达到最高时会沉积而不再溶解。图像中则代表颜色接近 100% 纯色的程度或“彩度”,饱和度越高则图像越鲜艳,为零时得到灰阶图像;最大时得到纯色图像。对比度接近饱和度,但不是饱和度。

像素 Pixel / pix / pel / px

像素是组成图像的最小单位,决定了图像的物理大小和分辨率。一般情况下分为屏幕的硬件像素大小,和图片/视频的软件像素大小两种。当软件像素小于硬件像素时,显示 100% 大小的图片会变小,或者通过缩放滤镜将图片放大以适应屏幕。反之,如果硬件像素大小小于软件像素,则会使用缩放滤镜将图片缩小至合适的尺寸,或者图片会溢出屏幕。图像中的每个像素都有位置和颜色两份信息。

每英寸像素 Pixel Per Inch / PPI

指屏幕或印刷设备上的物理像素密度,这取决于设计时预期的观看距离。屏幕的 PPI 高则适合近距离观看,因为物理像素更密集。在印刷设备的 PPI 越高,打印出的图像就越细腻。软件的 PPI 主要用于计算新建图像的像素大小和分辨率,以确保图像有着适当的大小和清晰度。

位图 Bitmap 与矢量图 Vector

位图使用像素和元数据(例如 n 个像素后换行)来表示图像。每个像素都包含特定的颜色信息,通常表示为 RGB 或 YCbCr 值。用于描述照片和复杂图像,但当放大时会失真,因为像素的信息已经固定,而放大只是用滤镜在其中插值填空的结果。

矢量图使用图形函数代码(例如 JavaFX,SVG)来描述图像。它们以函数定义图像的形状、线条和颜色。矢量图可以被放大而不失真,并且可以在不同的尺寸和分辨率下保持清晰度,因为它们是基于数学公式的。

色彩空间 Color Space

用于表示和处理图像颜色的数据格式,具有适用于各种不同应用场景的多种类型。最常用的是分为硬件和操作系统的 RGB 色彩空间,以及内容的 YUV/YCbCr 色彩空间。见 Captain Disillusion 的科普。

image5

RGB 色彩空间由红色 Red、绿色 Green、蓝色 Blue 三个通道组成,每个通道的数值表示相应颜色的强度,对应着显示器红色 LED,绿色 LED 和蓝色 LED。常用于显示器、摄像头和图形处理等硬件和软件系统中。

YUV/YCbCr 色彩空间是另一种常见的色彩表示方式,用于存储和传输视频数据。Y 代表亮度 Luminance,而 U、V 代表色度 Chrominance。YCbCr 特指数字信号,YUV 特指模拟信号,再无区别。由于后者字数更少且不需要频繁切换大小写,打字更容易,所以 YUV 拼写后来逐渐被当做讨论用的“平替”。

🎆色彩格式 构成色 特点 存在原因 支持范围
RGB 红绿蓝 通用 使显示器/照相机通用 几乎所有可视媒体
ARGB α 红绿蓝 透明通道 便于图层化编辑 图片,部分视频
CMYK 湛 zhàn 洋黄黑 减法色彩 卖墨盒 打印纸
YCbCr/YUV,YPbPr 白蓝 - 黄红 - 绿 压缩 压缩视频图片 所有有损压缩

色度采样 Chroma Subsampling

YUV/YCbCr 色彩空间下,利用人眼对色度细节不敏感的特性(见下图全分辨率和半分辨率的色度面),降低色度平面的分辨率,从而压缩图像。色度采样格式通常用三个数字表示,例如 4:4:4、4:2:2 等。

A:B:C 代表微观分辨率下,每个长 A 像素,高 2 像素的空间,第一行缩到 B 个色度像素,第二行缩到 C 个色度像素。如 4:2:2 代表色度平面的每个 4x2 微观区间中,第一行的色度分量保留 2 个像素,第二行的色度分量也保留 2 个像素。色度采样详见下表:

👾采样 1920x1080 下宏观 色度 4x2 微观 特点
4:4:4 亮度,色度皆 1920x1080 色素 色素 色素 色素 逐行,读写亮色度而不用从 RGB 转——剪视频快
色素 色素 色素 色素
4:2:2 亮度不变,色度 960x1080 色素 ← 色素 ← 逐行扫描,颜色像素靠插值还原。直接读写亮度所以剪视频比 RGB 快
色素 ← 色素 ←
4:2:0 亮度不变,色度半宽半高 960x540 色素 ←
↑       ⬉
色素 ←
↑       ⬉
4:4:0 亮度不变,色度全宽半高 1920x540 色素
色素
色素
色素
4:1:1 亮度不变,色度 1/4 宽全高 480x1080 色素 ← ← ← ← 交错扫描 Interlaced,横着扫就不会把另一场的帧参考而浪费算力了
色素 ← ← ← ←
4:1:1-par4:3 亮度 3/4 宽全高,1440x1080 色度 3/16 宽全高,360x1080 色度像素 5.333×←
色度像素 5.333×←
4:2:0-par4:3 亮度 3/4 宽全高,1440x1080 色度 3/8 宽半高,720x540 2.667 ←
↑ 2.848 ⬉
2.667 ←
↑ 2.848 ⬉
逐行扫描,非方形亮度像素,par 通常是 4:3 要不然太烧脑了
image6

图:RGB 与 YCbCr 的转换,色度采样的使用及编解码与录制,传输,显示的关系

压缩的策略

根据硬件的算力和带宽两种限制选择有损压缩,无损压缩和未压缩。一种(图/视/音频)编码可集成全部三种策略。

压缩强度 带宽负载 解码负载 硬盘损耗
未压缩 最高 最低 最高
无损压缩
无损压缩 中~高 中~低
有损压缩
有损压缩 最低

只要录制,剪辑,调色,混音,导出中一个环节用错了录制设备,采样率/色深,色彩空间,分辨率,像素宽高比,颜色混合,伽马映射等设定,损失就已成定局,此时选择有损或无损编码就不再有意义。

视频压制

视频压制是视频编码器用户追求画质、压缩率和速度的逻辑。很多人一看到硬件编码器速度快,就忽视了软件编码器的高压缩率。

视频压制三角定律

确定编码器好坏,压缩和采购策略的方法。专用编码器常用于单一目的,但难以控制另外两端的损失。高算力适合通用策略,而低算力则适合专用策略。

image7

图:三种出发点构成。其“完美中心点”不一定符合当下需求,但决定了是否能满足所有情况

音频格式(一维)

最简单的结构为——脉冲编码调制Pulse-code modulation PCM信号

🎶压缩 格式 音质
有损 mp3,aac,ogg(vorbis/opus) 取决于耳机/音响,混音,声场。分为 HiFi 音频和监听音频两种质量参考
无损 flac,alac,ape,it
未压缩 wav(PCM)

图像格式(二维)

最简单的结构为 PCM 信号 + 每 n 个像素换行的元数据

📷压缩 格式 最大 RGB 色深 最大 YCbCr 色深 最大 CMYK 色深 动图 HDR 透明
有损 jpg 8 8 8 8 8 8 24 24 24 24
有~无损 gif(编码无损,颜色少了) 24×3 中取 8×3 仅 1bit
webp 8 8 8 8 8 8
jpg-XR(兼容 jpg) 32 32 32 8 8 8 16 16 16 16
avif,heif/heic,filf 32 32 32 16 16 16
无损 pdf,jpg-LS,png 16 16 16 32 32 32 32 仅 mng
有损~无损~未压 tif 16 16 16 8 8 8 128 128 128 128
未压缩 raw,bmp 24 24 24 仅 raw
dpx 64 64 64 16 16 16

视频格式(三维)

最简单的结构为一组图片。

🎥压缩 格式 RGB/YCbCr 色深 HDR 透明
有~无损 qt,hevc,avc,vvc,vp8/9,DNxHR/HD,prores 12 12 12 除 avc,vp8 仅 prores
无损 rawvideo 32 32 32
cineform 16 16 16
有损~无损~未压 Flash 动画 内嵌图片决定

动图 vs 视频 vs 图片序列

图片与视频之间还有图像序列,动图两种中间格式。图像序列直接储存视频为大量图像,一般用于录制素材;动图如 .gif,.mng 则表面上和视频一致:

设计\格式 动图编码 视频编码 图片序列
播放用时 短(GOP = 1) 长(GOP >= 1)
帧率 随便 随便 随便
分辨率 随便 随便 随便
帧间冗余 一般不支持 一般都支持
需要封装 一般都需要

滤镜/滤波器 Filter 与滤镜工具

通常用于过滤水和净化空气。在信号中代表处理一维音频和二维视频信号的算法。滤镜工具代表能够将滤镜应用到信号上的软件或插件,比如 Adobe Premiere Pro,After Effects,ffmpegDaVinci ResolveScreenToGIFFL StudioReaperAbleton,Audition,GIMP,Photoshop,FSCapture,LightRoom,OpenCV 等编辑图像、视频和音频的工具。AviSynth(+)和 VapourSynth 严格上称为帧服务器,因为需要连接一个编码器才能导出视音频。

噪声 Noise 与颗粒 Grain

电影胶片(卤化银晶体)被投影灯放大得到胶片颗粒 Film Grain,在单独一帧胶片画面上是一种比较自然静态噪声。更广义且不自然的噪声来自于电视信号接收器或相机使用放大电路 amplifier 放大无信号频段,或黑暗场景产生的“雪花”画面和声音。以及根据厂家镀膜工艺水平而定的显示器屏幕磨砂表面“静态噪声”等等。

一般来说,如果噪点的边角尖锐,缺乏渐变,则属于“难看的噪声”。另一种比较极端的判断方法是——只有影视导演亲手保留,或游戏美工专门根据显示技术适配处理过的噪声才是好看的噪声、由第三方私自添加的噪声是“难看的噪声”。

实际上,人眼的“成像技术”也决定了人看到的画面本来就有很多噪点,而且是动态噪点,只是未注意的时候都是大脑的实时降噪和幻觉在抑制而已。

image8

图:正在放映 2023 年电影《奥本海默》的 70mm IMAX 胶片放映机。源:Nicolas Vega

image9

图:磨砂镀膜与镜面镀膜显示器的微观区别。可以看到没有漫反射的镜面更加“通透”。源:The Display Guy

抖动滤镜 Dither Filter

源自早期操作系统只能显示有限数量的颜色,因此采用了高频闪烁两种颜色的方法来欺骗视觉(副作用是使人感到晕眩)。在图像、音频和低端显示器中,抖动滤镜代表通过分辨率或采样率来换取位深的加噪补偿手段。

具体来说,通过间隔几个像素就调亮/调暗一个像素的颜色,使得宏观整体上看起来像是淡化或加深;在音频位深分量两端的采样点间成比例的(以方形波或三角波信号)快速摆动,从而利用扬声器反应速度,起到淡化或深化偏移宏观上位深的效果。

image10

图:音频的采样点,以及下采样音频,抖动关对比开的效果,见Micoope 科普

image11

图:下采样 8bit 到 1bit,开抖动的效果

抖动滤镜的效果主要是欺骗视觉,但因为效果独特,所以衍生出了 Ditherpunk 抖动朋克美术。典型如 1bit 游戏《奥博拉丁的回归》贯彻了这种风格——利用随机蓝噪声蓝噪声,黄金分割Riemersma 等不同的加噪算法来定义游戏引擎的材质。

image12

图:图像噪声算法

缩放滤镜 Resize Filter

Windows 系统一般用双线性缩放放大 PPI 小的软件,视频视频播放器一般用 bicubic 或 lanczos 插值

临近取样插值 Nearest Neighbour interpolation(NN):
  1. 2x2 的四个像素点移动到缩放后的四个位置
  2. 待插值的像素中,距离哪四个点近的像素就取哪个像素的值
双线性插值 Bi-linear interpolation(Bi-lerp / Bilinear):
  1. 2x2 的四个像素点移动到缩放后的四个位置
  2. 横向和纵向的点(像素值)间各连一条线
  3. 采样得到中间的像素,完成四边的插值
  4. 四边之间接着分为横向与纵向边缘
  5. 横向和纵向的边(像素值)间各连一条线,共六横六纵
  6. 所有线段接着采样得到中间的插值像素点,且在横纵线的两种插值之间取平均
样条插值 Spline interpolation(spline):
  1. “样条”代表软木条,通过铁钉塑形结合木条的应力做出连贯且一致的曲线
  2. 这种性质可以用多项式准确描述,而图像中的“铁钉”就是像素值
  3. 图像中的“木条”就是在 n 个“铁钉”构成的 n-1 个区间里各画一条曲线
  4. 二次多项式插值 quadratic interpolation 代表三个点间用二次多项式插值
  5. 三次多项式插值 cubic interpolation 代表四个点间用三次多项式插值,适用于放大图像
  6. 连起的线必须平滑连贯,数学上叫做“二阶导数连续”
    • 因此,放大一个 2x2 的区间需要 4x4 范围的采样点
  7. 插值的与逻辑和双线性插值一致,同时是三次多项式插值,则是双三次多项式插值 bi-cubic interpolation
Lanczos 插值:

使用 sinc 函数取近似,且引入了窗函数来矫正 sinc 函数,同时要计算 lanczos 核实现插值。计算量大,准确度略高于 bicubic 插值。

神经网络缩放滤镜:

通过认知大量训练数据,在其中找出更高维度规律的情况下,利用这些规律推断像素值。计算量极大,准确度时高时低。

宽高比/展弦比 Aspect Ratio

固定翼飞机,或者矩形的最小两边分量。被忽略的“长”是屏幕相对人眼的 z 轴。而换算长短边如 16:9 高 720 的方形像素视频,其宽就是 \( 720 \div 9 \times 16 = 1280 \)

image13

图:16:9 的矩形整数偶数放大后的图像分辨率

图片格式一般支持奇数分辨率,视频一般仅支持偶数分辨率。

高/低频 High/Low frequency

假设函数 \(y=\cos\left(ux\right)\)。可以调节变量 u,如 \(y=\cos\left(2x\right)\)\(y=\cos\left(3x\right)\) 等。u 越大则余弦函数周期越短——频率越高;u 越小则余弦函数周期越长——频率越低。

变换算法可以将这个 u 作为输出数轴,原始波形中从低频到高频的分量都会被准确地排列进来。

程序的交互界面 Interface

图形界面交互 Graphic user interface GUI:通过图形元素(如按钮、滑块、菜单等)进行用户交互的界面设计。随着程序复杂度的增加,设计出好用的界面会变得更加困难。因此,只有在利润丰厚、注重美学设计、且拥有设计人才的行业才会推出外观精美的 GUI。然而,外观好看并不一定意味着好用、再而,很多时候有外观用都是一种奢求。

image14

图:音频插件宿主软件 FL Studio,外加一堆免费和“免费”音频插件随便摆放的外观

image15

图:网页服务器宿主软件 XAMPP 的 GUI(左下为 CLI),作为跨行业对比

命令行交互 Commandline interface CLI:在如 Terminal、Bash、CMD 和 PowerShell 的终端环境中,用户可以手动输入命令并按回车键查看结果。通常,只要输出结果有一定的文本排版,就可以轻松阅读。随着使用的增加,CLI 可能反而会变得比 GUI 更方便。然而,由于某些命令太长,而且需要储存变量,因此为了简化操作,人们会将其写作批处理、Shell 等命令脚本文件。例如PowerShell 一站式系统硬件检测的脚本。

应用编程交互 Application programming interface API:定义了软件应用程序如何相互通信的规则和工具。用于使开发人员和从程序的外部访问,以便在自己的应用程序中集成其中的功能。API 可以被看作是 CLI 的映射源,当开发者没有编写 CLI 参数或 GUI 时,可以退而求 API 来调用所需的功能。尽管 API 通常不直接面向最终用户,但它们在软件开发中非常重要

帧率 Frame Rate / Frames per second / FPS

帧 Frame 通常用来描述连续图片序列中的单张图片。帧率 Frame Rate 是指每秒显示/刷新的图像帧数。帧率越高,则连续背景变化越流畅。因此,在游戏中,例如微软模拟飞行、极限着陆等模拟客机的游戏,以及电影运镜等故意限制背景变化程度的场景,其帧率一般只需要 30fps 左右(每秒更新 30 张画面)。而对于背景高速运动的游戏,比如模拟赛车等,即使是在手机平台上,最低帧率也不能低于 120fps。

在动漫中,如果背景高速移动变化时的帧率低于 60fps,观看体验会大大降低,例如《进击的巨人》中的部分场景。相反,对于一些其他动漫,如果设计师本意是为了降低成本而利用视觉暂留原理来创建顿挫画面,那么低帧率反而会破坏观赏体验。

逐行/交错扫描 Progressive / Interlaced

逐行扫描通过一行行从左到右地刷新像素来构建图像,是最为常用的屏幕刷新方式。交错扫描的方法不同,它使用 YUV 4:1:1 采样,将屏幕的奇数像素行视为一个场,偶数像素行视为另一个场。这两个场在录制时错开一定时间,这样在模拟电视上观看时会产生 60fps 的视觉效果。交错扫描的原理是为了节省模拟电视信号的频段区间(与收音机接收电台信号一样)。因此,通过在时间上分隔行来节省空间,可以几乎将每个频道的频宽减半,例如(\(525 \text{行} \times 30\text{fps} = 15.75 \text{MHz}\))。

image16

图:NTSC,PAL 和 SECAM 播放规格的地区分布,见维基百科

小数帧率 / 丢帧

NTSC 模拟电视每个频道的标准带宽大约为 6MHz(但实际宽度仅为 4.5MHz)。接下来发生的事谁都没有预料到——彩色电视出现了。结果是原本的频宽完全放不下彩色电视台信号,而且由于电视机已经售出,不可能更改用户家里电视的频宽和扫描线的数量。最终解决方案是在每秒 30 帧的基础上丢掉一帧,并加速 1 秒到 1.1001 秒,以保持与帧率的对齐(同时也引入了 YUV 4:2:0 色彩空间)。经过这般折腾,码率/频宽总算被压缩了下来,但后继影响了数字视频的丢帧帧率标准——为了与模拟电视兼容Reddit: GhostOfSorabji

在视频编辑中,对齐帧率不同视频是极其困难的操作,而这个天大的难题只需更改录制设定(点击两三个按钮)即可解决。

image17

图:不同帧率的对齐地狱,见Taran Van Hemert

编解码的过程

视频的打开

  1. 用户点击打开视频文件
  2. 操作系统根据文件扩展名,查找默认的应用程序
  3. 应用程序根据文件路径,在文件系统中定位视频,并读取文件的元数据,包括文件头和其他格式信息
  4. 应用程序根据文件的元数据确定所需的解码器和其他资源,然后在计算机的内存中分配空间
  5. 应用程序调用适当的解码器来解析视频文件,并将视频和音频数据解码为未压缩的原始数据
  6. 解码后的视频数据传递给图形处理芯片 GPU,而音频数据则传递给声音转换芯片 DAC。
  7. GPU 将图像信号转换为图像,输出到显示器
  8. 显示器上的芯片将图像数据转换为对每个像素上电路的控制,并显示出来
  9. DAC 将音频信号转换为模拟信号,并通过扬声器播放出来

此步骤涵盖了基本的视频打开过程,在实际情况下会因为软硬件,用户设定,以及操作系统逻辑的不同而有出入。

视频的压制

  1. 打开编码器
    • 用户可以通过多种方法打开编码器:
      • 如果编码器内置了如 Lavf 的解封装和解码动态链接库,则编码器可以自动解封装、解码和封装视频文件:
        • GUI 软件通过发送 CLI 命令,或者使用像 Bash/CMD 这样的 CLI 工具来打开编码器程序。
        • 编码器通常根据输出文件的后缀名自动封装视频文件。
      • 如果编码器没有内置解封装解码功能,则通常会使用 ffmpeg 内部的解封装工具和编码器。
        • 可以使用 pipe 将上游如 ffmpeg、VapourSynth、avs2yuv 连接到下游如 x264/5 的编码器
  2. 解封装和解码
    • 解封装解码工具将已解压缩的 YUV for MPEG 或 RAW 格式视频流传递给编码器,编码器得到输入信号。
      • 如果输入是 RGB24 的 RAW 色彩空间视频流,通常需要使用编码器内置的色彩空间转换功能将其转换为 YUV 格式。
      • 如果编码器是录像设备的一部分,则需要根据录像设置将视频流拆分为视频帧。
  3. 编码过程
    • 前瞻进程:Lookahead 从更小的分辨率预先分析未来几帧的复杂度、运动和场景切换 scenecut 的估计。
    • 分帧/帧类型决策:根据前瞻进程的结果和关键帧间隔 keyframe interval 设置关键帧 I 帧与参考帧 P-B 帧。
    • 粗分块
      • x264 中粗分到宏块 Macroblock MB
      • x265 中粗分到编码树单元 Coding Tree Unit CTU
    • 动态搜索:Motion Estimation ME 找到当前帧与参考帧之间的相似区域,并为每个块分配动态向量。
    • 运动补偿:Motion Compensation MC 对比实际画面与搜索到的向量生成预测块,并补偿¼子像素精度的对齐偏差。
    • 细分块:根据 MEMC,进一步细分宏块或编码树单元到最小的 4x4 编码块上。
    • 帧间残差编码:对比实际画面与动态搜索与补偿后所得的图像,得到残差,并对残差进行变换和量化,存储到 P-B 帧上,在解码时与 I 帧叠加以得到原始画面。
    • 帧内预测:在 I 帧或 P 帧的 I 块上,从前面步骤中生成的候选夹角、DC、趋平中的最佳模式冗余帧内的块,得到预测块。
    • 帧内残差编码:对比实际画面与帧内预测得到的残差块,进行变换和量化,存储在预测块中,在解码时与预测模式叠加以得到原始画面。
    • 跳过块编码:跳过帧间或帧内残差编码的块,进行变换和量化。
    • 变换:将图像从空间域转换到低频到高频信号分量之间的每个级别,也称为频域。
    • 量化:根据用户设置的质量级别,削减高频信号分量,这一步骤对画质和文件大小影响最大。
    • 熵编码/文本编码:将变换和量化后的频域分量统计为最小可能的二进制数。
  4. 生成视频流
    • 编码器将生成的数据流逐个 GOP Group of Pictures 打包,并附加如色彩空间、Supplemental Enhancement Information SEI、Video Usability Information/High Dynamic Range VUI/HDR 等元数据,得到视频流。
  5. 封装视频流
    • 用户可以通过多种方法将视频流封装为 .mp4、.mkv、.mov 等格式:
      • 如果编码器内置了像 Lavf 的解封装解码动态链接库,编码器可以根据输出命令行的文件后缀名自动完成。
      • 如果编码器没有内置解封装解码功能,则通常会使用 ffmpeg 内部的编码器和解封装工具,使编码完成后自动封装。
      • 可以使用工具如 ffmpegMP4BoxMKVToolNix 等来进行封装。
image18

视频编码的标准(MDN Docs

编码的标准 Encoding Standard 一般称为 coding 或 codec。其中 code 即代码,其末尾加 c 是一种名词化的形容词,如 politic/political,magic/magical,electric/electrical。

♙编码 全程 一般封装格式
AV1 AOMedia Video 1 MP4,WebM,MKV
AVC (H.264) Advanced Video Coding 3GP,MP4
H.263 H.263 Video 3GP
HEVC (H.265) High Efficiency Video Coding MP4
MP4V-ES MPEG-4 Video Elemental Stream 3GP,MP4
MPEG-1 MPEG-1 Part 2 Visual MPEG,QuickTime
MPEG-2 MPEG-2 Part 2 Visual MP4,MPEG,QuickTime
Theora Theora Ogg
VP8 Video Processor 8 3GP,Ogg,WebM
VP9 Video Processor 9 MP4,Ogg,WebM

其中,“H.264”的 H 代表国际电信联盟 - 电信标准化部门(ITU‑T)中的 H 系列建议指南,例如 ITU-T H.810 个人健康系统(血压计、血糖仪、体重秤)的交互设计建议指南。一些符合某个 H 系列视频编码建议指南的视频编码器会将这个标号写在程序名字上,就有了所谓的 x264,x265。

image19

命令行参数

由开发者定义,将函数或程序中的一些变量设为通过命令传递。命令行参数一般使用空格作为分隔符(也有用半角冒号,半角句号等其他情况),参数和参数值之间一般使用空格做分隔符(也有使用等于号的其他情况),多个参数值之间一般使用半角逗号或半角冒号分隔,而含有空格的参数值则使用半角直引号 "" 或 '' 连起来。

例如,x264 使用 CLI 参数的例子:

x264.exe --rc-lookahead 90 --bframes 12 --b-adapt 2 --me umh --subme 9 --merange 48 --no-fast-pskip --direct auto --weightb --keyint 360 --min-keyint 5 --ref 3 --crf 20 --qpmin 9 --chroma-qp-offset -2 --aq-mode 3 --aq-strength 0.7 --trellis 2 --deblock 0:0 --psy-rd 0.77:0.22 --fgo 10 --nr 4 --output ".\输出.mp4" ".\导入.mp4"

以及 ffmpeg 中使用 CLI 参数启用 libx264,通过 -x264-params 发送 API 参数的例子:

ffmpeg.exe -loglevel 16 -hwaccel auto -y -hide_banner -i ".\导入.mp4" -c:v libx264 -x264-params "rc-lookahead=90:bframes=12:b-adapt=2:me=umh:subme=9:merange=48:fast-pskip=0:direct=auto:weightb=1:keyint=360:min-keyint=5:ref=3:crf=20:qpmin=9:chroma-qp-offset=-2:aq-mode=3:aq-strength=0.7:trellis=2:deblock=0,0:psy-rd=0.77,0.22:nr=4" -fps_mode passthrough -c:a copy ".\输出.mp4"

ffmpeg 中,导入文件由 -i 参数完成,因为 ffmpeg 支持导入多个文件,而每个文件后跟随一些处理命令就需要较为严格的命令添加顺序,而 x264/5 这些编码器一般只是导入一个文件,所以在末尾加一个路径到文件的字符串就代表导入文件了。

image20

图:使用命令行窗口启动视频压制命令后,视频编码器会给出大致这些信息

本教程中命令行参数的说明格式

--参数

<开关 | 整数 A~B | 浮点 A~B | 其它格式,默认值,限制>说明信息,特点注解,推荐这个值,其它情况可设这个值

管道 Pipeline

在进程/软件之间通信的操作系统机制。在 CLI 中表现为两个程序的命令通过链接符 | 串联起来。常用于将上游程序/协议/函数输出的信息传递给下游程序/协议/函数实现过滤。最早的形式为 Unix Pipe。管道中程序/协议/函数的默认输入,输出与报错分别叫做 stdin,stdout,stderr,它们可以被手动指定到特定的路径,或作为管道传给更下游的程序/协议/函数。

串流管道 Stream-based pipeline

管道中的程序同步运行,持续地传输上游输出到下游。适用于串流作业和处理流数据,包括视音频和文本流。

对象管道 Object-based pipeline

管道中的程序按顺序接力,一个程序/协议/函数跑完后再打开下一个。适用于处理对象化 Object-oriented 的数据,面向对象的应用程序。Windows PowerShell 使用了对象管道,所以不适合处理视音频流文件。如 Bash 下:for txt in *.txt; do md5sum $txt; done | awk '{print $2, $1}' | sort -bn

指定默认输入和报错

在不默认从 stdin 读取管道上游 stdout 的程序/协议/函数,以及不默认从 stdout 输出数据流的程序/协议/函数中,需要添加“-”以特别指定上游输出到 stdout,下游从 stdin 输入;还可以额外选择导出 stderr 到文本文件,就有了这样的命令:

ffmpeg -i <源> -an -f yuv4mpegpipe -strict unofficial - | x264 --y4m - --output "" 2> D:\文件夹\报错.txt

数据结构

帧的结构

在 Advanced Video Coding AVC 中,帧的下级结构按照分辨率大小分为条带组 slice group → 条带 slice → 宏块 Macroblock → 块 block。从大小和功能上细分为:

image21

GOP 结构

而相对的,在帧之上的结构被称为图组 Group of pictures。每个 GOP 的创建取决于关键帧间隔设置以及转场判断,其中都包含了 IDR 帧,i 帧,P 帧,Pyramid-B 帧以及 b 帧。一个视频中最少由一个 GOP 组成,同视频长度下 GOP 的数量越多则视频体积越大,解码难度越低。

多参考帧的结构

x264 首次引入了超过前后一帧长度的帧间参考范围。因此动态搜索也能更好的检查运动向量的时间一致性,从而减少动态噪点对动态搜索的干扰,同时提高了压缩。多参考帧不是越长越好,而是只要满足「一帧只要参考前后各 n 帧的信息就足以还原自身」的长度就是对的,而超过后会导致视频体积增加,同时画质降低。这个长度一般是前后各 3 帧,

编码 - 前瞻进程 Lookahead

最先启动,设立关键帧和参考帧,决定了 GOP 划分的初始编码步骤。决定了后续所有步骤的大纲:

  1. 启动和初始化
    • 从视频或 --seek 参数指定的位置开始,顺序导入视频帧到 Lookahead 过程
    • 导入的视频帧会被缩小一半以提高速度
    • 后续步骤发现 GOP 结尾时,当前的 Lookahead 帧列表(h->Lookahead->next->list )会更新 Lookahead 起点。
  2. 软转场(Scenecut)
    • 如果输入的首帧是自动 AUTO 模式或 I 帧,则设为 IDR 帧
    • 如果使用 --no-open-gop,且输入帧与之前的非 B 帧画面差距达到 --scenecut 阈值触发转场,则设立 IDR 帧以建立新的 GOP
    • IDR 帧(于 --min-keyint 范围内则设 I 帧)的前一帧会被强制改为 P 帧,以阻止 B 帧的双向参考特性
  3. 启用/关闭连续 B 帧
    • 排除了设立 I/i 帧的可能后,还剩下 GOP 长度减 1 的帧数量没有被分配帧类型。
    • 此时不考虑 Pyramid-B 帧,所以这些帧可以是 IDR/i/P/B 帧。如果设 IDR 帧则 GOP 收尾
    • 根据 --b-adapt 指定的算法和 --bframes 的最大连续 B 帧长度,区分出 P 帧和 B 帧,得到如 PBBBPBP 的序列
    • --b-pyramid 参数允许连续 B 帧中的偶数帧作为参考帧使用,保证了长距离的连续 B 帧
  4. 维特比算法推演 P/B/b 帧
    • Viterbi shortest path 是一种多起点 - 多终点,类似于 A*和 Dijkstra 的最短路径算法。假设一种成本模型:
      • I 帧成本最高,记为设立成本 \(cost_I=10\);I 帧后的非 I 帧成本 +1,记为转换成本 \(cost_{IS}=1\)
      • P 帧成本次之,记为设立成本 \(cost_P=5\);P 帧后的非 P 帧成本 +1,记为转换成本 \(cost_{PS} = 1\)
      • B 帧成本最低,记为设立成本 \(cost_B=2\);B 帧压缩最高,记为转换成本 \(cost_{BS} = 0\)
      • --ref 参数设定了参考帧的最远范围,使编码器不总选择成本最低的 B 帧
    • 应用到推演各帧的可能路径图:
      • Frame 0: I0, -, -(GOP 首帧只能是路径中的 I 帧)
      • Frame 1: I1, P1, B1
      • Frame 2: I2, P2, B2
      • Frame 3: I3, P3, B3
      • Frame 4: I4, P4, B4
      • ...
    • 由此推演第 0,第 1 帧的成本,其中\(cost(I_0)\)代表第 0 帧为 I 帧的成本,\(cost(I)\)代表设立 I 帧的成本,则记为转换成本(当然此处严格一些应该直接写成 1):
      • Frame 0: \(10\), \(\text{-}\), \(\text{-}\)
      • Frame 1: \(cost_{I0} + 10\), \(cost_{P0} + cost_{IS} + 5\), \(cost_{B0} + cost_{IS} + 2\)
      • Frame 1: 20, 16, 13,或 \(cost_{I1} = 20\)\(cost_{P1} = 16\)\(cost_{B1} = 13\)
    • 正式采用维特比算法——选择每个帧的成本变为枝梢计算:设立成本 + 上帧三条路径中的最小值,推演第 2 帧及其后帧的成本:
      • \(I2 = min(cost_{I1} + 10, cost_{P1} + cost_{PS} + 10, cost_{B1} + 10) = min(20 + 10, 16 + 1 + 10, 13 + 10) = min(30,27,23) = 23\)
      • \(P2 = min(cost_{I1} + cost_{IS} + 5, cost_{P1} + 5, cost_{B1} + 5) = min(20 + 1 + 5, 16 + 5, 13 + 5) = min(26,21,18) = 18\)
      • \(B2 = min(cost_{I1} + cost_{IS} + 2, cost_{P1} + cost_{PS} + 2, cost_{B1} + 2) = min(20 + 1 + 2, 16 + 1 + 2, 13 + 2) = min(23,19,15) = 15\)
      • Frame 2: 23, 18, 15
      • \(I3 = min(cost_{I2} + 10, cost_{P2} + cost_{PS} + 10, cost_{B2} + 10) = min(23 + 10, 18 + 1 + 10, 15 + 10) = min(33,29,25) = 25\)
      • \(P3 = min(cost_{I2} + cost_{IS} + 5, cost_{P2} + 5, cost_{B2} + 5) = min(23 + 1 + 5, 18 + 5, 15 + 5) = min(29,23,20) = 20\)
      • \(B3 = min(cost_{I2} + cost_{IS} + 2, cost_{P2} + cost_{PS} + 2, cost_{B2} + 2) = min(23 + 1 + 2, 18 + 1 + 2, 15 + 2) = min(26,21,17) = 17\)
      • Frame 3: 25, 20, 17
      • \(I4 = min(cost_{I3} + 10, cost_{P3} + cost_{PS} + 10, cost_{B3} + 10) = min(25 + 10, 20 + 1 + 10, 17 + 10) = min(35,31,27) = 27\)
      • \(P4 = min(cost_{I3} + cost_{IS} + 5, cost_{P3} + 5, cost_{B2} + 5) = min(25 + 1 + 5, 20 + 5, 17 + 5) = min(26,25,22) = 22\)
      • \(B4 = min(cost_{I3} + cost_{IS} + 2, cost_{P3} + cost_{PS} + 2, cost_{B2} + 2) = min(25 + 1 + 2, 20 + 1 + 2, 17 + 2) = min(28,23,19) = 19\)
      • Frame 3: 27, 22, 19
      • Frame 4: 29, 24, 21
    • 将复杂度(前后两帧画面差距)视作分母 \(cplx_{0→1}\),将路径的值视作分子,就得到了该简化例子下的路径成本:
      • Frame 0: I0, -, -(GOP 首帧只能是路径中的 I 帧)
      • Frame 1: \(\frac{cplx_{1→2}}{I1}\), \(\frac{cplx_{0→1}}{P1}\), \(\frac{cplx_{0→1}}{B1}\)
      • Frame 2: \(\frac{cplx_{2→3}}{I2}\), \(\frac{cplx_{1→2}}{P2}\), \(\frac{cplx_{1→2}}{B2}\)
      • Frame 3: \(\frac{cplx_{3→4}}{I3}\), \(\frac{cplx_{2→3}}{P3}\), \(\frac{cplx_{2→3}}{B3}\)
      • Frame 4: \(\frac{cplx_{4→5}}{I4}\), \(\frac{cplx_{3→4}}{P4}\), \(\frac{cplx_{3→4}}{B4}\)
      • ...
    • 如此一来,编码器就能得知什么时候设什么帧了
  5. 宏块树搜索 MBTree
    • 分析时间空间范围内的块分布,找出少见的宏块,并计算出这些块的压缩强度偏移,以提高这些块的压缩
    • 偏移程度的计算考虑了 Lookahead 中快速动态预测的准确度(当前和未来两帧相减,差距越大越复杂)
    • 偏移程度的计算还考虑了当前帧与软转场 GOP 首帧的距离,距离越远越少见,分配的压缩强度越大
    • 在这之前的软转场已将画面分界,所以“距离远”代表画面不变的情况下的距离远
  6. 硬转场 keyint
    • (等 mbtree 完成后)如果使用 --no-open-gop,且当前 GOP 长度达 --min-keyint 大小,则设立 IDR 帧以建立新的 GOP
    • IDR 帧(于 --min-keyint 范围内则设 I 帧)的前一帧会被强制改为 P 帧,以阻止 B 帧的双向参考特性
  7. 量化值预设
    • 根据 --rc-lookahead 参数指定的长度为一组,通过当前和未来两帧相减,得到差值,以大致分配压缩强度
  8. VBV/VBR/CBR 码率限制:见x265 教程网页版:下层 - VBR,CBR 模式
--min-keyint

<整数,默认 25,小于等于 keyint>判断新发现的转场距离上个 IDR 帧是否小于该值长短。有两种设定逻辑,而它们给出的画质都一样:

--keyint

<整数>一般设 9 倍于帧率(9 秒一个 IDR 帧),影响视频解码计算难度和拖进度条的延迟:

--ref

<整数 1~16, 推荐 3\(1\%\times \text{fps} + 3.4\)>多参考帧前后帧数半径,一图流设 1。必须要在溯全尽可能多块的情况下降低参考长度,所以推荐 3

--no-mixed-refs

<开关>关闭参考帧混合溯块(16×8,8×8 分块的参考)以提速,增加误参考。不推荐

--scenecut

<整数,不推荐用>Lookahead 中两帧差距达到该参数值则触发转场

--seek

<整数,默认 0>从第几帧开始压缩

--frames

<整数,默认全部>一共压缩多少帧

--fps

<整数或除法>设定输出视频帧率,只有源视频帧率错误才用得到。制动该参数后会自动开启 -force-cfr

--force-cfr

<开关>强制设定视频帧到整数时间码,可能用于对齐小数帧率的误差(如 23.976 或 24000/1001 变成 23.977)用

--fullrange

<开关,7mod x264 自动>启用 0~255 色彩范围,而不是默认的 16~235。视频内容永远不在使用有限色域设备/软件/视频网站下播放下可以考虑开启

动态搜索

Motion Estimation。用于在连续帧间进行运动估计。过程涉及逐个像素点(微观)在相邻帧之间寻找最小失真朝向/最小失真方向 Direction of minimal distortion DMD,从而在每帧之间都生成一张向量表。如果这个过程缺失或不够理想,参考帧与分块的建立就可能会出现问题,从而导致潜在的压缩率损失或画质下降。

image22

图:宏块上帧间的动态向量

简化模型下,动态搜索得到帧间向量表,帧间向量表加上上帧的画面得到预测帧,原始帧减去预测帧得到残差帧,残差帧储存为参考帧,得到帧间结构。x264 使用了不对称多六边形搜索 Uneven multiple Hexagon,从而让一个搜索算法在多个分辨率下检查画面,制衡了动态噪点对传统动态搜索算法的干扰。搜索算法详见 x265 教程

--me

<hex/umh/esa/tesa,推荐 umh> 搜索算法,umh 平衡,star 四角星搜索之后收益递减,sea 是优化过的 x264 esa 穷举,但收益递减仍大。umh 通过多种分辨率大小的查找范围,减轻了动态噪点对传统搜索算法的干扰

--merange

<整数,推荐 4 的倍数,需 me>完全取决于 ME 算法和分辨率,过大会因「找不到更好,找到也是错」而损失画质和压缩。

--no-fast-pskip

<开关,推荐开> 关闭跳过编码 P 帧的功能

--direct

<temporal/spatial/auto> 指定动态搜索判断方式的参数,除直播和低性能设备录屏外建议 auto

--no-chroma-me

<开关,推荐直播/录屏用> 跳过色度平面动态搜索,将亮度平面直接应用。可能对画质有略微负面影响

绝对变换差和 SATD

Sum of absolute transformed difference 为两个变换块间做差,取和,取绝对值的步骤:

  1. 首先计算两个块 B 的插值,记做残差块 D:\(\text{D}(x,y) = B(x,y) - B\prime(x,y)\)
  2. 然后通过哈达码变换(相比 DCT 节约算力),得到变换残差块:\(T(D)\)
    • 省略这一步,则算法叫做绝对差 Sum of absolute difference SAD
  3. 最后,变换残差块的每个像素取绝对值和:\(\text{SATD}(B,B\prime) = \sum_{x=0}^{n-1}{\sum_{y=0}^{n-1}}| T(D(x,y)) |\)

注:为了简化所以写作 \(T(D(x,y))\),实际这样相当于每加一个像素值就要变换一遍

子像素运动补偿

全称 Subpixel refinement。动态预测的精度最高只有 1px,在此之上还有可能被动态噪点干扰,导致了预测帧相比原画会欠缺大量的纹理细节。动态补偿 Motion Compensation MC 通过对比原画与预测块得到残差,并根据残差的分布动态向量的精度到¼子像素,使“精加工预测块”的画面残差尽可能地缩小,得到足够准确的预测帧,即大幅减少残差帧的内容。

image23

图:根据动态搜索与补偿所得,完成宏块到块的细分

至于对抗动态噪点,大体上是允许真正移动的物件(所涵盖的分块)有运动向量,拦截噪点在静止画面上的孤立运动向量,即「允动之移,防静所变」解决,详见 x265 教程

  1. 此处省略上帧画面 + 帧间向量表中的动态向量 = 一系列粗加工预测块的步骤
  2. 使用有限冲激响应插值滤镜 FIR filter 放大画面以便后面对齐
  3. 将“粗加工预测块”与源视频所对应的画面使用 SATD 做差,得到当前的误差程度
  4. 当前向量的 x,y 分量分别加减 ½,¼ 像素(插值放大的画面中是整数像素),即分别让这些块对比原画跑一遍 SATD:
    • \(B\prime(x+½,y),B\prime(x,y+½),B\prime(x+½,y+½),B\prime(x+¼,y),B\prime(x,y+¼),B\prime(x+¼,y+¼),B\prime(x+¼,y+½),B\prime(x+½,y+¼)\)
  5. 选出 SATD 误差值最小的块——中的 x,y 分量,纠正帧间向量表的动态矢量,完成补偿

关于 FIR 插值滤镜,以及 x264/5 分别使用了哪些插值和搜索方法,见x265 教程网页版 - 动态补偿

--subme

<整数 1~10,推荐 6~10>调整具体的动态补偿强度,x264 中同时决定模式决策和率失真优化的强度。根据画质和编码速度选择合适的大小:

rd-refine

<嵌入 --subme 8 中,x265 中可手动打开> 率失真优化分析帧内预测的最佳量化和分块结果,耗时换压缩率和画质。x264 中还包括了最优动态向量的分析

细分块

--partitions

<字符串 p8x8,p4x4,b8x8,i8x8,i4x4/none/all,默认 p8x8,b8x8,i8x8,i4x4,推荐 all>允许的细分块种类。增加分块种类会增加计算负载,但 x264 放到现在来看,也只有高分辨率快速编码场景才需要保持默认。因此推荐 all

变换

模拟信号转换为数字信号的主要步骤之一——将音量、亮度、温度、压力等信号整理为低频到高频的分量。具体来说,这个过程根据要处理的信号和用途不同,会使用傅里叶变换,小波变换,和 x264/5 使用的二维离散余弦变换 2D Discrete cosine transform。二维离散余弦变换利用预制的二维波形模具,通过穷举加减将源信号逐步分解成不同频率的平面波形,并合成变换块。图:缩放到 8x8 的波形分布,由于 cos(x) 的波形范围是 -1~1,所以图中的黑色到白色并不是 0~255 或 16~235,而是 -1~1

image24

变换的过程中,“DCT 像素”称为系子 coefficients。这些系子的强度可以是负值,表示反相的波形;也可以是小数,表示低强度的波形(但因为计算机浮点计算慢于整数计算,所以一般仅用于演示)。变换块左上角的系数是直流 DC 分量,代表整体亮度水平,其余的系数是交流 AC 分量,表示图像中的细节和纹理信息。

离散余弦变换 DCT

每个频域分量都清点计算范围内所有像素值的“共鸣程度”,计为 DCT 频域系子的分量,得到变换块。

\[T(u,v) = \alpha(u) \alpha(v) \sum_{x=0}^{N-1} \sum_{y=0}^{N-1} f(x,y) \cos\left(\frac{(2x+1)u\pi}{2N}\right) \cos\left(\frac{(2y+1)v\pi}{2N}\right)\]

求和计算 Summation

上式的 \(\sum_{x=0}^{N-1} \sum_{y=0}^{N-1}\) 部分。等同于:

for($x=0; $x<$N-1; $x++) { for($y=0; $y<$N-1; $y++) { $T.u+=...; $T.v+=... } }

因此,一个 2x2 块下,上式一共要计算 (\2^2\) 次,将每次的结果取和,在乘以归一化缩放值 \( \alpha(u) \)\( \alpha(v) \) 完成变换。

均方根 Root Mean Square

RMS 是统计中常用的一种数据量化,用于表示一组数据平均值的平方根。对于一个包含 N 个值的数据集:\( \{ x_1, x_2, \ldots, x_N \} \)

\[ \text{RMS} = \sqrt{\frac{1}{N} \sum_{i=1}^{N} x_i^2} \]

具体到 DCT 变换,对于 N×N 块 \( f(x,y) \) 的均方根可以表示为:

\[ \text{RMS}_{f(x,y)} = \sqrt{\frac{1}{N^2} \sum_{x=0}^{N-1} \sum_{y=0}^{N-1} f(x, y)^2} \]

DCT 变换后,DCT 系子 \( T(u,v) \)的均方根为:

\[ \text{RMS}_{\text{DCT}} = \sqrt{\sum_{u=0}^{N-1} \sum_{v=0}^{N-1} C(u, v)^2} \]

归一化 Normalize

保证变换后,块的能量不变,所以通过 \( \alpha(u) \)\( \alpha(v) \) 两个缩放值来对齐,使得 \( \text{RMS}_{\text{DCT}} = \text{RMS}_{f(x,y)} \)

\[ \alpha(u), \alpha(v) = \begin{cases} \frac{1}{\sqrt{N}} & \text{if } u,v = 0 \text{ (DC)} \\ \frac{1}{\sqrt{2N}} & \text{if } u,v \neq 0 \text{ (AC)} \end{cases} \]

而在 JPEG 中:

\[ \alpha(u), \alpha(v) = \begin{cases} \frac{1}{\sqrt{2}} \approx 0.7071 & \text{if } u,v = 0 \text{ (DC)} \\ 1 & \text{if } u,v \neq 0 \text{ (AC)} \end{cases} \]

一种变换 2x2 矩阵的例子

  1. 输入:一个 2x2 矩阵 \[ C=\begin{bmatrix} 1 & 2 \\3 & 4 \end{bmatrix} \]
  2. 步骤 A1:设定 DCT 系子 设立变量 \(u\)\(v\),代表 DCT 空间内的横向与纵向格子
  3. 步骤 A2:计算平均值(DC 分量) \[ DC = \frac{1}{4} \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) = \frac{1}{4} \times (1 + 2 + 3 + 4) = 2.5 \]
  4. 步骤 A3:计算 DCT 系数
    1. \(C(0,0)\): \[ C(0, 0) = \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left[\frac{(2x+1)0\pi}{4}\right] \cos\left[\frac{(2y+1)0\pi}{4}\right] - DC \] \[ C(0, 0) = (1 \times \cos(0) \cos(0) + 2 \times \cos(0) \cos(0) + 3 \times \cos(0) \cos(0) + 4 \times \cos(0) \cos(0)) - 2.5 \] \[ C(0, 0) = (1 + 2 + 3 + 4) - 2.5 = 7.5 - 2.5 = 5 \]
    2. \(C(0,1)\): \[ C(0, 1) = \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left[\frac{(2x+1)0\pi}{4}\right] \cos\left[\frac{(2y+1)1\pi}{4}\right] \] \[ C(0, 1) = 1 \times \cos(0) \cos(\pi/4) + 2 \times \cos(0) \cos(\pi/4) + 3 \times \cos(0) \cos(3\pi/4) + 4 \times \cos(0) \cos(3\pi/4) \] \[ C(0, 1) = 1 \times \cos(0) \frac{\sqrt{2}}{2} + 2 \times \cos(0) \frac{\sqrt{2}}{2} + 3 \times \cos(0) \left(-\frac{\sqrt{2}}{2}\right) + 4 \times \cos(0) \left(-\frac{\sqrt{2}}{2}\right) \] \[ C(0, 1) = (1 + 2 - 3 - 4) \frac{\sqrt{2}}{2} = -4 \frac{\sqrt{2}}{2} = -2\sqrt{2} \]
    3. \(C(1,0)\): \[ C(1, 0) = \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left[\frac{(2x+1)1\pi}{4}\right] \cos\left[\frac{(2y+1)0\pi}{4}\right] \] \[ C(1, 0) = 1 \times \cos(\pi/4) \cos(0) + 2 \times \cos(\pi/4) \cos(0) + 3 \times \cos(3\pi/4) \cos(0) + 4 \times \cos(3\pi/4) \cos(0) \] \[ C(1, 0) = \frac{\sqrt{2}}{2} + \frac{\sqrt{2}}{2} - \frac{\sqrt{2}}{2} - \frac{\sqrt{2}}{2} = 0 \]
    4. \(C(1,1)\): \[ C(1, 1) = \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left[\frac{(2x+1)1\pi}{4}\right] \cos\left[\frac{(2y+1)1\pi}{4}\right] \] \[ C(1, 1) = 1 \times \cos(\pi/4) \cos(\pi/4) + 2 \times \cos(\pi/4) \cos(\pi/4) + 3 \times \cos(3\pi/4) \cos(3\pi/4) + 4 \times \cos(3\pi/4) \cos(3\pi/4) \] \[ C(1, 1) = \frac{\sqrt{2}}{2} + \frac{\sqrt{2}}{2} + \frac{\sqrt{2}}{2} + \frac{\sqrt{2}}{2} = 2\sqrt{2} \]
  5. 结果 A:得到的 DCT 系数矩阵为: \[ T = \begin{bmatrix} 5 & -2\sqrt{2} \\ 0 & 2\sqrt{2} \end{bmatrix} \]

类似 JPEG 计算 2x2 矩阵的例子

使用上述 JPEG 的缩放值对齐

  1. 输入:一个 2x2 矩阵 \[ C=\begin{bmatrix} 1 & 2 \\3 & 4 \end{bmatrix} \]
  2. 变换 B:直接计算 DCT 系数
    1. \(C(0,0)\): \[ C(0,0) = \alpha(0) \alpha(0) \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left(\frac{(2x+1)0\pi}{4}\right) \cos\left(\frac{(2y+1)0\pi}{4}\right) \] \[ C(0,0) = 0.7071 \times 0.7071 \times (1 + 2 + 3 + 4) = 5 \]
    2. \(C(0,1)\): \[ C(0,1) = \alpha(0) \alpha(1) \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left(\frac{(2x+1)0\pi}{4}\right) \cos\left(\frac{(2y+1)1\pi}{4}\right) \] \[ C(0,1) = 0.7071 \times 1 \times (1 \times 0.7071 + 2 \times -0.7071 + 3 \times 0.7071 + 4 \times -0.7071) = -1 \]
    3. \(C(1,0)\): \[ C(1,0) = \alpha(1) \alpha(0) \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left(\frac{(2x+1)1\pi}{4}\right) \cos\left(\frac{(2y+1)0\pi}{4}\right) \] \[ C(1,0) = 1 \times 0.7071 \times (1 \times 0.7071 + 2 \times 0.7071 + 3 \times -0.7071 + 4 \times -0.7071) = -2 \]
    4. \(C(1,1)\): \[ C(1,1) = \alpha(1) \alpha(1) \sum_{x=0}^{1} \sum_{y=0}^{1} f(x, y) \cos\left(\frac{(2x+1)1\pi}{4}\right) \cos\left(\frac{(2y+1)1\pi}{4}\right) \] \[ C(1,1) = 1 \times 1 \times (1 \times 0.5 - 2 \times 0.5 + 3 \times -0.5 + 4 \times 0.5) = -1 \]
  3. 输出 B: 2x2 像素块的 DCT 系数矩阵 \[ T = \begin{bmatrix} 5 & -1 \\ -2 & -1 \end{bmatrix} \]

以上提供了两种变换,因为理论上只要能通过逆运算还原原来的 1-2-3-4 矩阵就算能用(当然量化矩阵也要跟着修改),且不同信号的编码可能会使用不同的变换算法。

量化

简单地说,是根据不同的量化强度,块类型(亮度,色度,16x16,8x8,4x4)给出对应变换块的量化表/矩阵。将变换后的块点除量化矩阵,得到剩余 DCT 系子的块,从而减少高频的 DCT 系子分量,实现压缩。

量化值 qp

Quantization Parameter 是 CRF,ABR 等码率质量控制模式算出,以及率失真优化量化、模式决策等步骤所调整的中间变量

量化步长 qStep

Quantization Step-size 是 qp 于量化表中的映射值。规则是「qp 每 +1,则 qStep +0.125;qp 每 +6 则 qStep ×2,使其越往后增量越大」:

qp 0 1 2 3 4 5 6 7 8 9 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40…
qStep 0.625 0.6875 0.8125 0.875 1 1.125 1.25 1.375 1.625 1.75 2 2.5 3.25 4 5 6.5 8 10 13 16 20 26 32 40 52 64…

矩阵 Matrix

一串于特定位置换行的数字,用括号括起来得到。矩阵和矩阵之间可以根据情况选一种算法实现加减乘,和一种除法

矩阵间的点乘/内积 Dot Product

两个矩阵之间,位置相对应的数相乘,得到新的矩阵。如果两个矩阵大小不一,则需要设定其它规则才能计算。

\[ A = \begin{bmatrix} a_{1} & a_{2} \\ a_{3} & a_{4} \end{bmatrix}, \quad B = \begin{bmatrix} b_{1} & b_{2} \\ b_{3} & b_{4} \end{bmatrix}, \quad A \cdot B = \begin{bmatrix} a_{1} \cdot b_{1} + a_{2} \cdot b_{3} & a_{1} \cdot b_{2} + a_{2} \cdot b_{4} \\a_{3} \cdot b_{1} + a_{4} \cdot b_{3} & a_{3} \cdot b_{2} + a_{4} \cdot b_{4} \end{bmatrix} \]

矩阵间的行列式叉乘 Determinant cross product

通过一种算法(此处为行列式)将矩阵坍缩为一个数字(标量)得到。

\[ A = \begin{bmatrix} a_{1} & a_{2} \\ a_{3} & a_{4} \end{bmatrix}, \quad B = \begin{bmatrix} b_{1} & b_{2} \\ b_{3} & b_{4} \end{bmatrix} \]
\[ A \times B = (a_{1} \cdot a_{4} - a_{2} \cdot a_{3}) \cdot (b_{1} \cdot b_{4} - b_{2} \cdot b_{3}) \]

矩阵间的外积 - 克罗内克积 Outer product

将乘数矩阵本身乘进被乘数矩阵中的每一个值得到:

\[ A \otimes B = \begin{bmatrix} a_{1}B & a_{2}B \\ a_{3}B & a_{4}B \end{bmatrix} = \begin{bmatrix} a_{1}b_{1} & a_{1}b_{2} & a_{2}b_{1} & a_{2}b_{2} \\ a_{1}b_{3} & a_{1}b_{4} & a_{2}b_{3} & a_{2}b_{4} \\ a_{3}b_{1} & a_{3}b_{2} & a_{4}b_{1} & a_{4}b_{2} \\ a_{3}b_{3} & a_{3}b_{4} & a_{4}b_{3} & a_{4}b_{4} \end{bmatrix} \]

矩阵间的哈达玛积 Haramad product

两个大小相同矩阵下的一种常用的自定义点乘/内积算法。

\[ A = \begin{bmatrix} a_{1} & a_{2} \\ a_{3} & a_{4} \end{bmatrix}, \quad B = \begin{bmatrix} b_{1} & b_{2} \\ b_{3} & b_{4} \end{bmatrix}, \quad A \circ B = \begin{bmatrix} a_{1} \cdot b_{1} & a_{2} \cdot b_{2} \\ a_{3} \cdot b_{3} & a_{4} \cdot b_{4} \end{bmatrix} \]

x264 中,变换与量化会通过哈达玛积合并为一个矩阵表格,如 4x4 亮度块下为:

\[ \text{round} \begin{bmatrix}1/4 & 1/2\sqrt{10} & 1/4 & 1/2\sqrt{10} \\1/2\sqrt{10} & 1/10 & 1/2\sqrt{10} & 1/10 \\1/4 & 1/2\sqrt{10} & 1/4 & 1/2\sqrt{10} \\1/2\sqrt{10} & 1/10 & 1/2\sqrt{10} & 1/10 \end{bmatrix} \times Q_{Step} \times 2^6 \]

得到 qp 为 0,1,2,4,5 下的 4x4 块变换量化合并矩阵分别为:

\[ \text{QP}_0 \rightarrow \begin{bmatrix} 10&13&10&13 \\ 13&16&13&16 \\ 10&13&10&13 \\ 13&16&13&16 \end{bmatrix}, \text{QP}_1 \rightarrow \begin{bmatrix} 11&14&11&14 \\ 14&18&14&18 \\ 11&14&11&14 \\ 14&18&14&18 \end{bmatrix}, \text{QP}_2 \rightarrow \begin{bmatrix} 13&16&13&16 \\ 16&20&16&20 \\ 13&16&13&16 \\ 16&20&16&20 \end{bmatrix}, \text{QP}_4 \rightarrow \begin{bmatrix} 16&20&16&20 \\ 20&25&20&25 \\ 16&20&16&20 \\ 20&25&20&25 \end{bmatrix}, \text{QP}_5 \rightarrow \begin{bmatrix} 18&23&18&23 \\ 23&29&23&29 \\ 18&23&18&23 \\ 23&29&23&29 \end{bmatrix} \]

回到量化本身,如 JPEG 8x8 亮度变换块的量化,就是普通的矩阵除法:

\[ Q(u, v) = \text{round} \left( \frac{C(u, v)}{Q_m(u, v)} \right) \]
\[ \text{round} \left( {\begin{bmatrix} 515&65&-12&4&1&2&-8&5 \\ -16&3&2&0&0&-11&-2&3 \\ -12&6&11&-1&3&0&1&-2 \\ -8&3&-4&2&-2&-3&-5&-2 \\ 0&-2&7&-5&4&0&-1&-4 \\ 0&-3&-1&0&4&1&-1&0 \\ 3&-2&-3&3&3&-1&-1&3 \\ -2&5&-2&4&-2&2&-3&0 \end{bmatrix}}\div{ \begin{bmatrix} 16&11&10&16&24&40&51&61 \\ 12&12&14&19&26&58&60&55 \\ 14&13&16&24&40&57&69&56 \\ 14&17&22&29&51&87&80&62 \\ 18&22&37&56&68&109&103&77 \\ 24&35&55&64&81&104&113&92 \\ 49&64&78&87&103&121&120&101 \\ 72&92&95&98&112&100&103&99\end{bmatrix} } \right) = \begin{bmatrix} 32&6&-1&0&0&0&0&0 \\ -1&0&0&0&0&0&0&0 \\ -1&0&0&0&0&0&0&0 \\ -1&0&0&0&0&0&0&0 \\ 0&0&0&0&0&0&0&0 \\ 0&0&0&0&0&0&0&0 \\ 0&0&0&0&0&0&0&0 \\ 0&0&0&0&0&0&0&0 \end{bmatrix} \]

x264 中,量化值 qp 大致上存在于以下几处:

步骤 名称 应用
率控制 CRF/ABR 逐帧 qp(或 cqp → 全局 qp)
I-P 帧质量比 I-P Ratio P 帧 qp
P-B 帧质量比 P-B Ratio B 帧 qp
自适应量化 Adaptive Quantzation AQ 逐块 qp
(率失真优化)模式决策 Mode Decision MD 逐块 qp
率失真优化量化 Rate-distortion optimized quantization RDOQ 逐块 qp
色度块 qp 偏移 --chroma-qp-offset 亮度与色度块 qp

率控制

算出量化值

根据输入的质量设定参数,以及视频数据与人眼视觉的两种特性得到压缩强度——量化值 qp 的具体步骤。其中,视频数据已经分为了关键帧与参考帧(预测 + 残差)。根据这一性质就可以推断基础的质量差异,如:

--ipratio --pbratio

<浮点,默认 1.4、默认 1.3>P 帧相比 IDR/i 帧,以及 B/b 帧相比 P 帧的质量偏移。默认预设是给录像视频片源用的。

同时,“人眼视觉特性”代表人对明暗变化与画面细节程度的曲线感知。简单说就是人眼敏感于中低亮度的画面细节——应当非线性地根据亮度分配画面质量,以及画面细节损失的程度(量化压缩的强度)需要一个妥协平衡点——需要非线性地映射量化强度,共两个技术要求。具体的转换见 x265 教程网页版 - 率控制

其结果就是通过 CRF/ABR 通过非线性的推演,再转换出线性的 qp 值。一般分为 1~17,18~23,和 24~69 三个阶段,分配到 qp 1~17 的画面算作低量化,分配到 qp 18~23 的画面算作中量化,分配到 qp 24~69 的画面算作高量化。

平面等本来就没有什么信息的分块就算经过高量化,也不会影响其画质;主要的损失出现在有无意地给内容复杂的纹理细节分块分配了高量化或降噪处理,导致了信息的损失。降噪处理影响画面细节的原因是噪点本身是突兀的像素值,所以只能用高频信息表示。

上层,下层与双层模式

仅于本教程中使用以解释各种率控制模式搭配方法的概念。

上层 - 质量呼应码率 CRF 模式

全称 Constant Rate Factor。压制三角形下,距离妥协中心点最近的模式。因此也是压制最常用的模式。

--crf

<浮点范围 0~51,默认 23>据 cplxBlur,cutree,B 帧偏移给每帧分配各自 qp 的固定目标质量模式,或简称质量呼应码率模式,统称 crf。素材级画质设在 16~18,收藏~高压画质设在 19~20.5,YouTube 是 23。由于动画和录像的内容差距,动画比录像要给低点

上层 - 平均码率 ABR 模式

全称 Average Bitrate。编码器自行判断量化程度,尝试压缩到用户定义的平均码率 average bitrate 上,速度最快

--bitrate

<整数 kbps>平均码率。若视频易压缩且码率给高,就会得到码率比设定的片子;反过来低了会不照顾画质强行提高量化,使码率达标。如果给太低则会得到码率不达标,同时画质差的片子。平均码率模式,除 2pass 分隔,一般推流用的"码率选项"就是这个参数,速度快但同时妥协了压缩

双层 - 恒定量化值 CQP 模式

全称 Constant Quantizer Parameter

--qp

<整数 0~69,禁用 CRF/ABR/模式决策/率失真优化>设定全局量化强度。除非有既定目的,否则不建议使用。如果要手动指定特定范围的帧类型和量化值,则应使用 SBRC 下层模式

下层 - Zones 分段压制模式

手动指定帧数片段,每个片段允许指定一种上层模式和一些控制参数。

--zones

<开始帧,结束帧,参数 A,参数 B…> 手动在视频中划区,采用不同上层模式来实现如提高压制速度,节省平均码率,提高特定画面码率等用途 (一般用来"处理"片尾滚动字幕). zones 内的 me, merange 强度/大小不能超 zones 外。可用参数有 b=, q=, crf=, ref=, scenecut=, deblock=, psy-rd=, deadzone-intra=, deadzone-inter=, direct=, me=, merange=, subme=, trellis=

  1. 参数 b= 调整码率比率,限制 --zones 内的场景使用当前 0~99999% 的码率,100% 相当于不变
  2. 参数 q= 即 CQP 模式的 --qp 参数

双层 - 2pass-ABR 模式

先用 CRF 模式分析整个视频总结可压缩信息,后根据 ABR 模式的码率限制统一分配量化值。有 pass 2 给特别高的平均码率,输出最小损失的最小体积近无损模式,以及 pass2 给码率硬限的全局整体压缩模式

--pass 1

<挡位,导出 stats 数据文件>

--pass 2

<挡位,导入 stats 数据文件>

--stats

<路径,默认在 x264/5 所在目录下>设定导出和导入 stats 数据文件的路径和文件名

下层 - VBR,CBR 模式

说明见x265 教程网页版

--vbv-bufsize

<整数 kbps,默认关=0,小于 maxrate>编码器解出原画后,最多可占的缓存每秒。bufsize÷maxrate=播放时解码出每 gop 原画帧数的缓冲用时(秒)。值的大小相对于编完 GOP 平均大小。编码器用到是因为模式决策要解码出每个压缩步骤中的内容与原画作对比用

--vbv-maxrate

<整数 kbps,默认关 0>峰值红线。用「出缓帧码率 - 入缓帧码率必≤maxrate"」限制编码器在 GOP 码率超 bufsize,即缓存跑满时压缩超载帧(提高 qp 值 + 强降噪至码率合规为止)。当入缓帧较小时,出缓帧就算超 maxrate 也会因缓存有空而不被压缩。所以有四种状态,需经验判断:

由于 gop 多样,4 种状态可以出现在同一视频中。buf/max 实际控制了这些状态的出现概率

--nal-hrd

<开关,默认关,需 VBV>开启假想对照解码器 Hypothetical reference decoder。与假想播放流程中得到额外的 VBV 控制信息,并写进每段序列参数集 sps 及辅助优化信息 sei 里,适合提高 VBV 控制精度

--ratetol

<浮点百分比,默认 1> maxrate 限码的容错程度,用于防止压缩过大,但与此同时码率过大所导致卡顿的次数会增加

下层 - FTQP 模式

手动通过文件制定帧类型和 qp 值。

--qpfile

<路径到文件>手动指定帧类型和 qp 值 closed-gop 下 K 帧的 frame type qp 下层模式。qpfile 文件内的格式为"帧号 帧类型 QP"

qpfile.txt 例:

其它率控制

可以搭配除 CQP 以外的上下层模式使用,决定了视频各处的最终量化值

--qpmin

<整数 0~51>由于画质和优质参考帧呈正比,所以仅高压环境建议设最高 14

--qpmax

<整数 0~51>在要用到颜色键,颜色替换等需要清晰物件边缘的滤镜时,可以设26防止录屏时物件的边缘被压缩的太厉害,其他情况永远不如关 --cutree/--mbtree

--chroma-qp-offset

<整数,默认 0>AVC 规定 CbCr 的码率之和应等于 Y 平面,所以 x264 会拉高色度平面的量化。

--slow-firstpass

<开关>pass1 里自动关闭以下提速,或自动提高以下参数的强度,以保证 pass1 模式中 CRF 模式计算量化值的准确度:no-8x8dct me dia partitions none ref 1 subme <=2 trellis 0 fast-pskip,可手动覆盖为强度更高的参数

率控制模式的选择

一般情况下,用户要求在可接受的编码速度(fps 为单位)下,得到画质较高、文件体积较小的视频,构成了压制三角形。而不同的率控制模式,包括不同的视频编码器都会影响这一点。注意妥协点有三种:不省时,码率大,画质差,对应了三种不同的编码策略。

image25

图:不同率控制模式权衡编码速度,画质,文件体积的能力不同,当然有的模式根本没得选

自适应量化

CRF/ABR 设定每帧量化值 qp 后,方差自适应量化 Variance adaptive quantizer VAQ 会将每帧的 qp 细化到块上。AQ 与 VAQ 存在混淆。方差 variance 代表差→方→和→均的计算顺序。将数据样本逐个与整体平均做差,以偏差之和窥数据之衡。乘方是方便正负数一起求和计算(算式左的σ也乘方,优化了正负号在二进制中的额外占用):

方差 Variance

方差表示数据样本相对于整体平均值的离散程度,通过差→方→和→均的计算顺序,以偏差之和窥数据之衡。计算如下:

\[ \sigma^{2}=\frac{\sum_{i=1}^{N} (x_{i}-\overline{x})^{2}}{N} \]
  1. 每个数据样本减去整体平均值,得到差值
  2. 差值进行平方以放大误差,并消除负值差距的影响
  3. 平方后的值求和,得到总平方误差
  4. 总平方误差除以样本数得到方差
\[ \text{Variance} = \frac{\text{SSE}_{Pixels_{Macroblock}} - (\text{SAD}_{Macroblock})^2}{Pixels_{Macroblock}} = \frac{\text{SSE}_{Pixels_{Macroblock}} - (\text{SAD}_{Macroblock})^2}{256} \]

自适应量化与方差自适应量化的区别:

--aq-mode

<整数 0~3>据原画和 CRF/ABR 强度设定,码率不足时分配细分块 qp 值的策略

--aq-strength

<浮点>自适应量化强度,推荐搭配 --aq-mode 使用。如动漫和幻灯片录屏等平面多过纹理的场景下 --aq-mode 1 --aq-strength 0.8, 2:0.93:0.7

环路滤波

由于 AVC 的环路滤波步骤中只有去块,所以叫去块滤波在此更准确。

去块滤镜

修复“CRF/ABR 模式在某些场景的部分区域里分配量化值过高时,宏块间出现明显横纵割痕瑕疵”的平滑滤镜。编码器内去块利用了帧内帧间搜索到的信息,而理论上相比外部滤镜误判更少(当然外部滤镜也能做动态和帧内搜索)。去块大体上是检查 1 像素宽,且此块边缘没有较大像素值变化造成的横纵边缘(所以也存在误判)。块失真源自块间不统一的量化程度,有的块量化高流就会从邻近画面里凸现出来。而去块手段是平滑滤镜,因此要降低强度才适用于高码视频,动漫,素材录屏等锐利画面。边界强度 Boundary strength(去块力度判断)

image26 image27

图:取最小 8x8 范围(两个 4x4 分块)间的界线举例。

--deblock

<浮点偏移值,默认 1:0。推荐 0:0,-1:-1,-2:-1> 平滑强度:搜索精度。两值于原有强度上增减。

模式决策

  1. 整合动态搜索与补偿可能性
    • 计算并收集每种帧内,帧间预测模式的率失真分数(于下文解释)
  2. 整合分块参考量化可能性
    • 计算并收集每种细分块方案(4x4,8x8,16x16),参考帧长度,量化强度下的率失真分数
  3. 压缩方案的定制
    • 选择码率最小的压缩方案并不是最优解,因此找出率失真分数最高的方案集,得到码率与画质最为平衡的方案

注:如果片源已有明显的边缘失真,模式决策优化反而需要减少,因为优化方向是尽可能保留原画,导致失真本身被当成细节

优化量化策略

率失真优化

Rate distortion optimization,RDO。原本有损压缩视频编码器的策略就是码率越小越好——所以量化强度越大越好。但实际情况是量化压缩相当程度的破坏了 P-B 帧原本可以参考 I 帧的部分——因为量化后差异太大而只能放弃参考,换成 I 块。解决这种乱量化问题的方法是将失真,码率分别看成两种越大越差的程度;将其中一个程度乘以一个缩放值以对齐另一个(使两值权重对等)以相互牵制出平衡点。再将这个“新的程度”应用到原本的编码器策略里,就是率失真优化。

“编码器策略”就是模式决策。率失真程度由代价函数「开销=失真+λ⋅码率」(越小越好)得出:

\[J = D + \lambda \cdot R\]

由于这个式子是一元一次函数( \(y = mx + b\) ),所以可画图表示为每个方案 J 作为位于 D,R 坐标系,斜度 λ 线上的点;其中失真 D 用平方差 SSE 或总差异 SAD 判断(SSE 多取一步平方,使较大的差异呈指数增长)进而分到更多补偿的码率。拉格朗日值 λ 源于 qp,即 crf,abr 指定的率失真斜度区间。qp 越大斜度越小:

image28 image29

图:率失真优化中λ的斜度变化与效果

image30

图:率 - 失真表格中不断调整并得到“×”的实际效果

--fgo

<整数默认关,推荐 15 左右>将高频信号在量化前后的差距也算进率失真优化的统计中,使优化更倾向于保留细节和噪声

心理视觉优化

Psycho-visual optimization PVO 更类似于一种自适应量化,利用人类视觉系统的特性,通过掩蔽和感知量化量化,实现低码画面的码率再分配,保持高压缩的同时,提供更好的主观视觉质量。主观质量是建立在损失客观质量(与输入源的差距)上的质量。

  1. 空间掩蔽 Spatial Masking
    • 人眼会忽略高锐度边缘周边区域的画面,因此增加这些区域的 qp
  2. 频域掩蔽 Frequency Masking
    • 人眼会忽略高频细节区域内的低频信号分量,因此可以在变换块中剔除高频块的低频分量
--psy-rd

<a:b 浮点,默认 1:0>心理学优化设置。a 保留纹理,b 在 a 的基础上保留噪点细节,ab 值据画面复杂度拉高,

--no-psy

<开关>若视频量化很低,纹理清楚,没有优化的必要可以关。但大部分情况下不应该关,而是用较低的优化强度

image31

图:心理视觉优化保留高频信号,同时压缩周围和其中的非高频信号

宏块树与量化值曲线缩放 qComp

源自 libavcodec 以复杂动态场景的帧间参考少而应提高压缩,分码率给参考多而远的简单动态场景,以实现同码率下整体画质更高结果的逻辑。缺点则是动漫场景等「背景不动前景动」的场景被误压缩。说到底是处理精度低而非错误,应逐宏块应用这套逻辑,就有了 mbtree。量化值曲线缩放 quantizer curve compression 则被贬为控制 mbtree 等一系列 qp 分配算法中的一个缩放参数,见 desmos 互动例

量化值曲线缩放的用途

  1. (仅 ABR 模式)根据宏块的数量与 --qcomp 值设置初始复杂度百分比: \[ \text{Complexity} = \left( \frac{\text{Count}_{MB}}{2} \right) ×700000^{\left( \frac{qcomp}{100} \right)} \div 100 \]
  2. 获取当前场景的长度 \( \text{Duration}_{clip} \)
  3. 当前帧与下一帧间的图像模糊后做 SATD,获得「模糊复杂度 \( \text{Complexity}_{blurred} \)
--qcomp

<浮点范围 0.5~1,推荐默认 0.6>模糊复杂度 cplxBlur 以及 mbtree 迭代每帧 qp 值范围的曲线抑制参数。越小则复杂度迭代越符合实际状况,抑制 CRF,mb-cutree,bframes 影响的效果就越弱,搭配高 CRF 能使码率控制接近 VBV 的程度。越大则 CRF,mb-cutree,bframes 越没用,越接近 CQP

宏块树 Macroblock-Tree

如果一个宏块用作帧间参考的码率比用作帧内参考低 70%,则该宏块可视为 30% 非帧间参考和 70% 帧间参考两部分。已知帧间参考占据的部分越多,则这个宏块自身就没有多少信息;而在宏块树所运行的时间,这个宏块是位于软转场 GOP 末尾——scenecut 步骤之后,keyint 步骤之前。因此,可以增强其压缩——把更多的码率分给参考传播最远的 I 帧,以提拔整个软转场 GOP 的画质。详见 x264 率控制算法mbtree paper

由于宏块树的步骤位于前瞻进程 Lookahead 中,而前瞻进程位于动态搜索和运动补偿之前,且 Lookahead 只使用半分辨率的视频帧,因此,此时的宏块树只能额外尝试简单的帧内和帧间预测。

宏块帧内和帧间的参考代价记为 \(\text{Cost}_{intra}\)\(\text{Cost}_{inter}\),通过 Lookahead 中以下“搜索”步骤得到:

  1. 设定:各个宏块自身代价与参考代价的比率,再从 100% 减掉这个比率,得到给其他宏块参考的信息比率 \[ \text{propagate}_{fraction}=1-\left(\frac{\text{Cost}_{intra}}{\text{Cost}_{inter}}\right) \]
  2. 设定:各个宏块传播到下游宏块的代价量 = 给其他宏块参考的信息比率 × 溯块总代价 \[ \text{Propagate}_{amount} = \text{Propagate}_{fraction} \times (\text{Cost}_{intra} + \text{Cost}_{propagate}) \]
    • \( \text{Propagate}_{fraction} \) 越小,传播到下游宏块的信息量就越少
    • \( \text{Cost}_{intra} + \text{Cost}_{propagate} \) 越大,传播到下游宏块的信息量就越大
  3. 计算:从最远范围 --rc-lookahead 开始,从远到近地累积所有宏块的 \( \text{Propagate}_{amount} \),作为统计列表中下一个宏块的 \( \text{Cost}_{propagate} \)
    • 这里非常绕,总之就是「传播到下游宏块的信息量 \( \text{Propagate}_{amount} \)」是持续更新累积的过程,而不是从当前帧往后查找传播了多少信息
    • 如果出现动态位移(当前宏块的范围被切分到下帧的 4 个宏块之间时),这 4 个宏块会各自按比例拆分出各自的传播信息量
  4. 分配:从最远范围 --rc-lookahead 开始,从远到近地根据已有的 \( \text{Cost}_{propagate} \) 和自身的信息量 \( \text{Cost}_{intra} \),通过 \( \log_{2}() \) 非线性地映射到 qp,得到宏块树中每个宏块的量化值偏移: \[ \Delta \text{QP} = -\text{strength} \times \log_{2}\left( \frac{\text{Cost}_{intra}}{\text{Cost}_{propagate}} \right) \div \text{Cost}_{intra} \]

大部分情况下 mbtree 偏移值为零,因为宏块没有溯块信息可用

--rc-lookahead

<帧数量,范围 1~250,推荐 keyint÷2> 指定 cutree 的检索帧数,通常设在帧率的 2.5~3 倍。高则占用内存增加延迟,低则降低压缩率和平均画质。

注:mbtree/cutree 会自动选择 --rc-lookahead\( \max\left( \text{keyint}, \max\left( \text{vbv-maxrate}, \text{bitrate}\right)\div\text{vbv-bufsize} \times \text{fps} \right) \) 中最小的值作为检索帧数

--no-mbtree

<开关> 关闭少见宏块量化偏移。可能只有近无损,--crf 小于 16 才用的到。

熵编码/文本压缩

x264 教程中仅提及最简单的形式。完成的熵编码流程见 x265 教程网页版 - 熵编码

霍夫曼编码 Huffman Coding

曾经广泛应用于数据压缩的算法。步骤分为:

  1. 统计频率
    • 在第一次遍历文本/pass1 时,只统计每个字符出现的次数,并按出现频率从小到大排序
  2. 构建初始节点
    • 将每个字符和其出现频率作为一个节点,并将这些节点放入一个最小堆(优先队列)
  3. 构建霍夫曼树
    • 从最小堆中取出两个频率最小的节点
    • 创建一个新节点,其频率是这两个节点频率之和,将这两个节点作为新节点的子节点
    • 将新节点插回最小堆中
    • 重复上述步骤,直到堆中只剩下一个节点,这个节点就是霍夫曼树的根节点
  4. 生成编码
    • 通过遍历霍夫曼树,为每个字符生成霍夫曼编码。通常向左的节点表示为向 0,向右的节点表示为向 1

霍夫曼编码的例子

来源:geekforgeeks - greedy3 算法。假设第一次遍历文本得到了如下字符及其频率:

字符频率
A5
B9
C12
D13
E16
F45
  1. 统计频率:已完成
  2. 构建初始节点
    • 据出字次数从小到大排序:(5,'a'),(9,'b'),(12,'c'),(13,'d'),(16,'e'),(45,'f')
  3. 构建霍夫曼树
    1. 取出字次数最小的两个元素 (5, 'a'), (9, 'b'),合为 (14, 'ab')
      • 得 (12, 'c'), (13, 'd'), (16, 'e'), (45, 'f'), (14, 'ab')
      • image32
    2. 取出字次数最小的两个元素 (12, 'c'), (13, 'd'),合并为 (25, 'cd')
      • 得 (14, 'ab'), (16, 'e'), (45, 'f'), (25, 'cd')
      • image33
    3. 取出字次数最小的两个元素 (14, 'ab'), (16, 'e'),合并为 (30, 'abe')
      • 得 (25, 'cd'), (45, 'f'), (30, 'abe')
      • image34
    4. 取出字次数最小的两个元素 (25, 'cd'), (30, 'abe'),合并为 (55, 'cdeab')
      • 得 (45, 'f'), (55, 'CDEAB')
      • image35
    5. 取出字次数最小的两个元素 (45, 'f'), (55, 'cdeab'),合并为 (100, 'fcdeab')
      • 得 (100, 'FCDEAB')
      • image36
  4. 生成编码
    • 从霍夫曼树根节点开始,为每个字符生成霍夫曼编码:
字符编码
F0
C1-0-0
D1-0-1
A1-1-0-0
B1-1-0-1
E1-1-1

从上面的内容可见,熵编码是一种无损压缩(例如代码和小说都是文本,文本编码不可更改其内容)。既然是无损,那么不同的熵编码算法之间就只有编码速度和压缩率两个判断标准。x264 中使用了上下文自适应可变长度编码 Context Adaptive Variable Length Coding 和压缩率更高,速度慢一点的上下文自适应二进制算数编码 Context Adaptive Binary Arithmetic Coding CABAC。CABAC 的原理见 x265 教程网页版 - 熵编码

--no-cabac

<开关,不推荐>关闭 CABAC,使用 CAVLC

优化熵编码

残差 DCT 系子

Residual DCT coefficients 代表变换后量化前,已知经过量化后会被抹除的 DCT 系子。DCT 变换的例子见本教程的 变换 - DCT 变换 部分。总之就是与“残差块”无关,只是都叫做残差而已。

编码块标记 CBF

Coded block flag 会被标在预测和/或与其相应的变换单元(亮度块加色度块)中表示该单元是否附有较多的残差系子:CBF=1 代表有、0 代表没有。引导四叉树 quadtree 算法进一步分枝——宏块向下分块。详见 US9749645B2

image37

图:四叉树的分块。见维基百科

DCT 归零优化 Decimate

由上可知,录像与电影片源中的动态噪点会直接干扰编码器的分块,导致熵编码总是要处理系子数量少的 8x8,4x4 分块,而 x264 设计时面向了 640x480 分辨率的视频,所以就有了专门用于避免无效分块,避免性能和熵编码压缩率被浪费的系子归零处理(现已淘汰但默认开启)。具体是:

  1. 8x8 亮度块中分量为 -3~3 的 DCT 系子归零
  2. 16x16 亮度块中分量为 -5~5 的 DCT 系子归零
  3. 色度块中分量为 -6~6 的 DCT 系子归零
  4. 优化后的块回到原来的编码步骤——量化中
--no-dct-decimate

<开关,默认关>关闭 DCT 变换的低强度系子归零操作,以减少细节损失,降低有损压缩。适合幻灯片录屏等画面极其干净,或经过大量滤镜过滤噪声,--crf < 17或 ABR --bitrate > 500000 编码策略的源视频

死区量化器 Dead-zone Quantizer 优化

死区,也叫 dead-band,netual-zone,或无作用区,代表输入到输出波形中可归零的部分。

image38

图:信号死区设 0.5 时,输入到输出 y=x 波形的变化。见维基百科

死区量化器的用途同上。x264 中,--trellis没有完全开启(2)时,死区量化器会被替代启用。默认设置下:

  1. 帧间亮度块中,分量为 -21~21 的 DCT 系子归零
  2. 帧内亮度块中,分量为 -11~11 的 DCT 系子归零
  3. 优化后的块回到原来的编码步骤——量化中

因此,结果是在高分辨率下,默认设置对画面细节的涂抹较强,所以一般建议 --trellis 2,或在最少大于等于 1600x900 分辨率、--trellis < 2 的时候更改死区量化器相关参数的默认值。

--deadzone-inter

<整数 0~32,默认 21,--trellis 小于 2 时开启>一种逻辑简单的帧间再量化——细节面积小于死区就删掉,大就保留。一般用途建议 8,高画质建议 6

--deadzone-intra

<整数 0~32,默认 11,--trellis 小于 2 时开启>一种逻辑简单的帧间再量化——细节面积小于死区就删掉,大就保留。一般用途建议 8,高画质建议 4

软判决率失真游程走线优化 - SDQ trellis

Soft decision quantization / SDQ trellis 算法。通过限制量化后块中的 DCT 系子 coefficients 强度,甚至偏移当前系子的强度值到临近系子,以保证游程编码中走线的平稳。过程中由率失真计算确认对画面的影响符合预期质量,从而提高压缩率。--trellis 应用于量化步骤之后,并且对少数突兀的 DCT 系子强度做了平均化,所以叫“再量化”。详见 x265 教程 - 优化熵编码

--trellis

<整数,范围 0~2, 推荐 2>软判决优化 CABAC 的率失真再量化。

色彩信息

重复一开始说明的物理亮度:可见光电磁波的强度或振幅,以流明 lumen/lm,坎德拉/烛光量 candela/cd,或尼特 nits/cdm2 计量:

高动态范围 High Dynamic Range HDR

原本是高端音响的标准,代表最小音量下没有一点电流噪声,最大音量可以和真实场景媲美。与扬声器的大小和材质是否匹配场景相关,即「重硬件轻软件」。因为只要硬件达到 HDR 1000 规格,那么只要调高亮度就可以了;而随便一个中位水平的电视/显示器/手机屏幕亮度都能达到 HDR 400 标准。

注意高亮度的显示只适合电影和部分游戏场景,在工作学习用途下高亮度极其伤眼。

HDR 元数据设置

x264/5 皆支持的元数据设置。但因为 HDR 分为硬件标准,软件标准,以及数据标准,所以很乱。为了保证色彩正确,必须参考片源的元数据。如:

image39

图 1: cll 1000,640. master-display 由 G(13250…) 开头,L(10000000,1) 结尾

image40

图 2: cll 1655,117/L(40000000,50)/colorprim bt2020/colormatrix bt2020nc/transfer smpte2084

--cll

<最大内容光强,最大平均光强>压制 HDR 源一定照源视频信息设,找不到不要用,见上图例。

--colorprim

<字符>播放用三原色 (以及白点) 指标,查看视频信息可知:bt470m,bt470bg,smpte170m,smpte240m,film,bt2020,smpte428,smpte431,smpte432。如图 1 为 bt.2020。

--colormatrix

<字符>播放用矩阵格式/系数指标:fcc,bt470bg,smpte170m,smpte240m,GBR,YCgCo,bt2020c,smpte2085,chroma-derived-nc,chroma-derived-c,ICtCp。

注:不支持 bt2020nc

--transfer

<字符>传输特质:bt470m,bt470bg,smpte170m,smpte240m,linear,log100,log316,iec61966-2-4,bt1361e,iec61966-2-1,bt2020-10,bt2020-12,smpte2084,smpte428,arib-std-b67。

上图图 2 的 PQ 即 st.2084 的标准,所以参数值为 smpte2084。

--master-display

<G(x,y)B(,)R(,)WP(,)L(,)(绿,蓝,红,白点,光强)>写进 SEI 信息里,告诉解码端色彩空间/色域信息用

预设参数

--preset

<ultrafast/superfast/veryfast/faster/fast/medium/slow/slower/veryslow/placebo>见下:

参数\预设 ultrafast superfast veryfast faster fast medium slow slower veryslow placebo
b-adapt 0 默认 1 2
bframes 0 默认 3 8 16
direct 默认 spatio auto
me dia 默认 hex umh tesa
merange 默认 16 24
partitions none i8x8,i4x4 默认 p8x8,b8x8,i8x8,i4x4 all
rc-lookahead 0 10 20 30 默认 40 50 60
ref 1 2 默认 3 5 8 16
subme 0 1 2 4 6 默认 7 8 9 10 11
trellis 0 默认 1 2
weightp 0 1 默认 2
no-weightb 1 默认 0
no-8x8dct 1 默认 0
no-cabac 1 默认 0
no-deblock 1 默认 0
no-mbtree 1 默认 0
no-mixed-refs 1 默认 0
scenecut 0 默认 40
no-fast-pskip 默认 0 1
slow-first-pass 默认 0 1
--tune

<zerolatency/animation/grain/film/fastdecode/psnr/ssim>更改 preset 的一些参数,见下:

线程控制

线程管理一

不同的算法和数据决定了不同的多线程优化性能。量化的步骤中,进程之间几乎不需要共享数据,进程占用和运算速度也不变;而冗余的步骤中则是反过来——占用可大可小,速度忽快忽慢,也需要进程间共享一定数据/内存。可以说如果想要让进程吃满更多核心,就得尽可能减少搜索冗余相关算法的强度。x264 编码器一共会占用 --threads--lookahead-threads条进程。而 --threads 达到并超过 16~24 时,可以看到一定的画质下降,以及在更多核心的电脑上出现“吃不满”的问题,这就是 x264 中冗余算法的优化极限了。

因此,设置创建比电脑处理器更多的线程只会从任务数量上冲淡如各个冗余步骤需要“等到之前步骤给出结果才能运行”的时间窗口,甚至有时等不到就跳过,导致码率增加,画质降低,得不偿失。

x264 并没有对多路/多 CPU 节点电脑优化,但 x265 使用线程池 实现了。

--threads

<整数,建议保持默认(等于 CPU 线程)>由于参考帧步骤要等其之前的步骤算完才开始,所以远超默认的值会因为处理器随机算的特性而降低参考帧的计算时间,使码率增加,画质降低,速度变慢

--non-deterministic

<开关,默认关,推荐开> 降低多线程完算性——减少多线程共享数据的可靠性,些微提高画质。使用后每个进程的冗余判断更为独断,且让新的动态搜索线程得知「旧动态搜索线程线程实际搜素过,而非参数设定」的区域。「完算性」一般仅代表通过完整计算得到结果,而不是提前跳过的计算特征。而启用多线程,VBV 都会降低完算性

--lookahead-threads

<整数,建议保持默认(应该是 --threads 的 1/4)>指定前瞻进程占用的线程数,速度太快则其后的编码进程跟不上,太慢则其后的编码进程要等结果,所以不需要改动

--frame-threads

<整数,建议保持默认(自动)>指定 --threads 中分配多少线程给编码帧,决定一次同时编码多少帧(量化,优化,帧内预测,熵编码)。剩下则是上下游的处理(连续 B 帧推演,SEI,完算性错误管理),后者占用较少

--sliced-threads

<开关,默认关>逐条带分配多线程。x264 r1607 前的多线程分配机制,后来因为问题太多而改成了逐帧分配多线程。剩下直播录屏等场景降低延迟一个用途

  1. 熵编码中,CABAC(见 x265 教程 - 熵编码)因为内容不连续所以经常重设,降低压缩率
  2. 动态信息的动态矢量长度被限制到条带分片内部,干扰动态补偿,降低压缩率
  3. 帧内冗余降级为条带内冗余,降低帧内编码压缩率
  4. 增加了相当一部分(多余条带与 NAL 封包的)文件头
  5. 大量条带的并行提高了编码单帧的速度,降低延迟

线程管理二

退一步讲,若能占满全部 CPU 核心,那么也算是实现了多线程优化。因此,关于“最大化利用 CPU 核心数量很多的电脑并提升画质”的平价方法就是多开几个编码,并限制每个编码占用的 CPU 线程数实现。例如 Windows 批处理提供的 START 命令实现异步并行(多开些编码),搭配上述的 --threads 命令限制每个编码的进程数实现。

x264/5 中,理论画质与性能最平衡的多线程数实际是 4,所以若设定 --threads 4,则 --lookahead-threads 等于 1。得到共 5 个线程占用。

线程管理三

此外还有一种难办的情况:游戏录屏。这会导致编码进程与其它进程争夺 CPU 或内存资源。后者可以通过升级内存解决,但前者不同的地方在于“多线程优化越好则越糟”——因为软件开发也遵循了“应该占满全部 CPU 核心”的逻辑,因此当 CPU 的算力或核心数没有超过两个程序占用加起来的峰值,就只能保护其中优先级较高的进程了。另一边,多人游戏的反作弊机制和防盗版机制的升级也增加了游戏对 CPU 和内存的压力。

此时,“冗余”步骤的不稳定占用很可能会超出 CPU 算力极限导致卡顿(除非内存或散热不好,否则不会死机),因此要适当降低冗余强度来保证流畅(但需要一定经验并花费时间测试)。大多数人应对这种情况最简单的方案只有使用显卡或核显/SoC“附赠”的硬件视频编码器,或者购置高端些的视频采集卡两种方法实现;但如果 CPU 的算力足够同时流畅运行大型游戏和视频编码器,则可以参考附录:急用版 x264 教程配置 OBS 录屏的一些方案。

内置滤镜

降噪

--nr

<整数 0~65536,降噪范围 100~1000,不推荐>当 ABR/VBV 指定了高于 --qpmax 或默认最大 qp 值时,会调用这个滤镜。如果有降噪需求则建议使用效果更好的外部滤镜工具,或至少使用 --vf hqdn3d 滤镜

内置 AviSynth 滤镜

--vf

<crop:[←],[↑],[→],[↓]/pad: [←],[↑],[→],[↓]/resize:[宽,高],[变宽比],[装盒],[色度采样],[缩放算法/select_every:步,帧,帧…]/hqdn3d:[空域 Y 降噪强度],[空域 C 降噪强度],[降噪时域 Y 强度],[时域 C 降噪强度]/yadif[模式],[顺序]>裁剪,加边,缩放/更改分辨率,删除/保留视频帧,降噪,色彩空间转换滤镜。例:

这些滤镜可以在实现简单的画面处理。使用多个滤镜的例子如:--vf crop:10,0,10,0/resize:640,480,,,,lanczos

--sub

<路径到文件,--vf subtitles 和 VSFilter64.dll>导入字幕文件,

恒定 Constant 与可变 Variable 帧率

恒定帧率的视频播放和剪辑都最具兼容性。可变帧率有助于解决手机录像时的发热和耗电速度快的问题,同时,将可变帧率转换为恒定帧率以正常剪辑视频时则需要重新编码。因此,录制视频时应根据设备的电量和散热能力进行选择,而录制需要剪辑的素材时则应选择恒定帧率。

可变帧率转恒定帧率(60fps)需要借助外部滤镜完成。如 ffmpeg:

ffmpeg -i <输入> -vsync cfr -y -vf fps=60 -c:a copy -f yuv4mpegpipe - | x264 <参数> - --demuxer y4m --output <输出.mp4>

由于视频元数据里也有着标错的情况,所以用 MediaInfo 等工具查看到视频帧率是 Variable 时,还要用播放器逐帧查看,或者用 ffprobe 检测。见 ffprobe 教程

工具下载

下载套壳了 x264/libx264 的 GUI

并没有特别好的选择。尤其是当能够使用 CLI 参数命令 ffmpeg.exe,x264.exe 后。

Shana
Encoder
ffmpeg-CLI 搭配部分 GUI,上手较慢。使用了 ffmpeg 内嵌的 libx264,libx265,NVEnc image41
image42
Simple x264 Launcher 内嵌 AviSynth,支持便携版 VapourSynth,额外支持 x265 和 NVEnc 编码器,适合批量压制 image43
小丸工具箱 操作简单,适合入门的中文软件。网上能搜到详细教程。内嵌 MediaInfo,mp4box,Mkvtoolnix
镜像:百度云 提取码"crhu"
image44

下载基本工具

ffmpeg 强大的 CLI 开源视音频处理工具。几乎所有的视音频处理都绕不开
mpv 开源,支持便携的现代视频播放器。见 安装与配置教程 image45
Voukoder 开源 Premiere Vegas After Effects 压制导出插件,分为 Voukoder 和 V-Connector 两部分 image46
OBS 强大的开源直播框架和软件,设置略比传统录屏软件复杂,但效果也更好 image47
MediaInfo 开源的 GUI 媒体元数据/视音频格式读取器,用于配置正确的压制参数 image48
image49
ffprobe CLI 视音频格式读取器,若检测所得信息与 MediaInfo 所异,则优先参考 ffprobe
基本使用,以及搭配 Excel 的视频数据可视化教程

下载 x264

Patman 支持 FFMS2 Lavf 编解码 [8~10bit]
LigH 支持 FFMS2 Lavf 编解码 [8~10bit]
jspdr (tMod) 支持 FFMS2 Lavf 编解码,MCF 线程管理库
x264 7mod 支持 FFMS2 Lavf 编解码,支持 hqdn3d 降噪,镜像链接:谷歌盘/百度云
Komisar KMod 支持 FFMS2 编解码 [8][10]bit

视频编码后偏色的原因

将编码前后的视频窗口至于同屏下,使用截图软件截图,再使用取色器测量特定区域的颜色: