Personalize notificações de mídia e controles de mídia com a API Media Session

Como integrar com teclas de mídia de hardware, personalizar notificações de mídia e muito mais.

François Beaufort
François Beaufort

A API Media Session foi apresentada para que os usuários saibam o que está sendo reproduzido no navegador e controlem-o sem retornar à página que o iniciou. Ele permite que os desenvolvedores da Web personalizem essa experiência usando metadados em notificações de mídia personalizadas, eventos de mídia, como reprodução, pausa, busca, alteração de faixa e eventos de videoconferência, como ativar/desativar som do microfone, ligar/desligar câmera e desligar. Essas personalizações estão disponíveis em vários contextos, incluindo hubs de mídia em computadores, notificações de mídia em dispositivos móveis e até mesmo em dispositivos wearable. Vou descrever essas personalizações neste artigo.

Capturas de tela dos contextos de sessão de mídia.
Hub de mídia no computador, notificações de mídia no dispositivo móvel e dispositivo wearable.

Sobre a API Media Session

A API de sessão de mídia oferece vários benefícios e recursos:

  • Teclas de mídia de hardware são compatíveis.
  • As notificações de mídia são personalizadas em dispositivos móveis, computadores e wearables pareados.
  • O hub de mídia está disponível em computadores.
  • Os controles de mídia da tela de bloqueio estão disponíveis no ChromeOS e em dispositivos móveis.
  • Os controles de janela picture-in-picture estão disponíveis para reprodução de áudio, videoconferência e apresentação de slides.
  • A integração com o Google Assistente em dispositivos móveis está disponível.

Compatibilidade com navegadores

  • 73
  • 79
  • 82
  • 15

Origem

Alguns exemplos ilustram alguns desses pontos.

Exemplo 1:se o usuário pressionar a tecla de mídia "próxima faixa" do teclado, os desenvolvedores da Web vão poder processar essa ação do usuário, esteja o navegador em primeiro plano ou em segundo plano.

Exemplo 2:se os usuários ouvem um podcast na Web com a tela do dispositivo bloqueada, eles ainda podem tocar no ícone "voltar" nos controles de mídia da tela de bloqueio para que os desenvolvedores da Web movam o tempo de reprodução para trás em alguns segundos.

Exemplo 3:se os usuários tiverem guias que reproduzem áudio, eles poderão interromper facilmente a reprodução do hub de mídia no computador para que os desenvolvedores da Web tenham a chance de limpar o estado.

Exemplo 4:se os usuários estiverem em uma videochamada, eles vão poder pressionar o controle "microfone" na janela "Picture-in-Picture" para que o site não receba dados do microfone.

Isso é feito em duas interfaces diferentes: MediaSession e MediaMetadata. O primeiro permite que os usuários controlem o que está tocando. A segunda é como você diz ao MediaSession o que precisa ser controlado.

Para ilustrar, a imagem abaixo mostra como essas interfaces estão relacionadas a controles de mídia específicos. Nesse caso, uma notificação de mídia em dispositivos móveis.

Ilustração de interfaces de sessão de mídia.
Anatomia de uma notificação de mídia em dispositivos móveis
.

Permitir que os usuários saibam o que está tocando

Quando um site está reproduzindo áudio ou vídeo, os usuários recebem automaticamente as notificações de mídia na bandeja de notificações do dispositivo móvel ou no hub de mídia no computador. O navegador faz o possível para mostrar as informações adequadas usando o título do documento e a maior imagem de ícone que encontrar. Com a API Media Session, é possível personalizar a notificação de mídia com metadados mais avançados, como título, nome do artista, nome do álbum e arte, conforme mostrado abaixo.

O Chrome solicita a seleção de áudio "completa" para mostrar notificações de mídia somente quando a duração da mídia é de pelo menos cinco segundos. Isso garante que sons incidentais, como toques, não mostrem notificações.

// After media (video or audio) starts playing
await document.querySelector("video").play();

