Aller au contenu

Caméra

Accéder à la caméra depuis votre page avec les API web standard.

La caméra ne passe pas par window.mairies : votre page utilise l’API web standard navigator.mediaDevices.getUserMedia, comme dans un navigateur.

Deux autorisations sont nécessaires, dans cet ordre :

  1. La case « Caméra » cochée dans la section « WebApp avancée » de la carte lien. L’application accorde alors l’accès vidéo à votre origine, et à elle seule.
  2. L’accord de l’utilisateur, demandé par le système du téléphone la première fois que l’application utilise la caméra. S’il a déjà répondu, sa réponse s’applique sans nouvelle question ; il peut la modifier dans les réglages du téléphone.

Quand la case est cochée, mairies.commands contient 'camera'. Vous pouvez vous en servir pour décider d’afficher votre bouton de scan :

const cameraAllowed = Boolean(window.mairies?.commands.includes('camera'));
const video = document.querySelector('#scanner');
async function startCamera() {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }, // caméra arrière
audio: false, // le micro n’est jamais accordé
});
video.srcObject = stream;
video.setAttribute('playsinline', ''); // aperçu dans la page sur iPhone
video.muted = true;
await video.play();
return stream;
}

Points à respecter :

  • Vidéo seule. Toute demande qui inclut le micro (audio: true, ou caméra et micro ensemble) est refusée en bloc.
  • playsinline sur l’élément <video> : sans cet attribut, l’iPhone peut afficher l’aperçu en plein écran au lieu de l’intégrer à la page.
  • Arrêtez le flux dès que vous n’en avez plus besoin, pour libérer la caméra et éteindre le voyant :
function stopCamera(stream) {
stream.getTracks().forEach((track) => track.stop());
video.srcObject = null;
}

La demande peut venir de la page principale ou d’une iframe de la même origine que le lien ; une iframe d’une autre origine est refusée.

Quand votre page filme, la caméra est occupée par la vue web, et la lampe se pilote différemment selon la plateforme.

  • Android : mairies.torch.on(), off() et toggle() échouent avec UNAVAILABLE, et isAvailable() renvoie false. Utilisez la contrainte web standard sur la piste vidéo :

    const [track] = stream.getVideoTracks();
    const capabilities = track.getCapabilities?.() ?? {};
    if (capabilities.torch) {
    await track.applyConstraints({ advanced: [{ torch: true }] });
    }
  • iPhone : mairies.torch.on() peut fonctionner pendant la capture ; en cas d’échec, l’erreur est UNAVAILABLE. La contrainte web torch n’est pas prise en charge par Safari.

Stratégie recommandée : tentez mairies.torch.on(), puis, en cas d’UNAVAILABLE, essayez applyConstraints.

async function setTorchDuringScan(stream, on) {
try {
const result = await window.mairies.torch[on ? 'on' : 'off']();
return result.on;
} catch (error) {
if (error.code !== 'UNAVAILABLE') throw error;
const [track] = stream.getVideoTracks();
if (!track.getCapabilities?.().torch) return false; // aucune solution
await track.applyConstraints({ advanced: [{ torch: on }] });
return on;
}
}

getUserMedia est rejetée avec les erreurs standard du web. Les plus courantes :

error.name Cause probable
NotAllowedError Case « Caméra » non cochée, demande depuis une autre origine, micro demandé, ou refus de l’utilisateur.
NotFoundError Aucune caméra ne correspond à la demande.
NotReadableError Caméra déjà utilisée par une autre application.
try {
stream = await startCamera();
} catch (error) {
if (error.name === 'NotAllowedError') {
// Proposer une saisie manuelle du code.
}
}

Le message affiché à l’utilisateur doit lui proposer une solution : saisir le code à la main, ou autoriser la caméra dans les réglages du téléphone s’il l’a refusée.