Ich versuche, mit FFmpeg einige Abschnitte eines Videos zu löschen.
Stellen Sie sich beispielsweise vor, Sie hätten eine Fernsehsendung aufgenommen und wollten die Werbespots herausschneiden. Mit einem GUI-Videoeditor ist das ganz einfach: Sie markieren einfach den Anfang und das Ende jedes zu entfernenden Clips und wählen „Löschen“. Ich versuche, dasselbe über die Befehlszeile mit FFmpeg zu tun.
Ich weiß, wie man ein einzelnes Segment in einneuVideo wie folgt:
ffmpeg -i input.avi -ss 00:00:20 -t 00:00:05 -map 0 -codec copy output.avi
Dadurch wird ein fünfsekündiger Clip geschnitten und als neue Videodatei gespeichert. Wie kann ich jedoch das Gegenteil tun und das gesamte Video speichern?ohneder angegebene Clip und wie kann ich mehrere zu entfernende Clips angeben?
Wenn mein Video beispielsweise durch ABCDEFG dargestellt werden könnte, möchte ich ein neues erstellen, das aus ACDFG besteht.
Antwort1
Nun, Sie können immer noch dietrim
Filter dafür. Hier ist ein Beispiel. Nehmen wir an, Sie möchten drei Segmente am Anfang und Ende des Videos sowie in der Mitte herausschneiden:
ffmpeg -i in.ts -filter_complex \
"[0:v]trim=duration=30[a]; \
[0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[b]; \
[a][b]concat[c]; \
[0:v]trim=start=80,setpts=PTS-STARTPTS[d]; \
[c][d]concat[out1]" -map [out1] out.ts
out1
Was habe ich hier gemacht? Ich habe zuerst 30 Sekunden, 40-50 Sekunden und 80 Sekunden bis zum Ende gekürzt und sie dann mit dem Stream kombiniert.concat
Filtern, 30–40 Sek. (10 Sek.) und 50–80 Sek. (30 Sek.) verbleiben.
Zu Setpts: Wir brauchen das, weil Trim die Bildanzeigezeit nicht ändert und wenn wir 10 Sekunden ausschneiden, sieht der Decoderzähler für diese 10 Sekunden keine Frames.
Wenn Sie auch Audio haben möchten, müssen Sie dasselbe für Audiostreams tun. Der Befehl sollte also lauten:
ffmpeg -i utv.ts -filter_complex \
"[0:v]trim=duration=30[av];[0:a]atrim=duration=30[aa];\
[0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[bv];\
[0:a]atrim=start=40:end=50,asetpts=PTS-STARTPTS[ba];\
[av][bv]concat[cv];[aa][ba]concat=v=0:a=1[ca];\
[0:v]trim=start=80,setpts=PTS-STARTPTS[dv];\
[0:a]atrim=start=80,asetpts=PTS-STARTPTS[da];\
[cv][dv]concat[outv];[ca][da]concat=v=0:a=1[outa]" -map [outv] -map [outa] out.ts
Antwort2
Ich bekomme die Lösung von ptQa nie zum Laufen, hauptsächlich, weil ich nie herausfinden kann, was die Fehler der Filter bedeuten oder wie man sie behebt. Meine Lösung scheint etwas umständlicher, weil sie ein Chaos hinterlassen kann, aber wenn man sie in ein Skript einfügt, kann die Bereinigung automatisiert werden. Mir gefällt dieser Ansatz auch, weil man, wenn bei Schritt 4 etwas schief geht, am Ende die Schritte 1 bis 3 abgeschlossen hat, sodass die Behebung von Fehlern etwas effizienter ist.
Die grundlegende Strategie besteht darin -t
, -ss
von jedem gewünschten Segment Videos zu erhalten und dann alle Teile für die endgültige Version zusammenzufügen.
Angenommen, Sie haben 6 Segmente ABCDEF mit einer Länge von jeweils 5 Sekunden und möchten A (0-5 Sekunden), C (10-15 Sekunden) und E (20-25 Sekunden). Dann würden Sie Folgendes tun:
ffmpeg -i abcdef.tvshow -t 5 a.tvshow -ss 10 -t 5 c.tvshow -ss 20 -t 5 e.tvshow
oder
ffmpeg -i abcdef.tvshow -t 0:00:05 a.tvshow -ss 0:00:10 -t 0:00:05 c.tvshow -ss 0:00:20 -t 0:00:05 e.tvshow
Dadurch werden die Dateien a.tvshow, c.tvshow und e.tvshow erstellt. Die Option -t
gibt an, wie lang jeder Clip ist. Wenn c also 30 Sekunden lang ist, können Sie 30 oder 0:00:30 einfügen. Die -ss
Option gibt an, wie weit im Quellvideo gesprungen werden soll. Sie ist also immer relativ zum Anfang der Datei.
Wenn Sie dann einen Haufen Videodateien haben, erstelle ich eine Datei ace-files.txt
wie diese:
file 'a.tvshow'
file 'c.tvshow'
file 'e.tvshow'
Beachten Sie das „File“ am Anfang und den maskierten Dateinamen danach.
Dann der Befehl:
ffmpeg -f concat -i ace-files.txt -c copy ace.tvshow
Dadurch werden alle Dateien zusammengefügt abe-files.txt
, ihre Audio- und Video-Codecs kopiert und eine Datei erstellt ace.tvshow
, die nur aus den Abschnitten a, c und e bestehen sollte. Denken Sie dann daran ace-files.txt
, a.tvshow
, , c.tvshow
und zu löschen e.tvshow
.
Haftungsausschluss: Ich habe keine Ahnung, wie (in)effizient das im Vergleich zu den anderen Ansätzen ist, ffmpeg
aber für meine Zwecke funktioniert es besser. Hoffe, es hilft jemandem.
Antwort3
Für diejenigen, die Schwierigkeiten haben, dem Ansatz von ptQa zu folgen, gibt es eine etwas einfachere Möglichkeit. Anstatt die einzelnen Schritte zu verketten, führen Sie sie einfach alle am Ende aus.
Definieren Sie für jeden Eingang ein A/V-Paar:
//Input1:
[0:v]trim=start=10:end=20,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10:end=20,asetpts=PTS-STARTPTS[0a];
//Input2:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[1a];
//Input3:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[2v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[2a];
Definieren Sie so viele Paare wie Sie benötigen und verketten Sie sie dann alle in einem Durchgang, wobei n der Gesamtzahl der Eingaben entspricht.
[0v][0a][1v][1a][2v][2a]concat=n=3:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4
Dies lässt sich leicht in einer Schleife konstruieren.
Ein vollständiger Befehl, der zwei Eingaben verwendet, könnte folgendermaßen aussehen:
ffmpeg -i in.mp4 -filter_complex
[0:v]trim=start=10.0:end=15.0,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10.0:end=15.0,asetpts=PTS-STARTPTS[0a];
[0:v]trim=start=65.0:end=70.0,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=65.0:end=70.0,asetpts=PTS-STARTPTS[1a];[0v][0a][1v]
[1a]concat=n=2:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4
Antwort4
Ich habe ein Skript erstellt, um die Bearbeitung aufgezeichneter Fernsehsendungen zu beschleunigen. Das Skript fragt Sie nach den Anfangs- und Endzeiten der Segmente, die Sie behalten möchten, und teilt sie in Dateien auf. Es bietet Ihnen Optionen, Sie können:
- Nehmen Sie ein oder mehrere Segmente.
- Sie können die Segmente zu einer Ergebnisdatei kombinieren.
- Nach dem Beitritt können Sie die Teildateien behalten oder löschen.
- Sie können die Originaldatei behalten oder durch Ihre neue Datei ersetzen.
Lass mich wissen was du denkst.
#!/bin/bash
/bin/date >>segmenter.log
function segment (){
while true; do
echo "Would you like to cut out a segment ?"
echo -e "1) Yes\n2) No\n3) Quit"
read CHOICE
if [ "$CHOICE" == "3" ]; then
exit
elif [ "$CHOICE" == "2" ]; then
clear
break
elif [ "$CHOICE" == "1" ]; then
clear
((segments++))
echo "What time does segment $segments start ?"
read SEGSTART
clear
echo -e "Segment $segments start set to $SEGSTART\n"
echo "What time does segment $segments end ?"
read SEGEND
clear
echo -e "Segment $segments end set to $SEGEND\n"
break
else
clear
echo -e "Bad option"
segment "$segments"
fi
done
if [ "$CHOICE" == "1" ]; then
echo "Cutting file $file video segment $segments starting at $SEGSTART and ending at $SEGEND"
ffmpeg -i "$file" -ss $SEGSTART -to $SEGEND -map 0:0 -map 0:1 -c:a copy -c:v copy "$filename-part$segments.$extension" >> segmenter.log 2>&1
clear
echo -e "Cut file $filename-part$segments.$extension starting at $SEGSTART and ending at $SEGEND\n"
segment "$segments"
fi
}
file="$1"
filename="${file%.*}"
extension="${file##*.}"
clear
segments=0
segment "$segments"
clear
if (("$segments"==1)); then
mv $filename"-part1."$extension "$filename-segmented.$extension"
elif (("$segments">1)); then
echo "Would you like to join the segments into one file ?"
OPTIONS="Yes No Quit"
select opt in $OPTIONS; do
clear
if [ "$opt" == "Quit" ]; then
exit
elif [ "$opt" == "Yes" ]; then
clear
echo "Joining segments"
ffmpeg -f concat -i <(for f in $filename"-part"*$extension; do echo "file '$(pwd)/$f'"; done) -c:a copy -c:v copy "$filename-segmented.$extension" >> segmenter.log 2>&1
clear
echo "Would you like to delete the part files ?"
select opt in $OPTIONS; do
clear
if [ "$opt" == "Quit" ]; then
exit
elif [ "$opt" == "Yes" ]; then
for f in $filename"-part"*$extension; do rm $f; done
break
elif [ "$opt" == "No" ]; then
break
else
clear
echo -e "Bad option\n"
fi
done
break
clear
elif [ "$opt" == "No" ]; then
exit
else
clear
echo -e "Bad option\n"
fi
done
fi
echo "Would you like to replace the original file with the result of your changes ?"
OPTIONS="Yes No Quit"
select opt in $OPTIONS; do
clear
if [ "$opt" == "Quit" ]; then
exit
elif [ "$opt" == "Yes" ]; then
rm $file
mv "$filename-segmented.$extension" $file
break
elif [ "$opt" == "No" ]; then
break
else
clear
echo -e "Bad option\n"
fi
done