Flutter视频裁剪插件video_trimmer_pro的使用

Flutter视频裁剪插件video_trimmer_pro的使用

Awesome Flutter Pub Version GitHub stars GitHub license

Video Trimmer

一个用于裁剪视频的Flutter插件

功能 #

  • 可自定义的视频裁剪器。
  • 支持两种类型的裁剪预览器,固定长度和滚动。
  • 视频播放控制。
  • 获取并存储视频文件。

此外,还支持转换为GIF

注意:版本 3.0.0 及以上使用的是 "Full" 版本的 Flutter FFmpeg。要安装 "LTS" 版本,请使用此包的 "x.x.x-LTS" 版本。

下图展示了 TrimViewer 的结构。它包括顶部的 Duration(显示开始时间、结束时间和滑块时间),由缩略图组成的 TrimArea,以及允许你从视频中选择部分的 TrimEditor

示例 #

在iPhone 13 Pro设备上运行的示例应用:

Trimmer

使用 #

video_trimmer 添加到您的 <strong>pubspec.yaml</strong> 文件中:

对于使用FFmpeg主版本:

dependencies:
  video_trimmer: ^3.0.0

对于使用FFmpeg LTS版本:

dependencies:
  video_trimmer: ^3.0.0-LTS

Android配置 #

无需额外配置即可在Android平台上使用。您可以直接使用!

iOS配置 #

  • 将以下键添加到您的 <strong>Info.plist</strong> 文件中,位于 <project root>/ios/Runner/Info.plist
<key>NSCameraUsageDescription</key>
<string>Used to demonstrate image picker plugin</string>
<key>NSMicrophoneUsageDescription</key>
<string>Used to capture audio for image picker plugin</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Used to demonstrate image picker plugin</string>

FFmpeg发布 #

该插件支持FFmpeg的主版本和 LTS版本

主发布版 LTS发布版
Android API级别 24 16
Android相机访问 -
Android架构 arm-v7a-neon
arm64-v8a
x86
x86-64
arm-v7a
arm-v7a-neon
arm64-v8a
x86
x86-64
iOS最小SDK版本 12.1 10
iOS架构 arm64
arm64-simulator
arm64-mac-catalyst
x86-64
x86-64-mac-catalyst
armv7
arm64
i386
x86-64

功能 #

加载输入视频文件 #

final Trimmer _trimmer = Trimmer();
await _trimmer.loadVideo(videoFile: file);

保存裁剪后的视频 #

返回一个字符串以指示保存操作是否成功。

await _trimmer
    .saveTrimmedVideo(startValue: _startValue, endValue: _endValue)
    .then((value) {
  setState(() {
    _value = value;
  });
});

视频播放状态 #

返回视频播放状态。如果为 true 则视频正在播放,否则已暂停。

await _trimmer.videoPlaybackControl(
  startValue: _startValue,
  endValue: _endValue,
);

高级命令 #

如果您需要更多定制化功能,可以使用高级的 FFmpeg 命令。只需使用 ffmpegCommand 属性定义您的FFmpeg命令,并使用 customVideoFormat 设置输出视频格式。

请参阅 官方FFmpeg文档 获取更多信息。

注意:customVideoFormat 属性传递错误的视频格式可能会导致崩溃。

// 自定义命令示例

// 这个默认用于创建GIF,所以您不需要使用它。

await _trimmer
    .saveTrimmedVideo(
        startValue: _startValue,
        endValue: _endValue,
        ffmpegCommand:
            '-vf "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0',
        customVideoFormat: '.gif')
    .then((value) {
  setState(() {
    _value = value;
  });
});

小部件 #

显示视频播放区域 #

VideoViewer(trimmer: _trimmer)

显示视频裁剪区域 #

TrimViewer(
  trimmer: _trimmer,
  viewerHeight: 50.0,
  viewerWidth: MediaQuery.of(context).size.width,
  maxVideoLength: const Duration(seconds: 10),
  onChangeStart: (value) => _startValue = value,
  onChangeEnd: (value) => _endValue = value,
  onChangePlaybackState: (value) =>
      setState(() => _isPlaying = value),
)

