IP camera lag with Deepstream python binding

• Hardware Platform (Jetson / GPU) Jetson orin nano 8GB
• DeepStream Version 7.1.0
• JetPack Version (valid for Jetson only) 6.2
• TensorRT Version 10.3
• CUDA Driver/Runtime version 12.6
• Issue Type( questions, new requirements, bugs) Question
• How to reproduce the issue ? (This is for bugs. Including which sample app is using, the configuration files content, the command line used and other details for reproducing)
I’m using deepstream with python binding with pre-trained yolo model. Im using the Hikvision IP camera with 25 FPS. I’m using the sample app from python apps for multi rtsp-in-rtsp-out with some changes in the pipeline as the nvv4l2h264enc is not found in my gstreamer and so couldn’t use that in the app.

Changed:

nvvidconv_postosd.link(caps)
caps.link(encoder) 

To:

    nvvidconv_cpu = Gst.ElementFactory.make("videoconvert", "convertor_cpu")
    caps_cpu = Gst.ElementFactory.make("capsfilter", "filter_cpu_caps")
    caps_cpu.set_property("caps", Gst.Caps.from_string("video/x-raw, format=I420"))

    pipeline.add(nvvidconv_cpu)
    pipeline.add(caps_cpu)

    nvvidconv_postosd.link(nvvidconv_cpu)
    nvvidconv_cpu.link(caps_cpu)
    caps_cpu.link(encoder)

There are 2 issues i’m facing here

Issues:
1.Video lag - There is a drastic lag only in this rtsp-in-out. I have used 2 usb cameras without any lag in the same device.
2. CudaIllegalAddress - This occurs after some 2 mins of running the app. Even when i close all other apps its still the same case.

Deepstream-YOLO: GitHub - marcoslucianops/DeepStream-Yolo: NVIDIA DeepStream SDK 7.1 / 7.0 / 6.4 / 6.3 / 6.2 / 6.1.1 / 6.1 / 6.0.1 / 6.0 / 5.1 implementation for YOLO models

Model Conifg:
[property]
gpu-id=0
net-scale-factor=0.0039215697906911373
model-color-format=0
onnx-file=/home/infinity/Disk/deepstream-experiments/DeepStream-Yolo/v11_models/yolo11s.pt.onnx
model-engine-file=model_b1_gpu0_fp32.engine
labelfile-path=/home/infinity/Disk/deepstream-experiments/deepstream_python_apps/apps/custom_deepstream/labels.txt
batch-size=1
network-mode=2
num-detected-classes=80
interval=1
gie-unique-id=1
process-mode=1
network-type=0
cluster-mode=2
maintain-aspect-ratio=1
symmetric-padding=1
parse-bbox-func-name=NvDsInferParseYolo
custom-lib-path=../nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so
engine-create-func-name=NvDsInferYoloCudaEngineGet

[class-attrs-all]
nms-iou-threshold=0.50
pre-cluster-threshold=0.25
topk=300

Camera settings:

Can someone help me with my lag and CUDAIllegalMemoryAddress issues

  • Are you using the same sample code and same model, only the difference in the camera?
  • Could you try the yolo model from our deepstream_tools sample?
    I will try them and update here asap

  • I have used 2 usb cameras without any lag in the same device.
    No the sample code didn’t have one for usb cameras, so i changed something from the sample app called deepstream_demux_multi_in_multi_out.py with the help of chatgpt. But the model is same.

Multi usb/web camera config file:

import sys
sys.path.append('../')
import gi
import configparser
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
from gi.repository import GLib
from ctypes import *
import time
import sys
import math
import platform
from common.platform_info import PlatformInfo
from common.bus_call import bus_call
from common.FPS import GETFPS

import pyds

fps_streams={}

MAX_DISPLAY_LEN=64
PGIE_CLASS_ID_VEHICLE = 2
PGIE_CLASS_ID_BICYCLE = 1
PGIE_CLASS_ID_PERSON = 0
PGIE_CLASS_ID_ROADSIGN = 11
MUXER_OUTPUT_WIDTH=1344
MUXER_OUTPUT_HEIGHT=376
MUXER_BATCH_TIMEOUT_USEC=4000000
TILED_OUTPUT_WIDTH=1344
TILED_OUTPUT_HEIGHT=376 * 2
GST_CAPS_FEATURES_NVMM="memory:NVMM"
OSD_PROCESS_MODE= 0
OSD_DISPLAY_TEXT= 0
pgie_classes_str= ["Vehicle", "TwoWheeler", "Person","RoadSign"]

