Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move dependencies inside docker and format code #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM python:3.8

RUN set -ex; \
apt-get update; \
apt-get install -y --no-install-recommends \
gcc \
gir1.2-gstreamer-1.0 \
gir1.2-gst-plugins-base-1.0 \
gir1.2-gtk-3.0 \
gstreamer1.0-alsa \
gstreamer1.0-doc \
gstreamer1.0-gl \
gstreamer1.0-gtk3 \
gstreamer1.0-libav \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-ugly \
gstreamer1.0-pulseaudio \
gstreamer1.0-qt5 \
gstreamer1.0-tools \
gstreamer1.0-x \
libcairo2-dev \
libgirepository1.0-dev \
libgstreamer1.0-0 \
pkg-config \
python-gst-1.0; \
rm -rf /var/lib/apt/lists/*

COPY requirements.txt ./

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ./main_prg.py
44 changes: 3 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,10 @@ This project helps in fetching continous live RTSP stream using GStreamer, Pytho

## Getting Started

Just clone this Repo then in main_prg.py add your rtsp stream on below line:
Just clone this Repo and run:

```python
self.camlink = '' #Add your RTSP cam link
```

### Prerequisites

1. Python 3
2. GStreamer
3. OpenCV (if you want to run this example as is)
4. Numpy

##### 1. Python 3 Installation
This you would already know

##### 2. GStreamer Installation
You will need GStreamer. Installation instruction can be found on this link [GStreamer](https://gstreamer.freedesktop.org/download/)
Still for your quick reference will list installation instruction for Ubuntu:

```
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
```

##### 3. OpenCV Installation
There are various way to install OpenCV but example using (Conda, PIP or build from source). But for purpose of this project below is instruction using PIP

```
pip3 install opencv-contrib-python
```

##### 4. Numpy Installation
```
pip3 install numpy
```

### Running the program

Post cloning the Repo, go to repo dir (Also include cam link in main_prg.py as mentioned above).

```python
python3 main_prg.py
``` sh
$ CAMERA_URL=... docker-compose up --build
```

## License
Expand Down
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: "2.4"

services:
rtsp-client:
image: rtsp-client
build: .
environment:
QT_X11_NO_MITSHM: 1
DISPLAY: $DISPLAY
CAMERA_URL: $CAMERA_URL
devices:
- '/dev/dri'
- '/dev/snd'
- '/dev/video0'
volumes:
- '/tmp/.X11-unix:/tmp/.X11-unix'
53 changes: 22 additions & 31 deletions main_prg.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@

#!/usr/bin/env python3
'''\
This Simple program Demonstrates how to use G-Streamer and capture RTSP Frames in Opencv using Python
- Sahil Parekh
This Simple program Demonstrates how to use G-Streamer and capture RTSP Frames
in Opencv using Python
- Sahil Parekh
'''

import multiprocessing as mp
import time
import vid_streamv3 as vs
import cv2
import sys

'''
Main class
'''
class mainStreamClass:
def __init__(self):

#Current Cam
class mainStream:
def __init__(self, camlink):
# Current Cam
self.camProcess = None
self.cam_queue = None
self.stopbit = None
self.camlink = '' #Add your RTSP cam link
self.camlink = camlink
self.framerate = 6

def startMain(self):

#set queue size
def start(self):
# set queue size
self.cam_queue = mp.Queue(maxsize=100)

#get all cams
# get all cams
time.sleep(3)

self.stopbit = mp.Event()
self.camProcess = vs.StreamCapture(self.camlink,
self.stopbit,
self.cam_queue,
self.framerate)
self.camProcess = vs.StreamCapture(
self.camlink,
self.stopbit,
self.cam_queue,
self.framerate
)
self.camProcess.start()

# calculate FPS
lastFTime = time.time()

try:
while True:

Expand All @@ -53,10 +48,7 @@ def startMain(self):
diffTime = time.time() - lastFTime`
fps = 1 / diffTime
# print(fps)

'''
lastFTime = time.time()

# if cmd == vs.StreamCommands.RESOLUTION:
# pass #print(val)

Expand All @@ -68,15 +60,13 @@ def startMain(self):
except KeyboardInterrupt:
print('Caught Keyboard interrupt')

except:
e = sys.exc_info()
except Exception as e:
print('Caught Main Exception')
print(e)

self.stopCamStream()
cv2.destroyAllWindows()


def stopCamStream(self):
print('in stopCamStream')

Expand All @@ -85,13 +75,14 @@ def stopCamStream(self):
while not self.cam_queue.empty():
try:
_ = self.cam_queue.get()
except:
except Exception:
break
self.cam_queue.close()

self.camProcess.join()


if __name__ == "__main__":
mc = mainStreamClass()
mc.startMain()
import os
stream = mainStream(os.environ["CAMERA_URL"])
stream.start()
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
opencv-contrib-python
numpy
pycairo
PyGObject
65 changes: 40 additions & 25 deletions vid_streamv3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#cython: language_level=3, boundscheck=False
import multiprocessing as mp
from enum import Enum
import numpy as np
Expand All @@ -7,11 +6,12 @@
from gi.repository import Gst
Gst.init(None)

'''Konwn issues
'''Known issues

* if format changes at run time system hangs
'''


class StreamMode(Enum):
INIT_STREAM = 1
SETUP_STREAM = 1
Expand All @@ -27,7 +27,6 @@ class StreamCommands(Enum):


class StreamCapture(mp.Process):

def __init__(self, link, stop, outQueue, framerate):
"""
Initialize the stream capturing process
Expand All @@ -54,8 +53,6 @@ def __init__(self, link, stop, outQueue, framerate):
self.num_unexpected_tot = 40
self.unexpected_cnt = 0



def gst_to_opencv(self, sample):
buf = sample.get_buffer()
caps = sample.get_caps()
Expand All @@ -66,11 +63,14 @@ def gst_to_opencv(self, sample):
# print(caps.get_structure(0).get_value('width'))

arr = np.ndarray(
(caps.get_structure(0).get_value('height'),
caps.get_structure(0).get_value('width'),
3),
(
caps.get_structure(0).get_value('height'),
caps.get_structure(0).get_value('width'),
3
),
buffer=buf.extract_dup(0, buf.get_size()),
dtype=np.uint8)
dtype=np.uint8
)
return arr

def new_buffer(self, sink, _):
Expand All @@ -83,9 +83,13 @@ def new_buffer(self, sink, _):
def run(self):
# Create the empty pipeline
self.pipeline = Gst.parse_launch(
'rtspsrc name=m_rtspsrc ! rtph264depay name=m_rtph264depay ! avdec_h264 name=m_avdech264 ! videoconvert name=m_videoconvert ! videorate name=m_videorate ! appsink name=m_appsink')
'rtspsrc name=m_rtspsrc ! '
'rtph264depay name=m_rtph264depay ! '
'avdec_h264 name=m_avdech264 ! '
'videoconvert name=m_videoconvert ! '
'videorate name=m_videorate ! appsink name=m_appsink'
)

# source params
self.source = self.pipeline.get_by_name('m_rtspsrc')
self.source.set_property('latency', 0)
self.source.set_property('location', self.streamLink)
Expand All @@ -95,6 +99,8 @@ def run(self):
self.source.set_property('tcp-timeout', 5000000)
self.source.set_property('drop-on-latency', 'true')

print("stream_link - %s" % self.streamLink)

# decode params
self.decode = self.pipeline.get_by_name('m_avdech264')
self.decode.set_property('max-threads', 2)
Expand All @@ -103,15 +109,16 @@ def run(self):
# convert params
self.convert = self.pipeline.get_by_name('m_videoconvert')

#framerate parameters
# framerate parameters
self.framerate_ctr = self.pipeline.get_by_name('m_videorate')
self.framerate_ctr.set_property('max-rate', self.framerate/1)
self.framerate_ctr.set_property('drop-only', 'true')

# sink params
self.sink = self.pipeline.get_by_name('m_appsink')

# Maximum number of nanoseconds that a buffer can be late before it is dropped (-1 unlimited)
# Maximum number of nanoseconds that a buffer can be late before
# it is dropped (-1 unlimited)
# flags: readable, writable
# Integer64. Range: -1 - 9223372036854775807 Default: -1
self.sink.set_property('max-lateness', 500000000)
Expand All @@ -138,10 +145,17 @@ def run(self):
# flags: readable, writable
# Caps (NULL)
caps = Gst.caps_from_string(
'video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}')
'video/x-raw, format=(string){BGR, GRAY8};'
'video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}'
)
self.sink.set_property('caps', caps)

if not self.source or not self.sink or not self.pipeline or not self.decode or not self.convert:
if not self.source \
or not self.sink \
or not self.pipeline \
or not self.decode \
or not self.convert:

print("Not all elements could be created.")
self.stop.set()

Expand All @@ -156,38 +170,40 @@ def run(self):
# Wait until error or EOS
bus = self.pipeline.get_bus()

while True:
print("Before cycle")

while True:
if self.stop.is_set():
print('Stopping CAM Stream by main process')
break

message = bus.timed_pop_filtered(10000, Gst.MessageType.ANY)
# print "image_arr: ", image_arr
if self.image_arr is not None and self.newImage is True:

if not self.outQueue.full():

# print("\r adding to queue of size{}".format(self.outQueue.qsize()), end='\r')
self.outQueue.put((StreamCommands.FRAME, self.image_arr), block=False)
# print("\r adding to queue of size %s" %
# self.outQueue.qsize()), end='\r')
self.outQueue.put((StreamCommands.FRAME, self.image_arr),
block=False)

self.image_arr = None
self.unexpected_cnt = 0


if message:
if message.type == Gst.MessageType.ERROR:
err, debug = message.parse_error()
print("Error received from element %s: %s" % (
message.src.get_name(), err))
print("Error received from element %s: %s" %
(message.src.get_name(), err))
print("Debugging information: %s" % debug)
break
elif message.type == Gst.MessageType.EOS:
print("End-Of-Stream reached.")
break
elif message.type == Gst.MessageType.STATE_CHANGED:
if isinstance(message.src, Gst.Pipeline):
old_state, new_state, pending_state = message.parse_state_changed()
old_state, new_state, pending_state = \
message.parse_state_changed()
print("Pipeline state changed from %s to %s." %
(old_state.value_nick, new_state.value_nick))
else:
Expand All @@ -196,7 +212,6 @@ def run(self):
if self.unexpected_cnt == self.num_unexpected_tot:
break


print('terminating cam pipe')
self.stop.set()
self.pipeline.set_state(Gst.State.NULL)
self.pipeline.set_state(Gst.State.NULL)