Search code examples
pythonopencvvideovideo-capturertsp

Python Opencv Mulithreading with RTSP feeds


I'm working on a small project for personal use where I want to load the CCTV streams I have from home into Opencv. I've done alot of research and understand that I need to use multithreading to get them to work correctly. and using the following code I've got it working on one camera perfectly!

from threading import Thread
import imutils
import cv2, time

camlink1 = "rtsp://xx.xx.xx.xx.:xxx/user=xxx&password=xxx&channel=1&stream=0./"


class VideoStreamWidget(object):
    def __init__(self, link, camname, src=0):
        self.capture = cv2.VideoCapture(link)
        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()
        self.camname = camname
        self.link = link
        print(camname)
        print(link)

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()
            time.sleep(.01)

    def show_frame(self):

        # Display frames in main program
        frame = imutils.resize(self.frame, width=400)
        cv2.imshow('Frame ' + self.camname, frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)

if __name__ == '__main__':
    video_stream_widget = VideoStreamWidget(camlink1,"Cam1")

    while True:
        try:
            video_stream_widget.show_frame()
        except AttributeError:
            pass

But now I want run another camera alongside it (in parallel) but I'm not sure how what i need to change to the code to start another thread and run it side by side. I thought I did by using the following:

from threading import Thread
import imutils
import cv2, time

camlink1 = "rtsp://xx.xx.xx.xx.:xxx/user=xxx&password=xxx&channel=1&stream=0./"
camlink2 = "rtsp://xx.xx.xx.xx.:xxx/user=xxx&password=xxx&channel=2&stream=0./"

class VideoStreamWidget(object):
    def __init__(self, link, camname, src=0):
        self.capture = cv2.VideoCapture(link)
        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()
        self.camname = camname
        self.link = link
        print(camname)
        print(link)

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()
            time.sleep(.01)

    def show_frame(self):

        # Display frames in main program
        frame = imutils.resize(self.frame, width=400)
        cv2.imshow('Frame ' + self.camname, frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)

if __name__ == '__main__':
    video_stream_widget = VideoStreamWidget(camlink1,"Cam1")
    video_stream_widget = VideoStreamWidget(camlink2,"Cam2")

    while True:
        try:
            video_stream_widget.show_frame()
        except AttributeError:
            pass

But this just shows the second camera only, overriding the first. I know somewhere I'm missing something simple but after looking at this for hours I can't figure it out. Any help is appreciated

Cheers Chris


Solution

  • The problem is in these lines of code:

    if __name__ == '__main__':
        video_stream_widget = VideoStreamWidget(camlink1,"Cam1")
        video_stream_widget = VideoStreamWidget(camlink2,"Cam2")
    
        while True:
            try:
                video_stream_widget.show_frame()
            except AttributeError:
                pass
    

    To be precise, you are overwriting the the variable video_stream_widget and then calling once video_stream_widget.show_frame() This will be only call in the last one (even if the thread that captures the images is still working), so only the last one will be showing, i.e. "Cam2".


    Solution

    Add different names for each and call the show_frame() function on both, like this:

    if __name__ == '__main__':
        video_stream_widget = VideoStreamWidget(camlink1,"Cam1")
        video_stream_widget2 = VideoStreamWidget(camlink2,"Cam2")
    
        while True:
            try:
                video_stream_widget.show_frame()
                video_stream_widget2.show_frame()
            except AttributeError:
                pass