# tiler_sink_pad_buffer_probe  will extract metadata received on OSD sink pad
# and update params for drawing rectangle, object information etc.
def tiler_src_pad_buffer_probe(pad,info,u_data):
    frame_number=0
    num_rects=0
    gst_buffer = info.get_buffer()
    if not gst_buffer:
        print("Unable to get GstBuffer ")
        return

    # Retrieve batch metadata from the gst_buffer
    # Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
    # C address of gst_buffer as input, which is obtained with hash(gst_buffer)
    batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
    l_frame = batch_meta.frame_meta_list
    while l_frame is not None:
        try:
            # Note that l_frame.data needs a cast to pyds.NvDsFrameMeta
            # The casting is done by pyds.NvDsFrameMeta.cast()
            # The casting also keeps ownership of the underlying memory
            # in the C code, so the Python garbage collector will leave
            # it alone.
            frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
        except StopIteration:
            break

        '''
        print("Frame Number is ", frame_meta.frame_num)
        print("Source id is ", frame_meta.source_id)
        print("Batch id is ", frame_meta.batch_id)
        print("Source Frame Width ", frame_meta.source_frame_width)
        print("Source Frame Height ", frame_meta.source_frame_height)
        print("Num object meta ", frame_meta.num_obj_meta)
        '''
        frame_number=frame_meta.frame_num
        l_obj=frame_meta.obj_meta_list
        num_rects = frame_meta.num_obj_meta
        obj_counter = {i: 0 for i in range(80)}
        while l_obj is not None:
            try: 
                # Casting l_obj.data to pyds.NvDsObjectMeta
                obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
            except StopIteration:
                break
            obj_counter[obj_meta.class_id] += 1
            try: 
                l_obj=l_obj.next
            except StopIteration:
                break
        """display_meta=pyds.nvds_acquire_display_meta_from_pool(batch_meta)
        display_meta.num_labels = 1
        py_nvosd_text_params = display_meta.text_params[0]
        py_nvosd_text_params.display_text = "Frame Number={} Number of Objects={} Vehicle_count={} Person_count={}".format(frame_number, num_rects, vehicle_count, person)
        py_nvosd_text_params.x_offset = 10;
        py_nvosd_text_params.y_offset = 12;
        py_nvosd_text_params.font_params.font_name = "Serif"
        py_nvosd_text_params.font_params.font_size = 10
        py_nvosd_text_params.font_params.font_color.red = 1.0
        py_nvosd_text_params.font_params.font_color.green = 1.0
        py_nvosd_text_params.font_params.font_color.blue = 1.0
        py_nvosd_text_params.font_params.font_color.alpha = 1.0
        py_nvosd_text_params.set_bg_clr = 1
        py_nvosd_text_params.text_bg_clr.red = 0.0
        py_nvosd_text_params.text_bg_clr.green = 0.0
        py_nvosd_text_params.text_bg_clr.blue = 0.0
        py_nvosd_text_params.text_bg_clr.alpha = 1.0
        #print("Frame Number=", frame_number, "Number of Objects=",num_rects,"Vehicle_count=",vehicle_count,"Person_count=",person)
        pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)"""
        print("Frame Number=", frame_number, "Number of Objects=",num_rects,"Vehicle_count=",obj_counter[PGIE_CLASS_ID_VEHICLE],"Person_count=",obj_counter[PGIE_CLASS_ID_PERSON])

        # Get frame rate through this probe
        fps_streams["stream{0}".format(frame_meta.pad_index)].get_fps()
        try:
            l_frame=l_frame.next
        except StopIteration:
            break

    return Gst.PadProbeReturn.OK



def cb_newpad(decodebin, decoder_src_pad,data):
    print("In cb_newpad\n")
    caps=decoder_src_pad.get_current_caps()
    gststruct=caps.get_structure(0)
    gstname=gststruct.get_name()
    source_bin=data
    features=caps.get_features(0)

    # Need to check if the pad created by the decodebin is for video and not
    # audio.
    print("gstname=",gstname)
    if(gstname.find("video")!=-1):
        # Link the decodebin pad only if decodebin has picked nvidia
        # decoder plugin nvdec_*. We do this by checking if the pad caps contain
        # NVMM memory features.
        print("features=",features)
        if features.contains("memory:NVMM"):
            # Get the source bin ghost pad
            bin_ghost_pad=source_bin.get_static_pad("src")
            if not bin_ghost_pad.set_target(decoder_src_pad):
                sys.stderr.write("Failed to link decoder src pad to source bin ghost pad\n")
        else:
            sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")

