Flutter视频制作插件my_maker_video的使用

Flutter视频制作插件my_maker_video的使用

轻松地从一系列图像创建视频,添加精确位置的水印到视频中,降低视频质量以优化文件大小,并将视频转换为高质量的GIF。MyMakerVideo插件为您的Flutter项目提供了强大的、快速且可定制的功能。

安装

pubspec.yaml文件中添加以下依赖:

dependencies:
  my_maker_video: ^latest_version

然后运行以下命令:

flutter pub get

Android 配置

android/app/build.gradle文件中添加以下配置:

defaultConfig {
    applicationId = "com.example.my_maker_video_example"
    // 更新这些值以满足您的应用需求。
    minSdk = 24
    targetSdk = flutter.targetSdkVersion
    versionCode = flutter.versionCode
    versionName = flutter.versionName
}

AndroidManifest.xml文件中添加以下权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

iOS 配置

Info.plist文件中添加以下键:

<key>NSPhotoLibraryUsageDescription</key>
<string>我们需要访问您的照片库来选择文件。</string>
<key>NSDocumentDirectoryUsageDescription</key>
<string>我们需要访问您的文档来选择文件。</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

确保在ios/Podfile文件中设置平台版本为:

platform :ios, '13.0'

使用

导入插件包:

import 'package:my_maker_video/my_maker_video.dart';

权限

确保您的应用具有所需的写入权限。

功能

1. 从一系列图像创建视频

确保path/to/images目录包含按自然数命名的.png格式的图像,如示例所示:

final result = MyMakerVideo.ffmpegKit.convertImageDirectoryToVideo(
    imagesPath: "path/to/images",
    outputVideoPath: "path/to/videoOutput.mp4",
    // fps: 2
);

注意: 如果视频较大,请允许一些时间进行处理。

2. 向视频添加水印

final result = MyMakerVideo.ffmpegKit.addWatermarkToVideo(
    watermarkPath: watermarkPath!, // 水印图像或视频的路径
    videoPath: videoPath!,         // 要添加水印的视频路径
    outputPath: pathVideo,         // 输出视频保存路径
    x: 20,                         // X坐标(左上角原点)
    y: 30,                         // Y坐标
    width: 200,                    // 水印宽度
    height: 200                    // 水印高度
);

3. 降低视频质量

final result = MyMakerVideo.ffmpegKit.reduceVideoQualityByPercentage(
    inputPath: videoPath!,        // 输入视频路径
    outputPath: pathVideo,        // 输出视频保存路径
    qualityPercentage: 30         // 降低的质量百分比
);

4. 从视频创建GIF

final result = MyMakerVideo.ffmpegKit.createGifFromVideo(
    inputPath: videoPath!,        // 输入视频路径
    outputPath: pathGif,          // 输出GIF保存路径
    quality: 100,                 // GIF质量
    scale: 3200,                  // GIF缩放比例
    fps: 2,                       // 每秒帧数
);

完整示例代码

import 'dart:io';
import 'dart:math';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:my_maker_video/my_maker_video.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件my_maker_video示例应用'),
        ),
        body: Center(
          child: SingleChildScrollView(
            child: Column(
              children: [
                ImagesToVideo(),
                Watermark(),
                ReduceVideoQuality(),
                VideoToGif(),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  State<ImagesToVideo> createState() => _ImagesToVideoState();
}

class _ImagesToVideoState extends State<ImagesToVideo> {
  String? inputPath;
  String? outputPath;
  String? pathVideo;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "PART I | 图像转视频",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        Text("STEP 1 | 创建输入图像文件夹"),
        TextButton(
            onPressed: () async {
              final downloadPath = !kIsWeb && Platform.isAndroid
                  ? await createDirectory("/storage/emulated/0/Download/my_maker_video")
                  : await getApplicationDocumentsDirectory();
              inputPath = "${downloadPath.path}/input-image";
              await createDirectory(inputPath!);
              setState(() {});
            },
            child: Text("创建")),
        Text("STEP 2 | 将您的图像放入 $inputPath 文件夹中,文件名应为数字,例如 image.png"),
        SizedBox(height: 400, child: Image.asset("assets/image.jpeg")),
        Text("STEP 3 | 创建输出视频文件夹 $outputPath"),
        TextButton(
            onPressed: () async {
              final downloadPath = !kIsWeb && Platform.isAndroid
                  ? await createDirectory("/storage/emulated/0/Download/my_maker_video")
                  : await getApplicationDocumentsDirectory();
              outputPath = "${downloadPath.path}/video";
              await createDirectory(outputPath!);
              setState(() {});
            },
            child: Text("创建")),
        Text("STEP 4 | 允许保存视频的权限"),
        TextButton(
            onPressed: () async {
              await Permission.storage.request().isGranted;
              await Permission.photos.request().isGranted;
            },
            child: Text("允许")),
        Text("STEP 5 | 从图像列表创建视频 $pathVideo"),
        Text("注意:输出文件名必须唯一"),
        TextButton(
            onPressed: () async {
              if (outputPath != null && inputPath != null) {
                final pathVideo =
                    "$outputPath/image-to-video-${Random().nextInt(20)}.mp4";

                final result = MyMakerVideo.ffmpegKit.convertImageDirectoryToVideo(
                  imagesPath: inputPath!,
                  outputVideoPath: pathVideo,
                  // fps: 2
                );
                setState(() {
                  this.pathVideo = pathVideo;
                });
                print("Path | $pathVideo");
              }
            },
            child: Text("从图像创建视频")),
        Text("STEP 6 | 等待"),
      ],
    );
  }
}

