Dzięki nowemu interfejsowi Media Session API możesz teraz dostosowywać powiadomienia o multimediach, podając metadane multimediów odtwarzanych przez aplikację internetową. Umożliwia też obsługę zdarzeń związanych z multimediami, takich jak przeskakiwanie lub zmiana ścieżki, które mogą pochodzić z powiadomień lub kluczy multimediów. Brzmi dobrze? Wypróbuj oficjalne przykłady sesji multimediów.
Interfejs Media Session API jest obsługiwany w Chrome 57 (wersja beta w lutym 2017 roku, stabilna w marcu 2017 roku).

Gimme what I want
Czy znasz już interfejs MediaSession API i po prostu wracasz do kopiowania i wklejania gotowego kodu? Oto on.
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://dummyimage.com/96x96', sizes: '96x96', type: 'image/png' },
{ src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png' },
{ src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png' },
{ src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png' },
{ src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png' },
{ src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png' },
]
});
navigator.mediaSession.setActionHandler('play', function() {});
navigator.mediaSession.setActionHandler('pause', function() {});
navigator.mediaSession.setActionHandler('seekbackward', function() {});
navigator.mediaSession.setActionHandler('seekforward', function() {});
navigator.mediaSession.setActionHandler('previoustrack', function() {});
navigator.mediaSession.setActionHandler('nexttrack', function() {});
}
Poznaj kod
Zagrajmy 🎷
Dodaj do strony internetowej prosty element <audio>
i przypisz do niego kilka źródeł multimediów, aby przeglądarka mogła wybrać to, które działa najlepiej.
<audio controls>
<source src="audio.mp3" type="audio/mp3"/>
<source src="audio.ogg" type="audio/ogg"/>
</audio>
Jak pewnie wiesz, w Chrome na Androida funkcja autoplay
jest wyłączona w przypadku elementów audio, co oznacza, że musimy użyć metody play()
elementu audio. Ta metoda musi być wywoływana przez gestykację użytkownika, np. dotknięcie lub kliknięcie myszką.
Oznacza to nasłuchiwanie zdarzeń pointerup
, click
i touchend
. Inaczej mówiąc, użytkownik musi kliknąć przycisk, aby aplikacja internetowa mogła wygenerować dźwięk.
playButton.addEventListener('pointerup', function(event) {
let audio = document.querySelector('audio');
// User interacted with the page. Let's play audio...
audio.play()
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error) });
});
Jeśli nie chcesz odtwarzać dźwięku zaraz po pierwszej interakcji, zalecam użycie metody load()
elementu audio. Jest to jeden ze sposobów, dzięki któremu przeglądarka może śledzić, czy użytkownik wchodził w interakcję z elementem. Pamiętaj, że może to również ułatwić płynne odtwarzanie, ponieważ treści będą już załadowane.
let audio = document.querySelector('audio');
welcomeButton.addEventListener('pointerup', function(event) {
// User interacted with the page. Let's load audio...
<strong>audio.load()</strong>
.then(_ => { /* Show play button for instance... */ })
.catch(error => { console.log(error) });
});
// Later...
playButton.addEventListener('pointerup', function(event) {
<strong>audio.play()</strong>
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error) });
});
Dostosowywanie powiadomienia
Gdy aplikacja internetowa odtwarza dźwięk, na pasku powiadomień pojawia się powiadomienie multimedialne. Na Androidzie Chrome stara się wyświetlać odpowiednie informacje, korzystając z tytułu dokumentu i największego znalezionego obrazu ikony.


