无法为Android的audio_service 0.8.0 flutter添加自定义队列 [英] Unable to add custom queue to audio_service 0.8.0 flutter for android

查看:299
本文介绍了无法为Android的audio_service 0.8.0 flutter添加自定义队列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想查看一个如何向音频服务添加自定义队列的示例.我正在返回一个带有音乐列表的JSON.但是我无法将其添加到音频服务后台并运行它.请帮忙.这是我的球员班级的一个例子.songModel具有所有歌曲数据下面的代码是我的实现.

I wanted to view an example of how to add a custom queue to the Audio service. I am returning a JSON that has a list of music. But I am unable to add this to the audio service background and run it. Please help. This is an example of my player class. The songModel Has all the song data The code below is my implementation.

`import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
import 'Songs.dart';
import 'backgroundTask.dart';

class Player extends StatefulWidget {
  SongModel songModel;
      Player({@required this.songModel});
  @override
  _Player createState() => _Player();
}

class _Player extends State<Player> {
  static SongModel data;
   List<MediaItem> songs;
  @override
  Future<void> initState()  {
    super.initState();
    data = widget.songModel;
    songs = data.songs;
    print(songs);
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: StreamBuilder<ScreenState>(
        stream: _screenStateStream,
        builder: (context, snapshot) {
          final screenState = snapshot.data;
          final queue = screenState?.queue;
          final mediaItem = screenState?.mediaItem;
          final state = screenState?.playbackState;
          final basicState = state?.basicState ?? BasicPlaybackState.none;
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(data.currentSong.title),
              Text(data.songs.length.toString()),
              Column(mainAxisAlignment: MainAxisAlignment.center, children: [
                if (queue != null && queue.isNotEmpty)
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        icon: Icon(Icons.skip_previous),
                        iconSize: 64.0,
                        onPressed: mediaItem == queue.first
                            ? null
                            : AudioService.skipToPrevious,
                      ),
                      IconButton(
                        icon: Icon(Icons.skip_next),
                        iconSize: 64.0,
                        onPressed: mediaItem == queue.last
                            ? null
                            : AudioService.skipToNext,
                      ),
                      if (mediaItem?.title != null) Text(mediaItem.title),
                    ],
                  ),
              ]),
              if (mediaItem?.title != null) Text(mediaItem.title),
              if (basicState == BasicPlaybackState.playing)
                RaisedButton(child: Text("Pause"), onPressed: pause)
              else if (basicState == BasicPlaybackState.buffering ||
                  basicState == BasicPlaybackState.skippingToNext ||
                  basicState == BasicPlaybackState.skippingToPrevious)
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: SizedBox(
                    width: 64.0,
                    height: 64.0,
                    child: CircularProgressIndicator(),
                  ),
                )
              else if (state != BasicPlaybackState.stopped)
                RaisedButton(child: Text("Stop"), onPressed: stop),
              RaisedButton(child: Text("Play"), onPressed: play),
              RaisedButton(child: Text("StartService"),onPressed:()=> start(data.songs))
            ],
          );
        },
      )),
    );
  }


}


 _backgroundTaskEntrypoint(){

  AudioServiceBackground.run(() => AudioPlayerTask());
}

start(data) {
  AudioService.start(
    backgroundTaskEntrypoint:_backgroundTaskEntrypoint,
    androidNotificationChannelName: 'com.zimosound_app.us',
    notificationColor: 0xFF2196f3,
    androidNotificationIcon: 'mipmap/ic_launcher',
    enableQueue: true,
  );
  AudioServiceBackground.setQueue(data);
  print(AudioService.queue);
}







stop() => AudioService.stop();

play() async {
  if (await AudioService.running) {
    AudioService.play();
  }
}

pause() => AudioService.pause();

Stream<ScreenState> get _screenStateStream =>
    Rx.combineLatest3<List<MediaItem>, MediaItem, PlaybackState, ScreenState>(
        AudioService.queueStream,
        AudioService.currentMediaItemStream,
        AudioService.playbackStateStream,
        (queue, mediaItem, playbackState) =>
            ScreenState(queue, mediaItem, playbackState));

class ScreenState {
  final List<MediaItem> queue;
  final MediaItem mediaItem;
  final PlaybackState playbackState;

  ScreenState(this.queue, this.mediaItem, this.playbackState);
}
`

