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

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

  •  1
     
  •   qin20 · 175 天前 · 4292 次点击
    这是一个创建于 175 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,我在项目开发过程中,使用 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  
       174 天前   ❤️ 3
    https://github.com/mifi/lossless-cut

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

    应该可以读取关键帧的具体时间吧,这样这个思路还是可行的
    questionyu
        14
    questionyu  
       174 天前
    `ffmpeg -i a.mp4 -ss 00:00:05 -to 00:00:07 -c copy -copyts -o out.mp4`

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

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

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

    切两次呗
    Rheinmetal
        21
    Rheinmetal  
       174 天前
    patch ffmpeg 只重新编码两头关键帧以外的地方
    misdake
        22
    misdake  
       174 天前
    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
       174 天前
    @vanton 因为粗切也无法控制时间,然后就没有办法去实行精确切,不知道我这样说你理解吗,只要不知道切出来的实际时间,就无法去控制
    qin20
        24
    qin20  
    OP
       174 天前
    重新编码耗时太久了
    qin20
        25
    qin20  
    OP
       174 天前
    @JerryV2 我把所有的帧信息都解出来了,仍然无法找出是哪个关键帧,其实我也是初学。。。很多东西没有理解
    qin20
        26
    qin20  
    OP
       174 天前
    @wszgrcy 能不能帮忙给个链接
    qin20
        27
    qin20  
    OP
       174 天前
    @zouri ok ,我试试
    qin20
        28
    qin20  
    OP
       174 天前
    @3dwelcome 我知道无损压缩算法,现在就是要解决这个引发的问题
    qin20
        29
    qin20  
    OP
       174 天前
    @ksword 不一定是要 copy ,只要可以完成,并且耗时不这么长,就 ok
    qin20
        30
    qin20  
    OP
       174 天前
    @misdake 谢谢,这个我试过了,关键帧信息不稳定,比如现在我要从 10s 开始切,那么我要去算出离 10s 最近的一个关键帧。。。
    记录下这个误差,最后在调整,太难了,而且那个命令输出的关键帧感觉不是很准确
    所以我说 ffmpeg 能不能再切的时候把它实际切割的关键帧的时间返回给我,我拿到这时间,在做一个调整,就可以得到精确的视频,这样最方便
    misdake
        31
    misdake  
       174 天前
    @qin20 可以试试拉 loglevel ,然后监听类似于 [mov,mp4,m4a,3gp,3g2,mj2 @ 000001f53b1ae8c0] stream 0, sample 450, dts 14933333 的数据,就能知道他在复制那些帧
    misdake
        32
    misdake  
       174 天前
    “重新编码遇到几个 G 的视频文件时,切割两秒的视频,就需要处理好几分钟,在批量切割的需求下(几百个视频片段),完全达不到性能要求”

    这个问题我没有遇到哦,我从一个 13 分钟长、平均码率 4.5Mbps 的视频中,截取比较靠后的 1 秒钟
    ffmpeg -ss 700 -to 701 -i a.mp4 out.mp4
    用 measure-command 测了一下运行时间不到 0.7 秒
    hazardous
        33
    hazardous  
       174 天前
    我的思路是:其实只有开头和结尾与关键帧不对齐的那两个 gop 是需要重编码的,而中间的绝大部分直接 copy 就可以。具体就是把视频分成三段,第一段是开始点到下一个关键帧,第三段是结束点前的一个关键帧到结束点,这两段重编码,中间的 copy ,最后三段合并。
    Natsumoku
        34
    Natsumoku  
       174 天前 via Android
    @coderluan lossless-cut 绕不过关键帧的限制,在 UI 上把时间轴放大是可以看到一个个关键帧的,输出的时候不管怎么设置都没法精确到两个关键帧之间。
    事实上有一个官方 issue 跟踪的就是如何解决这个问题: https://github.com/mifi/lossless-cut/issues/126
    qin20
        35
    qin20  
    OP
       174 天前
    @misdake 我把日志全都拉出来了,loglevel 全开,然而没有找到,太多了,所以想问问有没有知道是 log 里面的哪个信息,能拿到实际的切割的关键帧。。。
    qin20
        36
    qin20  
    OP
       174 天前
    @misdake 你测试的命令只有 0.7 秒确实比我电脑上运行的快,但是你这个只是使用的 copy 模式,得出的开始时间和结束时间就是刚开始说的那个不准确问题,我说的久的情况,是为了得到准确的开始和结束,而用的重新编码模式,要加上输出命令 ffmpeg -i a.mp4 -ss 700 -to 701 -c:v libx264 -c:a copy out.mp4 ,这样就会比较慢
    qin20
        37
    qin20  
    OP
       174 天前
    @Natsumoku 你了解剪映之类的软件是如何切割的吗?他们切割起来好像挺准确的啊,而且他们的视频感觉导入的速度非常快,一下子就解码然后放到视频轨道上了
    qin20
        38
    qin20  
    OP
       174 天前
    @hazardous 思路不错,如何取得这三段视频的信息呢,有没有好的思路
    xuhaoyangx
        39
    xuhaoyangx  
       174 天前
    copy 不可能准确切的,比如 26x 的预测帧怎么切,切不了,只能找关键帧。

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

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

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

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

    https://www.v2ex.com/t/808095
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4317 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 02:10 · PVG 10:10 · LAX 19:10 · JFK 22:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.