[译]Flutter 特性丰富的音频播放器 just_audio (一) - 简介

3,356 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情


本文翻译自: just_audio | Flutter Package (flutter-io.cn)

译时版本:0.9.31


just_audio 

just_audio 是一个特性丰富的播放器,可用于 Android、 iOS、 macOS、 web、 Linux 和 Windows 。

平台支持 — API 文档 — 指南 — 后台音频 — 社区支持

image.png

快速概览

import 'package:just_audio/just_audio.dart';

final player = AudioPlayer();                   // 创建播放器
final duration = await player.setUrl(           // 加载 URL
    'https://foo.com/bar.mp3');                 // Schemes: (https: | file: | asset: )
player.play();                                  // 播放(不等待结束)
await player.play();                            // 播放(等待结束)
await player.pause();                           // 暂停(保持准备播放)
await player.seek(Duration(second: 10));        // 跳到第 10 秒的位置
await player.setSpeed(2.0);                     // 2倍加速播放
await player.setVolume(0.5);                    // 音量降半
await player.stop();                            // 停止并释放资源

使用多个播放器

// 用两个不同的音频文件准备播放器
final player1 = AudioPlayer(); await player1.setUrl(...);
final player2 = AudioPlayer(); await player2.setUrl(...);

// 同时播放
player1.play();
player2.play();

// 先播放一个,再播放另一个
await player1.play();
await player2.play();

// 循环播放 1 直到 2 播放完成
await player1.setLoopMode(LoopMode.one);
player1.play();          // 不等待
await player2.play();    // 等待 2 播放完成
await player1.pause();   // 完成 1 的播放

// 释放每个播放器的平台解码器和缓存
await player1.stop();
await player2.stop();

播放片段

// 播放 2 到 4 秒的片段,接着播放 10 到 12 秒的片段
await player.setClip(start: Duration(seconds: 2), end: Duration(seconds: 4));
await player.play(); await player.pause();
await player.setClip(start: Duration(seconds: 10), end: Duration(seconds: 12));
await player.play(); await player.pause();

await player.setClip(); // 清空片段区域

无缝播放 播放列表

// 定义播放列表
final playlist = ConcatenatingAudioSource(
  // 播放下个音频之前加载
  useLazyPreparation: true,
  // 定义切换算法
  shuffleOrder: DefaultShuffleOrder(),
  // 指定播放列表项目
  children: [
    AudioSource.uri(Uri.parse('https://example.com/track1.mp3')),
    AudioSource.uri(Uri.parse('https://example.com/track2.mp3')),
    AudioSource.uri(Uri.parse('https://example.com/track3.mp3')),
  ],
);

// 加载并播放 播放列表
await player.setAudioSource(playlist, initialIndex: 0, initialPosition: Duration.zero);
await player.seekToNext();                     // 跳到下个项目
await player.seekToPrevious();                 // 跳到前一个项目
await player.seek(Duration.zero, index: 2);    // 跳到 track3.mp3 的开始
await player.setLoopMode(LoopMode.all);        // 设置播放列表的循环模式 (关闭|所有|单个)
await player.setShuffleModeEnabled(true);      // 切换播放列表顺序()true|false)(真|假)

// 更新播放列表
await playlist.add(newChild1);
await playlist.insert(3, newChild2);
await playlist.removeAt(3);

设置 Header

// 设置 HTTP user agent
final player = AudioPlayer(
  userAgent: 'myradioapp/1.0 (Linux;Android 11) https://myradioapp.com',
);

// 设置请求 Header
final duration = await player.setUrl('https://foo.com/bar.mp3',
    headers: {'header1': 'value1', 'header2': 'value2'});

注意:Header 是通过本地的 HTTP 代理实现,在 Android 、 iOS 和 macOS 上需要启用 非 HTTPS 的支持。查看 平台特定配置.

使用缓存

// 清空资源缓存目录
await AudioPlayer.clearAssetCache();

// 播放时下载并缓存音频 (试验性特性)
final audioSource = LockCachingAudioSource('https://foo.com/bar.mp3');
await player.setAudioSource(audioSource);
// 删除缓存文件
await audioSource.clearCache();

注意:LockCachingAudioSource 是通过本地的 HTTP 代理实现,在 Android 、 iOS 和 macOS 上需要启用 非 HTTPS 的支持。查看 平台特定配置.

播放流音频资源

// 转送字节流到播放器中
class MyCustomSource extends StreamAudioSource {
  final List<int> bytes;
  MyCustomSource(this.bytes);
  
  @override
  Future<StreamAudioResponse> request([int? start, int? end]) async {
    start ??= 0;
    end ??= bytes.length;
    return StreamAudioResponse(
      sourceLength: bytes.length,
      contentLength: end - start,
      offset: start,
      stream: Stream.value(bytes.sublist(start, end)),
      contentType: 'audio/mpeg',
    );
  }
}

await player.setAudioSource(MyCustomSource());
player.play();

注意:StreamAudioSource 是通过本地的 HTTP 代理实现,在 Android 、 iOS 和 macOS 上需要启用 非 HTTPS 的支持。查看 平台特定配置.

错误处理

//  加载时捕获错误
try {
  await player.setUrl("https://s3.amazonaws.com/404-file.mp3");
} on PlayerException catch (e) {
  // iOS/macOS: 映射为 NSError.code
  // Android: 映射为 ExoPlayerException.type
  // Web: 映射为 MediaError.code
  // Linux/Windows: 映射为 PlayerErrorCode.index
  print("Error code: ${e.code}");
  // iOS/macOS: 映射为 NSError.localizedDescription
  // Android: 映射为 ExoPlaybackException.getMessage()
  // Web/Linux: 一个通用消息
  // Windows: MediaPlayerError.message
  print("Error message: ${e.message}");
} on PlayerInterruptedException catch (e) {
  // 该调用被中断,因为其它音频资源已加载或者在音频资源完成加载之前播放器已停止或已被释放。
  print("Connection aborted: ${e.message}");
} catch (e) {
  // 回退所有其它错误
  print('An error occured: $e');
}

// 回放时捕获错误(例,网络连接丢失)
player.playbackEventStream.listen((event) {}, onError: (Object e, StackTrace st) {
  if (e is PlayerException) {
    print('Error code: ${e.code}');
    print('Error message: ${e.message}');
  } else {
    print('An error occurred: $e');
  }
});

使用状态流

查看 状态模型 了解细节。

player.playerStateStream.listen((state) {
  if (state.playing) ... else ...
  switch (state.processingState) {
    case ProcessingState.idle: ...
    case ProcessingState.loading: ...
    case ProcessingState.buffering: ...
    case ProcessingState.ready: ...
    case ProcessingState.completed: ...
  }
});

//  也看一下:
// - durationStream
// - positionStream
// - bufferedPositionStream
// - sequenceStateStream
// - sequenceStream
// - currentIndexStream
// - icyMetadataStream
// - playingStream
// - processingStateStream
// - loopModeStream
// - shuffleModeEnabledStream
// - volumeStream
// - speedStream
// - playbackEventStream

相关人员

该项目是由 GitHub 贡献者 的惊艳的开源社区和捐献者支持。非常感谢!


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情