libaom
- libaom 是 AOMedia Video 1 (AV1) 视频编码格式的参考实现库,由 Alliance for Open Media (AOMedia) 开发和维护。AV1 是一个高效、开放、免专利授权的下一代视频编解码标准,设计目标是提供较高的视频压缩效率,同时适配各种分辨率、码率和平台。
- 下载:
git clone https://aomedia.googlesource.com/aom
- 以下是libaom的一些关键特点:
-
开放和免版税:libaom提供了一个开放源代码的编码器,任何个人和组织都可以免费使用,无需支付版税。
-
高效的编码:libaom旨在提供高效的视频压缩,以适应不同的网络条件和设备性能。
-
跨平台:libaom支持多种操作系统和平台,包括Windows、Linux和macOS。
-
AV1编解码器:libaom实现了AV1编解码器,这是一种基于块的视频编码格式,它使用了一系列先进的压缩技术,如CDEF(Constrained Directional Enhancement Filtering)、CIC(Compound Internal Coding)、和PAET(Probabilistic Angular Early Termination)等。
-
可配置性:libaom提供了多种配置选项,允许开发者根据应用需求调整编码参数。
-
实时编码:libaom支持实时编码,适用于直播和实时通信应用。
-
兼容性:libaom编码的视频可以在支持AV1解码的任何播放器或设备上播放。
-
社区支持:作为一个开源项目,libaom得到了活跃的社区支持,不断有新功能和改进被加入。
libaom是实现AV1视频编码标准的关键部分,它被许多视频播放器、浏览器和视频服务提供商所采用。随着AV1编解码器的成熟和普及,libaom将继续在视频编码领域发挥重要作用。
libaom 分区搜索类型
- 在 libaom 中会根据不同的分区搜索类型进行不同的分区操作,具体的分区搜索类型种类在 speed_features.h 头文件中共用体 PARTITION_SEARCH_TYPE 说明;类型主要有基于 RD 准则搜索分区、固定尺寸分区、基于方差分区、基于机器学习分区。
AV1分区编码
AV1 分区编码(Partition Coding)是 AV1 编码器中用于将图像分割成不同大小的块,并针对每个块选择最优分区模式的一种技术。通过选择不同的分区模式,AV1 编码器能够平衡压缩效率(码率)与图像质量(失真),从而获得更高的压缩性能。
AV1 分区模式
AV1 中定义了多种分区模式,编码器根据当前块的特性(如内容复杂度、大小等)选择最合适的分区模式。常见的分区模式包括:
- PARTITION_NONE:
该模式表示不对当前块进行分区,整个块作为一个单元进行编码。
适用于纹理较简单或者小块的场景。 - PARTITION_SPLIT:
将当前块分成四个相同大小的子块(通常是 2x2 或 4x4 分割),分别进行编码。
适用于较复杂或不规则的图像区域。 - PARTITION_HORZ:
将当前块沿水平方向进行分区,分成上下两部分,分别进行编码。
适用于横向纹理或大致水平方向的图像区域。 - PARTITION_VERT:
将当前块沿垂直方向进行分区,分成左右两部分,分别进行编码。
适用于纵向纹理或大致垂直方向的图像区域。
av1_rd_use_partition 函数
-
函数功能:av1_rd_use_partition 是 AV1 编码器中用于递归分区搜索的重要函数。其作用是根据当前块的大小和内容特性,在各种分区模式中选择最优的分区模式(如 PARTITION_NONE、PARTITION_SPLIT、PARTITION_HORZ、PARTITION_VERT 等),以最小化码率失真代价(Rate-Distortion Cost, RD Cost)。
-
源码内部原理流程图:
-
源码详细注释分析:
/*!\brief AV1 block partition search (partition estimation and partial search).
*
* \ingroup partition_search
* Encode the block by applying pre-calculated partition patterns that are
* represented by coding block sizes stored in the mbmi array. Minor partition
* adjustments are tested and applied if they lead to lower rd costs. The
* partition types are limited to a basic set: none, horz, vert, and split.
* 通过应用预先计算的分区模式来编码,这些模式由存储在 mbmi 数组中的编码块大小表示,
* 如果进行小的分区调整可以降低率失真 RD 成本,则会进行调整,分区类型被限制在基本集中:
* 不分、水平、垂直、分裂
* \param[in] cpi Top-level encoder structure
* \param[in] td Pointer to thread data
* \param[in] tile_data Pointer to struct holding adaptive
data/contexts/models for the tile during encoding
* \param[in] mib Array representing MB_MODE_INFO pointers for mi
blocks starting from the first pixel of the current
block
* \param[in] tp Pointer to the starting token
* \param[in] mi_row Row coordinate of the block in a step size of MI_SIZE
* \param[in] mi_col Column coordinate of the block in a step size of
MI_SIZE
* \param[in] bsize Current block size 当前块大小
* \param[in] rate Pointer to the final rate for encoding the current
block
* \param[in] dist Pointer to the final distortion of the current block
* \param[in] do_recon Whether the reconstruction function needs to be run,
either for finalizing a superblock or providing 是否需要运行重建函数,用于完成超块或未来的子块分区提供参考
reference for future sub-partitions
* \param[in] pc_tree Pointer to the PC_TREE node holding the picked
partitions and mode info for the current block
*
* \remark Nothing is returned. The pc_tree struct is modified to store the
* picked partition and modes. The rate and dist are also updated with those
* corresponding to the best partition found.
*/
void av1_rd_use_partition(AV1_COMP *cpi, ThreadData *td, TileDataEnc *tile_data,
MB_MODE_INFO **mib, TokenExtra **tp, int mi_row,
int mi_col, BLOCK_SIZE bsize, int *rate,
int64_t *dist, int do_recon, PC_TREE *pc_tree) {
AV1_COMMON *const cm = &cpi->common;
const CommonModeInfoParams *const mi_params = &cm->mi_params;
const int num_planes = av1_num_planes(cm);
TileInfo *const tile_info = &tile_data->tile_info;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
const ModeCosts *mode_costs = &x->mode_costs;
const int bs = mi_size_wide[bsize];
const int hbs = bs / 2;
//获取分区平面上下文索引
const int pl = (bsize >= BLOCK_8X8)
? partition_plane_context(xd, mi_row, mi_col, bsize)
: 0;
//获取块的分区类型
const PARTITION_TYPE partition =
(bsize >= BLOCK_8X8) ? get_partition(cm, mi_row, mi_col, bsize)
: PARTITION_NONE;
//根据分区类型获取子块大小
const BLOCK_SIZE subsize = get_partition_subsize(bsize, partition);
RD_SEARCH_MACROBLOCK_CONTEXT x_ctx;
RD_STATS last_part_rdc, none_rdc, chosen_rdc, invalid_rdc;
BLOCK_SIZE bs_type = mib[0]->bsize;
int use_partition_none = 0;
x->try_merge_partition = 0;
//分配内存,确保分区模式上下文存在
if (pc_tree->none == NULL) {
pc_tree->none = av1_alloc_pmc(cpi, bsize, &td->shared_coeff_buf);
if (!pc_tree->none)
aom_internal_error(xd->error_info, AOM_CODEC_MEM_ERROR,
"Failed to allocate PICK_MODE_CONTEXT");
}
PICK_MODE_CONTEXT *ctx_none = pc_tree->none;
if (mi_row >= mi_params->mi_rows || mi_col >= mi_params->mi_cols) return;//边界检查,如果块超过当前帧范围,直接退出
assert(mi_size_wide[bsize] == mi_size_high[bsize]);
// In rt mode, currently the min partition size is BLOCK_8X8.
assert(bsize >= cpi->sf.part_sf.default_min_partition_size);
av1_invalid_rd_stats(&last_part_rdc);
av1_invalid_rd_stats(&none_rdc);
av1_invalid_rd_stats(&chosen_rdc