How to Build Web Camera Recorder Using OpenCV and Flask

Recently, I was inspired by a blog post “Python Live Video Streaming Example” and thinking whether it is possible to save the camera streaming to a video file. Based on the example code, I managed to figure out a solution. In this post, I want to share the process of building the web camera recorder using OpenCV and Flask.

web camera recorder

How to Use OpenCV to Record a Video

Let’s start with the code snippet posted on OpenCV website:

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))

while(cap.isOpened()):

    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0)
        # write the flipped frame
        out.write(frame)
        cv2.imshow('frame',frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()

After running the code on Windows, I got a 0KB file. The reason is the codec does not exist in my Windows 10. Replace fourcc with -1 to check the available codec list:

out = cv2.VideoWriter('output.avi', -1, 20.0, (640,480))

windows video codec

Instead of XVID, using MJPG will work.

fourcc = cv2.VideoWriter_fourcc(*'MJPG')

MJPG codec results in high size video. To get a smaller size, we need to install X264, which is not in the codec list by default.

Change codec to X264:

fourcc = cv2.VideoWriter_fourcc(*'X264')

Once you run the app, an annoying log window will pop up:

x264 log window

I have found the solution here. Open Windows registry and set log_level value 0.

close x264 log window

How to Build Camera Recorder in Web Browser

The source code of video_streaming_with_flask_example is handy.

def get_frame(self):
        success, image = self.video.read()
        # We are using Motion JPEG, but OpenCV defaults to capture raw images,
        # so we must encode it into JPEG in order to correctly display the
        # video stream.
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()

In my case, I have to save camera instance globally for video recording.

def video_stream():
    global video_camera 
    global global_frame

    if video_camera == None:
        video_camera = VideoCamera()
        
    while True:
        frame = video_camera.get_frame()

        if frame != None:
            global_frame = frame
            yield (b'--frame\r\n'
                    b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
        else:
            yield (b'--frame\r\n'
                            b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n')

 

Use XMLHttpRequest to start and stop video recording event.

Client:

    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
            // alert(xhr.responseText);
        }
    }
    xhr.open("POST", "/record_status");
    xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
    xhr.send(JSON.stringify({ status: "true" }));

Server:

@app.route('/record_status', methods=['POST'])
def record_status():
    global video_camera 
    if video_camera == None:
        video_camera = VideoCamera()

    json = request.get_json()

    status = json['status']

    if status == "true":
        video_camera.start_record()
        return jsonify(result="started")
    else:
        video_camera.stop_record()
        return jsonify(result="stopped")

Every time the event is triggered, create a new thread to save camera stream to a video file.

class RecordingThread (threading.Thread):
    def __init__(self, name, camera):
        threading.Thread.__init__(self)
        self.name = name
        self.isRunning = True

        self.cap = camera
        fourcc = cv2.VideoWriter_fourcc(*'MJPG')
        self.out = cv2.VideoWriter('./static/video.avi',fourcc, 20.0, (640,480))

    def run(self):
        while self.isRunning:
            ret, frame = self.cap.read()
            if ret:
                self.out.write(frame)

        self.out.release()

    def stop(self):
        self.isRunning = False

    def __del__(self):
        self.out.release()

Run the app:

python server.py

Note: if you are using Python 2, you will see the socket connection issue:

python server error

To get rid of the exception, enable threaded mode:

if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True)

Or use Python 3 to run the app.

streaming video recorder

Source Code

https://github.com/yushulx/web-camera-recorder

2 thoughts on “How to Build Web Camera Recorder Using OpenCV and Flask

  1. hi
    i am getting the following error what should i do?

    * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
    127.0.0.1 – – [17/Feb/2018 17:25:19] “GET / HTTP/1.1” 200 –
    127.0.0.1 – – [17/Feb/2018 17:25:19] “GET /static/recorder.js HTTP/1.1” 304 –
    127.0.0.1 – – [17/Feb/2018 17:25:19] “GET /video_viewer HTTP/1.1” 500 –
    Error on request:
    Traceback (most recent call last):
    File “/usr/local/lib/python3.4/dist-packages/werkzeug/serving.py”, line 270, in run_wsgi
    execute(self.server.app)
    File “/usr/local/lib/python3.4/dist-packages/werkzeug/serving.py”, line 260, in execute
    for data in application_iter:
    File “/usr/local/lib/python3.4/dist-packages/werkzeug/wsgi.py”, line 870, in __next__
    return self._next()
    File “/usr/local/lib/python3.4/dist-packages/werkzeug/wrappers.py”, line 82, in _iter_encoded
    for item in iterable:
    File “/home/pi/web-camera-recorder-master/server.py”, line 38, in video_stream
    frame = video_camera.get_frame()
    File “/home/pi/web-camera-recorder-master/camera.py”, line 63, in get_frame
    return jpeg.tobytes()
    AttributeError: ‘numpy.ndarray’ object has no attribute ‘tobytes’

Comments are closed.