NVIDIA Jetson Nano 2GB 系列文章(36):加入USB输入与RTSP输出

本文以上一期 Python 版的 deepstream-test1 为基础,在输入与输出添加功能的两个扩展应用:deepstream-teat1-usbcam 与 deepstream-teat1-rtsp-out 中,分别将输入源从视频文件变成 USB 摄像头,以及将输出从显示屏变成 RTSP 视频流。接下去同样从代码内容来看看这两个部分需要进行怎样的修改。

将输入源从视频文件修改成 USB 摄像头
文件路径:/sources/deepstream_python_apps/apps的deepstream-test1-usbcam/deepstream_test_1_usb.py
范例功能:将 deepstream-test1 的视频文件输入改成 USB 摄像头

插件流:v4l2src --> videoconvert--> nvvideoconvert --> nvstreammux --> nvinfer --> nvvideoconvert --> nvdsosd --> nvegltransform --> nveglglessink

修改重点:

将原本数据源插件从 filesrc 改成 v4l2src

将原本格式转换插件从 h264 相关的,改成符合 USB 摄像头原始数据格式相关的

现在进去看看 deepstream_test_1_usb.py 与前面 deepstream_test_1.py 有那些修改的地方。

首先在创建阶段的修改,原本用 H264 视频文件输入的部分,需要以下四个元件:

source = Gst.ElementFactory.make("filesrc", "file-source")
h264parser = Gst.ElementFactory.make("h264parse", "h264-parser")
decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2-decoder")
streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")

如今要改成USB摄像头输入时,就需要把输入的元件改成以下六个元件:

source = Gst.ElementFactory.make("v4l2src","usb-cam-source")
caps_v4l2src = Gst.ElementFactory.make("capsfilter","v4l2src_caps")
vidconvsrc = Gst.ElementFactory.make("videoconvert","convertor_src1")
nvvidconvsrc=Gst.ElementFactory.make("nvvideoconvert","convertor_src2")
caps_vidconvsrc = Gst.ElementFactory.make("capsfilter","nvmm_caps")
streammux = Gst.ElementFactory.make("nvstreammux","Stream-muxer")

这里面的第 3~4 行添加 videoconvert->nvvideoconvert 的元件,最主要是因为 nvvideoconvert 并不支持所有原始格式,例如某些品牌 USB 摄像头常用的 YUYV 这种格式,但是我们需要获得 nvvideoconvert 所能支持的摄像头原始格式,于是 GStreamer 的 videoconvert 就能起到调节的作用。

Videoconvert 插件确保提供支持原始格式的超集,然后再由 nvvideoconvert 插件调用 NvBufSurface API 将传入的原始缓冲区,转换格式存到 NVMM Mem 里。

接下去在设置 caps_v4l2src 与 caps_vidconvsrc 元件参数的部分,并将输入源设定为程式所接受的第一个参数指,最后还设置“sync=false”以避免在显示接收器处出现延迟帧下降,其他设定指与 test1 的内容一致:

caps_v4l2src.set_property('caps',Gst.Caps.from_string("video/x-raw, framerate=30/1"))
caps_vidconvsrc.set_property('caps', \ Gst.Caps.from_string("video/x-raw(memory:NVMM)"))
source.set_property('device',args[1])
。。。。
sink.set_property('sync',False)

后面的元件添加、元件连接、创建事件循环、播放并收听事件等部分的代码,则与 deepstream_test_1.py 是一致的,甚至 osd_sink_pad_buffer_probe 的代码也是完全一样的,这表示我们只要简单地将输入阶段的插件进行合适的替换,就能修改输入源的变更,十分简单。

请先将 USB 摄像头连接到设备上,然后执行以代码:

cd<deepstream< span="">根目录>/sources/deepstream_python_apps/app
cd deepstream-test1-usbcam
python3 deepstream_test_1_usb.py

下面截屏就是打开 USB 摄像头,对着屏幕上播放的视频进行识别的结果:
image
NVIDIA Jetson Nano 2GB 系列文章(36):加入USB输入与RTSP输出
总的来说,大约就修改不到 10 行的内容,就将原本视频文件输入的功能改成 USB 输入的方式,里面最关键的重点就在于需要加入 videoconvert 这个插件,来适应各个摄像头硬件厂家所支持的原始格式。

您可以照此自行尝试将输入源修改成 CSI 摄像头或 IP 摄像头输入,或者可以在多种输入源中任选一种作为输入的方式。

将输出从显示屏转成 RTSP 视频流
文件路径:/sources/deepstream_python_apps/apps的deepstream-test1-rtsp-out/deepstream_test1_rtsp_out.py
范例功能:将 deepstream-test1 的输出透过 RTSP 协议转向其他电脑
修改重点:在 nvdsosd 之后添加将格式转成符合 RSTP 协议的 H264 格式,主要如下:

nvvideoconvert:将视频颜色格式从 RGBA 转换成 I420
CapsFilter:对数据实施限制(无数据修改)
nvv4l2h264enc:将 I420 格式的原始数据转成 H264 编码
RtpH264Pay:将 H264 编码的有效负载转换为 RFC 3984 格式的 RTP 数据包
UDPSink:向网络发送 UDP 数据包,当 rtph264pay 数据包与 RTP 有效负载配对时,就会生成 RTP 视频流。

这是一种实用性非常高的应用,很多时候部署在边缘端的 AIOT 计算设备,无法在机器上直接安装显示器去监控现场的状况,这时候用 RTSP 协议发出视频流,我们可以在其他设备上透过 VLC 之类的视频播放器,观看边缘设备上的识别结果,过程中可能会有些许的延迟,但总体来说确实非常方便部署的技术。

由于本实验需要用到 gst-rtsp-server 库,因此需要先进行以下的安装:

