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() 用来启动录制。调用时传入 startTimeduration,它会返回一个 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-testsrdeepstream-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 是一个非常实用的能力:平时不浪费存储,关键时刻保留事件前后的完整证据。

相关推荐
山西茄子12 小时前
DeepStream9.0 inference_builder
人工智能·deepstream
山西茄子1 天前
DeepStream9.0 Multi-View 3D Tracking
深度学习·deepstream
山西茄子2 天前
DeepStream Code Agent
人工智能·深度学习·deepstream
米优1 个月前
qt+gstreamer实现播放功能
qt·gstreamer
山西茄子2 个月前
GstAggregator的aggregate
开发语言·前端·javascript·gstreamer
野指针YZZ2 个月前
GStreamer RKNN 插件自制
linux·音视频·rk3588·gstreamer
山西茄子5 个月前
Issac sim 做测试视频
音视频·deepstream
xiaohouzi1122338 个月前
Python读取视频-硬解和软解
python·opencv·ffmpeg·视频编解码·gstreamer
阿飞__9 个月前
Linux开发板(如RK3588)上打开摄像头设备并获取实时画面
c++·gstreamer