4.5.1. 内存到内存有状态视频解码器接口

有状态视频解码器接收完整的字节流块(例如 Annex-B H.264/HEVC 流,原始 VP8/9 流)并将它们解码为显示顺序的原始视频帧。解码器应该不需要来自客户端的任何附加信息来处理这些缓冲区。

强烈不建议在驱动程序中执行软件解析、处理等流操作以支持此接口。如果需要此类操作,强烈建议使用无状态视频解码器接口(正在开发中)。

4.5.1.1. 本文档中使用的约定和符号

  1. 如果本文档中没有另外指定,则适用通用的 V4L2 API 规则。

  2. “必须”、“可以”、“应该”等词的含义符合 RFC 2119

  3. 所有未标记为“可选”的步骤都是必需的。

  4. VIDIOC_G_EXT_CTRLS()VIDIOC_S_EXT_CTRLS() 可以与 VIDIOC_G_CTRL()VIDIOC_S_CTRL() 互换使用,除非另有说明。

  5. 单平面 API(参见 单平面和多平面 API)和适用的结构可以与多平面 API 互换使用,除非另有说明,具体取决于解码器功能并遵循通用的 V4L2 指南。

  6. i = [a..b]: 从 a 到 b 的整数序列,包括 a 和 b,即 i = [0..2]: i = 0, 1, 2。

  7. 给定一个 OUTPUT 缓冲区 A,则 A' 表示 CAPTURE 队列上的缓冲区,其中包含处理缓冲区 A 产生的数据。

4.5.1.2. 术语表

CAPTURE

目标缓冲区队列;对于解码器,包含解码帧的缓冲区队列;对于编码器,包含编码字节流的缓冲区队列;V4L2_BUF_TYPE_VIDEO_CAPTUREV4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;数据从硬件捕获到 CAPTURE 缓冲区中。

client

与实现此接口的解码器或编码器通信的应用程序。

coded format

编码/压缩的视频字节流格式(例如 H.264、VP8 等);另请参见:raw format。

coded height

给定编码分辨率的高度。

coded resolution

像素流分辨率,与编解码器和硬件要求对齐;通常可见分辨率向上舍入到完整宏块;另请参见:visible resolution。

coded width

给定编码分辨率的宽度。

coding tree unit

HEVC 编解码器的处理单元(对应于 H.264、VP8、VP9 中的宏块单元),可以使用高达 64×64 像素的块结构。擅长将图片细分为可变大小的结构。

decode order

帧的解码顺序;如果编码格式包含帧重新排序的功能,则可能与显示顺序不同;对于解码器,OUTPUT 缓冲区必须由客户端按解码顺序排队;对于编码器,CAPTURE 缓冲区必须由编码器按解码顺序返回。

destination

解码过程产生的数据;参见 CAPTURE

display order

帧必须显示的顺序;对于编码器,OUTPUT 缓冲区必须由客户端按显示顺序排队;对于解码器,CAPTURE 缓冲区必须由解码器按显示顺序返回。

DPB

解码图片缓冲区;H.264/HEVC 术语,用于存储解码后的原始帧,可在进一步的解码步骤中用作参考。

EOS

流结束。

IDR

即时解码器刷新;H.264/HEVC 编码流中一种类型的关键帧,它清除早期参考帧 (DPB) 的列表。

keyframe

不参考之前解码的帧的编码帧,即可以完全独立解码。

macroblock

基于线性块变换的图像和视频压缩格式(例如 H.264、VP8、VP9)中的处理单元;特定于编解码器,但对于大多数流行的编解码器,大小为 16x16 样本(像素)。HEVC 编解码器使用稍微灵活的处理单元,称为编码树单元 (CTU)。

OUTPUT

源缓冲区队列;对于解码器,包含编码字节流的缓冲区队列;对于编码器,包含原始帧的缓冲区队列;V4L2_BUF_TYPE_VIDEO_OUTPUTV4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;硬件从 OUTPUT 缓冲区获取数据。

PPS

图片参数集;H.264/HEVC 字节流中的一种元数据实体。

raw format

包含原始像素数据(例如 YUV、RGB 格式)的未压缩格式。

resume point

字节流中的一个点,可以从该点开始/继续解码,而无需任何先前的状态/数据,例如:关键帧 (VP8/VP9) 或 SPS/PPS/IDR 序列 (H.264/HEVC);需要一个恢复点才能开始解码新流,或在查找后恢复解码。

source

馈送到解码器或编码器的数据;参见 OUTPUT

source height

给定源分辨率的像素高度;仅与编码器相关。

source resolution

源帧的像素分辨率,作为编码器的源,并受进一步裁剪到可见分辨率范围的限制;仅与编码器相关。

source width

