media_player_plugin 0.0.1 copy "media_player_plugin: ^0.0.1" to clipboard
media_player_plugin: ^0.0.1 copied to clipboard

media_player_plugin

media_player_plugin #

一个支持 ios/android 音频、视频播放的 flutter 插件,支持普通的网络流、dash、hls

安装 #

dependencies:
  media_player_plugin: ^0.0.1

开发与测试 #

运行 example 请使用 fvm

平台 开发语言 组件库 开发环境 测试机
android kotlin media3/exoplay android Studio 2024.2.2 模拟器 android12
ios Switf AVAudioEngine
AVAudioPlayerNode
VideoPlayer
xcode 14 se2 ios 13.6

效果展示 #

  • IOS

  • Android

Audio 使用 #

  • 创建播放实例

    import 'package:media_player_plugin/media_player_plugin.dart';
    
    const frequencys = [31.0,62.0,125.0,250.0,500.0,1000.0,2000.0,4000.0,8000.0,16000.0];
    
    final controller = MediaAudioPlayer(frequencys: frequencys);
    
    await controller.init();
    
    
  • 销毁

    controller.destroy();
    
  • 播放设置

    
    await controller.init();
    await controller.setUrl({
          //   file://xxx , asset://xxx , http://xxx
          required String url,
          bool? enableIosHttpRange,
          Map<String, String>? urlHeader,
          String? title,
          String? artist,
          // file://xxx , asset://xxx , http://xxx
          String? cover,
          Map<String, String>? coverHeader,
          bool? enableIosHttpRange,
      });
    await controller.prepare();
    

    enableIosHttpRange 只对 ios 的音频资源起作用,主要用于开启是否 http range 来快速 seek , 否则渐进式加载资源到 seek 的位置,考虑到兼容问题,不建议开启,测试正常支持 http range 的格式 flac\mp3\wav

  • 控制

    
    await controller.play();
    
    await controller.pause();
    
    // second 秒
    await controller.seek(2);
    
    // replay
    await controller.seek(0);
    await controller.play();
    
    
  • 播放事件监听

    await controller.init();
    
    List<StreamSubscription> _removes = [];
    
    _removes.add(
      controller.playStateStream!.listen((data) {
        setState(() {
            isPlaying = data.isPlaying;
            isBuffering = data.isBuffering;
            isPlayCompleted = data.isPlayCompleted;
        });
      }),
    );
    
    _removes.add(
      controller.durationStream!.listen((data) {
    
        setState(() {
            position = Duration(milliseconds: (data.position * 1000).round());
            duration = Duration(milliseconds: (data.duration * 1000).round());
            bufferd = Duration(milliseconds: (data.cacheDuration * 1000).round());
        });
    
      }),
    );
    
    
    @override
    dispose() {
      super.dispose();
      for (var item in _removes) {
        item.cancel();
      }
    }
    
  • 均衡器

    await controller.init();
    
    // 开启
    await controller.enableEq();
    
    // 关闭
    await  controller.disableEq();
    
    // 均衡器信息
    await controller.getEqBands();
    
    // 设置均衡器
    await updateBand(
        int index, {
        double? frequency,
        double? gain,
        // bypass为true时 跳过
        bool? bypass,
    })
    
    
  • 音频可视化 FFT

    
    // 开启
    await controller.enableFFT();
    
    // 关闭
    await controller.disableFFT();
    
    // 监听
    var _subscription = controller.fftStream!.listen((data) {
    
        var lefts = data.lefts;
        var rights = data.rights;
    
        print([lefts,rights])
        });
        _subscription.cancle();
    
    

    controller.fftStream 每 100ms 回调一次返回 audio pcm 长度为 sampleRate/10 也就是 4800 或 4410, pcm 范围为[-1,1],需要将其转为 rms, 可参考 FFtView 组件

  • 后台播放通知

    // 开启后台播放通知
    await controller.enablePlayback();
    
    // 关闭后台播放通知
    await controller.disablePlayback();
    
    // 监听
    controller.registerPlayBackEvent({
        required Function() onPlayBackPrevious,
        required Function() onPlayBackNext,
    })
    
    // 移除
    controller.removePlayBackEvent();
    
    

    enablePlayback 时,如果其他实例有开启则会被关闭,需要自己处理歌曲切换逻辑。 setUrl 如果没提供 title、cover 这些信息时会尝试读取歌曲提供的信息

  • 可访问属性

    position
    duration
    cacheDuration
    isPlaying
    isBuffering
    isPlayCompleted
    isPlaybackActive
    isFFTActive
    isEqActive
    
  • 效果视图组件

    FFtView 音频可视化

      FFtView(
          controller: controller,
          width: 340,
          indicatorWidth: 2,
          indicatorHeight: 60,
          spaceWidth: 1,
      )
    

    均衡器设置组件

     IconButton(
          onPressed: () async {
              var bands = await controller.getEqBands();
              EqualizerView.show(
                  context: context,
                  bands: bands,
                  height: 300,
                  enableEq: controller.isEqActive,
                  onEqSwitchChanged: (value) {
                      if (value) {
                          controller.enableEq();
                      } else {
                          controller.disableEq();
                      }
                  },
                  onChanged: (index, value) {
                      controller.updateBand(index, gain: value);
                  },
                  onReset: () {
                      for (var i = 0; i < bands.length; i++) {
                           controller.updateBand(i, gain: 0);
                      }
                  },
                  );
              },
              icon: Icon(Icons.equalizer_rounded),
          ),
    

