eshaz / icecast-metadata-js

Browser and NodeJS packages for playing and reading Icecast compatible streaming audio with realtime metadata updates.
157 stars 20 forks source link

icecast-metadata-player - navigator.mediaSession.metadata not working in iOS #193

Open blantonl opened 10 months ago

blantonl commented 10 months ago

setting navigator.mediaSession.metadata on iOS using icecast-metadata-player isn't working. Setting this property provides metadata to the OS to allow iOS to display a user control interface in iOS on the lock screen when a feed is streaming. I'm not dynamically (yet) trying to update this metadata from the stream itself, just setting it when the audio player loads. It works in normal audio tag scenarios fine but not when using icecast-metadata-player

See: https://developer.mozilla.org/en-US/docs/Web/API/MediaSession/metadata

if ("mediaSession" in navigator) {
                console.log("Set Metadata...");
                navigator.mediaSession.metadata = new MediaMetadata({
                    title: "Test Feed",
                    artist: "Broadcastify",
                    album: "Broadcastify Live Audio Feeds",
                    artwork: [
                        {
                            src: "https://s.broadcastify.com/pwa/ios/128.png",
                            sizes: "128x128",
                            type: "image/png",
                        },
                        {
                            src: "https://s.broadcastify.com/pwa/ios/192.png",
                            sizes: "192x192",
                            type: "image/png",
                        },
                        {
                            src: "https://s.broadcastify.com/pwa/ios/256.png",
                            sizes: "256x256",
                            type: "image/png",
                        },
                        {
                            src: "https://s.broadcastify.com/pwa/ios/512.png",
                            sizes: "512x512",
                            type: "image/png",
                        },
                    ],
                });
            }
blantonl commented 10 months ago

Quick update to this.

Setting playbackMethod: "html5" gets the mediaSession working and the player showing on the iOS lock screen, however playback stops when iOS safari looses focus and is put into the background (playback just stops at that point when navigating away from Safari)

Edit: html5 background audio is completely broken in 17.0.3 independent of this library.

One other point to note this that the html5 audio "timeupdate" event handler reports a random time using "webaudio", "mediasession" and the default playback methods, however "html5" reports the correct timeupdate for a stream.

blantonl commented 10 months ago

Ok, good news here. This issue seems to have been resolved in 17.1.1.

You'll need to set playbackMethod: "html5" - but background audio works and the mediaSession.metadata object works great.

eshaz commented 10 months ago

Thanks for entering this issue.

I did some digging, and it looks like there is a known issue / incompatibility (https://github.com/w3c/audio-session/issues/11) with Media Session and Web Audio.

IcecastMetadataPlayer uses the webaudio playback method by default in iOS, since mediasource isn't supported by iOS. The html5 playback method uses the Audio element which is what allows the Media Session to work properly.

The html5 playback method is meant to be used for compatibility and work around reasons (like this one) only. It uses two requests, one for parsing metadata, and another audio request to feed an Audio element. Since there are two requests, it doubles the amount of bandwidth used to listen to a stream in some platforms (I believe iOS merges these duplicate requests into one connection).

I'm thinking I can add a feature in here to allow more control over which playback methods are selected. In cases like this, I think the playback method should be mediasource be for non-iOS browsers, and html5 for iOS, where webaudio is not used. Maybe an option to specify the order in which playback methods are selected would allow this:

// default playback methods
// Chrome, Firefox, Android, Safari desktop will select `mediasource`
// iOS will select `webaudio`
const player = new IcecastMetadataPlayer("https://stream.example.com", {
  playbackMethod: ["mediasource", "webaudio", "html5"]
});

// prefer `html5`, then use `mediasource`, `webaudio`
// all browsers will select `html5` since it's universally supported
const player = new IcecastMetadataPlayer("https://stream.example.com", {
  playbackMethod: "html5"
});

// prefer `mediasource`, then use `html5`, `webaudio`
// Chrome, Firefox, Android, Safari desktop will select `mediasource`
// iOS will select `html5`
const player = new IcecastMetadataPlayer("https://stream.example.com", {
  playbackMethod: ["mediasource", "html5", "webaudio"]
});