给定源分辨率的像素宽度;仅与编码器相关。

SPS

序列参数集;H.264/HEVC 字节流中的一种元数据实体。

stream metadata

编码字节流中包含的附加(非视觉)信息;例如:编码分辨率、可见分辨率、编解码器配置文件。

visible height

给定可见分辨率的高度;显示高度。

visible resolution

可见图片的流分辨率,以像素为单位,用于显示目的;必须小于或等于编码分辨率;显示分辨率。

visible width

给定可见分辨率的宽度;显示宽度。

4.5.1.3. 状态机

DOT digraph of decoder state machine

解码器状态机

4.5.1.4. 查询功能

  1. 要枚举解码器支持的编码格式集,客户端可以在 OUTPUT 上调用 VIDIOC_ENUM_FMT()

    • 无论在 CAPTURE 上设置的格式如何,都将返回完整的支持格式集。

    • 检查 v4l2_fmtdesc 的标志字段,以获取有关解码器在每种编码格式方面的功能的更多信息。特别是解码器是否具有成熟的字节流解析器,以及解码器是否支持动态分辨率更改。

  2. 要枚举支持的原始格式集,客户端可以在 CAPTURE 上调用 VIDIOC_ENUM_FMT()

    • 仅返回当前在 OUTPUT 上活动的格式支持的格式。

    • 为了枚举给定编码格式支持的原始格式,客户端必须首先在 OUTPUT 上设置该编码格式,然后在 CAPTURE 上枚举格式。

  3. 客户端可以使用 VIDIOC_ENUM_FRAMESIZES() 来检测给定格式支持的分辨率,在 v4l2_frmsizeenum pixel_format 中传递所需的像素格式。

    • VIDIOC_ENUM_FRAMESIZES() 为编码像素格式返回的值将包括解码器为给定编码像素格式支持的所有可能的编码分辨率。

    • VIDIOC_ENUM_FRAMESIZES() 为原始像素格式返回的值将包括解码器为给定原始像素格式和当前在 OUTPUT 上设置的编码格式支持的所有可能的帧缓冲区分辨率。

  4. 可以使用各自的控件通过 VIDIOC_QUERYCTRL() 查询当前在 OUTPUT 上设置的编码格式支持的配置文件和级别(如果适用)。

4.5.1.5. 初始化

  1. 通过 VIDIOC_S_FMT()OUTPUT 上设置编码格式。

    • 必填字段

      type

      适用于 OUTPUTV4L2_BUF_TYPE_* 枚举。

      pixelformat

      编码像素格式。

      width, height

      流的编码分辨率;仅当无法从给定编码格式的流中解析时才需要;否则,解码器将使用此分辨率作为占位符分辨率,一旦可以从流中解析实际编码分辨率,该分辨率很可能会更改。

      sizeimage

      所需的 OUTPUT 缓冲区大小;解码器可以调整它以匹配硬件要求。

      other fields

      遵循标准语义。

    • 返回字段

      sizeimage

      调整后的 OUTPUT 缓冲区大小。

    • CAPTURE 格式将立即根据 VIDIOC_S_FMT() 返回的宽度和高度更新为适当的帧缓冲区分辨率。但是,对于包含流分辨率信息的编码格式,在解码器完成从流中解析信息后,它将使用新值更新 CAPTURE 格式并发出源更改事件信号,无论它们是否与客户端设置的值匹配。

    Important

    更改 OUTPUT 格式可能会更改当前设置的 CAPTURE 格式。如何确定新的 CAPTURE 格式由解码器决定,客户端必须确保之后满足其需求。

  2. 通过 OUTPUT 上的 VIDIOC_REQBUFS() 分配源(字节流)缓冲区。

    • 必填字段

      count

      请求分配的缓冲区数量;大于零。

      type

      适用于 OUTPUTV4L2_BUF_TYPE_* 枚举。

      memory

      遵循标准语义。

    • 返回字段

      count

      实际分配的缓冲区数量。

    Warning

    实际分配的缓冲区数量可能与给定的 count 不同。客户端必须在调用返回后检查 count 的更新值。

    或者,可以使用 OUTPUT 队列上的 VIDIOC_CREATE_BUFS() 来更好地控制缓冲区分配。

    • 必填字段

      count

      请求分配的缓冲区数量;大于零。

      type

      适用于 OUTPUTV4L2_BUF_TYPE_* 枚举。

      memory

      遵循标准语义。

      format

      遵循标准语义。

    • 返回字段

      count

      调整为分配的缓冲区数量。

    Warning

    实际分配的缓冲区数量可能与给定的 count 不同。客户端必须在调用返回后检查 count 的更新值。

  3. 通过 VIDIOC_STREAMON()OUTPUT 队列上启动流。

  4. 此步骤仅适用于在流中包含分辨率信息的编码格式。 继续通过 VIDIOC_QBUF()VIDIOC_DQBUF() 将字节流缓冲区排队/出队到/从 OUTPUT 队列。将处理缓冲区并按顺序返回给客户端,直到找到配置 CAPTURE 队列所需的元数据。这由解码器发送 V4L2_EVENT_SOURCE_CHANGE 事件指示,其中 changes 设置为 V4L2_EVENT_SRC_CH_RESOLUTION

    • 如果第一个缓冲区没有包含足够的数据来发生这种情况,则不会出错。只要需要更多数据,缓冲区的处理就会继续。

    • 如果需要触发事件的缓冲区中的数据来解码第一帧,则在初始化序列完成并解码帧之前,不会将其返回给客户端。

    • 如果客户端没有自己设置流的编码分辨率,则在 CAPTURE 队列上调用 VIDIOC_G_FMT()VIDIOC_S_FMT()VIDIOC_TRY_FMT()VIDIOC_REQBUFS() 不会返回流的实际值,直到发出 V4L2_EVENT_SOURCE_CHANGE 事件,其中 changes 设置为 V4L2_EVENT_SRC_CH_RESOLUTION

    Important

    在解码器将事件排队后发出的任何客户端查询都将返回适用于刚刚解析的流的值,包括队列格式、选择矩形和控件。

    Note

    有能力从字节流中自行获取流参数的客户端可以尝试将 OUTPUT 格式的宽度和高度设置为与流的编码大小匹配的非零值,跳过此步骤并继续执行捕获设置序列。但是,它不能依赖任何关于流参数的驱动程序查询,例如选择矩形和控件,因为解码器尚未从流中解析它们。如果客户端配置的值与解码器解析的值不匹配,则将触发动态分辨率更改以重新配置它们。

    Note

    在此阶段不会生成解码帧。

  5. 继续执行捕获设置序列。

