FFmpeg H264/H265边界填充一

在H264和H265编解码标准中,运动补偿的参考像素可越过参考图像的边界。图像边界之外的参考像素是不存在的,标准使用边界处的像素来填充处于边界之外的像素值。

1.  标准填充

标准对填充的算法做了如下的描述,如图1所示的两个图,分别描述了H264和H265亮度分量的标准填充算法(H265色度和亮度分量的填充算法是一致的,但是由于H264色度运动补偿和亮度补偿算法不一致,所以H264色度的填充方式和亮度填充方式略有差异。但是总得来说,他们的原理都是用边界值填充不存在的像素值)。虽然这两个图中的变量名不一样,但表示的是同一个意思。

(a).H264填充算法

(b).H265填充算法

图1. 标准填充算法

       标准算法在描述填充算法时,是一个一个的像素去判断是否越界,然后再去填充相应的像素值。这种方法的性能较差,会影响编解码效率。

2.  FFmpeg填充算法

FFmpeg使用了memcpy的方式,一次填充好几个像素值,该方法可以相对提高填充的效率。图2(a)简要说明了FFmpeg中垂直填充的例子。其中[0,start_y)是超出上边界的部分,这部分像素需要使用start_y处的像素行进行填充;[start_y,end_y]处的像素值是实际存在的,可以直接拷贝;(start_y,bh]处的像素是超出边界下部分的像素值,需要使用边界end_y处的像素值填充。实际中,一般不会出现图2(a)所示的情况,实际中一般出现图2(b)所示的情况,要么是上边界越界,要么是下边界越界,几乎不会出现上下边界都有越界的情况。因为在H264中宏块大小为16×16,而亮度运动补偿为6抽头滤波,如图3所示,所以H264最大只需要21×21的空间来存储当前块所需的参考像素。H265原理与此类似,只是CTU的大小为64,同时使用8抽头滤波,所以最大只需要71×71的空间来存储当前块所需的参考像素。

 

(a)                                  (b)

图2. 垂直填充

图3. 运动补偿

 

3.  FFmpeg源代码分析

void FUNC(ff_emulated_edge_mc)(uint8_t *buf, const uint8_t *src,
                               ptrdiff_t buf_linesize,
                               ptrdiff_t src_linesize,
                               int block_w, int block_h,
                               int src_x, int src_y, int w, int h)
{
    int x, y;
    int start_y, start_x, end_y, end_x;

    if (!w || !h)
        return;

    av_assert2(block_w * sizeof(pixel) <= FFABS(buf_linesize));

    if (src_y >= h) {
        src -= src_y * src_linesize;
        src += (h - 1) * src_linesize;
        src_y = h - 1;
    } else if (src_y <= -block_h) {
        src -= src_y * src_linesize;
        src += (1 - block_h) * src_linesize;
        src_y = 1 - block_h;
    }
    if (src_x >= w) {
        src  += (w - 1 - src_x) * sizeof(pixel);
        src_x = w - 1;
    } else if (src_x <= -block_w) {
        src  += (1 - block_w - src_x) * sizeof(pixel);
        src_x = 1 - block_w;
    }

    start_y = FFMAX(0, -src_y);
    start_x = FFMAX(0, -src_x);
    end_y = FFMIN(block_h, h-src_y);
    end_x = FFMIN(block_w, w-src_x);
    av_assert2(start_y < end_y && block_h);
    av_assert2(start_x < end_x && block_w);

    w    = end_x - start_x;
    src += start_y * src_linesize + start_x * sizeof(pixel);
    buf += start_x * sizeof(pixel);

    // top
    for (y = 0; y < start_y; y++) {
        memcpy(buf, src, w * sizeof(pixel));
        buf += buf_linesize;
    }

    // copy existing part
    for (; y < end_y; y++) {
        memcpy(buf, src, w * sizeof(pixel));
        src += src_linesize;
        buf += buf_linesize;
    }

    // bottom
    src -= src_linesize;
    for (; y < block_h; y++) {
        memcpy(buf, src, w * sizeof(pixel));
        buf += buf_linesize;
    }

    buf -= block_h * buf_linesize + start_x * sizeof(pixel);
    while (block_h--) {
        pixel *bufp = (pixel *) buf;

        // left
        for(x = 0; x < start_x; x++) {
            bufp[x] = bufp[start_x];
        }

        // right
        for (x = end_x; x < block_w; x++) {
            bufp[x] = bufp[end_x - 1];
        }
        buf += buf_linesize;
    }
}


ff_emulated_edge_mc函数第15~18行是判断参考的像素块是否超出参考图像的下边界,如图4(a)所示,当前src对应的坐标为(src_x,src_y),第16行代码的含义是将像素的坐标设置为(src_x,0),第17行代码的含义是将像素的坐标设置为(src_x,h-1),也即将当前图像的y坐标指向最后一行像素所在的位置,第18行将y坐标值src_y同步设置为h-1。

第19~22行是判断参考的像素是否超出参考图像的上边界,如图4(b)所示。24~26行是判断参考像素是否超出参考图像的右边界,如图4(c)所示,27~29行判断参考图像是否超出参考图像的左边界,如图4(d)所示。

32~35行根据15~30行代码计算像素垂直拷贝和水平拷贝边界范围。40和41行确定像素拷贝的源地址和目的地址。

 

    

(a). 超出下边界                    (b). 超出上边界

   

(c).超出右边界                       (d).超出左边界

图4. 超出边界的四种特殊情况

 

第44~46行对应4(b)将超出上边界部分用边界处的值填充。第50~53行将未超出边界区域的像素拷贝填充到临时空间中。第57~60行将超出下边界的部分用下边界行的像素值填充。68~70行用左边界填充超出左边界的部分,73~75行用右边界填充超出右边界的部分。如图5所示各区域的示意图,其中5号区域是在边界以内的区域,2区域是超出上边界的部分,4区域是超出左边界的部分,6是超出右边界的部分,8是超出下边界的部分,1是超出上边界和超出左边界的部分,3是超出上边界和右边界的部分,7是超出下边界和左边界的部分,9是超出下边界和右边界的部分,当然,针对某一个特定的宏块或者CTU,不会同时存在这9个区域。其中2号区域将使用2号和5号区域重叠的像素行填充,4号区域将使用4号和5号区域重叠的像素列填充,6号区域将使用6号区域和5号区域重叠的像素列填充,8号区域将使用8号区域和5号区域重叠的像素行填充,1号区域将使用1号区域和5号区域重叠的那一个像素点填充(也就是1号区域右下角和5号区域左上角重叠的那一个像素点填充),同理,3号区域将使用3号区域和5号区域重叠的那一个像素点填充,7号区域将使用7号区域和5号区域重叠的那一个像素点填充,9号区域将使用9号区域和5号区域重叠的那一个像素点填充。

图5. 各区域示意图

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值