本文以上一期Python版的deepstream-teat1为基础,分别在输入与输出添加功能的两个扩展应用:deepstream-teat1-usbcam与deepstream-teat1-rtsp-out,分别将输入源从视频文件变成USB摄像头,以及将输出从显示屏变成RTSP视频流。接下去同样从代码内容来看看这两个部分需要进行怎样的修改。
现在进去看看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根目录>/sources/deepstream_python_apps/apps cd deepstream-test1-usbcam python3 deepstream_test_1_usb.py |
下面截屏就是打开USB摄像头,对着屏幕上播放的视频进行识别的结果:
总的来说,大约就修改不到10行的内容,就将原本视频文件输入的功能改成USB输入的方式,里面最关键的重点就在于需要加入videoconvert这个插件,来适应各个摄像头硬件厂家所支持的原始格式。
您可以比照这个放松,自行尝试将输入源修改成CSI摄像头或IP摄像头输入,或者可以在多种输入源中任选一种作为输入的方式。
这是一种实用性非常高的应用,很多时候部署在边缘端的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/gst-rtsp-server
# 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://<IP_OF_DEVICE>: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”(如下图)
然后在Jetson Nano 2GB上执行一下指令:
# 在DeepStream设备(如Jetson Nano 2GB) cd <DeepStream根目录>/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之后的画面。
执行后:下图左可以看到Jetson Nano 2GB正在执行推理计算,并且将视频流送出,下图右是在PC的VLC按下“播放”键之后的画面。
这样就完成这个deepstream-test1-rtsp-out的范例,建议大家能多花时间将代码修改的部分好好再咀嚼一番。《完》
好文章,需要你的鼓励
临近年底,苹果公布了2024年App Store热门应用和游戏榜单,Temu再次成为美国下载量最多的免费应用。
云基础设施市场现在已经非常庞大,很难再有大的变化。但是,因为人们可以轻松地关闭服务器、存储和网络——就像开启它们那样,预测全球云基础设施开支可能非常困难。