DeepStream9.0 Smart Recording

在视频分析系统里,"录制"看起来很简单:把摄像头流保存成文件就行。但在真实项目中,持续录制往往意味着巨大的存储成本、检索成本和网络压力。

很多场景真正需要的不是全天录像,而是"事件发生前后的一小段视频":比如车辆闯入、人员聚集、异常行为、告警触发前后的 10 秒。

NVIDIA DeepStream 的 Smart Video Record,也就是 Smart Recording,就是为这种场景设计的。根据官方文档,它支持基于本地事件或云端事件录制关键片段,而不是持续保存完整视频流。录制过程与推理 pipeline 并行运行,并且内部维护一段音视频缓存,因此最终文件可以包含事件发生前和发生后的画面。参考:NVIDIA Smart Video Record 文档

Smart Recording 解决什么问题

传统录像方式通常有两种。

一种是全天候录制。优点是简单,缺点是成本高,后期检索困难。

另一种是事件发生后才开始录制。优点是节省存储,缺点是经常错过事件发生前的关键上下文。

Smart Recording 采用的是第三种方式:应用一直缓存最近一段已经编码好的音视频数据,但只有在事件触发时才把缓存片段封装成文件。

假设事件在 t1 时刻发生,应用调用录制 API 时传入:

startTime = 5

duration = 10

那么录制结果大致覆盖:

t1 - 5s 到 t1 + 10s

也就是事件前 5 秒和事件后 10 秒。

这对于告警系统非常重要。因为很多事件的判断依据并不只在告警瞬间,而是在告警前几秒已经开始出现。

它为什么缓存 encoded frame

Smart Recording 缓存的是编码后的帧,而不是解码后的 raw frame。

这点很关键。RTSP 摄像头通常输出 H.264/H.265 encoded stream,如果缓存 raw frame,内存压力会非常大;而缓存 encoded bitstream 则轻得多,也更适合直接封装成 MP4 或 MKV。

但这也带来一个限制:录制文件必须从 I-frame 开始。如果缓存最前面的帧不是 I-frame,Smart Recording 会丢弃前面一部分帧,直到遇到关键帧。因此实际生成的视频时长可能比配置的 startTime + duration 略短。

这不是 bug,而是视频编码格式本身决定的。

Smart Recording 的核心 API

底层 API 定义在 gst-nvdssr.h 中,核心函数只有四个:

NvDsSRCreate(...)

NvDsSRStart(...)

NvDsSRStop(...)

NvDsSRDestroy(...)

NvDsSRCreate() 用来创建 Smart Record 上下文。它会生成一个 recordbin,应用必须把这个 bin 加入 GStreamer pipeline。这个 recordbin 接收 encoded frame,并负责缓存、封装和写文件。

NvDsSRStart() 用来启动录制。调用时传入 startTime 和 duration,它会返回一个 session id。当前实现中,同一路 source 不支持重叠录制。

NvDsSRStop() 用来停止录制。如果应用不主动 stop,Smart Recording 也可以根据 defaultDuration 自动停止。

NvDsSRDestroy() 用来释放资源,通常在 pipeline 退出时调用。

录制完成后,callback 会收到 NvDsSRRecordingInfo,里面包含文件名、目录、时长、分辨率、容器类型,以及是否包含音频和视频等信息。

deepstream-testsr:最直接的学习入口

deepstream-testsr 是理解 Smart Recording 最好的 sample。

它支持单路 H.264 RTSP 输入,演示如何通过 NvDsSRStart() 和 NvDsSRStop() 自定义触发录制。示例里有几个默认参数:

#define SMART_REC_CONTAINER 0

#define CACHE_SIZE_SEC 15

#define SMART_REC_DEFAULT_DURATION 10

#define START_TIME 2

#define SMART_REC_DURATION 7

#define SMART_REC_INTERVAL 7

含义大致是:

缓存 15 秒

触发录制时保存事件前 2 秒

事件后继续录制 7 秒

每 7 秒触发一次 start/stop 演示事件

容器默认 MP4

deepstream-testsr 还有一个很实用的能力:可以选择录制带 bbox 的视频,或者录制原始输入流。

如果 bbox-enable=0,recordbin 接在解码前的 encoded stream 上,录制的是更接近原始 RTSP 输入的内容。

如果 bbox-enable=1,sample 会在 OSD 后面增加 encoder 和 parser,再接入 recordbin。这样录制出来的文件就包含检测框。但 README 也提醒:带 bbox 的录制目前只支持视频。

它还支持 sr-mode:

0: Audio + Video

1: Video only

2: Audio only

音频接入方式是把 encoded audio bitstream 链到 recordbin 的 asink pad;视频则链到 sink pad。

deepstream-app:生产场景里的 Smart Recording

相比 deepstream-testsr,deepstream-app 更接近真实业务。它支持多路 source、推理、跟踪、消息消费和 Smart Recording。

Smart Recording 主要通过 [sourceX] 配置启用:

source0

enable=1

type=4

uri=rtsp://...

smart-record=2

smart-rec-cache=30

smart-rec-start-time=5

smart-rec-duration=10

