// Rôle unique de ce fichier : ouvrir la caméra et fournir des pixels à C#. // AUCUN décodage ici — il est fait par ZXing.Net côté C# (décision actée dans CLAUDE.md). // getUserMedia et canvas/getImageData n'ont pas d'équivalent C#, d'où ces quelques lignes. let flux = null; let video = null; let canvas = null; let ctx = null; let derniersPixels = null; // getUserMedia exige un contexte sécurisé : HTTPS, ou localhost. // Une IP de réseau local (192.168.x.x) en http NE l'est PAS — piège classique en dev. export function diagnostic() { if (!window.isSecureContext) return 'contexte-non-securise'; if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) return 'api-absente'; return 'ok'; } export async function demarrer(element) { video = element; // facingMode "environment" = caméra arrière : on scanne un livre tenu devant soi. // "ideal" et non "exact" pour ne pas échouer sur un PC qui n'a qu'une webcam frontale. flux = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' }, width: { ideal: 1280 }, height: { ideal: 720 } }, audio: false, }); video.srcObject = flux; video.setAttribute('playsinline', ''); await video.play(); canvas = document.createElement('canvas'); ctx = canvas.getContext('2d', { willReadFrequently: true }); } // Capture une frame et la garde en mémoire. Renvoie [largeur, hauteur], ou null si // la vidéo n'est pas encore prête. Les pixels se récupèrent ensuite via pixels(). export function capturer(largeurMax, partHauteur) { if (!video || !ctx || video.readyState < 2 || !video.videoWidth) return null; const echelle = Math.min(1, largeurMax / video.videoWidth); const l = Math.round(video.videoWidth * echelle); const h = Math.round(video.videoHeight * echelle); if (canvas.width !== l || canvas.height !== h) { canvas.width = l; canvas.height = h; } ctx.drawImage(video, 0, 0, l, h); // On ne renvoie que la bande centrale, celle que l'utilisateur vise : moins de pixels // à transférer et à décoder, donc moins de travail par frame côté C#. const hb = Math.max(1, Math.round(h * partHauteur)); const y = Math.round((h - hb) / 2); derniersPixels = new Uint8Array(ctx.getImageData(0, y, l, hb).data.buffer); return [l, hb]; } export function pixels() { return derniersPixels; } // Libérer la caméra : sinon le voyant reste allumé et la batterie se vide, // ce que l'utilisateur perçoit — à raison — comme un dysfonctionnement. export function arreter() { if (flux) { flux.getTracks().forEach(t => t.stop()); flux = null; } if (video) { video.srcObject = null; video = null; } canvas = null; ctx = null; derniersPixels = null; }