X264enc in A100

Please provide complete information as applicable to your setup.

• Hardware Platform (Jetson / GPU) A100
• DeepStream Version 9.0
• JetPack Version (valid for Jetson only)
• TensorRT Version deepstream installation
• NVIDIA GPU Driver Version (valid for GPU only) 590
• Issue Type( questions, new requirements, bugs)
• 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)
• Requirement details( This is for new requirement. Including the module name-for which plugin or for which sample application, the function description)

i use x264enc in my a100 pipeline because it doesn’t have any nvEnc but i think i get backpresure because of that, can you help me in this?
this is the bin i used in pipeline for output(please don’t ask to give you other section of pipeline and i link and add them to other sections and itself to )
the problem is i think i get some kind of back presure from this bin and also it has some freezing in output
please help me to fix this

            outputUserInfoMessage(f"x264enc selected for webrtc with gpuId={gpu_id}", username)
            
            # -----------------------------------------------------------------------------
            # queue0 — RAW frames. This is the ONLY queue allowed to leak.
            # Dropping a raw frame costs nothing downstream. Dropping an *encoded* frame
            # breaks the reference chain and corrupts every frame until the next IDR.
            # -----------------------------------------------------------------------------
            queue0, unique_id = ElementMaker().make_element(
                element_name="queue", node=node, username=username)
            queue0.set_property("max-size-buffers", 5)
            queue0.set_property("max-size-time", 0)
            queue0.set_property("max-size-bytes", 0)
            queue0.set_property("leaky", 2)                      # downstream
            elements.append(queue0)
            
            # -----------------------------------------------------------------------------
            # NVMM -> system memory.
            # PINNED, not UNIFIED: x264enc reads this on the CPU. On a PCIe dGPU (A100)
            # unified memory demand-pages device->host on every CPU touch, which is a
            # per-frame stall. Pinned gives one bulk DMA instead.
            # -----------------------------------------------------------------------------
            nvvidconv_postosd, unique_id = ElementMaker().make_element(
                element_name="nvvideoconvert", node=node, username=username)
            nvvidconv_postosd.set_property("nvbuf-memory-type", int(pyds.NVBUF_MEM_CUDA_PINNED))
            nvvidconv_postosd.set_property(Parameters._PrivateProperties.GPU_ID.value, gpu_id)
            nvvidconv_postosd.set_property("output-buffers", 8)
            elements.append(nvvidconv_postosd)
            
            capsfilter, unique_id = ElementMaker().make_element(
                element_name="capsfilter", node=node, username=username)
            capsfilter.set_property("caps", Gst.Caps.from_string("video/x-raw, format=I420,  width=1920, height=1080,framerate=30/1"))
            elements.append(capsfilter)
            
            # -----------------------------------------------------------------------------
            # x264enc
            # -----------------------------------------------------------------------------
            x264enc, unique_id = ElementMaker().make_element(
                element_name="x264enc", node=node, username=username)
            if not x264enc:
                sys.stderr.write(" Unable to create x264enc \n")
            
            len_inputs = len(find_elements_by_subtype(
                graph_copy, [NodeOperation.VIDEO_DECODE, NodeOperation.MULTI_IMAGE_DECODE]))
            
            # NOTE: x264enc "bitrate" is in kbit/s. vp8enc "target-bitrate" was bit/s.
            # Passing 4_000_000 here would request 4 Gbit/s.
            bitrate_kbps = min(4000, 800 + (200 * len_inputs))   # 16 inputs -> 4000 kbps
            
            x264enc.set_property("bitrate", bitrate_kbps)
            x264enc.set_property("speed-preset", 3)        # veryfast
            x264enc.set_property("tune", 0x00000004)              # zerolatency
            x264enc.set_property("pass", 0)                # CBR
            # vbv-buf-capacity is THE critical rate-control setting. Default 600 ms lets a
            # frame overshoot enormously before RC reacts — that is what made VP8 keyframes
            # too big to survive transit. 200 ms bounds the burst.
            x264enc.set_property("vbv-buf-capacity", 200)
            x264enc.set_property("key-int-max", 30)        # 2 s ceiling; PLI drives the rest
            x264enc.set_property("bframes", 0)             # B-frames add reorder latency
            x264enc.set_property("b-adapt", False)
            x264enc.set_property("cabac", False)           # MUST be False for constrained-baseline
            x264enc.set_property("sliced-threads", True)   # no frame-level threading latency
            x264enc.set_property("sync-lookahead", 0)
            x264enc.set_property("rc-lookahead", 0)
            x264enc.set_property("threads", 8)
            x264enc.set_property("aud", False)
            x264enc.set_property("byte-stream", True)
            x264enc.set_property("insert-vui", True)


            # Enable ONLY if corruption still spreads across whole frames (Test 3).
            # Caps each slice to one RTP packet, so a lost packet damages a band not a frame.
            # Verified on 1.24: coexists with sliced-threads=True, no warning emitted.
            
            elements.append(x264enc)
            
            # Profile must match cabac=False. If these disagree, caps negotiation fails.
            enc_capsfilter, unique_id = ElementMaker().make_element(
                element_name="capsfilter", node=node, username=username)
            enc_capsfilter.set_property("caps", Gst.Caps.from_string(
                "video/x-h264,profile=constrained-baseline,stream-format=byte-stream,alignment=au"))
            elements.append(enc_capsfilter)
            
            # -----------------------------------------------------------------------------
            # h264parse — config-interval=-1 repeats SPS/PPS with every IDR.
            # Without this, losing the one initial parameter set means Chrome can never
            # decode again, no matter how many PLIs it sends. Permanent black stream.
            # -----------------------------------------------------------------------------
            h264parse, unique_id = ElementMaker().make_element(
                element_name="h264parse", node=node, username=username)
            h264parse.set_property("config-interval", -1)
            elements.append(h264parse)
            
            # -----------------------------------------------------------------------------
            # queue1 — ENCODED frames. NOT leaky, time-bounded.
            # When it fills: x264enc blocks -> queue0 leaks raw frames. FPS dips under
            # pressure, bitstream stays intact. This is the correct backpressure cascade.
            # It only works because CBR + vbv-buf-capacity bound the output rate.
            # -----------------------------------------------------------------------------
            # queue1, unique_id = ElementMaker().make_element(
            #     element_name="queue", node=node, username=username)
            # queue1.set_property("max-size-buffers", 10)
            # queue1.set_property("max-size-bytes", 0)
            # queue1.set_property("max-size-time", 0)   # 200 ms
            # # queue1.set_property("leaky", 2)                     # no leak
            # elements.append(queue1)
            
            # -----------------------------------------------------------------------------
            # rtph264pay
            # -----------------------------------------------------------------------------
            rtph264pay, unique_id = ElementMaker().make_element(
                element_name="rtph264pay", node=node, username=username)
            if not rtph264pay:
                sys.stderr.write(" Unable to create rtph264pay \n")
            rtph264pay.set_property("config-interval", -1)   # SPS/PPS before every IDR
            rtph264pay.set_property("aggregate-mode", 1)     # zero-latency STAP-A
            rtph264pay.set_property("mtu", 1200)
            rtph264pay.set_property("pt", 96)
            rtph264pay.set_property("auto-header-extension", True)
            elements.append(rtph264pay)
            
            rtp_capsfilter, unique_id = ElementMaker().make_element(
                element_name="capsfilter", node=node, username=username)
            rtp_capsfilter.set_property("caps", Gst.Caps.from_string(
                "application/x-rtp,media=(string)video,encoding-name=(string)H264,payload=(int)96,"
                "clock-rate=(int)90000,rtcp-fb-nack=(boolean)true,rtcp-fb-nack-pli=(boolean)true,"
                "rtcp-fb-ccm-fir=(boolean)true,rtcp-fb-transport-cc=(boolean)true"))
            elements.append(rtp_capsfilter)
            
            # -----------------------------------------------------------------------------
            # webrtcbin
            # -----------------------------------------------------------------------------
            webrtcbin, unique_id = ElementMaker().make_element(
                element_name="webrtcbin", node=node, username=username)
            if not webrtcbin:
                sys.stderr.write(" Unable to create webrtcbin \n")
            webrtcbin.set_property("bundle-policy", "max-bundle")
            webrtcbin.set_property(
                "turn-server",
                f"hidden")
            elements.append(webrtcbin)