Ustawianie metadanych
Zobaczmy, jak dostosować powiadomienie o multimediów, ustawiając niektóre metadane sesji multimedialnej, takie jak tytuł, wykonawca, nazwa albumu i grafika za pomocą interfejsu Media Session API.
// When audio starts playing...
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://dummyimage.com/96x96', sizes: '96x96', type: 'image/png' },
{ src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png' },
{ src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png' },
{ src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png' },
{ src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png' },
{ src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png' },
]
});
}
Po zakończeniu odtwarzania nie musisz „zwalniać” sesji multimediów, ponieważ powiadomienie zniknie automatycznie. Pamiętaj, że podczas odtwarzania będzie używana aktualna wersja navigator.mediaSession.metadata
. Dlatego musisz go zaktualizować, aby mieć pewność, że zawsze wyświetlasz odpowiednie informacje w powiadomieniu o mediach.
Poprzedni utwór / Następny utwór
Jeśli Twoja aplikacja internetowa zawiera playlistę, możesz zezwolić użytkownikowi na poruszanie się po niej bezpośrednio z powiadomienia o multimediach za pomocą ikon „Poprzedni utwór” i „Następny utwór”.
let audio = document.createElement('audio');
let playlist = ['audio1.mp3', 'audio2.mp3', 'audio3.mp3'];
let index = 0;
navigator.mediaSession.setActionHandler('previoustrack', function() {
// User clicked "Previous Track" media notification icon.
index = (index - 1 + playlist.length) % playlist.length;
playAudio();
});
navigator.mediaSession.setActionHandler('nexttrack', function() {
// User clicked "Next Track" media notification icon.
index = (index + 1) % playlist.length;
playAudio();
});
function playAudio() {
audio.src = playlist[index];
audio.play()
.then(_ => { /* Set up media session... */ })
.catch(error => { console.log(error); });
}
playButton.addEventListener('pointerup', function(event) {
playAudio();
});
Pamiętaj, że przetwarzanie działań związanych z multimediami pozostanie niezmienione. Jest to bardzo podobne do wzorca eventListener, z tym że obsługa zdarzenia oznacza, że przeglądarka przestaje wykonywać jakiekolwiek domyślne działania i używa tego jako sygnału, że Twoja aplikacja internetowa obsługuje działanie dotyczące multimediów. Dlatego opcje działania multimediów nie będą widoczne, dopóki nie skonfigurujesz odpowiedniego modułu obsługi działania.
Odznaczenie przetwarzacza akcji typu Media Action jest tak samo proste jak przypisanie go do null
.
Przewiń do tyłu / przewiń do przodu
Interfejs API sesji multimediów umożliwia wyświetlanie ikon „Przewinąć do tyłu” i „Przewinąć do przodu” w powiadomieniach multimedialnych, jeśli chcesz kontrolować ilość przewiniętego czasu.
let skipTime = 10; // Time to skip in seconds
navigator.mediaSession.setActionHandler('seekbackward', function() {
// User clicked "Seek Backward" media notification icon.
audio.currentTime = Math.max(audio.currentTime - skipTime, 0);
});
navigator.mediaSession.setActionHandler('seekforward', function() {
// User clicked "Seek Forward" media notification icon.
audio.currentTime = Math.min(audio.currentTime + skipTime, audio.duration);
});
Odtwarzanie / wstrzymywanie
Ikona „Odtwórz/Wstrzymaj” jest zawsze wyświetlana w powiadomieniu o multimediach, a powiązane zdarzenia są obsługiwane automatycznie przez przeglądarkę. Jeśli z jakiegoś powodu domyślne działanie nie zadziała, nadal możesz obsługiwać zdarzenia „Odtwórz” i „Wstrzymaj”.
navigator.mediaSession.setActionHandler('play', function() {
// User clicked "Play" media notification icon.
// Do something more than just playing current audio...
});
navigator.mediaSession.setActionHandler('pause', function() {
// User clicked "Pause" media notification icon.
// Do something more than just pausing current audio...
});
Powiadomienia wszędzie
Wspaniałą rzeczą w przypadku interfejsu Media Session API jest to, że panel powiadomień nie jest jedynym miejscem, w którym są widoczne metadane i opcje sterowania multimediami. Powiadomienie o multimediach jest automatycznie synchronizowane z dowolnym sparowanym urządzeniem do noszenia. Wyświetla się też na ekranie blokady.
Odtwarzanie offline
Wiem, co teraz myślisz. Pracownik obsługi klienta na ratunek!
Zgadza się, ale przede wszystkim musisz się upewnić, że wszystkie pozycje na tej liście są zaznaczone:
- Wszystkie pliki multimedialne i pliki z grafiką są wyświetlane z odpowiednim nagłówkiem HTTP
Cache-Control
. Pozwoli to przeglądarce przechowywać w pamięci podręcznej i ponownie używać wcześniej pobranych zasobów. Zobacz listę kontrolną dotyczącą pamięci podręcznej. - Upewnij się, że wszystkie pliki multimedialne i pliki z grafiką są przesyłane z nagłówkiem HTTP
Allow-Control-Allow-Origin: *
. Dzięki temu aplikacje internetowe innych firm będą mogły pobierać i wykorzystywać odpowiedzi HTTP z Twojego serwera WWW.
Strategia buforowania skryptu service worker
Jeśli chodzi o pliki multimedialne, polecam prostą strategię Cache, falling back to network, którą opisał Jake Archibald.
W przypadku grafiki warto jednak podać nieco więcej szczegółów i wybrać jedną z podanych niżej metod:
- Grafika
If
znajduje się już w pamięci podręcznej, wyświetl ją z pamięci podręcznej Else
pobiera grafikę z sieci.If
pobieranie się powiodło, dodaj elementy graficzne sieci do pamięci podręcznej i wyświetl jeElse
wyświetlanie zastępczego grafiki z pamięci podręcznej.
Dzięki temu powiadomienia o multimediach będą zawsze zawierać ładną ikonę elementu graficznego, nawet jeśli przeglądarka nie może ich pobrać. Oto jak to zrobić:
const FALLBACK_ARTWORK_URL = 'fallbackArtwork.png';
addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(initArtworkCache());
});
function initArtworkCache() {
caches.open('artwork-cache-v1')
.then(cache => cache.add(FALLBACK_ARTWORK_URL));
}
addEventListener('fetch', event => {
if (/artwork-[0-9]+\.png$/.test(event.request.url)) {
event.respondWith(handleFetchArtwork(event.request));
}
});
function handleFetchArtwork(request) {
// Return cache request if it's in the cache already, otherwise fetch
// network artwork.
return getCacheArtwork(request)
.then(cacheResponse => cacheResponse || getNetworkArtwork(request));
}
function getCacheArtwork(request) {
return caches.open('artwork-cache-v1')
.then(cache => cache.match(request));
}
function getNetworkArtwork(request) {
// Fetch network artwork.
return fetch(request)
.then(networkResponse => {
if (networkResponse.status !== 200) {
return Promise.reject('Network artwork response is not valid');
}
// Add artwork to the cache for later use and return network response.
addArtworkToCache(request, networkResponse.clone())
return networkResponse;
})
.catch(error => {
// Return cached fallback artwork.
return getCacheArtwork(new Request(FALLBACK_ARTWORK_URL))
});
}
function addArtworkToCache(request, response) {
return caches.open('artwork-cache-v1')
.then(cache => cache.put(request, response));
}
Zezwalanie użytkownikowi na kontrolowanie pamięci podręcznej
Gdy użytkownik korzysta z treści w aplikacji internetowej, pliki multimedialne i elementy graficzne mogą zajmować dużo miejsca na urządzeniu. Twoim obowiązkiem jest pokazanie, ile pamięci podręcznej jest używane i zapewnienie użytkownikom możliwości jej wyczyszczenia. Na szczęście można to zrobić za pomocą interfejsu Cache API.
// Here's how I'd compute how much cache is used by artwork files...
caches.open('artwork-cache-v1')
.then(cache => cache.matchAll())
.then(responses => {
let cacheSize = 0;
let blobQueue = Promise.resolve();
responses.forEach(response => {
let responseSize = response.headers.get('content-length');
if (responseSize) {
// Use content-length HTTP header when possible.
cacheSize += Number(responseSize);
} else {
// Otherwise, use the uncompressed blob size.
blobQueue = blobQueue.then(_ => response.blob())
.then(blob => { cacheSize += blob.size; blob.close(); });
}
});
return blobQueue.then(_ => {
console.log('Artwork cache is about ' + cacheSize + ' Bytes.');
});
})
.catch(error => { console.log(error); });
// And here's how to delete some artwork files...
const artworkFilesToDelete = ['artwork1.png', 'artwork2.png', 'artwork3.png'];
caches.open('artwork-cache-v1')
.then(cache => Promise.all(artworkFilesToDelete.map(artwork => cache.delete(artwork))))
.catch(error => { console.log(error); });
Uwagi dotyczące implementacji
- Chrome na Androida prosi o „pełne” skupienie na dźwięku, aby wyświetlać powiadomienia o multimediach, tylko gdy czas trwania pliku multimedialnego wynosi co najmniej 5 sekund.
- Grafika powiadomienia obsługuje adresy URL typu blob i adresy URL danych.
- Jeśli nie zdefiniujesz grafiki, a w bibliotece multimediów jest obraz ikony o pożądanym rozmiarze, będzie on używany w powiadomieniach.
- Rozmiar grafiki powiadomienia w Chrome na Androida to
512x512
. W przypadku urządzeń niższej klasy jest to256x256
. - Zamknij powiadomienia o multimediach za pomocą
audio.src = ''
. - Z historycznych względów interfejs Web Audio API nie prosi o użycie funkcji Android Audio Focus, dlatego jedynym sposobem na jego działanie z interfejsem Media Session API jest podłączenie elementu
<audio>
jako źródła danych do interfejsu Web Audio API. Mamy nadzieję, że zaproponowany interfejs Web AudioFocus API poprawi tę sytuację w najbliższej przyszłości. - Wywołania sesji multimediów będą miały wpływ na powiadomienia multimedialne tylko wtedy, gdy pochodzą z tego samego interfejsu, co zasób multimedialny. Zobacz poniższy fragment kodu.
<iframe id="iframe">
<audio>...</audio>
</iframe>
<script>
iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
...
});
</script>
Pomoc
W momencie pisania tego artykułu Chrome na Androida jest jedyną platformą, która obsługuje interfejs Media Session API. Najnowsze informacje o stanie wdrożenia przeglądarki znajdziesz na stronie Stan platformy Chrome.
Przykłady i wersje demonstracyjne
Zapoznaj się z oficjalnymi Media Session Samples w Chrome, które obejmują Blender Foundation i twórczość Jana Morgensterna.
Zasoby
Specyfikacja sesji multimediów: wicg.github.io/mediasession
Problemy ze specyfikacją: github.com/WICG/mediasession/issues
Błędy w Chrome: crbug.com