smart-rec-default-duration=10

smart-rec-container=0

smart-rec-interval=10

smart-rec-file-prefix=Camera_0

smart-rec-dir-path=./records

几个关键配置项如下:

smart-record=1 表示只响应云端消息触发录制。

smart-record=2 表示既响应云端消息,也启用本地事件触发。官方文档里提到,默认本地事件会每 10 秒触发一次 start/stop,主要用于演示。

smart-rec-cache 是缓存时长。它必须大于 smart-rec-start-time,否则你想录事件前 5 秒,但缓存里没有足够历史数据。

smart-rec-container=0 表示 MP4,1 表示 MKV。

smart-rec-dir-path 是保存目录,目录必须存在且有写权限。

在 deepstream_source_bin.c 中,RTSP source 创建时会根据配置创建 NvDsSRContext,把 recordbin 加进 source bin,并从 RTSP pre-decode tee 分一路 encoded stream 给 Smart Recording。这样录制过程不会干扰主推理 pipeline。

云端触发:通过 Kafka 控制录制

Smart Recording 也可以由云端消息触发。官方文档和 deepstream-app 都展示了这条路径。

应用从 Kafka 等消息系统接收 JSON,解析成 start/stop 命令,然后找到对应 sensor/source 的 NvDsSRContext,调用 NvDsSRStart() 或 NvDsSRStop()。

消息大致长这样:

{

"command": "start-recording",

"start": "2020-05-18T20:02:00.051Z",

"end": "2020-05-18T20:02:02.851Z",

"sensor": {

"id": "0"

}

}

这非常适合边缘 AI + 云端控制架构:边缘设备持续分析视频,云端告警系统或业务系统决定什么时候保存关键片段。

gst-nvdssr 关键源码逻辑分析

Smart Recording 的核心实现位于:

sources/libs/gst-nvdssr/gst-nvdssr.c

从源码看,它的设计可以概括为:

常驻缓存 + 事件放行 + 关键帧门控 + EOS 收尾

1. recordbin 的内部结构

NvDsSRCreate() 会创建一个 recordbin。这个 bin 内部有视频缓存路径和音频缓存路径。

视频入口 ghost pad 叫 sink,音频入口 ghost pad 叫 asink。

内部结构大致是:

encoded video -> cache queue -> tee -> encodebin sink

encoded audio -> cache queue -> tee -> encodebin asink

encodebin -> muxer -> filesink

这里的 encodebin 名字有点容易误解。它并不是负责重新编码,而是包含 queue、muxer 和 filesink,用来把 encoded bitstream 封装成文件。

源码里根据容器类型选择 muxer:

case NVDSSR_CONTAINER_MP4:

muxer = gst_element_factory_make ("qtmux", elem_name);

break;

case NVDSSR_CONTAINER_MKV:

muxer = gst_element_factory_make ("matroskamux", elem_name);

break;

因此 Smart Recording 支持 MP4 和 MKV。

2. cache queue 只按时间缓存

create_cache_queue() 是缓存机制的关键。

它关闭了按 buffer 数量和字节大小限制 queue 的行为:

g_object_set (G_OBJECT (queue), "max-size-buffers", 0, NULL);

g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL);

然后用时间控制缓存窗口:

g_object_set (G_OBJECT (queue), "min-threshold-time",

ctx->initParams.cacheSize * 1000 * 1000 * 1000ULL, NULL);

g_object_set (G_OBJECT (queue), "max-size-time",

1000000000ULL * (ctx->initParams.cacheSize + 5), NULL);

并设置 leaky:

g_object_set (G_OBJECT (queue), "leaky", 2, "silent", TRUE, NULL);

这意味着 queue 会始终保留最近一段时间的数据,超过上限就丢弃旧数据。Smart Recording 要的正是这种"滑动时间窗口"。

3. Start 的本质:调整 queue 阈值并打开放行状态

NvDsSRStart() 并不会临时搭建一条录制 pipeline。它做的是修改已有 recordbin 的状态。

首先设置新文件名:

SetNewFileName (ctx);

文件名由目录、前缀、递增 counter、UTC 时间戳和 pid 组成,类似:

Camera_0_00001_20260521-143000_12345.mp4

然后设置状态:

ctx->recordOn = TRUE;

ctx->resetDone = FALSE;

ctx->uData = userData;

接着把 queue 的 max-size-time 调整为 startTime:

g_object_set (G_OBJECT (ctx->recordQue), "max-size-time",

startTime * 1000 * 1000 * 1000ULL, NULL);

g_object_set (G_OBJECT (ctx->recordQue), "min-threshold-time", 0, NULL);

可以把它理解为:事件来了,现在从缓存中释放事件前 startTime 秒的数据,并继续把后续数据写入文件。

duration 则通过定时器控制:

privData->timeoutSrcId = g_timeout_add (timeout, DefaultStopCallback, ctx);

如果应用没有主动调用 NvDsSRStop(),定时器到期后会自动 stop。

4. 关键帧门控:录制为什么必须等 I-frame

视频 queue 的 src pad 上挂了 queue_src_pad_probe()。这段逻辑决定 buffer 是否可以进入文件写入路径:

if (ctx->recordOn && !ctx->gotKeyFrame &&

(!GST_BUFFER_FLAG_IS_SET (GST_BUFFER_CAST(info->data), GST_BUFFER_FLAG_DELTA_UNIT))) {

ctx->gotKeyFrame = TRUE;

}

GST_BUFFER_FLAG_DELTA_UNIT 通常表示非关键帧。也就是说,源码会等待第一个非 delta frame,也就是 I-frame。

只有拿到关键帧后,数据才允许通过:

if (ctx->recordOn && ctx->gotKeyFrame) {

return GST_PAD_PROBE_OK;

}

return GST_PAD_PROBE_DROP;

这解释了一个常见现象:即使配置了事件前 N 秒,实际输出文件也可能短一些。因为文件必须从关键帧开始,关键帧之前的 delta frame 不能单独构成可解码视频。

5. 音频路径更简单

音频的 probe 是 audio_queue_src_pad_probe()。它没有关键帧判断,只根据 recordOn 决定是否放行:

return ctx->recordOn ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP;

这也符合音频编码流的特点:它不需要像视频那样等待 I-frame。

6. Stop 的本质:恢复缓存窗口并发送 EOS

NvDsSRStop() 会把 queue 的缓存阈值恢复成正常 cache size:

g_object_set (G_OBJECT (ctx->recordQue), "min-threshold-time",

ctx->initParams.cacheSize * 1000 * 1000 * 1000ULL, NULL);

g_object_set (G_OBJECT (ctx->recordQue), "max-size-time",

1000000000ULL * (ctx->initParams.cacheSize + 5), NULL);

然后关闭录制状态:

ctx->gotKeyFrame = FALSE;

ctx->recordOn = FALSE;

最后向 encodebin 发送 EOS:

if (privData->haveVideo)

gst_pad_send_event (gst_element_get_static_pad (ctx->encodebin, "sink"),

gst_event_new_eos ());

if (privData->haveAudio)

gst_pad_send_event (gst_element_get_static_pad (ctx->encodebin, "asink"),

gst_event_new_eos ());

EOS 非常关键。MP4/MKV muxer 需要 EOS 才能正确写完容器尾部信息,否则文件可能无法正常播放。

随后 bus handler 捕获 encodebin 转发出来的 EOS,触发 callback,把录制文件信息返回给应用层。

使用 Smart Recording 时要注意什么

第一,Smart Recording 需要 encoded stream。recordbin 应该接在 parser 后面,典型是 H.264/H.265 parser 后。

第二,smart-rec-cache 要大于 smart-rec-start-time。否则你要求保存事件前 N 秒,但缓存里没有足够历史数据。

第三,录制依赖 I-frame。RTSP 源 GOP 太长、关键帧间隔太大、网络质量差,都可能导致实际录制时长不足。

第四,当前官方文档明确说明不支持 overlapping smart record。同一路 source 不要同时启动多段重叠录制。

第五,保存目录必须存在且可写。deepstream-app 的配置解析代码会检查 smart-rec-dir-path 的写权限。

第六,如果要录制带 bbox 的视频,需要在 OSD 后重新编码再送入 recordbin;如果只录原始流,可以从解码前的 encoded stream 分支进入 recordbin。

总结

DeepStream Smart Recording 的价值在于,它把"持续分析"和"按事件留证"分离开来。

主 pipeline 继续做解码、推理、跟踪和显示;Smart Recording 在旁路缓存 encoded stream。事件发生时,它动态调整缓存窗口,等待关键帧,把事件前后的片段封装成 MP4 或 MKV 文件。

如果只是学习 API,建议先看 deepstream-testsr。它足够小,能清楚展示 NvDsSRCreate()、NvDsSRStart()、NvDsSRStop() 和 NvDsSRDestroy() 的用法。

如果要接入多路 RTSP、Kafka 云端控制和配置化管理,则应该看 deepstream-app。它展示了 Smart Recording 在真实 DeepStream 应用中的集成方式。

在安防、交通、工业巡检和边缘告警场景中,Smart Recording 是一个非常实用的能力:平时不浪费存储,关键时刻保留事件前后的完整证据。

相关推荐
芝麻别开门3 天前
playbin3流切换源码分析
gstreamer·playbin·playbin3
allnlei15 天前
Opus In-Band FEC 详解:原理、实现与开源方案
ffmpeg·音视频·gstreamer
allnlei1 个月前
GStreamer:使用 Meson devenv 管理运行环境
gstreamer
山西茄子2 个月前
在NVIDIA Jetson上从`NvBufSurface`获取CUDA访问
人工智能·deepstream
m0_747124534 个月前
多媒体框架 FFmpeg 和 GStreamer
ffmpeg·gstreamer
山西茄子5 个月前
Deepstream 使用 REST API 动态管理视频流
deepstream
山西茄子5 个月前
DeepStream Single-View 3D Tracking
deepstream·tracker
山西茄子5 个月前
DeepStream9.0 service-maker
人工智能·deepstream
山西茄子5 个月前
DeepStream9.0 inference_builder
人工智能·deepstream