Unresponsive/slow RTSP stream from USB Cam + Deepstream on Jetson Nano

Hello. I’m trying to simply re-transmit video with a Jetson Nano (4GB). I am using a USB camera that operates ~30FPS @1280x960. When I connect to the Nano from another device (using VLC) the feed is black for a long time then only sends a single frame before staying the same. I’ve tried another camera that operates ~30FPS @640x480 without any issue. My script is below:

import argparse
import sys
import os

sys.path.append('../')

import gi

gi.require_version('Gst', '1.0')
gi.require_version('GstRtspServer', '1.0')
from gi.repository import GObject, Gst, GstRtspServer
from common.is_aarch_64 import is_aarch64
from common.bus_call import bus_call


def main(args):
    # Standard GStreamer initialization
    GObject.threads_init()
    Gst.init(None)

    # Create gstreamer elements
    # Create Pipeline element that will form a connection of other elements
    print("Creating Pipeline \n ")
    pipeline = Gst.Pipeline()
    if not pipeline:
        sys.stderr.write(" Unable to create Pipeline \n")

    # Source element for reading from the file
    print("Creating Source \n ")
    source = Gst.ElementFactory.make("v4l2src", "usb-cam-source")
    if not source:
        sys.stderr.write(" Unable to create Source \n")

    caps_v4l2src = Gst.ElementFactory.make("capsfilter", "v4l2src_caps")
    if not caps_v4l2src:
        sys.stderr.write(" Unable to create v4l2src capsfilter \n")

    print("Creating Video Converter \n")

    # Adding videoconvert -> nvvideoconvert as not all
    # raw formats are supported by nvvideoconvert;
    # Say YUYV is unsupported - which is the common
    # raw format for many logi usb cams
    # In case we have a camera with raw format supported in
    # nvvideoconvert, GStreamer plugins' capability negotiation
    # shall be intelligent enough to reduce compute by
    # videoconvert doing passthrough

    # videoconvert to make sure a superset of raw formats are supported
    vidconvsrc = Gst.ElementFactory.make("videoconvert", "convertor_src1")
    if not vidconvsrc:
        sys.stderr.write(" Unable to create videoconvert \n")

    nvvidconvsrc = Gst.ElementFactory.make("nvvideoconvert", "convertor_src2")
    if not nvvidconvsrc:
        sys.stderr.write(" Unable to create Nvvideoconvert \n")

    caps_vidconvsrc = Gst.ElementFactory.make("capsfilter", "nvmm_caps")
    if not caps_vidconvsrc:
        sys.stderr.write(" Unable to create capsfilter \n")

    # Make the encoder
    if codec == "H264":
        encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder")
        print("Creating H264 Encoder")
    elif codec == "H265":
        encoder = Gst.ElementFactory.make("nvv4l2h265enc", "encoder")
        print("Creating H265 Encoder")
    if not encoder:
        sys.stderr.write(" Unable to create encoder")
    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)
        encoder.set_property('maxperf-enable', 1)

    # Make the payload-encode video into RTP packets
    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")

    # 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)

    print("Playing cam %s " % camera_path)
    caps_v4l2src.set_property('caps', Gst.Caps.from_string("video/x-raw, framerate=30/1"))
    source.set_property('device', camera_path)

    print("Adding elements to Pipeline \n")
    pipeline.add(source)
    pipeline.add(caps_v4l2src)
    pipeline.add(vidconvsrc)
    pipeline.add(nvvidconvsrc)
    pipeline.add(caps_vidconvsrc)
    pipeline.add(encoder)
    pipeline.add(rtppay)
    pipeline.add(sink)

    # Link the elements together:
    # file-source -> h264-parser -> nvh264-decoder ->
    # caps -> encoder -> rtppay -> udpsink

    print("Linking elements in the Pipeline \n")
    source.link(caps_v4l2src)
    caps_v4l2src.link(vidconvsrc)
    vidconvsrc.link(nvvidconvsrc)
    nvvidconvsrc.link(caps_vidconvsrc)
    caps_vidconvsrc.link(encoder)
    encoder.link(rtppay)
    rtppay.link(sink)

    # create an event loop and feed gstreamer bus mesages to it
    loop = GObject.MainLoop()
    bus = pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect("message", bus_call, loop)

    # Start streaming
    rtsp_port_num = int(os.environ.get('RTSP_SERVER_PORT_NUMBER', '8554'))
    rtsp_stream_end = os.environ.get('RTSP_SERVER_STREAM_END', "/rtsp/camera")

    username = os.environ.get('CAMERA_USERNAME', 'user')
    password = os.environ.get('CAMERA_PASSWORD', "pass")

    server = GstRtspServer.RTSPServer.new()
    server.props.service = "%d" % rtsp_port_num

    auth = GstRtspServer.RTSPAuth()
    token = GstRtspServer.RTSPToken()
    token.set_string('media.factory.role', username)
    basic = GstRtspServer.RTSPAuth.make_basic(username, password)
    auth.add_basic(basic, token)
    server.set_auth(auth)

    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)
    permissions = GstRtspServer.RTSPPermissions()
    permissions.add_permission_for_role(username, "media.factory.access", True)
    permissions.add_permission_for_role(username, "media.factory.construct", True)
    factory.set_permissions(permissions)
    server.get_mount_points().add_factory(rtsp_stream_end, factory)

    print("\n *** DeepStream: Launched RTSP Streaming at rtsp://%s:%s@0.0.0.0:%d%s ***\n\n" %
          (username, password, rtsp_port_num, rtsp_stream_end))

    # start play back and listen to events
    print("Starting pipeline \n")
    pipeline.set_state(Gst.State.PLAYING)
    try:
        loop.run()
    except:
        pass
    # cleanup
    pipeline.set_state(Gst.State.NULL)


