├── README.md ├── open_rtsp.py ├── stream.bash └── stream.py /README.md: -------------------------------------------------------------------------------- 1 | # RTSP streaming using GStreamer 2 | 3 | Python implementation to stream camera feed from OpenCV videoCapture via RTSP server using GStreamer 1.0. 4 | 5 | ## Installation 6 | 7 | This implementation has been developed and tested on Ubuntu 16.04 and 18.04. So the installation steps are specific to debian based linux distros. 8 | 9 | ### Step-1 Install GStreamer-1.0 and related plugins 10 | sudo apt-get install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio 11 | ### Step-2 Install RTSP server 12 | sudo apt-get install libglib2.0-dev libgstrtspserver-1.0-dev gstreamer1.0-rtsp 13 | ### Requirement 14 | - Python 3.x 15 | - Opencv 3.x or above ( pip install opencv-python ) 16 | 17 | ### Usage 18 | > Run stream.py with required arguments to start the rtsp server 19 | ##### Sample 20 | python stream.py --device_id 0 --fps 30 --image_width 640 --image_height 480 --port 8554 --stream_uri /video_stream 21 | 22 | ### Visualization 23 | 24 | You can view the video feed on `rtsp://server-ip-address:8554/stream_uri` 25 | 26 | e.g: `rtsp://192.168.1.12:8554/video_stream` 27 | 28 | You can either use any video player which supports rtsp streaming like VLC player or you can use the `open-rtsp.py` script to view the video feed. 29 | 30 | -------------------------------------------------------------------------------- /open_rtsp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Oct 5 03:11:31 2020 5 | 6 | @author: prabhakar 7 | """ 8 | 9 | import cv2 10 | 11 | cv2.namedWindow("RTSP View", cv2.WINDOW_NORMAL) 12 | cap = cv2.VideoCapture("rtsp://admin:kotai@123@192.168.1.64:554/Streaming/Channels/101") 13 | while True: 14 | 15 | ret, frame = cap.read() 16 | if ret: 17 | cv2.imshow("RTSP View", frame) 18 | cv2.waitKey(1) 19 | else: 20 | print("unable to open camera") 21 | break 22 | cap.release() 23 | cv2.destroyAllWindows() 24 | -------------------------------------------------------------------------------- /stream.bash: -------------------------------------------------------------------------------- 1 | python3 stream.py \ 2 | --device_id 0 \ 3 | --fps 30 \ 4 | --image_width 640 \ 5 | --image_height 480 \ 6 | --stream_uri /video_stream 7 | -------------------------------------------------------------------------------- /stream.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Mon Jan 20 02:07:13 2019 5 | 6 | @author: prabhakar 7 | """ 8 | # import necessary argumnets 9 | import gi 10 | import cv2 11 | import argparse 12 | 13 | cam_url = 'rtsp://admin:kotai@123@192.168.1.64:554/Streaming/Channels/101' 14 | 15 | # import required library like Gstreamer and GstreamerRtspServer 16 | gi.require_version('Gst', '1.0') 17 | gi.require_version('GstRtspServer', '1.0') 18 | from gi.repository import Gst, GstRtspServer, GObject, GLib 19 | 20 | # Sensor Factory class which inherits the GstRtspServer base class and add 21 | # properties to it. 22 | class SensorFactory(GstRtspServer.RTSPMediaFactory): 23 | def __init__(self, **properties): 24 | super(SensorFactory, self).__init__(**properties) 25 | self.cap = cv2.VideoCapture(opt.device_id) 26 | #self.cap = cv2.VideoCapture(cam_url) 27 | self.number_frames = 0 28 | self.fps = opt.fps 29 | self.duration = 1 / self.fps * Gst.SECOND # duration of a frame in nanoseconds 30 | self.launch_string = 'appsrc name=source is-live=true block=true format=GST_FORMAT_TIME ' \ 31 | 'caps=video/x-raw,format=BGR,width={},height={},framerate={}/1 ' \ 32 | '! videoconvert ! video/x-raw,format=I420 ' \ 33 | '! x264enc speed-preset=ultrafast tune=zerolatency ' \ 34 | '! rtph264pay config-interval=1 name=pay0 pt=96' \ 35 | .format(opt.image_width, opt.image_height, self.fps) 36 | # method to capture the video feed from the camera and push it to the 37 | # streaming buffer. 38 | def on_need_data(self, src, length): 39 | if self.cap.isOpened(): 40 | ret, frame = self.cap.read() 41 | if ret: 42 | # It is better to change the resolution of the camera 43 | # instead of changing the image shape as it affects the image quality. 44 | frame = cv2.resize(frame, (opt.image_width, opt.image_height), \ 45 | interpolation = cv2.INTER_LINEAR) 46 | data = frame.tostring() 47 | buf = Gst.Buffer.new_allocate(None, len(data), None) 48 | buf.fill(0, data) 49 | buf.duration = self.duration 50 | timestamp = self.number_frames * self.duration 51 | buf.pts = buf.dts = int(timestamp) 52 | buf.offset = timestamp 53 | self.number_frames += 1 54 | retval = src.emit('push-buffer', buf) 55 | print('pushed buffer, frame {}, duration {} ns, durations {} s'.format(self.number_frames, 56 | self.duration, 57 | self.duration / Gst.SECOND)) 58 | if retval != Gst.FlowReturn.OK: 59 | print(retval) 60 | # attach the launch string to the override method 61 | def do_create_element(self, url): 62 | return Gst.parse_launch(self.launch_string) 63 | 64 | # attaching the source element to the rtsp media 65 | def do_configure(self, rtsp_media): 66 | self.number_frames = 0 67 | appsrc = rtsp_media.get_element().get_child_by_name('source') 68 | appsrc.connect('need-data', self.on_need_data) 69 | 70 | # Rtsp server implementation where we attach the factory sensor with the stream uri 71 | class GstServer(GstRtspServer.RTSPServer): 72 | def __init__(self, **properties): 73 | super(GstServer, self).__init__(**properties) 74 | self.factory = SensorFactory() 75 | self.factory.set_shared(True) 76 | self.set_service(str(opt.port)) 77 | self.get_mount_points().add_factory(opt.stream_uri, self.factory) 78 | self.attach(None) 79 | 80 | # getting the required information from the user 81 | parser = argparse.ArgumentParser() 82 | parser.add_argument("--device_id", required=True, help="device id for the \ 83 | video device or video file location") 84 | parser.add_argument("--fps", required=True, help="fps of the camera", type = int) 85 | parser.add_argument("--image_width", required=True, help="video frame width", type = int) 86 | parser.add_argument("--image_height", required=True, help="video frame height", type = int) 87 | parser.add_argument("--port", default=8554, help="port to stream video", type = int) 88 | parser.add_argument("--stream_uri", default = "/video_stream", help="rtsp video stream uri") 89 | opt = parser.parse_args() 90 | 91 | try: 92 | opt.device_id = int(opt.device_id) 93 | except ValueError: 94 | pass 95 | 96 | # initializing the threads and running the stream on loop. 97 | # GObject.threads_init() 98 | Gst.init(None) 99 | server = GstServer() 100 | GLib.MainLoop() --------------------------------------------------------------------------------