使用 ffmpeg 根据视频剪辑创建合辑剪辑

使用 ffmpeg 根据视频剪辑创建合辑剪辑

假设我有input.mp412 分钟长的视频。我想从输入中取出 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")

相关内容