ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Nvidia Deepstream极致细节:3. Deepstream Python RTSP视频输出显示

2022-01-21 13:33:02  阅读:406  来源: 互联网

标签:Deepstream deepstream pipeline rtsp .. Python RTSP file rtppay


Nvidia Deepstream极致细节:3. Deepstream Python RTSP视频输出显示

此章节将详细对官方案例:deepstream_test_1_rtsp_out.py作解读。deepstream_test_1_rtsp_out.py的主要作用是可以输入rtsp格式的视频流。当我们成功运行了这个Python文件后,我们在屏幕上并不会看到视频,但是,系统会生成一个rtsp地址。当我们使用VLC或者这个案例中flask进行读取,就会看到经过物体识别标注后的视频了。有一些模块在之前的案例讲解中已经解释,这里就一笔带过了。

喜欢的朋友记得收藏点赞哈。


文章目录


1. 如何运行

首先来解决一个大家最关心的问题:如何把这个py程序跑起来。如果还没有成功安装Deepstream6.0以及Deepstream Python 1.1.0的同学,请查看此系列第一篇博文:Nvidia Deepstream极致细节:Deepstream 6.0以及Deepstream Python 1.1.0的安装

关于如何跑起来deepstream_test_1_rtsp_out.py。首先我们可以找到官方文件夹中README文件。还是那句话,如果能看明白官方文档,那么强烈建议按照官方文档走。博客总是难以避免地会遗漏一些,或者资料过时。

在我们按照官方博客安装了对应的依赖:

$ sudo apt update
$ sudo apt-get install libgstrtspserver-1.0-0 gstreamer1.0-rtsp
For gst-rtsp-server (and other GStreamer stuff) to be accessible in
Python through gi.require_version(), it needs to be built with
gobject-introspection enabled (libgstrtspserver-1.0-0 is already).
Yet, we need to install the introspection typelib package:
$ sudo apt-get install libgirepository1.0-dev
$ sudo apt-get install gobject-introspection gir1.2-gst-rtsp-server-1.0

Terminal commands如下:

cd /opt/nvidia/deepstream/deepstream-6.0/sources/apps/deepstream_python_apps
python3 deepstream_test1_rtsp_out.py -i /opt/nvidia/deepstream/deepstream-6.0/samples/streams/sample_qHD.h264

这里需要注意两个地方,第一个是dstest1_pgie_config.txt中模型以及相关的路径。官方用的是相对路径:

model-file=../../../../samples/models/Primary_Detector/resnet10.caffemodel
proto-file=../../../../samples/models/Primary_Detector/resnet10.prototxt
model-engine-file=../../../../samples/models/Primary_Detector/resnet10.caffemodel_b1_gpu0_int8.engine
labelfile-path=../../../../samples/models/Primary_Detector/labels.txt
int8-calib-file=../../../../samples/models/Primary_Detector/cal_trt.bin

但如果我们移动了dstest1_pgie_config.txt的位置,建议使用绝对路径,比如:

model-file=/opt/nvidia/deepstream/deepstream-6.0/samples/models/Primary_Detector/resnet10.caffemodel
proto-file=/opt/nvidia/deepstream/deepstream-6.0/samples/models/Primary_Detector/resnet10.prototxt
model-engine-file=/opt/nvidia/deepstream/deepstream-6.0/models/Primary_Detector/resnet10.caffemodel_b1_gpu0_int8.engine
labelfile-path=/opt/nvidia/deepstream/deepstream-6.0/samples/models/Primary_Detector/labels.txt
int8-calib-file=/opt/nvidia/deepstream/deepstream-6.0/samples/models/Primary_Detector/cal_trt.bin

另外,在deepstream_test_1_rtsp_out.py文件中有一句sys.path.append('../')。这是因为需要调用上一个路径下common包里面的一些函数。

代码跑起来后,我们来看一下Terminal的记录:

Creating Pipeline
Creating Source
Creating H264Parser
Creating Decoder
Creating H264 Encoder
Creating H264 rtppay
Playing file /opt/nvidia/deepstream/deepstream-6.0/samples/streams/sample_qHD.h264
Adding elements to Pipeline
Linking elements in the Pipeline
 *** DeepStream: Launched RTSP Streaming at rtsp://localhost:8554/ds-test ***

Opening in BLOCKING MODE
Opening in BLOCKING MODE
Starting pipeline
Opening in BLOCKING MODE
Opening in BLOCKING MODE
0:00:00.720879172 15042     0x2996b530 WARN                 nvinfer gstnvinfer.cpp:635:gst_nvinfer_logger:<primary-inference> NvDsInferContext[UID 1]: Warning from NvDsInferContextImpl::initialize() <nvdsinfer_context_impl.cpp:1161> [UID = 1]: Warning, OpenCV has been deprecated. Using NMS for clustering instead of cv::groupRectangles with topK = 20 and NMS Threshold = 0.5
ERROR: Deserialize engine failed because file path: /opt/nvidia/deepstream/deepstream-6.0/models open error
0:00:02.190228549 15042     0x2996b530 WARN                 nvinfer gstnvinfer.cpp:635:gst_nvinfer_logger:<primary-inference> NvDsInferContext[UID 1]: Warning from NvDsInferContextImpl::deserializeEngineAndBackend() <nvdsinfer_context_impl.cpp:1889> [UID = 1]: deserialize engine from file :/opt/nvidia/deepstream/deepstream-6.0/models failed
0:00:02.190342602 15042     0x2996b530 WARN                 nvinfer gstnvinfer.cpp:635:gst_nvinfer_logger:<primary-inference> NvDsInferContext[UID 1]: Warning from NvDsInferContextImpl::generateBackendContext() <nvdsinfer_context_impl.cpp:1996> [UID = 1]: deserialize backend context from engine from file :/opt/nvidia/deepstream/deepstream-6.0/models failed, try rebuild
0:00:02.190377035 15042     0x2996b530 INFO                 nvinfer gstnvinfer.cpp:638:gst_nvinfer_logger:<primary-inference> NvDsInferContext[UID 1]: Info from NvDsInferContextImpl::buildModel() <nvdsinfer_context_impl.cpp:1914> [UID = 1]: Trying to create engine from model files
WARNING: [TRT]: Detected invalid timing cache, setup a local cache instead
0:00:35.550098071 15042     0x2996b530 INFO                 nvinfer gstnvinfer.cpp:638:gst_nvinfer_logger:<primary-inference> NvDsInferContext[UID 1]: Info from NvDsInferContextImpl::buildModel() <nvdsinfer_context_impl.cpp:1947> [UID = 1]: serialize cuda engine to file: /opt/nvidia/deepstream/deepstream-6.0/samples/models/Primary_Detector/resnet10.caffemodel_b1_gpu0_int8.engine successfully
INFO: [Implicit Engine Info]: layers num: 3
0   INPUT  kFLOAT input_1         3x368x640
1   OUTPUT kFLOAT conv2d_bbox     16x23x40
2   OUTPUT kFLOAT conv2d_cov/Sigmoid 4x23x40

0:00:35.607265055 15042     0x2996b530 INFO                 nvinfer gstnvinfer_impl.cpp:313:notifyLoadModelStatus:<primary-inference> [UID 1]: Load new model:dstest1_pgie_config.txt sucessfully
NvMMLiteOpen : Block : BlockType = 261
NVMEDIA: Reading vendor.tegra.display-size : status: 6
NvMMLiteBlockCreate : Block : BlockType = 261
NvMMLiteOpen : Block : BlockType = 4
===== NVMEDIA: NVENC =====
NvMMLiteBlockCreate : Block : BlockType = 4
Frame Number=0 Number of Objects=6 Vehicle_count=5 Person_count=1
H264: Profile = 66, Level = 0
NVMEDIA_ENC: bBlitMode is set to TRUE
Frame Number=1 Number of Objects=6 Vehicle_count=5 Person_count=1