def parse_args():
    parser = argparse.ArgumentParser(description='RTSP Output Sample Application Help ')
    parser.add_argument("-c", "--codec", default="H264",
                        help="RTSP Streaming Codec H264/H265 , default=H264", choices=['H264', 'H265'])
    parser.add_argument("-b", "--bitrate", default=4000000,
                        help="Set the encoding bitrate ", type=int)
    parser.add_argument("-i", "--input-device", default="/dev/video0",
                        help="Set the USB Camera device", type=str)
    args = parser.parse_args()
    global codec
    global bitrate
    global camera_path
    camera_path = args.input_device
    codec = args.codec
    bitrate = args.bitrate
    return 0


if __name__ == '__main__':
    parse_args()
    sys.exit(main(sys.argv))

I’ve enabled Jetson clocks + MAXN and when doing this I am running the Jetson Nano in headless mode.

I’ve tried viewing the camera using “Cheese” on the Jetson device and it views fine.

Is there something I can do to enable full resolution video transmission from this camera? Could I do a larger resolution potentially with more tweaks?

I’d suspect the delay in VLC itself…I would not advise it for low latency, and as new versions came I became unable to control it more than a few with VLC advanced parameters.
Better try gstreamer for reading from host.
You may also add insert-vui=1 into encoder properties, it might help VLC.

Also, you may try to use gst_parse_launch instead of your custom built pipeline and see if it makes differences that you may investigate if any.

However, as it seems working fine with lower resolution, you may also try to increase the max socket buffer size by setting udpsink property buffer-size=33554432, but that’s just one possible cause (your resolution and framerate are not so high for H26x, though, so I wouldn’t give a high probabilty to this…but easy to rule out).

Thanks for the suggestions. I tried both options and have them included below:

import argparse
import sys
import os

sys.path.append('../')

import gi

gi.require_version('Gst', '1.0')
gi.require_version('GstRtspServer', '1.0')
from gi.repository import GObject, Gst, GstRtspServer
from common.is_aarch_64 import is_aarch64
from common.bus_call import bus_call


