10

I have a video file on my local disk and i want to create an rtsp stream from it, which i am going to use in one of my project. One way is to create a rtsp stream from vlc but i want to do it with code (python would be better). I have tried opencv's VideoWritter like this

import cv2

_dir = "/path/to/video/file.mp4"
cap = cv2.VideoCapture(_dir)

framerate = 25.0
out = cv2.VideoWriter(
    "appsrc ! videoconvert ! x264enc noise-reduction=10000 speed-preset=ultrafast tune=zerolatency ! rtph264pay config-interval=1 pt=96 ! tcpserversink host=127.0.0.1 port=5000 sync=false",
    0,
    framerate,
    (1920, 1080),
)


counter = 0
while cap.isOpened():
    ret, frame = cap.read()
    if ret:
        out.write(frame)
        print(f"Read {counter} frames",sep='',end="\r",flush=True)
        counter += 1
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    else:
        break

cap.release()
out.release()

But when i stream it on vlc like this

vlc -v rtsp://127.0.0.1:5000 I am getting

[00007fbb307a3e18] access_realrtsp access error: cannot connect to 127.0.0.1:5000
[00007fbb2c189f08] core input error: open of `rtsp://127.0.0.1:5000' failed
[00007fbb307a4278] live555 demux error: Failed to connect with rtsp://127.0.0.1:5000

Gstreamer is another option but as i have never used it, so would be nice if someone points me in the right direction.

shahidammer
  • 1,026
  • 2
  • 10
  • 24

2 Answers2

15

You tried to expose RTP protocol via TCP server but please note that RTP is not RTSP and that RTP (and RTCP) can only be part of RTSP.

Anyways, there is a way to create RTSP server with GStreamer and Python by using GStreamer's GstRtspServer and Python interface for Gstreamer (gi package).

Assuming that you already have Gstreamer on your machine, first install gi python package and then install Gstreamer RTSP server (which is not part of standard Gstreamer installation).

Python code to expose mp4 container file via simple RTSP server

#!/usr/bin/env python

import sys
import gi

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

loop = GLib.MainLoop()
Gst.init(None)

class TestRtspMediaFactory(GstRtspServer.RTSPMediaFactory):
    def __init__(self):
        GstRtspServer.RTSPMediaFactory.__init__(self)

    def do_create_element(self, url):
        #set mp4 file path to filesrc's location property
        src_demux = "filesrc location=/path/to/dir/test.mp4 ! qtdemux name=demux"
        h264_transcode = "demux.video_0"
        #uncomment following line if video transcoding is necessary
        #h264_transcode = "demux.video_0 ! decodebin ! queue ! x264enc"
        pipeline = "{0} {1} ! queue ! rtph264pay name=pay0 config-interval=1 pt=96".format(src_demux, h264_transcode)
        print ("Element created: " + pipeline)
        return Gst.parse_launch(pipeline)

class GstreamerRtspServer():
    def __init__(self):
        self.rtspServer = GstRtspServer.RTSPServer()
        factory = TestRtspMediaFactory()
        factory.set_shared(True)
        mountPoints = self.rtspServer.get_mount_points()
        mountPoints.add_factory("/stream1", factory)
        self.rtspServer.attach(None)

if __name__ == '__main__':
    s = GstreamerRtspServer()
    loop.run()

Note that

  • this code will expose RTSP stream named stream1 on default port 8554
  • I used qtdemux to get video from MP4 container. You could extend above pipeline to extract audio too (and expose it too via RTSP server)
  • to decrease CPU processing you can only extract video without decoding it and encoding it again to H264. However, if transcoding is needed, I left one commented line that will do the job (but it might choke less powerful CPUs).

You can play this with VLC

vlc -v rtsp://127.0.0.1:8554/stream1

or with Gstreamer

gst-launch-1.0 playbin uri=rtsp://127.0.0.1:8554/stream1

However, If you actually do not need RTSP but just end-to-end RTP following Gstreamer pipeline (that utilizes rtpbin) will do the job

gst-launch-1.0 -v rtpbin name=rtpbin \ 
filesrc location=test.mp4 ! qtdemux name=demux \
demux.video_0 ! decodebin ! x264enc ! rtph264pay config-interval=1 pt=96 ! rtpbin.send_rtp_sink_0 \
rtpbin.send_rtp_src_0 ! udpsink host=127.0.0.1 port=5000 sync=true async=false

and VLC can play it with

vlc -v rtp://127.0.0.1:5000
Dusan Kovacevic
  • 1,377
  • 1
  • 13
  • 19
  • Thank you so the solution, it worked but only for video with codec: H.264 (Main Profile). Is there a way i can make video with codec H.265 (Main Profile) rtsp streams with the same code? @Dusan Kovacevic – shahidammer Jan 23 '20 at 09:09
  • @shahidammer maybe [this](https://stackoverflow.com/questions/32547276/how-to-stream-in-h265-using-gstreamer) SO Q/A can help you with H.265. In that case in example above you would need to uncomment transcoding part and add h265 encoder element. – Dusan Kovacevic Jan 23 '20 at 09:14
  • Nevermind, it did work with I uncomment the line you menioned. Thank you. However, the quality drops when i view it in vlc. Is there any way to improve it? – shahidammer Jan 23 '20 at 09:16
  • I assume that quality drops because software transcoding eats a lot of CPU power. Once I used hardware transcoding that utilizes GPU and it produced much much better performance. But that was on specific platform... and that is another story :) – Dusan Kovacevic Jan 23 '20 at 09:22
  • Is there a way i can move to GPU from CPU too? – shahidammer Jan 23 '20 at 09:31
  • 1
    [This](https://gstreamer.freedesktop.org/documentation/vaapi/index.html?gi-language=python) could be starting point for your research. – Dusan Kovacevic Jan 23 '20 at 09:34
  • Thanks for the code. New to GST. How to convert this code to continuously stream the same video in loops? – Prakash Vanapalli Feb 08 '21 at 06:21
0

How to add the framerate and bitrate in pipeline

def do_create_element(self, url):
    #set mp4 file path to filesrc's location property
    src_demux = "filesrc location=/path/to/dir/test.mp4 ! qtdemux name=demux"
    h264_transcode = "demux.video_0"
    #uncomment following line if video transcoding is necessary
    #h264_transcode = "demux.video_0 ! decodebin ! queue ! x264enc"
    pipeline = "{0} {1} ! queue ! rtph264pay name=pay0 config-interval=1 pt=96".format(src_demux, h264_transcode)
    print ("Element created: " + pipeline)
    return Gst.parse_launch(pipeline)
  • 2
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 25 '22 at 10:24
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 25 '22 at 11:48