def decodebin_child_added(child_proxy,Object,name,user_data):
    print("Decodebin child added:", name, "\n")
    if(name.find("decodebin") != -1):
        Object.connect("child-added",decodebin_child_added,user_data)   
    if(platform_info.is_platform_aarch64() and name.find("nvv4l2decoder") != -1):
        print("Seting bufapi_version\n")
        Object.set_property("bufapi-version",True)

def create_source_bin(index, uri):
    print(f"Creating source bin for index {index}, URI {uri}")

    bin_name = f"source-bin-{index:02d}"
    print(bin_name)
    nbin = Gst.Bin.new(bin_name)
    if not nbin:
        sys.stderr.write(" Unable to create source bin \n")
        return None

    print("Creating Source \n ")
    # Use unique names for elements within the bin by appending the index
    source = Gst.ElementFactory.make("v4l2src", f"usb-cam-source-{index}")
    if not source:
        sys.stderr.write(" Unable to create source \n")
        return None
    source.set_property('device', uri)

    # Common elements for the latter part of the source bin
    vidconvsrc = Gst.ElementFactory.make("videoconvert", f"convertor_src1-{index}")
    if not vidconvsrc:
        sys.stderr.write(f" Unable to create videoconvert for source {index}\n")
        return None

    nvvidconvsrc = Gst.ElementFactory.make("nvvideoconvert", f"convertor_src2-{index}")
    if not nvvidconvsrc:
        sys.stderr.write(f" Unable to create Nvvideoconvert for source {index}\n")
        return None

    caps_nvmm = Gst.ElementFactory.make("capsfilter", f"nvmm_caps-{index}")
    if not caps_nvmm:
        sys.stderr.write(f" Unable to create nvmm_caps capsfilter for source {index}\n")
        return None
    caps_nvmm.set_property('caps', Gst.Caps.from_string("video/x-raw(memory:NVMM)"))

    # Add common elements to the bin
    nbin.add(source)
    nbin.add(vidconvsrc)
    nbin.add(nvvidconvsrc)
    nbin.add(caps_nvmm)

    camera_width = 640
    camera_height = 480

    if uri == "/dev/video0":  # Camera with 25 FPS, expected to be video/x-raw
        framerate_str = "25/1"
        
    elif uri == "/dev/video2":  # Camera with 30 FPS, expected to be image/jpeg
        framerate_str = "30/1"

    else:
        sys.stderr.write(f"Error: URI {uri} is not explicitly handled. Configure appropriately.\n")
        return None # Or implement a default behavior
    caps_v4l2src = Gst.ElementFactory.make("capsfilter", f"v4l2src_caps-{index}")
    if not caps_v4l2src:
        sys.stderr.write(f"Could not create caps_v4l2src for {uri}\n")
        return None
    
    raw_caps_str = f"video/x-raw,width={camera_width},height={camera_height},framerate={framerate_str}"
    caps_v4l2src.set_property('caps', Gst.Caps.from_string(raw_caps_str))
    nbin.add(caps_v4l2src)

    # Link: source -> caps_v4l2src -> vidconvsrc -> nvvidconvsrc -> caps_nvmm
    if not source.link(caps_v4l2src):
        sys.stderr.write(f"Error: Failed to link source to caps_v4l2src for {uri}\n")
        return None
    if not caps_v4l2src.link(vidconvsrc):
        sys.stderr.write(f"Error: Failed to link caps_v4l2src to vidconvsrc for {uri}\n")
        return None        

    # Common linking for the rest of the pipeline
    if not vidconvsrc.link(nvvidconvsrc):
        sys.stderr.write(f"Error: Failed to link vidconvsrc to nvvidconvsrc for {uri}\n")
        return None
    if not nvvidconvsrc.link(caps_nvmm):
        sys.stderr.write(f"Error: Failed to link nvvidconvsrc to caps_nvmm for {uri}\n")
        return None
        
    # Create the ghost pad for the bin
    srcpad = caps_nvmm.get_static_pad("src")
    bin_pad = nbin.add_pad(Gst.GhostPad.new("src", srcpad))
    if not bin_pad:
        sys.stderr.write(f" Failed to add ghost pad in source bin for {uri}\n")
        return None
        
    return nbin

