如何使用ffmpeg提取时间准确的视频片段?

时间:2014-01-29 01:22:00

标签: video ffmpeg video-editing

这不是一个特别新的问题区域,但我已经尝试过没有太多运气的建议。所以,我的故事:

我有一段15秒的直--from-the-camera.mov视频,其中我想提取一个特定的块,我可以通过开始时间和停止时间来识别,以秒为单位。我开始尝试做我称之为“复制提取”的事情:得到秒9到12,

ffmpeg -i test.mov -vcodec copy -acodec copy -ss 9 -to 12 test-copy.mov

这是一个不错的开始,但是在剪辑的开头和结尾有一些黑框,我不能拥有它 - 它必须是原始的干净编辑。所以,我尝试将原始内容重新编码为一个新的,修剪过的剪辑:

ffmpeg -i test.mov -ss 00:00:09 -t 00:00:03 test-out.mov

这样更好,但并不完全:剪辑开头不再有任何黑框,但它们最后仍然存在。

经过一番浏览和阅读后,我怀疑问题是由于原始视频中缺少关键帧,ffmpeg无法找到合适的点。因此,我将原始视频重新编码为(可能)以几种不同的方式添加关键帧。由于我希望能够在一秒钟的边界(“从9秒到12秒”)选择视频,我尝试在网络上复制各种建议,

ffmpeg -i test.mov -force_key_frames "expr:gte(t, n_forced)" test-forced.mp4

ffmpeg -i test.mov -g 1 test-g-inserted.mp4

(我根据一些关于支持关键帧搜索所需的mp4容器的评论来构建这些mp4,但我老实说这只是在这里进行攻击。)然后我像以前一样尝试了提取,但是这些新视频可能是现在有关键帧。没有运气 - 两者似乎差不多;开始没问题,但最后仍有黑框。 (FWIW,test-forced.mp4和test-g-inserted.mp4也有尾随黑帧。)

所以:我仍然被困住,并且不想。关于我做错了什么的任何见解?我觉得我很亲密,但我真的需要摆脱那些拖尾的黑框....

5 个答案:

答案 0 :(得分:31)

好的,首先假设您知道开始和停止持续时间;我们将在此期间添加关键帧。

ffmpeg -i a.mp4 -force_key_frames 00:00:09,00:00:12 out.mp4

大多数情况下,您可以完美地直接剪切视频,但在您的情况下,它对您没有帮助;所以我们通过上面的命令照顾它。这里要小心不要添加太多的关键帧,因为根据Ffmpeg Docs进行编码时可能会出现问题。

现在,您可以再次尝试从特定时间剪切视频。

ffmpeg -ss 00:00:09 -i out.mp4 -t 00:00:03 -vcodec copy -acodec copy -y final.mp4

这将解决问题,因为我们在剪切的起点和终点手动添加了关键帧。它对我有用。

干杯:)

答案 1 :(得分:9)

我认为问题和其他答案存在的问题是他们在输出文件中使用-ss作为选项,而不是在输入文件上。大多数ffmpeg选项不是全局的,而是仅适用于它们之前的文件。通常情况下,选项需要去的地方并不明显,因此有时需要进行反复试验。

正确使用,在他们要申请的输入文件之前,-ss-t对我来说很好。当在输出中包含音频时,我必须使用-shortest作为输出文件的选项,或者我将获得2分钟的音频和2秒的视频。

ffmpeg版本N-67413-g2a88c74(基本上是2014年12月14日的git源代码)

这是我最近制作剪辑的命令行。 (实际上调整为一个更好的例子,因为我为此留下了音频,并没有慢慢来。)

ffmpeg -ss 120.2 -t 0.75 -i ../mcdeint.60p.lossless264.slow.mkv -c:a libopus -shortest -aspect 16:9 -preset veryslow -x264-params nr=250:ref=6 -crf 22 -movflags +faststart clip2.mkv

使用-c:a copy(来源有AC3音频),播放启动与mplayer一起使用。它可能从包含开头的音频帧的开头抓取音频,然后必须在容器中使用a / v偏移。在启动时,音频通过该偏移量在视频之前需要几分之一秒,直到那时视频以非常低的FPS播放。所以我对音频进行了编码。 opus和pcm_s16le都不能进入mp4,所以我在这个例子中使用了一个mkv容器。

源是一个无损x264编码(-qp 0)来自非常慢的yadif = 3:1的输出,mcdeint = 3:1:10(来自NTSC DVD的一些BFF隔行扫描视频,可能来自DV摄像机)。这不是所有I帧,而是具有正常关键帧间隔的P帧。

