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

时间:2012-12-22 17:47:51

标签: video ffmpeg split 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"

依旧......

6 个答案:

答案 0 :(得分:29)

FFMPEG的最新版本包括一个新选项“细分”,它完全符合我的需要。

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

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

答案 1 :(得分:28)

正如官方FFMPEG Docs所述,我最好在 -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"

我已经在.flv.mp4文件上测试了此方法。

答案 2 :(得分: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
    
  2. 使用ffprobe查找关键帧的位置。在所需的块大小后选择最接近的关键帧。

    ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv
    
  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
    
  5. 在第1步重新启动,使用-ss [迭代后在步骤3中找到的累计值总和]。

  6. 按照这种方式,我能够以一种高效而强大的方式在关键帧上分割视频。

答案 3 :(得分: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;}'

其中

  • [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)

答案 4 :(得分:6)

使用ffprobe -show_frames -pretty <stream>识别关键帧。

答案 5 :(得分:3)

如果你愿意做一些脚本并希望我以特定的间隔进行帧化,那么一种方法就是

  1. 运行ffprobe并从输出中收集I帧的位置

    ffprobe -show_streams

  2. 使用相同的脚本运行一系列-ss -t命令以获取所需的块。

  3. 然后你可以让你的脚本决定最小帧数[假设彼此10帧内有两张I图片,你真的不想在那里分块]。

    另一种方法是使用gstreamer的multisfilesink并将模式设置为关键帧[然而,这将在每个关键帧处进行块化,因此可能并不理想]