如何使用ffpmeg在关键帧之间剪切视频而无需重新编码整个视频?

14

我想在视频的任何时间戳处剪切视频的开头,需要非常准确,因此最近的关键帧不够好。

此外,这些视频相当长 - 一小时或更长时间 - 因此如果可能的话,我希望避免完全重新编码,或者只重新编码总持续时间的最小部分。因此,我想最大限度地利用-vcodec copy

如何使用ffmpeg实现这一点?

注意:请参见场景和我自己粗略的一个可能解决方案的想法如下。


场景:

  • 原始视频
    • 长度为1:00:00
    • 每10秒有一帧关键帧
  • 期望的切割:
    • 从0:01:35一直到结尾
  • 尝试 #1:
    • 使用-ss 0:01:35 -i blah.mp4 -vcodec copy,结果是一个文件,其中:
    • 音频从0:01:30开始
    • 视频也从0:01:30开始
    • 这会让音频和视频都提前了
  • 使用-i blah.mp4 -ss 0:01:35 -vcodec copy,结果是一个文件,其中:
    • 音频从0:01:35开始,
    • 但视频在前5秒内是空白/黑色,
      • 直到0:01:40,视频才开始
    • 这会让音频准时开始,但视频开始得太晚了

粗略的想法

  • (1)剪切0:01:30到0:01:40
  • 重新编码并生成新的关键帧,其中包括目标时间0:01:35处的一个关键帧。
  • 然后将其剪切,获取从0:01:35到0:01:40的5秒钟。
  • (2) 从0:01:40开始一直剪切到结束
    • 无需重新编码,使用-vcodec copy
  • (3) 使用ffmpeg concat连接第一个短视频片段(5秒钟)和第二个长视频片段。
  • 我知道/可以推断出(2)和(3)的命令,但是不确定需要哪些命令来完成(1)。


    请注意,由于目标视频播放器以及我在后处理此视频中所采取的后续步骤,对于mp4容器使用编辑列表不是我的选择。 - bguiz
    这可以帮助 - https://dev59.com/9mMl5IYBdhLWcg3wZGLo - Vijayendra
    要指定关键帧间隔(GOP大小),请使用“-g”标志。https://trac.ffmpeg.org/wiki/EncodingForStreamingSites - francis duvivier
    2个回答

    19

    列出关键帧的时间戳:

    ffprobe -v error -select_streams v:0 -skip_frame nokey -show_entries frame=pkt_pts_time -of csv=p=0 input.mp4
    

    它会输出类似于:

    0.000000
    2.502000
    3.795000
    6.131000
    10.344000
    12.554000
    16.266000
    ...
    

    假设您想删除时间戳从0到5,然后进行流拷贝。最接近的关键帧是6.131。

    重新编码5到6.131。确保输入和输出匹配属性和格式。对于MP4,默认设置应该可以胜任大部分工作,假设为H.264 / AAC,但您可能需要手动匹配配置文件

    ffmpeg -i input.mp4 -ss 5 -to 6.131 trimmed.mp4
    

    创建 input.txt 文件以供concat分离器使用:

    file 'trimmed.mp4'
    file 'input.mp4'
    inpoint 6.131
    

    连接:

    ffmpeg -f concat -i input.mp4 -c copy output.mp4
    

    1
    这看起来像是符合我的要求的东西。我按照你回答中的链接匹配了一下配置文件,但是不知道如何使用它。如果要输出修剪后的文件,你会用什么样的 ffmpeg 命令呢?类似于 -v:profile copy 这样的吗? - bguiz
    1
    @bguiz 使用ffmpeg查看输入并查找“Video: h264(High)”。 ()中可能包含Baseline或Main而不是High。 如果是这种情况,请添加-profile:v main-profile:v baseline。 如果是High,则无需执行任何其他操作。 或者使用ffprobe查找配置文件:ffprobe -v error -select_streams v -show_entries stream = profile -of csv = p = 0 input.mp4。 并非所有格式都具有配置文件。 - llogan
    ffmpeg -i input.mp4 -ss 5.0 -to <last_time> output.mp4 相比,有什么区别? - Andry
    2
    最终的命令行中input.mp4应该改为input.txt吗? - jhnc
    3
    我知道问题只涉及视频,但我也想要音频,并且我在按照说明操作时遇到了困难,因为我总是在第一次重新编码后出现了损坏的音频。原因是我也重新编码了音频,将其格式更改为与原始格式不兼容的格式。在重新编码第一部分时添加“-c:a copy”解决了这个问题。 - flagg19

    3

    尝试

    ffmpeg -i src.mp4 -vcodec copy -reset_timestamps 1 -map 0 out.mp4
    

    或者
    ffmpeg -i src.mp4 -vcodec copy -reset_timestamps 1 -map 0 src_.m3u8
    

    生成HLS播放列表


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接