this is both ice and offer

    def on_negotiation_needed(self, element,is_h264):
        outputUserInfoMessage(f"choosing negotiation based on is_h264 which is {is_h264}")
        promise = Gst.Promise.new_with_change_func(self.on_h264_offer_created, element, None)
        element.emit('create-offer', None, promise)


    def send_ice_candidate_message(self, _, mlineindex, candidate):
        icemsg = json.dumps({'ice': {'candidate': candidate, 'sdpMLineIndex': mlineindex}})
        
        # چک کن کانکشن هنوز باز هست
        if self.conn is None or self.conn.closed:
            outputUserErrorMessage(
                f"Cannot send ICE: connection is closed",
                user=self.user_name
            )
            return  # ← به جای crash، gracefully رد کن
        
        loop = asyncio.new_event_loop()
        try:
            outputUserInfoMessage(f"sending ice: {icemsg}", user=self.user_name)
            loop.run_until_complete(self.conn.send(icemsg))
        except Exception as e:
            outputUserErrorMessage(f"Failed to send ICE candidate: {e}", user=self.user_name)
        finally:
            loop.close()
    def on_h264_offer_created(self, promise, _, __):
        promise.wait()
        reply = promise.get_reply()
        offer = reply['offer']
        sdp = offer.sdp.as_text()

        def fix_h264_fmtp(sdp_text):
            import re
        
            h264_pts = set()
            lines = sdp_text.splitlines()
            for line in lines:
                m = re.match(r'a=rtpmap:(\d+)\s+H264/90000', line, re.IGNORECASE)
                if m:
                    h264_pts.add(m.group(1))
        
            out = []
            for line in lines:
                m = re.match(r'a=fmtp:(\d+)\s+(.*)$', line)
                if m and m.group(1) in h264_pts:
                    params = {}
                    for p in m.group(2).split(';'):
                        p = p.strip()
                        if '=' in p:
                            k, v = p.split('=', 1)     # first '=' only
                            params[k.strip()] = v.strip()
        
                    # DO NOT overwrite profile-level-id. rtph264pay derives it from the
                    # actual SPS. Verified on GStreamer 1.24 with the config above, it
                    # emits 42c028 — hardcoding 42e028 would advertise a stream that
                    # differs from what is really being sent.
                    # Only supply a fallback if the payloader gave us nothing.
                    params.setdefault('profile-level-id', '42c028')
                    params.setdefault('packetization-mode', '1')
                    params['level-asymmetry-allowed'] = '1'
        
                    line = f"a=fmtp:{m.group(1)} " + ';'.join(
                        f"{k}={v}" for k, v in params.items())
                out.append(line)
        
            return '\r\n'.join(out) + '\r\n'
 

        sdp = fix_h264_fmtp(sdp)
        
        # Parse SDP to GstSdp.SDPMessage
        result, sdpmsg = GstSdp.SDPMessage.new()

        GstSdp.sdp_message_parse_buffer(sdp.encode(), sdpmsg)
        # Create modified WebRTCSessionDescription
        modified_offer = GstWebRTC.WebRTCSessionDescription.new(offer.type, sdpmsg)
        
        promise = Gst.Promise.new()
        self.webrtc.emit('set-local-description', modified_offer, promise)
        self.send_sdp_offer(modified_offer)

Do you mean the bin is slower than you expected? What have you observed with this bin working?

What kind of freezing? How do you know the output freezing? Freeze forever or just freeze for some time?

yes it’s too slow

it sometimes get freezing

Can I suppose your concern is just for the video is not smooth in the web browser? Is there any clue to show that the unsmooth is caused by DeepStream elements?

the problem is, it have a lot of latency,so it makes back-presure in other elements

Have you monitored the GPU loading and CPU loading while you run the webrtc sending app?

it uses cpu, but it’s not about webrtc because i even try using Eglglessink but the same thing happends

I saw “nvvideoconvert” in your code, there is GPU in use. So the video is smooth but you think the latency is long, right?

oh sorry, i think it’s another topic, the problem is because of bottleneck in encoder my gpu utilization is only 80% in a100

What is the CPU loading?