本文介绍了如何通过audio_service flutter中的播放列表传递和播放特定队列位置媒体项目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用音频服务 just_audio 包.初始化音乐播放器时,我想播放播放列表中的特定队列位置媒体项目.当我调用AudioService.start()方法时,它始终会播放播放列表的第一项.启动音频服务时如何通过播放列表传递和播放特定队列位置媒体项目?

I am using flutter audio_service and just_audio package for music player. I want to play specific queue position media item from playlist when I initialize the music player. It is always playing first item of the playlist when I called AudioService.start() method. How can I pass and play specific queue position media item from playlist when I start the audio service?

AudioService启动

AudioService start

AudioService.start(
        backgroundTaskEntrypoint: _audioPlayerTaskEntrypoint,
        androidNotificationChannelName: 'Zenmind',
        androidNotificationColor: 0xFF2196f3,
        androidNotificationIcon: 'mipmap/ic_launcher',
        androidEnableQueue: true,
        params: params); // [params contains playlist ] 

_audioPlayerTaskEntrypoint代码

_audioPlayerTaskEntrypoint code

void _audioPlayerTaskEntrypoint() async {
  AudioServiceBackground.run(() => AudioPlayerTask());
}

AudioPlayerTask类

AudioPlayerTask class

class AudioPlayerTask extends BackgroundAudioTask {
  var _queue = <MediaItem>[];
  AudioPlayer _player = new AudioPlayer();
  AudioProcessingState _skipState;
  Seeker _seeker;
  StreamSubscription<PlaybackEvent> _eventSubscription;

  List<MediaItem> get queue => _queue;
  int get index => _player.currentIndex;
  MediaItem get mediaItem => index == null ? null : queue[index];

  @override
  Future<void> onStart(Map<String, dynamic> params) async {
    _queue.clear();
    List mediaItems = params['data'];
    // print(params['data']);
    for (int i = 0; i < mediaItems.length; i++) {
      MediaItem mediaItem = MediaItem.fromJson(mediaItems[i]);
      _queue.add(mediaItem);
    }

    _player.currentIndexStream.listen((index) {
      print("index value is $index");
      if (index != null) {
        AudioServiceBackground.setMediaItem(queue[index]);
      }
    });
    _eventSubscription = _player.playbackEventStream.listen((event) {
      _broadcastState();
    });

    _player.processingStateStream.listen((state) {
      switch (state) {
        case ProcessingState.completed:
          onStop();
          break;
        case ProcessingState.ready:
          _skipState = null;
          break;
        default:
          break;
      }
    });

    AudioServiceBackground.setQueue(queue);
    try {
      await _player.setAudioSource(ConcatenatingAudioSource(
        children:
            queue.map((item) => AudioSource.uri(Uri.parse(item.id))).toList(),
      ));
      onSkipToQueueItem(queue[1].id);
      onPlay();
    } catch (e) {
      print("Error: $e");
      onStop();
    }
  }

  @override
  Future<void> onSkipToQueueItem(String mediaId) async {
    final newIndex = queue.indexWhere((item) => item.id == mediaId);
    if (newIndex == -1) return;
    _skipState = newIndex > index
        ? AudioProcessingState.skippingToNext
        : AudioProcessingState.skippingToPrevious;
    _player.seek(Duration.zero, index: newIndex);
    AudioServiceBackground.sendCustomEvent('skip to $newIndex');
  }

  @override
  Future<void> onPlay() => _player.play();

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

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

  @override
  Future<void> onFastForward() => _seekRelative(fastForwardInterval);

  @override
  Future<void> onRewind() => _seekRelative(-rewindInterval);

  @override
  Future<void> onSeekForward(bool begin) async => _seekContinuously(begin, 1);

  @override
  Future<void> onSeekBackward(bool begin) async => _seekContinuously(begin, -1);

  @override
  Future<void> onStop() async {
    await _player.dispose();
    _eventSubscription.cancel();
    await _broadcastState();
    await super.onStop();
  }

