4

I have RTSP URLs for 80 total cameras. I am trying to get a screenshot from every camera using cv2.VideoCapture(rtsp_url). I am doing some image processing operations on the captured screenshots from each camera. I have total 80 such URLs for each camera. A sample RTSP URL is:

rtsp://192.168.0.101:554/user=admin_password=oyXv12aW_channel=1_stream=0.sdp?real_stream

Now, sometimes due to the network or other technical issues, some of the cameras temporarily don't function. In that case cv2.VideoCapture(rtsp_url) takes more than 30 seconds to return. In a normal scenario it usually takes 2-3 seconds to return with screenshots. But if a camera is down, it takes too much time. Since my application is running in a for loop on many cameras, I cannot afford to wait. Sometimes the inactive camera count is high and it takes forever to execute the loop. What can be done to reduce the waiting time for cv2.VideoCapture?

I have also used multiprocessing and have tried terminating processes if they exceed a given time threshold. I don't want to use multiprocessing though it is working very fine. I want to find a solution using only opencv

nathancy
  • 42,661
  • 14
  • 115
  • 137
sandeep.ganage
  • 1,409
  • 2
  • 21
  • 47
  • Can we add some how add wait time for function? If time expires then function call should return. Is there any workaround for this? – sandeep.ganage Aug 03 '21 at 06:00

2 Answers2

2

The multiprocessing version may really be what you want

  • may simplifies reading all the cameras in parallel
  • can handle all sorts of timeout-related woes

However, a little reading shows that OpenCV RTSP handling is done by ffmpeg internally and its behavior is controlled in part by an environmental var named OPENCV_FFMPEG_CAPTURE_OPTIONS

From the docs on ffmpeg http://ffmpeg.org/ffmpeg-protocols.html#rtsp

timeout  
  Set socket TCP I/O timeout in microseconds.

I don't have a practical way to try this, but perhaps setting this in your program or exporting it first is all you need

os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "timeout;5000"
cam = cv2.VideoCapture("rtsp://YOUR_STREAMING_IP_ADDRESS:PORT/foo.sdp", cv2.CAP_FFMPEG)

See selected Answer to RTSP stream and OpenCV (Python)

ti7
  • 16,375
  • 6
  • 40
  • 68
  • I had tried both options before. None of this works. – sandeep.ganage Aug 07 '21 at 17:55
  • alas! .. if you can reproduce the effect, does just running `ffmpeg` with those options work? that'll help you scope it down to where in the stack it goes awry – ti7 Aug 07 '21 at 21:31
1

Since you have not provided any code, my assumption is this: When cv2.videoCapture is attempting to retrieve a frame but the network cuts off, it freezes for X amount of seconds and stalls your program until it either timeouts or a frame is finally retrieved. I'm also assuming that your entire program is running in a single giant loop, in order words, your program is running synchronically and dependent on one another. Essentially this question can be reworded to: How do we capture RTSP camera frames asynchronically?


This is a classic programming model where we can use threading to handle heavy I/O operations. Your current situation is that you have multiple camera streams but if any one camera fails, it stalls the entire application. The reason why your application halts when a camera doesn't function is because accessing the webcam/stream/camera using cv2.VideoCapture().read() is a blocking operation, meaning our main program is stalled until a frame is read from the buffer and returned. The solution is simple: We can use threading to improve performance by alleviating the heavy I/O operations to a separate independent thread. The idea is to spawn another thread to handle polling the frames in parallel instead of relying on a single thread (our 'main' thread') that grabs the frames in sequential order. By using this approach, once the root thread finishes processing a frame, it simply needs to grab the current frame from the I/O thread without having to wait for blocking I/O operations.

The benefit of this approach is that if any camera dies, it will only stall the operation in that specific I/O thread without having any effect on the main program. With this method, it doesn't matter if any single camera experiences a technical issue since all the blocking I/O operation is in an individual thread instead of the main application's thread. You also mentioned:

I don't want to use multiprocessing ... I want to find a solution using only OpenCV

You want to be using threading instead of multiprocessing. The difference is that threads share the same memory space, while processes have their own independent memory stacks and do not share it with the main thread. This makes it a bit harder to share objects between processes with multiprocessing. Also I don't think it's possible to have a solution using only OpenCV due to the fact that cv2.videoCapture is a blocking operation. With that being said, the idea is to create a new thread for each camera that does nothing but poll for new frames while our main thread handles processing the current frame. You can create a new camera object for each RTSP stream

from threading import Thread
import cv2, time

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

    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
        cv2.imshow('frame', self.frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)

if __name__ == '__main__':
    video_stream_widget = VideoStreamWidget()
    while True:
        try:
            video_stream_widget.show_frame()
        except AttributeError:
            pass

For an implementation to handle multiple camera streams, take a look at capture multiple camera streams with OpenCV

For other similar threading and streaming from RTSP cameras

nathancy
  • 42,661
  • 14
  • 115
  • 137