def main(args):
    # Standard GStreamer initialization
    GObject.threads_init()
    Gst.init(None)

    # Create gstreamer elements
    # Create Pipeline element that will form a connection of other elements
    print("Creating Pipeline \n ")
    pipeline = Gst.Pipeline()
    if not pipeline:
        sys.stderr.write(" Unable to create Pipeline \n")

    # Source element for reading from the file
    print("Creating Source \n ")
    source = Gst.ElementFactory.make("v4l2src", "usb-cam-source")
    if not source:
        sys.stderr.write(" Unable to create Source \n")

    caps_v4l2src = Gst.ElementFactory.make("capsfilter", "v4l2src_caps")
    if not caps_v4l2src:
        sys.stderr.write(" Unable to create v4l2src capsfilter \n")

    print("Creating Video Converter \n")

    # Adding videoconvert -> nvvideoconvert as not all
    # raw formats are supported by nvvideoconvert;
    # Say YUYV is unsupported - which is the common
    # raw format for many logi usb cams
    # In case we have a camera with raw format supported in
    # nvvideoconvert, GStreamer plugins' capability negotiation
    # shall be intelligent enough to reduce compute by
    # videoconvert doing passthrough

    # videoconvert to make sure a superset of raw formats are supported
    vidconvsrc = Gst.ElementFactory.make("videoconvert", "convertor_src1")
    if not vidconvsrc:
        sys.stderr.write(" Unable to create videoconvert \n")

    nvvidconvsrc = Gst.ElementFactory.make("nvvideoconvert", "convertor_src2")
    if not nvvidconvsrc:
        sys.stderr.write(" Unable to create Nvvideoconvert \n")

    caps_vidconvsrc = Gst.ElementFactory.make("capsfilter", "nvmm_caps")
    if not caps_vidconvsrc:
        sys.stderr.write(" Unable to create capsfilter \n")

    # Make the encoder
    if codec == "H264":
        encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder")
        print("Creating H264 Encoder")
    elif codec == "H265":
        encoder = Gst.ElementFactory.make("nvv4l2h265enc", "encoder")
        print("Creating H265 Encoder")
    if not encoder:
        sys.stderr.write(" Unable to create encoder")
    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)
        encoder.set_property('maxperf-enable', 1)
        encoder.set_property('insert-vui', 1)

    # Make the payload-encode video into RTP packets
    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")

    # 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)

    print("Playing cam %s " % camera_path)
    caps_v4l2src.set_property('caps', Gst.Caps.from_string("video/x-raw, framerate=30/1"))
    source.set_property('device', camera_path)

    print("Adding elements to Pipeline \n")
    pipeline.add(source)
    pipeline.add(caps_v4l2src)
    pipeline.add(vidconvsrc)
    pipeline.add(nvvidconvsrc)
    pipeline.add(caps_vidconvsrc)
    pipeline.add(encoder)
    pipeline.add(rtppay)
    pipeline.add(sink)

    # Link the elements together:
    # file-source -> h264-parser -> nvh264-decoder ->
    # caps -> encoder -> rtppay -> udpsink

    print("Linking elements in the Pipeline \n")
    source.link(caps_v4l2src)
    caps_v4l2src.link(vidconvsrc)
    vidconvsrc.link(nvvidconvsrc)
    nvvidconvsrc.link(caps_vidconvsrc)
    caps_vidconvsrc.link(encoder)
    encoder.link(rtppay)
    rtppay.link(sink)

    # create an event loop and feed gstreamer bus messages to it
    loop = GObject.MainLoop()
    bus = pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect("message", bus_call, loop)

    # Start streaming
    rtsp_port_num = int(os.environ.get('RTSP_SERVER_PORT_NUMBER', '8554'))
    rtsp_stream_end = os.environ.get('RTSP_SERVER_STREAM_END', "/rtsp/camera")

    username = os.environ.get('CAMERA_USERNAME', 'user')
    password = os.environ.get('CAMERA_PASSWORD', "pass")

    server = GstRtspServer.RTSPServer.new()
    server.props.service = "%d" % rtsp_port_num

    auth = GstRtspServer.RTSPAuth()
    token = GstRtspServer.RTSPToken()
    token.set_string('media.factory.role', username)
    basic = GstRtspServer.RTSPAuth.make_basic(username, password)
    auth.add_basic(basic, token)
    server.set_auth(auth)

    server.attach(None)

    factory = GstRtspServer.RTSPMediaFactory.new()
    factory.set_launch(
        "( udpsrc name=pay0 port=%d buffer-size=33554432 caps=\"application/x-rtp, media=video, clock-rate=90000, "
        "encoding-name=(string)%s, payload=96 \" )" % (
            updsink_port_num, codec))
    factory.set_shared(True)
    permissions = GstRtspServer.RTSPPermissions()
    permissions.add_permission_for_role(username, "media.factory.access", True)
    permissions.add_permission_for_role(username, "media.factory.construct", True)
    factory.set_permissions(permissions)
    server.get_mount_points().add_factory(rtsp_stream_end, factory)

    print("\n *** DeepStream: Launched RTSP Streaming at rtsp://%s:%s@0.0.0.0:%d%s ***\n\n" %
          (username, password, rtsp_port_num, rtsp_stream_end))

    # start play back and listen to events
    print("Starting pipeline \n")
    pipeline.set_state(Gst.State.PLAYING)
    try:
        loop.run()
    except:
        pass
    # cleanup
    pipeline.set_state(Gst.State.NULL)


