4.6. 原始 VBI 数据接口

VBI 是垂直消隐间隔的缩写,是模拟视频信号线序列中的一个间隙。在 VBI 期间不传输图像信息,从而允许阴极射线管电视的电子束返回到屏幕顶部所需的时间。使用示波器,您会在这里找到垂直同步脉冲和短数据包 ASK 调制 [1] 到视频信号上。这些是 Teletext 或 Closed Caption 等服务的传输。

此接口类型的主题是原始 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 的设备分别在 v4l2_capability 结构的 capabilities 字段中设置 V4L2_CAP_VBI_CAPTUREV4L2_CAP_VBI_OUTPUT 标志,该结构由 ioctl VIDIOC_QUERYCAP ioctl 返回。必须支持至少一种读取/写入或流式 I/O 方法。VBI 设备可能有也可能没有调谐器或调制器。

4.6.2. 补充功能

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

4.6.3. 原始 VBI 格式协商

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

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

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

要请求不同的参数,应用程序如上所述设置 v4l2_format 结构的 type 字段,并初始化 fmt 联合体的 v4l2_vbi_format vbi 成员的所有字段,或者最好只是修改 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
struct 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_START, V4L2_VBI_ITU_525_F2_START, V4L2_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_idv4l2_standard 结构的 framelines 字段。

__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 错误代码。