视频函数

更新时间:
复制 MD 格式

本文为您介绍 Python 多模态函数库中视频函数的使用方法。通过内置 python 模块,您可以在 Flink SQL 作业中读取视频元数据、生成视频片段引用,以及按全部帧、关键帧或固定时间间隔抽取视频帧。

使用限制

仅实时计算引擎 VVR 11.8 及以上版本支持。

使用方法

使用说明

在平台 SQL 作业中,视频函数由内置 python 模块提供,无需在作业中引入额外依赖。使用由 python 模块提供的函数前,需要先加载该模块,再启用 python 和 core 模块:

LOAD MODULE python;
USE MODULES python, core;

基本调用

LOAD MODULE python;
USE MODULES python, core;

-- 读取视频元数据。
SELECT id, VIDEO_METADATA(uri, 'null') AS metadata
FROM videos;

-- 每隔 1 秒抽取一帧,并展开为一帧一行。
SELECT v.id, f.frame, f.metadata
FROM videos AS v
CROSS JOIN LATERAL TABLE(
  VIDEO_EXPLODE_FRAMES(v.uri, 'sample', 1000)
) AS f(frame, metadata);

参数说明

SQL 函数按签名中的顺序传参,只支持位置参数。函数签名中的 [ ] 表示可选参数。例如 VIDEO_METADATA 的签名如下:

VIDEO_METADATA(
  uri
  [, on_error
  [, read_chunk_size
  [, max_cached_blocks
  [, read_ahead_blocks]]]]
)

调用时遵循以下规则:

  • 省略的可选参数会使用参数默认值进行填充。

  • 如果需要显式设置靠后的可选参数,前面的可选参数也需要明确写出。

  • NULL 会作为参数值传入,不等同于省略参数时的默认值。多态数据参数需要使用 NULL 时,可以通过 CAST(NULL AS <精确类型>) 明确类型。

  • 除 video_input、VIDEO_METADATA 的 uri 以及 VIDEO_SPLIT 的 metadata 外,其他参数都是配置参数,必须使用 SQL 字面量(包括 NULL),不能使用表字段或 CAST 表达式。

  • VIDEO_SPLIT 和 VIDEO_EXPLODE_FRAMES 是表值函数(UDTF),需要通过 LATERAL TABLE 调用。

数据类型说明

视频数据形态

数据形态

SQL 类型

说明

视频 URI

STRING

Flink 文件系统可访问的视频 URI。

视频片段引用

ROW<uri STRING, start_time_ms BIGINT, end_time_ms BIGINT>

指向原视频中的一个时间片段,不复制视频内容。

VIDEO_METADATA 只接受视频 URI,其他三个视频函数同时接受 URI 和视频片段引用。读取视频时只选择第一个视频流。

视频片段引用

ROW<uri STRING, start_time_ms BIGINT, end_time_ms BIGINT>

字段

类型

说明

uri

STRING

原始视频文件路径或对象存储 URI。

start_time_ms

BIGINT

片段的开始时间,单位毫秒;NULL表示未指定起点。

end_time_ms

BIGINT

片段的结束时间,单位毫秒;NULL表示未指定终点。

VIDEO_SPLIT 的返回数据结构。视频片段采用闭区间 [start_time_ms, end_time_ms]。例如 [0, 999] 后一个相邻片段从 1000 开始。

视频元数据

VIDEO_METADATA 返回以下结构:

ROW<
  width INT,
  height INT,
  fps DOUBLE,
  duration_ms BIGINT,
  frame_count BIGINT,
  time_base DOUBLE,
  codec_name STRING,
  video_stream_index INT
>

字段

类型

说明

width

INT

视频宽度,单位像素。

height

INT

视频高度,单位像素。

fps

DOUBLE

视频帧率。

duration_ms

BIGINT

视频时长,单位毫秒。

frame_count

BIGINT

视频帧数。

time_base

DOUBLE

视频流时间基准,用于计算帧时间。

codec_name

STRING

视频编码格式名称,例如 h264、hevc。

video_stream_index

INT

选中的视频流编号。

底层视频流或容器未提供对应信息时,元数据字段可能为 NULL。

视频帧元数据

VIDEO_EXPLODE_FRAMES 为每个输出帧返回以下元数据:

