ffmpeg4.2.2-avpicture_fill()的使用心得
1. avpicture_fill()的声明
/**
 * @deprecated use av_image_fill_arrays() instead.
 */
attribute_deprecated
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                   enum AVPixelFormat pix_fmt, int width, int height);

FF_DISABLE_DEPRECATION_WARNINGS
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                   enum AVPixelFormat pix_fmt, int width, int height)
{
    return av_image_fill_arrays(picture->data, picture->linesize,
                                ptr, pix_fmt, width, height, 1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
由上可见, avpicture_fill()函数在ffmpeg4.2.2中就已经被抛弃了, 取而代之的是av_image_fill_arrays()函数. avpicture_fill()中实际调用的也是av_image_fill_arrays()函数, 所以我们拿av_image_fill_arrays()来分析.

2. av_image_fill_arrays()的形参说明
@param dst_data            要填充的数据指针
@param dst_linesize        dst_data中要被填充的线条大小(即linesize, 行宽)
@param src                包含实际图像的缓冲区(可以为空).
@param pix_fmt            图像的像素格式
@param width             图像的像素宽度
@param height            图像的像素高度
@param align             在src中用于行宽对齐的值

@return the size in bytes required for src, a negative error code in case of failure
    该函数返回src所需的大小(以字节为单位), 失败时返回一个负数(错误代码).
1
2
3
4
5
6
7
8
9
10
3. av_image_fill_arrays()的作用
  根据指定的图像参数和提供的数组设置数据指针和行宽(linesizes). avpicture_fill函数将ptr指向的数据填充到picture内,但并没有拷贝,只是将picture结构内的data指针指向了ptr的数据!

4. av_image_fill_arrays()的流程解析
// ffmpeg-4.2.2\libavutil\imgutils.c
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
                         const uint8_t *src, enum AVPixelFormat pix_fmt,
                         int width, int height, int align)
{
    int ret, i;

    // 检查输入的width和height是否可用.
    ret = av_image_check_size(width, height, 0, NULL);
    if (ret < 0)
        return ret;
    /**
     * 1. memset(linesizes, 0, 4*sizeof(linesizes[0])); 将dst_linesize数组的内容清零;
     * 2. 利用av_pix_fmt_desc_get函数得到输入格式的AVPixFmtDescriptor指针;
     * 3. 最后利用image_get_linesize函数设置linesizes数组中每个元素的值.
     * 
     * 可见该函数是用于填充dst_linesize数组.
     */
    ret = av_image_fill_linesizes(dst_linesize, pix_fmt, width);
    if (ret < 0)
        return ret;

    for (i = 0; i < 4; i++)
        /**
         * 由上面的第一节avpicture_fill的实现中可知align=1, 故该宏返回的值还是linesizes[i].
         */
        dst_linesize[i] = FFALIGN(dst_linesize[i], align);

    /**
     * 1. memset(data, 0, sizeof(data[0])*4);    将data数组内的指针置空
     * 2. data[i] = data[i-1] + size[i-1];        将data数组内的指针分别指向ptr内的数据
    */ 
    return av_image_fill_pointers(dst_data, pix_fmt, height, (uint8_t *)src, dst_linesize);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
5. 以前对avpicture_fill()的错误理解
uint8_t *out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
AVFrame *pFrameRGB = av_frame_alloc();

ret = avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB24,
                pCodecCtx->width, pCodecCtx->height);
1
2
3
4
5
错误理解:
  pFrameRGB和out_buffer都是已经申请到的一段内存, 会将pFrameRGB的数据按RGB24格式自动"关联(转换并放置)"到out_buffer。

正确理解:
  pFrameRGB和out_buffer都是已经申请到的一段内存, 但是pFrameRGB只是申请了一段结构体内存, 结构体里面的值是空的, 我们需要使用avpicture_fill()函数来使得pFrameRGB和out_buffer关联起来, pFrameRGB里面使用的是out_buffer所指向的内存空间.