1

I'm trying to generate a RTSP stream of opencv frames from python. I've used Gstreamer for that and was able to stream frames and read them with VLC in another PC. My problem is that my code uses the default port 8554 (i.e the stream is in rtsp://hostIp:8554/test), while I want to stream to port 554.

I've tried many additions to the launch_string, for example '! gdppay ! tcpserversink host=serverIp port=554'. However the app crashes with a gstreamer error or just nothing is streamed.

Is that even possible?

my code (took most of it from here):

import cv2
import gi
import subprocess
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, fps, img_shape, cols, properties={}):
        super(SensorFactory, self).__init__(**properties)
        self.height = int(img_shape[0])
        self.width = int(img_shape[1] * cols)
        self.number_frames = 0
        fps = int(fps)
        self.cap = None
        self.duration = 1.0 / fps * Gst.SECOND  # duration of a frame in nanoseconds
        caps_str = 'caps=video/x-raw,format=BGR,width={},height={},framerate={}/1 '.format(self.width,
                                                                                       self.height,
                                                                                       fps)
        self.launch_string = 'appsrc name=source is-live=true block=true format=GST_FORMAT_TIME ' + caps_str + \
                         '! videoconvert ' + \
                         '! video/x-raw,format=I420 ' + \
                         '! x264enc tune=zerolatency speed-preset=fast ' + \
                         '! rtph264pay config-interval=1 pt=96 name=pay0 ' + \
                         '' # <<<=== add more configs here
        print(self.launch_string)

    def set_cap(self, cap):
        self.cap = cap

    def on_need_data(self, src, length):
        if self.cap.isOpened():
            ret, frame = self.cap.read()
            if ret:
                if frame.shape[:2] != (self.height, self.width):
                    frame = cv2.resize(frame, (self.width, self.height))
                data = frame.tostring()
                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 = self.appsrc.emit('push-buffer', buf)
                print('[INFO]: frame {}'.format(self.number_frames))
                if retval != Gst.FlowReturn.OK:
                    print("[INFO]: retval not OK: {}".format(retval))

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

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


class GstServer(GstRtspServer.RTSPServer):
    def __init__(self, fps, img_shape, cols, properties={}):
        GObject.threads_init()
        Gst.init(None)
        super(GstServer, self).__init__(**properties)
        self.factory = SensorFactory(fps=fps, img_shape=img_shape, cols=cols)
        self.factory.set_shared(True)
        self.get_mount_points().add_factory('/test', self.factory)
        self.attach(None)
        self.loop = None

    def run(self):
        self.loop = GObject.MainLoop()
        self.loop.run()

Thank you.

user2999345
  • 4,195
  • 1
  • 13
  • 20

1 Answers1

2

Try something along self.set_service("554") in your GstServer class. Note that you will need to run this with elevated privileges as the port is < 1024 and also the reason why it defaults to 8554.

Florian Zwoch
  • 6,764
  • 2
  • 12
  • 21