def parse_args():
    parser = argparse.ArgumentParser(description='RTSP Output Sample Application Help ')
    parser.add_argument("-c", "--codec", default="H264",
                        help="RTSP Streaming Codec H264/H265 , default=H264", choices=['H264', 'H265'])
    parser.add_argument("-b", "--bitrate", default=4000000,
                        help="Set the encoding bitrate ", type=int)
    parser.add_argument("-i", "--input-device", default="/dev/video0",
                        help="Set the USB Camera device", type=str)
    args = parser.parse_args()
    global codec
    global bitrate
    global camera_path
    camera_path = args.input_device
    codec = args.codec
    bitrate = args.bitrate
    return 0


if __name__ == '__main__':
    parse_args()
    sys.exit(main(sys.argv))

The same problem persists unfortunately. I’ve used VLC with success in the past with larger streams (~4k) with no issue on my laptop so I would expect this to at least work. VLC is giving only a single frame after ~90 seocnds of buffering, then still being frozen… very bizarre.

You may try removing h26xparse after the encoder…the h26x stream should be already parsed with nvv4l2h26xenc.
Not sure at all, but that would be my last suggestion for now.
Someone better skilled with this code may better advise.

I have continued to debug further. I have tried different tweaks to the pipeline (ie. adding queues). I have also tried another 1080p USB video camera and videotestsrc. Both still have the terrible buffering time then only one frame sent. I then tested by saving the endcoded video to a file. The file produced seemed correct (ie video was normal and no frame drops). I then tested this container/pipeline on my Dell Laptop with a RTX2080. The same long buffer/freeze occurs (both in VLC and a direct gstreamer connection as suggested by @Honey_Patouceul)

From this testing I am thinking that it is something to do with the RTSP Server plugin. I was using the setup from deepstream_python_apps/apps/deepstream-test1-rtsp-out at master · NVIDIA-AI-IOT/deepstream_python_apps · GitHub

Any help here would be greatly appreciated!

I don’t have a Nano for trying, I only can check on AGX Xavier. You may boost your Nano with:

sudo nvpmodel -m 0
sudo jetson_clocks

I’d suggest to build test-launch example. You can find details in Nano FAQ with question:
Q: Is there any example of running RTSP streaming?

I’m using this command for streaming nvarguscamera:

./test-launch "nvarguscamerasrc do-timestamp=1 ! video/x-raw(memory:NVMM), width=1920, height=1080, framerate=30/1, format=NV12 ! nvv4l2h264enc insert-vui=true insert-sps-pps=1 insert-aud=1 maxperf-enable=1 ! h264parse ! video/x-h264, stream-format=byte-stream ! rtph264pay name=pay0 pt=96 "

