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
(带 FPS 的 10
, 480
规模& 循环 值 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设计。
通过将整个视频长度分成要在 TrimEditor 上显示的部分数量,可以从视频的不同位置生成背景中的图像。
放在两个顶角的两个“ 文本” 窗口小部件显示了修剪后的视频的 开始位置 和 结束位置 。
该 滑块 一直使用实施 GestureDetector & CustomPaint的 部件。
手势检测器
我主要使用了GestureDetector小部件的三个回调:
- onHorizontalDragStart
- onHorizontalDragUpdate
- onHorizontalDragEnd
该 onHorizontalDragStart被 指针触及一些位置上的屏幕和刚刚起步水平拖动执行回调。我用它来检测指针是在 起始位置 附近还是在 结束位置 附近,并相应地移动滑块。
当指针运动时, onHorizontalDragUpdate 回调处于活动状态。在这里,我试图给画家一个边界,以使其不会滑过视频的起点或终点。我还根据指针的位置计算了可以拖动滑块 开始 或 结束 位置的方向,并确定要拖动的滑块的 起点 还是 终点 。
并且,当指针不再触摸屏幕时执行 onHorizontalDragEnd 回调。我只是用它来将 圆形支架 (在滑块的两端)设置为正常大小。
我在这里面临的第一个挑战是,当滑块的 起点 和 终点 位于同一位置时,使用上述方法将无法实现其运动,并且它们将永远停留在原地。因此,当它们靠在一起时,我不得不为它们的移动编写不同的算法。
该算法并不是无懈可击的,可能会有一些错误,因此,欢迎对其感兴趣的并对进行改进的人为GitHub存储库提供PR 。
自定义画
我已使用CustomPaint小部件绘制 矩形滑块 ,两个 圆形固定器 以及当前的 视频播放位置 。
对于Slider,我只在画布上使用了 dr a wRec t 方法,传递了双 左 坐标和右下角坐标来绘制矩形。
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,
);
TrimEditor 的用户界面是完全可定制的。
结论
该软件包目前处于 测试 阶段,可能存在一些错误。可随时在 GitHub上对 项目进行改进。
GitHub存储库链接如下:
sbis04/video_trimmer
如果你喜欢这个项目,请给我的GitHub存储库加上“星星”()。
数据包链接:
video_trimmer | Flutter Package
原文作者 Souvik Biswas
原文链接 https://medium.com/flutter-community/my-journey-building-a-video-trimmer-package-for-flutter-73cd82997a7f