Open csacchetti opened 1 year ago
In this video I show a strange thing that happens with iOS 16 and I hope it can help solve the problem. The video is without sound but what is important is what happens to the background. Before you start the app you see that there is a wallpaper. Then I launch the app I start the song with 10 seconds to go and put the app in the background. If you look at the wallpaper it has changed. Then you wait until the song comes to the end and at that time when it tries to load the new file the background changes again. This suggests that there is a conflict with the new wallpaper management in iOS 16 Just a suspicion, but this behaviour is strange
I had the opportunity to test the problem on an iPad with iPadOS 16.1.1 and the bug is present. So it is likely that the bug started with iOS 16.0
I am copying @rjgpereira 's comment from the closed issue here:
@csacchetti I had a similar issue. I'm mimicking the playlist behaviour but not using the ConcatenatingAudioSource, just switching tracks manually calling player.setAudioSource() with the proper index, and listening to the "completed" playbackState in order to skip to the next index. Since I was using the player's 'LoopMode.off', after the 'completed' state was emitted, an 'idle' state was emitted as well. I think broadcasting this 'idle' state in iOS 16 might close the audio session. I don't know if this is exactly what's happening, and I can't explain why it didn't occur in the previous iOS versions. But this fixed it for me.
Try to broadcast AudioProcessingState.ready when you get a ProcessingState.idle, so that the OS keep your session alive while you switch tracks:
processingState: { ProcessingState.idle: Platform.isIOS ? AudioProcessingState.ready : AudioProcessingState.idle, ProcessingState.loading: AudioProcessingState.loading, ProcessingState.buffering: AudioProcessingState.buffering, ProcessingState.ready: AudioProcessingState.ready, ProcessingState.completed: AudioProcessingState.completed, }[_player.processingState]!,
I am unable to change the mediaitem using just_audio and audio_service package please help!
/// An [AudioHandler] for playing a single item.
class AudioPlayerHandler extends BaseAudioHandler with SeekHandler {
final _item = MediaItem(
id: songUri.toString(),
album: "Science Friday",
title: songTitle.toString(),
artist: "Science Friday and WNYC Studios",
duration: Duration(milliseconds: songDuration!),
artUri: Uri.tryParse(
"https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg"));
final _player = AudioPlayer();
/// Initialise our audio handler.
AudioPlayerHandler() {
// So that our clients (the Flutter UI and the system notification) know
// what state to display, here we set up our audio handler to broadcast all
// playback state changes as they happen via playbackState...
_player.playbackEventStream.map(_transformEvent).pipe(playbackState);
// ... and also the current media item via mediaItem.
mediaItem.add(_item);
// Load the player.
_player.setAudioSource(AudioSource.uri(Uri.parse(_item.id)));
}
// In this simple example, we handle only 4 actions: play, pause, seek and
// stop. Any button press from the Flutter UI, notification, lock screen or
// headset will be routed through to these 4 methods so that you can handle
// your audio playback logic in one place.
@override
Future<void> play() async {
print('Playing here');
_player.play();
}
@override
Future<void> pause() async {
print('Pausing here');
_player.pause();
}
@override
Future<void> seek(Duration position) => _player.seek(position);
@override
Future<void> stop() => _player.stop();
/// Transform a just_audio event into an audio_service state.
///
/// This method is used from the constructor. Every event received from the
/// just_audio player will be transformed into an audio_service state so that
/// it can be broadcast to audio_service clients.
PlaybackState _transformEvent(PlaybackEvent event) {
return PlaybackState(
controls: [
MediaControl.rewind,
if (_player.playing) MediaControl.pause else MediaControl.play,
MediaControl.stop,
MediaControl.fastForward,
],
systemActions: const {
MediaAction.seek,
MediaAction.seekForward,
MediaAction.seekBackward,
},
androidCompactActionIndices: const [0, 1, 3],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[_player.processingState]!,
playing: _player.playing,
updatePosition: _player.position,
bufferedPosition: _player.bufferedPosition,
speed: _player.speed,
queueIndex: event.currentIndex,
);
}
}
How do i updatemediaItem the right way. I tried to override this method and the audio changed but now when i pause and play again it starts from the beginning and does not resume it.
For info the same problem happens in flutter web when running on IOS 16.3 and same fix solves this problem
can,t work for me, what happend?
I have also encountered this issue. I have tried the workaround provided by @csacchetti and no luck. What ended up working for me, but it's a little bit of a hack, is to have a second player available and play a silence file in loop mode so iOS doesn't put the app to sleep (or whatever is going on). So, this is more or less what I'm doing now:
playSilence();
await player.setAudioSource(...);
pauseSilence();
Documented behaviour
play() → Future
Start or resume playback.
Actual behaviour
When the app is in the background with iOS 16.2 (or 16.0 but I have not tested this version) the audio, if I load a new audio file and play it the audio freezes. With iOS 14 the audio is played correctly and I think also in iOS15.
Runtime error
Minimal reproduction project
https://github.com/csacchetti/bug_audio_service_ios_16.git
Reproduction steps
First of all, download the example from GitHub. The code is that of the main of the example in audio_service gitHub. I have only added the final part where I put the explanations to reproduce the bug and I added in the construct of AudioPlayerHandler() the function _myListenVoiceState();
To check this, go to the end of the song and go back 10 seconds by clicking on the back button and then start play. If you are not in the background the song once it reaches the end will resume normally, whereas if you start play and go into the background the song if you are on iOS 14 will behave correctly and restart, whereas if you are on iOS 16.2 it will freeze.
Output of flutter doctor