实质上,这个例子和上一个deepstream_test_1.py除了RTSP输出外,没有什么区别。输出的rtsp地址为:rtsp://localhost:8554/ds-test。

2. Pipeline管道

在这里插入图片描述

Gst管道由下面几部分组成:

  • filesrc:视频文件的导入。上个博客已经描述,这里不再赘述;
  • h264parse:parse h264格式的视频文件。上个博客已经描述,这里不再赘述;
  • nvv4l2decoder:解码。上个博客已经描述,这里不再赘述。通过指令gst-inspect-1.0 nvv4l2decoder可以考到更多详细参数说明;
  • nvstreammux:将各视频流汇集起来。上个博客已经描述,这里不再赘述;
  • nvinfer:CNN模块的调用。上个博客已经描述,这里不再赘述;
  • nvvideoconvert:图像格式的转换,从NV12到RGBA,用于nvosd模块。上个博客已经描述,这里不再赘述;
  • nvdsosd:作用比如在图像上画bounding box以及文字。上个博客已经描述,这里不再赘述;
  • nvvideoconvert:图像格式的再次转化
  • capsfilter:The element does not modify data as such, but can enforce limitations on the data format.
  • nvv4l2h264enc/nvv4l2h265enc:编码,准备输出给rtsp流。通过指令gst-inspect-1.0 nvv4l2h264enc可以考到更多详细参数说明;
  • rtph264pay/rtph265pay:
  • udpsink:输出。

当我们实例化好所有上述插件模块,我们通过pipeline将他们添加:

pipeline.add(source)
pipeline.add(h264parser)
pipeline.add(decoder)
pipeline.add(streammux)
pipeline.add(pgie)
pipeline.add(nvvidconv)
pipeline.add(nvosd)
pipeline.add(nvvidconv_postosd)
pipeline.add(caps)
pipeline.add(encoder)
pipeline.add(rtppay)
pipeline.add(sink)

并链接:

source.link(h264parser)
h264parser.link(decoder)
sinkpad = streammux.get_request_pad("sink_0")
if not sinkpad:
    sys.stderr.write(" Unable to get the sink pad of streammux \n")

srcpad = decoder.get_static_pad("src")
if not srcpad:
    sys.stderr.write(" Unable to get source pad of decoder \n")

srcpad.link(sinkpad)
streammux.link(pgie)
pgie.link(nvvidconv)
nvvidconv.link(nvosd)
nvosd.link(nvvidconv_postosd)
nvvidconv_postosd.link(caps)
caps.link(encoder)
encoder.link(rtppay)
rtppay.link(sink)

3. nvvideoconvert 与 capsfilter

nvvideoconvert插件模块的作用就是图片格式的转换。在pipeline中用到很多,比如从NV12到RGBA,用于osd模块,后来又从RGBA转到I420,用于视频编码。

capsfilter一般和nvvideoconvert一起使用,用于定义要把图像格式转换成什么类型,比如这个例子caps.set_property("caps", Gst.Caps.from_string("video/x-raw(memory:NVMM), format=I420"))这个链接也有类似的解释:Caps filters are often placed after converter elements like audioconvert, audioresample, videoconvert or videoscale to force those converters to convert data to a specific output format at a certain point in a stream.

4. nvv4l2h264enc/nvv4l2h265enc

这两个模块就是V4l2 H.264/H。265 video encoder。通过指令gst-inspect-1.0 nvv4l2h264enc可以考到更多详细参数说明,我们在下面的pipeline图中也可以看到一些:

在这里插入图片描述

需要注意的是,nvv4l2h264enc/nvv4l2h265enc的输入是I420格式,而输出是byte-stream

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-h264
          stream-format: byte-stream
              alignment: au
  
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-raw(memory:NVMM)
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
                 format: { (string)I420, (string)NV12, (string)P010_10LE, (string)NV24 }
              framerate: [ 0/1, 2147483647/1 ]

