Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 Audio & VideoSafari & Web

WWDC21 · 12 min · Audio & Video / Safari & Web

Coordinate media playback in Safari with Group Activities

Create SharePlay experiences that people can enjoy on the web and in your companion app. Learn how you can use the Group Activities framework in combination with a companion website to bring SharePlay to Safari, letting people connect with each other for enjoyable group interactions — even if they haven’t yet downloaded your app from the App Store.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 3 snippets

Preparing your app swift · at 2:50 ↗
struct WatchTogether: GroupActivity {

    var contentIdentifier: String

    func metadata() async -> GroupActivityMetadata {
        var metadata = ActivityMetadata()
        metadata.fallbackURL = URL(string: "https://example.com/title/\(contentIdentifier)")
        return metadata
    }
}
Adopting Media Session javascript · at 5:29 ↗
if (navigator.mediaSession) {
    navigator.mediaSession.setActionHandler('play', () => video.play() );
    navigator.mediaSession.setActionHandler('pause', () => video.pause() );
    navigator.mediaSession.setActionHandler('seekto', details => {
        video.currentTime = details.seekTime;
    });
}

let updateMediaSessionState = function() {
    if (!navigator.mediaSession)
        return;
    let playbackState = video.paused ? 'paused' : 'playing';
    navigator.mediaSession.playbackState = playbackState;

    let positionState = { video.duration, video.playbackRate, video.currentTime };
    navigator.mediaSession.setPositionState(positionState);
};

for (var event of ['playing', 'pause', 'durationchange', 'ratechange', 'timechange'])
    video.addEventListener(event, updateMediaSessionState);

navigator.mediaSession.metadata = new MediaMetadata({
    title: myPlayer.titleString,
    artwork: [{ src: myPlayer.artworkURL }]
});
Adopting Coordinator javascript · at 9:32 ↗
navigator.mediaSession.addEventListener('coordinatorchange', coordinator => {
    if (coordinator)
        coordinator.join();
    controls.inSessionIcon.style.hidden = !coordinator;
});

controls.inSessionIcon.addEventListener('click', event => {
    let coordinator = navigator.mediaSession.coordinator;
    if (coordinator && coordinator.state == 'joined')
        navigator.mediaSession.coordinator.leave();
});

controls.playButton.addEventHandler('click', event => {
    if (navigator.mediaSession.coordinator)
        navigator.mediaSession.coordinator.play();
    else
        video.play();
});
controls.pauseButton.addEventHandler('click', event => {
    if (navigator.mediaSession.coordinator)
        navigator.mediaSession.coordinator.pause();
    else
        video.pause();
});
controls.timeline.addEventHandler('onchange', event => {
    if (navigator.mediaSession.coordinator)
        navigator.mediaSession.coordinator.seekTo(event.target.value);
    else
        video.currentTime = event.target.value;
});

Resources