if ("mediaSession" in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    artist: 'Rick Astley',
    album: 'Whenever You Need Somebody',
    artwork: [
      { src: 'https://via.placeholder.com/96',   sizes: '96x96',   type: 'image/png' },
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/192', sizes: '192x192', type: 'image/png' },
      { src: 'https://via.placeholder.com/256', sizes: '256x256', type: 'image/png' },
      { src: 'https://via.placeholder.com/384', sizes: '384x384', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  });

  // TODO: Update playback state.
}

Quando a reprodução termina, não é necessário "liberar" a sessão de mídia, uma vez que a notificação desaparece automaticamente. Não esqueça que navigator.mediaSession.metadata vai ser usado quando a próxima reprodução começar. Por isso, é importante atualizá-la quando a fonte de reprodução de mídia mudar para garantir que as informações relevantes sejam mostradas na notificação de mídia.

Há algumas coisas a serem observadas sobre os metadados de mídia.

  • A matriz de arte de notificação é compatível com URLs de blob e dados.
  • Se nenhuma arte for definida e houver uma imagem de ícone (especificada usando <link rel=icon>) em um tamanho desejável, as notificações de mídia a usarão.
  • O tamanho do destino da arte da notificação no Chrome para Android é 512x512. Para dispositivos mais simples, é 256x256.
  • O atributo title do elemento HTML de mídia é usado no widget "Tocando agora" do macOS.
  • Se o recurso de mídia estiver incorporado (por exemplo, em um iframe), as informações da API Media Session precisam ser definidas a partir do contexto incorporado. Veja o snippet abaixo.
<iframe id="iframe">
  <video>...</video>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Permitir que os usuários controlem o que está tocando

Uma ação de sessão de mídia é uma ação (por exemplo, "reproduzir" ou "pausar") que um site pode processar para os usuários quando eles interagem com a reprodução de mídia atual. As ações são análogas e funcionam da mesma forma que os eventos. Assim como os eventos, as ações são implementadas definindo gerenciadores em um objeto apropriado, uma instância de MediaSession, nesse caso. Algumas ações são acionadas quando os usuários pressionam botões de um fone de ouvido, outro dispositivo remoto ou um teclado ou interagem com uma notificação de mídia.

Captura de tela de uma notificação de mídia no Windows 10.
Notificação de mídia personalizada no Windows 10.

Como algumas ações de sessão de mídia podem não ter suporte, recomendamos usar um bloco try…catch ao configurá-las.

const actionHandlers = [
  ['play',          () => { /* ... */ }],
  ['pause',         () => { /* ... */ }],
  ['previoustrack', () => { /* ... */ }],
  ['nexttrack',     () => { /* ... */ }],
  ['stop',          () => { /* ... */ }],
  ['seekbackward',  (details) => { /* ... */ }],
  ['seekforward',   (details) => { /* ... */ }],
  ['seekto',        (details) => { /* ... */ }],
  /* Video conferencing actions */
  ['togglemicrophone', () => { /* ... */ }],
  ['togglecamera',     () => { /* ... */ }],
  ['hangup',           () => { /* ... */ }],
  /* Presenting slides actions */
  ['previousslide', () => { /* ... */ }],
  ['nextslide',     () => { /* ... */ }],
];

for (const [action, handler] of actionHandlers) {
  try {
    navigator.mediaSession.setActionHandler(action, handler);
  } catch (error) {
    console.log(`The media session action "${action}" is not supported yet.`);
  }
}

A desativação de um gerenciador de ações de sessão de mídia é tão fácil quanto defini-lo como null.

try {
  // Unset the "nexttrack" action handler at the end of a playlist.
  navigator.mediaSession.setActionHandler('nexttrack', null);
} catch (error) {
  console.log(`The media session action "nexttrack" is not supported yet.`);
}

Depois de definidos, os gerenciadores de ação da sessão de mídia vão persistir durante as reproduções de mídia. Isso é semelhante ao padrão de listener de eventos, mas com o processamento de um evento, o navegador interrompe o comportamento padrão e usa isso como um sinal de que o site oferece suporte à ação de mídia. Portanto, os controles de ação de mídia não serão exibidos, a menos que o gerenciador de ação adequado seja definido.

Captura de tela do widget Tocando agora no macOS Big Sur.
Widget do Tocando agora no macOS Big Sur.