Using VLC for reading from Jetson itself has seconds of delay.
However using this gstreamer pipeline I see much less latency:

gst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/test latency=0 ! application/x-rtp,media=video,encoding-name=H264 ! rtph264depay ! h264parse ! nvv4l2decoder ! nvvidconv ! xvimagesink

For remote reading, you may increase the latency such as latency=500.

@Honey_Patouceul thank you for the suggestion. I have tried the following pipeline:

./test-launch "v4l2src device=/dev/video0 do-timestamp=1 ! video/x-raw,interlace-mode=(string)progressive,width=1280,height=960 ! videoconvert !  nvvidconv ! nvv4l2h264enc ! h264parse ! rtph264pay name=pay0 pt=96" 

The same behaviour as before persists (black for several seconds, single frame, then frozen). When I change the H/W to 480p then it streams with no issue.

You’d better use insert-vui=1 in encoder properties.

Ruling out your camera and USB issues, does the following work ?

./test-launch "videotestsrc do-timestamp=1 ! video/x-raw,format=YUY2,width=320,height=240 ! nvvidconv ! video/x-raw(memory:NVMM),format=NV12,width=1280,height=960,framerate=30/1 ! nvv4l2h264enc insert-vui=true insert-sps-pps=1 maxperf-enable=1! h264parse ! rtph264pay name=pay0 pt=96"

and receiving on same jetson with gstreamer as mentioned above.

If the above works, for better advice you may post the output of:

lsusb -t
v4l2-ctl -d0 --list-formats-ext

I tested the pipeline you provided above and it works without issue. So it must not be the RTSP server side?

Here is the requested output:

$ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=tegra-xusb/4p, 5000M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=tegra-xusb/5p, 480M
    |__ Port 2: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 2: Dev 4, If 0, Class=Vendor Specific Class, Driver=, 12M
        |__ Port 3: Dev 8, If 1, Class=Video, Driver=uvcvideo, 480M
        |__ Port 3: Dev 8, If 0, Class=Video, Driver=uvcvideo, 480M
        |__ Port 4: Dev 5, If 0, Class=Audio, Driver=snd-usb-audio, 12M
        |__ Port 4: Dev 5, If 1, Class=Audio, Driver=snd-usb-audio, 12M
 v4l2-ctl -d0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.033s (30.000 fps)

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.033s (30.000 fps)

You may first try to read your camera and just display before going into encoding and streaming:

# Raw mode YUYV 4:2:2
gst-launch-1.0 -v v4l2src device=/dev/video ! video/x-raw,format=YUY2,width=1280,height=960,framerate=30/1 ! xvimagesink

# or MJPG mode
gst-launch-1.0 -v v4l2src device=/dev/video ! image/jpeg,format=MJPG,width=1280,height=960,framerate=30/1 ! nvv4l2decoder mjpeg=1 ! nvvidconv ! xvimagesink

As your camera is USB2 only, may be highest resolution in raw mode fails… MJPG may be lighter.
You may also try adding io-mode=2 into v4l2src properties.

1 Like

@Honey_Patouceul thank you for your help. This pipeline worked:

./test-launch "v4l2src do-timestamp=1 ! image/jpeg,format=MJPG,width=1280,height=960 ! nvv4l2decoder mjpeg=1 ! nvvidconv ! video/x-raw(memory:NVMM),format=NV12,width=1280,height=960,framerate=30/1 ! nvv4l2h264enc insert-vui=true insert-sps-pps=1 maxperf-enable=1 ! h264parse ! rtph264pay name=pay0 pt=96"

So I have modified the code to be the following:

import argparse
import sys
import os

sys.path.append('../')

import gi

gi.require_version('Gst', '1.0')
gi.require_version('GstRtspServer', '1.0')
from gi.repository import GObject, Gst, GstRtspServer
from common.is_aarch_64 import is_aarch64
from common.bus_call import bus_call