示例 #

在将此示例直接应用于Flutter应用之前,请不要忘记将 <code>video_trimmer</code><code>file_picker</code> 包添加到您的 <code>pubspec.yaml</code> 文件中。

您可以尝试通过替换新创建的Flutter项目的 <code>main.dart</code> 文件的全部内容来运行此示例。

import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:video_trimmer/video_trimmer.dart';

void main() => runApp(MyApp());

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

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Video Trimmer"),
      ),
      body: Center(
        child: Container(
          child: ElevatedButton(
            child: Text("LOAD VIDEO"),
            onPressed: () async {
              FilePickerResult? result = await FilePicker.platform.pickFiles(
                type: FileType.video,
                allowCompression: false,
              );
              if (result != null) {
                File file = File(result.files.single.path!);
                Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) {
                    return TrimmerView(file);
                  }),
                );
              }
            },
          ),
        ),
      ),
    );
  }
}

class TrimmerView extends StatefulWidget {
  final File file;

  TrimmerView(this.file);

  @override
  _TrimmerViewState createState() => _TrimmerViewState();
}

class _TrimmerViewState extends State<TrimmerView> {
  final Trimmer _trimmer = Trimmer();

  double _startValue = 0.0;
  double _endValue = 0.0;

  bool _isPlaying = false;
  bool _progressVisibility = false;

  Future<String?> _saveVideo() async {
    setState(() {
      _progressVisibility = true;
    });

    String? _value;

    await _trimmer
        .saveTrimmedVideo(startValue: _startValue, endValue: _endValue)
        .then((value) {
      setState(() {
        _progressVisibility = false;
        _value = value;
      });
    });

    return _value;
  }

  void _loadVideo() {
    _trimmer.loadVideo(videoFile: widget.file);
  }

