FFmpeg中的色彩空间与像素格式3-像素格式

FFmpeg 中的色彩与像素系列文章如下:
[1]. FFmpeg中的色彩空间与像素格式1-色彩空间基础
[2]. FFmpeg中的色彩空间与像素格式2-RGB/YUV色彩空间
[3]. FFmpeg中的色彩空间与像素格式3-像素格式

4. FFmpeg 中的像素格式

FFmpeg 中的像素格式是 pixel format,每种像素格式包含有色彩空间、采样方式、存储模式、位深等信息。

4.1 基础概念

与像素格式相关的几个基础概念如下:

pixel_format:像素格式,图像像素在内存中的排列格式。一种像素格式包含有色彩空间、采样方式、存储模式、位深等信息,其中体现的最重要信息就是存储模式,具体某一类的存储模式参照本文第 2 节、第 3 节。

bit_depth: 位深,指每个分量(Y、U、V、R、G、B 等)单个采样点所占的位宽度。

例如对于 yuv420p(位深是8)格式而言,每一个 Y 样本、U 样本和 V 样本都是 8 位的宽度,只不过在水平方向和垂直方向,U 样本数目和 V 样本数目都只有 Y 样本数目的一半。而 bpp (Bits Per Pixel)则是将图像总比特数分摊到每个像素上,计算出平均每个像素占多少个 bit,例如 yuv420p 的 bpp 是 12,表示平均每个像素占 12 bit(Y占8位、U占2位、V占2位),实际每个 U 样本和 V 样本都是 8 位宽度而不是 2 位宽度。

plane: 存储图像中一个或多个分量的一片内存区域。一个 plane 包含一个或多个分量。planar 存储模式中,至少有一个分量占用单独的一个 plane,具体到 yuv420p 格式有 Y、U、V 三个 plane,nv12 格式有 Y、UV 两个 plane,gbrap 格式有 G、B、R、A 四个 plane。packed 存储模式中,因为所有分量的像素是交织存放的,所以 packed 存储模式只有一个 plane。

slice: slice 是 FFmpeg 中使用的一个内部结构,在 codec、filter 中常有涉及,通常指图像中一片连续的行,表示将一帧图像分成多个片段。注意 slice 是针对图像分片,而不是针对 plane 分片,一帧图像有多个 plane,一个 slice 里同样包含多个 plane。

stride/pitch: 一行图像的一个 plane 中一行数据的宽度。有对齐要求,计算公式如下:

stride 值 = 图像宽度 * 分量数 * 单样本位宽度 / 水平子采样因子 / 8

其中,图像宽度表示图像宽度是多少个像素,分量数指当前 plane 包含多少个分量(如 rgb24 格式一个 plane 有 R、G、B 三个分量),单样本位宽度指某分量的一个样本在考虑对齐后在内存中占用的实际位数(例如位深 8 占 8 位宽,位深 10 实际占 16 位宽,对齐值与平台相关),水平子采样因子指在水平方向上每多少个像素采样出一个色度样本(亮度样本不进行下采样,所以采样因子总是 1)。

需要注意的是,stride 考虑的是 plane 中的一行。对 yuv420p 格式而言,Y 分量是完全采样,因此一行 Y 样本数等于图像宽度,U 分量和 V 分量水平采样因子是 2(每两个像素采样出一个U样本和V样本),因此一行 U 样本数和一行 V 样本数都等于图像宽度的一半。U 分量和 V 分量垂直采样因子也是 2,因此 U 分量和 V 分量的行数少了,只有图像高度的一半,但垂直方向的采样率并不影响一个 plane 的 stride 值,因为 stride 的定义决定了其值只取决于水平方向的采样率。

若源图像像素格式是 yuv420p(有 Y、U、V 三个 plane),位深是 8(每一个Y样本、U样本、V样本所占位宽度是 8 位),分辨率是 1280x720,则在 Y plane 的一行数据中,有 1280 个 Y 样本,占用 1280 个字节,stride 值是 1280;在 U plane 的一行数据中,有 640 个 U 样本,占用 640 个字节,stride 值是 640;在 V plane 的一行数据中,有 640 个样本,占用 640 个字节,stride 值是 640。

