我们需要将大型实时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"
依旧......
答案 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个步骤:
从原始文件导出视频块,大约是所需块大小的两倍。
ffmpeg -i source.wmv -ss 00:00:00 -t 00:00:06 -acodec copy -vcodec copy -async 1 -y 0001.wmv
使用ffprobe查找关键帧的位置。在所需的块大小后选择最接近的关键帧。
ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv
从ffprobe的输出中获取该关键帧之前的帧的pkt_dts_time
。
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
在第1步重新启动,使用-ss
[迭代后在步骤3中找到的累计值总和]。
按照这种方式,我能够以一种高效而强大的方式在关键帧上分割视频。
答案 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;}'
其中
输出是以逗号分隔的关键帧字符串。
2.使用上面的关键帧输出作为ffmpeg的输入。
ffmpeg -i [SOURCE_VIDEO] -codec copy -map 0 -f segment -segment_frames [OUTPUT_OF_STEP_1] [SEGMENT_PREFIX] _%03d。 [SOURCE_VIDEO_EXTENSION] 强>
其中
答案 4 :(得分:6)
使用ffprobe -show_frames -pretty <stream>
识别关键帧。
答案 5 :(得分:3)
如果你愿意做一些脚本并希望我以特定的间隔进行帧化,那么一种方法就是
运行ffprobe并从输出中收集I帧的位置
ffprobe -show_streams
使用相同的脚本运行一系列-ss -t命令以获取所需的块。
然后你可以让你的脚本决定最小帧数[假设彼此10帧内有两张I图片,你真的不想在那里分块]。
另一种方法是使用gstreamer的multisfilesink并将模式设置为关键帧[然而,这将在每个关键帧处进行块化,因此可能并不理想]