这是我的背景音频课程

    import 'dart:async';

import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';

MediaControl playControl = MediaControl(
  androidIcon: 'drawable/ic_action_play_arrow',
  label: 'Play',
  action: MediaAction.play,
);
MediaControl pauseControl = MediaControl(
  androidIcon: 'drawable/ic_action_pause',
  label: 'Pause',
  action: MediaAction.pause,
);
MediaControl skipToNextControl = MediaControl(
  androidIcon: 'drawable/ic_action_skip_next',
  label: 'Next',
  action: MediaAction.skipToNext,
);
MediaControl skipToPreviousControl = MediaControl(
  androidIcon: 'drawable/ic_action_skip_previous',
  label: 'Previous',
  action: MediaAction.skipToPrevious,
);
MediaControl stopControl = MediaControl(
  androidIcon: 'drawable/ic_action_stop',
  label: 'Stop',
  action: MediaAction.stop,
);


final prevControl = MediaControl(
  androidIcon: 'drawable/ic_action_stop',
  label: 'Stop',
  action: MediaAction.stop,
);

class AudioPlayerTask extends BackgroundAudioTask {
  var _queue = AudioService.queue;
  int _queueIndex;
  AudioPlayer _audioPlayer = new AudioPlayer();
  Completer _completer = Completer();
  BasicPlaybackState _skipState;
  bool _playing;

//  AudioPlayerTask(List<MediaItem> queue, int queueIndex){
//    this._queue = queue;
//    this._queueIndex = queueIndex;
//  }

  bool get hasNext => _queueIndex + 1 < _queue.length;

  bool get hasPrevious => _queueIndex > 0;

  MediaItem get mediaItem => _queue[_queueIndex];

  BasicPlaybackState _eventToBasicState(AudioPlaybackEvent event) {
    if (event.buffering) {
      return BasicPlaybackState.buffering;
    } else {
      switch (event.state) {
        case AudioPlaybackState.none:
          return BasicPlaybackState.none;
        case AudioPlaybackState.stopped:
          return BasicPlaybackState.stopped;
        case AudioPlaybackState.paused:
          return BasicPlaybackState.paused;
        case AudioPlaybackState.playing:
          return BasicPlaybackState.playing;
        case AudioPlaybackState.connecting:
          return _skipState ?? BasicPlaybackState.connecting;
        case AudioPlaybackState.completed:
          return BasicPlaybackState.stopped;
        default:
          throw Exception("Illegal state");
      }
    }
  }

  @override
  Future<void> onStart() async {
    var playerStateSubscription = _audioPlayer.playbackStateStream
        .where((state) => state == AudioPlaybackState.completed)
        .listen((state) {
      _handlePlaybackCompleted();
    });
    var eventSubscription = _audioPlayer.playbackEventStream.listen((event) {
      final state = _eventToBasicState(event);
      if (state != BasicPlaybackState.stopped) {
        _setState(
          state: state,
          position: event.position.inMilliseconds,
        );
      }
    });
   // AudioServiceBackground.setQueue(_queue);
    await onSkipToNext();
    await _completer.future;
    playerStateSubscription.cancel();
    eventSubscription.cancel();
  }

  void _handlePlaybackCompleted() {
    if (hasNext) {
      onSkipToNext();
    } else {
      onStop();
    }
  }

  void playPause() {
    if (AudioServiceBackground.state.basicState == BasicPlaybackState.playing)
      onPause();
    else
      onPlay();
  }

  @override
  Future<void> onSkipToNext() => _skip(1);

  @override
  Future<void> onSkipToPrevious() => _skip(-1);

  Future<void> _skip(int offset) async {
    final newPos = _queueIndex + offset;
    if (!(newPos >= 0 && newPos < _queue.length)) return;
    if (_playing == null) {
      // First time, we want to start playing
      _playing = true;
    } else if (_playing) {
      // Stop current item
      await _audioPlayer.stop();
    }
    // Load next item
    _queueIndex = newPos;
    AudioServiceBackground.setMediaItem(mediaItem);
    _skipState = offset > 0
        ? BasicPlaybackState.skippingToNext
        : BasicPlaybackState.skippingToPrevious;
    await _audioPlayer.setUrl(mediaItem.id);
    _skipState = null;
    // Resume playback if we were playing
    if (_playing) {
      onPlay();
    } else {
      _setState(state: BasicPlaybackState.paused);
    }
  }

