V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
qin20
V2EX  ›  FFmpeg

程序 FFmpeg 准确切割视频问题,请大神指教?

  •  1
     
  •   qin20 · 2021-11-30 09:58:49 +08:00 · 8577 次点击
    这是一个创建于 849 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,我在项目开发过程中,使用 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. 有没有一款软件可以替代 FFmpeg 实现准确切割的功能
    2. 如果使用 FFmpeg ,能不能拿到 FFmpeg 它实际切割的关键帧的时间,这样方便我快速重新编码调整切割的误差
    67 条回复    2021-12-01 12:20:35 +08:00
    coderluan
        1
    coderluan  
       2021-11-30 10:13:14 +08:00   ❤️ 4
    https://github.com/mifi/lossless-cut

    如果能用 GUI ,这个是神器,准确而且不用重新编码,应该是自己插了些 PPS 信息帧。
    yedra
        2
    yedra  
       2021-11-30 10:17:12 +08:00   ❤️ 1
    我也遇到一样的情况,最终说服了产品同意不精确的切割
    qin20
        3
    qin20  
    OP
       2021-11-30 10:21:27 +08:00
    @coderluan 我研究研究
    qin20
        4
    qin20  
    OP
       2021-11-30 10:22:06 +08:00
    @yedra 哈哈我是产品也是程序
    qin20
        5
    qin20  
    OP
       2021-11-30 10:24:33 +08:00
    @coderluan 我的程序就是 gui 的,不能再使用 gui 啦,有没有命令行?
    coderluan
        6
    coderluan  
       2021-11-30 10:32:41 +08:00
    @qin20 温馨提示:这个是开源的。
    tushankaka
        7
    tushankaka  
       2021-11-30 10:46:45 +08:00
    -c copy 去掉,重新编码
    anzu
        8
    anzu  
       2021-11-30 10:53:24 +08:00
    7 楼正解,不过重新编码速度比较慢,尤其是如果你需要从几小时的视频中截取末尾的几分钟时
    anzu
        9
    anzu  
       2021-11-30 10:58:06 +08:00
    啊没仔细看,要求性能的话,大概只能-c 宽切后再细切重新编码了吧,比如前后+10 秒之类的。
    wszgrcy
        10
    wszgrcy  
       2021-11-30 10:58:16 +08:00
    我记得有个解决方案时先按关键帧来,再切前几秒?但是不知道是怎么算出来前几秒是怎么的出来的
    JerryV2
        11
    JerryV2  
       2021-11-30 10:58:29 +08:00
    先用 -c copy ,时间范围设置大些,再用重新编码精确切割
    zouri
        12
    zouri  
       2021-11-30 10:58:32 +08:00
    OpenCV 应该可以
    JerryV2
        13
    JerryV2  
       2021-11-30 11:01:11 +08:00
    @JerryV2
    请忽略。。。
    楼主已想到此方法,关键不知道首次切割后,再次切割时的时间起始和终止

    应该可以读取关键帧的具体时间吧,这样这个思路还是可行的
    questionyu
        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`

    我之前在网上找的这个命令去完成比较精准的切割的,也不知道这样做是个什么原理
    muchengxue
        15
    muchengxue  
       2021-11-30 11:30:54 +08:00
    提供一种思路,先截取一定范围,然后把视频搞成帧图,然后再按照原视频的帧率和帧图合成新视频
    cairnechen
        16
    cairnechen  
       2021-11-30 11:41:47 +08:00
    楼主试试把-ss 参数放在-i 参数前面看看?
    vanton
        17
    vanton  
       2021-11-30 11:45:04 +08:00   ❤️ 1
    为什么不切两次?
    先粗切,前后+10s 。
    然后在编码细切。
    3dwelcome
        18
    3dwelcome  
       2021-11-30 11:45:46 +08:00   ❤️ 3
    楼主没搞懂无损剪切原理。

    所谓关键帧,就是后面几秒的视频,都是根据这个关键帧推导出图像的。

    不编码直接硬切,就意味着丢失关键帧,那解压出来的视频,就都是马赛克了。
    ksword
        19
    ksword  
       2021-11-30 12:03:37 +08:00
    copy 模式是没办法了,只能重编码或试下按关键帧切,而不是时间
    xylophone21
        20
    xylophone21  
       2021-11-30 12:05:11 +08:00
    copy 模式下,开头不是关键帧怎么播放呢?

    切两次呗
    Rheinmetal
        21
    Rheinmetal  
       2021-11-30 12:17:38 +08:00
    patch ffmpeg 只重新编码两头关键帧以外的地方
    misdake
        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. 精切
    接着就可以用重编码的方法来切到指定帧了。
    qin20
        23
    qin20  
    OP
       2021-11-30 12:54:50 +08:00
    @vanton 因为粗切也无法控制时间,然后就没有办法去实行精确切,不知道我这样说你理解吗,只要不知道切出来的实际时间,就无法去控制
    qin20
        24
    qin20  
    OP
       2021-11-30 12:55:28 +08:00
    重新编码耗时太久了
    qin20
        25
    qin20  
    OP
       2021-11-30 12:56:16 +08:00
    @JerryV2 我把所有的帧信息都解出来了,仍然无法找出是哪个关键帧,其实我也是初学。。。很多东西没有理解
    qin20
        26
    qin20  
    OP
       2021-11-30 12:57:00 +08:00
    @wszgrcy 能不能帮忙给个链接
    qin20
        27
    qin20  
    OP
       2021-11-30 12:57:08 +08:00
    @zouri ok ,我试试
    qin20
        28
    qin20  
    OP
       2021-11-30 12:59:05 +08:00
    @3dwelcome 我知道无损压缩算法,现在就是要解决这个引发的问题
    qin20
        29
    qin20  
    OP
       2021-11-30 12:59:56 +08:00
    @ksword 不一定是要 copy ,只要可以完成,并且耗时不这么长,就 ok
    qin20
        30
    qin20  
    OP
       2021-11-30 13:03:21 +08:00
    @misdake 谢谢,这个我试过了,关键帧信息不稳定,比如现在我要从 10s 开始切,那么我要去算出离 10s 最近的一个关键帧。。。
    记录下这个误差,最后在调整,太难了,而且那个命令输出的关键帧感觉不是很准确
    所以我说 ffmpeg 能不能再切的时候把它实际切割的关键帧的时间返回给我,我拿到这时间,在做一个调整,就可以得到精确的视频,这样最方便
    misdake
        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 的数据,就能知道他在复制那些帧
    misdake
        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 秒
    hazardous
        33
    hazardous  
       2021-11-30 13:52:23 +08:00
    我的思路是:其实只有开头和结尾与关键帧不对齐的那两个 gop 是需要重编码的,而中间的绝大部分直接 copy 就可以。具体就是把视频分成三段,第一段是开始点到下一个关键帧,第三段是结束点前的一个关键帧到结束点,这两段重编码,中间的 copy ,最后三段合并。
    Natsumoku
        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
    qin20
        35
    qin20  
    OP
       2021-11-30 14:19:33 +08:00
    @misdake 我把日志全都拉出来了,loglevel 全开,然而没有找到,太多了,所以想问问有没有知道是 log 里面的哪个信息,能拿到实际的切割的关键帧。。。
    qin20
        36
    qin20  
    OP
       2021-11-30 14:27:53 +08:00
    @misdake 你测试的命令只有 0.7 秒确实比我电脑上运行的快,但是你这个只是使用的 copy 模式,得出的开始时间和结束时间就是刚开始说的那个不准确问题,我说的久的情况,是为了得到准确的开始和结束,而用的重新编码模式,要加上输出命令 ffmpeg -i a.mp4 -ss 700 -to 701 -c:v libx264 -c:a copy out.mp4 ,这样就会比较慢
    qin20
        37
    qin20  
    OP
       2021-11-30 14:30:28 +08:00
    @Natsumoku 你了解剪映之类的软件是如何切割的吗?他们切割起来好像挺准确的啊,而且他们的视频感觉导入的速度非常快,一下子就解码然后放到视频轨道上了
    qin20
        38
    qin20  
    OP
       2021-11-30 14:32:12 +08:00
    @hazardous 思路不错,如何取得这三段视频的信息呢,有没有好的思路
    xuhaoyangx
        39
    xuhaoyangx  
       2021-11-30 14:32:43 +08:00
    copy 不可能准确切的,比如 26x 的预测帧怎么切,切不了,只能找关键帧。

    @qin20 剪映切割只是记录了,你切的位置,并没有对原视频进行切割,只有最后输出的时候,根据操作记录去进行合成输出
    xuhaoyangx
        40
    xuhaoyangx  
       2021-11-30 14:33:55 +08:00
    建议你出门搜 I 帧、P 帧、B 帧、GOP 、IDR 和 PTS, DTS 之间的关系
    qin20
        41
    qin20  
    OP
       2021-11-30 14:34:56 +08:00
    @xuhaoyangx 最后合成的时候也要进行一次切割的吧,这个时候可以保证精准吗
    qin20
        42
    qin20  
    OP
       2021-11-30 14:37:43 +08:00
    @xuhaoyangx 我只了解大概,因为刚接触视频处理一个多月,看了一些文章,GOP 、IDR 和 PTS, DTS 很多概念还是很模糊
    xuhaoyangx
        43
    xuhaoyangx  
       2021-11-30 14:38:21 +08:00
    @qin20 所以他们合成用的时有损压缩,只不过调用了硬件编码而已
    xuhaoyangx
        44
    xuhaoyangx  
       2021-11-30 14:39:46 +08:00
    无损切割,只能切 I 帧,但是绝大多数视频,i 帧只占少数部分,P B 才是多数。这就是 copy 为啥不能精确切割的问题,你的时间码,正好可能是 P B 帧。

    如果你要做剪影哪一类的软件,那么你现在的思路就是错的
    misdake
        45
    misdake  
       2021-11-30 14:47:34 +08:00
    @qin20 默认不指定 copy 就是重新编码的,而且输出正好是 1 秒钟,共 61 帧数据。
    需要把-ss 和-to 放在-i 前面,这样时间修饰的是输入,会跳过前面的编码,只编码制定的区间。
    放在-i 后面就变成修饰输出了,不符合你的需求,会导致要全编码一遍然后在输出的地方截断。
    qin20
        46
    qin20  
    OP
       2021-11-30 14:51:41 +08:00
    @xuhaoyangx 哦哦,我怀疑有没有一种可能剪影它在视频轨道上显示用图片显示出来的,都是 I 帧,所以用户切的时候,都是在切 I 帧

    我现在软件功能都 ok 了,采用的也是类似有损压缩的方式,但是这种方式需要用户提前把视频编码成全部是 I 帧的,比较麻烦,现在就一个难点,就是这个切割时间问题
    xuhaoyangx
        47
    xuhaoyangx  
       2021-11-30 15:00:53 +08:00
    @qin20 所有的剪辑软件,都不会再编辑时期,去操作原视频,只是记录用户对视频文件的操作,最终导出,才会操作
    zzfer
        48
    zzfer  
       2021-11-30 15:04:48 +08:00
    研究过,除非重新编码切割,不然无法做到完全精准,这样做就是慢,但是是和切割的视频时长有关,如果只要三分钟的话,也没太慢。或者网上有人说用 openCV 切割视频,FFmpeg 切割音频,然后合并,但没试过。
    mxalbert1996
        49
    mxalbert1996  
       2021-11-30 15:08:47 +08:00 via Android   ❤️ 3
    https://trac.ffmpeg.org/wiki/Seeking
    建议读一下这个了解一下 Input Seeking 和 Output Seeking 的区别。
    Sasasu
        50
    Sasasu  
       2021-11-30 15:38:42 +08:00
    -i 前后各加一个 -ss
    q197
        51
    q197  
       2021-11-30 15:42:39 +08:00   ❤️ 1
    @anzu 越后面越慢是 ffmpeg 使用的经典错误,-ss 要放在-i 前面,否则意思是处理视频直到-ss 处才开始输出,而正确的意思是从-ss 处开始处理且输出
    qin20
        52
    qin20  
    OP
       2021-11-30 17:44:50 +08:00
    @misdake 作为输入参数会跳过开始片段,作为输出参数就会重新编码全部,但是这两种做法,都无法做到精确切割,我都尝试过了
    qin20
        53
    qin20  
    OP
       2021-11-30 17:45:40 +08:00
    @Sasasu 还有这种骚操作?
    qin20
        54
    qin20  
    OP
       2021-11-30 17:48:21 +08:00
    @xuhaoyangx 是的,我也想到了不应该频繁的去操作原视频,只是我的功能比较特殊,需要实时的预览,涉及到字幕、声音、图片等都要同步,所以索性直接当场合成了,现在用户用的反馈还算可以
    qin20
        55
    qin20  
    OP
       2021-11-30 17:50:29 +08:00
    @coderluan 我发现我做的软件竟然和这个有点类似,卧槽了。。。
    digimoon
        56
    digimoon  
       2021-11-30 18:38:04 +08:00
    粗切,精确切重编码头尾,再将头尾和中间大段拼起来
    mxalbert1996
        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 。
    wudicgi
        58
    wudicgi  
       2021-11-30 19:38:12 +08:00
    lossless-cut 我试过好几次了,很早的时候试过,几个月前也试过,就没有真正可用过

    现在自己试下来,唯一能用的是 Bandicut, 需要付费购买
    不过对我来说它的最大问题是没有命令行支持
    所以为了自动化,我最终是写了个 AutoHotkey 脚本自动操作它的 GUI 来完成批量工作的
    wudicgi
        59
    wudicgi  
       2021-11-30 19:39:30 +08:00
    对了,Bandicut 快速模式只有头尾需要进行一些操作,中间部分不要重编码,所以很快
    royzxq
        60
    royzxq  
       2021-11-30 19:56:09 +08:00
    cxbig
        61
    cxbig  
       2021-11-30 21:11:42 +08:00
    如果不考虑用其他 GUI 软件,只用 ffmpeg
    可以考虑把参数精确到毫秒,然后开头和结尾只剪出 5 秒左右用来确认关键帧和音频位置,再算总长度做完整的剪切。
    tcp
        62
    tcp  
       2021-12-01 00:41:15 +08:00
    一般这种需求直接 ffmpeg 解出 yuv ,算出需要的帧数,直接二进制截出来,然后 ffmpeg -vcodec libx264 压一遍,很快的。解决本质问题,比上面这些折腾都省事。
    qin20
        63
    qin20  
    OP
       2021-12-01 02:07:30 +08:00 via iPad
    @tcp 我试试能不能局部解压,整个视频解压的话,体积大,时间也久
    qin20
        64
    qin20  
    OP
       2021-12-01 02:09:12 +08:00 via iPad
    @mxalbert1996 不是不行,是很慢。。。几个 g 的视频相当于每次切都重新编码
    qin20
        65
    qin20  
    OP
       2021-12-01 02:20:09 +08:00 via iPad
    @mxalbert1996 这么说吧,input seeking 快但是切割时间不准确,output seeking 准确但是耗时久,两种我都试了,都有各种问题
    tcp
        66
    tcp  
       2021-12-01 08:23:10 +08:00
    @qin20 根据帧率和时间计算要解码的帧数,ffmpeg 解码时会打印帧数,到了就 ctrl-c 停掉即可。
    rosu
        67
    rosu  
       2021-12-01 12:20:35 +08:00 via iPhone
    可以参考我在下面帖子里的回答。先看 ffmepg 的文档吧。

    https://www.v2ex.com/t/808095
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1377 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 17:38 · PVG 01:38 · LAX 10:38 · JFK 13:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.