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 de 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á tocando no navegador e controlem essa ação sem retornar à página que a iniciou, a API Media Session foi surgiu. Ele permite que os desenvolvedores Web personalizem essa experiência por meio de metadados em notificações de mídia personalizadas, eventos de mídia como reproduzir, pausar buscar, rastrear eventos de alteração e videoconferência, como ativar/desativar som microfone, ligar/desligar câmera e desligar. Essas personalizações são disponíveis em vários contextos, incluindo hubs de mídia em computadores, notificações de mídia em dispositivos móveis e wearables. Vou descrever essas personalizações neste artigo.

Capturas de tela de contextos de sessão de mídia.
Hub de mídia no computador, notificação de mídia em dispositivos móveis e dispositivo wearable.

Sobre a API Media Session

A API Media session oferece vários benefícios e recursos:

  • As 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 dispositivos wearable pareados.
  • O hub de mídia está disponível para 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

  • Chrome: 73.
  • Borda: 79.
  • Firefox: 82.
  • Safari: 15

Origem

Alguns exemplos ilustrarão alguns desses pontos.

Exemplo 1:se os usuários pressionarem "Próxima faixa" a tecla de mídia do teclado, os desenvolvedores Web podem processar essa ação do usuário se o navegador estiver em primeiro plano. ou no plano de fundo.

Exemplo 2:se os usuários ouvem um podcast na Web enquanto o dispositivo a tela está bloqueada, ainda é possível pressionar o botão ícone do cadeado controles de mídia da tela para que os desenvolvedores web atrasem o tempo de reprodução em poucos segundos.

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

Exemplo 4:se os usuários estiverem em uma videochamada, eles podem pressionar o botão "Alternar microfone" na janela picture-in-picture para impedir que o site recebendo dados do microfone.

Tudo isso é feito em duas interfaces diferentes: MediaSession. e a interface MediaMetadata. A primeira permite que os usuários controlem a reprodução. O segundo é como você informa a MediaSession o que precisa ser controlado.

Para ilustrar, a imagem abaixo mostra como essas interfaces se relacionam com parâmetros controles de mídia. Neste caso, uma notificação de mídia em um dispositivo móvel.

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

Mostre aos usuários o que está tocando

Quando um site está tocando áudio ou vídeo, os usuários recebem mídia automaticamente. notificações na bandeja de notificações em dispositivos móveis ou no hub de mídia ativado computador. O navegador faz o possível para mostrar as informações adequadas usando o do título do documento e da maior imagem de ícone que ele encontrar. Com a sessão de mídia API, é possível personalizar a notificação de mídia com alguns recursos de mídia metadados, como título, nome do artista, nome do álbum e capa, conforme mostrado abaixo.

O Chrome solicita a versão "completa" a seleção de áudio para mostrar notificações de mídia somente quando o duração da mídia de pelo menos cinco segundos. Isso garante que sons incidentais como pings, não mostram 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 será necessário "liberar" a sessão de mídia como a notificação desaparecerá automaticamente. Lembre-se de que O app navigator.mediaSession.metadata será usado quando a próxima reprodução começar No entanto, Por isso, é importante atualizá-lo quando a fonte de reprodução de mídia para garantir que as informações relevantes sejam exibidas 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 de dados.
  • Se nenhuma arte estiver 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 Google Chrome para Android é 512x512. Para dispositivos mais simples, é 256x256.
  • O atributo title do elemento HTML de mídia é usado no jogo Widget do macOS.
  • Se o recurso de mídia estiver incorporado (por exemplo, em um iframe), a API Media Session informações precisam ser definidas a partir do 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>