  @override
  void onPlay() {
    if (_skipState == null) {
      _playing = true;
      _audioPlayer.play();
      AudioServiceBackground.sendCustomEvent('just played');
    }
  }

  @override
  void onPause() {
    if (_skipState == null) {
      _playing = false;
      _audioPlayer.pause();
      AudioServiceBackground.sendCustomEvent('just paused');
    }
  }

  @override
  void onSeekTo(int position) {
    _audioPlayer.seek(Duration(milliseconds: position));
  }

  @override
  void onClick(MediaButton button) {
    playPause();
  }

  @override
  Future<void> onStop() async {
    await _audioPlayer.stop();
    await _audioPlayer.dispose();
    _setState(state: BasicPlaybackState.stopped);
    _completer.complete();
  }

  void _setState({@required BasicPlaybackState state, int position}) {
    if (position == null) {
      position = _audioPlayer.playbackEvent.position.inMilliseconds;
    }
    AudioServiceBackground.setState(
      controls: getControls(state),
      systemActions: [MediaAction.seekTo],
      basicState: state,
      position: position,
    );
  }

  List<MediaControl> getControls(BasicPlaybackState state) {
    if (_playing) {
      return [
        skipToPreviousControl,
        pauseControl,
        skipToNextControl,
        stopControl,
      ];
    } else {
      return [

        skipToPreviousControl,
        playControl,
        skipToNextControl,
        stopControl,
      ];
    }
  }

  @override
  void onAudioBecomingNoisy() {
    playPause();
  }

  @override
  void onAudioFocusGained() {
    playPause();
  }


  @override
  void onAudioFocusLost() {
    playPause();
  }

  @override
  void onAudioFocusLostTransient() {
    playPause();
  }

  @override
  void onAudioFocusLostTransientCanDuck() {
    playPause();
  }

}

推荐答案

我遇到了类似的问题,正在用歌曲数据解析JSON文件以从中形成队列.这是我到目前为止发现的.

I have faced a similar issue and am parsing JSON files with song data to form a queue from them. This is what I've found so far.

据我了解,只有 BackgroundAudioTask 类应该调用 AudioServiceBackground ,并且您的UI通过 AudioService 进行上述调用.话虽这么说,我还没能使队列工作得那么好.我所做的是使用2秒无声的本地mp3初始化队列,然后使用以下内容将歌曲添加到队列中(只是一个俗气的解决方法,但目前似乎可以正常工作):

As I understand it, only the BackgroundAudioTask class should make calls to AudioServiceBackground, and your UI works through AudioService to perform said calls. That being said, I haven't been able to get the queue to work all that well. What I've done is initialized the queue with a local mp3 of 2 seconds of silence, and then added songs to the queue with the following (just a tacky workaround, but seems to work at the moment):

MediaItem myMediaItem = new MediaItem(...);
queue.add(myMediaItem);
AudioService.updateQueue(queue);
AudioService.addQueueItem(myMediaItem);

我认为这是可行的,因为这样可以将媒体项目添加到主要和背景隔离中.它不是最大的,但是可以.我在我的 StreamBuilder< ScreenState> 函数中调用添加到队列中的尽可能多的项,但为确保不会太多,我一次只能执行10到20,仅当当前正在播放的MediaItem 与队列中的最后一个项目之间的距离不到5.

I think this works because this way the media item is added to both the main and background isolate. It's not the greatest, but it works. I call in my StreamBuilder<ScreenState> function for as many items I add to the queue, but to be sure it's not too much I only do 10-20 at a time, only when the current MediaItem being played is within 5 from the last items in the queue.

希望这会有所帮助,祝你好运.

Hope this helps, good luck.

还值得一提的是,您应该在定义的 BackgroundAudioTask 类中覆盖诸如 onUpdateQueue 之类的函数.这是使队列正确更新的关键因素.

Also worth mentioning, you should be overriding functions like onUpdateQueue in the BackgroundAudioTask class you define. That’s a crucial element to getting your queue to update properly.

这篇关于无法为Android的audio_service 0.8.0 flutter添加自定义队列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