#For reference here is the code for setting up the pipelines and the linking for the app:

def main(args):
    # Check input arguments
    if len(args) < 2:
        sys.stderr.write("usage: %s <uri1> [uri2] ... [uriN]\n" % args[0])
        sys.exit(1)

    for i in range(0,len(args)-1):
        fps_streams["stream{0}".format(i)]=GETFPS(i)
        print(GETFPS(i))
    number_sources=len(args)-1

    # Standard GStreamer initialization
    GObject.threads_init()
    Gst.init(None)
    platform_info = PlatformInfo()
    # Create gstreamer elements */
    # Create Pipeline element that will form a connection of other elements
    print("Creating Pipeline \n ")
    pipeline = Gst.Pipeline()
    is_live = False

    if not pipeline:
        sys.stderr.write(" Unable to create Pipeline \n")
    print("Creating streamux \n ")

    # Create nvstreammux instance to form batches from one or more sources.
    streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
    if not streammux:
        sys.stderr.write(" Unable to create NvStreamMux \n")

    pipeline.add(streammux)
    for i in range(number_sources):
        print("Creating source_bin ",i," \n ")
        usb_cam=args[i+1]
        # if uri_name.find("rtsp://") == 0 :
        is_live = True
        source_bin=create_source_bin(i, usb_cam)
        if not source_bin:
            sys.stderr.write("Unable to create source bin \n")
        pipeline.add(source_bin)
        padname="sink_%u" %i
        sinkpad= streammux.get_request_pad(padname) 
        if not sinkpad:
            sys.stderr.write("Unable to create sink pad bin \n")
        srcpad=source_bin.get_static_pad("src")
        if not srcpad:
            sys.stderr.write("Unable to create src pad bin \n")
        srcpad.link(sinkpad)
    queue1=Gst.ElementFactory.make("queue","queue1")
    queue2=Gst.ElementFactory.make("queue","queue2")
    queue3=Gst.ElementFactory.make("queue","queue3")
    queue4=Gst.ElementFactory.make("queue","queue4")
    queue5=Gst.ElementFactory.make("queue","queue5")
    pipeline.add(queue1)
    pipeline.add(queue2)
    pipeline.add(queue3)
    pipeline.add(queue4)
    pipeline.add(queue5)
    print("Creating Pgie \n ")
    pgie = Gst.ElementFactory.make("nvinfer", "p376rimary-inference")
    if not pgie:
        sys.stderr.write(" Unable to create pgie \n")
    print("Creating tiler \n ")
    tiler=Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
    if not tiler:
        sys.stderr.write(" Unable to create tiler \n")
    print("Creating nvvidconv \n ")
    nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
    if not nvvidconv:
        sys.stderr.write(" Unable to create nvvidconv \n")
    print("Creating nvosd \n ")
    nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
    if not nvosd:
        sys.stderr.write(" Unable to create nvosd \n")
    nvosd.set_property('process-mode',OSD_PROCESS_MODE)
    nvosd.set_property('display-text',OSD_DISPLAY_TEXT)
    if platform_info.is_platform_aarch64():
        print("Creating transform \n ")
        transform=Gst.ElementFactory.make("nvegltransform", "nvegl-transform")
        if not transform:
            sys.stderr.write(" Unable to create transform \n")

    print("Creating EGLSink \n")
    sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
    #sink = Gst.ElementFactory.make("nvoverlaysink", "nvvideo-renderer")
    sink.set_property('sync', 0)
    if not sink:
        sys.stderr.write(" Unable to create egl sink \n")

    if is_live:
        print("Atleast one of the sources is live")
        streammux.set_property('live-source', 1)

    streammux.set_property('width', 1344)
    streammux.set_property('height', 376)
    streammux.set_property('batch-size', number_sources)
    streammux.set_property('batched-push-timeout', 4000000)
    pgie.set_property('config-file-path', "multi_usb.txt")
    pgie_batch_size=pgie.get_property("batch-size")
    if(pgie_batch_size != number_sources):
        print("WARNING: Overriding infer-config batch-size",pgie_batch_size," with number of sources ", number_sources," \n")
        pgie.set_property("batch-size",number_sources)
    #tiler_rows=int(math.sqrt(number_sources))
    tiler_rows=2
    #tiler_columns=int(math.ceil((1.0*number_sources)/tiler_rows))
    tiler_columns=1
    tiler.set_property("rows",tiler_rows)
    tiler.set_property("columns",tiler_columns)
    tiler.set_property("width", TILED_OUTPUT_WIDTH)
    tiler.set_property("height", TILED_OUTPUT_HEIGHT)
    sink.set_property("qos",0)

    print("Adding elements to Pipeline \n")
    pipeline.add(pgie)
    pipeline.add(tiler)
    pipeline.add(nvvidconv)
    pipeline.add(nvosd)
    if platform_info.is_platform_aarch64():
        pipeline.add(transform)
    pipeline.add(sink)

    print("Linking elements in the Pipeline \n")
    streammux.link(queue1)
    queue1.link(pgie)
    pgie.link(queue2)
    queue2.link(tiler)
    tiler.link(queue3)
    queue3.link(nvvidconv)
    nvvidconv.link(queue4)
    queue4.link(nvosd)
    if platform_info.is_platform_aarch64():
        nvosd.link(queue5)
        queue5.link(transform)
        transform.link(sink)
    else:
        nvosd.link(queue5)
        queue5.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)
    tiler_src_pad=pgie.get_static_pad("src")
    if not tiler_src_pad:
        sys.stderr.write(" Unable to get src pad \n")
    else:
        tiler_src_pad.add_probe(Gst.PadProbeType.BUFFER, tiler_src_pad_buffer_probe, 0)

    # List the sources
    print("Now playing...")
    for i, source in enumerate(args):
        if (i != 0):
            print(i, ": ", source)

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

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

The method you used of comparison is not comparable. The source plugin and sink plugin you used are all different.
You can try the following several methods below to narrowdown.

  1. Try the yolo model from our deepstream_tools sample

  2. Modify our rtsp-in-rtsp-out sample to rtsp-in-nv3dsink, please refer to our sample to learn how to use nv3dsink

1.Deepstream_tool model - I have modified the yolo9 from the deepstream_tools sample using the following code

from ultralytics import YOLO
# Load a model
model = YOLO("yolov9s.pt")  # load a pretrained model (recommended for training)
path = model.export(format="onnx")  # export the model to ONNX format

python append_transpose_yolov8_v9.py

2. nv3sink - Also i have modified the sample rtsp-in-rtsp-out to rtsp-in-nv3dsink just by replacing the UDP sink with nv3dsink.

From:

sink = Gst.ElementFactory.make("udpsink", "udpsink")
    if not sink:
        sys.stderr.write(" Unable to create udpsink")

To:

if platform_info.is_integrated_gpu():
        print("Creating nv3dsink \n")
        sink = Gst.ElementFactory.make("nv3dsink", "nv3d-sink")
        if not sink:
            sys.stderr.write(" Unable to create nv3dsink \n")
    else:
        if platform_info.is_platform_aarch64():
            print("Creating nv3dsink \n")
            sink = Gst.ElementFactory.make("nv3dsink", "nv3d-sink")
        else:
            print("Creating EGLSink \n")
            sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
        if not sink:
            sys.stderr.write(" Unable to create egl sink \n")

The lag has reduced drastically from 15 seconds to 5 seconds. But still i have the 5 seconds lag with the yolov9 from deepstream_tools and the cudaErrorIllegalAddress issue still presists.

Issues:

1. There is some lag upto 5 seconds.
2. cudaErrorIllegalAddress issue comes after running the app for 4 mins.
3. The detection is not consistent. There is some flickering in the detection.

I found out the reason for the detection bbox flickering. I had added the interval and drop-frame-rate in the model config with 5 as value (Note: I haven’t added the tracker only detection is added in the model config). After commenting them, the flickering is resolved and the detection box is consistent

Could you describe the specific method you used to obtain this lag?

This is because there are compatibility issues between DeepStream 7.1 and Jetpack 6.2, topic 320358. You can try to use Jetpack 6.1.

There is no update from you for a period, assuming this is not an issue anymore. Hence we are closing this topic. If need further support, please open a new one. Thanks

I just counted manually. There are no specific methods with code or programs

1.It is possible that there is cached data in your camera. You need to investigate this.
2. You can also try to set interval=10 to the nvinfer plugin to check the lag. If this can reduce the lag, it indicates that your machine has a performance bottleneck.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.