1.7. 视频标准

视频设备通常支持一种或多种不同的视频标准或其变体。每个视频输入和输出可能支持另一组标准。此集合由 ioctl VIDIOC_ENUMINPUTioctl VIDIOC_ENUMOUTPUT ioctl 分别返回的 struct v4l2_input 和 struct v4l2_outputstd 字段报告。

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 摄像头等设备,视频标准的概念意义不大,因此适用特殊规则。更普遍地,对于任何捕获或输出设备,如果它

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

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

在这种情况下,驱动程序应将 struct v4l2_input 和 struct v4l2_outputstd 字段设置为零,并且 VIDIOC_G_STDVIDIOC_S_STDioctl VIDIOC_QUERYSTD, VIDIOC_SUBDEV_QUERYSTDioctl VIDIOC_ENUMSTD, VIDIOC_SUBDEV_ENUMSTD ioctl 应返回 ENOTTYEINVAL 错误代码。

应用程序可以利用 输入功能输出功能 标志来确定视频标准 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);
}