Wie kann ich mit FFmpeg mehrere Segmente aus einem Video entfernen?

Wie kann ich mit FFmpeg mehrere Segmente aus einem Video entfernen?

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 dietrimFilter 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

out1Was 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.concatFiltern, 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, -ssvon 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 -tgibt an, wie lang jeder Clip ist. Wenn c also 30 Sekunden lang ist, können Sie 30 oder 0:00:30 einfügen. Die -ssOption 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.txtwie 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.tvshowund zu löschen e.tvshow.

Haftungsausschluss: Ich habe keine Ahnung, wie (in)effizient das im Vergleich zu den anderen Ansätzen ist, ffmpegaber 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

verwandte Informationen