若源图像像素格式是 yuv420p10(有 Y、U、V 三个 plane),位深是 10 (内存对齐后每个样本占 16 位),分辨率仍然是 1280x720,则 Y plane 的 stride 值为 1280 x 16 / 8 = 2560,U plane stride 值为 640 x 16 / 8 = 1280,V plane stride 值为 640 x 16 / 8 = 1280。

若源图像像素格式是 yuv420p16le(有 Y、U、V 三个 plane),位深是 16,分辨率仍然是 1280x720,则 Y plane 的 stride 值为 1280 x 16 / 8 = 2560,U plane stride 值为 640 x 16 / 8 = 1280,V plane stride 值为 640 x 10 / 8 = 1280。

若源图像像素格式是 p010le(有 Y、UV 两个 plane),位深是 10 (内存对齐后,每个样本占 16 位),分辨率仍然是 1280x720,则 Y plane 的 stride 值为 1280 x 16 / 8 = 2560,UV plane stride 值为 640 x 2 x 16 / 8 = 2560。

若源图像像素格式是 bgr24(有 BGR 一个 plane),位深是 8,分辨率仍然是 1280x720。因 bgr24 像素格式是 packed 存储模式,每个像素 R、G、B 三个采样点交织存放,内存区的排列形式为 BGRBGR...,因此可以认为它只有一个 plane,此 plane 中一行图像有 1280 个 R 样本,1280 个 G 样本,1280 个 B 样本,此 plane 的 stride 值为 1280 x 3 x 8 / 8 = 3840。

4.2 数据结构

4.2.1 AVPixelFormat

AVPixelFormat 定义了像素格式 ID,AVPixelFormat 由FFmpeg 内部代码使用,用来标识某一像素格式。例如,在 FFmpeg 转码命令行中指定了 yuv420p 像素格式,则对应此结构体中的 AV_PIX_FMT_YUV420P。几个有代表性的像素格式列举如下,其他格式省略:

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
    AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
    AV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp, RGBRGB...
    AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp, BGRBGR...
    AV_PIX_FMT_YUV422P,   ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
    AV_PIX_FMT_YUV444P,   ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)

    ......

    AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
    AV_PIX_FMT_NV21,      ///< as above, but U and V bytes are swapped

    AV_PIX_FMT_ARGB,      ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
    AV_PIX_FMT_RGBA,      ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
    AV_PIX_FMT_ABGR,      ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
    AV_PIX_FMT_BGRA,      ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...

    ......

    AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
    AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian

    ......

    AV_PIX_FMT_NV16,         ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
    AV_PIX_FMT_NV20LE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
    AV_PIX_FMT_NV20BE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian

    ......

    /**
     *  HW acceleration through QSV, data[3] contains a pointer to the
     *  mfxFrameSurface1 structure.
     */
    AV_PIX_FMT_QSV,

    ......

    AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian
    AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian

    ......

    AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
};

4.2.2 AVPixFmtDescriptor

AVPixFmtDescriptor 定义了图像数据在内存中的组织排列形式,此数据结构定义了像素格式在 FFmpeg 中的实现细节。

/**
 * Descriptor that unambiguously describes how the bits of a pixel are
 * stored in the up to 4 data planes of an image. It also stores the
 * subsampling factors and number of components.
 *
 * @note This is separate of the colorspace (RGB, YCbCr, YPbPr, JPEG-style YUV
 *       and all the YUV variants) AVPixFmtDescriptor just stores how values
 *       are stored not what these values represent.
 */
