Cámara y micrófono
La vista previa aparecerá aquí cuando concedas acceso a la cámara.
Activa el control para solicitar acceso a la cámara y al micrófono.
Explora <usermedia> como control de confianza gestionado por el navegador para solicitar cámara y micrófono. Configura setConstraints(), conecta el MediaStream a una previsualización y compara el elemento nativo con un fallback progresivo basado en getUserMedia().
La vista previa aparecerá aquí cuando concedas acceso a la cámara.
Activa el control para solicitar acceso a la cámara y al micrófono.
<section class="usermedia-demo" aria-labelledby="usermedia-title">
<h2 id="usermedia-title">Cámara y micrófono</h2>
<div class="stream-preview">
<video
id="media-preview"
autoplay
playsinline
muted
hidden
aria-label="Vista previa de la cámara"
></video>
<p data-media-placeholder>La vista previa aparecerá aquí cuando concedas acceso a la cámara.</p>
</div>
<div class="media-actions">
<usermedia id="media-control" aria-describedby="media-status">
<button type="button" id="fallback-media-control">
Activar cámara y micrófono
</button>
</usermedia>
<button type="button" id="stop-media" disabled>
Detener captura
</button>
</div>
<p id="media-status" role="status" aria-live="polite">
Activa el control para solicitar acceso a la cámara y al micrófono.
</p>
</section>const mediaControl = document.querySelector('#media-control');
const fallbackButton = document.querySelector('#fallback-media-control');
const videoPreview = document.querySelector('#media-preview');
const placeholder = document.querySelector('[data-media-placeholder]');
const stopButton = document.querySelector('#stop-media');
const status = document.querySelector('#media-status');
const constraints = {
video: {
width: 1280,
height: 720,
facingMode: "user",
frameRate: 30,
},
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
};
let currentStream = null;
const showStatus = (message) => {
status.textContent = message;
};
const attachStream = (stream) => {
if (!stream) return;
currentStream = stream;
videoPreview.srcObject = stream;
videoPreview.hidden = false;
placeholder.hidden = true;
stopButton.disabled = false;
showStatus("Captura activa. El vídeo está silenciado para evitar acoples.");
};
const stopStream = () => {
currentStream?.getTracks().forEach((track) => track.stop());
currentStream = null;
videoPreview.srcObject = null;
videoPreview.hidden = true;
placeholder.hidden = false;
stopButton.disabled = true;
showStatus("La captura se ha detenido.");
};
if ('HTMLUserMediaElement' in window) {
mediaControl.setConstraints(constraints);
mediaControl.addEventListener('stream', () => {
attachStream(mediaControl.stream);
});
mediaControl.addEventListener('error', () => {
const errorName = mediaControl.error?.name ?? 'UnknownError';
showStatus(`No se pudo iniciar la captura: ${errorName}.`);
});
mediaControl.addEventListener('cancel', () => {
showStatus("La solicitud de permiso se ha cancelado.");
});
} else {
fallbackButton.addEventListener('click', async () => {
if (!navigator.mediaDevices?.getUserMedia) {
showStatus("La captura no está disponible en este contexto o navegador.");
return;
}
showStatus("Esperando la respuesta del navegador…");
try {
attachStream(await navigator.mediaDevices.getUserMedia(constraints));
} catch (error) {
const errorName = error instanceof DOMException ? error.name : 'UnknownError';
showStatus(`No se pudo iniciar la captura: ${errorName}.`);
}
});
}
stopButton.addEventListener('click', stopStream);