加里·沙利文和斯蒂芬·埃斯特罗普
Microsoft Corporation
2002 年 4 月,更新时间:2008 年 11 月
本主题介绍建议在 Windows作系统中呈现视频的 8 位 YUV 颜色格式。 本文介绍了在 YUV 和 RGB 格式之间转换的技术,还提供了用于 YUV 格式向上采样的技术。 本文适用于在 Windows 中使用 YUV 视频解码或渲染的任何人。
介绍
整个视频行业都定义了许多 YUV 格式。 本文标识建议用于在 Windows 中呈现视频的 8 位 YUV 格式。 建议解码器供应商和显示供应商支持本文中所述的格式。 本文不解决 YUV 颜色的其他用途,如静止摄影。
本文中所述的格式均使用每个像素位置 8 位来编码 Y 通道(也称为 luma 通道),并使用每个样本 8 位对每个 U 或 V 色度样本进行编码。 但是,大多数 YUV 格式平均使用不到 24 位/像素,因为它们的 U 和 V 通道所用的位数少于 Y。本文不涉及 Y 通道为 10 位或更高的 YUV 格式。
注释
在本文中,术语 U 等效于 Cb,术语 V 等效于 Cr。
本文涵盖以下主题:
- YUV 采样。 介绍最常见的 YUV 采样技术。
- 表面定义。 描述推荐的 YUV 格式。
- 颜色空间和色度采样率转换。 提供有关在 YUV 和 RGB 格式之间进行转换以及在不同 YUV 格式之间进行转换的一些准则。
- 在媒体基础中识别 YUV 格式。 介绍如何在媒体基础中描述 YUV 格式类型。
YUV 采样
色度通道的采样率可能低于 luma 通道,而不会产生任何戏剧性的感知质量损失。 称为“A:B:C”表示法的记号用于描述 U 和 V 相对于 Y 的采样频率:
- 4:4:4 表示不进行色度通道的向下采样。
- 4:2:2 表示 2:1 水平向下采样,无垂直下采样。 每个扫描行中,每两个 U 或 V 样本对应四个 Y 样本。
- 4:2:0 表示 2:1 水平下采样,垂直向下采样为 2:1。
- 4:1:1 表示 4:1 水平下采样,无垂直下采样。 每个扫描行包含四个 Y 样本,每个 U 和 V 样本都有四个对应的 Y 样本。 4:1:1 采样比其他格式不太常见,本文未详细讨论。
下图显示了如何针对每个降采样率对色度进行采样。 Luma 样本由十字表示,色度样本由圆表示。
ITU-R 建议 BT.601 中定义了 4:2:2 采样的主要形式。 4:2:0 采样有两种常见变体。 其中一个用于 MPEG-2 视频,另一个用于 MPEG-1 和 ITU-T 建议 H.261 和 H.263。
与 MPEG-1 方案相比,在 MPEG-2 方案与为 4:2:2 和 4:4:4:4 格式定义的采样网格之间转换更简单。 因此,MPEG-2 方案在 Windows 中是首选方案,应被视为 4:2:0 格式的默认解释。
表面定义
本部分介绍建议用于视频呈现的 8 位 YUV 格式。 这些类别分为以下几个类别:
首先,应了解以下概念,以了解以下内容:
- 表面原点。 对于本文中所述的 YUV 格式,原点 (0,0) 始终是表面左上角。
- 步幅。 图面的步幅(有时称为间距)是指图面的宽度(以字节为单位)。 鉴于左上角的图面原点,步幅始终为正。
- 对齐。 图面的对齐方式由图形显示驱动程序决定。 表面必须始终是 DWORD 对齐的;也就是说,确保表面内的每一行都从 32 位 (DWORD) 边界开始。 但是,对齐方式可能大于 32 位,具体取决于硬件的需求。
- 打包格式与平面格式。 YUV 格式分为 打包 格式和 平面 格式。 在打包格式中,Y、U 和 V 组件存储在单个数组中。 像素组织成一组宏像素,其布局取决于格式。 在平面格式中,Y、U 和 V 组件存储为三个单独的平面。
本文中所述的每个 YUV 格式都有一个分配的 FOURCC 代码。 FOURCC 代码是通过连接四个 ASCII 字符创建的 32 位无符号整数。
- 4:4:4 (32 bpp)
- 4:4:4 (24 bpp)
- 4:2:2 (16 bpp)
- 4:2:0 (16 bpp)
- 4:2:0 (12 bpp)
4:4:4 格式,每个像素 32 位
AYUV
推荐使用单一的 4:4:4 格式,并采用 FOURCC 代码 AYUV。 这是一种打包格式,其中每个像素编码为四个连续字节,按下图所示的序列排列。
标记为 A 的字节包含 alpha 的值。
4:4:4 格式,每个像素 24 位
I444
在 I444 格式中,所有 Y 样本首先以无符号字符值数组的形式出现在内存中。 紧随此数组之后的是所有 U (Cb) 样本。 U 平面的步幅与 Y 平面的步幅相同:和 U 平面包含与 Y 平面相同的行数。 紧随 U 平面之后的是所有 V (Cr) 样本,其步幅和行数与 U 平面相同。 Y/U/V 平面没有填充。
4:2:2 格式,每个像素 16 位
推荐两种 4:2:2 格式,以下是其 FOURCC 代码:
- YUY2
- UYVY
这两种格式都是打包格式,其中每个宏像素都是编码为四个连续字节的两个像素。 这会导致按系数 2 对色度进行水平向下采样。
YUY2
在 YUY2 格式中,可以将数据视为无符号 字符 值的数组,其中第一个字节包含第一个 Y 样本,第二个字节包含第一个 U (Cb) 样本,第三个字节包含第二个 Y 样本,第四个字节包含第一个 V (Cr) 示例,如下图所示。
如果图像作为小端 WORD 值的数组进行寻址,则第一个 WORD 包含最小有效位(LSB)中的第一个 Y 样本和最有效位(MSB)中的第一个 U (Cb) 样本。 第二个 WORD 包含 LSB 中的第二个 Y 样本和 MSB 中的第一个 V (Cr) 样本。
YUY2 是 Microsoft DirectX 视频加速(DirectX VA)的首选 4:2:2 像素格式。 它预计是 DirectX VA 加速器支持 4:2:2 视频的中期要求。
UYVY
此格式与 YUY2 格式相同,但字节顺序相反,即色度和亮度字节位置颠倒(图 4)。 如果图像作为两个小端 WORD 值的数组进行寻址,则第一个 WORD 在 MSB 中的 LSB 和 Y0 中包含 U,第二个 WORD 在 MSB 中包含 LSB 和 Y1 中的 V。
I422
在 I422 格式中,所有 Y 样本首先以无符号字符值数组的形式出现在内存中。 紧随此数组之后的是所有 U (Cb) 样本。 U 平面的步幅是 Y 平面的一半步长:和 U 平面包含与 Y 平面相同的行数。 如下图所示,紧随 U 平面之后的是所有 V (Cr) 样本,其中步幅和行数与 U 平面相同。 Y/U/V 平面没有填充。
4:2:0 格式,每个像素 16 位
建议使用两个 4:2:0 16 位/像素 (bpp) 格式,并使用以下 FOURCC 代码:
- IMC1
- IMC3
这两种 YUV 格式都是平面格式。 色度通道在水平和垂直维度都以系数 2 进行子采样。
IMC1
所有 Y 样本首先以无符号 字符 值数组的形式出现在内存中。 随后是所有 V (Cr) 示例,然后是所有 U (Cb) 示例。 V 和 U 平面的步幅与 Y 平面相同,导致内存区未使用,如图 5 所示。 U 和 V 平面必须以 16 行为倍数的内存边界上开始。 图 5 显示了 352 x 240 视频帧中 U 和 V 的原点。 U 和 V 平面的起始地址按如下方式计算:
BYTE* pV = pY + (((Height + 15) & ~15) * Stride);
BYTE* pU = pY + (((((Height * 3) / 2) + 15) & ~15) * Stride);
其中 pY 是指向内存数组开头的字节指针,如下图所示。
IMC3
此格式与 IMC1 相同,只是交换了 U 和 V 平面,如下图所示。
4:2:0 格式,每个像素 12 位
建议采用以下四种 4:2:0 12-bpp 格式,分别对应的 FOURCC 代码是:
- IMC2
- IMC4
- YV12
- NV12
在所有这些格式中,色度通道在水平和垂直维度上都以系数 2 进行子采样。
IMC2
此格式与 IMC1 相同,但有以下区别:V(Cr)和 U(Cb)信号在半偏移边界处交错排列。 换言之,色度区域中的每个全步幅行都从 V 样本行开始,接着是在下一个半步幅边界开始的 U 样本行(图 7)。 此布局比 IMC1 更有效地使用地址空间。 它会将色度地址空间减半,从而使总地址空间减少 25%。 在 4:2:0 格式中,IMC2 是 NV12 之后的第二种首选格式。 下图说明了此过程。
IMC4
此格式与 IMC2 相同,只是交换了 U (Cb) 和 V (Cr) 行,如下图所示。
YV12
所有 Y 样本首先以无符号 字符 值数组的形式出现在内存中。 紧随此数组之后的是所有 V (Cr) 样本。 V 平面的步幅是 Y 平面的一半步长:和 V 平面包含的行数与 Y 平面多一半。 如下图所示,紧随 V 平面之后的是所有 U (Cb) 样本,其中步幅和行数与 V 平面相同。
NV12
所有 Y 样本首先以无符号 字符 值数组的形式出现在内存中,其中包含偶数行。 紧随 Y 平面之后的是包含打包的 U (Cb) 和 V (Cr) 样本的无符号 char 值数组。 当组合的 U-V 数组作为小端 WORD 值的数组进行寻址时,LSB 包含 U 值,而 MSB 包含 V 值。 NV12 是 DirectX VA 的首选 4:2:0 像素格式。 它预计是 DirectX VA 加速器支持 4:2:0 视频的中期要求。 下图显示了 Y 平面以及包含打包的 U 和 V 示例的数组。
颜色空间和色度采样率转换
本部分提供了在 YUV 和 RGB 之间进行转换以及在某些不同 YUV 格式之间进行转换的指南。 在本部分中,我们考虑了两种 RGB 编码方案:8 位计算机 RGB,也称为 sRGB 或“全尺度”RGB,以及 工作室视频 RGB,或“头部间隙和底部间隙的 RGB”。这些定义如下:
- 计算机 RGB 为每个红色、绿色和蓝色样本使用 8 位。 黑色由 R = G = B = 0 表示,白色由 R = G = B = 255 表示。
- 工作室视频 RGB 对每个红色、绿色和蓝色样本使用一些位数 N,其中 N 为 8 或更多。 工作室视频 RGB 使用与计算机 RGB 不同的缩放系数,并且具有偏移量。 黑色由 R = G = B = 16*2^(N-8),白色由 R = G = B = 235*2^(N-8) 表示。 但是,实际值可能超出此范围。
在 Windows 系统中,工作室视频 RGB 是视频的首选标准 RGB,而在非视频应用程序中,计算机 RGB 是首选的标准 RGB。 在任一形式的 RGB 中,色度坐标都按照 ITU-R BT.709 中的规定,用于定义 RGB 颜色原色。 R、G 和 B 的 (x,y) 坐标分别为 (0.64、0.33)、(0.30、0.60)和 (0.15, 0.06)。 参考白色为 D65,坐标为 (0.3127, 0.3290)。 名义伽玛是 1/0.45 (大约 2.2),精确伽玛在 ITU-R BT.709 中定义。
RGB 与 4:4:4 YUV 之间的转换
我们首先介绍 RGB 与 4:4:4 YUV 之间的转换。 若要将 4:2:0 或 4:2:2 YUV 转换为 RGB,建议将 YUV 数据转换为 4:4:4 YUV,然后将 4:4:4 YUV 转换为 RGB。 AYUV 格式(4:4:4 格式)对 Y、U 和 V 样本使用 8 位。 对于某些应用程序,还可以为每个示例使用 8 位以上的 YUV 进行定义。
已为数字视频定义了两种主要的从 RGB 到 YUV 的转换方式。 两者都基于称为 ITU-R 建议 BT.709 的规范。 第一个转换是在 BT.709 中定义的为 50 Hz 应用的较旧 YUV 格式。 它与 ITU-R 建议书 BT.601 中指定的各项关系相同,也称为旧称 CCIR 601。 它应该被视为标准定义电视分辨率(720 x 576)和低分辨率视频的首选 YUV 格式。 它的特点是两个常量 Kr 和 Kb 的值:
Kr = 0.299
Kb = 0.114
第二种转换是在 BT.709 中为 60-Hz 使用定义的较新的 YUV 形式,应被视为高于 SDTV 的视频分辨率的首选格式。 它的特点是这两个常量的不同值:
Kr = 0.2126
Kb = 0.0722
从 RGB 到 YUV 的转换从以下内容开始定义:
L = Kr * R + Kb * B + (1 - Kr - Kb) * G
然后,获取 YUV 值如下所示:
Y = floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5)
U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5))
V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5))
其中
- M 是每个 YUV 样本的位数(M >= 8)。
- Z 是黑色级别变量。 对于计算机 RGB,Z 等于 0。 对于工作室视频 RGB,Z 等于 16*2^(N-8),其中 N 是每个 RGB 样本的位数(N >= 8)。
- S 是缩放变量。 对于计算机 RGB,S 等于 255。 对于工作室视频 RGB,S 等于 219*2^(N-8)。
函数 floor(x) 返回小于或等于 x 的最大整数。 函数 clip3(x, y, z) 的定义如下:
clip3(x, y, z) = ((z < x) ? x : ((z > y) ? y : z))
注释
clip3 应作为函数而不是预处理器宏实现;否则,将进行多个参数计算。
Y 样本表示亮度,U 和 V 样本分别表示偏向蓝色和红色的颜色偏差。 Y 的名义范围为 16*2^(M-8) 到 235*2^(M-8)。 黑色表示为 16*2^(M-8),白色表示为 235*2^(M-8)。 U 与 V 的名义范围为 16*2^(M-8) 到 240*2^(M-8),值为 128*2^(M-8) 表示中性色度。 但是,实际值可能超出这些范围。
对于采用工作室视频 RGB 格式的输入数据,剪辑操作必须使 U 和 V 值保持在 0 到 (2^M)-1 范围内。 如果输入是计算机 RGB,则不需要剪辑操作,因为转换公式无法生成超出此范围的值。
这些是没有近似的确切公式。 本文档后面的所有内容都派生自这些公式。 本部分介绍以下转换:
- 将 RGB888 转换为 YUV 4:4:4
- 将 8 位 YUV 转换为 RGB888
- 将 4:2:0 YUV 转换为 4:2:2 YUV
- 将 4:2:2 YUV 转换为 4:4:4 YUV
- 将 4:2:0 YUV 转换为 4:4:4 YUV
将 RGB888 转换为 YUV 4:4:4
对于计算机 RGB 输入和 8 位 BT.601 YUV 输出,我们认为上一部分中给出的公式可以按以下公式进行合理近似计算:
Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16
U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128
V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128
这些公式使用不超过 8 位(无符号)精度的系数生成 8 位结果。 中间结果最多需要 16 位精度。
将 8 位 YUV 转换为 RGB888
从原始 RGB 到 YUV 公式,可以派生 BT.601 的以下关系。
Y = round( 0.256788 * R + 0.504129 * G + 0.097906 * B) + 16
U = round(-0.148223 * R - 0.290993 * G + 0.439216 * B) + 128
V = round( 0.439216 * R - 0.367788 * G - 0.071427 * B) + 128
因此,鉴于:
C = Y - 16
D = U - 128
E = V - 128
将 YUV 转换为 RGB 的公式可以派生如下:
R = clip( round( 1.164383 * C + 1.596027 * E ) )
G = clip( round( 1.164383 * C - (0.391762 * D) - (0.812968 * E) ) )
B = clip( round( 1.164383 * C + 2.017232 * D ) )
其中 clip()
表示裁剪到 [0..255] 的范围。 我们认为这些公式可以按以下公式进行合理近似计算:
R = clip(( 298 * C + 409 * E + 128) >> 8)
G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
B = clip(( 298 * C + 516 * D + 128) >> 8)
为了生成每个 8 位结果,这些公式使用的一些系数需要超过 8 位的精度,并且中间结果需要超过 16 位的精度。
若要将 4:2:0 或 4:2:2 YUV 转换为 RGB,建议将 YUV 数据转换为 4:4:4 YUV,然后将 4:4:4 YUV 转换为 RGB。 以下部分介绍了将 4:2:0 和 4:2:2 格式转换为 4:4:4 格式的一些方法。
将 4:2:0 YUV 转换为 4:2:2 YUV
将 4:2:0 YUV 转换为 4:2:2 YUV 时,需要按系数 2 进行垂直向上转换。 本节介绍一种执行向上转换的示例方法。 该方法假定视频图片是渐进式扫描。
注释
4:2:0 到 4:2:2 交错扫描转换过程存在异常问题,难以实现。 本文未解决将交错扫描从 4:2:0 转换为 4:2:2:2 的问题。
让每个输入色度样本的垂直线都是一个 Cin[]
数组,范围为 0 到 N - 1。 输出图像上的相应垂直线将是一个数组 Cout[]
,范围为 0 到 2N - 1。 若要转换每个垂直线,请执行以下步骤:
Cout[0] = Cin[0];
Cout[1] = clip((9 * (Cin[0] + Cin[1]) - (Cin[0] + Cin[2]) + 8) >> 4);
Cout[2] = Cin[1];
Cout[3] = clip((9 * (Cin[1] + Cin[2]) - (Cin[0] + Cin[3]) + 8) >> 4);
Cout[4] = Cin[2]
Cout[5] = clip((9 * (Cin[2] + Cin[3]) - (Cin[1] + Cin[4]) + 8) >> 4);
...
Cout[2*i] = Cin[i]
Cout[2*i+1] = clip((9 * (Cin[i] + Cin[i+1]) - (Cin[i-1] + Cin[i+2]) + 8) >> 4);
...
Cout[2*N-3] = clip((9 * (Cin[N-2] + Cin[N-1]) - (Cin[N-3] + Cin[N-1]) + 8) >> 4);
Cout[2*N-2] = Cin[N-1];
Cout[2*N-1] = clip((9 * (Cin[N-1] + Cin[N-1]) - (Cin[N-2] + Cin[N-1]) + 8) >> 4);
其中 clip() 表示在 [0..255] 范围内剪辑。
注释
可以数学简化处理边缘的公式。 它们将以这种形式显示,以说明图片边缘的固定效果。
实际上,此方法将在四个相邻像素上内插曲线来计算每个缺失值,并将按两个最接近像素的值进行加权(图 11)。 此示例中使用的特定内插方法使用名为 Catmull-Rom 内插(也称为立方卷积内插)的著名方法,在半整数位置生成缺失样本。
在信号处理术语中,理想情况下,垂直向上转换应包括相移补偿,以考虑 4:2:0 样本行位置与每隔一个 4:2:2 样本行位置之间的半像素垂直偏移量(相对于输出 4:2:2 采样网格)。 但是,引入此偏移量会增加生成样本时所需的处理过程,并且使得无法从上采样的 4:2:2 图像重建原始的 4:2:0 样本。 它还会使无法将视频直接解码为 4:2:2 图面,然后使用这些图面作为参考图片来解码流中的后续图片。 因此,此处提供的方法不考虑样本的精确垂直对齐方式。 这样做在相当高的图片分辨率下可能并不具有视觉危害。
如果从使用在 H.261、H.263 或 MPEG-1 视频中定义的采样网格的 4:2:0 视频开始,输出 4:2:2 色度样本的相位也将相对于亮度采样网格的间距按半像素水平偏移量进行移位(相对于 4:2:2 色度采样网格间距而言是四分之一像素偏移量)。 但是,MPEG-2 形式的 4:2:0 视频在电脑上可能更常用,并且不会受到此问题的影响。 此外,这种区别在相当高的图片分辨率下可能并不具有视觉危害。 如果尝试更正此问题,将创建针对垂直阶段偏移讨论的相同问题。
将 4:2:2 YUV 转换为 4:4:4 YUV
将 4:2:2 YUV 转换为 4:4:4 YUV 时,需要按系数 2 进行水平向上转换。 前面描述的垂直上转方法也可以应用于水平向上转换。 对于 MPEG-2 和 ITU-R BT.601 视频,此方法将生成具有正确相位对齐的示例。
将 4:2:0 YUV 转换为 4:4:4 YUV
若要将 4:2:0 YUV 转换为 4:4:4 YUV,只需遵循前面所述的两种方法即可。 将 4:2:0 图像转换为 4:2:2,然后将 4:2:2 图像转换为 4:4:4。 还可以切换两个上转过程的顺序,因为操作顺序对结果的视觉质量并没有实质性影响。
将 YUY2 转换为 I422
对于 4:2:2 颜色格式,某些解码器默认可能会输出MFVideoFormat_YUY2,这是打包格式。 但是,许多编码器需要平面格式的输入MFVideoFormat_I422。 在这种情况下,可以在管道中插入转码视频处理媒体基础转换器(通常称为“XVP”),以将MFVideoFormat_YUY2转换为MFVideoFormat_I422。 有关详细信息,请参阅转码视频处理器媒体基础转换。 此示例演示如何使用 XVP 对编码工作流执行此转换。
#include <mfapi.h>
#include <wmcodecdsp.h> // CLSID_VideoProcessorMFT
using Microsoft::WRL;
HRESULT ConvertYUY2toI422WithXVP(
_In_ IMFSample* inputSample,
_In_ MFT_OUTPUT_DATA_BUFFER* outputBuffer,
UINT32 width,
UINT32 height)
{
RETURN_HR_IF_NULL(E_INVALIDARG, inputSample);
RETURN_HR_IF_NULL(E_INVALIDARG, outputBuffer);
ComPtr<IMFTransform> xvp;
RETURN_IF_FAILED(CoCreateInstance(
CLSID_VideoProcessorMFT,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&xvp)));
// Set input type: MFVideoFormat_YUY2
ComPtr<IMFMediaType> inputType;
RETURN_IF_FAILED(MFCreateMediaType(&inputType));
RETURN_IF_FAILED(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_IF_FAILED(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2));
RETURN_IF_FAILED(MFSetAttributeSize(inputType.Get(), MF_MT_FRAME_SIZE, width, height));
RETURN_IF_FAILED(spXVP->SetInputType(0, inputType.Get(), 0));
// Set output type: MFVideoFormat_I422
ComPtr<IMFMediaType> outputType;
RETURN_IF_FAILED(MFCreateMediaType(&outputType));
RETURN_IF_FAILED(outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_IF_FAILED(outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I422));
RETURN_IF_FAILED(MFSetAttributeSize(outputType.Get(), MF_MT_FRAME_SIZE, width, height));
RETURN_IF_FAILED(xvp->SetOutputType(0, outputType.Get(), 0));
// Submit input sample
RETURN_IF_FAILED(xvp->ProcessInput(0, inputSample, 0));
// Request the converted output sample
DWORD status = 0;
return xvp->ProcessOutput(0, 1, outputBuffer, &status);
}
将 AYUV 转换为 I444
对于 4:4:4 颜色格式,某些解码器默认可能会输出MFVideoFormat_AYUV,这是打包格式。 但是,许多编码器需要平面格式的输入MFVideoFormat_I444。 在这种情况下,可以将转码视频处理器媒体基础转换(XVP)插入管道,将MFVideoFormat_AYUV转换为MFVideoFormat_I444。 此示例演示如何使用 XVP 对编码工作流执行此转换。
#include <mfapi.h>
#include <wmcodecdsp.h> // CLSID_VideoProcessorMFT
using Microsoft::WRL;
HRESULT ConvertAYUVtoI444WithXVP(
_In_ IMFSample* inputSample,
_In_ MFT_OUTPUT_DATA_BUFFER* outputBuffer,
UINT32 width,
UINT32 height)
{
RETURN_HR_IF_NULL(E_INVALIDARG, inputSample);
RETURN_HR_IF_NULL(E_INVALIDARG, outputBuffer);
ComPtr<IMFTransform> xvp;
RETURN_IF_FAILED(CoCreateInstance(
CLSID_VideoProcessorMFT,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&xvp)));
// Set input type: MFVideoFormat_AYUV
ComPtr<IMFMediaType> inputType;
RETURN_IF_FAILED(MFCreateMediaType(&inputType));
RETURN_IF_FAILED(inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_IF_FAILED(inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV));
RETURN_IF_FAILED(MFSetAttributeSize(inputType.Get(), MF_MT_FRAME_SIZE, width, height));
RETURN_IF_FAILED(xvp->SetInputType(0, inputType.Get(), 0));
// Set output type: MFVideoFormat_I444
ComPtr<IMFMediaType> outputType;
RETURN_IF_FAILED(MFCreateMediaType(&outputType));
RETURN_IF_FAILED(outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_IF_FAILED(outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I444));
RETURN_IF_FAILED(MFSetAttributeSize(outputType.Get(), MF_MT_FRAME_SIZE, width, height));
RETURN_IF_FAILED(xvp->SetOutputType(0, outputType.Get(), 0));
// Submit input sample
RETURN_IF_FAILED(xvp->ProcessInput(0, inputSample, 0));
// Request the converted output sample
DWORD status = 0;
return xvp->ProcessOutput(0, 1, outputBuffer, &status);
}
其他 YUV 格式
其他一些不太常见的 YUV 格式包括:
- AI44 是一种调色板 YUV 格式,每个样本有 8 位。 每个样本都包含 4 个最有效位(MSB)中的索引和 4 个最小有效位(LSB)中的 alpha 值。 索引引用了 YUV 调色板条目数组,必须在适用于格式的媒体类型中定义这些条目。 此格式主要用于子图片图像。
- NV11 是一种 4:1:1 平面格式,每个像素 12 位。 Y 样本首先显示在内存中。 Y 平面后接打包 U (Cb) 和 V (Cr) 样本的数组。 当组合的 U-V 数组作为小端 WORD 值的数组进行寻址时,U 样本包含在每个 WORD 的 LSB 中,V 样本包含在 MSB 中。 (尽管色度采样不同,但此内存布局类似于 NV12。
- Y41P 是一种 4:1:1 的打包格式,其中 U 和 V 每隔四个像素水平采样。 每个 macropixel 包含 8 个字节(以三个字节为单位),具有以下字节布局:
U0 Y0 V0 Y1 U4 Y2 V4 Y3 Y4 Y5 Y6 Y7
- Y41T 与 Y41P 相同,但每个 Y 样本的最小有效位指定色度键(0 = 透明,1 = 不透明)。
- Y42T 与 UYVY 相同,但每个 Y 样本的最小有效位指定色度键(0 = 透明,1 = 不透明)。
- YVYU 等效于 YUYV,但交换了 U 和 V 样本。
在媒体基础中识别 YUV 格式
本文中所述的每个 YUV 格式都有一个分配的 FOURCC 代码。 FOURCC 代码是通过连接四个 ASCII 字符创建的 32 位无符号整数。
有多种 C/C++ 宏便于在源代码中声明 FOURCC 值。 例如,MAKEFOURCC 宏在 Mmsystem.h 中声明,FCC 宏在 Aviriff.h 中声明。 按如下所示使用它们:
DWORD fccYUY2 = MAKEFOURCC('Y','U','Y','2');
DWORD fccYUY2 = FCC('YUY2');
还可以直接将 FOURCC 代码声明为字符串字面量,只需反转字符的顺序即可。 例如:
DWORD fccYUY2 = '2YUY'; // Declares the FOURCC 'YUY2'
由于 Windows 操作系统使用 little-endian 体系结构,因此需要反转顺序。 'Y' = 0x59、'U' = 0x55、'2' = 0x32,因此“2YUY”为 0x32595559。
在 Media Foundation 中,格式通过主要类型 GUID 和子类型 GUID 进行标识。 计算机视频格式的主要类型始终MFMediaType_Video。 可以通过将 FOURCC 代码映射到 GUID 来构造子类型,如下所示:
XXXXXXXX-0000-0010-8000-00AA00389B71
其中 XXXXXXXX
为 FOURCC 代码。 因此,YUY2 的子类型 GUID 为:
32595559-0000-0010-8000-00AA00389B71
最常见的 YUV 格式 GUID 的常量在头文件 mfapi.h 中定义。 有关这些常量的列表,请参阅 视频子类型 GUID。
相关主题