typedef struct AVPixFmtDescriptor {
    const char *name;
    uint8_t nb_components;  ///< The number of components each pixel has, (1-4)

    /**
     * Amount to shift the luma width right to find the chroma width.
     * For YV12 this is 1 for example.
     * chroma_width = AV_CEIL_RSHIFT(luma_width, log2_chroma_w)
     * The note above is needed to ensure rounding up.
     * This value only refers to the chroma components.
     */
    uint8_t log2_chroma_w;

    /**
     * Amount to shift the luma height right to find the chroma height.
     * For YV12 this is 1 for example.
     * chroma_height= AV_CEIL_RSHIFT(luma_height, log2_chroma_h)
     * The note above is needed to ensure rounding up.
     * This value only refers to the chroma components.
     */
    uint8_t log2_chroma_h;

    /**
     * Combination of AV_PIX_FMT_FLAG_... flags.
     */
    uint64_t flags;

    /**
     * Parameters that describe how pixels are packed.
     * If the format has 1 or 2 components, then luma is 0.
     * If the format has 3 or 4 components:
     *   if the RGB flag is set then 0 is red, 1 is green and 2 is blue;
     *   otherwise 0 is luma, 1 is chroma-U and 2 is chroma-V.
     *
     * If present, the Alpha channel is always the last component.
     */
    AVComponentDescriptor comp[4];

    /**
     * Alternative comma-separated names.
     */
    const char *alias;
} AVPixFmtDescriptor;

const char *name

像素格式名称。例如 AV_PIX_FMT_YUV420P 的名称为 yuv420p,AV_PIX_FMT_YUV420P 在 FFmpeg 代码中使用,而像素格式名称 yuv420p 则在 FFmpeg 命令行中使用。

uint8_t nb_components

图像分量数,取值范围 1 - 4。例如 AV_PIX_FMT_GRAY8 只有 Y 一个分量,AV_PIX_FMT_YUV420P 有 Y、U、V 三个分量,AV_PIX_FMT_NV12 也有 Y、U、V 三个分量,AV_PIX_FMT_ARGB 有 A、R、G、B 四个分量。

uint8_t log2_chroma_w

移位的位数,表示将亮度样本宽度右移多少位能得到色度样本的宽度,此值等于水平方向色度子采样因子。例如对于 yuv420p 格式,若图像分辨率为 1280 x 720,则亮度样本宽度(水平方向亮度样本数)为 1280,色度样本宽度(水平方向色度样本数)为 1280/2 = 640,log2_chroma_w 值为 1(右移 1 位)。

uint8_t log2_chroma_h

移位的位数,表示将亮度样本高度右移多少位能得到色度样本的高度,此值等于垂直方向色度子采样因子。例如对于 yuv420p 格式,若图像分辨率为 1280 x 720,则亮度样本高度(垂直方向亮度样本数)为 720,色度样本高度(垂直方向色度样本数)为 720/2 = 360,log2_chroma_w 值为 1(右移 1 位)。

uint64_t flags

像素格式标志位组合,形如 AV_PIX_FMT_FLAG_BE | AV_PIX_FMT_FLAG_HWACCEL 。例如,标志 AV_PIX_FMT_FLAG_BE 表示大端格式,AV_PIX_FMT_FLAG_HWACCEL 表示此像素格式用于硬解或硬编等硬件加速场合。

AVComponentDescriptor comp[4]

这个成员非常重要。数组的每个元素表示一个分量,注意是一个分量而不是一个 plane,一个 plane 可能含有多个分量。AVComponentDescriptor 数据结构定义了每个分量的像素数据在内存中的格式,详情参 4.2.4 节。

const char *alias

以逗号分隔的别名列表。在 av_pix_fmt_descriptors[] 数组的定义中可以看到,AV_PIX_FMT_GRAY8 像素格式的名称是 "gray8",alias 值为 "gray8,y8"。

4.2.4 AVComponentDescriptor

AVComponentDescriptor 定义了每个分量在内存中的实际组织形式,包含所有细节。

