Bonjour,
Pour un projet, nous devons faire en sorte de streamer le flux video d’une caméra sur une page web. Pour cela nous utilisons opencv pour capturer la vidéo puis nous l’envoyons via gstreamer sur un port udp. On récupère ensuite ce flux gstreamer avec Opencv et on l’affiche dans un serveur web fait via flask.
Les problèmes sont les suivants :
- Nous avons une perte importante de 'key-frame" lors de l’envoie du flux en h264 ce qui fait que sur le serveur les images s’affichent très bruités
- Nous avons une grande latence
Voici un un exemple minimal pour repoduire notre application :
Programme python d’envoie du flux video
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()
Serveur de réception
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)
Pour que le serveur fonctionne, il faut, dans le même dossier que le fichier serveur, créer un dossier nommé "templates" et y mettre la page html suivante :
<html>
<head>
<title>Front Cam</title>
</head>
<body>
<h1>This is Front cam</h1> <br />
<img src="{{ url_for('video_feed') }}">
</body>
</html>
Auriez-vous une idée de pourquoi nous avons ces problèmes avec notre solutions ? Avez-vous d’autres propositions de solutions pour envoyer une vidéo à travers le réseau et l’afficher sur une page web ?
Merci d’avance, Bonne journée!