1.7. 视频标准

视频设备通常支持一种或多种不同的视频标准或标准的变体。每个视频输入和输出可能支持另一组标准。此集合由 std 字段报告,该字段位于 v4l2_input 结构体和 v4l2_output 结构体中,分别由 ioctl VIDIOC_ENUMINPUTioctl VIDIOC_ENUMOUTPUT ioctl 返回。

V4L2 为目前全球使用的每个模拟视频标准定义了一个位,并为驱动程序定义的标准预留了位,例如,在 PAL 电视上观看 NTSC 录像带,反之亦然的混合标准。应用程序可以使用预定义的位来选择特定的标准,尽管最好向用户提供一个受支持的标准菜单。要枚举和查询受支持标准的属性,应用程序可以使用 ioctl VIDIOC_ENUMSTD, VIDIOC_SUBDEV_ENUMSTD ioctl。

许多定义的标准实际上只是几个主要标准的变体。硬件实际上可能不会区分它们,或者在内部进行区分并自动切换。因此,枚举的标准也包含一个或多个标准位的集合。

假设一个假设的调谐器能够解调 B/PAL、G/PAL 和 I/PAL 信号。第一个枚举的标准是一组 B 和 G/PAL,根据 UHF 或 VHF 频段中选择的无线电频率自动切换。枚举给出了“PAL-B/G”或“PAL-I”的选择。类似地,复合输入可以折叠标准,枚举“PAL-B/G/H/I”、“NTSC-M”和“SECAM-D/K”。[1]

要查询和选择当前视频输入或输出使用的标准,应用程序分别调用 VIDIOC_G_STDVIDIOC_S_STD ioctl。接收到的标准可以使用 ioctl VIDIOC_QUERYSTD, VIDIOC_SUBDEV_QUERYSTD ioctl 感知。

注意

所有这些 ioctl 的参数是指向 v4l2_std_id 类型(标准集)的指针,而不是标准枚举的索引。当设备有一个或多个视频输入或输出时,驱动程序必须实现所有视频标准 ioctl。

特殊的规则适用于诸如 USB 摄像头之类的设备,其中视频标准的概念意义不大。更一般地说,对于任何捕获或输出设备,该设备:

  • 无法以视频标准的标称速率捕获场或帧,或者

  • 根本不支持视频标准格式。

在这里,驱动程序应将 v4l2_input 结构体和 v4l2_output 结构体的 std 字段设置为零,并且 VIDIOC_G_STD, VIDIOC_S_STD, ioctl VIDIOC_QUERYSTD, VIDIOC_SUBDEV_QUERYSTDioctl VIDIOC_ENUMSTD, VIDIOC_SUBDEV_ENUMSTD ioctl 应返回 ENOTTY 错误代码或 EINVAL 错误代码。

应用程序可以利用 输入功能输出功能 标志来确定是否可以将视频标准 ioctl 与给定的输入或输出一起使用。

1.7.1. 示例:关于当前视频标准的信息

v4l2_std_id std_id;
struct v4l2_standard standard;

if (-1 == ioctl(fd, VIDIOC_G_STD, &std_id)) {
    /* Note when VIDIOC_ENUMSTD always returns ENOTTY this
       is no video device or it falls under the USB exception,
       and VIDIOC_G_STD returning ENOTTY is no error. */

    perror("VIDIOC_G_STD");
    exit(EXIT_FAILURE);
}

memset(&standard, 0, sizeof(standard));
standard.index = 0;

while (0 == ioctl(fd, VIDIOC_ENUMSTD, &standard)) {
    if (standard.id & std_id) {
           printf("Current video standard: %s\\n", standard.name);
           exit(EXIT_SUCCESS);
    }

    standard.index++;
}

/* EINVAL indicates the end of the enumeration, which cannot be
   empty unless this device falls under the USB exception. */

if (errno == EINVAL || standard.index == 0) {
    perror("VIDIOC_ENUMSTD");
    exit(EXIT_FAILURE);
}

1.7.2. 示例:列出当前输入支持的视频标准

struct v4l2_input input;
struct v4l2_standard standard;

memset(&input, 0, sizeof(input));

if (-1 == ioctl(fd, VIDIOC_G_INPUT, &input.index)) {
    perror("VIDIOC_G_INPUT");
    exit(EXIT_FAILURE);
}

if (-1 == ioctl(fd, VIDIOC_ENUMINPUT, &input)) {
    perror("VIDIOC_ENUM_INPUT");
    exit(EXIT_FAILURE);
}

printf("Current input %s supports:\\n", input.name);

memset(&standard, 0, sizeof(standard));
standard.index = 0;

while (0 == ioctl(fd, VIDIOC_ENUMSTD, &standard)) {
    if (standard.id & input.std)
        printf("%s\\n", standard.name);

    standard.index++;
}

/* EINVAL indicates the end of the enumeration, which cannot be
   empty unless this device falls under the USB exception. */

if (errno != EINVAL || standard.index == 0) {
    perror("VIDIOC_ENUMSTD");
    exit(EXIT_FAILURE);
}

1.7.3. 示例:选择新的视频标准

struct v4l2_input input;
v4l2_std_id std_id;

memset(&input, 0, sizeof(input));

if (-1 == ioctl(fd, VIDIOC_G_INPUT, &input.index)) {
    perror("VIDIOC_G_INPUT");
    exit(EXIT_FAILURE);
}

if (-1 == ioctl(fd, VIDIOC_ENUMINPUT, &input)) {
    perror("VIDIOC_ENUM_INPUT");
    exit(EXIT_FAILURE);
}

if (0 == (input.std & V4L2_STD_PAL_BG)) {
    fprintf(stderr, "Oops. B/G PAL is not supported.\\n");
    exit(EXIT_FAILURE);
}

/* Note this is also supposed to work when only B
   or G/PAL is supported. */

std_id = V4L2_STD_PAL_BG;

if (-1 == ioctl(fd, VIDIOC_S_STD, &std_id)) {
    perror("VIDIOC_S_STD");
    exit(EXIT_FAILURE);
}