Como integrar com teclas de mídia de hardware, personalizar notificações de mídia e muito mais.
Para que os usuários saibam o que está sendo reproduzido no navegador e o controlem sem retornar à página que o iniciou, a API Media Session foi introduzida. Ele permite que os desenvolvedores da Web personalizem essa experiência com metadados em notificações de mídia personalizadas, eventos de mídia, como reprodução, pausa, busca, mudança de faixa e eventos de videoconferência, como silenciar/ativar microfone, ativar/desativar 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.
Sobre a API Media Session
A API Media session oferece vários benefícios e recursos:
- Chaves de mídia de hardware são aceitas.
- 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 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 do Google Assistente em dispositivos móveis está disponível.
Confira alguns exemplos para ilustrar alguns desses pontos.
Exemplo 1:se os usuários pressionarem a tecla de mídia "Próxima faixa" no teclado, os desenvolvedores Web poderão processar essa ação do usuário com o navegador em primeiro ou segundo plano.
Exemplo 2: se os usuários ouvirem um podcast na Web enquanto a tela do dispositivo estiver bloqueada, eles ainda poderão tocar no ícone "procurar para trás" nos controles de mídia da tela de bloqueio para que os desenvolvedores da Web movam o tempo de reprodução para trás por alguns segundos.
Exemplo 3:se os usuários tiverem guias que reproduzem áudio, eles poderão interromper facilmente a reprodução no hub de mídia no computador para que os desenvolvedores 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.
Tudo isso é feito por duas interfaces diferentes: a interface MediaSession
e a interface MediaMetadata
. O primeiro permite que os usuários controlem o que está
sendo reproduzido. A segunda é como você informa 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 um dispositivo móvel.
Informe aos usuários o que está sendo reproduzido
Quando um site está reproduzindo áudio ou vídeo, os usuários recebem automaticamente notificações de mídia na bandeja de notificações em dispositivos móveis ou no hub de mídia em computadores. O navegador faz o possível para mostrar as informações adequadas usando o título do documento e a imagem de ícone maior que puder encontrar. Com a API Media Session, é possível personalizar a notificação de mídia com alguns metadados de mídia mais ricos, como título, nome do artista, nome do álbum e arte, conforme mostrado abaixo.
O Chrome solicita o foco de áudio "total" para mostrar notificações de mídia somente quando a duração da mídia é de pelo menos 5 segundos. Isso garante que sons incidentais, como ruídos, 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, porque a
notificação desaparece automaticamente. No entanto,
navigator.mediaSession.metadata
será usado quando a próxima reprodução começar. Por isso, é importante atualizar o conteúdo 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 observações sobre os metadados de mídia.
- A matriz de arte da notificação oferece suporte a URLs de dados e blobs.
- Se nenhuma arte for definida e houver uma imagem de ícone (especificada usando
<link rel=icon>
) no tamanho desejado, as notificações de mídia vão usar essa imagem. - O tamanho de destino da arte da notificação no Chrome para Android é
512x512
. Para dispositivos básicos, é256x256
. - O atributo
title
do elemento HTML de mídia é usado no widget "Now playing" 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>
Você também pode 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' },
]
}]
});
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 de maneira semelhante aos eventos. Assim como os eventos, as ações são
implementadas definindo manipuladores em um objeto apropriado, uma instância de
MediaSession
, neste caso. Algumas ações são acionadas quando os usuários pressionam
botões de um fone de ouvido, outro dispositivo remoto, um teclado ou interagem com uma
notificação de mídia.
Como algumas ações de sessão de mídia podem não ser compatíveis, é 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.`);
}
}
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 manipuladores de ação da sessão de mídia vão persistir durante as reproduções de mídia. Isso é semelhante ao padrão do listener de eventos, exceto que o processamento de um evento significa que o navegador para de fazer qualquer 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 mostrados, a menos que o manipulador de ação adequado seja definido.
Reproduzir / pausar
A ação "play"
indica que o usuário quer retomar a reprodução de mídia,
enquanto "pause"
indica que ele quer interromper temporariamente.
O ícone "tocar/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 "play" e "pause", conforme mostrado abaixo.
O navegador pode considerar que um site não está reproduzindo mídia ao procurar ou
carregar, 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 do início, se a reprodução de mídia tiver uma noção de
início, ou mover para o item anterior na playlist, se a reprodução de mídia
tiver 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 da playlist se ela tiver 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 apropriado.
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
da mídia para trás por um curto período, enquanto "seekforward"
indica o desejo
de mover o tempo de reprodução da mídia para frente por um curto período. Em ambos os casos, um
período curto significa alguns segundos.
O valor seekOffset
fornecido no action handler é o tempo em segundos para
mover a reprodução de mídia. Se ele não for fornecido (por exemplo, undefined
),
use um tempo razoável (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.
});
Buscar um horário específico
A ação "seekto"
indica que o usuário quer mover o tempo de reprodução
da mídia para um horário específico.
O valor seekTime
fornecido no action handler é o tempo em segundos para
mover a reprodução de mídia.
O booleano fastSeek
fornecido no action handler é 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
naquela 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 de 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, duração e hora atual.
A duração precisa ser fornecida e positiva. A posição precisa ser positiva e menor que a duração. A taxa de reprodução 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();
});
É fácil redefinir o estado da posição para 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 as processa usando as ações de videoconferência abaixo. Confira este exemplo de videoconferência.
Ativar ou desativar o microfone
A ação "togglemicrophone"
indica que o usuário quer ativar ou desativar
o 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);
});
Abrir/fechar 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 clicar neles, o site vai processá-los pela API Media Session. Para um 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 slides.
navigator.mediaSession.setActionHandler('previousslide', () => {
// Show previous slide.
});
Compatibilidade com navegadores
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
Amostras
Confira alguns exemplos de Sessões de mídia com a Liquider Foundation e o trabalho de Jan Morgenstern (links em inglês).
Recursos
- Especificações de sessão de mídia: wicg.github.io/mediasession (link em inglês)
- Problemas de especificação: github.com/WICG/mediasession/issues (link em inglês)
- Bugs do Chrome: crbug.com (link em inglês)