def main(args):
    input_framerate = 30
    input_video_height = 960
    input_video_width = 1280

    output_framerate = 30
    output_video_height = 960
    output_video_width = 1280

    # Standard GStreamer initialization
    GObject.threads_init()
    Gst.init(None)

    Gst.debug_set_active(True)
    Gst.debug_set_default_threshold(3)

    # Create gstreamer elements
    # Create Pipeline element that will form a connection of other elements
    print("Creating Pipeline \n ")
    pipeline = Gst.Pipeline()
    if not pipeline:
        sys.stderr.write(" Unable to create Pipeline \n")

    # Source element for reading from the file
    print("Creating Source \n ")
    source = Gst.ElementFactory.make("v4l2src", "usb-cam-source")
    if not source:
        sys.stderr.write(" Unable to create Source \n")

    source.set_property('device', camera_path)
    source.set_property("do-timestamp", 1)

    caps_v4l2src = Gst.ElementFactory.make("capsfilter", "v4l2src_caps")
    if not caps_v4l2src:
        sys.stderr.write(" Unable to create v4l2src capsfilter \n")

    nvv4l2decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2decoder")
    if not nvv4l2decoder:
        sys.stderr.write(" Unable to create nvv4l2decoder \n")

    nvv4l2decoder.set_property("mjpeg", 1)
    if is_aarch64():
        nvv4l2decoder.set_property('enable-max-performance', 1)

    print("Creating Video Converter \n")

    nvvidconvsrc = Gst.ElementFactory.make("nvvideoconvert", "convertor_src2")
    if not nvvidconvsrc:
        sys.stderr.write(" Unable to create Nvvideoconvert \n")
    caps_v4l2src.set_property('caps', Gst.Caps.from_string("image/jpeg,format=MJPG, interlace-mode=(string)progressive,"
                                                           f"width={input_video_width},height={input_video_height},"
                                                           f"framerate={input_framerate}/1"))

    caps_vidconvsrc = Gst.ElementFactory.make("capsfilter", "nvmm_caps")
    if not caps_vidconvsrc:
        sys.stderr.write(" Unable to create capsfilter \n")

    caps_vidconvsrc.set_property("caps", Gst.Caps.from_string("video/x-raw(memory:NVMM), format=NV12"
                                                              f"width={output_video_width},height={output_video_height},"
                                                              f"framerate={output_framerate}/1"))

    # Make the encoder
    if codec == "H264":
        encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder")
        print("Creating H264 Encoder")
    elif codec == "H265":
        encoder = Gst.ElementFactory.make("nvv4l2h265enc", "encoder")
        print("Creating H265 Encoder")
    if not encoder:
        sys.stderr.write(" Unable to create encoder")
    encoder.set_property('bitrate', bitrate)
    if is_aarch64():
        encoder.set_property('preset-level', 1)
        encoder.set_property('insert-sps-pps', 1)
        encoder.set_property('maxperf-enable', 1)
        encoder.set_property('insert-vui', 1)
        encoder.set_property('insert-sps-pps', 1)
        encoder.set_property('poc-type', 2)

    # Make the payload-encode video into RTP packets
    if codec == "H264":
        parser = Gst.ElementFactory.make("h264parse", "h264parse")
        print("Creating H264 parser")
    elif codec == "H265":
        parser = Gst.ElementFactory.make("h265parse", "h265parse")
        print("Creating H265 parser")
    if not parser:
        sys.stderr.write(" Unable to create parser")

    parser_caps = Gst.ElementFactory.make("capsfilter", "parser_caps")
    if not parser_caps:
        sys.stderr.write(" Unable to create parser_caps \n")

    # Make the payload-encode video into RTP packets
    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")
    rtppay.set_property("name", "pay0")
    rtppay.set_property("config-interval", 10)
    rtppay.set_property("pt", 127)

    # 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', '127.0.0.1')
    sink.set_property('port', updsink_port_num)
    sink.set_property('async', False)
    sink.set_property('sync', 1)

    print("Playing cam %s " % camera_path)

    print("Adding elements to Pipeline \n")
    pipeline.add(source)
    pipeline.add(caps_v4l2src)
    pipeline.add(nvv4l2decoder)
    pipeline.add(nvvidconvsrc)
    pipeline.add(caps_vidconvsrc)
    pipeline.add(encoder)
    pipeline.add(rtppay)
    pipeline.add(sink)

    print("Linking elements in the Pipeline \n")
    source.link(nvv4l2decoder)
    nvv4l2decoder.link(nvvidconvsrc)
    nvvidconvsrc.link(caps_vidconvsrc)
    caps_vidconvsrc.link(encoder)
    encoder.link(rtppay)
    rtppay.link(sink)

    # create an event loop and feed gstreamer bus messages to it
    loop = GObject.MainLoop()
    bus = pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect("message", bus_call, loop)

    # Start streaming
    rtsp_port_num = int(os.environ.get('RTSP_SERVER_PORT_NUMBER', '8554'))
    rtsp_stream_end = os.environ.get('RTSP_SERVER_STREAM_END', "/rtsp/camera")

    username = os.environ.get('CAMERA_USERNAME', 'user')
    password = os.environ.get('CAMERA_PASSWORD', "pass")

    server = GstRtspServer.RTSPServer.new()
    server.props.service = "%d" % rtsp_port_num

    auth = GstRtspServer.RTSPAuth()
    token = GstRtspServer.RTSPToken()
    token.set_string('media.factory.role', username)
    basic = GstRtspServer.RTSPAuth.make_basic(username, password)
    auth.add_basic(basic, token)
    server.set_auth(auth)

    server.attach(None)

    factory = GstRtspServer.RTSPMediaFactory.new()
    factory.set_launch(
        "( udpsrc name=pay0 port=%d buffer-size=52428 caps=\"application/x-rtp, media=video, clock-rate=90000, "
        "encoding-name=(string)%s, payload=127 \" )" % (
            updsink_port_num, codec))
    factory.set_shared(True)
    permissions = GstRtspServer.RTSPPermissions()
    permissions.add_permission_for_role(username, "media.factory.access", True)
    permissions.add_permission_for_role(username, "media.factory.construct", True)
    factory.set_permissions(permissions)
    server.get_mount_points().add_factory(rtsp_stream_end, factory)

    print("\n *** DeepStream: Launched RTSP Streaming at rtsp://%s:%s@0.0.0.0:%d%s ***\n\n" %
          (username, password, rtsp_port_num, rtsp_stream_end))

    # start play back and listen to events
    print("Starting pipeline \n")
    pipeline.set_state(Gst.State.PLAYING)
    try:
        loop.run()
    except:
        pass
    # cleanup
    pipeline.set_state(Gst.State.NULL)


