Kvazaar
一个基于3条款BSD许可的开源HEVC编码器
加入IRC网络Libera.Chat上的#ultravideo频道与我们联系,或加入我们的Discord
Kvazaar仍在开发中。速度和RD质量将继续改进。
更多信息请访问 http://ultravideo.fi/#encoder。
目录
使用Kvazaar
示例:
kvazaar --input BQMall_832x480_60.yuv --output out.hevc
必需的参数是输入和输出。如果输入文件名中没有分辨率信息,或者使用管道时,必须另外指定输入分辨率:--input-res=1920x1080
。
默认的输入格式是8位yuv420p,10位则是yuv420p10le。可以使用--input-format
和--input-bitdepth
选择输入格式和位深。
可以通过--preset
选择速度和压缩质量,或手动设置选项。
参数
Kvazaar v2.3.1 2024-04-10
Kvazaar许可证: 3条款BSD
用法:
kvazaar -i <输入> --input-res <宽度>x<高度> -o <输出>
必需:
-i, --input <文件名> : 输入文件
--input-res <分辨率> : 输入分辨率 [auto]
- auto: 从文件名检测。
- <整数>x<整数>: 宽度乘以高度
-o, --output <文件名> : 输出文件
预设:
--preset <预设> : 设置选项为预设 [medium]
- ultrafast, superfast, veryfast, faster,
fast, medium, slow, slower, veryslow
placebo
输入:
-n, --frames <整数> : 要编码的帧数 [所有]
--seek <整数> : 开始编码的第一帧 [0]
--input-fps <分子>[/<分母>] : 输入视频的帧率 [25]
--source-scan-type <字符串> : 源扫描类型 [progressive]
- progressive: 逐行扫描
- tff: 上场优先
- bff: 下场优先
--input-format <字符串> : P420 或 P400 [P420]
--input-bitdepth <整数> : 8-16 [8]
--loop-input : 永远重复读取输入文件
--input-file-format <字符串> : 输入文件格式 [auto]
- auto: 根据文件扩展名检查格式
- y4m (跳过帧头)
- yuv
选项:
--help : 打印此帮助信息并退出。
--version : 打印版本信息并退出。
--(no-)aud : 使用访问单元分隔符。[默认禁用]
--debug <文件名> : 输出内部重建。
--(no-)cpuid : 启用运行时CPU优化。[默认启用]
--hash <字符串> : 解码图像哈希 [校验和]
- none:0字节
- checksum:18字节
- md5:56字节
--(no-)psnr : 计算帧的PSNR。[默认启用]
--(no-)info : 添加编码器信息SEI。[默认启用]
--(no-)enable-logging : 启用常规编码器性能日志记录,
错误消息始终显示。[默认启用]
--crypto <字符串> : 选择性加密。必须在编译时启用加密支持。
可以是'on'或'off'或以'+'分隔的功能列表。[默认off]
- on:启用所有加密功能。
- off:禁用选择性加密。
- mvs:运动矢量幅度。
- mv_signs:运动矢量符号。
- trans_coeffs:系数幅度。
- trans_coeff_signs:系数符号。
- intra_pred_modes:帧内预测模式。
--key <字符串> : 加密密钥 [16,213,27,56,255,127,242,112,
97,126,197,204,25,59,38,30]
--stats-file-prefix : 用于统计文件的前缀,包括每个CTU的
比特、lambda、失真和qp。这些文件用于
调试,除非定义了前缀,否则不会写入。
视频结构:
-q, --qp <整数> : 量化参数 [22]
-p, --period <整数> : 帧内图像周期 [64]
- 0: 仅第一帧为帧内编码。
- 1: 所有帧均为帧内编码。
- N: 每N帧插入一个帧内编码帧。
--vps-period <整数> : 视频参数集重发送频率 [0]
- 0: 仅在第一帧发送VPS。
- N: 每N个帧内编码帧发送一次VPS。
-r, --ref <整数> : 参考帧数量,范围1..15 [4]
--gop <字符串> : GOP结构 [lp-g4d3t1]
- 0: 禁用
- 8: 长度为8的B帧金字塔
- 16: 长度为16的B帧金字塔
- lp-<字符串>: 低延迟P/B帧GOP
(例如 lp-g8d4t2,详见README)
--intra-qp-offset <整数>: 帧内编码帧的QP偏移量 [-51..51] [自动]
- N: 设置QP偏移量为N。
- auto: 根据GOP长度自动选择偏移量。
--(no-)open-gop : 使用开放式GOP配置。[启用]
--cqmfile <文件名> : 从文件读取自定义量化矩阵。
--scaling-list <字符串>: 设置缩放列表模式。[关闭]
- off: 禁用缩放列表。
- custom: 使用自定义列表(需配合--cqmfile)。
- default: 使用默认列表。
--bitrate <整数> : 目标比特率 [0]
- 0: 禁用码率控制。
- N: 目标每秒N比特。
--rc-algorithm <字符串>: 选择使用的码率控制算法。[lambda]
- lambda: 来自以下论文的码率控制:
DOI: 10.1109/TIP.2014.2336550
- oba: DOI: 10.1109/TCSVT.2016.2589878
--(no-)intra-bits : 使用基于Hadamard成本的帧内帧分配。
GOP 8默认开启,lp-gop默认关闭
--(no-)clip-neighbour : 在基于oba的码率控制中,是否将lambda值限制在
同一帧的CTU或前一帧的范围内。
随机访问GOP默认开启,低延迟GOP默认禁用。
--(no-)lossless : 使用无损编码。[禁用]
--mv-constraint <字符串> : 约束运动矢量。[无]
- none: 无约束
- frametile: 在分片内约束。
- frametilemargin: 更严格的约束。
--roi <文件名> : 使用增量QP图进行感兴趣区域编码。
从文件读取增量QP值数组。
支持并自动检测文本和二进制文件(.txt/.bin)。
如果未找到已知扩展名,则视为文本文件。
文件可包含一个或多个ROI帧,每帧格式如下:
QP增量图的宽度和高度,随后是宽度*高度个
以光栅顺序排列的增量QP值。二进制格式中,
宽度和高度为32位整数,增量QP值为有符号8位值。
图可为任意大小,将缩放至视频尺寸。
文件读取到末尾时将循环。
参见examples文件夹中的roi.txt。
--set-qp-in-cu : 在CU级别设置QP,保持PPS中的pic_init_qp_minus26
和片段头中的slice_qp_delta为零。
--(no-)erp-aqp : 对使用等距柱面投影的360度视频使用自适应QP。[禁用]
--level <数字> : 在输出中使用指定的HEVC级别,如超出级别限制则报错。[6.2]
- 1, 2, 2.1, 3, 3.1, 4, 4.1, 5, 5.1, 5.2, 6,
6.1, 6.2
--force-level <数字> : 与--level相同,但超出限制时给出警告而非错误。
--high-tier : 与--level一起使用。编码时使用高层级比特率限制
而非主层级限制。高层级要求级别4或更高。
--(no-)vaq <整数> : 启用方差自适应量化,强度范围1..20。推荐值:5。
[禁用]
压缩工具:
--(no-)deblock <beta:tc> : 去块滤波器。[0:0]
- beta:介于-6和6之间
- tc:介于-6和6之间
--sao <string> : 样本自适应偏移 [full]
- off:禁用SAO
- band:仅带偏移
- edge:仅边缘偏移
- full:完整SAO
--(no-)rdoq : 率失真优化量化 [已启用]
--(no-)rdoq-skip : 跳过4x4块的RDOQ。[已禁用]
--(no-)signhide : 符号隐藏 [已禁用]
--(no-)smp : 对称运动分区 [已禁用]
--(no-)amp : 非对称运动分区 [已禁用]
--rd <integer> : 模式搜索复杂度 [0]
- 0:如果帧间足够好则跳过帧内。
- 1:使用SATD进行粗略帧内模式搜索。
- 2:使用SSE细化模式搜索。
- 3:为帧间和4x4帧内色度模式搜索提供更多SSE候选。
- 4:为两者提供更多SSE候选。
- 5:尝试所有帧内模式。
--(no-)mv-rdo : 率失真优化运动矢量代价
[已禁用]
--(no-)zero-coeff-rdo : 如果设置为帧间CU,检查强制零残差是否改善RD代价。[已启用]
--(no-)full-intra-search : 在粗搜索期间尝试所有帧内模式。
[已禁用]
--(no-)intra-chroma-search : 测试非派生帧内色度模式。
[已禁用]
--(no-)transform-skip : 尝试变换跳过 [已禁用]
--me <string> : 整数运动估计算法 [hexbs]
- hexbs: 六边形基搜索
- tz: 测试区域搜索
- full: 全搜索
- full8, full16, full32, full64
- dia: 菱形搜索
--me-steps <integer> : 运动估计搜索步骤限制。仅
影响'hexbs'和'dia'。[-1]
--subme <integer> : 分像素运动估计级别 [4]
- 0: 仅整数运动估计
- 1: + 1/2像素水平和垂直
- 2: + 1/2像素对角线
- 3: + 1/4像素水平和垂直
- 4: + 1/4像素对角线
--(no-)fast-bipred : 仅执行快速双向预测搜索。[已启用]
--pu-depth-inter <int>-<int> : 帧间预测单元大小 [0-3]
- 0, 1, 2, 3: 从64x64到8x8
- 接受用','分隔的值列表,
为每个GOP层设置单独的深度
(可以省略值以对相应层使用第一个值)。
--pu-depth-intra <int>-<int> : 帧内预测单元大小 [1-4]
- 0, 1, 2, 3, 4: 从64x64到4x4
- 接受用','分隔的值列表,
为每个GOP层设置单独的深度
(可以省略值以对相应层使用第一个值)。
--ml-pu-depth-intra : 使用机器学习树预测pu-depth-intra,
覆盖--pu-depth-intra参数。[已禁用]
--(no-)combine-intra-cus: 编码器是否尝试在较低深度上编码cu,
即使在该深度上未执行搜索。仅当cu
绝对不能大于搜索限制时才应禁用。
[已启用]
--force-inter : 强制编码器始终使用帧间。
这主要用于调试,不保证产生
合理的比特流或正常工作。[已禁用]
--tr-depth-intra <int> : 帧内块的变换分割深度 [0]
--(no-)bipred : 双向预测 [已禁用]
--cu-split-termination <string> : CU分割搜索终止 [zero]
- off: 不提前终止。
- zero: 当残差为零时终止。
--me-early-termination <string> : 运动估计终止 [on]
- off: 不提前终止。
- on: 提前终止。
- sensitive: 更早终止。
--fast-residual-cost <int> : 当QP低于限制时跳过残差系数的CABAC代价。[0]
--fast-coeff-table <string> : 从文件读取残差系数的自定义权重,
而不使用默认值 [default]
--fast-rd-sampling : 启用快速系数表生成的学习数据采样
--fastrd-accuracy-check : 评估快速系数预测的准确性
--fastrd-outdir : 用于输出采样数据或准确性数据的目录,
输出到<fastrd-outdir>/0.txt到50.txt,
每个QP估计的块对应一个文件
--(no-)intra-rdo-et : 在rdo阶段仅检查帧内模式,直到
找到零系数CU。[已禁用]
--(no-)early-skip : 尝试从合并候选中找到跳过cu。
如果找到跳过,不进行进一步搜索。
对于rd=0..1:尝试第一个候选。
对于rd=2及以上:基于亮度SATD代价选择最佳候选项。[已启用]
--max-merge <整数> : 最大合并候选项数量,1至5 [5]
--(no-)implicit-rdpcm : 隐式残差DPCM。目前仅支持无损编码。[已禁用]
--(no-)tmvp : 时域运动矢量预测 [已启用]
并行处理:
--threads <整数> : 使用的线程数 [自动]
- 0: 使用主线程处理所有内容。
- N: 使用N个线程进行编码。
- auto: 自动选择。
--owf <整数> : 帧级并行 [自动]
- N: 同时处理N+1帧。
- auto: 自动选择。
--(no-)wpp : 波前并行处理。[已启用]
启用分块会自动禁用WPP。
要在分块的同时启用WPP,请在启用分块后
重新启用WPP。但是,在分块的同时启用WPP
是一个实验性功能,因为它不被任何HEVC配置支持。
--tiles <int>x<int> : 将画面划分为宽x高的均匀分块。
--tiles-width-split <字符串>|u<int> :
- <字符串>: 以逗号分隔的分块列像素坐标列表。
- u<int>: 均匀宽度的分块列数。
--tiles-height-split <字符串>|u<int> :
- <字符串>: 以逗号分隔的分块行像素坐标列表。
- u<int>: 均匀高度的分块行数。
--slices <字符串> : 控制切片的使用方式。
- tiles: 将分块放入独立切片。
- wpp: 将行放入依赖切片。
- tiles+wpp: 同时执行两者。
--partial-coding <x-偏移>!<y-偏移>!<切片宽度>!<切片高度>
: 编码部分帧。
各部分必须合并以形成有效的比特流。
X和Y是CTU偏移。
切片宽度和高度必须能被CTU像素整除,
除非是最后一行/列CTU。
此参数由kvaShare使用。
视频可用性信息:
--sar <宽度:高度> : 指定采样宽高比
--overscan <字符串> : 指定裁剪过扫描设置 [未定义]
- undef, show, crop
--videoformat <字符串> : 指定视频格式 [未定义]
- undef, component, pal, ntsc, secam, mac
--range <字符串> : 指定色彩范围 [tv]
- tv, pc
--colorprim <字符串> : 指定色彩原色 [未定义]
- undef, bt709, bt470m, bt470bg,
smpte170m, smpte240m, film, bt2020
--transfer <字符串> : 指定传输特性 [未定义]
- undef, bt709, bt470m, bt470bg,
smpte170m, smpte240m, linear, log100,
log316, iec61966-2-4, bt1361e,
iec61966-2-1, bt2020-10, bt2020-12
--colormatrix <字符串> : 指定色彩矩阵设置 [未定义]
- undef, bt709, fcc, bt470bg, smpte170m,
smpte240m, GBR, YCgCo, bt2020nc, bt2020c
--chromaloc <整数> : 指定色度采样位置(0到5)[0]
已弃用的参数:(可能在某个时候会被移除)
-w, --width <整数> : 使用 --input-res。
-h, --height <整数> : 使用 --input-res。
### LP-GOP语法
LP-GOP语法为"lp-g(num)d(num)t(num)",其中
- g = GOP长度。
- d = GOP层数。
- t = 时间缩放需要跳过的参考帧数,4表示只需解码每第四帧。
QP
+4 o o o o
+3 o o o o o o
+2 o o o o ooooooo
+1 o o o o o o ooooooooo
g8d4t1 g8d3t1 g8d2t1 g8d1t1
## 预设
预设的名称与x264相同:ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow和placebo。预设的效果列在下表中,为适应GitHub的布局,名称已被缩写。
| | 0-uf | 1-sf | 2-vf | 3-fr | 4-f | 5-m | 6-s | 7-sr | 8-vs | 9-p |
| -------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| rd | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 2 | 2 |
| pu-depth-intra | 2-3 | 2-3 | 2-3 | 2-3 | 1-3 | 1-4 | 1-4 | 1-4 | 1-4 | 1-4 |
| pu-depth-inter | 1-2 | 1-2 | 1-3 | 1-3 | 1-3 | 0-3 | 0-3 | 0-3 | 0-3 | 0-3 |
| me | hexbs | hexbs | hexbs | hexbs | hexbs | hexbs | hexbs | hexbs | tz | tz |
| gop | 8 | 8 | 8 | 8 | 8 | 16 | 16 | 16 | 16 | 16 |
| ref | 1 | 1 | 1 | 1 | 2 | 4 | 4 | 4 | 4 | 4 |
| bipred | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| deblock | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| signhide | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| subme | 0 | 2 | 2 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
| sao | 关闭 | 全开 | 全开 | 全开 | 全开 | 全开 | 全开 | 全开 | 全开 | 全开 |
| rdoq | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
| rdoq-skip | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| transform-skip | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| mv-rdo | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| full-intra-search | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| smp | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| amp | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| cu-split-termination | 零 | 零 | 零 | 零 | 零 | 零 | 零 | 零 | 零 | 关闭 |
| me-early-termination | 敏感 | 敏感 | 敏感 | 敏感 | 敏感 | 开启 | 开启 | 关闭 | 关闭 | 关闭 |
| intra-rdo-et | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| early-skip | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| fast-residual-cost | 28 | 28 | 28 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| max-merge | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 |
## Kvazaar库
请参阅[kvazaar.h](https://github.com/ultravideo/kvazaar/blob/master/src/kvazaar.h)获取库API及其文档。
在Windows上使用Kvazaar静态库时,必须定义宏`KVZ_STATIC_LIB`。在其他平台上,这不是严格要求的。
所需的链接器和编译器标志可以通过pkg-config获得。
## 编译 Kvazaar
如果您在编译源代码时遇到问题,请在 Github 上提交一个[issue](https://github.com/ultravideo/kvazaar/issues)。其他人可能会遇到相同的问题,而且构建过程可能还有很多需要改进的地方。我们希望使这个过程尽可能简单。
### Autotools
根据平台不同,使用 Autotools 编译 Kvazaar 可能需要一些额外的工具。对于 Ubuntu,需要安装的软件包有 `automake autoconf libtool m4 build-essential`。
运行以下命令来编译和安装 Kvazaar:
./autogen.sh
./configure
make
sudo make install
sudo ldconfig
查看 `./configure --help` 了解更多选项。
**使用 Visual Studio 构建共享库时,测试将无法链接,但主程序仍然可以正常工作**
### MinGW 上的 Autotools
在 MinGW 环境中,建议使用 Clang 而不是 GCC。GCC 也可以工作,但由于 2012 年 GCC 的一个已知问题,AVX2 优化将被禁用,因此性能会大大降低。不要运行 `./configure`,而是运行:
CC=clang ./configure
以使用 Clang 构建 Kvazaar。
### CMake
根据平台不同,使用 CMake 编译 Kvazaar 可能需要一些额外的工具。对于 Ubuntu,需要安装的软件包有 `build-essential cmake`。
### OS X
- 安装 Homebrew
- 运行 ```brew install automake libtool yasm```
- 参考 Autotools 说明
### Visual Studio
- 至少需要 VisualStudio 2015.2。
- 项目文件可以在 build/ 目录下找到。
- 需要在 %PATH% 中包含外部 [vsyasm.exe](http://yasm.tortall.net/Download.html)
### Docker
本项目包含一个 [Dockerfile](https://github.com/ultravideo/kvazaar/blob/master/./Dockerfile),可以用于 Docker 构建。Kvazaar 也可以在 Docker Hub 的 [`ultravideo/kvazaar`](https://hub.docker.com/r/ultravideo/kvazaar/) 中找到。
使用 Docker 构建:`docker build -t kvazaar .`
使用示例:`docker run -i -a STDIN -a STDOUT kvazaar -i - --input-res=320x240 -o - < testfile_320x240.yuv > out.265`
更多示例,请参见 [Dockerfile](https://github.com/ultravideo/kvazaar/blob/master/./Dockerfile)
### 可视化(仅限 Windows)
在 `visualizer` 分支中编译 `kvazaar_cli` 项目将生成一个启用可视化功能的 Kvazaar 可执行文件。
额外要求:[`SDL2`](https://www.libsdl.org/download-2.0.php),[`SDL2-ttf`](https://www.libsdl.org/projects/SDL_ttf/)。
`visualizer_extras` 目录应添加到与 kvazaar 项目目录相同的目录级别。其中应包含从开发库 zip 包中找到的 `include` 和 `lib` 目录。
运行可视化工具时,`SDL2.dll`、`SDL2_ttf.dll`、`libfreetype-6.dll` 和 `zlib1.dll` 应放在工作目录中(即编译 `kvazaar_cli` 项目/解决方案后 `kvazaar.exe` 所在的文件夹)。所需的 `.dll` 文件可以在前面提到的 `lib` 文件夹(`lib\x64`)中找到。
注意:解决方案应在 Visual Studio 中的 x64 平台上编译。
如果使用块信息工具,可选的字体文件 `arial.ttf` 应放在工作目录中。
## 论文
请引用 Kvazaar 的[这篇论文](https://dl.acm.org/citation.cfm?doid=2964284.2973796):
```M. Viitanen, A. Koivula, A. Lemmetti, A. Ylä-Outinen, J. Vanne, and T. D. Hämäläinen, Kvazaar: open-source HEVC/H.265 encoder, in Proc. ACM Int. Conf. Multimedia, Amsterdam, The Netherlands, Oct. 2016.```
或使用 BibTex 格式:
@inproceedings{Kvazaar2016, author = {Viitanen, Marko and Koivula, Ari and Lemmetti, Ari and Yl"{a}-Outinen, Arttu and Vanne, Jarno and H"{a}m"{a}l"{a}inen, Timo D.}, title = {Kvazaar: Open-Source HEVC/H.265 Encoder}, booktitle = {Proceedings of the 24th ACM International Conference on Multimedia}, year = {2016}, isbn = {978-1-4503-3603-1}, location = {Amsterdam, The Netherlands}, url = {http://doi.acm.org/10.1145/2964284.2973796}, }
## 为 Kvazaar 做贡献
我们很乐意在 Github 上查看 pull 请求。还有很多工作要做。
### 代码文档
您可以通过运行命令 "doxygen docs.doxy" 生成 Doxygen 文档页面。以下是模块结构的大致草图:
![Kvazaar 模块层次结构](https://yellow-cdn.veclightyear.com/0a4dffa0/fa69f8cd-01a8-4b48-89d9-7bf613863787.png)
### 版本控制,我们尝试遵循以下约定:
- 主分支始终产生可工作的比特流(可以用 HM 解码)。
- 新功能和重大更改/修复的提交首先放在一个合理命名的功能分支中,然后再合并到主分支。
- 始终将功能分支合并到主分支,而不是反过来,必要时禁用快进。
- 每次提交都应该至少能够编译。
### 测试
- 主要的自动测试方式是使用Travis CI。提交、分支和拉取请求都会自动进行测试。
- 使用Valgrind检查未初始化变量等问题。
- 使用VTM检查比特流的有效性。
- 在Linux上使用GCC和Clang,在OSX上使用Clang进行编译检查。
- Windows的msys2和msvc构建在Appveyor上自动检查。
- 如果你的更改影响了比特流,请使用HM解码以确保不会出现校验和错误或断言。
- 如果你的更改不应该改变比特流,请确认它们确实没有改变。
- 自动压缩质量测试正在开发中。
### 单元测试
- tests目录下有一些单元测试。我们希望能有更多。
- Visual Studio项目将单元测试链接到编码器实际使用的.lib文件。目前还没有Makefile。
- 单元测试使用"greatest"单元测试框架。它作为子模块包含,但获取它需要在kvazaar的根目录下运行以下命令:
git submodule init
git submodule update
- 在Linux上,运行```make test```。
### 代码风格
我们尽量遵循以下约定:
- 使用C99,但不包括Visual Studio 2015不支持的特性(如可变长数组)。
- 允许并鼓励使用//注释。
- 遵循代码中已建立的整体约定。
- 使用2个空格缩进(不用制表符)。
- 控制逻辑的{与行同级,函数的{在下一行。
- 引用和解引用符号紧贴变量名。
- 变量名使用小写字母,单词间用下划线分隔。
- 尽可能将最大行长度限制在79个字符。
- 仅在模块内使用的函数不应在模块头文件中定义。必要时可以在.c文件开头定义。
- 在头文件中定义的符号以kvz_或KVZ_为前缀。
- 按字母顺序包含头文件。