python-gstreamerrtsp-server

How to let multiple clients share the same pipeline in python gstreamer rtspserver


when multiple clients are connected, gstreamer creates a pipeline for each client. How can all clients share the same pipeline to reduce CPU usage?

my rtsp server code:

import cv2
import gi

gi.require_version('Gst', '1.0')
gi.require_version('GstRtspServer', '1.0')
from gi.repository import Gst, GstRtspServer, GObject


class SensorFactory(GstRtspServer.RTSPMediaFactory):
    def __init__(self, **properties):
        super(SensorFactory, self).__init__(**properties)

        pipeline = ' ! '.join(
            [
                'nvv4l2camerasrc device=/dev/video0',
                'video/x-raw(memory:NVMM),format=UYVY,width=1280,height=960,framerate=25/1',
                'nvvidconv',
                'video/x-raw,format=RGBA',
                'appsink',
            ]
        )

        self.cap = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
        self.number_frames = 0
        self.fps = 25
        self.duration = 1 / self.fps * Gst.SECOND  # duration of a frame in nanoseconds
        self.launch_string = (
            'appsrc name=source block=true format=GST_FORMAT_TIME '
            'caps=video/x-raw,format=BGR,width=1280,height=960,framerate={}/1 '
            '! videoconvert ! video/x-raw,format=I420 '
            '! x264enc speed-preset=ultrafast tune=zerolatency ! queue '
            '! rtph264pay config-interval=1 name=pay0 pt=96 '.format(self.fps)
        )
        # streams to gst-launch-1.0 rtspsrc location=rtsp://localhost:8554/test latency=50 ! decodebin ! autovideosink

    def on_need_data(self, src, lenght):
        if self.cap.isOpened():
            ret, frame = self.cap.read()
            if ret:
                data = frame.tostring()
                # print(data)
                buf = Gst.Buffer.new_allocate(None, len(data), None)
                buf.fill(0, data)
                buf.duration = self.duration
                timestamp = self.number_frames * self.duration
                buf.pts = buf.dts = int(timestamp)
                buf.offset = timestamp
                self.number_frames += 1
                retval = src.emit('push-buffer', buf)
                if retval != Gst.FlowReturn.OK:
                    print(retval)

    def do_create_element(self, url):
        return Gst.parse_launch(self.launch_string)

    def do_configure(self, rtsp_media):
        self.number_frames = 0
        appsrc = rtsp_media.get_element().get_child_by_name('source')
        appsrc.connect('need-data', self.on_need_data)


class GstServer(GstRtspServer.RTSPServer):
    def __init__(self, **properties):
        super(GstServer, self).__init__(**properties)
        self.factory = SensorFactory()
        self.factory.set_shared(True)
        self.get_mount_points().add_factory('/test', self.factory)
        self.attach(None)


GObject.threads_init()
Gst.init(None)

server = GstServer()

loop = GObject.MainLoop()
loop.run()

self.factory.set_shared(True) is executed in the code. According to the document, the factory should return the same RTSPMedia. However, in actual testing, it seems that this does not happen, because the CPU usage increases and do_configure is called with each connection

Media from a factory can be shared by setting the shared flag with GstRtspServer.RTSPMediaFactory.set_shared. When a factory is shared, GstRtspServer.RTSPMediaFactory.construct will return the same GstRtspServer.RTSPMedia when the url matches.


Solution

  • I found the solution. Just need to execute rtsp_media.set_shared(True) in the do_configure callback.

    https://gstreamer.freedesktop.org/documentation/gst-rtsp-server/rtsp-media-factory.html#GstRTSPMediaFactoryClass::configure