ROW<
  uri STRING,
  video_stream_index INT,
  frame_index BIGINT,
  pts BIGINT,
  time_ms BIGINT,
  key_frame BOOLEAN,
  start_time_ms BIGINT,
  end_time_ms BIGINT
>

字段

类型

说明

uri

STRING

原始视频文件路径或对象存储 URI。

video_stream_index

INT

输出帧所属的视频流编号。

frame_index

BIGINT

输出帧在当前抽帧结果中的序号,从 0 开始。

pts

BIGINT

视频文件中的原始帧时间戳,通常用于和视频处理工具对齐。无法确定时为 NULL。

time_ms

BIGINT

帧对应的视频时间,单位毫秒。无法确定时为 NULL。

key_frame

BOOLEAN

是否为关键帧。

start_time_ms

BIGINT

当前抽帧输入片段的开始时间,单位毫秒;输入为完整 URI 时为 NULL。

end_time_ms

BIGINT

当前抽帧输入片段的结束时间,单位毫秒;输入为完整 URI 时为 NULL。

视频元数据

VIDEO_METADATA

读取视频基本信息,例如分辨率、帧率、时长和编码格式。该函数只读取容器和视频流元数据,不解码视频帧。

VIDEO_METADATA(
  uri
  [, on_error
  [, read_chunk_size
  [, max_cached_blocks
  [, read_ahead_blocks]]]]
)

参数

类型

默认值

说明

uri

STRING

必填

视频 URI。

on_error

STRING NOT NULL

'raise'

错误处理策略:'raise' 抛出异常;'null' 返回 NULL。

read_chunk_size

INT

NULL

单次从 Java 文件系统桥读取的字节数,必须大于 0,且不能超过 16 MiB。NULL 使用运行时默认值。

max_cached_blocks

INT

NULL

每个文件最多缓存的块数,必须大于或等于 0;0 表示关闭块缓存。NULL 使用运行时默认值。

read_ahead_blocks

INT

NULL

缓存未命中后预读的块数,必须大于或等于 0;0 表示关闭预读。NULL 使用运行时默认值。

返回类型: 视频元数据 ROW。输入 URI 为 NULL 时返回 NULL。

WITH source_with_metadata AS (
  SELECT id, VIDEO_METADATA(uri, 'null') AS metadata
  FROM videos
)
SELECT id, metadata.width AS width, metadata.duration_ms AS duration_ms
FROM source_with_metadata;

视频切分与抽帧

VIDEO_EXTRACT_FRAMES

从完整视频或视频片段中抽帧,并把同一个输入视频的结果收集到同一行的数组中。若视频较长或抽帧数量较多,推荐使用 VIDEO_EXPLODE_FRAMES,避免单行持有过多图像。

VIDEO_EXTRACT_FRAMES(
  video_input
  [, frame_selector
  [, sample_interval_ms
  [, max_frames
  [, image_height, image_width
  [, on_error
  [, read_chunk_size
  [, max_cached_blocks
  [, read_ahead_blocks]]]]]]]]]
)

参数

类型

默认值

说明

video_input

STRING 或视频片段引用 ROW

必填

视频 URI 或 VIDEO_SPLIT 返回的视频片段引用。

frame_selector

STRING NOT NULL

'all_frames'

抽帧策略,支持 'all_frames'、'keyframe'、'sample'。

sample_interval_ms

INT

NULL

当 frame_selector='sample' 时的采样间隔,单位毫秒,必须大于 0;此时 NULL 表示 1000 毫秒。

max_frames

INT

NULL

保留在同一输出行中的帧数安全上限,必须大于 0;NULL 表示保留全部选中的帧。

image_height

INT

NULL

输出图像高度,单位为像素,必须与 image_width 同时设置且大于 0。

image_width

INT

NULL

输出图像宽度,单位为像素,必须与 image_height 同时设置且大于 0。

on_error

STRING NOT NULL

'raise'

错误处理策略:'raise' 抛出异常;'null' 返回 NULL。

read_chunk_size

INT

NULL

单次从 Java 文件系统桥读取的字节数,必须大于 0,且不能超过 16 MiB。NULL 使用运行时默认值。

max_cached_blocks

INT

NULL

每个文件最多缓存的块数,必须大于或等于 0;0 表示关闭块缓存。NULL 使用运行时默认值。

read_ahead_blocks

