I'm trying to stream my webcam video to show it on an HTML page. To do that, I'm using Opencv to capture the video from my webcam. Then, I'm using GStreamer to send the video from the sender app to my webserver which I've built with Flask. For my Gstreamer pipeline, I'm using the codec h264 and UDP (IP: 127.0.0.1, port: 5000). For now, I'm using UDP locally to test my server. Later the camera won't necessarily be my webcam so I will send the video through the network. That is why I'm not directly getting the webcam video in the server application.
Problems are the following :
- I'm losing key frames so the image shown on the webserver is very noisy
- I have a big latency (more than 2 sec)
Does anyone know how to improve that? Is it a better way than using GStreamer?
Here is my code :
you have to put the file sender.py and server.py in a directory. In this same directory create another directory named templates. In this last directory put the file home.html
sender.py
import cv2
cap = cv2.VideoCapture(0) # open the camera
if cap.isOpened():
# get vcap property
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # float
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # float
else:
width = 1280
height = 720
fourcc = cv2.VideoWriter_fourcc(*'H264')
out = cv2.VideoWriter('appsrc ! videoconvert ! x264enc tune=zerolatency noise-reduction=0 speed-preset=ultrafast ! rtph264pay config-interval=1 pt=96 ! udpsink host=127.0.0.1 port=5000', fourcc, cv2.CAP_PROP_FPS, (1280, 720), True) #ouput GStreamer pipeline
if not out.isOpened():
print('VideoWriter not opened')
exit(0)
while cap.isOpened():
ret, frame = cap.read()
if ret:
# Write to pipeline
frame = cv2.resize(frame, (1280, 720))
out.write(frame)
cv2.imshow("test", frame)
if cv2.waitKey(1)&0xFF == ord('q'):
break
cap.release()
out.release()
server.py
import cv2
from flask import Flask, render_template, Response
import os
ip = "127.0.0.1"
port = 5001
cap = cv2.VideoCapture(
'udpsrc port=5000 caps = "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, payload=(int)96" ! rtph264depay ! decodebin ! videoconvert ! appsink',
cv2.CAP_GSTREAMER)
if not cap.isOpened():
print('VideoCapture not opened')
raise ValueError("Cam with Gstreamer pipeline not opened")
def generate():
while True:
ret, frame = cap.read()
if not ret:
print('empty frame')
break
frame = cv2.flip(frame, 1)
(flag, frame) = cv2.imencode(".png", frame)
yield b'--frame\r\n' b'Content-Type: image/png\r\n\r\n' + bytearray(frame) + b'\r\n'
def create_app(ip, port, test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True) # creation of the app
app.config.from_mapping(
SECRET_KEY='dev',
DATABASDE=os.path.join(app.instance_path, 'flaskr.sqlite')
)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path) # create instance directory because flask doesn't create it on its own
except OSError:
pass
@app.route('/')
def init_page():
return render_template('home.html', ip=ip, port=port)
@app.route('/video_feed')
def video_feed():
return Response(generate(), mimetype="multipart/x-mixed-replace; boundary=frame")
return app
if __name__ == "__main__":
app = create_app(ip, port)
app.run(host=ip, port=port, debug=True, threaded=True, use_reloader=False)
home.html
<html>
<head>
<title>Front Cam</title>
</head>
<body>
<h1>This is Front cam</h1> <br />
<img src="{{ url_for('video_feed') }}">
</body>
</html>
Thanks!