Mit ffmpeg Video an I-Frames zerlegen

Ich möchte ein Video durch Schneiden zerlegen, sodass ich die einzelnen Szenen in separaten Dateien habe. Dabei ist es wichtig, dass jede Datei mit einem I-Frame (auch bekannt als Key-Frame) beginnt. Ich möchte also ffmpeg nötigen, nur bei I-Frames zu schneiden.

2017-06-02: Dies ist offenbar direkt möglich durch einen Befehl wie diesen:

ffmpeg -i input.mp4 -c copy -f segment -reset_timestamps 1 output_%04d.mp4

(gefunden auf stackoverflow)

Manuelles Vorgehen

Ich habe den hier beschriebenen Ansatz mit zwei Durchgängen verwendet.

Ich gehe davon aus, dass das Video mit einem encoder Komprimiert wurde, welcher automatisch I-Frames bei Szenenwechseln einfügt. Falls das nicht der Fall ist, kann das Video beispielsweise mit x264 neu komprimiert werden. Dabei sollte der Encoder angewiesen werden, möglichst nur bei Szenenwechseln I-Frames einzufügen. Normalerweise wird das Einfügen von zusätzlichen I-Frames erzwungen, um das Spulen im Video zu erleichtern. In den ffmpeg/x264-Optionen ist das der Parameter -g. Mit dem Constant Rate Factor 15 ist die Kompression für meine Zwecke ausreichend verlustarm.

ffmpeg -i input.mp4 -an -c:v libx264 -g 1500 -crf 15 recompressed.mp4

ffmpeg nummeriert ungern Frames. Meistens wird mit Zeitpunkten gearbeitet. Doch zusammen mit der Zählfunktion in grep kann man mit ffmpeg die Indizes der I-Frames finden. Dieser Befehl sammelt die Indizes der I-Frames in einer kommasepariereten Liste:

frames=$( ffprobe -select_streams v -show_frames -show_entries frame=pict_type -of csv recompressed.mp4 | grep -n I | cut -d ':' -f 1 | awk '{print $1-1}' | paste -s -d, )

Die Liste der Indizes von I-Frames kann durch den ffmpeg Segment Filter (empfohlen durch diesen Beitrag) verarbeitet werden.

ffmpeg -i recompressed.mp4 -c:v copy -f segment -segment_frames $frames part%03d.mp4

Dieser Befehl zerschneidet nun das Video anhand der vorbereiteten Liste von Indizes.

Es ist nicht unbedingt notwendig, das Video zu Beginn neu zu komprimieren. Es ist auch möglich, mit Hilfe eines ffmpeg Filters nach Szenenübergängen zu suchen. Dann ist jedoch nicht sichergestellt, dass der Schnitt an einem I-Frame erfolgt. Das bedeutet, dass die Ausgabe einzeln komprimiert werden muss, das kann jedoch auch mit einem verlustfreien Encoder geschehen, welcher nicht mit I-Frames arbeitet.