重要
本文稍后讨论的自动校正方法是 推荐的 相机传感器非参考定向安装的解决方案。 这是为了确保应用程序的兼容性,因为大多数已编写用于使用相机数据流的应用程序不知道检查,也无法校正旋转信息。 请仔细查看下面的自动更正部分中的信息。
由于引入了不同的外形规格计算设备,一些物理约束导致相机传感器安装在非传统方向上。 因此,必须正确描述 OS 和应用程序,如何装载传感器,以便正确呈现/录制生成的视频。
从 Windows 10 版本 1607 开始,所有相机驱动程序都需要显式指定相机方向,而不管相机是否根据 最低硬件要求进行安装。 具体而言,相机驱动程序必须在与捕获设备接口关联的 ACPI _PLD结构中设置新引入的字段 “旋转”:
typedef struct _ACPI_PLD_V2_BUFFER {
UINT32 Revision:7;
UINT32 IgnoreColor:1;
UINT32 Color:24;
// …
UINT32 Panel:3; // Already supported by camera.
// …
UINT32 CardCageNumber:8;
UINT32 Reference:1;
UINT32 Rotation:4; // 0 – Rotate by 0° clockwise
// 1 – Rotate by 45° clockwise (N/A to camera)
// 2 – Rotate by 90° clockwise
// 3 – Rotate by 135° clockwise (N/A to camera)
// 4 – Rotate by 180° clockwise
// 5 – Rotate by 225° clockwise (N/A to camera)
// 6 – Rotate by 270° clockwise
UINT32 Order:5;
UINT32 Reserved:4;
//
// _PLD v2 definition fields.
//
USHORT VerticalOffset;
USHORT HorizontalOffset;
} ACPI_PLD_V2_BUFFER, *PACPI_PLD_V2_BUFFER;
对于相机,ACPI _PLD结构中的 旋转 字段指定相对于屏幕旋转的度数:当显示器处于本机方向时,捕获的画面旋转0°对应‘0’,90°对应‘2’,180°对应‘4’,270°对应‘6’。
根据 “旋转 ”字段中的值,应用程序可以根据需要执行其他旋转,以便正确呈现捕获的帧。
旋转值
对于相机和显示器共享相同外壳(或 机箱/外壳)的设备,可以将这些外围设备安装在不同的表面上,每个外围设备都由其各自的平面上的固定但任意度旋转。 因此,应用程序需要一种机制来描述两个外围设备之间的空间关系,这样捕获的帧就可以以正确的方向转置到呈现图面上。
解决问题的一种方法是使用 ACPI _PLD结构,该结构已经定义了 表面 和 旋转度 的概念。 例如,_PLD结构已具有 面板 字段,用于指定外围设备所在的图面:
ACPI _PLD 面板字段的定义(Rev. 5.0a)
接下来的两个关系图直观地说明了每个面板的定义:
桌面电脑和大多数设备的面板定义
可折叠设备的面板定义
事实上,ACPI“面板”的概念已经由 Windows 采用,其中:
当捕获设备被静态安装在固定位置时,摄像设备接口将与_PLD结构关联,并相应地设置面板字段。
应用程序可以通过调用 Windows.Devices.Enumeration.DeviceInformation.EnclosureLocation.Panel 属性来检索捕获设备所在的面板。
ACPI _PLD结构还定义了一个旋转字段,如下所示:
ACPI _PLD旋转字段的定义(Rev 5.0a)
我们不再按原样使用上述定义,而是进一步优化它以避免歧义:
- 对于相机,ACPI _PLD 结构中的“旋转”字段指定捕获的帧相对于屏幕在显示器处于本机方向时旋转的角度(‘0’表示 0°,‘2’表示 90°,‘4’表示 180°,‘6’表示 270°)。
横向主视图与纵向主视图
在 Windows 中,可以通过调用属性 Windows.Graphics.Display.DisplayInformation.NativeOrientation 来查询本机显示方向,该属性返回 横向 或 纵向:
无论哪个值 NativeOrientation 返回,逻辑显示扫描模式都从显示左上角开始,从左到右向下移动(见图 5)。 对于默认物理方向不明确的设备,此属性不仅指示 ACPI 顶部 面板的位置,还提供相机输出缓冲区和渲染表面之间的空间关系。
请注意,与相机不同, NativeOrientation 属性不基于 ACPI,因此没有_PLD结构。 即使将显示器静态装载到设备,也是如此。
在纵向主设备上安装时,相机驱动程序必须注意,大多数应用程序都会将设备视为横向相机输出缓冲区,而不考虑实际的相机输出缓冲区方向。 因此,我们建议在纵向主设备上,相机驱动程序输出与“NativeOrientation Portrait”相差90度的相机缓冲区。 然后,这将允许在纵向设备上执行额外旋转的应用程序将其旋转纠正到预期的方向。 可以使用 带旋转示例的相机应用程序来验证这一点。
偏移装载
强烈建议 IHV/OEM 避免以非 0 度偏移方式安装传感器,以保持应用兼容性。 许多现有和旧版应用不知道要查找 ACPI 的 PLD 表,也不会尝试更正非 0 度偏移量。 因此,对于此类应用,生成的视频将不正确呈现。
如果 IHV/OEM 无法按上述顺序以 0 度方向装载传感器,建议按首选项顺序执行以下缓解步骤:
自动校正相机驱动程序中的非 0 度方向(在内核模式下通过使用 AV 流微型端口驱动程序,或在用户模式下采用设备 MFT 和 MFT0 等插件),以确保生成的输出帧为 0 度方向。
通过 FSSensorOrientation 标记声明非 0 度方向,以便相机管道可以更正捕获的图像。
如前所述,在 ACPI 的 PLD 表中声明非 0 度方向。
压缩/编码媒体类型
对于压缩和/或编码媒体类型(如 MJPG、JPEG、H264、HEVC),无法使用管道校正功能。 因此,如果 FSSensorOrientation 设置为非零值,则会筛选出压缩/编码媒体类型。
对于 MJPG 媒体类型(例如来自 UVC 相机的媒体类型),帧服务器管道提供自动解码的媒体类型(NV12 或 YUY2,用于基于 DShow 的应用程序)。 将显示自动解码和更正的媒体类型,但原始 MJPG 格式不会显示。
[!注意!] 如果必须将压缩/编码媒体类型公开给应用程序,IHV/ODM 应避免使用 FSSensorOrientation 校正。 相反,更正必须由相机驱动程序完成(可以是通过内核模式中的 AV Stream 驱动程序,或者在用户模式下通过 DMFT/MFT0)。
通过 AV 流微型端口/设备 MFT/MFT0 自动校正
如果传感器无法以 0 度偏移量安装,建议的方案是 使用 AV Stream 微型端口驱动程序(或用户模式插件,以 DMFT 或 MFT0 的形式)来更正生成的捕获帧,从而使其以 0 度偏移量暴露给管道。
更正来自 AV 流微型端口和/或设备 MFT/MFT0 插件的视频帧时,所得的媒体类型声明必须基于已更正的帧。 如果传感器是以 90 度角偏置安装的,生成的视频其纵横比为 9:16,但经过更正的视频会为 16:9,则媒体类型必须标明 16:9 的纵横比。
这包括所产生的步幅信息。 这是必要的,因为负责执行更正的组件由 IHV/OEM 控制,相机管道无法看到视频帧,只有在视频帧被更正后才能看到。
强烈建议在用户模式下完成更正,并且必须遵循管道和用户模式插件之间的 API 协定。 具体而言,在使用 DMFT 或 MFT0 时,当 IMFDeviceTransform::ProcessMessage 或 IMFTransform::ProcessMessage 使用 MFT_MESSAGE_SET_D3D_MANAGER 消息调用时,用户模式插件必须遵守以下准则:
- 如果未提供 D3D 管理器(消息的 ulParam 为 0),则用户模式插件不得调用任何 GPU作来处理旋转更正。 生成的帧必须在系统内存中提供。
- 如果提供了 D3D 管理器(消息的 ulParam 是 DXGI 管理器的 IUnknown),则 DXGI 管理器必须用于旋转更正,生成的帧必须是 GPU 内存。
- 用户模式插件还必须在运行时处理 D3D 管理器消息。 发出MFT_MESSAGE_SET_D3D_MANAGER消息时,插件生成的下一帧必须与请求的内存类型(即如果提供了DXGI 管理器,则为GPU,否则为CPU)相对应。
- 当 AV 流驱动程序(或用户模式插件)处理旋转更正时,ACPI 的 PLD 结构的旋转字段必须设置为 0。
注释
使用自动更正时,OEM 和 IHV 不得通过“_PLD 旋转 ”字段播发传感器的实际方向。 在这种情况下, 旋转 字段必须指示更正后的方向:0 度。
通过 FSSensorOrientation 声明
; Defines the sensor mounting orientation offset angle in
; degrees clockwise.
FSSensorOrientation: REG_DWORD: 90, 180, 270
通过 FSSensorOrientation 注册表标记声明传感器的非零度方向,相机管道可以在将捕获的帧展示给应用程序之前进行校正。
管道将基于使用案例和应用请求/场景利用 GPU 或 CPU 资源来优化旋转逻辑。
ACPI PLD 旋转
ACPI PLD 结构的旋转字段必须为 0。 这是为了避免那些可能会使用 PLD 信息来更正帧的应用程序产生混淆。
媒体类型信息
驱动程序提供的媒体类型必须是未更正的媒体类型。 使用 FSSensorOrientation 条目通知相机管道非 0 度偏移时,传感器呈现的媒体类型信息必须是未经校正的媒体类型。 例如,如果传感器顺时针旋转了 90 度,那么生成的视频将从 16:9 变为 9:16,从而需要将 9:16 纵横比的媒体类型传递到相机管道。
这是确保管道能够正确配置计数器轮换过程所必需的:管道需要输入媒体类型和应用程序的所需输出媒体类型。
这包括步幅信息。 必须为未经校正的媒体类型向相机管道提供步幅信息。
注册表子项
FSSensorOrientation 注册表项必须在设备接口节点上发布。 建议的方法是在相机驱动程序的 INF 中的 AddInterface 指令声明期间将此声明为 AddReg 指令。
FSSensorOrientation 中显示的数据必须是REG_DWORD,接受的唯一有效值为 90、180 和 270。 任何其他值都将被视为 0 度偏移量(即忽略)。
每个值以度为单位,以顺时针方向表示传感器方向。 相机流水线将通过将视频以相同的角度逆时针旋转来校正生成的视频帧:也就是说,90 度顺时针旋转将导致 90 度逆时针旋转,使生成的视频帧恢复为 0 度偏移。
MS OS 描述符 1.0
对于基于 USB 的相机,FSSensorOrientation 也可以通过 MSOS 描述符发布。
MS OS 描述符 1.0 有两个组件:
- 固定长度标头部分
- 标题节后面的一个或多个可变长度自定义属性节
MS OS 描述符 1.0 标头部分
“Header”部分描述一个自定义属性(Face Auth Profile)。
| Offset | 领域 | 大小(字节) | 价值 | DESCRIPTION |
|---|---|---|---|---|
| 0 | dwLength | 4 | <> | |
| 4 | bcdVersion | 2 | 0x0100 | 版本 1.0 |
| 6 | wIndex | 2 | 0x0005 | 扩展属性 OS 描述符 |
| 8 | wCount | 2 | 0x0001 | 一个自定义属性 |
自定义 MS OS 描述符 1.0 属性部分
| Offset | 领域 | 大小(字节) | 价值 | DESCRIPTION |
|---|---|---|---|---|
| 0 | dwSize | 4 | 0x00000036 (54) | 此属性的总大小(以字节为单位)。 |
| 4 | dwPropertyDataType | 4 | 0x00000004 | REG_DWORD_LITTLE_ENDIAN |
| 8 | wPropertyNameLength | 2 | 0x00000024 (36) | 属性名称的大小(以字节为单位)。 |
| 10 | bPropertyName | 50 | UVC-FSSensorOrientation | Unicode 中的“UVC-FSSensorOrientation”字符串。 |
| 六十 | dwPropertyDataLength | 4 | 0x00000004 | 属性数据(sizeof(DWORD)的 4 字节。 |
| 64 | bPropertyData | 4 | 顺时针方向的偏移角度(以度为单位)。 | 有效值为 90、180 和 270。 |
MS OS 描述符 2.0
MSOS 扩展描述符 2.0 可用于定义注册表值以添加 FSSensorOrientation 支持。 这是使用 Microsoft OS 2.0 注册表属性描述符完成的。
对于 UVC-FSSensorOrientation 注册表项,下面显示了一个示例 MSOS 2.0 描述符集:
UCHAR Example2_MSOS20DescriptorSet_UVCFSSensorOrientationForFutureWindows[0x3C] =
{
//
// Microsoft OS 2.0 Descriptor Set Header
//
0x0A, 0x00, // wLength - 10 bytes
0x00, 0x00, // MSOS20_SET_HEADER_DESCRIPTOR
0x00, 0x00, 0x0?, 0x06, // dwWindowsVersion – 0x060?0000 for future Windows version
0x4A, 0x00, // wTotalLength – 74 bytes
//
// Microsoft OS 2.0 Registry Value Feature Descriptor
//
0x40, 0x00, // wLength - 64 bytes
0x04, 0x00, // wDescriptorType – 4 for Registry Property
0x04, 0x00, // wPropertyDataType - 4 for REG_DWORD_LITTLE_ENDIAN
0x32, 0x00, // wPropertyNameLength – 50 bytes
0x55, 0x00, 0x56, 0x00, // Property Name - "UVC-FSSensorOrientation"
0x43, 0x00, 0x2D, 0x00,
0x46, 0x00, 0x53, 0x00,
0x53, 0x00, 0x65, 0x00,
0x6E, 0x00, 0x73, 0x00,
0x6F, 0x00, 0x72, 0x00,
0x4F, 0x00, 0x72, 0x00,
0x69, 0x00, 0x65, 0x00,
0x6E, 0x00, 0x74, 0x00,
0x61, 0x00, 0x74, 0x00,
0x69, 0x00, 0x6F, 0x00,
0x6E, 0x00, 0x00, 0x00,
0x00, 0x00,
0x04, 0x00, // wPropertyDataLength – 4 bytes
0x5A, 0x00, 0x00, 0x00 // PropertyData – 0x0000005A (90 degrees offset)
}
通过 ACPI PLD 信息声明
作为最后一种方法,可以按上述方式利用 PLD 信息,以向应用程序指示在呈现/编码之前必须更正视频帧。 但是,如前所述,许多现有应用程序不使用 PLD 信息或处理帧旋转,因此在某些情况下,应用可能无法正确呈现生成的视频。
下图说明了每个硬件配置的“_PLD轮换”字段的值:
旋转:0 度顺时针
在上图中:
左侧的图片展示了要捕获的场景。
中间的图片描绘了一个场景如何被一个CMOS传感器查看,其物理读取顺序从左下角开始,先从左到右,然后向上移动。
右侧的图片表示相机驱动程序的输出。 在此示例中,可以在显示器按其本身的方向时直接渲染媒体缓冲区的内容,而无需额外的旋转。 因此,ACPI _PLD旋转字段的值为 0。
旋转:90 度顺时针
在这种情况下,与原始场景相比,媒体缓冲区的内容按 90 度顺时针旋转。 因此,ACPI _PLD旋转字段的值为 2。
旋转:180 度顺时针
在这种情况下,与原始场景相比,媒体缓冲区的内容按 180 度顺时针旋转。 因此,ACPI _PLD旋转字段的值为 4。
旋转:270 度顺时针
在这种情况下,与原始场景相比,媒体缓冲区的内容按 270 度顺时针旋转。 因此,ACPI _PLD旋转字段的值为 6。