假设我有input.mp4
12 分钟长的视频。我想从输入中取出 4 次 2.5 秒的视频到一个文件output.webm
。
因此,第一个 2.5 秒在 2.5 分钟标记处,第二个 2.5 秒在 5 分钟标记处,然后是相同的 7.5 秒标记处,最后是 10 分钟标记处的最后 2.5 秒。然后将其全部输出到同一个output.webm
文件。
如果可能的话,那么是否也有可能,以便如果input.mp4
长度发生变化,我会从长度基础中获得这 4 个部分,而不是始终是标准的 2.5、5、7.5 和 10?
答案1
使用 ffmpeg 根据视频剪辑创建合辑剪辑
请注意以下内容基于ptQa 的回答经过修改,所以如果这个答案有用,它值得赞成。
以下fffmpeg
咒语将为您提供 10 秒的 VP8 webm,并在 2.5、5、7.5 和 10 分钟处进行剪辑:
ffmpeg -hide_banner -i "input.mp4" -filter_complex " \
[0:v]trim=start=150:duration=2.5,setpts=PTS-STARTPTS[av];\
[0:a]atrim=start=150:duration=2.5,asetpts=PTS-STARTPTS[aa];\
[0:v]trim=start=300:duration=2.5,setpts=PTS-STARTPTS[bv];\
[0:a]atrim=start=300:duration=2.5,asetpts=PTS-STARTPTS[ba];\
[0:v]trim=start=450:duration=2.5,setpts=PTS-STARTPTS[cv];\
[0:a]atrim=start=450:duration=2.5,asetpts=PTS-STARTPTS[ca];\
[0:v]trim=start=900:duration=2.5,setpts=PTS-STARTPTS[dv];\
[0:a]atrim=start=900:duration=2.5,asetpts=PTS-STARTPTS[da];\
[av][aa][bv][ba][cv][ca][dv][da]concat=n=4:v=1:a=1[outv][outa]" \
-map [outv] -map [outa] -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis out.webm
编码选项是libvpx
来自 FFmpeg wiki 的编码指南。
奖励:根据输入长度进行可变修剪
这可以通过一些脚本来实现,用来ffprobe
获取持续时间,例如:
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
这将返回以秒为单位的剪辑长度,可以除以 5,然后乘以 1、2、3 和 4,分别得到四个等距剪辑的位置。
性能说明
虽然以这种方式生成剪辑非常巧妙,因为它可以精确地从输入视频中切除子剪辑,但它慢的。
更快捷的方法是使用输入搜索并进行流复制以制作子剪辑,然后将它们连接在一起并进行编码。
但是,如果您需要精确的持续时间,或者性能不是问题,那么使用一堆trim
过滤器就可以完成工作。
用于生成剪辑的脚本,trim
适用于任意输入长度和任意数量的剪辑/子剪辑
#!/bin/bash
# clip.sh - create a clip of short sequences of a passed-in media file
# takes one argument, the input file
# can customise number of cuts, total length of clip
# clips will be evenly-spaced in input file
SUBCLIPS=4 # number of clips to make up clip
OUTPUTLENGTH=10 # output file length, in seconds
OUTPUTFILE=out.webm # name
cliplength=$(echo "$OUTPUTLENGTH/$SUBCLIPS"| bc -l)
if [ -e "$1" ]; then
videofile="$1"
else
echo "file not found: $1"
exit 1
fi
ffmpeg_preamble="ffmpeg -hide_banner -i '$videofile' -filter_complex \""
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$videofile")
cutfilters=""
concatfilter=""
for cutno in $( seq 1 $SUBCLIPS ); do
cut=$(echo "(1 / ($SUBCLIPS+1)) * $cutno" | bc -l)
let cutchar=$cutno+96 # ascii value so we get max 26 cuts rather than 10
cutletter=$(printf "\x$(printf %x $cutchar)")
cutpos=$(echo "$duration * $cut" |bc)
cutfilters=$(printf "%s[0:v]trim=start=%f:duration=%f,setpts=PTS-STARTPTS[%sv];" "$cutfilters" "$cutpos" "$cliplength" "$cutletter")
cutfilters=$(printf "%s[0:a]atrim=start=%f:duration=%f,asetpts=PTS-STARTPTS[%sa];" "$cutfilters" "$cutpos" "$cliplength" "$cutletter")
concatfilter=$(printf "%s[%sv][%sa]" "$concatfilter" "$cutletter" "$cutletter")
done
# concat the cuts together
concatfilter=$(printf "%sconcat=n=%s:v=1:a=1[outv][outa]" "$concatfilter" "$SUBCLIPS")
ffmpeg_webmopts=" -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis "
ffmpeg_postscript="\" -map [outv] -map [outa] $ffmpeg_webmopts '$OUTPUTFILE'"
# Chance to cancel before we start
printf "***(hit ctrl+c to cancel)***\n\nExecuting: %s%s%s%s\n" "$ffmpeg_preamble" "$cutfilters" "$concatfilter" "$ffmpeg_postscript"
sleep 3
eval $(echo "$ffmpeg_preamble $cutfilters $concatfilter $ffmpeg_postscript")