def parse_args():
    parser = argparse.ArgumentParser(description='RTSP Output Sample Application Help ')
    parser.add_argument("-c", "--codec", default="H264",
                        help="RTSP Streaming Codec H264/H265 , default=H264", choices=['H264', 'H265'])
    parser.add_argument("-b", "--bitrate", default=4000000,
                        help="Set the encoding bitrate ", type=int)
    parser.add_argument("-i", "--input-device", default="/dev/video0",
                        help="Set the USB Camera device", type=str)
    args = parser.parse_args()
    global codec
    global bitrate
    global camera_path
    camera_path = args.input_device
    codec = args.codec
    bitrate = args.bitrate
    return 0


if __name__ == '__main__':
    parse_args()
    sys.exit(main(sys.argv))

Thanks for your help!

Any bonus suggestions for lowering transmission latency on the server side?

Glad to see you’ve moved on.
I don’t think you can do much for latency on server side…Well there might be some encoding options, but someone else would better advise for these. Searching this forum you may find some tracks…

You would try to adjust latency on receiver side, trying to find the best value for latency property of rtspsrc (that may instanciate subplugin rtpjitterbuffer proving this feature). The best value depends on the network between your source and sink. Better try with 500 (ms) on a LAN and try decreasing checking what works fine for a quite long period.

1 Like