How to pass and play specific queue position media item from playlist in audio_service flutter?

眉间皱痕 提交于 2021-02-11 12:22:25

问题


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 start

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

_audioPlayerTaskEntrypoint code

void _audioPlayerTaskEntrypoint() async {
  AudioServiceBackground.run(() => 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}");
    }
  }
}

回答1:


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!

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.

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

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.



来源:https://stackoverflow.com/questions/66005048/how-to-pass-and-play-specific-queue-position-media-item-from-playlist-in-audio-s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!