gstreamerrtsprtsp-server

How can I get the RTSP stream to show up on multiple devices?


I have a code which uses gstreamer RTSP server and I am trying to create some rtsp streams from images/frames. The implementation works fine and converts the images into an RTSP stream.

The process is I run the script and the server is now active and then I can use the RTSP stream on another machine with my current machine IP.

Questions:

  1. I would like to know how I can change the rtsp port and take it from the config.json file. For some reason, the default port which gets used is 8554 even though I have not explicitly stated to use that port.
  2. Will I be able to use multiple rtsp streams through one RTSP server with the help of different ports? For example, the current implementation I have does not work from more than one machine if I stream the same RTSP URL through VLC.

This is my code so far:

class TestRtspMediaFactory(GstRtspServer.RTSPMediaFactory):
    def __init__(self, source):
        GstRtspServer.RTSPMediaFactory.__init__(self)
        self.source = source

    def do_create_element(self, url):
        image_folder = self.source.get("image_folder", None)

        if image_folder:
            # print('Image folder paths:', image_folder)
            pipeline = f"multifilesrc location={image_folder}/%d.jpg index=1 caps=\"image/jpeg,framerate=30/1\" ! jpegdec ! videoconvert ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! rtph264pay name=pay0 config-interval=1 pt=96"
        else:
            print("Invalid source. Specify 'image_folder' in the configuration.")
            return None
    
        print("Element created:", pipeline)
        return Gst.parse_launch(pipeline)

class GstreamerRtspServer():
    def __init__(self, sources):
        self.rtspServer = GstRtspServer.RTSPServer()
        self.mountPoints = self.rtspServer.get_mount_points()
        self.create_rtsp_streams(sources)

    def create_rtsp_streams(self, sources):
        for source in sources:
            rtsp_port = source["rtsp_port"]
            rtsp_mount_point = source["rtsp_mount_point"]
            print(rtsp_mount_point)
            print(rtsp_port)

            factory = TestRtspMediaFactory(source)
            factory.set_shared(True)
            self.mountPoints.add_factory(rtsp_mount_point, factory)

        self.rtspServer.attach(None)

def main(config_file):
    with open(config_file, 'r') as f:
        config = json.load(f)

    sources = config.get("sources", [])
    
    s = GstreamerRtspServer(sources)
    loop = GLib.MainLoop()
    loop.run()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Run RTSP server for multiple video sources.")
    parser.add_argument("--config", required=True, help="Path to the JSON configuration file.")
    args = parser.parse_args()
    
    main(args.config)

This is my config.json file:

{
    "sources": [
        {
            "image_folder": "/path/to/another/image/folder",
            "rtsp_port": 4000,
            "rtsp_mount_point": "/stream1"
        },
        {
            "image_folder": "/path/to/another/image/folder",
            "rtsp_port": 8555,
            "rtsp_mount_point": "/stream2"
        }
    ]
}

Solution

    1. For setting the port used by the server, you would use method set_service of the RtspServer.

    2. You don't need to specify different ports for different clients, the RTSP server will negociate with client and manage the actual ports and transport protocols. Clients can select the stream just with the URI using stream1, stream2.

    So after modifying your json config file with:

    {
        "server": {
                "port": "8556"
        },
            
        "sources": [
            {
                "image_folder": "/home/neville/Documents/convert_rtsp_video/frames",
                "rtsp_mount_point": "/stream1"
            },
            {
                "image_folder": "/path/to/another/image/folder",
                "rtsp_mount_point": "/stream2"
            }
        ]
    }
    

    and modified your code to:

    #!/usr/bin/env python3
    
    import argparse
    import json
    import gi
    gi.require_version('Gst','1.0')
    gi.require_version('GstVideo','1.0')
    gi.require_version('GstRtspServer','1.0')
    from gi.repository import GObject, GLib, Gst, GstVideo, GstRtspServer
    
    
    class TestRtspMediaFactory(GstRtspServer.RTSPMediaFactory):
        def __init__(self, source):
            GstRtspServer.RTSPMediaFactory.__init__(self)
            self.source = source
    
        def do_create_element(self, url):
            image_folder = self.source.get("image_folder", None)
    
            if image_folder:
                # print('Image folder paths:', image_folder)
                pipeline = f"multifilesrc location={image_folder}/%d.jpg index=1 loop=1 caps=\"image/jpeg,framerate=30/1\" ! jpegdec ! videoconvert ! x264enc tune=zerolatency bitrate=500 speed-preset=superfast key-int-max=30 ! rtph264pay name=pay0 config-interval=1 pt=96"
    
            else:
                print("Invalid source. Specify 'image_folder' in the configuration.")
                return None
        
            print("Element created:", pipeline)
            return Gst.parse_launch(pipeline)
            
    
    class GstreamerRtspServer():
        def __init__(self, server_conf, sources):
            self.rtspServer = GstRtspServer.RTSPServer()
            self.rtspServer.set_service(server_conf["port"])
            print("RTSP Server on port", server_conf["port"]) 
            self.mountPoints = self.rtspServer.get_mount_points()
            self.create_rtsp_streams(sources)
    
        def create_rtsp_streams(self, sources):
            for source in sources:
                rtsp_mount_point = source["rtsp_mount_point"]
                print(rtsp_mount_point)
    
                factory = TestRtspMediaFactory(source)
                factory.set_shared(True)
                self.mountPoints.add_factory(rtsp_mount_point, factory)
    
            self.rtspServer.attach(None)
    
    def main(config_file):
        with open(config_file, 'r') as f:
            config = json.load(f)
    
        server_conf = config.get("server", {})
        sources = config.get("sources", [])
        
        s = GstreamerRtspServer(server_conf,sources)
        loop = GLib.MainLoop()
        loop.run()
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(description="Run RTSP server for multiple video sources.")
        parser.add_argument("--config", required=True, help="Path to the JSON configuration file.")
        args = parser.parse_args()
        Gst.init(None)
        main(args.config)
    

    it works fine with 2 clients (one for each stream) on localhost and 2 other clients from a remote host.