Flutter音频播放插件ogg_opus_player的使用

Flutter音频播放插件ogg_opus_player的使用

简介

ogg_opus_player 是一个用于Flutter应用程序的插件,支持播放OGG Opus格式的音频文件。该插件支持多个平台,包括iOS、macOS、Windows、Linux和Android。

支持的平台及最低系统版本要求

平台 是否支持 最低系统版本
iOS 10.0
macOS 10.12
Windows -
Linux -
Android minSdk 21

快速开始

1. 添加依赖

pubspec.yaml 文件中添加 ogg_opus_player 依赖:

dependencies:
  ogg_opus_player: $latest_version
2. 使用 OggOpusPlayer 播放 OGG Opus 文件

以下是一个简单的示例,展示了如何使用 OggOpusPlayer 播放 OGG Opus 文件:

import 'package:ogg_opus_player/ogg_opus_player.dart';

void playOggOpusFile() {
  // 初始化播放器,传入音频文件路径
  final player = OggOpusPlayer("file_path");

  // 播放音频
  player.play();

  // 暂停音频
  player.pause();

  // 释放资源
  player.dispose();
}

完整示例 Demo

下面是一个完整的示例应用,展示了如何在Flutter应用中使用 ogg_opus_player 插件来播放和录制 OGG Opus 音频文件。

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ogg_opus_player/ogg_opus_player.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final tempDir = await getTemporaryDirectory();
  final workDir = p.join(tempDir.path, 'ogg_opus_player');
  debugPrint('workDir: $workDir');
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Column(
          children: [
            _PlayAssetExample(directory: workDir),
            const SizedBox(height: 20),
            _RecorderExample(dir: workDir),
          ],
        ),
      ),
    ),
  );
}

class _PlayAssetExample extends StatefulWidget {
  const _PlayAssetExample({Key? key, required this.directory}) : super(key: key);
  final String directory;

  @override
  _PlayAssetExampleState createState() => _PlayAssetExampleState();
}

class _PlayAssetExampleState extends State<_PlayAssetExample> {
  bool _copyCompleted = false;
  String _path = '';

  @override
  void initState() {
    super.initState();
    _copyAssets();
  }

  Future<void> _copyAssets() async {
    final dir = await getApplicationDocumentsDirectory();
    final dest = File(p.join(dir.path, "test.ogg"));
    _path = dest.path;
    if (await dest.exists()) {
      setState(() {
        _copyCompleted = true;
      });
      return;
    }

    // 从 assets 中加载音频文件并保存到本地
    final bytes = await rootBundle.load('audios/test.ogg');
    await dest.writeAsBytes(bytes.buffer.asUint8List());
    setState(() {
      _copyCompleted = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return _copyCompleted
        ? _OpusOggPlayerWidget(
            path: _path,
            key: ValueKey(_path),
          )
        : const Center(
            child: SizedBox(
              width: 24,
              height: 24,
              child: CircularProgressIndicator(),
            ),
          );
  }
}

class _OpusOggPlayerWidget extends StatefulWidget {
  const _OpusOggPlayerWidget({Key? key, required this.path}) : super(key: key);

  final String path;

  @override
  State<_OpusOggPlayerWidget> createState() => _OpusOggPlayerWidgetState();
}

class _OpusOggPlayerWidgetState extends State<_OpusOggPlayerWidget> {
  OggOpusPlayer? _player;
  Timer? timer;
  double _playingPosition = 0;
  static const _kPlaybackSpeedSteps = [0.5, 1.0, 1.5, 2.0];
  int _speedIndex = 1;

  @override
  void initState() {
    super.initState();
    // 每50毫秒更新一次播放进度
    timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
      setState(() {
        _playingPosition = _player?.currentPosition ?? 0;
      });
    });
  }

  @override
  void dispose() {
    timer?.cancel();
    _player?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final state = _player?.state.value ?? PlayerState.idle;
    return Center(
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text('position: ${_playingPosition.toStringAsFixed(2)}'), // 显示当前播放位置
          const SizedBox(height: 8),
          if (state == PlayerState.playing)
            IconButton(
              onPressed: () {
                _player?.pause(); // 暂停播放
              },
              icon: const Icon(Icons.pause),
            )
          else
            IconButton(
              onPressed: () {
                _player?.dispose();
                _speedIndex = 1;
                _player = OggOpusPlayer(widget.path); // 初始化播放器
                _player?.play(); // 开始播放
                _player?.state.addListener(() {
                  setState(() {});
                  if (_player?.state.value == PlayerState.ended) {
                    _player?.dispose();
                    _player = null;
                  }
                });
              },
              icon: const Icon(Icons.play_arrow),
            ),
          IconButton(
            onPressed: () {
              setState(() {
                _player?.dispose();
                _player = null;
              });
            },
            icon: const Icon(Icons.stop), // 停止播放
          ),
          if (_player != null)
            TextButton(
              onPressed: () {
                _speedIndex++;
                if (_speedIndex >= _kPlaybackSpeedSteps.length) {
                  _speedIndex = 0;
                }
                _player?.setPlaybackRate(_kPlaybackSpeedSteps[_speedIndex]); // 设置播放速度
              },
              child: Text('X${_kPlaybackSpeedSteps[_speedIndex]}'),
            ),
        ],
      ),
    );
  }
}

