5
\$\begingroup\$

Worked with same IP cameras for some time, and noticed that camera use much processing power because of multiple connections. Writing streaming server looked as nice solution so I decided to write my own. This code is posted to my github page

Main.py

import sys
import socket
import _thread
import time
import signal
from Libs import Connection

# Create socket and listen on port 5005
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("", 5005))
server_socket.listen(5)

connections = []
opened_cameras = {}


def signal_handler(signal=None, frame=None):
    exit(0)

# Loop and check for new connections
while 1:
    try:
        client_socket, address = server_socket.accept()
        print
        "Conencted to - ", address, "\n"
        cam_url = client_socket.recv(1024)
        # if camera url does not exsists in oppened camera, open new connection,
        # or else just append client params and pass to Connection thread
        if cam_url not in opened_cameras:
            # opened_cameras.append(cam_url)
            client = Connection.Connection([client_socket, cam_url])
            opened_cameras[cam_url] = client
            _thread.start_new_thread(client.capture, (opened_cameras,))

        else:
            opened_cameras[cam_url].addConnection(client_socket)
        connections.append([client_socket, cam_url])

    except socket.timeout:
        continue
    except KeyboardInterrupt:
        server_socket.close()

        del connections
        exit(0)

Libs/Connection.py

import cv2
import socket
import signal

class Connection(object):

    def __init__(self, connections):
        print(connections)
        self.url = connections[1].decode("utf-8")
        print(self.url)
        self.socket = []
        self.socket.append(connections[0])
        self.connections = connections
        # signal.signal(signal.SIGINT, signal_handler)
        self.connect()
        pass



    def connect(self):
        self.connection = cv2.VideoCapture(self.url)
        return self.connection

    def addConnection(self, client):
        self.socket.append(client)

    def capture(self, opened_cameras):
        self.opened_cameras = opened_cameras
        while 1:
            try:
                ret, frame = self.connection.read()
                data = cv2.imencode('.jpg', frame)[1].tostring()
                if len(self.socket):
                    for c in self.socket:
                        self.send(c,data)
                else:
                    self.connection.release()
                    del self.opened_cameras[self.connections[1]]
                    exit(0)

                    # self.connections[1].close()
            except KeyboardInterrupt:
                self.signal_handler()

    def send(self,c, data):
        try:
            c.send(data)
            c.send(b"END!") # send param to end loop in client
        except socket.error:
            self.socket.remove(c)

This will check for user to connect and if same camera url is passed as parameter, Connection class will handle passing captured images to users that request from that camera.

client/recv.py

#!/usr/bin/python
import socket
import cv2
import numpy
import random
import sys

host = sys.argv[1] # e.g. localhost, 192.168.1.123
cam_url = sys.argv[2] # rtsp://user:pass@url/live.sdp , http://url/video.mjpg ...
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
client_socket.connect((host, 5005))
name = str(random.random()) # gives random name to create window

client_socket.send(str.encode(cam_url))

def rcv():
    data = b''
    while 1:

        try:
            r = client_socket.recv(90456)
            if len(r) == 0:
                exit(0)
            a = r.find(b'END!')
            if a != -1:
                data += r[:a]
                break
            data += r
        except Exception as e:
            print(e)
            continue
    nparr = numpy.fromstring(data, numpy.uint8)
    frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    if type(frame) is type(None):
        pass
    else:
        try:
            cv2.imshow(name,frame)
            if cv2.waitKey(10) == ord('q'):
                client_socket.close()
                sys.exit()
        except:
            client_socket.close()
            exit(0)

while 1:
    rcv()

Unique window name is required because there will no be two windows if two clients are opened from the same PC. CPU usage is very low, on my PC cpu usage is 4% when server have connection, and on every other connection which uses the same params, there is no update in cpu usage.

Basic server usage: python3 Main.py

Client usage:

cd client
python3 recv.py localhost rtsp://192.168.1.123/live2.sdp

Params for recv.py are

  1. host where server is running
  2. Camera video address
\$\endgroup\$
2
  • \$\begingroup\$ How do you see the results? what about cannot connect to X server? how about no matches found error? \$\endgroup\$
    – Ghasem
    Commented Apr 5, 2018 at 16:08
  • \$\begingroup\$ Make sure that you are using python3 and have opencv and other imports installed. Once you start server, start the client where 1st param is host (eg localhost,192.168.1.123...) and the second is the actual path to ip camera stream (mjpg,rtsp...). Results are displayed in client as window displaying live stream. Cannot connect to X server error? Are you ssh to client or server script? If that's the case, use export DISPLAY=:0 (or what your display is) before running. And I dont understand last part, no matches found? Did you mean no found for host info? Just provide right host address. \$\endgroup\$
    – Aleksandar
    Commented Apr 8, 2018 at 6:52

0

Browse other questions tagged or ask your own question.