sudo apt update
sudo apt-get install libgstrtspserver-1.0-0 gstreamer1.0-rtsp
sudo apt-get install libgirepository1.0-dev
sudo apt-get install gobject-introspection gir1.2-gst-rtsp-server-1.0

这个项目的输入部分还是使用 deepstream-test1 的 H264 视频文件,唯一改变的地方就是输出的方式,也就是在 deepstream_test_1.py 管道流 nvdsosd 之前的代码可以不做更动,重点在后面的部分。


deepstream_test1_rtsp_out.py 里面,从第 179 行(nvdsosd 之后)才开始产生变化,这里开始添加一些插件元件:

nvvidconv_postosd = Gst.ElementFactory.make("nvvideoconvert", "convertor_postosd")

# 创建caps过滤器
caps = Gst.ElementFactory.make("capsfilter", "filter")
caps.set_property("caps",   Gst.Caps.from_string("video/x-raw(memory:NVMM), format=I420"))


# 根据输入参数选择使用的编码器,预设为H264
if codec == "H264":
    encoder =   Gst.ElementFactory.make("nvv4l2h264enc",   "encoder")
elif codec == "H265":
    encoder =   Gst.ElementFactory.make("nvv4l2h265enc",   "encoder")
encoder.set_property('bitrate', bitrate)


# 如果是在 Jetson 设备上,执行下面的设定
if is_aarch64():
      encoder.set_property('preset-level', 1)
      encoder.set_property('insert-sps-pps', 1)
      encoder.set_property('bufapi-version', 1) 
      
# 使有效负载将视频编码为RTP数据包
if codec == "H264":
   rtppay =   Gst.ElementFactory.make("rtph264pay", "rtppay")
elif codec == "H265":
   rtppay =   Gst.ElementFactory.make("rtph265pay", "rtppay") 
   
# 建立 UDP 接收器
updsink_port_num = 5400
sink = Gst.ElementFactory.make("udpsink",   "udpsink")


# 设定接收器的值
sink.set_property('host', '224.224.255.255')
sink.set_property('port', updsink_port_num)
sink.set_property('async', False)
sink.set_property('sync', 1)

接下去就是按照 file-source -> h264-parser -> nvh264-decoder -> nvinfer -> nvvidconv -> nvosd -> nvvidconv_postosd -> caps -> encoder -> rtppay -> udpsink 顺序,将元件依序进行连接。

以上部分的操作逻辑与先前所说的都是一致的,最后只剩一个部分,就是在代码第 276~286 行,需要调用 Gstreamer 的 gst-rtsp-server 库的 GstRtspServer 对象,细节请自行参考
https://github.com/GStreamer/...


# Start streaming
rtsp_port_num = 8554  # 设定端口值
server = GstRtspServer.RTSPServer.new()
server.props.service = "%d" % rtsp_port_num
server.attach(None)


factory = GstRtspServer.RTSPMediaFactory.new()
factory.set_launch( "( udpsrc name=pay0 port=%d buffer-size=524288   caps=\"application/x-rtp, media=video, clock-rate=90000,   encoding-name=(string)%s, payload=96 \" )" % (updsink_port_num,   codec))
factory.set_shared(True)
server.get_mount_points().add_factory("/ds-test",   factory)  # 设定加载点

最后面的 mount_points 设定为“/ds-test”,因此将来在其他设备上要读取 RTSP 视频流的完整地址就是“rtsp://:8554/ds-test”,到此就完成将推理计算的结果透过 RTSP 服务向外推送的工作。

最后关于 osd_sink_pad_buffer_probe 的部分,与 deepstream_test_1.py 的内容完全一样,就不重复说明,至于代码最后面有个“def parse_args():”的部分,只是为了解析程式读入的指令参数,很容易就能看懂,这里也不浪费篇幅去讲解。

现在是否可以准备执行这个应用呢?当然是可以的,只不过好像还没有准备好“接收视频”的环境!这个其实也很简单,请找到与本台边缘设备(Jetson Nano 2GB)在相同网段的另一台电脑,可以是 Windows、MacOS、Ubuntu 操作系统,只要能安装 VLC 这个播放软件就可以。

假设是使用 USB 线连接 Jetson Nano 2GB 与 PC 的状态,这时在 PC 上可以透过“192.168.55.1”这个 IP 访问到 Jetson Nano 2GB。现在可以先打开 VLC,选择“打开网路串流”后,输入“
rtsp://192.168.55.1:8554/ds-test”(如下图)
image
NVIDIA Jetson Nano 2GB 系列文章(36):加入USB输入与RTSP输出
然后在Jetson Nano 2GB上执行一下指令:


# 在DeepStream设备(如Jetson   Nano 2GB)
cd<deepstream< span="">根目录>/sources/deepstream_python_apps/apps
cd deepstream-test1-rtsp-out
ln -s   ../../../../samples/streams/sample_720p.h264 sample_720.h264
python3   deepstream_test_1_usb.py -i sample_720.h264

执行前:下图左是用 Nomachine 连接到 Jetson Nano 2GB 的显示,下图右是在 PC 上打开 VLC 之后的画面。
image
NVIDIA Jetson Nano 2GB 系列文章(36):加入USB输入与RTSP输出
执行后:下图左可以看到 Jetson Nano 2GB 正在执行推理计算,并且将视频流送出,下图右是在 PC 的 VLC 按下“播放”键之后的画面。
image
NVIDIA Jetson Nano 2GB 系列文章(36):加入USB输入与RTSP输出
这样就完成这个 deepstream-test1-rtsp-out 的范例,建议大家能多花时间将代码修改的部分好好再咀嚼一番。

推荐阅读
关注数
7021
内容数
61
深耕嵌入式 AI 计算,助力自主机器研发
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息