Fix preview + miroir : thread continu, grace period, lock gphoto2
- _thread_preview tourne TOUJOURS quand DSLR connecté (plus de condition _preview_actif) → capture_preview() appelé en continu → miroir reste levé - Grace period 15 frames (~500ms) avant de signaler camera_erreur : évite la race condition qui tuait le preview avant la 1ère frame - _preview_gphoto2 protégé par _gp_lock (accès concurrent safe) - surveiller_dslr ne tourne plus quand le DSLR est en mode gphoto2 connecté Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user