1

I am trying to record a video using opencv in python when doing multithreading within the thread that displays the stream on the window. I m fairly new to multithreading and I am not sure what is the reason I am unable to get a video recorded. I save a file but it does not have the stream in it. Pointers greatly appreciated.This is my code:

import cv2
import os
import threading
import shutil
import json
import re
import datetime
import time
now=datetime.datetime.now()



class camThread(threading.Thread):
    def __init__(self, previewName, camID):
        threading.Thread.__init__(self)
        self.previewName = previewName

        self.camID = camID

    def run(self):
        print("Starting " + self.previewName)
        camPreview(self.previewName, self.camID)

def camPreview(previewName, camID):
    cv2.namedWindow(previewName)
    cam = cv2.VideoCapture(camID)
    cam.set(cv2.CAP_PROP_FRAME_WIDTH, 480)
    cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
    if cam.isOpened():
        rval, frame = cam.read()
        frame_width = int(cam.get(3))
        frame_height = int(cam.get(4))

    else:
        rval = False

    while rval:
        cv2.namedWindow(previewName, cv2.WINDOW_NORMAL)
        if (camID == 2):
            frame= cv2.flip(frame,-1)
       # cv2.setWindowProperty(previewName, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
        cv2.imshow(previewName, frame)
      # cam.set(CV_CAP_PROP_SETTINGS, 0)
        rval, frame = cam.read()

        key = cv2.waitKey(20)
        if key == 115 :
            Cam1="Cam"+str(camID)+"_"+timestr
            ts=datetime.datetime.now()
            filename="{}.avi".format(Cam1+ts.strftime("%Y%m%d_%H-%M-%S"))
            out=cv2.VideoWriter(filename,cv2.VideoWriter_fourcc('M','J', 'P','G'),10,(480,720))
            out.write(frame)
            if key == 27:
              print("Stopping recording")
              break
        if key == 27:  # exit on ESC
            break
    cv2.destroyWindow(previewName)

# Create threads as follows
thread1 = camThread("Camera 1", 0)
thread2 = camThread("Camera 2", 2)
thread3 = camThread("Camera 3", 3)
timestr=str(now.strftime("%Y%m%d_%H-%M-%S"))

print("Working Directory:")
print(timestr)

#thread1.start()
thread2.start()
thread3.start()
print()
print("Active threads", threading.activeCount())

2 Answers 2

2
from threading import Thread
import cv2
import time

class VideoWriterWidget(object):
    def __init__(self, video_file_name, src=0):
        # Create a VideoCapture object
        self.frame_name = str(src) # if using webcams, else just use src as it is.
        self.video_file = video_file_name
        self.video_file_name = video_file_name + '.avi'
        self.capture = cv2.VideoCapture(src)

        # Default resolutions of the frame are obtained (system dependent)
        self.frame_width = int(self.capture.get(3))
        self.frame_height = int(self.capture.get(4))

        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

        # Start another thread to show/save frames
        self.start_recording()
        print('initialized {}'.format(self.video_file))

    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()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow(self.frame_name, self.frame)

        # Press Q on keyboard to stop recording
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            self.output_video.release()
            cv2.destroyAllWindows()
            exit(1)

    def save_frame(self):
        # Save obtained frame into video output file
        self.output_video.write(self.frame)

    def start_recording(self):
        # Create another thread to show/save frames
        def start_recording_thread():
            while True:
                try:
                    self.show_frame()
                    self.save_frame()
                except AttributeError:
                    pass
        self.recording_thread = Thread(target=start_recording_thread, args=())
        self.recording_thread.daemon = True
        self.recording_thread.start()

if __name__ == '__main__':
    src1 = 'Your link1'
    video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
    src2 = 'Your link2'
    video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
    src3 = 'Your link3'
    video_writer_widget3 = VideoWriterWidget('Camera 3', src3)

    # Since each video player is in its own thread, we need to keep the main thread alive.
    # Keep spinning using time.sleep() so the background threads keep running
    # Threads are set to daemon=True so they will automatically die 
    # when the main thread dies
    while True:
        time.sleep(5)
Sign up to request clarification or add additional context in comments.

Comments

2

I think you're on the right track but I was unable to save a file with your code. Here's a video-stream-to-video widget using multithreading to obtain the frames. There are two threads for each camera stream:

  • Thread #1 - Dedicated to only reading frames from the camera stream.
  • Thread #2 - Dedicated for processing frames (showing and writing).

We separate reading frames from showing/writing because cv2.VideoCapture.read() is a blocking operation. Thus we read frames in its own independent thread to 'improve' FPS by reducing latency due to I/O operations. In addition, by isolating frame capture to its own thread, there will always be a frame ready to be processed instead of having to wait for the I/O operation to complete and return a new frame. In our second thread dedicated to processing, we are now freely able to show and save each frame to our output file.

Also by encapsulating all this into a single object, we can create a set of threads for each camera which scales easily no matter how many cameras are being used. Since each camera stream is spawned in a background thread, we must keep the main thread alive. Be sure to change the src string to your own camera. Here's an example of recording three video streams simultaneously.

from threading import Thread
import cv2
import time

class VideoWriterWidget(object):
    def __init__(self, video_file_name, src=0):
        # Create a VideoCapture object
        self.frame_name = str(src)
        self.video_file = video_file_name
        self.video_file_name = video_file_name + '.avi'
        self.capture = cv2.VideoCapture(src)

        # Default resolutions of the frame are obtained (system dependent)
        self.frame_width = int(self.capture.get(3))
        self.frame_height = int(self.capture.get(4))

        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

        # Start another thread to show/save frames
        self.start_recording()
        print('initialized {}'.format(self.video_file))

    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()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow(self.frame_name, self.frame)

        # Press Q on keyboard to stop recording
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            self.output_video.release()
            cv2.destroyAllWindows()
            exit(1)

    def save_frame(self):
        # Save obtained frame into video output file
        self.output_video.write(self.frame)

    def start_recording(self):
        # Create another thread to show/save frames
        def start_recording_thread():
            while True:
                try:
                    self.show_frame()
                    self.save_frame()
                except AttributeError:
                    pass
        self.recording_thread = Thread(target=start_recording_thread, args=())
        self.recording_thread.daemon = True
        self.recording_thread.start()

if __name__ == '__main__':
    src1 = 'Your link1'
    video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
    src2 = 'Your link2'
    video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
    src3 = 'Your link3'
    video_writer_widget3 = VideoWriterWidget('Camera 3', src3)

    # Since each video player is in its own thread, we need to keep the main thread alive.
    # Keep spinning using time.sleep() so the background threads keep running
    # Threads are set to daemon=True so they will automatically die 
    # when the main thread dies
    while True:
        time.sleep(5)

20 Comments

Hi Nathancy, thank you so much for the detailed reply. I did try to use your code but it failed to display the stream and also record the videos after updating my source for the cameras. When I run the code, i get exceptions on the console and the program hangs and does not exit. So I have to force quite the application and so I have taken a snapshot of the the console for you to see what is happening. This is a link to the image: i.imgur.com/LhIAzSn.png. Could you please guide me as to why the line which displays the frame raises an exception when using the threading?
Since you're unable to display the stream, I have a feeling the src is wrong. From the docs it mentions that to use webcam, you pass an integer number to cv2.VideoCapture(). So maybe the src is a string instead of a integer. Try only playing one stream at a time and change src1 = 'Your link1' to src1 = 0
I did try with one stream at a time but that gave me the same error. But were you suggesting that I was passing a string to cv2.VideoCapture()?
No try pass a integer to cv2.VideoCapture(). When I was testing, I used my RTSP stream link which is a string but to use webcam, the docs say to use a integer.
Yes I was passing an integer, 0, 2 and 3. And I got these exceptions and then it was hanging.
|

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.