Future<Directory> createDirectory(String path) async {
  print("Path | $path");
  return await Directory(path).create(recursive: true);
}

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

  [@override](/user/override)
  State<Watermark> createState() => _WatermarkState();
}

class _WatermarkState extends State<Watermark> {
  String? watermarkPath;
  String? videoPath;
  String? outputPath;
  String? pathVideo;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "PART II | 向视频添加水印",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        Text("STEP 1 | 选择视频 $videoPath"),
        TextButton(
            onPressed: () async {
              videoPath = await pickOneVideo();
              setState(() {});
            },
            child: Text("选择视频")),
        Text("STEP 2 | 选择水印(视频或图像) $watermarkPath"),
        TextButton(
            onPressed: () async {
              watermarkPath = await pickOneFile(allowedExtensions: ["mp4", "png"]);
              setState(() {});
            },
            child: Text("选择水印")),
        Text("STEP 3 | 创建输出视频文件夹 $outputPath"),
        TextButton(
            onPressed: () async {
              final downloadPath = !kIsWeb && Platform.isAndroid
                  ? await createDirectory("/storage/emulated/0/Download/my_maker_video")
                  : await getApplicationDocumentsDirectory();
              outputPath = "${downloadPath.path}/video";
              await createDirectory(outputPath!);
              setState(() {});
            },
            child: Text("创建")),
        Text("STEP 4 | 允许保存视频的权限"),
        TextButton(
            onPressed: () async {
              await Permission.storage.request().isGranted;
              await Permission.photos.request().isGranted;
            },
            child: Text("允许")),
        Text("STEP 5 | 创建带有水印的视频 $pathVideo"),
        TextButton(
            onPressed: () async {
              if (outputPath != null &&
                  watermarkPath != null &&
                  videoPath != null) {
                final pathVideo =
                    "$outputPath/watermark-${Random().nextInt(20)}.mp4";

                final result = MyMakerVideo.ffmpegKit.addWatermarkToVideo(
                    watermarkPath: watermarkPath!,
                    videoPath: videoPath!,
                    outputPath: pathVideo,
                    x: 20,
                    y: 30,
                    width: 200,
                    height: 200

                    // fps: 2
                    );
                setState(() {
                  this.pathVideo = pathVideo;
                });
                print("Path | $pathVideo");
              }
            },
            child: Text("创建带水印的视频")),
        Text("STEP 6 | 等待"),
      ],
    );
  }
}

Future<String?> pickOneFile({List<String>? allowedExtensions}) async {
  final FilePickerResult? result = await FilePicker.platform.pickFiles(
    type: allowedExtensions == null ? FileType.any : FileType.custom,
    allowedExtensions: allowedExtensions,
  );
  return result?.files.single.path!;
}

Future<String?> pickOneVideo() async {
  final FilePickerResult? result = await FilePicker.platform.pickFiles(
    type: FileType.video,
  );
  return result?.files.single.path!;
}

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

  [@override](/user/override)
  State<ReduceVideoQuality> createState() => _ReduceVideoQualityState();
}

