ryanheise / audio_session

MIT License
107 stars 68 forks source link

Trouble closing an audio session #122

Closed appcapsergen closed 5 months ago

appcapsergen commented 5 months ago

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.

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

class MyAudioHandler extends BaseAudioHandler {
  StreamController<PlaybackState>? 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<void> resetVideoFunctions() async {
    mediaItem.add(null);
    _videoPlay = null;
    _videoPause = null;
    _videoStop = null;
    _videoSeek = null;
  }

  @override
  Future<void> play() async {
    _videoPlay?.call();
  }

  @override
  Future<void> pause() async {
    _videoPause?.call();
  }

  @override
  Future<void> stop() async {
    _videoStop?.call();
  }

  @override
  Future<void> 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>[
          MediaControl.rewind,
          if (isPlaying()) MediaControl.pause else MediaControl.play,
          MediaControl.stop,
          MediaControl.fastForward,
        ],
        systemActions: const <MediaAction>{
          MediaAction.seek,
          MediaAction.seekForward,
          MediaAction.seekBackward,
        },
        androidCompactActionIndices: const <int>[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<PlaybackState>(
        onListen: startStream, onPause: stopStream, onResume: startStream, onCancel: stopStream);
  }
}

Hopefully someone can help me close the audio session!

appcapsergen commented 5 months ago

My apologies, wrong package