Flutter视频播放插件flutter_ffplay的使用

Flutter视频播放插件flutter_ffplay的使用

flutter_ffplay 是一个基于 ffmpeg 的视频播放器。

开始使用

此项目是一个使用 ffmpeg 的视频播放器。目前插件支持Android和Windows平台。欢迎在其他平台上引入并使用它。

编译ffmpeg

在使用此插件之前,你需要先编译 ffmpeg

对于Android,构建脚本会使用 ANDROID_NDK_HOME 来查找Android NDK。

对于Windows,你需要使用 msys2msvc,或者使用 mingw64 工具链在Linux上构建。

对于 msys2,你应该在调用 cxx/build.sh 之前设置 vcvarsall。例如:

set MSYS2_PATH_TYPE=inherit
call "D:\Apps\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
"D:\Apps\msys64\usr\bin\bash.exe" --login cxx/build.sh

基础使用

首先,你需要创建一个 IOHandler 的实例。这里提供了一个 http 协议的示例代码位于 example/lib/iohandler.dart

final ioHandler = HttpIOHandler();

接着,创建一个 Playback 实例来保存播放信息。可以传递一个 onFrame 回调来获取当前播放位置:

final playback = await Playback.create(onFrame: (pts) {
  setState(() {
    if (pts == null) {
      _isPlaying = false;
    } else {
      _isPlaying = true;
      _position = _isSeeking ? _position : pts;
    }
  });
});

Playback 实例有 textureIdaspectRatio 参数,用户可以用这些参数来创建 TextureView

AspectRatio(
  aspectRatio: playback.aspectRatio,
  child: Texture(textureId: playback.textureId),
)

然后,创建 FFMpegContext

final ctx = FFMpegContext(url, ioHandler, playback);

接下来,调用 getStream 方法来获取 FFMpegContext 的信息:

final streams = await ctx.getStreams();

最后,使用包含 FFMpegStream 列表的 play 方法来开始播放:

await ctx.play(streams);

集成到其他平台

除了播放部分,dartffmpeg 之间的交互通过 ffi 实现。要将此插件集成到其他平台,你需要使用你的平台代码编译 cxx/ffi.cpp,并将库路径添加到 ffi.dart。还需要实现 flutter_ffplay 方法通道来完成播放功能。

完整示例代码

以下是一个完整的示例代码,展示了如何使用 flutter_ffplay 插件:

import 'dart:math';

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

import 'iohandler.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final TextEditingController _controller = TextEditingController(
    text: 'http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8',
  );
  FFMpegContext? _ctx;
  Playback? _playback;
  final ioHandler = HttpIOHandler();

  bool _isPlaying = false;
  int _duration = 0;
  int _position = 0;
  bool _isSeeking = false;

  String parseHHMMSS(int pts) {
    final sec = pts ~/ AV_TIME_BASE;
    final min = sec ~/ 60;
    final hour = min ~/ 60;
    String ret = (min % 60).toString().padLeft(2, '0') +
        ':' +
        (sec % 60).toString().padLeft(2, '0');
    if (hour == 0) return ret;
    return '$hour:$ret';
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Material(
        type: MaterialType.canvas,
        child: SafeArea(
          child: Column(children: [
            Row(
              children: [
                const SizedBox(width: 8),
                Expanded(
                  child: TextField(
                    controller: _controller,
                  ),
                ),
                TextButton(
                  child: const Text("加载"),
                  onPressed: () async {
                    if (_ctx != null) {
                      final ctx = _ctx;
                      _ctx = null;
                      await ctx?.close();
                    }
                    final url = _controller.text;
                    final playback =
                        _playback ??= await Playback.create(onFrame: (pts) {
                      setState(() {
                        if (pts == null) {
                          _isPlaying = false;
                        } else {
                          _isPlaying = true;
                          _position = _isSeeking ? _position : pts;
                        }
                      });
                    });
                    final ctx = _ctx = FFMpegContext(
                      url,
                      ioHandler,
                      playback,
                    );
                    final streams = await ctx.getStreams();
                    _duration = await ctx.getDuration();
                    await ctx.play(streams);
                    setState(() {});
                  },
                ),
              ],
            ),
            Expanded(
                child: (_playback?.textureId ?? -1) != -1
                    ? Center(
                        child: AspectRatio(
                        aspectRatio: _playback!.aspectRatio,
                        child: Texture(textureId: _playback!.textureId),
                      ))
                    : const SizedBox()),
            Row(
              children: [
                IconButton(
                  icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
                  onPressed: () async {
                    _isPlaying ? _ctx?.pause() : _ctx?.resume();
                  },
                ),
                Expanded(
                  child: Slider(
                      value: max(
                          0, min(_position.toDouble(), _duration.toDouble())),
                      max: max(0, _duration.toDouble()),
                      onChanged: (pos) {
                        _isSeeking = true;
                        setState(() {
                          _position = pos.toInt();
                        });
                      },
                      onChangeEnd: (pos) async {
                        await _ctx?.seekTo(pos.toInt());
                        _isSeeking = false;
                      }),
                ),
                Text(_duration < 0
                    ? parseHHMMSS(_position)
                    : "${parseHHMMSS(_position)}/${parseHHMMSS(_duration)}"),
                const SizedBox(width: 8),
              ],
            ),
          ]),
        ),
      ),
    );
  }
}

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

1 回复

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


flutter_ffplay 是一个基于 FFmpeg 的 Flutter 视频播放插件,支持多种视频格式和协议。它使用了 FFmpeg 作为底层引擎,因此能够处理更多的视频格式和编解码器。以下是如何使用 flutter_ffplay 插件来播放视频的步骤。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 flutter_ffplay 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_ffplay: ^0.3.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 flutter_ffplay 插件:

import 'package:flutter_ffplay/flutter_ffplay.dart';

3. 初始化播放器

创建一个 FFPlayerController 实例,并初始化它。

FFPlayerController _controller = FFPlayerController();

@override
void initState() {
  super.initState();
  _controller.initialize().then((_) {
    setState(() {});
  });
}

4. 播放视频

使用 _controller 来播放视频。你可以通过 setDataSource 方法来设置视频源,然后调用 start 方法开始播放。

void _playVideo() async {
  await _controller.setDataSource(
    "https://www.example.com/path/to/your/video.mp4",
    isNetwork: true,
  );
  _controller.start();
}

5. 显示视频

使用 FFPlayer widget 来显示视频:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Flutter FFPlay Example'),
    ),
    body: Center(
      child: _controller.isInitialized
          ? FFPlayer(controller: _controller)
          : CircularProgressIndicator(),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _playVideo,
      child: Icon(Icons.play_arrow),
    ),
  );
}

6. 控制播放

你可以使用 _controller 来控制视频的播放、暂停、停止等操作:

void _pauseVideo() {
  _controller.pause();
}

void _stopVideo() {
  _controller.stop();
}

void _seekTo(int milliseconds) {
  _controller.seekTo(milliseconds);
}

7. 释放资源

在页面销毁时,记得释放播放器资源:

@override
void dispose() {
  _controller.dispose();
  super.dispose();
}

8. 处理事件

你可以监听播放器的事件,如播放完成、错误等:

_controller.onPlayerStateChanged.listen((state) {
  if (state == FFPlayerState.completed) {
    print("Playback completed");
  } else if (state == FFPlayerState.error) {
    print("Error occurred during playback");
  }
});
回到顶部