Tocar / pausar

A ação "play" indica que o usuário quer retomar a reprodução de mídia, enquanto "pause" indica que quer interrompê-la temporariamente.

O ícone "reproduzir/pausar" sempre é mostrado em uma notificação de mídia, e os eventos de mídia relacionados são processados automaticamente pelo navegador. Para substituir o comportamento padrão, processe as ações de mídia "tocar" e "pausar", conforme mostrado abaixo.

O navegador pode considerar que um site não está tocando mídia ao buscar ou carregar, por exemplo. Nesse caso, substitua esse comportamento definindo navigator.mediaSession.playbackState como "playing" ou "paused" para garantir que a IU do site permaneça sincronizada com os controles de notificação de mídia.

const video = document.querySelector('video');

navigator.mediaSession.setActionHandler('play', async () => {
  // Resume playback
  await video.play();
});

navigator.mediaSession.setActionHandler('pause', () => {
  // Pause active playback
  video.pause();
});

video.addEventListener('play', () => {
  navigator.mediaSession.playbackState = 'playing';
});

video.addEventListener('pause', () => {
  navigator.mediaSession.playbackState = 'paused';
});

Faixa anterior

A ação "previoustrack" indica que o usuário quer iniciar a reprodução de mídia atual desde o início, se a reprodução tem uma noção de início, ou passar para o item anterior na playlist, se a reprodução tem uma noção de uma playlist.

navigator.mediaSession.setActionHandler('previoustrack', () => {
  // Play previous track.
});

Próxima faixa

A ação "nexttrack" indica que o usuário quer mover a reprodução de mídia para o próximo item na playlist se a reprodução de mídia tiver uma noção de uma playlist.

navigator.mediaSession.setActionHandler('nexttrack', () => {
  // Play next track.
});

Parar

A ação "stop" indica que o usuário quer interromper a reprodução de mídia e limpar o estado, se adequado.

navigator.mediaSession.setActionHandler('stop', () => {
  // Stop playback and clear state if appropriate.
});

Voltar / avançar

A ação "seekbackward" indica que o usuário quer mover o tempo de reprodução de mídia para trás em um curto período, enquanto "seekforward" indica o desejo de avançar o tempo de reprodução de mídia por um curto período. Em ambos os casos, um curto período significa alguns segundos.

O valor de seekOffset fornecido no gerenciador de ações é o tempo em segundos para mover o tempo de reprodução de mídia. Se ele não for fornecido (por exemplo, undefined), use um horário adequado (por exemplo, 10 a 30 segundos).

const video = document.querySelector('video');
const defaultSkipTime = 10; /* Time to skip in seconds by default */

navigator.mediaSession.setActionHandler('seekbackward', (details) => {
  const skipTime = details.seekOffset || defaultSkipTime;
  video.currentTime = Math.max(video.currentTime - skipTime, 0);
  // TODO: Update playback state.
});

navigator.mediaSession.setActionHandler('seekforward', (details) => {
  const skipTime = details.seekOffset || defaultSkipTime;
  video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
  // TODO: Update playback state.
});

Procurar um horário específico

A ação "seekto" indica que o usuário quer mover o tempo de reprodução de mídia para um horário específico.

O valor de seekTime fornecido no gerenciador de ações é o tempo em segundos para o tempo de reprodução de mídia.

O booleano fastSeek fornecido no gerenciador de ações será verdadeiro se a ação estiver sendo chamada várias vezes como parte de uma sequência e essa não for a última chamada nessa sequência.

const video = document.querySelector('video');

navigator.mediaSession.setActionHandler('seekto', (details) => {
  if (details.fastSeek && 'fastSeek' in video) {
    // Only use fast seek if supported.
    video.fastSeek(details.seekTime);
    return;
  }
  video.currentTime = details.seekTime;
  // TODO: Update playback state.
});

Definir a posição da reprodução

A exibição precisa da posição de reprodução de mídia em uma notificação é tão simples quanto definir o estado da posição em um momento adequado, conforme mostrado abaixo. O estado de posição é uma combinação da taxa de reprodução de mídia, da duração e do tempo atual.

