Flutter视频帧导出插件export_video_frame的使用
Flutter视频帧导出插件export_video_frame
的使用
export_video_frame
是一个用于iOS和Android平台从视频文件中导出图片的Flutter插件。本文将介绍如何安装、配置以及使用这个插件,并提供一个完整的示例Demo。
安装
首先,在你的pubspec.yaml
文件中添加export_video_frame
作为依赖项:
dependencies:
export_video_frame: ^latest_version
请确保将^latest_version
替换为实际的最新版本号。
使用
导入包
在开始之前,你需要导入必要的包:
import 'package:export_video_frame/export_video_frame.dart';
import 'package:image_picker/image_picker.dart';
示例代码
下面是一个完整的示例Demo,展示了如何使用export_video_frame
来导出视频帧并显示在UI上。
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:export_video_frame/export_video_frame.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Plugin Example App",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(images: <Image>[]),
);
}
}
class ImageItem extends StatelessWidget {
ImageItem({required this.image}) : super(key: ObjectKey(image));
final Image image;
@override
Widget build(BuildContext context) {
return Container(child: image);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.images}) : super(key: key);
final List<Image> images;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _isClean = false;
final ImagePicker _picker = ImagePicker();
Stream<File>? _imagesStream;
Future _getImagesByTime() async {
final PickedFile? file = await _picker.getVideo(
source: ImageSource.gallery, maxDuration: const Duration(seconds: 10));
_imagesStream = ExportVideoFrame.exportImagesFromFile(
File(file!.path),
const Duration(milliseconds: 500),
pi / 2,
);
setState(() {
_isClean = true;
});
_imagesStream!.listen((image) {
setState(() {
widget.images.add(Image.file(image));
});
});
}
Future _getImages() async {
final PickedFile? file = await _picker.getVideo(
source: ImageSource.gallery, maxDuration: const Duration(seconds: 10));
var images = await ExportVideoFrame.exportImage(file!.path, 10, 0);
var result = images.map((file) => Image.file(file)).toList();
setState(() {
widget.images.addAll(result);
_isClean = true;
});
}
Future _getGifImages() async {
final PickedFile? file =
await _picker.getImage(source: ImageSource.gallery);
var images = await ExportVideoFrame.exportGifImage(file!.path, 0);
var result = images.map((file) => Image.file(file)).toList();
setState(() {
widget.images.addAll(result);
_isClean = true;
});
}
Future _getImagesByDuration() async {
final PickedFile? pickedFile =
await _picker.getImage(source: ImageSource.gallery);
final File file = File(pickedFile!.path);
var duration = Duration(seconds: 1);
var image =
await ExportVideoFrame.exportImageBySeconds(file, duration, pi / 2);
setState(() {
widget.images.add(Image.file(image));
_isClean = true;
});
await ExportVideoFrame.saveImage(image, "Video Export Demo",
waterMark: "images/water_mark.png",
alignment: Alignment.bottomLeft,
scale: 2.0);
}
Future _cleanCache() async {
var result = await ExportVideoFrame.cleanImageCache();
print(result);
setState(() {
widget.images.clear();
_isClean = false;
});
}
Future _handleClickFirst() async {
if (_isClean) {
await _cleanCache();
} else {
await _getImages();
}
}
Future _handleClickSecond() async {
if (_isClean) {
await _cleanCache();
} else {
await _getImagesByDuration();
}
}
Future _handleClickThird() async {
if (_isClean) {
await _cleanCache();
} else {
await _getGifImages();
}
}
Future _handleClickFourth() async {
if (_isClean) {
await _cleanCache();
} else {
await _getImagesByTime();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Export Image"),
),
body: Container(
padding: EdgeInsets.zero,
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: GridView.extent(
maxCrossAxisExtent: 400,
childAspectRatio: 1.0,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: widget.images.length > 0
? widget.images
.map((image) => ImageItem(image: image))
.toList()
: [Container()]),
),
Expanded(
flex: 0,
child: Center(
child: MaterialButton(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
height: 40,
minWidth: 100,
onPressed: () {
_handleClickFirst();
},
color: Colors.orange,
child: Text(_isClean ? "Clean" : "Export image list"),
),
),
),
Expanded(
flex: 0,
child: Center(
child: MaterialButton(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
height: 40,
minWidth: 150,
onPressed: () {
_handleClickSecond();
},
color: Colors.orange,
child: Text(_isClean ? "Clean" : "Export one image and save"),
),
),
),
Expanded(
flex: 0,
child: Center(
child: MaterialButton(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
height: 40,
minWidth: 150,
onPressed: () {
_handleClickThird();
},
color: Colors.orange,
child: Text(_isClean ? "Clean" : "Export gif image"),
),
),
),
Expanded(
flex: 0,
child: Center(
child: MaterialButton(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
height: 40,
minWidth: 150,
onPressed: () {
_handleClickFourth();
},
color: Colors.orange,
child: Text(_isClean ? "Clean" : "Export images by interval"),
),
),
),
],
),
),
);
}
}
iOS配置
如果你在编译iOS应用时遇到错误,请编辑ios/Podfile
文件如下:
target 'Runner' do
use_frameworks! # <-- add this
...
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['SWIFT_VERSION'] = '4.2' # <-- add this
end
end
end
如果你使用了保存相册的功能,还需要在ios/Runner/Info.plist
中添加以下键值:
<key>NSPhotoLibraryUsageDescription</key>
<string>Use Ablum For your purpose</string>
Android配置
确保你在AndroidManifest.xml
中添加了所需的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
更多关于Flutter视频帧导出插件export_video_frame的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter视频帧导出插件export_video_frame的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用export_video_frame
插件来导出视频帧的示例代码。这个插件允许你从视频文件中提取帧并保存为图像文件。
首先,确保你已经在pubspec.yaml
文件中添加了export_video_frame
依赖:
dependencies:
flutter:
sdk: flutter
export_video_frame: ^最新版本号 # 请替换为实际的最新版本号
然后,运行flutter pub get
来获取依赖。
接下来,以下是一个完整的示例代码,展示如何使用export_video_frame
插件来导出视频帧:
import 'package:flutter/material.dart';
import 'package:export_video_frame/export_video_frame.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: VideoFrameExporterPage(),
);
}
}
class VideoFrameExporterPage extends StatefulWidget {
@override
_VideoFrameExporterPageState createState() => _VideoFrameExporterPageState();
}
class _VideoFrameExporterPageState extends State<VideoFrameExporterPage> {
String? _outputDirectory;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Video Frame Exporter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: _exportVideoFrame,
child: Text('Export Video Frame'),
),
Text(_outputDirectory ?? 'No frames exported yet.'),
],
),
),
);
}
Future<void> _exportVideoFrame() async {
// 指定视频文件路径(这里使用本地资源路径,实际应用中可以是任何有效的视频文件路径)
final videoPath = 'assets/sample_video.mp4'; // 请确保在pubspec.yaml中声明了assets
// 或者使用设备上的文件路径,例如:
// final videoPath = '/storage/emulated/0/DCIM/Camera/sample_video.mp4';
// 获取应用文档目录路径
final directory = await getApplicationDocumentsDirectory();
final outputPath = directory.path;
setState(() {
_outputDirectory = outputPath;
});
// 创建导出器实例
final exporter = VideoFrameExporter();
try {
// 导出第10秒(10000毫秒)的视频帧
final frame = await exporter.exportFrameAtTime(
videoPath: videoPath,
time: Duration(seconds: 10),
outputPath: '$outputPath/frame_${DateTime.now().millisecondsSinceEpoch}.png',
);
// 输出帧文件路径
print('Exported frame to: $frame');
setState(() {
_outputDirectory = 'Frame exported to: $frame';
});
} catch (e) {
print('Error exporting frame: $e');
setState(() {
_outputDirectory = 'Error: $e';
});
}
}
}
注意事项:
- 视频文件路径:在示例中,视频路径被硬编码为
assets/sample_video.mp4
。如果你使用设备上的视频文件,请确保你有文件的访问权限,并修改路径为实际文件路径。 - 权限问题:如果视频文件位于设备的存储上,你可能需要在
AndroidManifest.xml
中请求存储权限,并在运行时请求权限。 - assets配置:如果你使用Flutter assets中的视频文件,请确保在
pubspec.yaml
中正确声明了assets。
flutter:
assets:
- assets/sample_video.mp4
-
错误处理:示例中包含了基本的错误处理,但在实际应用中,你可能需要更详细的错误处理逻辑。
-
依赖:示例代码使用了
path_provider
插件来获取应用的文档目录路径。如果你还没有添加这个依赖,请确保在pubspec.yaml
中添加它。
dependencies:
path_provider: ^最新版本号 # 请替换为实际的最新版本号
这个示例展示了如何使用export_video_frame
插件从视频中导出帧,并将其保存到应用的文档目录中。希望这对你有所帮助!