0Apoyar

Playground de <usermedia> en HTML

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().

Preguntar a la IA

Playground de <usermedia> en HTML

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.
Comprobando soporte…

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.

HTML
<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>
JS
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);