class _RecorderExample extends StatefulWidget {
  const _RecorderExample({
    Key? key,
    required this.dir,
  }) : super(key: key);

  final String dir;

  @override
  State<_RecorderExample> createState() => _RecorderExampleState();
}

class _RecorderExampleState extends State<_RecorderExample> {
  late String _recordedPath;
  OggOpusRecorder? _recorder;

  @override
  void initState() {
    super.initState();
    _recordedPath = p.join(widget.dir, 'test_recorded.ogg');
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        const SizedBox(height: 8),
        if (_recorder == null)
          IconButton(
            onPressed: () {
              final file = File(_recordedPath);
              if (file.existsSync()) {
                File(_recordedPath).deleteSync();
              }
              File(_recordedPath).createSync(recursive: true);
              final recorder = OggOpusRecorder(_recordedPath);
              recorder.start(); // 开始录音
              setState(() {
                _recorder = recorder;
              });
            },
            icon: const Icon(Icons.keyboard_voice_outlined),
          )
        else
          IconButton(
            onPressed: () async {
              await _recorder?.stop(); // 停止录音
              debugPrint('recording stopped');
              debugPrint('duration: ${await _recorder?.duration()}'); // 打印录音时长
              debugPrint('waveform: ${await _recorder?.getWaveformData()}'); // 打印波形数据
              _recorder?.dispose();
              setState(() {
                _recorder = null;
              });
            },
            icon: const Icon(Icons.stop),
          ),
        const SizedBox(height: 8),
        if (_recorder == null && File(_recordedPath).existsSync())
          _OpusOggPlayerWidget(path: _recordedPath), // 播放录制的音频
      ],
    );
  }
}

特殊平台要求

Linux

在Linux上使用 ogg_opus_player 时,需要安装 SDL2 和 Opus 库:

sudo apt-get install libsdl2-dev
sudo apt-get install libopus-dev
iOS/macOS

如果您的应用需要使用麦克风进行录音,请确保在 Info.plist 文件中添加 NSMicrophoneUsageDescription 键,并提供一段说明,解释应用为何需要访问麦克风。例如:

<key>NSMicrophoneUsageDescription</key>
<string>Example uses your microphone to record voice for test.</string>

对于 macOS,还需要在 DebugProfile.entitlementsReleaseProfile.entitlements 文件中添加以下权限:

<key>com.apple.security.device.microphone</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>

更多关于Flutter音频播放插件ogg_opus_player的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter音频播放插件ogg_opus_player的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用ogg_opus_player插件进行音频播放的代码示例。这个插件专门用于播放OGG Opus格式的音频文件。

首先,确保你已经在pubspec.yaml文件中添加了ogg_opus_player依赖:

dependencies:
  flutter:
    sdk: flutter
  ogg_opus_player: ^最新版本号  # 替换为实际的最新版本号

然后,运行flutter pub get来安装依赖。

接下来,创建一个Flutter应用并实现音频播放功能。以下是一个简单的示例:

main.dart

import 'package:flutter/material.dart';
import 'package:ogg_opus_player/ogg_opus_player.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Ogg Opus Player Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  OggOpusPlayer? _player;
  bool _isPlaying = false;

  @override
  void initState() {
    super.initState();
    _initPlayer();
  }

  @override
  void dispose() {
    _player?.dispose();
    super.dispose();
  }

  Future<void> _initPlayer() async {
    // 替换为你的OGG Opus音频文件路径
    String audioUrl = 'assets/audio/sample.opus';  // 如果是本地文件,确保在pubspec.yaml中声明
    // 如果是网络URL,则直接使用URL字符串
    // String audioUrl = 'https://example.com/audio/sample.opus';

    _player = OggOpusPlayer();
    _player!.setDataSource(audioUrl);
    _player!.prepare();
  }

  void _playPause() async {
    if (_isPlaying) {
      await _player!.pause();
    } else {
      await _player!.start();
    }
    setState(() {
      _isPlaying = !_isPlaying;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Ogg Opus Player Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '$_isPlaying',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _playPause,
              child: Text(_isPlaying ? 'Pause' : 'Play'),
            ),
          ],
        ),
      ),
    );
  }
}

pubspec.yaml (部分)

如果你使用的是本地音频文件,确保在pubspec.yaml中声明资源:

flutter:
  assets:
    - assets/audio/sample.opus

注意事项

  1. 音频文件路径:确保音频文件路径正确。如果是本地文件,路径应与pubspec.yaml中声明的路径一致。如果是网络URL,直接使用URL字符串。
  2. 错误处理:在实际应用中,你可能需要添加错误处理逻辑,例如处理音频文件加载失败或播放错误。
  3. 生命周期管理:在dispose方法中释放_player资源,以避免内存泄漏。

这个示例展示了如何使用ogg_opus_player插件在Flutter应用中播放OGG Opus格式的音频文件。你可以根据需要进一步扩展和自定义这个示例。

回到顶部