4.6. 原始 VBI 数据接口

VBI 是垂直消隐间隔 (Vertical Blanking Interval) 的缩写,是模拟视频信号中一系列行之间的间隙。在 VBI 期间,不会传输图像信息,从而允许阴极射线管电视的电子束返回到屏幕顶部时有一些时间。使用示波器,您会发现这里的垂直同步脉冲和短数据包 ASK 调制[1] 到视频信号上。这些是诸如图文电视或隐藏字幕等服务的传输。

此接口类型的主题是原始 VBI 数据,它从视频信号中采样,或添加到信号中以进行输出。数据格式类似于未压缩的视频图像,即若干行乘以每行若干个样本,我们称之为 VBI 图像。

按照惯例,V4L2 VBI 设备通过名为 /dev/vbi/dev/vbi0/dev/vbi31 的字符设备特殊文件访问,主设备号为 81,次设备号为 224 到 255。/dev/vbi 通常是指向首选 VBI 设备的符号链接。此惯例适用于输入和输出设备。

为了解决查找相关视频和 VBI 设备的问题,VBI 捕获和输出也可作为 /dev/video 下的设备功能使用。要使用这些设备捕获或输出原始 VBI 数据,应用程序必须调用 VIDIOC_S_FMT ioctl。以 /dev/vbi 的方式访问时,原始 VBI 捕获或输出是默认设备功能。

4.6.1. 查询功能

支持原始 VBI 捕获或输出 API 的设备在由 ioctl VIDIOC_QUERYCAP ioctl 返回的结构体 v4l2_capabilitycapabilities 字段中分别设置 V4L2_CAP_VBI_CAPTUREV4L2_CAP_VBI_OUTPUT 标志。必须至少支持一种读取/写入或流式 I/O 方法。VBI 设备可能具有或不具有调谐器或调制器。

4.6.2. 补充功能

VBI 设备应根据需要支持 视频输入或输出调谐器或调制器控件 ioctl。视频标准 ioctl 提供对 VBI 设备进行编程至关重要的信息,因此必须支持。

4.6.3. 原始 VBI 格式协商

原始 VBI 采样能力可能会有所不同,特别是采样频率。为了正确解释数据,V4L2 指定了一个 ioctl 来查询采样参数。此外,为了允许一定的灵活性,应用程序还可以建议不同的参数。

通常,这些参数在 open()不会重置,以允许 Unix 工具链,即对设备进行编程,然后像读取普通文件一样从中读取。编写良好的 V4L2 应用程序应始终确保它们真正获得所需的内容,请求合理的参数,然后检查实际参数是否合适。

要查询当前的原始 VBI 捕获参数,应用程序将结构体 v4l2_formattype 字段设置为 V4L2_BUF_TYPE_VBI_CAPTUREV4L2_BUF_TYPE_VBI_OUTPUT,并使用指向此结构的指针调用 VIDIOC_G_FMT ioctl。驱动程序填充 fmt 联合的结构体 v4l2_vbi_formatvbi 成员。

要请求不同的参数,应用程序将结构体 v4l2_formattype 字段设置为如上所述,并初始化结构体 v4l2_vbi_format vbi 成员的 fmt 联合的所有字段,或者最好只是修改 VIDIOC_G_FMT 的结果,并使用指向此结构的指针调用 VIDIOC_S_FMT ioctl。仅当给定参数不明确时,驱动程序才会返回 EINVAL 错误代码,否则,它们将根据硬件功能修改参数并返回实际参数。当驱动程序在此处分配资源时,它可能会返回 EBUSY 错误代码,以指示返回的参数有效,但当前无法使用所需的资源。例如,当要捕获的视频和 VBI 区域重叠时,或者当驱动程序支持多次打开而另一个进程已经请求 VBI 捕获或输出时,可能会发生这种情况。无论如何,应用程序必须预期其他可能返回 EBUSY 的资源分配点,即在 ioctl VIDIOC_STREAMON、VIDIOC_STREAMOFF ioctl 以及第一个 read()write()select() 调用时。

VBI 设备必须实现 VIDIOC_G_FMTVIDIOC_S_FMT ioctl,即使 VIDIOC_S_FMT 忽略所有请求并且始终返回与 VIDIOC_G_FMT 相同的默认参数。VIDIOC_TRY_FMT 是可选的。

type v4l2_vbi_format
结构体 v4l2_vbi_format

__u32

sampling_rate

每秒采样数,即单位为 1 Hz。

__u32

offset

VBI 图像的水平偏移量,相对于行同步脉冲的前沿,以样本计数:VBI 图像中的第一个样本将位于前沿之后的 offset / sampling_rate 秒处。另请参见 图 4.1. 行同步

__u32

samples_per_line

__u32

sample_format

