72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
"""Accès simple à la caméra d'Alfred, avec aperçu MJPEG activé à la demande.
|
|
|
|
Une seule lecture par instance Alfred. La vidéo ne démarre que lorsque
|
|
l'utilisateur clique sur Activer, pour éviter de monopoliser la caméra.
|
|
"""
|
|
import os
|
|
import threading
|
|
import time
|
|
|
|
_CAMERA_LOCK = threading.Lock()
|
|
|
|
|
|
def snapshot(path, index=0):
|
|
import cv2
|
|
if not _CAMERA_LOCK.acquire(blocking=False):
|
|
raise RuntimeError('Caméra déjà utilisée par la prévisualisation de ' + os.getenv('ALFRED_NAME', 'Alfred')) # ALFRED_IDENTITY_GLASS_V1
|
|
try:
|
|
cap = cv2.VideoCapture(index)
|
|
try:
|
|
ok, frame = cap.read()
|
|
finally:
|
|
cap.release()
|
|
if not ok:
|
|
raise RuntimeError('Impossible de lire la caméra locale')
|
|
cv2.imwrite(path, frame)
|
|
return path
|
|
finally:
|
|
_CAMERA_LOCK.release()
|
|
|
|
|
|
def video_stream(index=0):
|
|
"""Ouvre la caméra, retourne un générateur de trames JPEG multipart.
|
|
|
|
L'appelant doit retourner ce générateur dans une réponse Flask en streaming.
|
|
L'absence de cv2 ou de webcam déclenche une exception avant HTTP 200.
|
|
"""
|
|
import cv2
|
|
if not _CAMERA_LOCK.acquire(blocking=False):
|
|
raise RuntimeError('Prévisualisation vidéo déjà active')
|
|
|
|
cap = None
|
|
try:
|
|
cap = cv2.VideoCapture(index)
|
|
if not cap.isOpened():
|
|
cap.release()
|
|
raise RuntimeError(f'Caméra {index} indisponible')
|
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, int(os.getenv('CAMERA_WIDTH', '640')))
|
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, int(os.getenv('CAMERA_HEIGHT', '480')))
|
|
fps = min(15.0, max(1.0, float(os.getenv('CAMERA_FPS', '8'))))
|
|
quality = min(95, max(35, int(os.getenv('CAMERA_JPEG_QUALITY', '75'))))
|
|
except Exception:
|
|
if cap is not None:
|
|
cap.release()
|
|
_CAMERA_LOCK.release()
|
|
raise
|
|
|
|
def frames():
|
|
try:
|
|
while True:
|
|
ok, frame = cap.read()
|
|
if not ok:
|
|
break
|
|
ok_jpeg, encoded = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, quality])
|
|
if ok_jpeg:
|
|
yield b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + encoded.tobytes() + b'\r\n'
|
|
time.sleep(1.0 / fps)
|
|
finally:
|
|
cap.release()
|
|
_CAMERA_LOCK.release()
|
|
|
|
return frames()
|