  @override
  void initState() {
    super.initState();

    _loadVideo();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Video Trimmer"),
      ),
      body: Builder(
        builder: (context) => Center(
          child: Container(
            padding: EdgeInsets.only(bottom: 30.0),
            color: Colors.black,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                Visibility(
                  visible: _progressVisibility,
                  child: LinearProgressIndicator(
                    backgroundColor: Colors.red,
                  ),
                ),
                ElevatedButton(
                  onPressed: _progressVisibility
                      ? null
                      : () async {
                          _saveVideo().then((outputPath) {
                            print('OUTPUT PATH: $outputPath');
                            final snackBar = SnackBar(
                                content: Text('Video Saved successfully'));
                            ScaffoldMessenger.of(context).showSnackBar(
                              snackBar,
                            );
                          });
                        },
                  child: Text("SAVE"),
                ),
                Expanded(
                  child: VideoViewer(trimmer: _trimmer),
                ),
                Center(
                  child: TrimViewer(
                    trimmer: _trimmer,
                    viewerHeight: 50.0,
                    viewerWidth: MediaQuery.of(context).size.width,
                    maxVideoLength: const Duration(seconds: 10),
                    onChangeStart: (value) => _startValue = value,
                    onChangeEnd: (value) => _endValue = value,
                    onChangePlaybackState: (value) =>
                        setState(() => _isPlaying = value),
                  ),
                ),
                TextButton(
                  child: _isPlaying
                      ? Icon(
                          Icons.pause,
                          size: 80.0,
                          color: Colors.white,
                        )
                      : Icon(
                          Icons.play_arrow,
                          size: 80.0,
                          color: Colors.white,
                        ),
                  onPressed: () async {
                    bool playbackState = await _trimmer.videoPlaybackControl(
                      startValue: _startValue,
                      endValue: _endValue,
                    );
                    setState(() {
                      _isPlaying = playbackState;
                    });
                  },
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

解决LTS版本问题 #

如果在Android平台上运行时出现错误提示 minSdkVersion 需要为 24,或者在iOS平台上提示Podfile平台版本应为 11,首先去 <code>pubspec.lock</code> 文件查看 ffmpeg_kit_flutter 是否带有 -LTS 后缀。这应该可以解决iOS平台上的所有问题。

如果在Android上仍然遇到相同的问题,尝试在 <code>&lt;project_directory&gt;/android/app/src/main/AndroidManifest.xml</code> 中添加以下内容:

&lt;manifest xmlns:tools="http://schemas.android.com/tools" ....... &gt;
    &lt;uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter, com.arthenica.ffmpegkit" /&gt;
&lt;/manifest&gt;

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

1 回复

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


当然,以下是一个关于如何使用Flutter视频裁剪插件video_trimmer_pro的代码案例。这个插件允许用户在Flutter应用中裁剪视频。

首先,你需要在pubspec.yaml文件中添加video_trimmer_pro依赖:

dependencies:
  flutter:
    sdk: flutter
  video_trimmer_pro: ^0.3.0  # 请检查最新版本号

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

接下来是一个简单的示例代码,展示如何使用video_trimmer_pro插件来裁剪视频:

import 'package:flutter/material.dart';
import 'package:video_trimmer_pro/video_trimmer_pro.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: VideoTrimmerScreen(),
    );
  }
}

class VideoTrimmerScreen extends StatefulWidget {
  @override
  _VideoTrimmerScreenState createState() => _VideoTrimmerScreenState();
}

class _VideoTrimmerScreenState extends State<VideoTrimmerScreen> {
  File? _videoFile;
  String? _trimmedVideoPath;

  @override
  void initState() {
    super.initState();
    _getVideoFile();
  }

  Future<void> _getVideoFile() async {
    // 获取应用文档目录并创建一个示例视频文件路径(这里你需要替换为你的视频文件路径)
    final Directory appDocDir = await getApplicationDocumentsDirectory();
    String videoFilePath = '${appDocDir.path}/sample_video.mp4';

    // 确保视频文件存在(在实际应用中,你可能需要从网络下载或选择用户设备中的视频)
    File videoFile = File(videoFilePath);
    if (!await videoFile.exists()) {
      // 这里你可以添加代码来下载或复制视频文件到该路径
      // 例如,从assets复制或者从网络下载
      print('Video file does not exist at $videoFilePath');
    } else {
      setState(() {
        _videoFile = videoFile;
      });
    }
  }

  Future<void> _trimVideo(VideoTrimmerController controller) async {
    final VideoTrimResult result = await controller.trimVideo();
    if (result.status == VideoTrimStatus.completed) {
      setState(() {
        _trimmedVideoPath = result.outputPath;
      });
      print('Trimmed video saved at ${result.outputPath}');
    } else {
      print('Video trimming failed: ${result.message}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Trimmer Example'),
      ),
      body: Center(
        child: _videoFile == null
            ? CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: () async {
                      final VideoTrimmerController controller =
                          VideoTrimmerController(
                        videoFile: _videoFile!,
                        startTime: Duration(seconds: 0), // 你可以设置默认开始时间
                        endTime: Duration(seconds: 30),  // 你可以设置默认结束时间
                      );

                      final VideoTrimmerPro videoTrimmerPro = VideoTrimmerPro(
                        controller: controller,
                      );

                      await Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => videoTrimmerPro,
                        ),
                      );

                      _trimVideo(controller);
                    },
                    child: Text('Trim Video'),
                  ),
                  if (_trimmedVideoPath != null)
                    Text(
                      'Trimmed Video Path: $_trimmedVideoPath',
                      style: TextStyle(fontSize: 16),
                    ),
                ],
              ),
      ),
    );
  }
}

在这个示例中,我们做了以下几步:

  1. pubspec.yaml中添加video_trimmer_pro依赖。
  2. 创建一个Flutter应用,并在主屏幕VideoTrimmerScreen中加载视频文件。
  3. 使用getApplicationDocumentsDirectory获取应用的文档目录,并假设视频文件已经存在该目录中(在实际应用中,你可能需要让用户从设备中选择视频或者从网络下载视频)。
  4. 创建一个按钮,点击按钮后打开视频裁剪界面。
  5. 在裁剪完成后,获取裁剪后的视频路径并显示。

请注意,在实际应用中,你需要确保视频文件路径正确,并且处理可能的异常和错误情况。

回到顶部