class _ReduceVideoQualityState extends State<ReduceVideoQuality> {
  String? videoPath;
  String? outputPath;
  String? pathVideo;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "PART III | 降低视频质量",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        Text("STEP 1 | 选择视频 $videoPath"),
        TextButton(
            onPressed: () async {
              videoPath = await pickOneVideo();
              setState(() {});
            },
            child: Text("选择视频")),
        Text("STEP 2 | 创建输出视频文件夹 $outputPath"),
        TextButton(
            onPressed: () async {
              final downloadPath = !kIsWeb && Platform.isAndroid
                  ? await createDirectory("/storage/emulated/0/Download/my_maker_video")
                  : await getApplicationDocumentsDirectory();
              outputPath = "${downloadPath.path}/video";
              await createDirectory(outputPath!);
              setState(() {});
            },
            child: Text("创建")),
        Text("STEP 3 | 允许保存视频的权限"),
        TextButton(
            onPressed: () async {
              await Permission.storage.request().isGranted;
              await Permission.photos.request().isGranted;
            },
            child: Text("允许")),
        Text("STEP 4 | 降低视频质量 $pathVideo"),
        TextButton(
            onPressed: () async {
              if (outputPath != null && videoPath != null) {
                final pathVideo =
                    "$outputPath/reduce-quality-${Random().nextInt(20)}.mp4";

                final result = MyMakerVideo.ffmpegKit.reduceVideoQualityByPercentage(
                    inputPath: videoPath!,
                    outputPath: pathVideo,
                    qualityPercentage: 30);

                setState(() {
                  this.pathVideo = pathVideo;
                });
                print("Path | $pathVideo");
              }
            },
            child: Text("降低视频质量")),
        Text("STEP 5 | 等待"),
      ],
    );
  }
}

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

  [@override](/user/override)
  State<VideoToGif> createState() => _VideoToGifState();
}

class _VideoToGifState extends State<VideoToGif> {
  String? videoPath;
  String? outputPath;
  String? pathVideo;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          "PART IV | 视频转GIF",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        Text("STEP 1 | 选择视频 $videoPath"),
        TextButton(
            onPressed: () async {
              videoPath = await pickOneVideo();
              setState(() {});
            },
            child: Text("选择视频")),
        Text("STEP 2 | 创建输出视频文件夹 $outputPath"),
        TextButton(
            onPressed: () async {
              final downloadPath = !kIsWeb && Platform.isAndroid
                  ? await createDirectory("/storage/emulated/0/Download/my_maker_video")
                  : await getApplicationDocumentsDirectory();
              outputPath = "${downloadPath.path}/video";
              await createDirectory(outputPath!);
              setState(() {});
            },
            child: Text("创建")),
        Text("STEP 3 | 允许保存视频的权限"),
        TextButton(
            onPressed: () async {
              await Permission.storage.request().isGranted;
              await Permission.photos.request().isGranted;
            },
            child: Text("允许")),
        Text("STEP 4 | 从视频创建GIF $pathVideo"),
        TextButton(
            onPressed: () async {
              if (outputPath != null && videoPath != null) {
                final pathGif = "$outputPath/gif-${Random().nextInt(20)}.gif";

                final result = MyMakerVideo.ffmpegKit.createGifFromVideo(
                  inputPath: videoPath!,
                  outputPath: pathGif,
                  quality: 100,
                  scale: 3200,
                  fps: 2,
                );

                setState(() {
                  pathVideo = pathGif;
                });
                print("Path | $pathGif");
              }
            },
            child: Text("创建GIF")),
        Text("STEP 5 | 等待"),
      ],
    );
  }
}

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

1 回复

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


my_maker_video 是一个用于 Flutter 的视频制作插件,它允许开发者在应用中创建和编辑视频。以下是如何使用 my_maker_video 插件的基本步骤。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 my_maker_video 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  my_maker_video: ^1.0.0  # 请使用最新的版本号

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

2. 导入插件

在需要使用 my_maker_video 的 Dart 文件中导入插件:

import 'package:my_maker_video/my_maker_video.dart';

3. 初始化插件

在使用插件之前,通常需要对其进行初始化:

void initVideoMaker() async {
  await MyMakerVideo.initialize();
}

4. 创建视频

my_maker_video 提供了多种方法来创建和编辑视频。以下是一个简单的示例,展示如何将多张图片合并成一个视频:

void createVideo() async {
  List<String> imagePaths = [
    'assets/image1.jpg',
    'assets/image2.jpg',
    'assets/image3.jpg',
  ];

  String outputPath = '/storage/emulated/0/Download/output.mp4';

  await MyMakerVideo.createVideoFromImages(
    imagePaths: imagePaths,
    outputPath: outputPath,
    durationPerImage: 3,  // 每张图片显示3秒
    onProgress: (double progress) {
      print('视频制作进度: $progress');
    },
    onCompleted: (String filePath) {
      print('视频制作完成,保存路径: $filePath');
    },
    onError: (String error) {
      print('视频制作出错: $error');
    },
  );
}

5. 添加音频

你还可以为视频添加背景音乐:

void addAudioToVideo() async {
  String videoPath = '/storage/emulated/0/Download/output.mp4';
  String audioPath = 'assets/background_music.mp3';
  String outputPath = '/storage/emulated/0/Download/output_with_audio.mp4';

  await MyMakerVideo.addAudioToVideo(
    videoPath: videoPath,
    audioPath: audioPath,
    outputPath: outputPath,
    onCompleted: (String filePath) {
      print('音频添加完成,保存路径: $filePath');
    },
    onError: (String error) {
      print('音频添加出错: $error');
    },
  );
}
回到顶部