调整-ss 0.2秒完全符合我的预期,因此ffm​​peg必须处理解码到所需的点。这不仅仅是我想要的I帧的巧合。也许-accurate_seek是默认值?我也得到了与使用ffvhuff无损源作为输入时相同的结果(逐字节相同的gif输出)。 (但它运行得更快,因为它不必解码到请求的点。)

另一个可能相关的选项是-seek2any,但它表示“在解复用程序级别寻找非关键帧”,听起来它会让你以一种会产生乱码输出的方式寻找。 (即开始解码而不实际生成当前帧所需的引用,只需使用全灰色?)

我没有尝试使用-c:v copy,因为我正在剪切一个非常短的剪辑循环,所以我知道我不需要I帧。

这是我实际使用的命令行,用于创建一个没有声音的短缓冲剪辑。

ffmpeg -ss 120.2 -t 0.75 -i ../vid.yadif3.1,mcdeint3.1.10.ffvhuff.mkv -an -shortest -aspect 16:9 -c:v libx264 -preset veryslow -x264-params nr=250:ref=6 -crf 22 -filter:v "setpts=3.0*PTS" -movflags +faststart -r 20 clip.mp4

请注意,-r 20很重要,因为与mkv不同,ffmpeg的MP4输出仅为恒定帧速率(编辑:因为-vsync vfr不是mp4复用器的默认值)。在没有说明不同的情况下,它会设置输出FPS =输入FPS,并在需要时重复帧以实现这一点。 x264和动画gif(带透明度)可以非常有效地编码重复帧,但它仍然很傻。

在烹饪之前,我做了两个步骤,一个输出到mkv,然后ffmpeg -i clip.mkv -c:v copy -movflags +faststart -r 20 clip.mp4重新启动。顺便说一句,有可能在没有xcoding的情况下更改视频的fps,而不是使用ffmpeg。 https://superuser.com/questions/740196/reducing-video-size-with-avconv-why-does-the-size-increase。但无论如何,ffmpeg在制作mkv时只向libx264发送了45帧,尽管它认为它以60fps制作了2.2秒的视频。不要将ffmpeg与mp4一起使用来处理变量FPS。

编辑:对于mkv输出,ffmpeg默认为-vsync vfr,但对于mp4则不然。使用-vsync vfr,ffmpeg可以将VFR写入mp4输出就好了。

再次gif输出,如果我决定不用HTML5视频(<video controls autoplay loop> <source src="clip.mp4" type="video/mp4"> </video>

ffmpeg -ss 120.2 -t 0.75 -i ../vid.yadif3.1,mcdeint3.1.10.ffvhuff.mkv -an -shortest -aspect 16:9 -filter:v "setpts=3.0*PTS,scale=854x480" -r 20 clip.gif

我不得不使用scale=,因为gif容器不存储宽高比,因此无法在播放时自动缩放。 (我的720x480像素16:9视频在播放时缩放到854x480。实际上应该是853.333,但是它会四舍五入,然后ffmpeg将853x480存储在mkv容器中,因此一直使用-aspect 16:9,所以我的mp4将会存储正确的宽高比[SAR 32:27 DAR 16:9],而不是[SAR 186:157 DAR 279:157]

答案 2 :(得分:5)

无需添加关键帧;正如彼得所说,这只是一个以正确的顺序获得选项的问题。但是,请参阅https://trac.ffmpeg.org/wiki/Seeking以获取有关如何正确执行该权利的最终官方指南。

答案 3 :(得分:1)

在树林里戳一头熟睡的熊‍♂️。我没有看到此线程中提到的-to标志,并且发现它比-t标志更有用。一个适合我的示例命令,

ffmpeg -y -i [INPUT.file] -ss 00:42:42 -to 00:84:84 -codec copy [OUTPUT.file]

答案 4 :(得分:0)

我也想知道。到目前为止,我已经无损地转换了我的视频,提取了一个片段,并在编辑后对它们进行了重新编码:

ffmpeg -i input -ss 9.48 -t 3.52 -c:v libx264 -crf 0 -g 1 -c:a copy TempIframe.mp4

但这是一个耗时且有损的过程...

我尝试过以下但没有成功:

ffmpeg -i source.mp4 -ss 1 -c:v copy -seek2any 1 -avoid_negative_ts 1 -safe 1 -c:a copy segment.mp4