INT

NULL

缓存未命中后预读的块数,必须大于或等于 0;0 表示关闭预读。NULL 使用运行时默认值。

返回类型: ARRAY<IMAGE>。

  • image_height 和 image_width 指定的是精确输出尺寸,不保持原始宽高比。

  • 输入为视频片段引用时,使用闭区间 [start_time_ms, end_time_ms] 筛选能够确定 time_ms 的帧。'all_frames' 和 'keyframe' 策略下,无法确定 time_ms 的帧仍可能输出;'sample' 策略会跳过这类帧。

  • frame_selector='sample' 从片段起点开始建立采样时间点,输出时间不早于各采样点的第一帧;无法确定 time_ms 的帧不会作为采样结果。

  • 输入为 NULL 或在 'null' 策略下处理失败时返回 NULL;处理成功但没有选中帧时返回空数组。

-- 每隔 1 秒抽一帧,每个视频最多返回 10 帧,并缩放为 224 × 224。
SELECT VIDEO_EXTRACT_FRAMES(
  uri, 'sample', 1000, 10, 224, 224, 'null'
) AS frames
FROM videos;

VIDEO_SPLIT

将完整视频或已有视频片段切分为多个片段引用,展开为一行一个片段引用。该函数本身不复制视频内容,也不读取视频帧,只计算时间窗口。

VIDEO_SPLIT(
  video_input
  [, metadata
  [, segment_duration_ms
  [, num_segments
  [, video_duration_ms
  [, max_segments
  [, on_error
  [, read_chunk_size
  [, max_cached_blocks
  [, read_ahead_blocks]]]]]]]]]
)

参数

类型

默认值

说明

video_input

STRING 或视频片段引用 ROW

必填

视频 URI 或已有视频片段引用。NULL 不输出任何行。

metadata

视频元数据 ROW 或 BIGINT

NULL

包含有效 duration_ms 的视频元数据,或直接给出的毫秒时长。提供有效时长可避免再次探测视频;该参数可以是表字段。

segment_duration_ms

INT

NULL

固定片段长度,单位毫秒,必须大于 0。与 num_segments 必须恰好设置一个。

num_segments

INT

NULL

切分成近似等长的片段数量,必须大于 0。与 segment_duration_ms 必须恰好设置一个。

video_duration_ms

INT

NULL

显式视频时长,单位毫秒,必须大于 0;在输入没有结束时间且 metadata 没有有效时长时作为后备值。

max_segments

INT NOT NULL

1024

单行最多输出的片段数,必须大于 0。达到限制后停止输出剩余片段。

on_error

STRING NOT NULL

'raise'

错误处理策略:'raise' 抛出异常;'skip' 不输出当前输入的片段。

read_chunk_size

INT

NULL

单次从 Java 文件系统桥读取的字节数,必须大于 0,且不能超过 16 MiB。NULL 使用运行时默认值。

max_cached_blocks

INT

NULL

每个文件最多缓存的块数,必须大于或等于 0;0 表示关闭块缓存。NULL 使用运行时默认值。

read_ahead_blocks

INT

NULL

缓存未命中后预读的块数,必须大于或等于 0;0 表示关闭预读。NULL 使用运行时默认值。

返回列: segment ROW<uri STRING, start_time_ms BIGINT, end_time_ms BIGINT>,一行一个片段。

read_chunk_size、max_cached_blocks 和 read_ahead_blocks 仅在函数自行探测元数据时使用。

  • 输入引用已经包含 end_time_ms 时,直接使用该片段边界。

  • 输入没有 end_time_ms 时,依次使用 metadata 中的时长和 video_duration_ms;二者都没有有效值时,函数会探测视频时长。

  • 按固定时长切分时,每段是闭区间。例如 2 秒视频按 1000 毫秒切分为 [0, 999] 和 [1000, 1999]。

  • 按片段数切分时,函数使用向上取整的片段时长,因此实际片段数可能少于 num_segments。

-- metadata 位置使用有类型的 NULL 占位,按 1 秒切分。
SELECT v.id, s.segment
FROM videos AS v
CROSS JOIN LATERAL TABLE(
  VIDEO_SPLIT(v.uri, CAST(NULL AS BIGINT), 1000)
) AS s(segment);

