Video stream (RTSP/RTMP/HLS) (Beta)
The video stream connector continuously reads video streams such as RTSP, RTMP, and HTTP/HLS by using FFmpeg, converts live video into JPEG images or MP4 video clips at fixed intervals, and outputs them as streaming records. This topic describes how to use the video stream connector in Flink SQL and data ingestion YAML.
Overview
The following table lists the capabilities of the video stream connector.
|
Category |
Details |
|
Supported type |
Source table |
|
Execution mode |
Streaming mode only |
|
Changelog mode |
INSERT only |
|
Data format |
Fixed schema |
|
API types |
Flink SQL and data ingestion YAML |
|
Output |
JPEG images or MP4 video clips |
To use the video stream connector, you must upgrade to Realtime Compute engine VVR 11.9 (including preview versions) or later.
Features
-
Two output modes:
KEYFRAME_EXTRACTextracts JPEG images at a fixed interval, andMOVIE_CLIPsplits the stream by duration and outputs MP4 video clips. -
Parallel reading of multiple video streams: you can configure a static URL list. Each URL corresponds to one source split, and source splits can be assigned to different source readers for parallel processing.
-
Multi-protocol access: protocol parsing is handled by FFmpeg, so you can access video sources that FFmpeg supports, such as RTSP, RTMP, and HTTP/HLS.
-
FFmpeg parameter pass-through: you can use
ffmpeg.properties.andffmpeg-video.properties.to configure FFmpeg parameters such as the transport protocol, timeout, and decoding threads. -
URL-based routing: data ingestion YAML can extract a table ID from each video URL by using a regular expression and route different video streams to different source tables.
-
Backpressure buffering: each source reader uses a bounded queue to buffer generated media records. When downstream is congested, the number of pending records in memory is limited.
Prerequisites
Before you use the video stream connector, make sure that the following requirements are met.
|
Configurations |
Requirement |
|
Video source |
A continuously available video stream URL is prepared, and the video stream contains a video track. |
|
Network connectivity |
The network of Flink TaskManagers can access the video stream URL and its associated media URLs. |
|
Protocol and codec |
The video stream uses a protocol, container format, and codec that FFmpeg supports. |
|
Access credentials (optional) |
If the video source requires authentication, an authenticated URL or protocol parameters that FFmpeg can use are prepared. |
Limits
-
Only source tables are supported. Dimension tables and result tables are not supported.
-
Only unbounded streaming reads are supported. Snapshots, batch reads, and historical backtracking are not supported.
-
The video URL list is determined when the job starts. Automatic discovery, addition, or removal of video sources at runtime is not supported.
SQL
Syntax
CREATE TEMPORARY TABLE video_stream_source (
video_source STRING NOT NULL,
start_ts BIGINT NOT NULL,
end_ts BIGINT NOT NULL,
`blob` BYTES NOT NULL,
mime_type STRING NOT NULL
) WITH (
'connector' = 'video-stream',
'video-stream.url-list' = '<videoStreamUrl>',
'mode' = 'KEYFRAME_EXTRACT',
'sample-interval' = '5s'
);
The DDL must declare all five columns of the fixed schema in the order shown above. The NULL constraints of the columns do not affect validation, but we recommend that you declare all columns as NOT NULL to match the connector output.
WITH parameters
The following table lists the WITH parameters of the video stream connector in Flink SQL. Set connector to video-stream.
|
Parameter |
Description |
Type |
Required |
Default value |
Remarks |
|
|
The connector type. |
STRING |
Yes |
None |
Set the value to |
|
|
A static list of video stream URLs. |
STRING |
Yes |
None |
URLs are separated by commas (,) by default. The list must contain at least one non-empty, unique URL. |
|
|
The separator of the URL list. |
STRING |
No |
|
The separator is parsed as a plain string, not a regular expression. It cannot be empty. |
|
|
The media output mode. |
STRING |
Yes |
None |
Valid values: |
|
|
The frame extraction interval or the target duration of video clips. |
DURATION |
Yes |
None |
The value must be greater than or equal to 1 millisecond, for example, |
|
|
The maximum number of generated media records that each source reader can buffer. |
INTEGER |
No |
|
The value must be a positive integer. A larger value better absorbs data jitter but consumes more memory. |
|
|
General input parameters passed to the FFmpeg grabber. |
STRING |
No |
None |
Parameters with the prefix removed are forwarded to FFmpeg. For example, if you set Note
For all configurable options, see FFmpeg Formats and FFmpeg Protocols. |
|
|
Video input parameters passed to the FFmpeg grabber. |
STRING |
No |
None |
Parameters with the prefix removed are forwarded to the FFmpeg video codec. For example, if you set Note
For all configurable options, see FFmpeg Codecs. |
Metadata field mapping
The video stream connector uses a fixed schema. The SQL DDL must declare all of the following columns in the same order.
|
Field |
Flink SQL type |
Nullable |
Description |
|
|
STRING |
No |
The video stream URL that produced the current record. It matches the URL in the configuration. |
|
|
BIGINT |
No |
The start time of the image or video clip. The value is a UNIX timestamp in milliseconds. The time of the first frame is the system time when the connector starts receiving frames from the video stream. Subsequent times increase along the media timeline. |
|
|
BIGINT |
No |
The end time of the image or video clip. The value is a UNIX timestamp in milliseconds. For image records, the value is the same as |
|
|
BYTES |
No |
The binary content of the JPEG image or MP4 video clip. |
|
|
STRING |
No |
The media type. The value is |
Examples
Example 1: Read an RTSP video and extract JPEG images
The following example extracts one frame every 5 seconds and connects to the RTSP service over TCP.
CREATE TEMPORARY TABLE video_stream_source (
video_source STRING NOT NULL,
start_ts BIGINT NOT NULL,
end_ts BIGINT NOT NULL,
`blob` BYTES NOT NULL,
mime_type STRING NOT NULL
) WITH (
'connector' = 'video-stream',
'video-stream.url-list' = 'rtsp://<host>:<port>/<path>',
'mode' = 'KEYFRAME_EXTRACT',
'sample-interval' = '5s',
-- The following parameters are passed through directly to the FFmpeg library
'ffmpeg.properties.rtsp_transport' = 'tcp',
'ffmpeg-video.properties.threads' = '1'
);
INSERT INTO paimon_catalog.default.video_stream_table
SELECT video_source, start_ts, end_ts, `blob`, mime_type
FROM video_stream_source;
Example 2: Read an RTMP video and generate MP4 clips
The following example splits the live stream into MP4 clips with a target duration of 10 seconds.
CREATE TEMPORARY TABLE video_clip_source (
video_source STRING NOT NULL,
start_ts BIGINT NOT NULL,
end_ts BIGINT NOT NULL,
`blob` BYTES NOT NULL,
mime_type STRING NOT NULL
) WITH (
'connector' = 'video-stream',
'video-stream.url-list' = 'rtmp://<host>:<port>/<app>/<stream>',
'mode' = 'MOVIE_CLIP',
'sample-interval' = '10s',
'record-queue-capacity' = '4'
);
Example 3: Read multiple video streams
The following example uses | to separate two URLs. We recommend this format when the query parameters of a URL contain commas.
CREATE TEMPORARY TABLE multi_video_source (
video_source STRING NOT NULL,
start_ts BIGINT NOT NULL,
end_ts BIGINT NOT NULL,
`blob` BYTES NOT NULL,
mime_type STRING NOT NULL
) WITH (
'connector' = 'video-stream',
'video-stream.url-list' = 'https://<host>/live.m3u8?tracks=video,audio|rtsp://<host>/<camera>',
'video-stream.url-list-separator' = '|',
'mode' = 'KEYFRAME_EXTRACT',
'sample-interval' = '3s'
);
Example 4: Write video content to a Paimon Blob column
The following example writes the media binaries output by the video stream connector to a Paimon Blob column. A Paimon Blob table is an append-only table. Do not define a primary key.
CREATE TABLE my_catalog.my_db.video_archive (
video_source STRING,
start_ts BIGINT,
end_ts BIGINT,
`blob` BYTES,
mime_type STRING
) WITH (
'blob-field' = 'blob',
'row-tracking.enabled' = 'true',
'data-evolution.enabled' = 'true'
);
INSERT INTO paimon_catalog.default.video_stream_table
SELECT video_source, start_ts, end_ts, `blob`, mime_type
FROM video_stream_source;
Data ingestion
Syntax
source:
type: video-stream
name: Video Stream Source
video-stream.url-list: rtsp://<host>:<port>/<path>
mode: KEYFRAME_EXTRACT
sample-interval: 5s
transform:
- source-table: default
projection: video_source, start_ts, end_ts, blob, mime_type
table-options: blob-field=blob;row-tracking.enabled=true;data-evolution.enabled=true
route:
- source-table: default
sink-table: <sinkDatabase>.<sinkTable>
sink:
type: paimon
name: Paimon Sink
catalog.properties.metastore: rest
catalog.properties.uri: <catalogEndpoint>
catalog.properties.warehouse: <warehouseName>
If url.to.table-id.matching.pattern is not configured, all video streams are written to a source table named default.
Configurations
The following table lists the configurations of the video stream connector in data ingestion YAML. Set type to video-stream.
|
Parameter |
Description |
Type |
Required |
Default value |
Remarks |
|
|
The connector type. |
STRING |
Yes |
None |
Set the value to |
|
|
The name of the source. |
STRING |
No |
None |
Used to identify the current source. |
|
|
A static list of video stream URLs. |
STRING |
Yes |
None |
URLs are separated by commas (,) by default. The list must contain at least one non-empty, unique URL. |
|
|
The separator of the URL list. |
STRING |
No |
|
The separator is parsed as a plain string, not a regular expression. It cannot be empty. |
|
|
The media output mode. |
STRING |
Yes |
None |
Valid values: |
|
|
The frame extraction interval or the target duration of video clips. |
DURATION |
Yes |
None |
The value must be greater than or equal to 1 millisecond. |
|
|
The maximum number of generated media records that each source reader can buffer. |
INTEGER |
No |
|
The value must be a positive integer. |
|
|
A regular expression that extracts a table ID from each full video URL. |
STRING |
No |
None |
The regular expression must contain 1 to 3 capturing groups and match the full URL. Unmatched URLs are written to the |
|
|
General input parameters passed to the FFmpeg grabber. |
STRING |
No |
None |
The name with the prefix removed is used as an FFmpeg parameter name. |
|
|
Video input parameters passed to the FFmpeg grabber. |
STRING |
No |
None |
The name with the prefix removed is used as an FFmpeg video parameter name. |
URL to Table ID mapping
url.to.table-id.matching.pattern matches the full URL by using a Java regular expression and generates a table ID based on the number of capturing groups. For example, if the video URL is rtmp://server/video/shop001/camera_01, you can use the following configuration to map it to shop001.camera_01:
url.to.table-id.matching.pattern: ^rtmp://[^/]+/video/([^/]+)/([^/?]+)$
The regular expression must contain exactly 1 to 3 capturing groups, and the captured results cannot be empty. If no regular expression is configured or a URL does not match, the connector uses default as the table ID.
Examples
Example 1: Write a single video stream to Paimon
The following example extracts one JPEG image every 5 seconds and stores the image content in a Paimon Blob column.
source:
type: video-stream
name: Camera Source
video-stream.url-list: rtsp://<host>:<port>/<camera>
mode: KEYFRAME_EXTRACT
sample-interval: 5s
record-queue-capacity: 8
ffmpeg.properties.rtsp_transport: tcp
ffmpeg.properties.timeout: 10000000
transform:
- source-table: default
projection: video_source, start_ts, end_ts, blob, mime_type
table-options: blob-field=blob;row-tracking.enabled=true;data-evolution.enabled=true
route:
- source-table: default
sink-table: video_db.camera_frames
sink:
type: paimon
name: Paimon Sink
catalog.properties.metastore: rest
catalog.properties.uri: <catalogEndpoint>
catalog.properties.warehouse: <warehouseName>
pipeline:
name: Camera Frames Pipeline
parallelism: 1
Example 2: Map multiple video streams to different tables by URL
The following example maps two RTMP video streams to shop001.camera_01 and shop002.camera_02 respectively. You can then use the table matching rules supported by the pipeline in the route section to route these source tables to the corresponding sink tables.
source:
type: video-stream
name: Multi-camera Source
video-stream.url-list: rtmp://server/video/shop001/camera_01,rtmp://server/video/shop002/camera_02
mode: MOVIE_CLIP
sample-interval: 10s
url.to.table-id.matching.pattern: ^rtmp://[^/]+/video/([^/]+)/([^/?]+)$
route:
- source-table: shop001.camera_01
sink-table: video_db.shop001_camera_01
- source-table: shop002.camera_02
sink-table: video_db.shop002_camera_02
sink:
type: paimon
name: Paimon Sink
catalog.properties.metastore: rest
catalog.properties.uri: <catalogEndpoint>
catalog.properties.warehouse: <warehouseName>
pipeline:
name: Multi-camera Clips Pipeline
parallelism: 2
FAQ
No data is output after the job starts
Check the following items in order:
-
Whether the TaskManager can access the video URL.
-
Whether FFmpeg supports the protocol, container format, and codec.
-
Whether the video stream contains a decodable video track.
-
Whether
sample-intervalis set too large. -
Whether the RTSP service requires
ffmpeg.properties.rtsp_transport: tcp.
Why does the job not resume reading from the interruption point after a recovery?
Live streams have no stable, recoverable media position. A checkpoint can only restore the assignment of URL splits. Frames during a failure may not be captured, and duplicate records may be generated after reconnection. Therefore, the video stream connector does not support resuming from the interruption point and does not provide end-to-end exactly-once guarantees.
How do I reduce CPU and memory consumption?
-
Increase
sample-intervalto reduce the number of images or the video clipping frequency. -
Use
KEYFRAME_EXTRACTwhen only single-frame analysis is required. -
Decrease
record-queue-capacityto limit the number of buffered media records. -
Distribute multiple URLs across an appropriate source parallelism so that a single source reader does not handle too many video streams at the same time.
-
We recommend that you set
ffmpeg-video.properties.threadsto 1 to reduce CPU usage.