Hassaan Ahmed
Hassaan Ahmed

Reputation: 1

Issue with song switching and buffering in iOS background using audio_service package in Flutter

I am working on an audio streaming app using the audio_service package in Flutter, and I am facing an issue where the song switching works fine in the foreground, but when the app is in the background, the song doesn't switch, and the audio player gets stuck on buffering. This issue is specific to iOS, as the app works fine on Android.

Here is what I have done so far:

I have set up background modes in the Info.plist to allow audio and Xcode also. I have implemented the necessary background task for audio playback, but song switching in the background is not functioning as expected.

This is my songHandler file

import 'dart:developer';

import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart' as rx;

class SongHandler extends BaseAudioHandler with SeekHandler {
  final AudioPlayer audioPlayer = AudioPlayer();
  MediaItem? currentMediaItem;
  bool hasPrevious = false;
  bool hasNext = false;

  void _broadcastState() {
    mediaItem.add(currentMediaItem);

    playbackState.add(playbackState.value.copyWith(
      controls: [
        if (hasPrevious) MediaControl.skipToPrevious,
        if (audioPlayer.playing) MediaControl.pause else MediaControl.play,
        if (hasNext) MediaControl.skipToNext,
      ],
      systemActions: {
        MediaAction.seek,
        MediaAction.seekForward,
        MediaAction.seekBackward,
      },
      processingState: {
        ProcessingState.idle: AudioProcessingState.idle,
        ProcessingState.loading: AudioProcessingState.loading,
        ProcessingState.buffering: AudioProcessingState.buffering,
        ProcessingState.ready: AudioProcessingState.ready,
        ProcessingState.completed: AudioProcessingState.completed,
      }[audioPlayer.processingState]!,
      playing: audioPlayer.playing,
      updatePosition: audioPlayer.position,
      bufferedPosition: audioPlayer.bufferedPosition,
      speed: audioPlayer.speed,
      queueIndex: 0,
    ));
  }

  Future<void> initSong(
    AudioSource audioSource,
    MediaItem song,
  ) async {
    currentMediaItem = song; // Set current media item
    audioPlayer.playbackEventStream.listen((_) => _broadcastState());
    await audioPlayer.setAudioSource(audioSource);
    play();
  }

  Future<void> updateAudioSource(AudioSource audioSource, MediaItem song,
      {bool? isShowButton = false}) async {
    await audioPlayer.setAudioSource(audioSource);

    currentMediaItem = song;
    hasPrevious = isShowButton ?? false;
    hasNext = isShowButton ?? false;
  }

  @override
  Future<void> play() async {
    if (currentMediaItem != null) {
      await audioPlayer.play();
    }
  }

  @override
  Future<void> pause() => audioPlayer.pause();

  @override
  Future<void> seek(Duration position) => audioPlayer.seek(position);

  @override
  Future<void> skipToNext() async {
    final musicPlayerViewModel = Get.find<MusicPlayerViewModel>();
    musicPlayerViewModel.moveNextSong();
    // Logic to skip to the next song
    log('message');
  }

  @override
  Future<void> skipToPrevious() async {
    final musicPlayerViewModel = Get.find<MusicPlayerViewModel>();
    musicPlayerViewModel.movePreviousSong();
    // Logic to skip to the previous song
    log('message');
  }

  Stream<PositionData> get positionDataStream =>
      rx.CombineLatestStream.combine3(
        audioPlayer.positionStream,
        audioPlayer.bufferedPositionStream,
        audioPlayer.durationStream,
        (position, bufferedPosition, duration) => PositionData(
          position: position,
          bufferedPosition: bufferedPosition,
          duration: duration ?? Duration.zero,
        ),
      );
}

this is my listner and song switching function

  void musicSetupListeners() {
    playerStateSubscription?.cancel();
    playerStateSubscription =
        songHandler.audioPlayer.playerStateStream.listen((state) async {
      log('Player state: ${state.processingState}');
      switch (state.processingState) {
        case ProcessingState.idle:
          // Handle idle state if needed
          break;
        case ProcessingState.loading:
          // Handle loading state if needed
          break;
        case ProcessingState.buffering:
          // Handle buffering state if needed
          break;
        case ProcessingState.ready:
          // Handle ready state if needed
          break;
        case ProcessingState.completed:
          await handleCompletion();
          break;
      }
    }, onError: (error) {
      log('Stream error: $error');
    });
  }

  Future<void> handleCompletion() async {
    await songHandler.pause();
    await songHandler.seek(Duration.zero);
    await Future.delayed(const Duration(milliseconds: 500), () async {
      try {
        if (!isProcessingCompletion.value) {
          isNewSongLoading.value = true;
          isNewSongLoading.refresh();
          isProcessingCompletion.value = true;
          isProcessingCompletion.refresh();

          await songHandler.stop();
          await Future.delayed(const Duration(milliseconds: 500), () async {
            await _playNextSong(this);
            isProcessingCompletion.value = false;
          });
        }
      } catch (e) {
        log('Error during completion handling: $e');
      }
    });
  }

  Future _playNextSong(MusicPlayerViewModel musicPlayerViewModel) async {
    if (isChangeList.value == false) {
      if (britanyPlyController.isShuffle.value) {
        await britanyPlyController.selectRandomSongs(musicPlayerViewModel);
      } else {
        await britanyPlyController.selectSequenceSongs(musicPlayerViewModel);
      }
    } else {
      await selectSequenceSongs();
    }
    Future.delayed(
      const Duration(milliseconds: 300),
      () {
        isNewSongLoading.value = false;
        isNewSongLoading.refresh();
      },
    );

    await songHandler.play();
  }

i want any one can help me to solve out this issue.

Upvotes: -1

Views: 32

Answers (1)

Rauf Endro
Rauf Endro

Reputation: 1

I also had this problem in my code

Upvotes: 0

Related Questions