为Flutter构建视频修剪器程序包

1_xY5szcfg9NkW1k3NH5gcbw

6个月 我当时正在从事一个需要使用视频编辑器来处理视频,然后再将其上传到后端服务器的项目。我想到的第一件事是搜索 Flutter软件包 以实现此功能,但是,看到pub.dev上没有任何可用于视频编辑的软件包时,我十分恼火。

那时,我只具有对Flutter app的开发能力,并没有自己构建整个编辑器的技能,因此,我决定离开该项目。

但是即使在6个月后,我惊讶地发现Flutter中还是没有用于视频编辑的程序包,也没有良好的UI / UX。因此,凭借过去几个月里获得的技能,我决定为Flutter构建一个具有良好 UI 支持的 video trimmer 程序包。

你可以在此处找到 video_trimmer 软件包:

video_trimmer | Flutter 包
Flutter软件包,用语修剪视频。可定制的视频修剪器视频播放控制检索和……
pub.dev

功能性

视频微调器的主要功能是:

  • 以用户选择的指定格式 检索 视频文件并将其 存储 到文件系统。该软件包支持大多数广泛使用的视频格式,例如 MP4、MKV、MOV、FLV、AVI、WVM 以及 GIF, 用于保存输出视频。
  • 基本的 视频播放控件
  • 支持高级 FFmpeg 自定义命令。

加载和存储

该插件支持大多数视频格式,因为 输入输出 修整后的视频可以保存为以下格式: MP4、MKV、MOV、FLV、AVI、WVM 以及 GIF 。可以从 FileFormat 类中选择格式。

如果你要将输出作为 GIF, 则有两个附加选项可用:

  • fpsGIF :用于输出GIF设置 FPS 值。
  • scaleGIF :定义输出GIF的 宽度 ,并根据纵横比对高度进行相应缩放。

修剪后的视频可以保存在 StorageDir 类中定义的三个目录中的任何一个中。这些目录是:

  • 临时目录 :仅能从app内部访问,可随时清除
  • applicationDocumentsDirectory :仅可从应用程序内部访问
  • externalStorageDirectory :仅支持 Android 平台,可从外部访问

默认情况下,修剪后的视频以 MP4 格式存储在所选目录名为 Trimmer 的文件夹中,文件名遵循以下格式:

< original_file_name > _trimmed:< DATE_TIME。> < file_format >

加载视频:

final Trimmer _trimmer = Trimmer();
File _videoFile = await _trimmer.loadVideo();

保存修剪过的视频:

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

你还可以自定义文件夹名称和文件名,这些文件名将用于存储视频文件。

视频播放控件

这是一种非常简单的方法,该方法返回一个具有视频播放状态的 boolean 值,即它是播放还是暂停。这将对你在播放状态更改后立即触发app中的某些操作很有用。

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

编辑影片

该软件包使用 FFmpeg 命令来修剪和定义视频的输出格式。

FFmpeg命令的基础实施如下:

-i <path_to_video> -ss <start_point> -t <duration> -c copy <output_video_path>/<video_file_name>.<video_format>
  • -i :用于提供 输入视频路径
  • -ss :用于寻找视频的 起点
  • -t :用于指定视频的 持续时间
  • -c :用于向视频提供 编解码器
  • copy :一种称为 流复制模式 的编解码器,省略了指定流的解码和编码步骤。它有助于非常快速地进行质量损失转换。

从修整的视频为例,起 始点00:00:03 与所述 端点00:00:08 (即, 持续时间 将是 00:00:05 ),以及视频输出格式 .mp4 给出以下:

-i /Photos/video.mp4 -ss 00:00:03 -t 00:00:05 -c copy /Photos/trim_video.mp4

为了把输入视频生成一个 GIF ,使用了另一个FFmpeg命令。具体如下:

-i <path_to_video> -ss <start_point> -t <duration> -vf "fps=<fps_of_gif>,scale=<scale_of_gif>:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 <output_video_path>/<video_file_name>.gif
  • -vf :用于向输出视频提供 帧数
  • Palettegen Paletteuse 过滤器会在你的输入中 生成使用 已产生 自定义调色板。
  • -loop :用于指定视频将循环播放 的次数 。数值 0无限的 循环, -1没有循环 ,并且 any positive value 会循环很多次,如果该值代表着 3 它会播放视频的4倍。

从修整的视频为例,起始点00:00:03 与所述 端点00:00:08 (即, 持续时间 将是 00:00:05 ),并且将其转换为 .gif (带 FPS10480 规模& 循环0 ,即, 无限循环 )给出如下:

-i /Photos/video.mp4 -ss 00:00:03 -t 00:00:05 -vf "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 /Photos/trim_video.gif

该软件包还支持 自定义FFmpeg命令 ,可查看以下部分以了解更多信息。

进阶命令

该软件包支持具有用户定义的输出视频格式的任何 FFmpeg 命令,用于编辑视频。通常,你不需要使用此自定义命令,因为大多数视频修剪功能已包含在包装中。

Refer to the Official FFmpeg Documentation for more information

有关更多信息,请参考FFmpeg官方文档

注意:高级选项不提供任何安全检查,因此,如果在应用程序中传递了错误的视频格式,则可能会导致系统崩溃。

// Example of defining a custom command

// This is already used for creating GIF by
// default, so you do not need to use this.

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;
  });
});

UI / UX

现在让我们进入最有趣的部分,即视频修剪器的UI设计。

%E6%9C%AA%E5%91%BD%E5%90%8D_%E5%89%AF%E6%9C%AC

通过将整个视频长度分成要在 TrimEditor 上显示的部分数量,可以从视频的不同位置生成背景中的图像。

放在两个顶角的两个“ 文本” 窗口小部件显示了修剪后的视频的 开始位置结束位置

滑块 一直使用实施 GestureDetectorCustomPaint的 部件。

手势检测器

我主要使用了GestureDetector小部件的三个回调:

  • onHorizo​​ntalDragStart
  • onHorizo​​ntalDragUpdate
  • onHorizo​​ntalDragEnd

onHorizo​​ntalDragStart被 指针触及一些位置上的屏幕和刚刚起步水平拖动执行回调。我用它来检测指针是在 起始位置 附近还是在 结束位置 附近,并相应地移动滑块。

当指针运动时, onHorizo​​ntalDragUpdate 回调处于活动状态。在这里,我试图给画家一个边界,以使其不会滑过视频的起点或终点。我还根据指针的位置计算了可以拖动滑块 开始结束 位置的方向,并确定要拖动的滑块的 起点 还是 终点

并且,当指针不再触摸屏幕时执行 onHorizo​​ntalDragEnd 回调。我只是用它来将 圆形支架 (在滑块的两端)设置为正常大小。

我在这里面临的第一个挑战是,当滑块的 起点终点 位于同一位置时,使用上述方法将无法实现其运动,并且它们将永远停留在原地。因此,当它们靠在一起时,我不得不为它们的移动编写不同的算法。

该算法并不是无懈可击的,可能会有一些错误,因此,欢迎对其感兴趣的并对进行改进的人为GitHub存储库提供PR 。

自定义画

我已使用CustomPaint小部件绘制 矩形滑块 ,两个 圆形固定器 以及当前的 视频播放位置

对于Slider,我只在画布上使用了 dr a wRec t 方法,传递了双 坐标和右下角坐标来绘制矩形。

1_tqPZ7fBZ4tSat1FHhay_8Q

var borderPaint = Paint()
   ..color = borderPaintColor
   ..strokeWidth = borderWidth
   ..style = PaintingStyle.stroke
   ..strokeCap = StrokeCap.round;
final rect = Rect.fromPoints(startPos, endPos);
canvas.drawRect(rect, borderPaint);

绘制两个 圆形支架

canvas.drawCircle(
startPos + Offset(0, endPos.dy / 2), circleSize, circlePaint);

canvas.drawCircle(
endPos + Offset(0, -endPos.dy / 2), circleSize, circlePaint);

对于当前的 视频播放位置

canvas.drawLine(
   currentPos,
   currentPos + Offset(0, endPos.dy),
   scrubberPaint,
);

1_qsmhgB4dyCra32sdMZiE8w

TrimEditor 的用户界面是完全可定制的。

结论

该软件包目前处于 测试 阶段,可能存在一些错误。可随时在 GitHub上对 项目进行改进。

GitHub存储库链接如下:

sbis04/video_trimmer

Flutter 软件包,用于修剪视频。可定制的视频微调器视频播放控制检索和存储视频…
github.com

如果你喜欢这个项目,请给我的GitHub存储库加上“星星”(:star:)。

数据包链接:

video_trimmer | Flutter Package

Flutter 软件包,用语修剪视频。功能可定制的视频修剪器视频播放控制检索和…
pub.dev

原文作者 Souvik Biswas
原文链接 https://medium.com/flutter-community/my-journey-building-a-video-trimmer-package-for-flutter-73cd82997a7f

推荐阅读
作者信息
AgoraTechnicalTeam
TA 暂未填写个人简介
文章
154
相关专栏
本专栏仅用于分享音视频相关的技术文章,与其他开发者和 Agora 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。