【问题标题】:Flutter just_audio loop mode is not loopingFlutter just_audio 循环模式不循环
【发布时间】:2021-03-11 10:27:18
【问题描述】:

我正在使用flutter audio_service (https://pub.dev/packages/audio_service) 和just_audio (https://pub.dev/packages/just_audio) 在前台和后台播放音频文件。

我继承了 BackgroundAudioTask,添加了一个 AudioPlayer 实例,并覆盖了所需的方法。例如我更新了重复模式:

@override
  Future<void> onSetRepeatMode(AudioServiceRepeatMode repeatMode) async {
    super.onSetRepeatMode(repeatMode);
    switch (repeatMode)
    {
      case  AudioServiceRepeatMode.all:
        _audioPlayer.setLoopMode(LoopMode.all);
        break;
      case  AudioServiceRepeatMode.none:
        _audioPlayer.setLoopMode(LoopMode.off);
        break;
      case AudioServiceRepeatMode.one:
        _audioPlayer.setLoopMode(LoopMode.one);
        break;
      case AudioServiceRepeatMode.group:
        _audioPlayer.setLoopMode(LoopMode.all);
        break;
    }
  }

@override
  Future<void> onPlayFromMediaId(String mediaId) async {
    await _audioPlayer.stop();
    // Get queue index by mediaId.
    _queueIndex = _queue.indexWhere((test) => test.id == mediaId);
    // Set url source to _audioPlayer.
    downloadAndPlay(_mediaItem);
  }

我正在尝试添加播放列表并循环播放音频文件,但似乎缺少某些内容,并且在播放曲目 #1 后,播放器试图从相同的 #1 曲目开始并再次播放。 这是我的代码(downloadAndPlay 的一部分):

var list=List<AudioSource>();

for (int t=0;t<_queue.length;t++)
  {
    var mi=_queue[t];
    var url = mi.extras['source'];
    list.add(AudioSource.uri(Uri.parse(url)));
  }

D("Loading list with ${list.length} items");
D("Seeking to index : $_queueIndex");
await _audioPlayer.load(ConcatenatingAudioSource(children: list),
    initialIndex: _queueIndex, initialPosition: Duration.zero);
AudioService.updateQueue(_queue);
AudioService.setRepeatMode(AudioServiceRepeatMode.all);
_audioPlayer.play();

我添加了这个来检查 ProcessingState.completed 是否被触发,这意味着它到达了轨道的结尾,但它没有触发:

playerEventSubscription = _audioPlayer.playbackEventStream.listen((event) {
  D("audioPlayerTask: playbackEventStream : ${event.processingState}");
  switch (event.processingState) {
    case ProcessingState.ready:
      _setState(state: AudioProcessingState.ready);
      break;
    case ProcessingState.buffering:
      _setState(state: AudioProcessingState.buffering);
      break;
    case ProcessingState.completed:
      _handlePlaybackCompleted();
      break;
    default:
      break;
  }
});

【问题讨论】:

  • 您应该等待“加载”完成后再搜索(使用等待),或者最好将初始搜索索引和位置传递给“加载”方法(有关详细信息,请参阅文档)。所有这些方法都是异步的,因此如果您希望它们按顺序完成,您应该等待它们的完成。唯一的例外是“播放”,它需要很长时间才能完成,您可能不需要等待它完成。如果您正在编写正确的代码但循环仍然无法正常工作,则这是一个错误,您应该报告它。
  • 我添加了等待,并更新了上面的代码。但是它在播放完当前曲目后仍然不会进入下一曲目。 @RyanHeise

标签: flutter audio queue just-audio


【解决方案1】:

在您的 BackgroundAudioTask 子类中,您应该重写 onSetRepeatMode 方法。

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

class AudioPlayerTask extends BackgroundAudioTask {
  AudioPlayer _player = new AudioPlayer();

  ...

  @override
  Future<void> onSetRepeatMode(AudioServiceRepeatMode repeatMode) async {
    switch (repeatMode) {
      case AudioServiceRepeatMode.none:
        await _player.setLoopMode(LoopMode.off);
        break;
      case AudioServiceRepeatMode.one:
        await _player.setLoopMode(LoopMode.one);
        break;
      case AudioServiceRepeatMode.all:
      case AudioServiceRepeatMode.group:
        await _player.setLoopMode(LoopMode.all);
        break;
    }
  }
}

这个特定的实现使用just_audio 作为音频播放器。请注意,AudioPlayerTask 是 just_audio AudioPlayer 出现的唯一类。它不应出现在应用程序的任何其他类中。您将在其他任何地方使用 audio_serviceAudioService 类。

例如,当用户按下按钮时,您可以运行以下代码行:

AudioService.setRepeatMode(AudioServiceRepeatMode.none);

这将通知系统并让BackgroundAudioTask 运行onSetRepeatMode 以便内部AudioPlayer 类可以做它的事情。

【讨论】:

  • 感谢这个简单的例子!
【解决方案2】:

您从未调用过_audioPlayer.setLoopMode(),因此它不会循环。

您也在使用 audio_service,但您的音频代码似乎是一半 inside 和一半 outside audio_service。具体来说,您的_audioPlayer 实例位于audio_service 之外,而您的setRepeatMode 实现位于audio_service 中,并且无法访问您位于audio_service 之外的_audioPlayer 实例。

将所有音频逻辑封装在 audio_service 中,以便您的 setRepeatMode 实现能够引用您的 _audioPlayer 实例并在其上调用 _audioPlayer.setLoopMode()。这实际上是我们封装的原因(将方法与它们需要操作的数据捆绑在一起),但在这种情况下,我们在 audio_service 中这样做还有另一个原因,那就是 audio_service 之外的所有内容“封装”随时可能被摧毁。因此,如果您的应用程序转换到后台并且您的 UI 被破坏,您不希望一半的音频逻辑被破坏。如果您将其全部封装在 audio_service 中,它将能够在 UI 的破坏中幸存下来,并且由于它是自包含的,因此将拥有能够在后台继续播放音频所需的一切。

此外,您的应用可能想要在前台播放音频这一事实并不意味着您需要打破这种封装。这个封装的音频代码完全能够在有 UI 和没有 UI 的情况下运行,因此您实际上不需要更改编程风格来支持前台情况。

【讨论】:

  • 我没有分享足够的,但_audioPlayer在BackgroundAudioTask之内和范围内。我在那里覆盖了 onSetRepeatMode(在上面的代码中更新)。但还是不行!
  • 有一段代码,您可以在其中使用客户端 API(例如 AudioService.updateQueue)“从”客户端“向”后台任务发送消息。相同的代码块(位于客户端)引用了一个名为 _audioPlayer 的变量。因此,该音频播放器位于客户端而不是后台任务中。您从未在该播放器上调用 _audioPlayer.setLoopMode()。如果该块实际上在后台任务中,它看起来不像。它使用客户端 API,因此代码在这种解释中也没有意义。您需要清楚地分开这两层。
  • 我想我找到了问题:AudioService.currentMediaItemStream 没有在曲目完成并开始播放下一首曲目时触发。
  • 正如我上面解释的那样,您似乎有两个不同的 _audioPlayer 变量,一个在后台任务内部,一个在后台任务外部,您正在混淆,将其中一个与媒体源一起加载,但配置另一种是循环模式。如果不是这种情况,您需要回答我之前的评论并解释该代码块是在后台音频任务内部还是外部,如果在内部,为什么它使用只能从外部使用的客户端 API。
  • 不,先生,我没有使用两个音频播放器,只有一个,它位于 BackgroundAudioTask 的子类中,与您订购的完全一样 :) 但是 AudioService.currentMediaItemStream 不会在曲目自动转到下一个时添加任何内容.
【解决方案3】:

我这样做的方式是简单地放入 onStart 方法:

await _player.setLoopMode(LoopMode.all);

这当然不能由用户更改。

【讨论】:

    猜你喜欢
    • 2017-11-17
    • 2020-02-19
    • 1970-01-01
    • 2014-12-29
    • 1970-01-01
    • 2016-04-24
    • 2020-01-27
    • 2023-03-10
    相关资源
    最近更新 更多