美文网首页
ffmpeg是如何计算linesize的

ffmpeg是如何计算linesize的

作者: 叶迎宪 | 来源:发表于2019-01-21 20:19 被阅读0次

ffmpeg的AVFrame结构体,有个linesize的成员,记录了某个分量行与行之间相隔了多少个字节。今天测试了三个不同尺寸的jpg,结果为

分辨率 1920*1080,linesize[0]=1920
分辨率 4299*2418,linesize[0]=4320
分辨率 2479*1394,linesize[0]=2496

到底ffmpeg是如何计算这个linesize的呢?需要好好研究一下代码了。

所有的视频解码器,分配解码缓冲都是调用ff_get_buffer完成的。

libavcodec/decode.c
ff_get_buffer
int ret = get_buffer_internal(avctx, frame, flags);

libavcodec/decode.c
get_buffer_internal
ret = avctx->get_buffer2(avctx, frame, flags);

libavcodec/decode.c
avcodec_default_get_buffer2
if ((ret = update_frame_pool(avctx, frame)) < 0)

libavcodec/decode.c
update_frame_pool
avcodec_align_dimensions2(avctx, &w, &h, pool->stride_align);

libavcodec/utils.c
avcodec_align_dimensions2

根据调用栈,找到avcodec_align_dimensions2,里面的核心代码

    switch (s->pix_fmt) {
    case AV_PIX_FMT_YUV420P:

        w_align = 16; //FIXME assume 16 pixel per macroblock
        h_align = 16 * 2; // interlaced needs 2 macroblocks height


    *width  = FFALIGN(*width, w_align);
    *height = FFALIGN(*height, h_align);

    for (i = 0; i < 4; i++)
        linesize_align[i] = STRIDE_ALIGN;

用到的宏FFALIGN定义在 libavutil/macros.h
#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1)

而STRIDE_ALIGN定义在 libavcodec/internal.h

#if HAVE_SIMD_ALIGN_64
#   define STRIDE_ALIGN 64 /* AVX-512 */
#elif HAVE_SIMD_ALIGN_32
#   define STRIDE_ALIGN 32
#elif HAVE_SIMD_ALIGN_16
#   define STRIDE_ALIGN 16
#else
#   define STRIDE_ALIGN 8
#endif

HAVE_SIMD_ALIGN是configure自动检测出来的

simd_align_16_if_any="altivec neon sse"
simd_align_32_if_any="avx"
simd_align_64_if_any="avx512"

对于arm neon, STRIDE_ALIGN的值是16。不难理解,经过avcodec_align_dimensions2之后,宽被对齐到16字节,而pool->stride_align都被置为16。

回到 update_frame_pool 的代码。执行完 avcodec_align_dimensions2 之后是一段循环

        do {
            // NOTE: do not align linesizes individually, this breaks e.g. assumptions
            // that linesize[0] == 2*linesize[1] in the MPEG-encoder for 4:2:2
            ret = av_image_fill_linesizes(linesize, avctx->pix_fmt, w);
            if (ret < 0)
                return ret;
            // increase alignment of w for next try (rhs gives the lowest bit set in w)
            w += w & ~(w - 1);

            unaligned = 0;
            for (i = 0; i < 4; i++)
                unaligned |= linesize[i] % pool->stride_align[i];
        } while (unaligned);

这其中的av_image_fill_linesizes用于计算每个分量的linesize

libavutil/imgutils.c
av_image_fill_linesizes, image_get_linesize

    s = (max_step_comp == 1 || max_step_comp == 2) ? desc->log2_chroma_w : 0;
    shifted_w = ((width + (1 << s) - 1)) >> s;
    linesize = max_step * shifted_w;

这段代码有点绕。不过实际的执行结果是,对于y分量,s=0,shifted_w就是width,max_step=1,因此linesize=width;对于u、v分量,desc->log2_chroma_w为1,shifted_w=(width+1)/2,linesize就是width的一半。

套用上述的输入样例,模拟执行。对于4299*2418, avcodec_align_dimensions2对齐到16字节后,width=4304。第一次调用av_image_fill_linesizes,算出linesize[0]=4304,linesize[1]=linesize[2]=2152。由于pool->stride_align都是16,而2152不是16的倍数,因此循环不通过,需要继续对齐到32字节。对齐到32字节后,linesize[0]=4320,linesize[1]=linesize[2]=2160,可以满足pool->stride_align的要求了。

相关文章

网友评论

      本文标题:ffmpeg是如何计算linesize的

      本文链接:https://www.haomeiwen.com/subject/ohyhjqtx.html