Programação Por Equipe DevBras

Capa do artigo: Autoplay de vídeo bloqueado no HTML: mudo e com pausa

Autoplay de vídeo bloqueado no HTML: mudo e com pausa

Vídeo de fundo não toca sozinho? O navegador bloqueia autoplay com som. Veja o teste que prova, o HTML com muted e playsinline e o botão de pausa da WCAG.

O vídeo da capa funciona no computador de quem fez o site e, no navegador do cliente, fica parado no primeiro quadro. Quase sempre o motivo é o mesmo: ele tenta começar com som, e o navegador recusa. Chrome, Firefox e Safari só deixam um vídeo tocar sozinho quando ele está mudo ou quando a pessoa já interagiu com o site.

Este post trata do vídeo decorativo de fundo, aquele que roda atrás do título de uma landing page: por que ele não toca, como escrever o HTML que toca em qualquer navegador e como deixar a pessoa parar o movimento. Para vídeo com fala, que precisa de controles e legenda, o caminho é outro e está em vídeo HTML5 com controles e legenda.

O que o navegador permite

A política de autoplay do Chrome diz que autoplay mudo é sempre permitido. Com som, só quando a pessoa já clicou ou tocou na página, quando o índice de engajamento com mídia daquele site passou de um limite (no desktop) ou quando o site foi instalado como app. O guia de autoplay da MDN descreve regras parecidas nos outros navegadores.

Na prática, um visitante novo vindo do Google nunca cumpre essas condições. Se o vídeo depende de som para começar, ele não começa.

O teste que mostra a diferença

Para ver isso sem depender da sorte, uma página com dois vídeos, um com faixa de áudio e outro com muted, chama play() nos dois ao carregar. O play() devolve uma promessa, e a recusa chega como erro:

async function tenta(id) {
  const v = document.getElementById(id);
  try { await v.play(); res[id] = "tocando"; }
  catch (e) { res[id] = e.name; }
}

Rodando a página no Chrome 156 com Puppeteer, primeiro com a regra padrão do desktop (--autoplay-policy=document-user-activation-required) e depois liberando tudo, o resultado foi:

document-user-activation-required {"com":"NotAllowedError","mudo":"tocando"}
 após clique: tocando
no-user-gesture-required {"mudo":"tocando","com":"tocando"}

O vídeo com som é recusado com NotAllowedError, o mudo toca, e depois de um clique na página o vídeo com som também toca. A última linha, com a regra liberada, lembra o que acontece na máquina de quem desenvolve: depois de muito uso do próprio site, o índice de engajamento passa do limite e o som é permitido. Para um visitante novo, isso não vale.

O vídeo de fundo que toca

O exemplo é a capa de uma academia, com o horário das aulas por cima de um vídeo de treino. O vídeo é decoração: a informação está no texto, não nele.

<!doctype html>
<html lang="pt-BR">
<meta charset="utf-8">
<style>
  .capa { position: relative; min-height: 60vh; overflow: hidden; }
  .capa video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  .capa .texto { position: relative; padding: 2rem; color: #fff; background: rgb(0 0 0 / .55); max-width: 32rem; }
  .capa button { position: absolute; right: 1rem; bottom: 1rem; }
</style>
<section class="capa">
  <video id="video-capa" muted loop playsinline preload="metadata"
         poster="capa.jpg" aria-hidden="true">
    <source src="fundo.mp4" type="video/mp4">
  </video>
  <div class="texto">
    <h1>Aulas de segunda a sábado, das 6h às 22h</h1>
    <p>Primeira aula experimental grátis.</p>
  </div>
  <button type="button" id="alternar">Pausar vídeo</button>
</section>
<script>
  const video = document.getElementById("video-capa");
  const botao = document.getElementById("alternar");
  const menosMovimento = matchMedia("(prefers-reduced-motion: reduce)");

  function mostrar(tocando) {
    botao.textContent = tocando ? "Pausar vídeo" : "Tocar vídeo";
  }

  async function tocar() {
    try {
      await video.play();
      mostrar(true);
    } catch (erro) {
      // NotAllowedError: o navegador recusou; fica o poster e o botão
      mostrar(false);
    }
  }

  botao.addEventListener("click", () => {
    if (video.paused) tocar();
    else { video.pause(); mostrar(false); }
  });

  if (menosMovimento.matches) mostrar(false);
  else tocar();
</script>
</html>

Os detalhes que fazem diferença:

  • muted é o que libera o autoplay. Sem ele, o play() é recusado como no teste.
  • playsinline faz o vídeo tocar dentro da página no iPhone, em vez de abrir em tela cheia (veja o elemento video na MDN).
  • poster mostra uma imagem enquanto o vídeo carrega e quando ele não toca, então a capa nunca fica preta.
  • O play() pelo script, em vez do atributo autoplay, permite não tocar para quem pediu menos movimento e tratar a recusa.
  • aria-hidden="true" no vídeo, porque ele não tem informação; o texto por cima é o que o leitor de tela lê.

Também vale tirar a faixa de áudio do arquivo, para que ninguém ouça som se alguém remover o muted sem querer:

ffmpeg -i treino.mp4 -an -c:v libx264 -pix_fmt yuv420p -movflags +faststart fundo.mp4

O mesmo teste com Puppeteer, simulando com emulateMediaFeatures as duas preferências de movimento, confirmou o comportamento da capa:

no-preference ao abrir: {"paused":false,"botao":"Pausar vídeo"}
no-preference após clicar: {"paused":true,"botao":"Tocar vídeo"}
reduce ao abrir: {"paused":true,"botao":"Tocar vídeo"}
reduce após clicar: {"paused":false,"botao":"Pausar vídeo"}

Sem preferência, o vídeo toca e o botão pausa. Com prefers-reduced-motion: reduce, a página abre com o vídeo parado no poster e só toca se a pessoa pedir.

Por que o botão de pausa não é opcional

O critério 2.2.2 da WCAG (Pausar, parar, ocultar), de nível A, pede um jeito de pausar qualquer conteúdo em movimento que comece sozinho, dure mais de 5 segundos e apareça junto com outro conteúdo. Um vídeo em loop atrás do título se encaixa nisso. Movimento contínuo atrapalha quem tem dificuldade de atenção e pode causar enjoo em quem tem distúrbios vestibulares; é para essas pessoas que existe a preferência do sistema lida por prefers-reduced-motion. Mais sobre ela em animação que respeita quem pede menos movimento.

Se em algum momento o som for indispensável, o critério 1.4.2 (Controle de áudio) exige que qualquer áudio tocado sozinho por mais de 3 segundos possa ser pausado ou ter o volume controlado à parte. Como o navegador já bloqueia esse caso para visitantes novos, o mais simples é nunca depender dele.

Antes de publicar

Abra a página numa janela anônima, que não tem histórico de interação com o site, e no celular com o modo de economia de bateria ligado. Se o vídeo não carregar em algum navegador, confira o tipo do arquivo servido, como em MIME de vídeo no servidor. O HTML e os scripts de teste deste post rodaram no Chrome 156; em Safari e Firefox o comportamento segue as regras descritas na MDN, mas não foi testado aqui.

Avalie este artigo

Referência: https://msdn.microsoft.com/pt-br/library/hh924820(v=vs.85).aspx