Fix race condition gphoto2 : lock non-bloquant preview cede la place a la capture

This commit is contained in:
2026-04-09 07:28:33 +02:00
parent 57ac16d366
commit b4ec4dbf3f

View File

@@ -1,6 +1,7 @@
import io import io
import logging import logging
import subprocess import subprocess
import threading
import time import time
from datetime import datetime from datetime import datetime
@@ -66,6 +67,7 @@ class Camera:
self.webcam = None self.webcam = None
self.webcam_index = -1 self.webcam_index = -1
self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi self.preview_dslr_ok = True # True si le dernier preview DSLR a reussi
self._gp_lock = threading.Lock() # Un seul appel gphoto2 a la fois
def connecter(self, source=None) -> bool: def connecter(self, source=None) -> bool:
"""Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc.""" """Connecte la camera. source: 'gphoto2', 'webcam:0', 'webcam:2', etc."""
@@ -147,21 +149,20 @@ class Camera:
for attempt in range(3): for attempt in range(3):
try: try:
chemin_camera = self.camera.capture(gp.GP_CAPTURE_IMAGE) with self._gp_lock:
fichier_camera = gp.CameraFile() chemin_camera = self.camera.capture(gp.GP_CAPTURE_IMAGE)
self.camera.file_get( fichier_camera = gp.CameraFile()
chemin_camera.folder, chemin_camera.name, gp.GP_FILE_TYPE_NORMAL, fichier_camera self.camera.file_get(
) chemin_camera.folder, chemin_camera.name, gp.GP_FILE_TYPE_NORMAL, fichier_camera
tmp_path = str(chemin_dest) + ".tmp" )
fichier_camera.save(tmp_path) tmp_path = str(chemin_dest) + ".tmp"
fichier_camera.save(tmp_path)
img = PILImage.open(tmp_path) img = PILImage.open(tmp_path)
if img.width > 4000: if img.width > 4000:
ratio = 4000 / img.width ratio = 4000 / img.width
img = img.resize((4000, int(img.height * ratio)), PILImage.LANCZOS) img = img.resize((4000, int(img.height * ratio)), PILImage.LANCZOS)
img.save(str(chemin_dest), "JPEG", quality=92) img.save(str(chemin_dest), "JPEG", quality=92)
Path(tmp_path).unlink(missing_ok=True) Path(tmp_path).unlink(missing_ok=True)
log.info(f"Photo capturee (DSLR) : {chemin_dest} ({img.width}x{img.height})") log.info(f"Photo capturee (DSLR) : {chemin_dest} ({img.width}x{img.height})")
return chemin_dest return chemin_dest
except gp.GPhoto2Error as e: except gp.GPhoto2Error as e:
@@ -224,6 +225,8 @@ class Camera:
log.warning(f"Viewfinder non supporté : {e}") log.warning(f"Viewfinder non supporté : {e}")
def _preview_gphoto2(self) -> bytes | None: def _preview_gphoto2(self) -> bytes | None:
if not self._gp_lock.acquire(blocking=False):
return None # Capture en cours, on saute ce frame
try: try:
fichier = self.camera.capture_preview() fichier = self.camera.capture_preview()
donnees = bytes(fichier.get_data_and_size()) donnees = bytes(fichier.get_data_and_size())
@@ -238,6 +241,8 @@ class Camera:
except gp.GPhoto2Error as e: except gp.GPhoto2Error as e:
log.error(f"Erreur preview DSLR : {e}") log.error(f"Erreur preview DSLR : {e}")
return None return None
finally:
self._gp_lock.release()
def _preview_webcam(self) -> bytes | None: def _preview_webcam(self) -> bytes | None:
if not self.webcam or not self.webcam.isOpened(): if not self.webcam or not self.webcam.isOpened():