如题,我在项目开发过程中,使用 FFmpeg 来批量处理视频,但是发现 FFmpeg 剪切的视频并不能按照传参的开始时间和结束时间去切割视频,原因是关键帧的问题,比如我需要切割 5 到 7 秒的视频片段
ffmpeg -i a.mp4 -ss 00:00:05 -to 00:00:07 -c copy -o out.mp4
FFmpeg 会自动寻找5
秒和7
秒附近的关键帧,并从关键帧的位置去切割,导致视频最终的时间可能是00:00:04
~ 00:00:06
这种,导致切出来的视频有时候声音被切了一半
我知道在切割的时候使用重新编码
可以准确切割,但是重新编码遇到几个 G 的视频文件时,切割两秒的视频,就需要处理好几分钟,在批量切割的需求下(几百个视频片段),完全达不到性能要求
我想知道这几个问题:
1
coderluan 2021-11-30 10:13:14 +08:00 4
|
2
yedra 2021-11-30 10:17:12 +08:00 1
我也遇到一样的情况,最终说服了产品同意不精确的切割
|
7
tushankaka 2021-11-30 10:46:45 +08:00
-c copy 去掉,重新编码
|
8
anzu 2021-11-30 10:53:24 +08:00
7 楼正解,不过重新编码速度比较慢,尤其是如果你需要从几小时的视频中截取末尾的几分钟时
|
9
anzu 2021-11-30 10:58:06 +08:00
啊没仔细看,要求性能的话,大概只能-c 宽切后再细切重新编码了吧,比如前后+10 秒之类的。
|
10
wszgrcy 2021-11-30 10:58:16 +08:00
我记得有个解决方案时先按关键帧来,再切前几秒?但是不知道是怎么算出来前几秒是怎么的出来的
|
11
JerryV2 2021-11-30 10:58:29 +08:00
先用 -c copy ,时间范围设置大些,再用重新编码精确切割
|
12
zouri 2021-11-30 10:58:32 +08:00
OpenCV 应该可以
|
13
JerryV2 2021-11-30 11:01:11 +08:00
|
14
questionyu 2021-11-30 11:21:17 +08:00
`ffmpeg -i a.mp4 -ss 00:00:05 -to 00:00:07 -c copy -copyts -o out.mp4`
我之前在网上找的这个命令去完成比较精准的切割的,也不知道这样做是个什么原理 |
15
muchengxue 2021-11-30 11:30:54 +08:00
提供一种思路,先截取一定范围,然后把视频搞成帧图,然后再按照原视频的帧率和帧图合成新视频
|
16
cairnechen 2021-11-30 11:41:47 +08:00
楼主试试把-ss 参数放在-i 参数前面看看?
|
17
vanton 2021-11-30 11:45:04 +08:00 1
为什么不切两次?
先粗切,前后+10s 。 然后在编码细切。 |
18
3dwelcome 2021-11-30 11:45:46 +08:00 3
楼主没搞懂无损剪切原理。
所谓关键帧,就是后面几秒的视频,都是根据这个关键帧推导出图像的。 不编码直接硬切,就意味着丢失关键帧,那解压出来的视频,就都是马赛克了。 |
19
ksword 2021-11-30 12:03:37 +08:00
copy 模式是没办法了,只能重编码或试下按关键帧切,而不是时间
|
20
xylophone21 2021-11-30 12:05:11 +08:00
copy 模式下,开头不是关键帧怎么播放呢?
切两次呗 |
21
Rheinmetal 2021-11-30 12:17:38 +08:00
patch ffmpeg 只重新编码两头关键帧以外的地方
|
22
misdake 2021-11-30 12:23:41 +08:00
1. 先找到关键帧
网上找的命令,比如 >ffprobe -loglevel error -skip_frame nokey -select_streams v:0 -show_entries frame=pkt_pts_time -of csv=print_section=0 input.mp4 得到关键帧的时间戳,只是感觉输出不是很稳定,会有数字之外的数据,可能要多调试兼容 2. 粗切 ffmpeg 用 -c copy 的时候,手动指定前后的关键帧的时间戳,这样应该就会从指定的帧开始。至少我实验下来开始的关键帧是准的,结束的关键帧不稳定,可能还会多几帧。 3. 精切 接着就可以用重编码的方法来切到指定帧了。 |
23
qin20 OP @vanton 因为粗切也无法控制时间,然后就没有办法去实行精确切,不知道我这样说你理解吗,只要不知道切出来的实际时间,就无法去控制
|
24
qin20 OP 重新编码耗时太久了
|
30
qin20 OP @misdake 谢谢,这个我试过了,关键帧信息不稳定,比如现在我要从 10s 开始切,那么我要去算出离 10s 最近的一个关键帧。。。
记录下这个误差,最后在调整,太难了,而且那个命令输出的关键帧感觉不是很准确 所以我说 ffmpeg 能不能再切的时候把它实际切割的关键帧的时间返回给我,我拿到这时间,在做一个调整,就可以得到精确的视频,这样最方便 |
31
misdake 2021-11-30 13:08:23 +08:00
@qin20 可以试试拉 loglevel ,然后监听类似于 [mov,mp4,m4a,3gp,3g2,mj2 @ 000001f53b1ae8c0] stream 0, sample 450, dts 14933333 的数据,就能知道他在复制那些帧
|
32
misdake 2021-11-30 13:40:17 +08:00
“重新编码遇到几个 G 的视频文件时,切割两秒的视频,就需要处理好几分钟,在批量切割的需求下(几百个视频片段),完全达不到性能要求”
这个问题我没有遇到哦,我从一个 13 分钟长、平均码率 4.5Mbps 的视频中,截取比较靠后的 1 秒钟 ffmpeg -ss 700 -to 701 -i a.mp4 out.mp4 用 measure-command 测了一下运行时间不到 0.7 秒 |
33
hazardous 2021-11-30 13:52:23 +08:00
我的思路是:其实只有开头和结尾与关键帧不对齐的那两个 gop 是需要重编码的,而中间的绝大部分直接 copy 就可以。具体就是把视频分成三段,第一段是开始点到下一个关键帧,第三段是结束点前的一个关键帧到结束点,这两段重编码,中间的 copy ,最后三段合并。
|
34
Natsumoku 2021-11-30 14:04:02 +08:00 via Android
@coderluan lossless-cut 绕不过关键帧的限制,在 UI 上把时间轴放大是可以看到一个个关键帧的,输出的时候不管怎么设置都没法精确到两个关键帧之间。
事实上有一个官方 issue 跟踪的就是如何解决这个问题: https://github.com/mifi/lossless-cut/issues/126 |
35
qin20 OP @misdake 我把日志全都拉出来了,loglevel 全开,然而没有找到,太多了,所以想问问有没有知道是 log 里面的哪个信息,能拿到实际的切割的关键帧。。。
|
36
qin20 OP @misdake 你测试的命令只有 0.7 秒确实比我电脑上运行的快,但是你这个只是使用的 copy 模式,得出的开始时间和结束时间就是刚开始说的那个不准确问题,我说的久的情况,是为了得到准确的开始和结束,而用的重新编码模式,要加上输出命令 ffmpeg -i a.mp4 -ss 700 -to 701 -c:v libx264 -c:a copy out.mp4 ,这样就会比较慢
|
37
qin20 OP @Natsumoku 你了解剪映之类的软件是如何切割的吗?他们切割起来好像挺准确的啊,而且他们的视频感觉导入的速度非常快,一下子就解码然后放到视频轨道上了
|
39
xuhaoyangx 2021-11-30 14:32:43 +08:00
|
40
xuhaoyangx 2021-11-30 14:33:55 +08:00
建议你出门搜 I 帧、P 帧、B 帧、GOP 、IDR 和 PTS, DTS 之间的关系
|
41
qin20 OP @xuhaoyangx 最后合成的时候也要进行一次切割的吧,这个时候可以保证精准吗
|
42
qin20 OP @xuhaoyangx 我只了解大概,因为刚接触视频处理一个多月,看了一些文章,GOP 、IDR 和 PTS, DTS 很多概念还是很模糊
|
43
xuhaoyangx 2021-11-30 14:38:21 +08:00
@qin20 所以他们合成用的时有损压缩,只不过调用了硬件编码而已
|
44
xuhaoyangx 2021-11-30 14:39:46 +08:00
无损切割,只能切 I 帧,但是绝大多数视频,i 帧只占少数部分,P B 才是多数。这就是 copy 为啥不能精确切割的问题,你的时间码,正好可能是 P B 帧。
如果你要做剪影哪一类的软件,那么你现在的思路就是错的 |
45
misdake 2021-11-30 14:47:34 +08:00
@qin20 默认不指定 copy 就是重新编码的,而且输出正好是 1 秒钟,共 61 帧数据。
需要把-ss 和-to 放在-i 前面,这样时间修饰的是输入,会跳过前面的编码,只编码制定的区间。 放在-i 后面就变成修饰输出了,不符合你的需求,会导致要全编码一遍然后在输出的地方截断。 |
46
qin20 OP @xuhaoyangx 哦哦,我怀疑有没有一种可能剪影它在视频轨道上显示用图片显示出来的,都是 I 帧,所以用户切的时候,都是在切 I 帧
我现在软件功能都 ok 了,采用的也是类似有损压缩的方式,但是这种方式需要用户提前把视频编码成全部是 I 帧的,比较麻烦,现在就一个难点,就是这个切割时间问题 |
47
xuhaoyangx 2021-11-30 15:00:53 +08:00
@qin20 所有的剪辑软件,都不会再编辑时期,去操作原视频,只是记录用户对视频文件的操作,最终导出,才会操作
|
48
zzfer 2021-11-30 15:04:48 +08:00
研究过,除非重新编码切割,不然无法做到完全精准,这样做就是慢,但是是和切割的视频时长有关,如果只要三分钟的话,也没太慢。或者网上有人说用 openCV 切割视频,FFmpeg 切割音频,然后合并,但没试过。
|
49
mxalbert1996 2021-11-30 15:08:47 +08:00 via Android 3
https://trac.ffmpeg.org/wiki/Seeking
建议读一下这个了解一下 Input Seeking 和 Output Seeking 的区别。 |
50
Sasasu 2021-11-30 15:38:42 +08:00
-i 前后各加一个 -ss
|
51
q197 2021-11-30 15:42:39 +08:00 1
@anzu 越后面越慢是 ffmpeg 使用的经典错误,-ss 要放在-i 前面,否则意思是处理视频直到-ss 处才开始输出,而正确的意思是从-ss 处开始处理且输出
|
52
qin20 OP @misdake 作为输入参数会跳过开始片段,作为输出参数就会重新编码全部,但是这两种做法,都无法做到精确切割,我都尝试过了
|
54
qin20 OP @xuhaoyangx 是的,我也想到了不应该频繁的去操作原视频,只是我的功能比较特殊,需要实时的预览,涉及到字幕、声音、图片等都要同步,所以索性直接当场合成了,现在用户用的反馈还算可以
|
56
digimoon 2021-11-30 18:38:04 +08:00
粗切,精确切重编码头尾,再将头尾和中间大段拼起来
|
57
mxalbert1996 2021-11-30 18:51:12 +08:00 via Android
摘录我上面发的链接里的一句话:
As of FFmpeg 2.1, when transcoding with ffmpeg (i.e. not just stream copying), -ss is now also "frame-accurate" even when used as an input option. 如果楼主发现实际行为不是这样的话(可能性很小,这么常用的功能有 BUG 的话应该早修了),请去提一个 BUG 。 |
58
wudicgi 2021-11-30 19:38:12 +08:00
lossless-cut 我试过好几次了,很早的时候试过,几个月前也试过,就没有真正可用过
现在自己试下来,唯一能用的是 Bandicut, 需要付费购买 不过对我来说它的最大问题是没有命令行支持 所以为了自动化,我最终是写了个 AutoHotkey 脚本自动操作它的 GUI 来完成批量工作的 |
59
wudicgi 2021-11-30 19:39:30 +08:00
对了,Bandicut 快速模式只有头尾需要进行一些操作,中间部分不要重编码,所以很快
|
60
royzxq 2021-11-30 19:56:09 +08:00
|
61
cxbig 2021-11-30 21:11:42 +08:00
如果不考虑用其他 GUI 软件,只用 ffmpeg
可以考虑把参数精确到毫秒,然后开头和结尾只剪出 5 秒左右用来确认关键帧和音频位置,再算总长度做完整的剪切。 |
62
tcp 2021-12-01 00:41:15 +08:00
一般这种需求直接 ffmpeg 解出 yuv ,算出需要的帧数,直接二进制截出来,然后 ffmpeg -vcodec libx264 压一遍,很快的。解决本质问题,比上面这些折腾都省事。
|
64
qin20 OP @mxalbert1996 不是不行,是很慢。。。几个 g 的视频相当于每次切都重新编码
|
65
qin20 OP @mxalbert1996 这么说吧,input seeking 快但是切割时间不准确,output seeking 准确但是耗时久,两种我都试了,都有各种问题
|
67
rosu 2021-12-01 12:20:35 +08:00 via iPhone
|