如何使用FFMPEG分割视频,以便每个块以关键帧开始?

sbo*_*sse 33 video split ffmpeg keyframe

我们需要将大型实时WMV视频源分成大小相同的小块.我们创建了一个可以正常工作的脚本,除了一件事:视频块不是以关键帧开始的,所以当播放大多数视频块时,它们不显示任何图像,直到原始视频的关键帧最终到达.

有没有办法让ffmpeg让输出视频以关键帧开始?

以下是我们的命令行现在的样子:

ffmpeg.exe -i "C:\test.wmv" -ss 00:00:00 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"
ffmpeg.exe -i "C:\test.wmv" -ss 00:00:05 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0001.wmv"
Run Code Online (Sandbox Code Playgroud)

等等...

Tim*_*ull 29

FFMPEG的最新版本包括一个新的选项"段",它完全符合我的需要.

ffmpeg -i INPUT.mp4 -acodec copy -f segment -vcodec copy -reset_timestamps 1 -map 0 OUTPUT%d.mp4

这将生成一系列编号的输出文件,这些文件根据关键帧分割成段.在我自己的测试中,它运行良好,虽然我没有在超过几分钟的时间内使用它,而且只使用MP4格式.

  • 更多示例:https://www.ffmpeg.org/ffmpeg-formats.html#Examples-5 (4认同)

Sop*_*rez 29

正如官方FFMPEG文档中所述,我-ss timestart 之前 指定它更好-i input_file.ext,因为它将生成的视频的开头设置(或者我理解)到指定时间戳之前找到最近的关键帧.

将您的示例更改为:

ffmpeg.exe -ss 00:00:00 -i "C:\test.wmv" -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"
Run Code Online (Sandbox Code Playgroud)

我已经测试了这个方法.flv.mp4文件.

  • 我认为这是不正确的。如果您在“寻求”页面向下滚动,它会专门针对“编解码器复制”说:“使用 -ss 作为输入选项和 -c:v 复制可能不准确,因为 ffmpeg 被迫仅在 i 帧上使用/分割。不过,如果可能的话,它会将流的开始时间调整为负值以对此进行补偿。基本上,如果您指定“第二个 157”并且直到第二个 159 为止没有关键帧,它将在开始时包含两秒的音频(没有视频),然后将从第一个关键帧开始。因此,在分割和进行编解码器复制时要小心。 (3认同)
  • 虽然这有点弄乱了视频的持续时间,例如,即使我指定视频持续 5 秒,我得到的视频也显示为 8 秒长。视频确实在 5 秒时结束,但在所有媒体播放器中,它显示为 8 秒长。有任何想法吗? (2认同)

sbo*_*sse 19

这是我可以开始工作的解决方案:

正如av501和d33pika所建议的那样,我使用ffprobe来找到关键帧的位置.因为ffprobe非常冗长,并且可能需要几秒甚至几分钟才能输出所有关键帧,并且无法从长视频中调整我们想要的帧范围,所以我将进入5个步骤:

  1. 从原始文件导出视频块,大约是所需块大小的两倍.

    ffmpeg -i source.wmv -ss 00:00:00 -t 00:00:06 -acodec copy -vcodec copy -async 1 -y  0001.wmv
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用ffprobe查找关键帧的位置.在所需的块大小后选择最接近的关键帧.

    ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从ffprobe的输出中获取该pkt_dts_time关键帧之前的帧.

  4. ffmpeg在步骤1的导出块上,指定相同的输入和输出文件,并指定-ss 00:00:00-t[在步骤3中找到的值].

    ffmpeg -i 0001.wmv -ss 00:00:00 -t 00:00:03.1350000 -acodec copy -vcodec copy -async 1 -y 0001.wmv
    
    Run Code Online (Sandbox Code Playgroud)
  5. 使用-ss[在迭代中在步骤3中找到的值的累积总和] 重新开始步骤1 .

以这种方式进行,我能够以一种高效且稳健的方式在关键帧处分割视频.


小智 12

使用更新的ffmpeg构建,可以通过使用ffprobe和ffmpeg段muxer来实现.

1.使用ffprobe和awk识别关键帧尽可能接近所需的块长度.

ffprobe -show_frames -select_streams v:0 -print_format csv **[SOURCE_VIDEO]** 2>&1 | grep -n frame,video,1 | awk 'BEGIN { FS="," } { print $1 " " $5 }' | sed 's/:frame//g' | awk 'BEGIN { previous=0; frameIdx=0; size=0; } { split($2,time,"."); current=time[1]; if (current-previous >= **[DURATION_IN_SECONDS]**){ a[frameIdx]=$1; frameIdx++; size++; previous=current;} } END { str=a[0]; for(i=1;i<size;i++) { str = str "," a[i]; } print str;}'
Run Code Online (Sandbox Code Playgroud)

哪里

  • [SOURCE_VIDEO] =要分段的视频的路径
  • [DURATION_IN_SECONDS] =所需的段长度,以秒为单位

输出是以逗号分隔的关键帧字符串.

2.使用上面的关键帧输出作为ffmpeg的输入.

ffmpeg -i [SOURCE_VIDEO] -codec copy -map 0 -f segment -segment_frames [OUTPUT_OF_STEP_1] [SEGMENT_PREFIX] _%03d.[SOURCE_VIDEO_EXTENSION]

哪里

  • [SOURCE_VIDEO] =要分段的视频的路径
  • [OUTPUT_OF_STEP_1] =以逗号分隔的关键帧字符串
  • [SEGMENT_PREFIX] =段输出的名称
  • [SOURCE_VIDEO_EXTENSION] =源视频的扩展名(例如,mp4,mkv)


d33*_*ika 6

使用ffprobe -show_frames -pretty <stream>找出关键帧.

  • @sboisse,`-read_intervals` 现在允许您指定要探测的时间或数据包间隔。例如`ffprobe -show_frames -read_intervals 10%+20,01:30%01:45 -i &lt;file&gt;` 探测 10-30 秒,然后是 1:30-1:45。 (2认同)