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

Como fazer a integração com teclas de mídia do hardware, personalizar notificações de mídia e muito mais.

François Beaufort
François Beaufort

Para permitir que os usuários saibam o que está sendo reproduzido no navegador e controlem-o sem retornar à página que o iniciou, a API Media Session foi apresentada. 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 a câmera e desligar o som do microfone, ativar/desativar a 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ção 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 da 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 os usuários pressionarem a tecla de mídia "Próxima faixa" no teclado, os desenvolvedores da Web vão poder processar essa ação do usuário independente do navegador estar em primeiro ou 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 alguns segundos para trás.

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 possam limpar o estado.

Exemplo 4:se os usuários estiverem em uma videochamada, eles poderão pressionar o controle "ativar microfone" na janela picture-in-picture para impedir que o site 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 à 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. Neste 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 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 "total" para mostrar notificações de mídia somente quando a duração da mídia for 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 terminar, não é necessário "liberar" a sessão de mídia, já que a notificação desaparecerá automaticamente. Lembre-se de que o navigator.mediaSession.metadata será usado quando a próxima reprodução for iniciada. Por isso, é importante atualizá-lo 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 da arte de notificação é compatível com URLs de blob e URLs de 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. Em 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 no contexto incorporado. Confira o snippet abaixo.
<iframe id="iframe">
  <video>...</video>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

Também é possível adicionar informações de capítulos individuais, como o título da seção, o carimbo de data/hora e uma imagem de captura de tela aos metadados de mídia. Isso permite que os usuários naveguem pelo conteúdo da mídia.

navigator.mediaSession.metadata = new MediaMetadata({
  // title, artist, album, artwork, ...
  chapterInfo: [{
    title: 'Chapter 1',
    startTime: 0,
    artwork: [
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  }, {
    title: 'Chapter 2',
    startTime: 42,
    artwork: [
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  }]
});
Informações sobre o capítulo exibidas em uma notificação de mídia do ChromeOS.
Notificação de mídia com capítulos no ChromeOS.

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 em um fone de ouvido, outro dispositivo remoto, 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, é recomendável 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.`);
  }
}

Cancelar a configuração de um gerenciador de ações da 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, exceto pelo fato de que o processamento de um evento significa que o navegador cessa os comportamentos padrão e o usa como 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 mostrados a menos que o gerenciador adequado seja definido.

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

Reproduzir / pausar

A ação "play" indica que o usuário quer retomar a reprodução de mídia, enquanto "pause" indica um desejo de 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, gerencie as ações de mídia "tocar" e "pausar", conforme mostrado abaixo.

O navegador pode considerar que um site não está reproduzindo mídia durante a busca ou o carregamento, por exemplo. Nesse caso, substitua esse comportamento definindo navigator.mediaSession.playbackState como "playing" ou "paused" para garantir que a interface 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 tiver uma noção de início, ou passar para o item anterior na playlist, se a reprodução de mídia tiver 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, caso ela tenha uma noção de 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.
});

Retroceder / 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 um desejo de avançar o tempo de reprodução de mídia por um curto período. Em ambos os casos, um período curto significa alguns segundos.

O valor 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 informado (por exemplo, undefined), use um período 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 seekTime fornecido no gerenciador de ações é o tempo em segundos para mover 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 dessa 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

Exibir com precisão a posição de reprodução de mídia em uma notificação é tão simples quanto definir o estado da posição no 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 precisa 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 mostrar controles para o microfone e a câmera, e para desligar. Quando o usuário clica nelas, o site processa com as ações de videoconferência abaixo. Por exemplo, consulte a Amostra 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 ativar ou desativar o som do microfone. O método setMicrophoneActive(isActive) informa ao navegador se o site considera o microfone 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 que o site está 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, o site as processa usando a 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 para o 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 durante a apresentação.

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

Compatibilidade com navegadores

  • 111
  • 111
  • x
  • x

Exemplos

Confira alguns exemplos de sessões de mídia com a Blender Foundation e o trabalho de Jan Morgenstern (link em inglês).

Um screencast que ilustra a API Media Session.

Recursos