typedef struct AVComponentDescriptor {
    /**
     * Which of the 4 planes contains the component.
     */
    int plane;

    /**
     * Number of elements between 2 horizontally consecutive pixels.
     * Elements are bits for bitstream formats, bytes otherwise.
     */
    int step;

    /**
     * Number of elements before the component of the first pixel.
     * Elements are bits for bitstream formats, bytes otherwise.
     */
    int offset;

    /**
     * Number of least significant bits that must be shifted away
     * to get the value.
     */
    int shift;

    /**
     * Number of bits in the component.
     */
    int depth;

#if FF_API_PLUS1_MINUS1
    /** deprecated, use step instead */
    attribute_deprecated int step_minus1;

    /** deprecated, use depth instead */
    attribute_deprecated int depth_minus1;

    /** deprecated, use offset instead */
    attribute_deprecated int offset_plus1;
#endif
} AVComponentDescriptor;

int plane

当前分量位于哪个 plane 中。

例如 p010 格式有三个分量:Y、U、V,两个 plane:Y、UV。Y plane 的形式为YYYY...,UV plane 的形式为UVUVUV...。Y 分量的 plane 值是 0, U 分量和 V 分量的 plane 值是 1,U 样本和 V 样本交织存放在 plane 1中。

int step

步长,表示水平方向连续的两个样本间距是多少个字节(或比特),如果像素格式是比特流格式(标志 AV_PIX_FMT_FLAG_BITSTREAM 有效),此值表示比特数,否则此值表示字节数。

以 p010 格式为例,Y plane 的形式为YYYY...,UV plane 的形式为UVUVUV...,位深是 10,考虑对齐后,每一个 Y、每一个 U、每一个 V 都占 2 个字节,因此 Y 分量的 step 是 2(两个 Y 相距两字节),U 分量的 step 是 4(两个 U 相距 4 字节),V 分量的 step 也是 4(两个 V 相距 4 字节)。

int offset

偏移,表示在当前 plane 中,当前分量的第一个样本之前有多少个字节的数据,如果像素格式是比特流格式(标志 AV_PIX_FMT_FLAG_BITSTREAM 有效),此值表示比特数,否则此值表示字节数。

以 p010 格式为例,每一个 U 或 V 都占 2 个字节,第一个 V 样本前有 2 个字节被 U 样本占了,所以 U 分量的 offset 值是 0,V 分量的 offset 值是 2。

int shift

右移位数,表示将对应内存单元的值右移多少位可以得到实际值。

以 p010 格式为例,位深是 10,而内存对齐后每一个 Y、U、V 样本占 16 bit,那么 10 位的数据放在 16 位的内存单元中, 是占据高 10 位还是占据低 10 位,即是由 shift 值决定的。p010 格式中,各分量的 shift 值都是 6 ,表示数据放在高 10 位。从 Y plane 中获取第一个 Y 样本的值,示意代码如下:

uint8_t y_plane[1280*2];
uint16_t *p_y0 = (uint16_t *)y_plane;
uint16_t y0 = (*p_y0) >> 6;

yuv420p10le 中分量的 shift 值都是 0 ,表示数据放在低 10 位。 从 Y plane 中获取第一个 Y 样本的值,示意代码如下:

uint8_t y_plane[1280*2];
uint16_t *p_y0 = (uint16_t *)y_plane;
uint16_t y0 = (*p_y0) >> 0;

int depth

当前分量每个样本的位宽度,即位深。

上述参数中,plane 表示分量所在的 plane 的序号,offset 表示多个分量交织存放在同一个 plane 中时的排列顺序(如 p010 格式的 UV plane 中 U 在前 V 在后),step、shift 和 depth 则是和内存对齐相关,例如 p010 格式 depth 是 10(bit), step 是 2 字节(16 bit),shift 是 6(bit),表示每个 10 bit 的样本占用 16 bit的内存单元,低 6 位是无用位(高位对齐,靠左对齐)。

4.3 节将详细解释几个常用像素格式各具体参数的含义。

4.2.3 av_pix_fmt_descriptors[]

