ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
804 stars 480 forks source link

No action after disconect bluetooht headset play/pause blocked on control center - IOS #480

Open Cassianosch opened 4 years ago

Cassianosch commented 4 years ago

Which API doesn't behave as documented, and how does it misbehave? The main example have the problem Example

Minimal reproduction project Provide a link here using one of two options: 1 i don`t have any suggestion

To Reproduce (i.e. user steps, not code) Steps to reproduce the behavior:

  1. Start the radio
  2. Set the bluetooth off
  3. The control center on ios just can`t to start or pause the music, you must to open the app again to start the music again

Error messages N/A

Expected behavior The button should be able to play again the music or just continue playing after the disconection of the bluetooth

Screenshots WhatsApp Image 2020-10-02 at 18 31 41

Runtime Environment (please complete the following information if relevant):

Flutter SDK version

[✓] Flutter (Channel stable, 1.20.4, on Mac OS X 10.15.3 19D76, locale en-BR)

Additional context Add any other context about the problem here.

ryanheise commented 4 years ago

Hi @Cassianosch , do you know of a way to reproduce this on the simulator?

Cassianosch commented 4 years ago

Hi @ryanheise. Thanks for check my question. No i dont know. On Stack Overflow says only on iOS Simulator v10.3.1 os less. But Ive tested only in real devices

ryanheise commented 4 years ago

This sounds like an important issue although unfortunately it will be difficult for me to reproduce and therefore investigate.

Hopefully you are able to work with me to conduct an experiment on your device. Can you try activating the audio session after the bluetooth connection is lost? Or at least, can you add a temporary button to your UI that will allow you to activate the audio session on demand, and then see if pressing it will cause things to start working again?

import 'package:audio_session/audio_session.dart';

// In your button handler:
final audioSession = await AudioSession.instance;
await audioSession.setActive(true);
Cassianosch commented 4 years ago

Hi @ryanheise. Didnt work. I tried this method: can you add a temporary button to your UI that will allow you to activate the audio session on demand, and then see if pressing it will cause things to start working again?

If I start the radio - conect a bluetooht headset - disconect the general bluetooth in IOS system the control center became blocked But if the user open the app and click in a button that has the commands AudioService.stop(); AudioService.start(); the radio starts play again. BUT the user must to open the app to do the proccess. Sorry for bad english.

ryanheise commented 4 years ago

Out of curiosity, can you test a few different popular apps in the same scenario and tell me their behaviour?

Cassianosch commented 4 years ago

Hi @ryanheise again thanks for continuing helping me. I came from react native apps. But i runned out of those because several bugs. Ive tested those apps for this problem and when the bluetooht is disconected just continuous playing with no major chages. The user do not need to press any button.

ryanheise commented 4 years ago

@Cassianosch would you mind doing another experiment? Can you try modifying the app so that both the play and pause media controls are always passed into AudioServiceBackground.setState? For example, in the audio_service example, try changing this code:

  Future<void> _broadcastState() async {
    await AudioServiceBackground.setState(
      controls: [
        MediaControl.skipToPrevious,
        if (_player.playing) MediaControl.pause else MediaControl.play,
        MediaControl.stop,
        MediaControl.skipToNext,
      ],
      systemActions: [
        MediaAction.seekTo,
        MediaAction.seekForward,
        MediaAction.seekBackward,
      ],
      processingState: _getProcessingState(),
      playing: _player.playing,
      position: _player.position,
      bufferedPosition: _player.bufferedPosition,
      speed: _player.speed,
    );
  }

To this:

  Future<void> _broadcastState() async {
    await AudioServiceBackground.setState(
      controls: [
        MediaControl.skipToPrevious,
        MediaControl.play,
        MediaControl.pause,
        MediaControl.stop,
        MediaControl.skipToNext,
      ],
      systemActions: [
        MediaAction.seekTo,
        MediaAction.seekForward,
        MediaAction.seekBackward,
      ],
      processingState: _getProcessingState(),
      playing: _player.playing,
      position: _player.position,
      bufferedPosition: _player.bufferedPosition,
      speed: _player.speed,
    );
  }
Cassianosch commented 4 years ago

@ryanheise now the button pause was able to click but i need to click 2 time in control center. Play>pause>play and then the music starts normally again

ryanheise commented 4 years ago

OK, that gives me a clue. I will think about a solution.

snaeji commented 4 years ago

I don't remember completely but I think I had this issue when I wasn't update'ing the state in audio_service after the audioplayer paused by itself when route change is detected. You would have to listen to becomingNoisyEventStream in this case.

This should do the trick if you update the state in onPause()

    // Handle unplugged headphones.
    session.becomingNoisyEventStream.listen((_) {
      if (_audioPlayer.playing) onPause();
    });
Cassianosch commented 4 years ago

Hi @snaeji This code is on the example already but didnt works as well image

snaeji commented 4 years ago

Hey @Cassianosch sorry now I remember...

I just checked the pubspec for the example project and it's not using the newest version of audio_session. Also the commit that fixed this (added the listener for routeChange on iOS that sends an event to becomingNoisyEventStream) is not yet released, but you could download the git with the latest commit from the master branch here and add it to the example. https://github.com/ryanheise/audio_session/commits/master

@ryanheise do you think we are ready for a release there? If we release audio_session 0.8 and update the pubspec in the example this bug should be fixed.

ryanheise commented 4 years ago

@snaeji The only thing I was thinking about before releasing it was to remove AVAudioSession.becomingNoisyEventStream and have AudioSession.becomingNoisyEventStream directly subscribe to AVAudioSession.routeChangeStream (so that AudioSession exposes the common API and AVAudioSession exposes the iOS API). If I make this change, would you be able to test it on your end?

ryanheise commented 4 years ago

I have just made this change available for testing on the ios_becoming_noisy branch (on audio_session).

snaeji commented 4 years ago

Great! Thank you @ryanheise

I just tested it and it works as intended 👍

ryanheise commented 4 years ago

Just published audio_session 0.0.8.

@Cassianosch you can try adding this specific version of audio_session to your pubspec dependencies. Note that just_audio will automatically pause when receiving an event on AudioSession.becomingNoisyEventStream so updating audio_session to 0.0.8 should cause this built-in feature to work in just_audio for iOS. Let me know if it works as I'd be interested to know whether any further code improvements might be required in the plugin.

snaeji commented 4 years ago

I think that it would maybe be ideal to find a solution that doesn't leave the play/pause button disabled in cases like this.

I am looking into another bug that causes that makes to play/pause button disabled.

If we pause music in the lock screen for 20 seconds and press the play button, we get an AudioInterruptionType event that causes onPause to be called again.

Cassianosch commented 4 years ago

It Works!! Thanks @ryanheise and @snaeji! For futures developers the solutions was update the audio_session to 0.0.8.