diff --git a/backend/camera.py b/backend/camera.py index da9e409..115aacc 100644 --- a/backend/camera.py +++ b/backend/camera.py @@ -216,8 +216,9 @@ class Camera: def _preview_gphoto2(self) -> bytes | None: try: - fichier = self.camera.capture_preview() - donnees = bytes(fichier.get_data_and_size()) + with self._gp_lock: + fichier = self.camera.capture_preview() + donnees = bytes(fichier.get_data_and_size()) img = cv2.imdecode(np.frombuffer(donnees, dtype=np.uint8), cv2.IMREAD_COLOR) if img is not None: h, w = img.shape[:2] diff --git a/backend/main.py b/backend/main.py index 7e393b9..0f3f6a4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -48,11 +48,16 @@ _preview_lock = threading.Lock() _loop_principal: asyncio.AbstractEventLoop | None = None # loop asyncio principal +_preview_none_count = 0 +_PREVIEW_GRACE_FRAMES = 15 # ~500ms avant de declarer une erreur + + def _thread_preview(): - """Thread de fond : capture les frames DSLR en continu et les stocke.""" - global _derniere_frame_preview, _preview_actif + """Thread de fond : capture les frames DSLR en continu. + Tourne TOUJOURS quand le DSLR est connecte pour maintenir le miroir leve.""" + global _derniere_frame_preview while True: - if not _preview_actif or capture_en_cours: + if capture_en_cours or camera.mode != "gphoto2" or not camera.connectee: time.sleep(0.05) continue try: @@ -67,20 +72,26 @@ def _thread_preview(): async def _pusher_preview(): """Tache asyncio : pousse la derniere frame a tous les clients WS abonnes.""" - global _preview_actif + global _preview_actif, _preview_none_count while True: await asyncio.sleep(0.05) # 20 fps max envoye aux clients + if not _preview_actif: + _preview_none_count = 0 + continue if not clients_ws: _preview_actif = False continue with _preview_lock: donnees = _derniere_frame_preview if donnees is None: - # Signaler l'erreur si le DSLR est sense etre connecte - if camera.mode == "gphoto2" and _preview_actif: + _preview_none_count += 1 + # Grace period : attendre plusieurs frames avant de signaler l'erreur + if camera.mode == "gphoto2" and _preview_none_count > _PREVIEW_GRACE_FRAMES: await diffuser_ws({"type": "camera_erreur", "message": "DSLR ne repond pas au preview"}) _preview_actif = False + _preview_none_count = 0 continue + _preview_none_count = 0 loop = asyncio.get_event_loop() b64 = await loop.run_in_executor(None, lambda d=donnees: base64.b64encode(d).decode("ascii")) await diffuser_ws({"type": "preview", "image": f"data:image/jpeg;base64,{b64}"}) @@ -90,7 +101,8 @@ async def surveiller_dslr(): global _dslr_erreurs while True: await asyncio.sleep(5) - if capture_en_cours or _preview_actif: + # Ne pas interférer pendant que le thread preview utilise la camera + if capture_en_cours or (camera.mode == "gphoto2" and camera.connectee): continue try: if GPHOTO2_DISPONIBLE: