Flutter视频缩略图生成插件fc_native_video_thumbnail的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter视频缩略图生成插件fc_native_video_thumbnail的使用

插件简介

fc_native_video_thumbnail 是一个用于通过原生API创建视频缩略图的Flutter插件。该插件支持iOS、Android、macOS和Windows平台,可以方便地从视频文件中提取缩略图,并保存为指定路径下的图片文件。

支持的平台与特性

平台 Path Uri
iOS
Android
macOS
Windows -

使用方法

以下是fc_native_video_thumbnail的基本用法示例:

final plugin = FcNativeVideoThumbnail();

try {
  /// 从[srcFile]获取缩略图,并根据给定选项保存到[destFile]。
  ///
  /// [srcFile]:源视频路径或Uri(参见[srcFileUri])。
  /// [srcFileUri]:如果为true,则[srcFile]是一个Uri(仅限Android/iOS/macOS)。
  /// [destFile]:目标缩略图路径。
  /// [width]/[height]:目标缩略图的最大尺寸。
  /// Windows不支持非正方形缩略图图像,仅使用[width],结果为[width]x[width]的最大缩略图。
  /// [format]:仅支持"jpeg"格式,默认为"jpeg"。
  /// [quality]:缩略图图像的质量(0-100),可能被平台忽略。
  ///
  /// 如果成功创建缩略图返回true;如果无法获取缩略图返回false。
  /// 如果在生成缩略图时发生错误则抛出异常。
  final thumbnailGenerated = await plugin.getVideoThumbnail(
      srcFile: 'path/to/video.mp4',
      destFile: 'path/to/thumbnail.jpg',
      width: 300,
      height: 300,
      format: 'jpeg',
      quality: 90);
} catch (err) {
  // 处理平台错误。
}

完整示例代码

下面是一个完整的示例应用程序,演示了如何使用fc_native_video_thumbnail插件来选择视频并生成缩略图:

import 'dart:io';
import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart';
import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:async';
import 'package:tmp_path/tmp_path.dart';
import 'package:path/path.dart' as p;

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHome(),
    );
  }
}

class Task {
  final String name;
  final String srcFile;
  final int width;
  final int height;
  final bool? isSrcUri;

  String? destFile;
  String? destImgSize;
  String? error;

  Task({
    required this.name,
    required this.srcFile,
    required this.width,
    required this.height,
    this.isSrcUri,
  });

  Future<void> run() async {
    try {
      var plugin = FcNativeVideoThumbnail();
      final destFile = tmpPath() + p.extension(srcFile);
      await plugin.getVideoThumbnail(
          srcFile: srcFile,
          destFile: destFile,
          width: width,
          height: height,
          srcFileUri: isSrcUri,
          format: 'jpeg');
      if (await File(destFile).exists()) {
        var imageFile = File(destFile);
        var decodedImage =
            await decodeImageFromList(imageFile.readAsBytesSync());
        destImgSize =
            'Decoded size: ${decodedImage.width}x${decodedImage.height}';
        this.destFile = destFile;
      } else {
        error = 'No thumbnail extracted';
      }
    } catch (err) {
      error = err.toString();
    }
  }
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  final _tasks = <Task>[];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Plugin example app'),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              if (Platform.isAndroid || Platform.isIOS) ...[
                ElevatedButton(
                  onPressed: () => _selectVideo(true),
                  child: const Text('Select video from gallery'),
                ),
                const SizedBox(height: 8.0),
              ],
              ElevatedButton(
                onPressed: () => _selectVideo(false),
                child: const Text('Select video from file'),
              ),
              const SizedBox(height: 8.0),
              ..._tasks.map((task) {
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const SizedBox(height: 8.0),
                    Text('>>> ${task.name}',
                        style: const TextStyle(
                            fontSize: 16.0, fontWeight: FontWeight.bold)),
                    if (task.error != null) ...[
                      const SizedBox(height: 8.0),
                      Text(task.error!,
                          style: const TextStyle(color: Colors.red)),
                    ],
                    if (task.destFile != null) ...[
                      const SizedBox(height: 8.0),
                      Text('Dest image: ${task.destFile}'),
                      const SizedBox(height: 8.0),
                      Text(task.destImgSize ?? ''),
                      const SizedBox(height: 8.0),
                      Image(image: FileImage(File(task.destFile!))),
                    ],
                  ],
                );
              }),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _selectVideo(bool gallery) async {
    try {
      String? srcPath;
      bool srcUri = false;

      if (!gallery) {
        var src = await openFile();
        if (src == null) {
          return;
        }
        srcPath = src.path;
      } else {
        final picker = ImagePicker();
        final xfile = await picker.pickVideo(source: ImageSource.gallery);
        if (xfile == null) {
          return;
        }
        srcPath = xfile.path;
      }

      setState(() {
        _tasks.clear();
      });

      final smallVidBytes = await rootBundle.load('res/a.mp4');
      final smallVidPath = '${tmpPath()}_small.mp4';
      await File(smallVidPath).writeAsBytes(smallVidBytes.buffer.asUint8List());

      _tasks.add(Task(
          name: 'Resize to 300x300',
          srcFile: srcPath,
          isSrcUri: srcUri,
          width: 300,
          height: 300));

      // Upscaling task.
      _tasks.add(Task(
          name: 'No upscaling to 1000x1000',
          srcFile: smallVidPath,
          width: 1000,
          height: 1000));

      await Future.forEach(_tasks, (Task task) async {
        await task.run();
        setState(() {});
      });
    } catch (err) {
      if (!mounted) {
        return;
      }
      await _showErrorAlert(context, err.toString());
    }
  }

  Future<void> _showErrorAlert(BuildContext context, String msg) async {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        title: const SelectableText('Error'),
        content: SelectableText(msg),
        actions: <Widget>[
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }
}

此示例展示了如何使用fc_native_video_thumbnail插件从用户选择的视频文件中生成缩略图,并在界面上显示生成的缩略图。同时,它还处理了不同平台上的特定情况,如iOS和Android上从相册选择视频,以及Windows平台上不支持Uri的情况。

希望这个详细的说明和示例能够帮助你更好地理解和使用fc_native_video_thumbnail插件。如果你有任何问题或需要进一步的帮助,请随时提问!


更多关于Flutter视频缩略图生成插件fc_native_video_thumbnail的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频缩略图生成插件fc_native_video_thumbnail的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用fc_native_video_thumbnail插件来生成视频缩略图的示例代码。这个插件允许你从本地视频文件中生成高质量的缩略图。

首先,你需要在你的Flutter项目中添加这个插件。在你的pubspec.yaml文件中添加以下依赖项:

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

然后运行flutter pub get来安装这个插件。

接下来,在你的Flutter项目中的Dart文件(例如main.dart)中,你可以按照以下步骤使用fc_native_video_thumbnail插件:

import 'package:flutter/material.dart';
import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart';
import 'package:path_provider/path_provider.dart';

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

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

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

class _MyHomePageState extends State<MyHomePage> {
  String? thumbnailPath;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Thumbnail Generator'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            thumbnailPath == null
                ? Text('Generating Thumbnail...')
                : Image.file(File(thumbnailPath!)),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _generateThumbnail,
              child: Text('Generate Thumbnail'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _generateThumbnail() async {
    // 获取临时目录路径
    final Directory tempDir = await getTemporaryDirectory();
    final String videoPath = 'path/to/your/video.mp4'; // 替换为你的视频文件路径
    final String thumbnailFilePath = '${tempDir.path}/thumbnail.jpg';

    try {
      // 生成缩略图
      await FcNativeVideoThumbnail.getThumbnail(
        videoPath: videoPath,
        imagePath: thumbnailFilePath,
        atSecond: 1.0, // 你可以指定从视频的哪一秒生成缩略图,这里设置为1秒
        quality: 100, // 质量,范围从0到100
      );

      // 更新状态以显示缩略图
      setState(() {
        thumbnailPath = thumbnailFilePath;
      });
    } catch (e) {
      print('Error generating thumbnail: $e');
    }
  }
}

说明:

  1. 添加依赖:首先,你需要在pubspec.yaml文件中添加fc_native_video_thumbnail依赖。
  2. 获取临时目录:使用path_provider插件来获取设备的临时目录路径,以便保存生成的缩略图。
  3. 生成缩略图:调用FcNativeVideoThumbnail.getThumbnail方法,传入视频路径、缩略图保存路径、生成缩略图的时间点(秒)以及质量(0-100)。
  4. 显示缩略图:如果缩略图生成成功,使用Image.file来显示生成的缩略图。

请确保替换示例中的videoPath为你的实际视频文件路径。这个示例代码展示了如何从视频文件中生成缩略图并在Flutter应用中显示它。

回到顶部