在NVIDIA-Jetson平台上构建智能多媒体服务器
在NVIDIA-Jetson平台上构建智能多媒体服务器
Building a Multi-Camera Media Server for AI Processing on the NVIDIA Jetson Platform
媒体服务器提供多媒体一体功能,例如视频捕获、处理、流式处理、录制,在某些情况下,还能够在某些事件下触发操作,例如自动拍摄快照。
要使媒体服务器发挥最佳性能,必须是可扩展的、模块化的,并且易于与其进程集成。一个典型的例子是通过进程间通信控制媒体服务器的GUI。
在本文中,将向展示如何在NVIDIA Jetson平台上构建一个简单的用于人工智能处理的实时多摄像机媒体服务器。将演示如何使用GStreamer守护进程(GstD)、GstInterpipe和NVIDIA DeepStream SDK开发一个可伸缩的健壮原型,以便从多个不同的视频源捕获。 除了实现实时的深度学习推断之外,服务器是完全动态的,因为可以在运行时更改正在处理的多个视频流的属性和状态。
一个有价值的附加组件是触发动作的能力,例如拍摄快照或录制视频作为对特定事件的响应。图1显示了在NVIDIA DeepStream软件模块执行一些AI处理之后,视频流的一个示例快照,其中包含生成的边界框。稍后描述的示例媒体服务器使用两个摄像机,但是,复制这两个视频流以演示可以添加多个视频流。
Figure 1. AI media server sample snapshot.
在本文的最后,将了解一组基于GStreamer的工具,以及如何根据特定的多媒体和人工智能需求来扩展。要学习本教程,应该具备GStreamer和DeepStream框架的基本知识。
Why a media server?
在媒体服务器中使用DeepStream为智能媒体产品提供了快速上市解决方案。监视和运动流(如图2所示)是两个用例的例子,在这些用例中,可以使用增强了人工智能功能的媒体服务器来分析捕获的视频并提取有用的信息。当检测到异常行为时,服务器可以触发记录或快照等事件。
Figure 2: Media server example for sports streaming.
AI media server modules
人工智能媒体服务器模块
媒体服务器划分为四个模块:
视频捕获
视频处理与人工智能
视频编码
其功能,如录制、流式处理和快照。
与集成媒体服务器的每个模块相对应的四个模块:视频捕获、视频处理和人工智能、视频编码和附加功能。
如果使用GStreamer实现媒体服务器,则最终会得到如下代码示例所示的管道:
v4l2src device=/dev/video1 ! video/x-raw,width=640,height=480 ! videoconvert ! video/x-raw,format=I420,width=640,height=480 ! queue ! tee name=camera0 \
nvarguscamerasrc ! nvvidconv ! video/x-raw,format=I420,width=640,height=480 ! queue ! tee name=camera1 \
camera0. ! video/x-raw,format=I420,width=640,height=480 ! nvvideoconvert ! video/x-raw(memory:NVMM),format=NV12,width=640,height=480 ! nvstreammux0.sink_0 \
camera0. ! video/x-raw,format=I420,width=640,height=480 ! nvvideoconvert ! video/x-raw(memory:NVMM),format=NV12,width=640,height=480 ! nvstreammux0.sink_1 \
camera1. ! video/x-raw,format=I420,width=640,height=480 ! nvvideoconvert ! video/x-raw(memory:NVMM),format=NV12,width=640,height=480 ! nvstreammux0.sink_2 \
camera1. ! video/x-raw,format=I420,width=640,height=480 ! nvvideoconvert ! video/x-raw(memory:NVMM),format=NV12,width=640,height=480 ! nvstreammux0.sink_3 \
nvstreammux name=nvstreammux0 batch-size=4
batched-push-timeout=40000 width=640 height=480 ! queue ! nvinfer batch-size=4
config-file-path=deepstream-models/config_infer_primary_4_cameras.txt ! queue !
nvtracker ll-lib-file=deepstream-models/libnvds_mot_klt.so enable-batch-process=
true
! queue ! nvmultistreamtiler width=640 height=480
rows=2 columns=2 ! nvvideoconvert ! nvdsosd ! queue ! tee name=deep \
deep. ! nvvideoconvert ! nvv4l2h264enc insert-sps-pps=true iframeinterval=10 ! tee name=h264 \
deep. ! nvvideoconvert ! nvv4l2h265enc insert-sps-pps=true iframeinterval=10 ! tee name=h265 \
deep. ! nvvideoconvert ! nvv4l2vp9enc ! tee name=vp9 \
deep. ! nvvideoconvert ! video/x-raw,width=640,height=480 ! nvjpegenc ! tee name=jpeg \
h264. ! h264parse ! matroskamux ! filesink name=file location=test-h264-0.mkv \
h265. ! h265parse ! matroskamux ! filesink name=file location=test-h265-0.mkv \
vp9. ! matroskamux ! filesink name=file location=test-vp9-0.mkv \
jpeg. ! filesink name=file location=test-snapshot0.jpg
除了很难阅读之外,这段代码对于动态控制模块连接和管道状态也不是很简单。此外,这种方法不可扩展,可能导致代码复制,更不用说实现简单原型所需经历的耗时学习曲线了。以下是如何克服这些问题的方法:
模块互连
模块控制
Module interconnection
一个更好的实现应该有多个更小的管道,而不是一个非常大的管道。问题是如何连接这些管道。
RidgeRun的GstInterpipe是一个开源插件,可以解决这个问题。GstInterpipe插件提供了两个元素interpipesrc和interpipesink。
Module control
媒体服务器的一个理想特性是能够对正在处理的不同流的状态和属性进行某种程度的控制。 RidgeRun的GstD是一个开源项目,是处理GStreamer框架的多线程Linux守护进程。GstD提供了一个类似gst启动的命令行接口gst client,以及C和Python绑定。简化了动态管道管理,加快了原型和开发时间。
Building an AI media server in 30 minutes
所有这些都准备好了,现在可以使用这里描述的工具将所有内容放在一个真实的实现中。可以使用DeepStream、基于GStreamer的工具(GstD、GstInterpipe)和硬件加速插件,在Python中为Jetson TX2板创建一个示例媒体服务器。
Prerequisites
要运行AI媒体服务器,需要安装Jetpack 4.3的Jetson TX2板。此外,演示假设板上连接了两个摄像头,一个在MIPI CSI上,一个在USB上。
按照wiki页面安装GStreamer守护进程和GstInterpipe。安装这些开源项目后,可以按照运行演示wiki页面中的说明进行操作。
Video capture
在这个模块中,应用程序通常使用一个或多个摄像头或捕获硬件设备,为媒体服务器提供视频输入。对于每个用例,考虑接口和硬件制造商来选择正确的解决方案。
有多种接口可用于捕获与Jetson板兼容的视频。这些选项包括MIPI CSI、USB、SLVS、GMSL和FPD Link。有多家制造商为Jetson平台板提供摄像头,如索尼、OmniVision和OnSemi。
有些应用需要使用许多摄像头。在这种情况下,需要特殊的硬件和软件。一个例子是D3工程的子板(图3),连接到Jetson AGX Xavier上,使用RidgeRun的相机驱动程序获得最多16个FPD Link III工作相机。有关更多信息,请参见演示,RidgeRun D3 NVIDIA Partner Showcase Jetson Xavier多摄像头AI演示。
Figure 3: D3 Engineering hardware for 16 FPD-Link III cameras on Jetson AGX Xavier.
在这个媒体服务器示例中,使用两个摄像头:MIPI CSI和USB摄像头。以下是USB摄像头的捕获管道: client.pipeline_create('camera0', 'v4l2src device=/dev/video0 ! \ video/x-raw,format=YUY2,width=1280,height=720 ! \interpipesink name=camera0 forward-events=true forward-eos=true sync=false')
client对象是用于控制媒体服务器管道的GstD客户端实例。
使用管道名称和描述为参数的管道创建命令。这个camera0管道使用v4l2src元素从USB摄像机捕获,在本例中,设备属性设置为/dev/video0。此属性必须根据每个板中的视频设备排列进行设置。通过名为camera0的interpipesink实例使USB摄像头捕获可用。
现在,考虑一下用于从CSI MIPI相机捕获的camera1管道:
client.pipeline_create(
'camera1', 'nvarguscamerasrc ! nvvidconv !
video/x-raw,format=I420,width=1280,height=720 ! queue ! interpipesink
name=camera1 forward-events=true forward-eos=true sync=false')
要从该传感器捕获,请使用nvarguscamerasrc元素,NVIDIA视频捕获专有元素下面使用libargus。CSI MIPI camera视频流通过名为camera1的interpipesink实例提供。
Video processing and AI
视频处理块由两个功能单元组成。一个负责调整和修改捕获的视频缓冲区(视频处理)的属性,另一个负责将原始流数据转换为可操作的视野(人工智能处理)。
视频处理
通常,原始摄像机视频流需要一些格式更改才能被其模块使用。这些更改可能与帧速率、分辨率、颜色空间或用于分配缓冲区的内存类型有关。
要执行这些操作,可以利用NVIDIA的硬件加速GStreamer元素。例如,考虑camera0_rgba_nvmm和camera1_rgba_nvmm管道,各自监听各自的相机。nvvideoconvert元素用于同时执行颜色空间、内存转换和内存:
client.pipeline_create(
'camera0_rgba_nvmm', 'interpipesrc listen-to=camera0 !
video/x-raw,format=YUY2,width=1280,height=720 ! videoconvert !
video/x-raw,format=NV12,width=1280,height=720 ! nvvideoconvert !
video/x-raw(memory:NVMM),format=RGBA,width=1280,height=720 ! queue !
interpipesink name=camera0_rgba_nvmm forward-events=true forward-eos=true
sync=false caps=video/x-raw(memory:NVMM), format=RGBA, width=1280, height=720, pixel-aspect-ratio=1/1,interlace-mode=progressive,framerate=30/1')
已处理的视频流由名为camera0_rgba_nvmm和camera1_rgba_nvmm的管道间接收器实例提供。
人工智能处理
AI
处理模块完全依赖于DeepStream SDK中提供的一组基于GStreamer的工具。提供了一个可操作的洞察级别,涉及对原始数据(在媒体服务器的情况下,来自摄像机的视频缓冲区)的分析和GPU上的后续处理。可以通过DeepStream访问的一些功能包括:
流聚合和批处理
基于时态相关的推理用于检测、分类和分割
目标跟踪参考实现
用于突出显示对象和文本覆盖的屏幕显示API
参考媒体服务器示例,interpipesrc元素侦听摄像机处理的视频流,并将其转换为运行批处理机制的nvreammux元素的四个输入。尽管在这个示例媒体服务器中仅使用两个摄像机,但可以将输入复制到nvreammux以演示可以使用更多摄像机。
mux
元素的输出进入nvinfer实例和其可用的DeepStream元素,最终到达interpipesink实例,该实例向媒体服务器的下一个阶段提供包含推断信息的视频帧。
client.pipeline_create('deepstream', '\
interpipesrc listen-to=camera0_rgba_nvmm ! nvstreammux0.sink_0 \
interpipesrc listen-to=camera0_rgba_nvmm ! nvstreammux0.sink_1 \
interpipesrc listen-to=camera1_rgba_nvmm ! nvstreammux0.sink_2 \
interpipesrc listen-to=camera1_rgba_nvmm ! nvstreammux0.sink_3 \
nvstreammux name=nvstreammux0 batch-size=4 batched-push-timeout=40000 width=1280 height=720 ! queue ! nvinfer batch-size=4 config-file-path=../deepstream-models/config_infer_primary_4_cameras.txt ! queue ! \
nvtracker
ll-lib-file=../deepstream-models/libnvds_mot_klt.so enable-batch-process=
true
! queue ! nvmultistreamtiler width=1280 height=720
rows=2 columns=2 ! nvvideoconvert ! nvdsosd ! queue ! \
interpipesink name=deep
forward-events=
true
forward-eos=
true
sync=
false')
Video encoding
在这一点上,有一个包含(或不包含)人工智能信息的视频缓冲流。对于其模块(如录制、流式处理或拍摄快照),需要数据压缩以避免不可管理的文件大小问题、网络过载和其一些问题。当嵌入式系统的可用硬件资源有限且功耗是关键竞争因素时,这些问题尤其重要。
编码和解码是一种资源密集型操作。当接收到重要的数据流时,这可能会导致处理瓶颈。这个限制可以通过使用基于硬件的编码器和解码器(编解码器)来解决。NVIDIA提供了硬件编解码器,可以在专门的硬件上加速编码和解码,从而为其任务卸载CPU和GPU单元。
正在构建的媒体服务器描述了两种不同的视频编码格式(H.264和VP9)选项和一种图像编码(JPEG)选项。
下面的代码示例显示了H.264、VP9和JPEG的这种编码管道的实现:
client.pipeline_create('h264', 'interpipesrc name=h264_src format=time listen-to=deep ! video/x-raw(memory:NVMM),format=RGBA,width=1280,height=720 ! nvvideoconvert ! nvv4l2h264enc ! interpipesink name=h264_sink forward-events=true forward-eos=true sync=false async=false enable-last-sample=false drop=true')
client.pipeline_create('vp9', 'interpipesrc name=vp9_src format=time listen-to=deep ! nvvideoconvert ! nvv4l2vp9enc max-perf=true ! interpipesink name=vp9_sink forward-events=true forward-eos=true sync=false async=false enable-last-sample=false drop=true')
client.pipeline_create('jpeg', 'interpipesrc name=src format=time listen-to=deep ! nvvideoconvert ! video/x-raw,format=I420,width=1280,height=720 ! nvjpegenc ! interpipesink name=jpeg forward-events=true forward-eos=true sync=false async=false enable-last-sample=false drop=true')
前面描述的管道处理DeepStream模块的输出。通过nvvideoconvert将缓冲区转换为适合编码器的颜色空间,并使用nv4l2h264enc、nv4l2vp9enc和nvjpegenc(硬件加速)元素压缩数据。最后,通过管道将编码的缓冲区传递给以下媒体服务器模块使用的interpipesink实例。
在vp9管道中,nv4l2vp9enc元素的属性max perf设置为true。这将启用编码器的高性能模式,并且在使用高分辨率和帧速率时非常有用。但是,启用此属性会导致功耗增加,这需要在移动系统上加以考虑。
Additional features
最后一个阶段可以称为最终产品模块。可以生成视频记录、通过网络发起视频流,或者简单地从照相机生成其中一个视频流的快照。所有选项都包括在DeepStream模块中完成的先前AI处理。
同样,每个管道都有一个listen to属性设置到每个对应的编码流。两个视频录制管道都使用Matroska容器来生成生成的文件。
H264录音
client.pipeline_create(
'record_h264', 'interpipesrc format=time allow-renegotiation=false
listen-to=h264_sink ! h264parse ! matroskamux ! filesink
name=filesink_record_h264')
在H.264管道的情况下,需要h264parse元素使编码块的输出适应matroskamux接受的内容。
VP9录制
client.pipeline_create(
'record_vp9', 'interpipesrc format=time listen-to=vp9_sink !
matroskamux ! filesink name=filesink_record_vp9')
同样,VP9也被打包到Matroska容器中。另一方面,不需要解析器。
快照
client.pipeline_create(
'snapshot', 'interpipesrc format=time listen-to=jpeg
num-buffers=1 ! filesink name=filesink_snapshot')
快照管道使用num buffers属性仅接受启用快照数据路径后通过interpipesrc实例的第一个缓冲区。捕获第一个缓冲区后,会自动发送一个流结束(EOS事件,以避免用更新的事件覆盖所需的缓冲区)。
网络流媒体
流协议(UDP、TCP、RTSP和WebRTC)的实际选择取决于用例场景需求和参数,如延迟、质量、安全性和成本等。
WebRTC和RTSP是最常用的流媒体解决方案,GStreamer支持这两种协议。为了加快开发速度,RidgeRun提供了GstWebRTC和GstRtspSink插件等产品。GstWebRTC用于将管道转换为与WebRTC兼容的端点,而GstRtspSink则加速原型制作并促进集成。
AI media server dynamics
在前面的章节中,展示了AI媒体服务器实现的一些细节。现在,将探索如何控制这些模块,并利用GstInterpipes更改连接。
要播放管道,请使用命令pipeline\u play,并将管道名称用作参数。例如,要从两个摄像机开始捕获,并在媒体服务器中启动视频处理和DeepStream管道,请使用以下代码示例:
client.pipeline_play('camera0')
client.pipeline_play('camera1')
client.pipeline_play('camera0_rgba_nvmm')
client.pipeline_play('camera1_rgba_nvmm')
client.pipeline_play('deepstream')
记录
要开始录制,首先需要设置文件的写入位置。为此,请使用element\u set命令设置filelink元素的location属性,如下所示:
client.element_set(
'record_vp9', 'filesink', 'location', 'test_record_vp9_0.mkv')
该位置可以设置为绝对路径,也可以像前面的示例一样设置为相对路径。在位置设置为相对路径的情况下,文件将写入GtsD启动的目录。
现在可以播放编码和录制管道以开始录制过程。例如,以下命令开始录制vp9:
client.pipeline_play('vp9')
client.pipeline_play('record_vp9')
要停止录制,请将EOS事件发送到编码器管道以允许编码器正确完成,等待管道完成对任何缓冲数据的处理,然后停止编码和录制管道:
client.event_eos('vp9')
client.bus_filter('vp9', 'eos')
client.bus_read('vp9')
client.pipeline_stop('vp9')
client.pipeline_stop('record_vp9')
在某些情况下,更改编码器视频源会很有用。例如,如果要从相机输出(无对象检测)而不是DeepStream输出中录制一个常规视频流,这将非常有用。GstInterpipes允许执行此动态更改。
使用element_set命令将编码管道的listen to属性更改为提供要录制的视频流的interpipesink的名称。例如,要仅录制camera1输出,请连接到该相机的视频处理管道interpipesink,如下所示:
client.element_set('vp9', 'vp9_src', 'listen-to', 'camera0_rgba_nvmm')
然后,按照设置所需文件名和播放编码和录制管道的相同步骤启动该过程
client.element_set('record_vp9', 'filesink', 'location', 'test_record_vp9_1.mkv')
client.pipeline_play('vp9')
client.pipeline_play('record_vp9')
快照
拍摄快照与前面描述的录制类似。要拍摄快照,必须设置快照的写入位置。然后可以启动编码和快照管道。
client.element_set('snapshot', 'filesink', 'location', 'test_snapshot_0.jpeg')
client.pipeline_play('jpeg')
client.pipeline_play('snapshot')
与记录连接更改类似,快照源也可以更改。例如,这将允许拍摄单个相机捕获的快照。只需更改JPEG管道interpipesrc listen to属性
client.element_set('jpeg', 'jpeg_src', 'listen-to', 'camera0_rgba_nvmm')
复杂的场景
GtsD的灵活性允许媒体服务器根据AI模块检测到的事件触发操作。例如,检测到危险区域中的人员可能会触发媒体服务器中的信号。自定义应用程序可以监听这些类型的事件,并使用这些信息触发媒体服务器中的操作,例如拍摄快照,甚至在媒体服务器域之外执行进一步的操作。
总结
现在已经学会了创建一个媒体服务器解决方案,可以从多个摄像头捕获视频,通过人工智能处理应用一些基本级别的图像识别,并生成一个文件(视频或照片),而无需从Gstreamer和Glib学习曲线导出麻烦。在开发过程中,了解了一些工具(GstD、GstInterPipe),这些工具可以为设计带来灵活性和动态性,允许对媒体服务器中的几个数据处理分支进行运行时控制。