Você também pode adicionar informações de capítulos individuais aos metadados de mídia, como o título da seção, o carimbo de data/hora e uma captura de tela. 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 de capítulos 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 para os usuários quando eles interagem com a reprodução de mídia atual. As ações são análogos e funcionam de forma muito semelhante aos eventos. Assim como os eventos, as ações são implementado pela definição de manipuladores em um objeto apropriado, uma instância de MediaSession, neste caso. Algumas ações são acionadas quando os usuários pressionam de um fone de ouvido, de outro dispositivo remoto, de um teclado, ou interagir com um 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 da sessão de mídia podem não ser suportadas, é recomendado use 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.`);
  }
}

Desativar um gerenciador de ações de sessão de mídia é tão fácil quanto configurá-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 de sessão de mídia vão persistir nas reproduções de mídia. É semelhante ao padrão de listener de eventos, exceto pelo fato de que lidar com um evento significa que o navegador deixa de executar qualquer comportamento padrão e o usa como que indica que o site oferece suporte à ação de mídia. Portanto, os controles de ação de mídia não será exibido, a menos que o gerenciador de ações adequado esteja definido.

Captura de tela do widget Tocando agora 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ê-lo temporariamente.

A opção de reproduzir/pausar é sempre mostrado em uma notificação de mídia e os eventos eventos de mídia são manipulados automaticamente pelo navegador. Para substituir o padrão comportamento, trate de "reproduzir" e "pausar" ações de mídia, conforme mostrado abaixo.

O navegador pode considerar um site que não está reproduzindo mídia ao buscar ou o carregamento, por exemplo. Nesse caso, modifique esse comportamento definindo navigator.mediaSession.playbackState para "playing" ou "paused" para garantir a interface do site permanece 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 o a reprodução da mídia atual desde o início, se ela tiver uma noção de ou voltar ao item anterior da playlist se a reprodução de mídia tenha uma noção de 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 ideia 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 apropriado.

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 a mídia. o tempo de reprodução é reduzido em um curto período, enquanto "seekforward" indica um desejo para avançar o tempo de reprodução da mídia por um curto período. Em ambos os casos, uma um período curto significa alguns segundos.

O valor seekOffset fornecido no gerenciador de ações é o tempo em segundos para mudar o tempo de reprodução da mídia. Se ele não for informado (por exemplo, undefined), use um tempo adequado (por exemplo, de 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 a reprodução de mídia. em um horário específico.

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

O booleano fastSeek fornecido no gerenciador de ações será verdadeiro se a ação for sendo chamado várias vezes como parte de uma sequência e essa não é 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 definir o estado da posição em um momento apropriado, conforme mostrado abaixo. A o estado da posição é uma combinação da velocidade de reprodução de mídia, duração e no horário 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 fornecida e positiva. A posição deve ser positiva e menor que a duração. A velocidade do vídeo precisa ser maior 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 da 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 vídeo chamada em uma janela picture-in-picture, o navegador pode exibir controles para o microfone e a câmera e para desligar. Quando o usuário clica nelas, o site trata delas pelo vídeo. de videoconferência abaixo. Confira este 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. o microfone. O método setMicrophoneActive(isActive) informa ao navegador se o site atualmente considera que o microfone está ativo.

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 câmera ligada ou desligada. O método setCameraActive(isActive) indica se a 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 exibir controles para navegar pelos slides. Quando o usuário clicar neles, o site os tratará pela API Media Session. Para um exemplo, consulte o exemplo Apresentação de slides.

Slide anterior

A ação "previousslide" indica que o usuário quer voltar para o slide anterior durante a apresentação.

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

Compatibilidade com navegadores

  • Chrome: 111
  • Borda: 111.
  • Firefox: incompatível.
  • Safari: incompatível.

Próximo slide

A ação "nextslide" indica que o usuário quer acessar o próximo slide. durante a apresentação de slides.

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

Compatibilidade com navegadores

  • Chrome: 111
  • Borda: 111.
  • Firefox: incompatível.
  • Safari: incompatível.

Amostras

Confira alguns exemplos de Sessões de mídia com o Remixer Foundation (em inglês) e O trabalho de Jan Morgenstern.

Um screencast que ilustra a API Media Session.

Recursos