ryanheise / audio_service

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

Trouble closing an audio session #1057

Closed appcapsergen closed 9 months ago

appcapsergen commented 9 months ago

Documented behaviour

Could not find anything related since I don't get an error or anything, the audio session just doesn't close.

Actual behaviour

In my Flutter app the user is able to play a video, I use the chewie package for this. This player uses a VideoPlayerController (from the video_player package) to somewhat control the video player, which ultimately allows me to use the audio_service package to create an audio session. I use this to continue playing the video when the app is paused (in the background or when the phone is locked).

The problem I'm faced with now is that when I've started a video with background audio playback successfully, but then want to close that video, I can't seem to get the audio session to stop as well. This is especially visible on iOS (like in this screenshot), where the media player stays active even though I shut down the video and attempted to shut down the audio session as well.

Runtime error

There's no error, audio session just doesn't close.

Minimal reproduction project

Official example: main.dart

Here's my audio handler, I basically close the audio handler by calling resetVideoFunctions. I have tried multiple things though, like closing/disposing the mediaItem, closing/disposing the player state, closing/disposing the streamController, closing/disposing the video controller, etc., etc... Nothing works

MyAudioHandler ```dart class MyAudioHandler extends BaseAudioHandler { StreamController? streamController; VoidCallback? _videoPlay, _videoPause, _videoStop; void Function(Duration position)? _videoSeek; HuddleAudioHandler(); void setVideoFunctions( VoidCallback play, VoidCallback pause, VoidCallback stop, void Function(Duration position) seek, MediaItem item) { mediaItem.add(item); _videoPlay = play; _videoPause = pause; _videoStop = stop; _videoSeek = seek; } Future resetVideoFunctions() async { mediaItem.add(null); _videoPlay = null; _videoPause = null; _videoStop = null; _videoSeek = null; } @override Future play() async { _videoPlay?.call(); } @override Future pause() async { _videoPause?.call(); } @override Future stop() async { _videoStop?.call(); } @override Future seek(Duration position) async { _videoSeek?.call(position); } void initializeStreamController(VideoPlayerController? videoPlayerController) { bool isPlaying() => videoPlayerController?.value.isPlaying ?? false; AudioProcessingState processingState() { if (videoPlayerController == null) { return AudioProcessingState.idle; } if (videoPlayerController.value.isInitialized) { return AudioProcessingState.ready; } return AudioProcessingState.idle; } Duration bufferedPosition() { final DurationRange? currentBufferedRange = videoPlayerController?.value.buffered.firstOrNullWhere((DurationRange durationRange) { final Duration position = videoPlayerController.value.position; final bool isCurrentBufferedRange = durationRange.start < position && durationRange.end > position; return isCurrentBufferedRange; }); return currentBufferedRange != null ? currentBufferedRange.end : Duration.zero; } void addVideoEvent() { streamController?.add(PlaybackState( controls: [ MediaControl.rewind, if (isPlaying()) MediaControl.pause else MediaControl.play, MediaControl.stop, MediaControl.fastForward, ], systemActions: const { MediaAction.seek, MediaAction.seekForward, MediaAction.seekBackward, }, androidCompactActionIndices: const [0, 1, 3], processingState: processingState(), playing: isPlaying(), updatePosition: videoPlayerController?.value.position ?? Duration.zero, bufferedPosition: bufferedPosition(), speed: videoPlayerController?.value.playbackSpeed ?? 1.0, )); } void startStream() { videoPlayerController?.addListener(addVideoEvent); } void stopStream() { videoPlayerController?.removeListener(addVideoEvent); streamController?.close(); streamController = null; } streamController = StreamController( onListen: startStream, onPause: stopStream, onResume: startStream, onCancel: stopStream); } } ```

Reproduction steps

  1. Use the audio handler I provided.
  2. Start a video, at least with a videoController, with an audio session using the audio handler.
  3. Make sure the media controls appear in the notifications centre.
  4. Close the video by closing the video or closing the entire screen (if applicable).
  5. You can still see the media controls in the notifications centre.

    Output of flutter doctor

    
    Doctor summary (to see all details, run flutter doctor -v):
    [✓] Flutter (Channel stable, 3.16.9, on macOS 14.3 23D56 darwin-arm64, locale en-NL)
    [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 15.2)
    [✓] Chrome - develop for the web
    [✓] Android Studio (version 2023.1)
    [✓] VS Code (version 1.85.2)
    [✓] Connected device (3 available)
    [✓] Network resources

• No issues found!


### Devices exhibiting the bug
At least on iOS 16.7.5, pretty sure it's on any version though.
ryanheise commented 9 months ago

Hello, your bug report does not follow the instructions. The two options were to either indicate the bug is reproduced via the unmodified official example, OR fork the official example and make minimal modifications to it to reproduce the bug.

appcapsergen commented 9 months ago

My apologies, I've made a new issue for this https://github.com/ryanheise/audio_service/issues/1058

github-actions[bot] commented 8 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.