av_pix_fmt_descriptors 是 FFmpeg 中定义各个像素格式的实际格式的。这个数组非常重要,当不知道具体的某个像素格式的实现细节时,查看此数组中的定义即可明白。常用的以及有代表性的几个像素格式将在下一节具体分析。

如下是 yuv420p 像素格式的定义:

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    [AV_PIX_FMT_YUV420P] = {
        .name = "yuv420p",
        .nb_components = 3,
        .log2_chroma_w = 1,
        .log2_chroma_h = 1,
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 1, 0, 0, 8, 0, 7, 1 },        /* U */
            { 2, 1, 0, 0, 8, 0, 7, 1 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3 典型像素格式实例分析

FFmpeg 中所有的像素格式都定义在 av_pix_fmt_descriptors[] 数组中。下面只列出最常用到的几个像素格式进行分析:

4.3.1 像素格式 yuv420p

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    [AV_PIX_FMT_YUV420P] = {
        .name = "yuv420p",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 1,    // 水平采样因子是 2,pow(2, 1)
        .log2_chroma_h = 1,    // 垂直采样因子是 2, pow(2, 1)
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 1, 0, 0, 8, 0, 7, 1 },        /* U */
            { 2, 1, 0, 0, 8, 0, 7, 1 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3.2 像素格式 yuv422p

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_YUV422P] = {
        .name = "yuv422p",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 1,    // 水平采样因子是 2,pow(2, 1)
        .log2_chroma_h = 0,    // 垂直采样因子是 1,pow(2, 0)
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 1, 0, 0, 8, 0, 7, 1 },        /* U */
            { 2, 1, 0, 0, 8, 0, 7, 1 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3.3 像素格式 yuv444p

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_YUV444P] = {
        .name = "yuv444p",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 0,    // 水平采样因子是 1,pow(2, 0)
        .log2_chroma_h = 0,    // 垂直采样因子是 1,pow(2, 0)
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 1, 0, 0, 8, 0, 7, 1 },        /* U */
            { 2, 1, 0, 0, 8, 0, 7, 1 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3.4 像素格式 nv12 和 nv21

nv12 格式有 Y、U、V 三个分量,Y 分量存放在 plane 0 中,U 和 V 交织存放在 plane 1 中。

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_NV12] = {
        .name = "nv12",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 1,    // 水平采样因子是 2,pow(2, 1)
        .log2_chroma_h = 1,    // 垂直采样因子是 2,pow(2, 1)
        .comp = {
            {                  // Y 分量
              0,               // plane:  plane 0, YYYYYYYY...
              1,               // step:   两个 Y 间距 1 字节
              0,
              0,
              8,               // 8 位宽
              0, 7, 1 },
            {                  // U 分量
              1,               // plane:  plane 1, UVUVUVUV...
              2,               // step:   两个 U 间距 2 字节
              0,               // offset: U 在前 V 在后
              0,
              8,               // 8 位宽
              1, 7, 1 },
            {                  // V 分量
              1,               // plane: plane 1, UVUVUVUV...
              2,               // step:  两个 V 间距 2 字节
              1,               // offset: U 在前 V 在后,因 V 前有 1 个字节的 U
              0, 
              8,               // 8 位宽
              1, 7, 2 },
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

nv21 格式和 nv12 格式只有一点不同:plane 1 中 U 和 V 的顺序不同,对比下列二者的定义,只有 .comp.offset 成员值不同

    [AV_PIX_FMT_NV12] = {
        .name = "nv12",
        .nb_components = 3,
        .log2_chroma_w = 1,
        .log2_chroma_h = 1,
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 2, 0, 0, 8, 1, 7, 1 },        /* U */
            { 1, 2, 1, 0, 8, 1, 7, 2 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    [AV_PIX_FMT_NV21] = {
        .name = "nv21",
        .nb_components = 3,
        .log2_chroma_w = 1,
        .log2_chroma_h = 1,
        .comp = {
            { 0, 1, 0, 0, 8, 0, 7, 1 },        /* Y */
            { 1, 2, 1, 0, 8, 1, 7, 2 },        /* U */
            { 1, 2, 0, 0, 8, 1, 7, 1 },        /* V */
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },

4.3.5 像素格式 p010le

p010le 和 nv12 格式类似,只是 p010le 位深是 10。

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_P010LE] = {
        .name = "p010le",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 1,    // 水平采样因子是 2,pow(2, 1)
        .log2_chroma_h = 1,    // 垂直采样因子是 2,pow(2, 1)
        .comp = {
            {                  // Y 分量
              0,               // plane:  plane 0, YYYYYYYY...
              2,               // step:   两个 Y 相距 2 字节
              0,               // offset: 0
              6,               // shift:  10 位数据按高位对齐,低 6 位是无效值
              10,              // depth:  10 位宽
              1, 9, 1 },
            {                  // U 分量
              1,               // plane:  plane 1, UVUVUVUV...
              4,               // step:   两个 U 相距 4 字节
              0,               // offset: U 在前 V 在后
              6,               // shift:  10 位数据按高位对齐,低 6 位是无效值
              10,              // depth:  10 位宽
              3, 9, 1 },
            {                  // V 分量
              1,               // plane:  plane 1, UVUVUVUV...
              4,               // step:   两个 V 相距 4 字节
              2,               // offset: U 在前 V 在后,因 V 前有 2 个字节被 U 占了
              6,               // shift:  10 位数据按高位对齐,低 6 位是无效值
              10,              // depth:  10 位宽
              3, 9, 3 },
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3.6 像素格式 yuv420p10le

yuv420p10le 格式有 Y、U、V 三个分量,三个分量分别存放在三个 plane 中。

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_YUV420P10LE] = {
        .name = "yuv420p10le",
        .nb_components = 3,    // 一共三个分量:Y、U、V
        .log2_chroma_w = 1,
        .log2_chroma_h = 1,
        .comp = {
            {                  // Y 分量
              0,               // plane:  plane 0, YYYYYYYY...
              2,               // step:   两个 Y 相距 2 字节
              0,               // offset: 0
              0,               // shift:  10 位数据按低位对齐
              10,              // depth:  10 位宽
              1, 9, 1 },
            {                  // U 分量
              1,               // plane:  plane 1, UUUUUUUU...
              2,               // step:   两个 U 相距 2 字节
              0,               // offset: 0
              0,               // shift:  10 位数据按低位对齐
              10,              // depth:  10 位宽
              1, 9, 1 },
            {                  // V 分量
              2,               // plane:  plane 2, VVVVVVVV...
              2,               // step:   两个 U 相距 2 字节
              0,               // offset: 0
              0,               // shift:  10 位数据按低位对齐
              10,              // depth:  10 位宽
              1, 9, 1 },
        },
        .flags = AV_PIX_FMT_FLAG_PLANAR,
    },
    ......
}

4.3.7 像素格式 argb

argb 格式有 A、R、G、B 四个分量,交织存放在 plane 0 中,所以 argb 是一种 packed 存储模式。

如是,注意R、G、B、A 各分量的 .comp.offset 值依次是 1、2、3、0,表示 A、R、G、B 在内存中的排列顺序是 ARGBARGBARGB...

static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
    ......
    [AV_PIX_FMT_ARGB] = {
        .name = "argb",
        .nb_components = 4,
        .log2_chroma_w = 0,
        .log2_chroma_h = 0,
        .comp = {
            { 0, 4, 1, 0, 8, 3, 7, 2 },        /* R */
            { 0, 4, 2, 0, 8, 3, 7, 3 },        /* G */
            { 0, 4, 3, 0, 8, 3, 7, 4 },        /* B */
            { 0, 4, 0, 0, 8, 3, 7, 1 },        /* A */
        },
        .flags = AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_ALPHA,
    },
    ......
}
posted @ 2024-12-16 14:28  叶余  阅读(12)  评论(0编辑  收藏  举报