-- 复用已经读取的元数据,按目标数量切分。
WITH source_with_metadata AS (
  SELECT id, uri, VIDEO_METADATA(uri) AS metadata
  FROM videos
)
SELECT v.id, s.segment
FROM source_with_metadata AS v
CROSS JOIN LATERAL TABLE(
  VIDEO_SPLIT(
    v.uri,
    v.metadata,
    NULL,
    4
  )
) AS s(segment);

VIDEO_EXPLODE_FRAMES

从完整视频或视频片段中抽帧,并将结果展开为一帧一行。

VIDEO_EXPLODE_FRAMES(
  video_input
  [, frame_selector
  [, sample_interval_ms
  [, max_frames
  [, image_height, image_width
  [, on_error
  [, read_chunk_size
  [, max_cached_blocks
  [, read_ahead_blocks]]]]]]]]]
)

参数

类型

默认值

说明

video_input

STRING 或视频片段引用 ROW

必填

视频 URI 或 VIDEO_SPLIT 返回的视频片段引用。NULL 不输出任何行。

frame_selector

STRING NOT NULL

'all_frames'

抽帧策略,支持 'all_frames'、'keyframe'、'sample'。

sample_interval_ms

INT

NULL

当 frame_selector='sample' 时的采样间隔,单位毫秒,必须大于 0;此时 NULL 表示 1000 毫秒。

max_frames

INT

NULL

每个输入最多输出的帧数,必须大于 0;NULL 表示输出全部选中的帧。

image_height

INT

NULL

输出帧高度,单位像素,必须与 image_width 同时设置且大于 0。

image_width

INT

NULL

输出帧宽度,单位像素,必须与 image_height 同时设置且大于 0。

on_error

STRING NOT NULL

'raise'

错误处理策略:'raise' 抛出异常;'skip' 不输出失败输入的行。

read_chunk_size

INT

NULL

单次从 Java 文件系统桥读取的字节数,必须大于 0,且不能超过 16 MiB。NULL 使用运行时默认值。

max_cached_blocks

INT

NULL

每个文件最多缓存的块数,必须大于或等于 0;0 表示关闭块缓存。NULL 使用运行时默认值。

read_ahead_blocks

INT

NULL

缓存未命中后预读的块数,必须大于或等于 0;0 表示关闭预读。NULL 使用运行时默认值。

返回列: frame IMAGE, metadata ROW<...>,一行一个视频帧。

选帧、缩放和片段边界规则与 VIDEO_EXTRACT_FRAMES 相同。使用 on_error='skip' 时,如果解码在第一帧输出前失败,则不输出任何行;如果输出部分帧后发生错误,已经输出的行会保留。

SELECT v.id, f.frame, f.metadata.time_ms AS frame_time_ms
FROM videos AS v
CROSS JOIN LATERAL TABLE(
  VIDEO_EXPLODE_FRAMES(
    v.uri, 'sample', 1000, 10, 224, 224, 'skip'
  )
) AS f(frame, metadata);

完整 Pipeline 示例

以下示例先读取视频元数据,再把视频按 1 秒切分,最后从每个片段抽取一帧:

LOAD MODULE python;
USE MODULES python, core;

-- 步骤 1:读取每个视频 URI,并提取对应的视频元数据。
WITH source_with_metadata AS (
  SELECT
    id,
    uri,
    VIDEO_METADATA(uri, 'null') AS metadata
  FROM videos
), 

-- 步骤 2:结合视频 URI 和元数据,将每个视频拆分为多个视频片段。
video_segments AS (
  SELECT
    v.id,
    s.segment
  FROM source_with_metadata AS v
  CROSS JOIN LATERAL TABLE(
    VIDEO_SPLIT(v.uri, v.metadata, 1000, NULL, NULL, 1024, 'skip')
  ) AS s(segment)
)

-- 步骤 3:以采样模式从每个视频片段中提取视频帧,并输出帧时间及其所属片段的起止时间。
SELECT
  s.id,
  f.frame,
  f.metadata.time_ms AS frame_time_ms,
  f.metadata.start_time_ms AS segment_start_ms,
  f.metadata.end_time_ms AS segment_end_ms
FROM video_segments AS s
CROSS JOIN LATERAL TABLE(
  VIDEO_EXPLODE_FRAMES(s.segment, 'sample', 1000, 1)
) AS f(frame, metadata);