定义样本格式,如 图像格式 中所示,这是一个四字符代码。[2]通常这是 V4L2_PIX_FMT_GREY,即每个样本由 8 位组成,较低的值面向黑色电平。不要假定值与信号电平之间有任何其他相关性。例如,MSB 不一定指示信号是“高”还是“低”,因为 128 可能不是信号的平均值。驱动程序不应通过软件转换样本格式。

__u32

start[2]

这是与VBI图像的第一行关联的扫描系统行号,分别对应第一场和第二场。有关有效值,请参阅图 4.2. ITU-R 525 行号 (M/NTSC 和 M/PAL)图 4.3. ITU-R 625 行号V4L2_VBI_ITU_525_F1_STARTV4L2_VBI_ITU_525_F2_STARTV4L2_VBI_ITU_625_F1_STARTV4L2_VBI_ITU_625_F2_START 定义为了方便起见,给出了每种 525 或 625 行格式的每个场的起始行号。请记住,ITU 行号从 1 开始,而不是 0。如果硬件无法可靠地识别扫描线,VBI 输入驱动程序可以返回起始值 0,VBI 采集可能不需要此信息。

__u32

count[2]

分别是第一场和第二场图像中的行数。

驱动程序应尽可能灵活。 例如,可以将 VBI 捕获窗口向下扩展或移动到图像区域,从而实现“全场模式”以捕获嵌入在图像中的数据服务传输。

如果不需要来自相应场的数据,应用程序可以将第一个或第二个 count 值设置为零;如果扫描系统是逐行的,即不是隔行的,则 count[1]。相应的起始值应被应用程序和驱动程序忽略。无论如何,驱动程序可能不支持单场捕获,并返回两个非零的计数器值。

两个 count 值都设置为零,或者行号超出描绘的范围[4],或者覆盖两个场的行的场图像,都是无效的,驱动程序不应返回。

要初始化 startcount 字段,应用程序必须首先确定当前的视频标准选择。为此,可以评估 v4l2_std_id 或结构体 v4l2_standardframelines 字段。

__u32

flags

请参阅下方的原始 VBI 格式标志。目前只有驱动程序设置标志,应用程序必须将此字段设置为零。

__u32

reserved[2]

此数组保留供将来扩展使用。驱动程序和应用程序必须将其设置为零。

原始 VBI 格式标志

V4L2_VBI_UNSYNC

0x0001

此标志指示无法正确区分场的硬件。 通常,VBI 图像首先在内存中存储第一场(较低的扫描线号)。 这可能是顶场或底场,具体取决于视频标准。 设置此标志后,可以首先存储第一场或第二场,但这些场仍然按正确的时间顺序排列,较旧的场先存储在内存中。[3]

V4L2_VBI_INTERLACED

0x0002

默认情况下,两个场图像将按顺序传递;第一场的所有行之后是第二场的所有行(比较场序 V4L2_FIELD_SEQ_TBV4L2_FIELD_SEQ_BT,顶场或底场是否首先存储在内存中取决于视频标准)。 设置此标志后,两个场将交错(参见 V4L2_FIELD_INTERLACED)。 第一场的第一行,然后是第二场的第一行,然后是第二行,依此类推。 当硬件被编程为捕获或输出隔行扫描视频图像并且无法同时分离场以进行 VBI 捕获时,可能需要这样的布局。 为了简单起见,设置此标志意味着两个 count 值相等且非零。

vbi_hsync.svg

图 4.1. 行同步

vbi_525.svg

图 4.2. ITU-R 525 行号 (M/NTSC 和 M/PAL)

vbi_625.svg

图 4.3. ITU-R 625 行号

请记住,VBI 图像格式取决于所选的视频标准,因此应用程序必须先选择新的标准或查询当前标准。 在格式协商之前或在切换视频标准后(可能会使协商的 VBI 参数失效)尝试读取或写入数据,应被驱动程序拒绝。 活动 I/O 期间不允许进行格式更改。

4.6.4. 读取和写入 VBI 图像

为了确保与场号同步并更易于实现,一次传递的最小数据单元是一个帧,它由两个 VBI 图像场组成,它们在内存中紧随其后。

一个帧的总大小计算如下:

(count[0] + count[1]) * samples_per_line * sample size in bytes

样本大小很可能始终为一个字节,但应用程序必须检查 sample_format 字段,以便与其他驱动程序正常工作。

VBI 设备可以支持 读/写 和/或流式传输(内存映射用户指针)I/O。后者可以通过使用缓冲区时间戳来实现视频和 VBI 数据的同步。

请记住,VIDIOC_STREAMON ioctl 和第一个 read()write()select() 调用可能是资源分配点,如果所需的硬件资源暂时不可用,则返回 EBUSY 错误代码,例如设备已被另一个进程使用。