  Future<void> _seekRelative(Duration offset) async {
    var newPosition = _player.position + offset;
    if (newPosition < Duration.zero) newPosition = Duration.zero;
    if (newPosition > mediaItem.duration) newPosition = mediaItem.duration;
    // if (newPosition > _player.duration) newPosition = _player.duration;
    await _player.seek(newPosition);
  }

  void _seekContinuously(bool begin, int direction) {
    _seeker?.stop();
    if (begin) {
      _seeker = Seeker(
          _player,
          Duration(seconds: 10 * direction),
          // Duration(seconds: 1), mediaItem)
          Duration(seconds: 1),
          queue[_player.currentIndex])
        ..start();
    }
  }

  Future<void> _broadcastState() async {
    await AudioServiceBackground.setState(
      controls: [
        MediaControl.skipToPrevious,
        if (_player.playing) MediaControl.pause else MediaControl.play,
        MediaControl.stop,
        MediaControl.skipToNext,
      ],
      systemActions: [
        MediaAction.seekTo,
        MediaAction.seekForward,
        MediaAction.seekBackward,
      ],
      androidCompactActions: [0, 1, 3],
      processingState: _getProcessingState(),
      playing: _player.playing,
      position: _player.position,
      bufferedPosition: _player.bufferedPosition,
      speed: _player.speed,
    );
  }

  AudioProcessingState _getProcessingState() {
    if (_skipState != null) return _skipState;
    switch (_player.processingState) {
      case ProcessingState.idle:
        return AudioProcessingState.stopped;
      case ProcessingState.loading:
        return AudioProcessingState.connecting;
      case ProcessingState.buffering:
        return AudioProcessingState.buffering;
      case ProcessingState.ready:
        return AudioProcessingState.ready;
      case ProcessingState.completed:
        return AudioProcessingState.completed;
      default:
        throw Exception("Invalid state: ${_player.processingState}");
    }
  }
}

推荐答案

start params 参数仅限于简单数据类型,无法传递 MediaItem 的列表.相反,您可以通过以下启动顺序来启动音频服务:

The params parameter to start is restricted to simple data types and it is not possible to pass in a list of MediaItems. Instead, you can start up the audio service via this startup sequence:

// Just start the service
await AudioService.start(....);
// Set the playlist
await AudioService.updateQueue(playlist);
// Jump to the right item
await AudioService.skipToQueueItem(playlist[startIndex].id);
// Play
AudioService.play(); // don't await!

await 关键字很重要.这些方法是异步的,在较早的方法完成之前,不应调用较新的方法.例如,您不希望跳到特定的队列项目,直到之后实际上已经设置了队列.但是请注意,在最后一步缺少 await :除非您要等待播放完成,否则不要等待 play 调用.

The await keyword is important. These methods are asynchronous, and the later methods should not be called until the earlier ones have completed. For example, you don't want to skip to a particular queue item until after the queue has actually been set. But note the lack of await on the last step: you don't await the play call unless you want to wait for playback to complete.

适当地定义您的后台音频任务回调: onStart onUpdateQueue onSkipToQueueItem onPlay .您的 onSkipToQueueItem onPlay 回调看起来已经不错.但是您需要 onUpdateQueue :

Define your background audio task callbacks appropriately: onStart, onUpdateQueue, onSkipToQueueItem and onPlay. Your onSkipToQueueItem and onPlay callbacks already look fine. But you need onUpdateQueue:

Future<void> onUpdateQueue(List<MediaItem> queue) async {
  AudioServiceBackground.setQueue(_queue = queue);
  await _player.setAudioSource(ConcatenatingAudioSource(
    children:
        queue.map((item) => AudioSource.uri(Uri.parse(item.id))).toList(),
));
}

您已经有一个 onStart ,但是请记住,使用上面建议的启动顺序,将在后续步骤中设置队列,并且播放器将在后续步骤中跳至右侧的队列项目,因此您可以从 onStart 中删除这些部分,而只需保留用于初始化事件监听器的代码即可.

You already have an onStart, but remember that using the suggested startup sequence above, the queue will be set in a later step, and the player will skip to the right queue item in a later step, so you can remove those parts from your onStart, and just keep the code that initialises the event listeners.

这篇关于如何通过audio_service flutter中的播放列表传递和播放特定队列位置媒体项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 14:39