Open the same rtsp capture fails (cap.isOpened is false)

Hello! I am using opencv-python~=4.10.0.84 with flask
In my code, when flask app starts I push to global array cameras_list entities (cameras) from sqlite database:

with app.app_context():
    # Register main tasks
    t = Thread(target=camera_stream_task, daemon=True)
    t.start()

camera_stream_task:

def camera_stream_task():
    while True:
        with app.app_context():
            cameras = Camera.query.all()
            for camera in cameras:
                if camera.id not in cameras_list:
                    cameras_list[camera.id] = OpenCV(camera)
                    task = Thread(target=camera_opencv_task, args=[camera], daemon=True)
                    task.start()
            time.sleep(10)

OpenCV is my wrapper for cv2 and its look like this:

class OpenCV:
    path: str | int = None
    camera_directory: str = None
    cameras_root: str = None
    cap: cv.VideoCapture = None
    camera: Camera = None
    cover_timeout = 3
    ret = None
    frame: Mat = None
    _cv = None

    def __init__(self, _camera: Camera = None):
        self._cv = cv
        _path = Crypt.decrypt(_camera.path)

        self.path = _path
        self.cameras_root = os.path.join(_camera.storage.path, 'cameras')
        self.camera_directory = os.path.join(_camera.storage.path, 'cameras', str(_camera.id))
        self.cap = cv.VideoCapture(self.path)
        self.camera = _camera

        self.create_camera_directory()
        print('Initialize class Opencv for camera', self.camera.id, 'path', self.path, 'cap is ', self.cap.isOpened())

        def loop_frames():
            while True:
                if self.cap.isOpened():
                    ret, frame = self.cap.read()
                    if ret:
                        self.ret = ret
                        self.frame = frame

        task = Thread(daemon=True, target=loop_frames)
        task.start()
        # other stuff

For tests I created 6 cameras (id 1,2,3,4,5,6) with the same urls:

  • Three with rtsp://...ip.../stream1 (id 1,2,3)
  • Three with rtsp://...ip.../stream2 (id 4,5,6)
    The problem is that cap is not opened for id (2,3,5,6)

Then I create a separate file with content:

def motionDetection():
    # capturing video in real time
    cap = cv2.VideoCapture('rtsp://...ip.../stream1')
    print(cap.isOpened())
    cap.release() # or without this line


if __name__ == "__main__":
    motionDetection() # true
    motionDetection() # true
    motionDetection() # true

What could be the problem?

you haven’t verified that the server even allows multiple consumers for the same stream.

do that first. use ffmpeg CLI or VLC or anything else.


you present code involving flask and threads. that is adding needless complexity to the problem.

remove flask. remove threads. test again.

Thank you!
I started working with it quite recently. Zoneminder, ffplay, vlc can simultaneously connect to the stream.

import cv2

stream_urls = [
    'rtsp://...ip.../stream1', # make sure this string has a sensible value
    ...
]
streams = [
    cv2.VideoCapture(stream_urls[0]) # leave this as is
    for k in range(2)
]
print([s.isOpened() for s in streams])

run this, just this, no additions or deletions, just fill in the stream URL.

does this say [True, True] or something else?

Thank you for reply!
It’s return

[True, True]

It turns out that in your code example there are no problems when using the same address multiple times. However, I am now trying to simplify the code, as you said, but try to reproduce the problem with threads.

I wrote a gist here Opencv in threads · GitHub
when daemon=True in motion_detection thread, program exit, but with

task1 = Thread(target=motion_detection, args=('one',))
task2 = Thread(target=motion_detection, args=('two',))

it’s ok.

Maybe I need to revise some of the code. which create threads…

I updated gist and it’s worked. Strange =)

So, I found solution. In my case there is no OpenCV fault. But if someone runs into a similar problem, knows, that Flask use_reloader triggered this behaviour

Solution:

socketio.run(
    app=app, 
    log_output=True, 
    use_reloader=False, # here must be False
    allow_unsafe_werkzeug=True
)

Perhaps there is another way, but for the time being, this is quite enough for me to develop!

Thank you!

1 Like