代码中设置了一些参数:

encoder.set_property('bitrate', bitrate)
if is_aarch64():
    encoder.set_property('preset-level', 1)
    encoder.set_property('insert-sps-pps', 1)
    encoder.set_property('bufapi-version', 1)

分别的解释:

- bitrate: Set bitrate for v4l2 encode. Unsigned Integer. Range: 0 - 4294967295 Default: 4000000;
- preset-level: HW preset level for encoder flags: readable, writable, Enum "GstV4L2VideoEncHwPreset" Default: 1, "UltraFastPreset"
            (0): DisablePreset    - Disable HW-Preset
            (1): UltraFastPreset  - UltraFastPreset for high perf
            (2): FastPreset       - FastPreset
            (3): MediumPreset     - MediumPreset
            (4): SlowPreset       - SlowPreset
- insert-sps-pps: Insert H.264 SPS, PPS at every IDR frame. Default: false
- bufapi-version: Set to use new buf API. Default: false

对于这些参数,我不是特别懂,有熟悉的朋友可以流言补充。bufapi-version这个参数我查阅了一些资料,好像在Deepstream这个场景下必须设置为True。

5. rtppay 与 udpsink

在这里插入图片描述

rtppay的作用就是:Make the payload-encode video into RTP packets。这个参数我不是很懂,感兴趣的同学可以参考这个链接

关于RTP和UDP的解释,我觉得这个网站写的不多。如果大家对其中的概念感兴趣,可以学习一下。总的来说,RTP 是介于传输层和应用层之间的,默认是UDP作为传输协议。UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。在TCP/IP协议族(Internet protocol suite)的网络架构中,RTP处于应用层,UDP处于传输层,IP处于网络层,数据的封装以及传输是这样一层层下来的。所以,这也就是为什么在这个pipeline里面,系统先通过nvvideoconvert以及capsfilter将数据先转化成I420格式,再通过nvv4l2h264enc/nvv4l2h265enc变成byte-stream,然后开始封装,经过rtppay,到udpsink,最后生成一个rtsp地址。

代码中相关部分:

if codec == "H264":
    rtppay = Gst.ElementFactory.make("rtph264pay", "rtppay")
    print("Creating H264 rtppay")
elif codec == "H265":
    rtppay = Gst.ElementFactory.make("rtph265pay", "rtppay")
    print("Creating H265 rtppay")
if not rtppay:
    sys.stderr.write(" Unable to create rtppay")

最后就是UDP sink部分了。代码中涉猎的部分:

# Make the UDP sink
updsink_port_num = 5400
sink = Gst.ElementFactory.make("udpsink", "udpsink")
if not sink:
    sys.stderr.write(" Unable to create 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)

udpsink的详细说明,我们在terminal中输入gst-inspect-1.0 udpsink

最令人困惑的实际上是syncasync这两个参数。这里引用这个网站的解释:

Gstreamer sets a timestamp for when a frame should be played, if sync=true it will block the pipeline and only play the frame after that time. This is useful for playing from a video file, or other non-live source. If you play a video file with sync=false it would play back as fast as it can be read and processed. Note that for a live source this doesn’t matter, because you are only getting frames in at the capture rate of the camera anyway.

Use sync=true if:
There is a human watching the output, e.g. movie playback
Use sync=false if:
You are using a live source
The pipeline is being post-processed, e.g. neural net
As to your other question, async=false tells the pipeline not to wait for a state change before continuing. Seems mostly useful for debugging.

所以,如果我们对实时性要求比较高的话,那么这两个参数都应该设置为False。

6. 在Flask上实现视频流的读取

最后我们在Flask上实现了对视频流的读取。代码非常简单,我写在另外一个博客里了。

结果如下:

在这里插入图片描述

标签:Deepstream,deepstream,pipeline,rtsp,..,Python,RTSP,file,rtppay
来源: https://blog.csdn.net/zyctimes/article/details/122619384

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有