libavcodec 中的 write_back_intra_pred_mode() 函数有什么作用?
what does write_back_intra_pred_mode() function from libavcodec do?
Bellow 是 libavcodec/h264.h 中定义的 ffmpeg 函数:
static av_always_inline void write_back_intra_pred_mode(const H264Context *h,
H264SliceContext *sl)
{
int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy];
int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache;
AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4);
i4x4[4] = i4x4_cache[7 + 8 * 3];
i4x4[5] = i4x4_cache[7 + 8 * 2];
i4x4[6] = i4x4_cache[7 + 8 * 1];
}
这个函数有什么作用?
你也能解释一下函数体吗?
该函数更新帧内预测模式(以 4x4 块分辨率)的 frame-wide 缓存,位于每个切片的变量 sl->intra4x4_pred_mode
或整个帧的 h->intra4x4_pred_mode
中。此缓存稍后在 h264_mvpred.h
中使用,特别是 fill_decode_caches()
行周围的函数 510-528,以设置上下文(left/above 邻居)块信息,用于解码位于下方的后续 intra4x4 块或当前 4x4 块集的右侧。
[编辑]
OK,这里再介绍一些变量的设计。 sl->mb_xy 是 sl->mb_x + sl->mb_y * mb_stride。将 mb_stride 视为图像宽度(以 mbs 为单位)的填充版本。所以mb_xy就是当前宏块的raster-ordered索引。一些变量在块 (4x4) 而不是宏块 (16x16) 分辨率中索引,因此要在单位之间转换,您使用 mb2br_xy。这应该可以解释 frame-wide 缓存 (intra4x4_pred_mode/i4x4) 的布局。
现在,本地 per-macroblock 缓存,它包含当前宏块的 4x4 条目,加上 left/above 边缘条目,所以 5x5。然而,将某个值乘以 5 在 lea 指令中需要 2 个寄存器,而 8 只需要一个,所以我们更喜欢 8(更一般地说,我们更喜欢 2 的幂)。所以分辨率变成8(width)x5(height)一共40个entry,其中每行左边3个未使用,第四个是左边,右边4个是当前宏块的实际entry。最上面一行,下面4行是当前宏块的实际条目
因此,从缓存到 frame-wide 缓存的备份使用 8 作为步幅,4/3/2/1 作为 y=3/2/1/0 的索引和 4-7 作为索引对于 x=0-3。在备份中,您会注意到我们实际上并没有复制整个 4x4 块,而只是复制最后一行(AVCOPY32 复制 4 个条目,offset=4[y=3]+8[stride]*4[x=0] ) 和其他每一行的 right-most 条目 (7[x=3]+8[stride]*1-3[y=0-2])。那是因为只有 right/bottom 边缘作为未来宏块解码的 top/left 上下文是有趣的,所以其余的是不必要的。
如图所示,i4x4_pred_mode_cache 的布局是:
x x x TL T0 T1 T2 T3
x x x L0 00 01 02 03
x x x L1 10 11 12 13
x x x L2 20 21 22 23
x x x L3 30 31 32 33
x 表示未使用,TL 是左上角,Ln 是左[n],Tn 是前[n],编号条目 ab 是 y=a,x=b 对于 16x16 宏块中的 4x4 块。
您可能想知道为什么 TL 放在 [3] 而不是 [0],即为什么不是 TL T0-3 x x x
(其余行依此类推);这样做的原因是在 frame-wide 和 block-local 缓存中,T0-3(以及 00-03、10-13、20-23、30-33)是 4 个字节对齐的 4 组模式,这意味着在大多数机器上,在一条指令 (COPY32) 中复制 4 个条目要快得多。如果我们进行未对齐的复制,这会增加额外的开销并(稍微)减慢解码速度。
Bellow 是 libavcodec/h264.h 中定义的 ffmpeg 函数:
static av_always_inline void write_back_intra_pred_mode(const H264Context *h,
H264SliceContext *sl)
{
int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy];
int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache;
AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4);
i4x4[4] = i4x4_cache[7 + 8 * 3];
i4x4[5] = i4x4_cache[7 + 8 * 2];
i4x4[6] = i4x4_cache[7 + 8 * 1];
}
这个函数有什么作用?
你也能解释一下函数体吗?
该函数更新帧内预测模式(以 4x4 块分辨率)的 frame-wide 缓存,位于每个切片的变量 sl->intra4x4_pred_mode
或整个帧的 h->intra4x4_pred_mode
中。此缓存稍后在 h264_mvpred.h
中使用,特别是 fill_decode_caches()
行周围的函数 510-528,以设置上下文(left/above 邻居)块信息,用于解码位于下方的后续 intra4x4 块或当前 4x4 块集的右侧。
[编辑]
OK,这里再介绍一些变量的设计。 sl->mb_xy 是 sl->mb_x + sl->mb_y * mb_stride。将 mb_stride 视为图像宽度(以 mbs 为单位)的填充版本。所以mb_xy就是当前宏块的raster-ordered索引。一些变量在块 (4x4) 而不是宏块 (16x16) 分辨率中索引,因此要在单位之间转换,您使用 mb2br_xy。这应该可以解释 frame-wide 缓存 (intra4x4_pred_mode/i4x4) 的布局。
现在,本地 per-macroblock 缓存,它包含当前宏块的 4x4 条目,加上 left/above 边缘条目,所以 5x5。然而,将某个值乘以 5 在 lea 指令中需要 2 个寄存器,而 8 只需要一个,所以我们更喜欢 8(更一般地说,我们更喜欢 2 的幂)。所以分辨率变成8(width)x5(height)一共40个entry,其中每行左边3个未使用,第四个是左边,右边4个是当前宏块的实际entry。最上面一行,下面4行是当前宏块的实际条目
因此,从缓存到 frame-wide 缓存的备份使用 8 作为步幅,4/3/2/1 作为 y=3/2/1/0 的索引和 4-7 作为索引对于 x=0-3。在备份中,您会注意到我们实际上并没有复制整个 4x4 块,而只是复制最后一行(AVCOPY32 复制 4 个条目,offset=4[y=3]+8[stride]*4[x=0] ) 和其他每一行的 right-most 条目 (7[x=3]+8[stride]*1-3[y=0-2])。那是因为只有 right/bottom 边缘作为未来宏块解码的 top/left 上下文是有趣的,所以其余的是不必要的。
如图所示,i4x4_pred_mode_cache 的布局是:
x x x TL T0 T1 T2 T3
x x x L0 00 01 02 03
x x x L1 10 11 12 13
x x x L2 20 21 22 23
x x x L3 30 31 32 33
x 表示未使用,TL 是左上角,Ln 是左[n],Tn 是前[n],编号条目 ab 是 y=a,x=b 对于 16x16 宏块中的 4x4 块。
您可能想知道为什么 TL 放在 [3] 而不是 [0],即为什么不是 TL T0-3 x x x
(其余行依此类推);这样做的原因是在 frame-wide 和 block-local 缓存中,T0-3(以及 00-03、10-13、20-23、30-33)是 4 个字节对齐的 4 组模式,这意味着在大多数机器上,在一条指令 (COPY32) 中复制 4 个条目要快得多。如果我们进行未对齐的复制,这会增加额外的开销并(稍微)减慢解码速度。