4.5.1.6. 捕获设置

  1. CAPTURE 队列上调用 VIDIOC_G_FMT() 以获取从字节流中解析/解码的目标缓冲区的格式。

    • 必填字段

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

    • 返回字段

      width, height

      解码帧的帧缓冲区分辨率。

      pixelformat

      解码帧的像素格式。

      num_planes(仅适用于 _MPLANE type

      像素格式的平面数量。

      sizeimage, bytesperline

      按照标准语义;匹配帧缓冲区格式。

    Note

    pixelformat 的值可以是解码器为当前流支持的任何像素格式。解码器应为默认配置选择首选/最佳格式。例如,如果后者需要额外的转换步骤,则 YUV 格式可能优于 RGB 格式。

  2. 可选。 通过 VIDIOC_G_SELECTION() 获取可见分辨率。

    • 必填字段

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      target

      设置为 V4L2_SEL_TGT_COMPOSE

    • 返回字段

      r.left, r.top, r.width, r.height

      可见矩形;它必须适合 CAPTUREVIDIOC_G_FMT() 返回的帧缓冲区分辨率。

    • 以下选择目标在 CAPTURE 上受支持

      V4L2_SEL_TGT_CROP_BOUNDS

      对应于流的编码分辨率。

      V4L2_SEL_TGT_CROP_DEFAULT

      覆盖 CAPTURE 缓冲区中包含有意义的图片数据(可见区域)的部分的矩形;宽度和高度将等于流的可见分辨率。

      V4L2_SEL_TGT_CROP

      要输出到 CAPTURE 的编码分辨率内的矩形;默认为 V4L2_SEL_TGT_CROP_DEFAULT;在没有额外合成/缩放功能的硬件上是只读的。

      V4L2_SEL_TGT_COMPOSE_BOUNDS

      裁剪帧可以合成到 CAPTURE 缓冲区中的最大矩形;如果硬件不支持合成/缩放,则等于 V4L2_SEL_TGT_CROP

      V4L2_SEL_TGT_COMPOSE_DEFAULT

      等于 V4L2_SEL_TGT_CROP

      V4L2_SEL_TGT_COMPOSE

      CAPTURE 缓冲区内写入裁剪帧的矩形;默认为 V4L2_SEL_TGT_COMPOSE_DEFAULT;在没有额外合成/缩放功能的硬件上是只读的。

      V4L2_SEL_TGT_COMPOSE_PADDED

      CAPTURE 缓冲区内被硬件覆盖的矩形;如果硬件不写入填充像素,则等于 V4L2_SEL_TGT_COMPOSE

    Warning

    仅当解码器成功解析流元数据后,才能保证这些值有意义。客户端在此之前不得依赖查询。

  3. 可选。 通过 CAPTURE 队列上的 VIDIOC_ENUM_FMT() 枚举 CAPTURE 格式。一旦解析并知道流信息,客户端就可以使用此 ioctl 来发现给定流支持哪些原始格式,并通过 VIDIOC_S_FMT() 选择其中一种格式。

    Important

    解码器将仅返回当前建立的编码格式支持的格式,按照 OUTPUT 格式和/或在此初始化序列中解析的流元数据,即使解码器通常可能支持更多格式。换句话说,返回的集合将是查询功能部分中提到的初始查询的子集。

    例如,解码器可能支持 1920x1088 及更低分辨率的 YUV 和 RGB 格式,但仅支持更高分辨率的 YUV(由于硬件限制)。在解析 1920x1088 或更低分辨率后,VIDIOC_ENUM_FMT() 可能会返回一组 YUV 和 RGB 像素格式,但在解析高于 1920x1088 的分辨率后,解码器将不会返回此分辨率不支持的 RGB。

    但是,在发现同一流中的分辨率更改后触发的后续分辨率更改事件可能会将流切换到较低的分辨率,并且在这种情况下,VIDIOC_ENUM_FMT() 将再次返回 RGB 格式。

  4. 可选。 通过 CAPTURE 队列上的 VIDIOC_S_FMT() 设置 CAPTURE 格式。客户端可以选择与解码器在 VIDIOC_G_FMT() 中选择/建议的格式不同的格式。

    • 必填字段

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      pixelformat

      原始像素格式。

      width, height

      解码流的帧缓冲区分辨率;通常与 VIDIOC_G_FMT() 返回的分辨率相同,但如果硬件支持合成和/或缩放,则可能会有所不同。

  • 设置 CAPTURE 格式会将合成选择矩形重置为基于新分辨率的默认值,如前一步所述。

  1. 可选。 如果需要并且解码器具有合成和/或缩放功能,则通过 CAPTURE 队列上的 VIDIOC_S_SELECTION() 设置合成矩形。

    • 必填字段

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      target

      设置为 V4L2_SEL_TGT_COMPOSE

      r.left, r.top, r.width, r.height

      CAPTURE 缓冲区内写入裁剪帧的矩形;默认为 V4L2_SEL_TGT_COMPOSE_DEFAULT;在没有额外合成/缩放功能的硬件上是只读的。

    • 返回字段

      r.left, r.top, r.width, r.height

      可见矩形;它必须适合 CAPTUREVIDIOC_G_FMT() 返回的帧缓冲区分辨率。

    Warning

    解码器可能会将合成矩形调整为最接近支持的矩形,以满足编解码器和硬件要求。客户端需要检查 VIDIOC_S_SELECTION() 返回的调整后的矩形。

  2. 如果满足以下所有条件,客户端可以立即恢复解码

    • 新格式的 sizeimage(在前几步中确定)小于或等于当前分配的缓冲区的大小,

    • 当前分配的缓冲区数量大于或等于先前步骤中获取的最小缓冲区数量。为了满足此要求,客户端可以使用 VIDIOC_CREATE_BUFS() 添加新缓冲区。

    在这种情况下,其余步骤不适用,客户端可以通过以下操作之一恢复解码

    • 如果 CAPTURE 队列正在流式传输,则调用带有 V4L2_DEC_CMD_START 命令的 VIDIOC_DECODER_CMD()

    • 如果 CAPTURE 队列未流式传输,则在 CAPTURE 队列上调用 VIDIOC_STREAMON()

    但是,如果客户端打算更改缓冲区集,以降低内存使用率或出于任何其他原因,可以通过以下步骤实现。

  3. 如果 CAPTURE 队列正在流式传输, 则继续在 CAPTURE 队列上排队和出队缓冲区,直到出队带有 V4L2_BUF_FLAG_LAST 标志的缓冲区。

  4. 如果 CAPTURE 队列正在流式传输, 则在 CAPTURE 队列上调用 VIDIOC_STREAMOFF() 以停止流式传输。

    Warning

    OUTPUT 队列必须保持流式传输。在其上调用 VIDIOC_STREAMOFF() 将中止序列并触发查找。

  5. 如果 CAPTURE 队列已分配缓冲区, 则使用 VIDIOC_REQBUFS() 释放 CAPTURE 缓冲区。

    • 必填字段

      count

      设置为 0。

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      memory

      遵循标准语义。

  6. 通过 CAPTURE 队列上的 VIDIOC_REQBUFS() 分配 CAPTURE 缓冲区。

    • 必填字段

      count

      请求分配的缓冲区数量;大于零。

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      memory

      遵循标准语义。

    • 返回字段

      count

      实际分配的缓冲区数量。

    Warning

    实际分配的缓冲区数量可能与给定的 count 不同。客户端必须在调用返回后检查 count 的更新值。

    Note

    为了分配多于最小数量的缓冲区(用于管道深度),客户端可以查询 V4L2_CID_MIN_BUFFERS_FOR_CAPTURE 控件以获取所需的最小缓冲区数量,并将获得的值加上 VIDIOC_REQBUFS()count 字段中所需的额外缓冲区数量。

    或者,可以使用 VIDIOC_CREATE_BUFS()CAPTURE 队列上更灵活地控制缓冲区分配。例如,通过分配比当前 CAPTURE 格式更大的缓冲区,可以适应未来的分辨率更改。

    • 必填字段

      count

      请求分配的缓冲区数量;大于零。

      type

      适用于 CAPTUREV4L2_BUF_TYPE_* 枚举。

      memory

      遵循标准语义。

      format

      一种格式,表示新分配的缓冲区可以容纳的最大帧缓冲区分辨率。

    • 返回字段

      count

      调整为分配的缓冲区数量。

    Warning

    实际分配的缓冲区数量可能与给定的 count 不同。客户端必须在调用返回后检查 count 的更新值。

    Note

    要为与从流元数据解析的格式不同的格式分配缓冲区,客户端必须在启动元数据解析之前按以下步骤操作

    • OUTPUT 格式的宽度和高度设置为所需的编码分辨率,以便解码器正确配置 CAPTURE 格式,

    • 使用 VIDIOC_G_FMT() 查询 CAPTURE 格式,并将其保存到此步骤。

    查询中获得的格式可以与此步骤中的 VIDIOC_CREATE_BUFS() 一起使用以分配缓冲区。

  7. CAPTURE 队列上调用 VIDIOC_STREAMON() 以启动帧解码。

4.5.1.7. 解码

成功完成“采集设置”序列后,将达到此状态。在此状态下,客户端通过 VIDIOC_QBUF()VIDIOC_DQBUF() 将缓冲区排队和出队到两个队列,遵循标准语义。

OUTPUT 缓冲区的内容取决于活动的编码像素格式,并且可能受到编解码器特定的扩展控制的影响,如每种格式的文档中所述。

两个队列独立运行,遵循 V4L2 缓冲区队列和内存到内存设备的标准行为。此外,由于所选编码格式的属性(例如帧重新排序),从 CAPTURE 队列中出队的解码帧的顺序可能与编码帧排队到 OUTPUT 队列的顺序不同。

客户端不得假设 CAPTUREOUTPUT 缓冲区之间存在任何直接关系,以及缓冲区变为可出队的任何特定时间。具体来说

  • 排队到 OUTPUT 的缓冲区可能不会在 CAPTURE 上产生任何缓冲区(例如,如果它不包含编码数据,或者如果其中只存在元数据语法结构),

  • 排队到 OUTPUT 的缓冲区可能会在 CAPTURE 上产生多个缓冲区(如果编码数据包含多个帧,或者如果返回解码帧允许解码器返回在解码中位于其之前,但在显示顺序中位于其之后的帧),

  • 排队到 OUTPUT 的缓冲区可能会导致缓冲区在稍后的解码过程中和/或在处理其他 OUTPUT 缓冲区之后在 CAPTURE 上产生,或者乱序返回,例如,如果使用显示重新排序,

  • 缓冲区可能会在没有额外缓冲区排队到 OUTPUT 的情况下在 CAPTURE 队列上变为可用(例如,在排空或 EOS 期间),这是因为过去排队的 OUTPUT 缓冲区的解码结果仅在稍后时间可用,这是由于解码过程的特殊性。

Note

为了允许将解码的 CAPTURE 缓冲区与它们所源自的 OUTPUT 缓冲区匹配,客户端可以在排队 OUTPUT 缓冲区时设置 v4l2_buffer 结构体的 timestamp 字段。由解码该 OUTPUT 缓冲区产生的 CAPTURE 缓冲区将在出队时将其 timestamp 字段设置为相同的值。

除了一个 OUTPUT 缓冲区产生一个 CAPTURE 缓冲区的简单情况之外,还定义了以下情况

  • 一个 OUTPUT 缓冲区生成多个 CAPTURE 缓冲区:相同的 OUTPUT 时间戳将被复制到多个 CAPTURE 缓冲区。

  • 多个 OUTPUT 缓冲区生成一个 CAPTURE 缓冲区:将复制第一个排队的 OUTPUT 缓冲区的时间戳。

  • 解码顺序与显示顺序不同(即,CAPTURE 缓冲区与 OUTPUT 缓冲区相比是乱序的):CAPTURE 时间戳将不保留 OUTPUT 时间戳的顺序。

Note

用作流的参考帧的 CAPTURE 缓冲区的后备内存甚至可以在它们出队后被硬件读取。因此,客户端应避免在 CAPTURE 队列正在流式传输时写入此内存。不遵守此规定可能会导致解码帧损坏。

类似地,当使用 V4L2_MEMORY_MMAP 以外的内存类型时,客户端应确保每个 CAPTURE 缓冲区始终以相同的后备内存排队,只要 CAPTURE 队列正在流式传输。原因是 V4L2 缓冲区索引可以被驱动程序用来识别帧。因此,如果在不同的缓冲区 ID 下提交参考帧的后备内存,则驱动程序可能会错误地识别它并将新帧解码到其中,而该帧仍在被使用,从而导致以下帧损坏。

在解码期间,解码器可能会启动以下列出的特殊序列之一。这些序列将导致解码器返回所有 CAPTURE 缓冲区,这些缓冲区源自序列开始之前处理的所有 OUTPUT 缓冲区。最后一个缓冲区将设置 V4L2_BUF_FLAG_LAST 标志。为了确定要遵循的序列,客户端必须检查是否存在任何挂起的事件,并且

  • 如果存在 V4L2_EVENT_SOURCE_CHANGE 事件,其中 changes 设置为 V4L2_EVENT_SRC_CH_RESOLUTION,则需要遵循“动态分辨率更改”序列,

  • 如果存在 V4L2_EVENT_EOS 事件,则需要遵循“流结束”序列。

某些序列可以相互混合,并且需要按发生的情况进行处理。每个序列的确切操作都有文档记录。

如果发生解码错误,将向客户端报告,详细程度取决于解码器的功能。具体来说

  • 包含失败的解码操作结果的 CAPTURE 缓冲区将返回,并设置 V4L2_BUF_FLAG_ERROR 标志,

  • 如果解码器能够精确地报告触发错误的 OUTPUT 缓冲区,则此类缓冲区将返回,并设置 V4L2_BUF_FLAG_ERROR 标志。

如果发生不允许继续解码的致命故障,则对相应解码器文件句柄的任何进一步操作都将返回 -EIO 错误代码。客户端可以关闭文件句柄并打开一个新句柄,或者通过停止两个队列上的流式传输、释放所有缓冲区并再次执行初始化序列来重新初始化实例。

4.5.1.8. 搜索

搜索由 OUTPUT 队列控制,因为它是编码数据的来源。搜索不需要对 CAPTURE 队列执行任何特定操作,但可能会按照正常的解码器操作受到影响。

  1. 通过 VIDIOC_STREAMOFF() 停止 OUTPUT 队列以开始搜索序列。

    • 必填字段

      type

      适用于 OUTPUTV4L2_BUF_TYPE_* 枚举。

    • 解码器将删除所有挂起的 OUTPUT 缓冲区,并且必须将它们视为返回给客户端(遵循标准语义)。

  2. 通过 VIDIOC_STREAMON() 重新启动 OUTPUT 队列。

    • 必填字段

      type

      适用于 OUTPUTV4L2_BUF_TYPE_* 枚举。

    • 调用返回后,解码器将开始接受新的源字节流缓冲区。

  3. 在搜索到 OUTPUT 队列之后,开始排队包含编码数据的缓冲区,直到找到合适的恢复点。

    Note

    没有要求从恢复点(例如 SPS 或关键帧)开始精确地排队编码数据。任何排队的 OUTPUT 缓冲区都将被处理并返回给客户端,直到找到合适的恢复点。在寻找恢复点时,解码器不应在 CAPTURE 缓冲区中生成任何解码帧。

    已知某些硬件会错误地处理搜索到非恢复点的情况。此类操作可能会导致在 CAPTURE 队列上提供未指定数量的损坏的解码帧。驱动程序必须确保不会发生致命的解码错误或崩溃,并实现任何必要的处理和解决方法来解决与搜索操作相关的硬件问题。

    Warning

    对于 H.264/HEVC 编解码器,客户端必须注意不要搜索 SPS/PPS 的更改。即使目标帧可能是关键帧,解码器状态中的陈旧 SPS/PPS 也会导致解码时出现不确定的结果。尽管解码器必须在没有崩溃或致命解码错误的情况下处理这种情况,但客户端不得期望有合理的解码输出。

    如果硬件可以检测到此类损坏的解码帧,则相应的缓冲区将返回给客户端,并设置 V4L2_BUF_FLAG_ERROR。有关解码错误报告的进一步说明,请参见“解码”部分。

  4. 找到恢复点后,解码器将开始返回包含解码帧的 CAPTURE 缓冲区。

Important

搜索可能会导致启动“动态分辨率更改”序列,这是因为搜索目标具有与搜索之前解码的流部分不同的解码参数。该序列必须按照正常的解码器操作进行处理。

Warning

未指定 CAPTURE 队列何时开始生成包含来自搜索后排队的 OUTPUT 缓冲区的解码数据的缓冲区,因为它与 OUTPUT 队列独立运行。

解码器可能会返回许多剩余的 CAPTURE 缓冲区,这些缓冲区包含源自搜索序列执行之前排队的 OUTPUT 缓冲区的解码帧。

VIDIOC_STREAMOFF 操作会丢弃任何剩余的排队 OUTPUT 缓冲区,这意味着并非所有在搜索序列之前排队的 OUTPUT 缓冲区都可能生成匹配的 CAPTURE 缓冲区。例如,给定 OUTPUT 队列上的操作序列

QBUF(A)、QBUF(B)、STREAMOFF()、STREAMON()、QBUF(G)、QBUF(H),

允许在 CAPTURE 队列上执行以下任何结果

{A’、B’、G’、H’}、{A’、G’、H’}、{G’、H’}。

为了确定包含搜索后第一个解码帧的 CAPTURE 缓冲区,客户端可以观察时间戳以匹配 CAPTURE 和 OUTPUT 缓冲区,或者使用 V4L2_DEC_CMD_STOP 和 V4L2_DEC_CMD_START 来排空解码器。

Note

为了实现即时搜索,客户端也可以重新启动 CAPTURE 队列上的流式传输,以丢弃已解码但尚未出队的缓冲区。

4.5.1.9. 动态分辨率更改

在字节流中包含分辨率元数据的流可能需要在解码期间切换到不同的分辨率。

Note

并非所有解码器都能检测到分辨率更改。那些能够检测到的解码器会在调用 VIDIOC_ENUM_FMT() 时为编码格式设置 V4L2_FMT_FLAG_DYN_RESOLUTION 标志。

当解码器检测到编码帧具有与先前建立的帧不同的以下一个或多个参数(并由相应的查询反映)时,该序列开始

  • 编码分辨率(OUTPUT 宽度和高度),

  • 可见分辨率(选择矩形),

  • 解码所需的最小缓冲区数,

  • 比特流的位深已更改。

每当发生这种情况时,解码器必须按如下方式进行

  1. 在流中遇到分辨率更改后,解码器会发送一个 V4L2_EVENT_SOURCE_CHANGE 事件,其中 changes 设置为 V4L2_EVENT_SRC_CH_RESOLUTION

    Important

    在解码器对事件进行排队之后发出的任何客户端查询都将返回适用于分辨率更改后流的值,包括队列格式、选择矩形和控件。

  2. 然后,解码器将处理和解码分辨率更改点之前的所有剩余缓冲区。

    • 来自更改之前的最后一个缓冲区必须使用 V4L2_BUF_FLAG_LAST 标志标记,类似于上面的“排空”序列。

    Warning

    最后一个缓冲区可能为空(v4l2_buffer bytesused = 0),在这种情况下,客户端必须忽略它,因为它不包含解码帧。

    Note

    任何尝试将更多 CAPTURE 缓冲区出队到标记有 V4L2_BUF_FLAG_LAST 的缓冲区之外的尝试都将导致来自 VIDIOC_DQBUF() 的 -EPIPE 错误。

客户端必须按照以下描述继续该序列以继续解码过程。

  1. 使源更改事件出队。

    Important

    源更改会触发隐式解码器排空,类似于显式“排空”序列。解码器在完成后停止。必须通过在 CAPTURE 队列上调用一对 VIDIOC_STREAMOFF()VIDIOC_STREAMON(),或者使用 V4L2_DEC_CMD_START 命令调用 VIDIOC_DECODER_CMD() 来恢复解码过程。

  2. 继续执行捕获设置序列。

Note

在分辨率更改序列期间,OUTPUT 队列必须保持流式传输。在 OUTPUT 队列上调用 VIDIOC_STREAMOFF() 将中止序列并启动搜索。

原则上,OUTPUT 队列与 CAPTURE 队列分开运行,并且这在整个分辨率更改序列期间仍然成立。

客户端应该为了获得最佳性能和简单性,甚至在处理此序列时也保持将缓冲区排队/从 OUTPUT 队列出队。

4.5.1.10. 排空

为了确保已处理所有排队的 OUTPUT 缓冲区,并且相关的 CAPTURE 缓冲区已提供给客户端,客户端必须遵循下面描述的排空序列。在排空序列结束后,客户端已收到在序列开始之前排队的所有 OUTPUT 缓冲区的全部解码帧。

  1. 通过发出 VIDIOC_DECODER_CMD() 开始排空。

    • 必填字段

      cmd

      设置为 V4L2_DEC_CMD_STOP

      flags

      设置为 0。

      pts

      设置为 0。

    Warning

    只有当 OUTPUTCAPTURE 队列都在流式传输时,才能启动该序列。出于兼容性原因,即使任何一个队列未流式传输,对 VIDIOC_DECODER_CMD() 的调用也不会失败,但同时它不会启动“排空”序列,因此下面描述的步骤将不适用。

  2. 客户端在发出 VIDIOC_DECODER_CMD() 之前排队的任何 OUTPUT 缓冲区都将像往常一样被处理和解码。客户端必须继续独立处理两个队列,类似于正常的解码操作。这包括

    • 在继续排空序列之前,处理由于处理这些缓冲区而触发的任何操作,例如“动态分辨率更改”序列,

    • 排队和出队 CAPTURE 缓冲区,直到出队标记有 V4L2_BUF_FLAG_LAST 标志的缓冲区,

      Warning

      最后一个缓冲区可能为空(v4l2_buffer bytesused = 0),在这种情况下,客户端必须忽略它,因为它不包含解码帧。

      Note

      任何尝试将更多 CAPTURE 缓冲区出队到标记有 V4L2_BUF_FLAG_LAST 的缓冲区之外的尝试都将导致来自 VIDIOC_DQBUF() 的 -EPIPE 错误。

    • 出队已处理的 OUTPUT 缓冲区,直到所有在 V4L2_DEC_CMD_STOP 命令之前排队的缓冲区都出队,

    • 如果客户端订阅了 V4L2_EVENT_EOS 事件,则出队该事件。

    Note

    为了向后兼容,当最后一个帧已被解码并且所有帧都已准备好出队时,解码器将发出 V4L2_EVENT_EOS 事件的信号。这是一种已弃用的行为,客户端不得依赖它。应该改用 V4L2_BUF_FLAG_LAST 缓冲区标志。

  3. 一旦出队了所有在调用 V4L2_DEC_CMD_STOP 之前排队的 OUTPUT 缓冲区,并且最后一个 CAPTURE 缓冲区已出队,解码器将停止并且它将接受但不会处理任何新排队的 OUTPUT 缓冲区,直到客户端发出以下任何操作

    • V4L2_DEC_CMD_START - 解码器不会被重置并且将正常恢复操作,并保留排空之前的所有状态,

    • CAPTURE 队列上的一对 VIDIOC_STREAMOFF()VIDIOC_STREAMON() - 解码器将正常恢复操作,但是队列中仍有的任何 CAPTURE 缓冲区都将返回给客户端,

    • OUTPUT 队列上的一对 VIDIOC_STREAMOFF()VIDIOC_STREAMON() - 任何挂起的源缓冲区都将返回给客户端,并且将触发“搜索”序列。

Note

一旦启动排空序列,客户端就需要将其驱动到完成,如上面的步骤所述,除非它通过在任何 OUTPUTCAPTURE 队列上发出 VIDIOC_STREAMOFF() 来中止该过程。不允许客户端在排空序列正在进行时再次发出 V4L2_DEC_CMD_STARTV4L2_DEC_CMD_STOP,并且如果尝试这样做,它们将失败并显示 -EBUSY 错误代码。

尽管不是强制性的,但可以使用 VIDIOC_TRY_DECODER_CMD() 查询解码器命令的可用性。

4.5.1.11. 流结束

如果解码器在流中遇到流结束标记,则解码器将启动“排空”序列,客户端必须按照上面的描述处理该序列,跳过初始 VIDIOC_DECODER_CMD()

4.5.1.12. 提交点

设置格式和分配缓冲区会触发解码器行为的更改。

  1. OUTPUT 队列上设置格式可能会更改在 CAPTURE 队列上支持/公布的格式集。特别是,它也意味着 CAPTURE 格式可能会被重置,并且客户端不得依赖于先前设置的格式被保留。

  2. CAPTURE 队列上枚举格式始终仅返回当前 OUTPUT 格式支持的格式。

  3. CAPTURE 队列上设置格式不会更改 OUTPUT 队列上可用的格式列表。尝试设置不支持当前选择的 OUTPUT 格式的 CAPTURE 格式将导致解码器将请求的 CAPTURE 格式调整为支持的格式。

  4. OUTPUT 队列上枚举格式始终返回完整的支持编码格式集,而与当前的 CAPTURE 格式无关。

  5. 当在任何 OUTPUTCAPTURE 队列上分配缓冲区时,客户端不得更改 OUTPUT 队列上的格式。对于任何此类格式更改尝试,驱动程序将返回 -EBUSY 错误代码。

总而言之,设置格式和分配必须始终从 OUTPUT 队列开始,并且 OUTPUT 队列是管理 CAPTURE 队列的支持格式集的主队列。