4

below is the code i used to play multiple videos in parallel using multi threading pool. but only one video is playing for each input. i want each video to open separately. not combined

import concurrent.futures

RTSP_URL = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4"
RTSP_List = [RTSP_URL, RTSP_URL, RTSP_URL, RTSP_URL]


def url_to_video(url):
    video = cv2.VideoCapture(url)
    while True:
        _, frame = video.read()
        cv2.imshow("RTSP", frame)
        k = cv2.waitKey(1)
        if k == ord('q'):
            break
    video.release()
    cv2.destroyAllWindows()


while True:
    with concurrent.futures.ThreadPoolExecutor() as executor:
        executor.map(url_to_video, RTSP_List)```

how to play each video separately.


0

1 Answer 1

8

you just need each thread to use a different name for the window in cv2.imshow, so that each thread will generate a different window, and you should place them somewhere distinct so that they aren't appearing one over the other, i just added in index to them so that each distinct index will have a position on screen and different title, also you shouldn't destroy all windows when one is done ...

import concurrent.futures
import cv2
RTSP_URL = r"C:/Users/Ahmed/Desktop/test.mp4"
RTSP_List = [(RTSP_URL,0), (RTSP_URL,1), (RTSP_URL,2), (RTSP_URL,3)]


def url_to_video(tup):
    url,index = tup
    video = cv2.VideoCapture(url)
    while True:
        _, frame = video.read()
        cv2.imshow(f"RTSP {index}", frame)
        cv2.moveWindow(f"RTSP {index}", index*300, 0)
        k = cv2.waitKey(1)
        if k == ord('q'):
            break
    video.release()


while True:
    with concurrent.futures.ThreadPoolExecutor() as executor:
        executor.map(url_to_video, RTSP_List)
    cv2.destroyAllWindows()

This code works on Windows because win32 backend allows each window to belong to a thread, for other operating systems and backends you can use the following non-threaded code.

import cv2
import asyncio

RTSP_URL = r"C:/Users/Ahmed/Desktop/test.mp4"
RTSP_List = [(RTSP_URL,0), (RTSP_URL,1), (RTSP_URL,2), (RTSP_URL,3)]

break_all = False
async def url_to_video(tup):
    global break_all
    url,index = tup
    video = cv2.VideoCapture(url)
    while True:
        result, frame = await asyncio.get_event_loop().run_in_executor(None, video.read)
        if not result:
            break
        cv2.imshow(f"RTSP {index}", frame)
        cv2.moveWindow(f"RTSP {index}", index*300, 0)
        k = cv2.waitKey(1)
        if k == ord('q'):
            break_all = True
        if break_all:
            break
    video.release()

async def main():
    global break_all
    break_all = False
    await asyncio.gather(*[url_to_video(x) for x in RTSP_List])

while True:
    asyncio.run(main())
    cv2.destroyAllWindows()
Sign up to request clarification or add additional context in comments.

6 Comments

That will cause trouble. OpenCV simply uses some GUI toolkit. many GUI toolkits don't tolerate GUI toolkit calls coming from multiple threads.
@ChristophRackwitz you just nerd-sniped me into writing a non-threaded version :|
yeah no, that's not gonna work either. you just assume that all sources have precisely the same frame rate. that's only valid to assume if they're genlocked. otherwise, they will have slightly differing rates, and eventually your synchronous reading will cause visible delays.
@ChristophRackwitz now it should work for mixed framerate cameras
I haven't tested this. It looks wild. remember that video.read() will block. if that blocks the default executor, which all the streams will use because you specify None... you'd need a separate thread for each video stream, and I guess that means multiple executors. I'm inclined to post my own answer.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.