I have been trying to track this down and there doesn't seem to be a consistent answer. If I have a website that tries to play multiple songs in a row (think playlist) using the HTML 5 audio element, can it continue to work on the iOS lock screen?
For some background, this answer seems to indicate it may be possible. But then this article suggests it is not.
I tried following the closest example of what Apple recommends, as found here, to replicate this. I am using plain, vanilla javascript and HTML, nothing fancy. I copied their example exactly and just substituted the audio tag for the video one, picking two random mp3 songs. All it does is wait for one song to end, then switch the src, load, and play the next track.
When I hit Play on the website, I then lock the iPhone. The first song will play, then stop. It does not continue to the next song.
If the website is open to the page, it will properly transition to the next song. On Android, it will continue to the next song even if the phone is locked.
I tried this with iOS 11 and 12. Neither worked. I have read many differing answers about how javascript is stopped when the website isn't in the foreground, and how iOS requires user interaction to play audio (even going from one song to the next). So it doesn't seem like this would work. But then other answers out there seem to indicate this is possible.
Is there a definitive yes or no? Am I doing something wrong here? Or is it just not possible?
There are multiple issues, which is causing some of the confusion.
First track plays, second does not due to user permission
If the first track was started with user permission, then the only way you can switch to a new track via script is with Apple's recommendation. Handle the ended event and swap in a new src, and call .play() before the callback completes. Otherwise, your code will not have permission to start the new audio.
Cannot get to ended event due to time-constrained source (#t=5,10)
Say you have a 30-second audio file and you tell the browser to only load seconds 5 through 10, via something like #t=5,10 at the end of the URL. When you reach 10, a paused event will fire, not ended. Therefore, it isn't possible to go on to the next track normally, as this event is not "blessed" by Apple to count as a relay of the previous user interaction.
I got around this by having my JavaScript check the currentTime repeatedly, and when it crossed a threshold, seek it to the duration (end) of the file myself. This allows ended to fire normally.
iOS doesn't allow scripts to run after the screen is locked or other apps are in use
This one is a real problem to debug, since it doesn't turn up in any of the simulators. In any case, you're right, your JavaScript is going to get suspended so even if you follow Apple's recommendations, you're out of luck. Or, are you?
A hack... set up a ScriptProcessorNode on a Web Audio context. Set the buffer size to like 512 samples or something. Then, on script process, force a timeupdate event for your Audio Element. This keeps your JavaScript there ticking away. ScriptProcessorNode has to have special privileges here, by its nature.
Apple: If you read this, please fix your browser. It would be great if we didn't have to hack around things to make something as simple as an audio playlist to work. Other browsers don't have nearly this level of problems.
Related
Greetings to all fellow front-end coders.
I have quite a headache because of Apple's autoplay policy. As you've probably experienced, you can't autoplay a video / audio element with sound without user interaction. (I'm referring to this)
I'm currently trying to create my own music web application that will support playlists. I work with the Next.js framework and use react-player as player. Since it does not natively support playlists, I created a two-player pendulum system, where one plays the current sound file and the other loads the next. Then their roles change.
Everything works great in all browsers, except those on iOS, because this player uses autoplay to start playing.
I've been worried about this for several days. I've already thought about letting an Apple product users press play button every time a song changes as a punishment, but of course it's not a good solution.
I think a lot of you have encountered this and you have certainly found a solution. Please help me :(
I have an app that uses AVPlayer to play live streams and on-demand video. That in-house app (and recent in-house update) plays video beautifully in all sorts of conditions. Until a user in the company updated to iOS 13.
The same app run under iOS 12 from the same location will work perfect as before.
Now, after a reboot of the phone the first video played will play perfect and if it is a live stream will run for an indefinite amount of time without trouble. The second video played will always play audio only or fail depending on the bit rates available for the video. Even the exact same video.
Quiting and restarting the app makes no difference in the results. Restarting the phone WILL fix it for the next video played.
What appears to be happening is that on the first play, observedBitrate in the event log is correct and the correct bit rate stream is played. The second playback shows observedBitrate starting out an order of magnitude less and no reasonable amount of time sees that rate change significantly.
If the connection is an order of magnitude better than necessary (evidently that is true for most of these users including myself pre-Network Link Conditioner testing), then everything appears to work normally and life is good. What is even stranger, is that on these higher quality connections I don't see the same observedBitrate drop. Also, it appears that video served up from a different ip (but not different domains from same ip) will work once and then fail the second time as if some kind of connection bit rate cache is being used? These last two observations have not been repeated enough to be cast in stone but have been observed more than once.
I've scoured the iOS 13 release notes in hopes that I'm missing some change or need for a new key but nothing strikes me as relevant.
Any ideas appreciated!!!
A very similar question was posted earlier this year Video playback issues only on iOS 13 with AVPlayerViewController and AVPlayer when using HLS video. The unaccepted answer in that question does not apply here (and may not apply there either for all we know). I do wait for StreamPlayerItemStatusObserverContext to change to AVPlayerStatusReadyToPlay.
I'm working in an app where I'm able to play a HLS m3u8 playlist of a streaming radio (audio only) without any problem using an instance of AVPlayer. Using Charles I can see how the playlist is updated properly in a normal pace (each 9-10 seconds, which takes one media segment file). When I perform a seekToTime: (back in time), the player success playing the stream from when I want to, but in Charles I observe how the player starts dowloading a huge amount of media segment files, consuming a lot of data. It seems that the player downloads all the media segment files until that time and then keeps again with the normal behaviour.
I understand that the correct behaviour would be to download the media segment file for the time I'm seeking to, start playing it and then download constantly 1 or 2 media segment files each 9-10 seconds, as it does when I play the stream without timeshift.
My question is if this is a normal behaviour, or if something could be wrong with my m3u8 playlist or the client implementation. Anyone could help me to clarify this?
UPDATED: I can confirm this doesn't happen in iOS 7, so it seems to be a bug introduced by iOS 8.
I've been told by Apple that this is not a bug, but a feature. They've made the buffer bigger since iOS 8.
I am using CocoaLibSpotify in an iOS application. When I set markedForOfflinePlayback to YES on an instance of SPPlaylist the offlineStatus almost immediately changes to SP_PLAYLIST_OFFLINE_STATUS_YES for the playlist and the tracks in the playlist. However, the offlineDownloadProgress is 0.0 all the time.
The tracks have clearly not been cached. First and foremost, it would take much longer to cache them. Secondly, when I put my device in airplane mode and try to play one of the tracks marked as offline, no audio is played.
Does anyone have an idea what I might be doing wrong?
The most common cause for this is not shutting down CocoaLibSpotify properly when the application goes into the background - this can cause the cache to end up in an odd state that will cause offline syncing to fail. To avoid this, when your app goes into the background, you should start a background task, begin the logout process then end the task when logout is complete. The Empty CocoaLibSpotify Project sample project demos this.
Also, make sure you're updated to the latest version of CocoaLibSpotify - it contains an update to the underlying libSpotify library that makes offline syncing quite a lot more reliable. You still need to perform the proper backgrounding technique, though.
Finally, have a look at the Offline Syncing Mac sample project. It lets you see the offlining process in a visual way.
I have built an app, designed to play each sound as the first one is finished by using 'ended' event.
In my initial version, each sound has its own audio element, resulting in something like:
function play_next_audio(){
speaker = $('audio#' + sounds[i++]).get(0);
speaker.addEventListener('ended',play_next_audio);
speaker.play();
}
This works great on all desktop browsers, including Safari, but does not go beyond the very first letter on iOS.
I have also tried a different approach - a single audio element that loads each sound in turn. There I experimented with binding the 'ended' event as well as loading first and binding 'canplaythrough' event instead. In both cases, it fails to work even on the desktop Safari - this time successfully playing the first two letters.
Here is the isolated test:
http://dev.connectfu.com:42001/app/test-sounds.html
Note that audio.load() is commented out several places - having it in or out seems to make no difference.
What am I doing wrong? How can I play series of sounds on iOS? Thank you so much for the help!
update As of 2017, the ended event doesn't fire on Safari (or Chrome) on iOS under several conditions. More information can be found here: It's almost 2017, and HTML5 audio is still broken on iOS.
I've built an HTML5 audio player (Chavah Messianic Radio) that "works" on Safari on iOS.
By "works", I mean, it plays the best it can on Apple's crippled iOS <audio> implementation. At the time of this writing, this includes iOS 5. I've tested this on iPhone 3 and up, and iPad original, iPad 2, and iPad 3.
Here are my findings:
Apple does not allow you to call .play on any audio until user interaction. For me, this means detecting iOS, then showing the music as paused until the user clicks play. Their reasoning is that this will consume data and battery; in practice, though, it just cripples web apps and stifles the evolution of the web.
If you want to play successive sounds (one after another), use a single element. When it's time to play the next sound, set existingAudio.src, then call existingAudio.load(), then call existingAudio.play(). This will allow you to play successive sounds.
Audio events don't fire if Safari is in the background.. While audio will continue playing if the user switches to a different app, the .ended event won't fire. This means it's practically impossible to build a music player app.
Hope this helps.
<rant>
In the meantime: Apple, please, please, please give us better support for HTML5 in iOS Safari. Here are your action items, Apple:
Let HTML5 audio work in the background.
Support OGG.
Support pre-loading audio.
Support concurrent audio.
Let us play audio without user interaction.
Do those things, Apple, you'll be the industry leader in mobile HTML5 audio, everyone will emulate you, you'll once again be leading the way, and web apps will work perfectly on your platforms, while being crippled on other mobile platforms. Yes, these features will use data and the battery, but native apps already do this. Stop crippling web apps and be the leader. Make HTML5 <audio> a first-class citizen on iOS.
</rant>
I don't believe the .play() method is supported for audio or video in iOS. Apple does not like the idea of videos or audio automatically playing upon visiting a page.
Here is a helpful reading about the state of HTML5 audio support across platforms: http://www.phoboslab.org/log/2011/03/the-state-of-html5-audio