Captura de tela dos controles de mídia da tela de bloqueio no ChromeOS.
Controles de mídia da tela de bloqueio no ChromeOS.

A duração precisa ser positiva e fornecida. A posição precisa ser positiva e menor que a duração. A taxa de reprodução deve ser maior do que 0.

const video = document.querySelector('video');

function updatePositionState() {
  if ('setPositionState' in navigator.mediaSession) {
    navigator.mediaSession.setPositionState({
      duration: video.duration,
      playbackRate: video.playbackRate,
      position: video.currentTime,
    });
  }
}

// When video starts playing, update duration.
await video.play();
updatePositionState();

// When user wants to seek backward, update position.
navigator.mediaSession.setActionHandler('seekbackward', (details) => {
  /* ... */
  updatePositionState();
});

// When user wants to seek forward, update position.
navigator.mediaSession.setActionHandler('seekforward', (details) => {
  /* ... */
  updatePositionState();
});

// When user wants to seek to a specific time, update position.
navigator.mediaSession.setActionHandler('seekto', (details) => {
  /* ... */
  updatePositionState();
});

// When video playback rate changes, update position state.
video.addEventListener('ratechange', (event) => {
  updatePositionState();
});

Redefinir o estado de posição é tão fácil quanto defini-lo como null.

// Reset position state when media is reset.
navigator.mediaSession.setPositionState(null);

Ações de videoconferência

Quando o usuário coloca a videochamada em uma janela picture-in-picture, o navegador pode exibir controles para o microfone, a câmera e para desligar. Quando o usuário clica neles, o site realiza as ações de videoconferência abaixo. Consulte o exemplo de videoconferência.

Captura de tela dos controles de videoconferência em uma janela picture-in-picture.
Controles de videoconferência em uma janela picture-in-picture.

Alternar microfone

A ação "togglemicrophone" indica que o usuário quer desativar ou ativar o som do microfone. O método setMicrophoneActive(isActive) informa ao navegador se o site considera que o microfone está ativo no momento.

let isMicrophoneActive = false;

navigator.mediaSession.setActionHandler('togglemicrophone', () => {
  if (isMicrophoneActive) {
    // Mute the microphone.
  } else {
    // Unmute the microphone.
  }
  isMicrophoneActive = !isMicrophoneActive;
  navigator.mediaSession.setMicrophoneActive(isMicrophoneActive);
});

Alternar câmera

A ação "togglecamera" indica que o usuário quer ativar ou desativar a câmera ativa. O método setCameraActive(isActive) indica se o navegador considera o site ativo.

let isCameraActive = false;

navigator.mediaSession.setActionHandler('togglecamera', () => {
  if (isCameraActive) {
    // Disable the camera.
  } else {
    // Enable the camera.
  }
  isCameraActive = !isCameraActive;
  navigator.mediaSession.setCameraActive(isCameraActive);
});

Desligar

A ação "hangup" indica que o usuário quer encerrar uma chamada.

navigator.mediaSession.setActionHandler('hangup', () => {
  // End the call.
});

Ações de apresentação de slides

Quando o usuário coloca a apresentação de slides em uma janela Picture-in-Picture, o navegador pode mostrar controles para navegar pelos slides. Quando o usuário clica, ele é processado pelo site pela API Media Session. Por exemplo, consulte o exemplo de apresentação de slides.

Slide anterior

A ação "previousslide" indica que o usuário quer voltar ao slide anterior ao apresentar os slides.

navigator.mediaSession.setActionHandler('previousslide', () => {
  // Show previous slide.
});

Compatibilidade com navegadores

  • 111
  • 111
  • x
  • x

Próximo slide

A ação "nextslide" indica que o usuário quer ir para o próximo slide ao apresentar slides.

navigator.mediaSession.setActionHandler('nextslide', () => {
  // Show next slide.
});

Compatibilidade com navegadores

  • 111
  • 111
  • x
  • x

Trechos

Confira alguns exemplos de sessões de mídia com a Blender Foundation e o trabalho de Jan Morgenstern.

Um screencast que ilustra a API Media Session.

Recursos