Video 使用 #

  • 创建播放实例

    import 'package:media_player_plugin/media_player_plugin.dart';
    
    final controller = MediaVideoPlayer();
    
    await controller.init();
    
    
  • 销毁

    controller.destroy();
    
  • 播放设置

    
    await controller.init();
    await controller.setUrl({
      //   file://xxx , asset://xxx , http://xxx
      required String url,
      bool? enableIosHttpRange,
      Map<String, String>? urlHeader,
      String? title,
      String? artist,
      // file://xxx , asset://xxx , http://xxx
      String? cover,
      Map<String, String>? coverHeader,
    });
    await controller.prepare();
    
  • 播放视图

      SizedBox(
          width: double.infinity,
          child: MediaVideoView(controller: controller,fit: BoxFit.contain),
      )
    
  • 播放控件

    MediaVideoControls(
          controller: controller,
          child: SizedBox(
              width: double.infinity,
              child: MediaVideoView(controller: controller,fit: BoxFit.contain),
          ),
          onFullScreen: () {
             // 参考example
          },
      ),
    
  • 控制

    
    await controller.play();
    
    await controller.pause();
    
    // second 秒
    await controller.seek(2);
    
    // replay
    await controller.seek(0);
    await controller.play();
    
    
  • 播放事件监听

    await controller.init();
    
    List<StreamSubscription> _removes = [];
    
    _removes.add(
        controller.playStateStream!.listen((data) {
    
          setState(() {
            isPlaying = data.isPlaying;
            isBuffering = data.isBuffering;
            isPlayCompleted = data.isPlayCompleted;
          });
    
        }),
    );
    
    _removes.add(
        controller.durationStream!.listen((data) {
    
          setState(() {
            position = Duration(milliseconds: (data.position * 1000).round());
            duration = Duration(milliseconds: (data.duration * 1000).round());
            bufferd = Duration(milliseconds: (data.cacheDuration * 1000).round());
          });
    
        }),
    );
    
    
    @override
    dispose() {
        super.dispose();
        for (var item in _removes) {
          item.cancel();
        }
    }
    
  • 读取设置轨道资源

      // 读取视频轨道
     var video = await controller.readVideoTrack()
    
     // 设置视频轨道
     await controller.selectVideoTrack("1080p")
    
     // 读取音频轨道
     var audio = await = controller.readAudioTrack();
    
     // 设置音频轨道
     await controller.selectAudioTrack("audio_id");
    
  • 事件监听

    获取播放视频的宽高

    
    var off = controller.registerPresentationSize((
      width,
      height,
    ) {
      setState(() {
        videoWidth = width;
        videoHeight = height;
      });
    });
    
    // 注销
    off()
    

    视频是否可播放

    var off = controller.registerReadyToPlay((){
    
    });
    
    // 注销
    off()
    
  • 后台播放通知

    // 开启后台播放通知
    await controller.enablePlayback();
    
    // 关闭后台播放通知
    await controller.disablePlayback();
    
    // 监听
    controller.registerPlayBackEvent({
        required Function() onPlayBackPrevious,
        required Function() onPlayBackNext,
    })
    
    // 移除
    controller.removePlayBackEvent();
    
    
  • 可访问属性

    width
    height
    position
    duration
    cacheDuration
    isPlaying
    isBuffering
    isPlayCompleted
    isPlaybackActive
    

后期计划 #

  • 提供字幕选择
  • debug 日志输出
  • 提供缓存接口