You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter audio_service iOS后台切歌卡顿缓冲问题求助

iOS后台使用audio_service切歌失败,播放器持续卡在缓冲状态

我基于Flutter的audio_service包开发音频流应用,前台切歌功能正常,但在iOS后台状态下无法切换歌曲,音频播放器始终卡在缓冲状态。该问题仅出现于iOS平台,Android端运行完全正常。

已完成的配置与操作

  • 在Info.plist和Xcode中配置了音频后台模式
  • 实现了音频播放所需的后台任务

SongHandler代码

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,
        ),
      );
}

监听器与切歌函数代码

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();
  }

希望有人能帮忙解决这个问题。

内容的提问来源于stack exchange,提问作者Hassaan Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:47:17