admin管理员组

文章数量:1335444

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,
        ProcessingStatepleted: AudioProcessingStatepleted,
      }[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.CombineLatestStreambine3(
        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 ProcessingStatepleted:
          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.

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,
        ProcessingStatepleted: AudioProcessingStatepleted,
      }[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.CombineLatestStreambine3(
        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 ProcessingStatepleted:
          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.

Share Improve this question asked Nov 20, 2024 at 6:42 Hassaan AhmedHassaan Ahmed 1
Add a comment  | 

1 Answer 1

Reset to default 0

I also had this problem in my code

本文标签: