Präzises Extrahieren von Frames alle N Sekunden mit FFmpeg

Präzises Extrahieren von Frames alle N Sekunden mit FFmpeg

Die Verwendung

Ich extrahiere Bilder aus Videos mithilfe von ffmpeg.

Ich lade alle 10 Sekunden (einschließlich) ein verkleinertes Bild hoch, das ich mit zu Montagen kombiniere imagemagick. Diese Montagen werden wiederum verwendet, um eine Vorschau des Videos anzuzeigen, wenn man den Scrubber in einem webbasierten Videoplayer bewegt. (Berechnen, welches Bild in der Montage angezeigt werden soll).

Der Befehl

Nach einigem Herumprobieren bin ich schließlich auf den folgenden Befehl gekommen, bei dem Geschwindigkeit vor Qualität steht:

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -r 0.1 \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12" \
    -color_range 2 \
    f%09d.jpg

Das scheint gut zu funktionieren. Die Aufnahmen sind hier und da um etwa ± 0,5 - 1 Sekunde abweichend, aber das ist verschmerzbar.

Das Problem

Das Problem besteht darin, dass ffmpegam Anfang des Videos ein zusätzliches Bild erzeugt wird. Beispiele für Dateien:

file             time
f000000001.jpg   00:00:00
f000000002.jpg   00:00:00
f000000003.jpg   00:00:10
f000000004.jpg   00:00:20
f000000005.jpg   00:00:30
...

Manchmal weichen die erste und die zweite Sekunde um einige Millisekunden voneinander ab.

So wie ich es (jetzt) ​​weiß, kann ich einfach das erste Bild löschen und mit dem Rest fortfahren, bin mir aber nicht sicher, warum das passiert und ob es ein Fehler oder etwas anderes ist.

Anders ausgedrückt: Ich muss wissen, ob der "Effekt" der ersten beiden Frameszuverlässigdamit ich es ffmpegauch in anderen Versionen löschen kann.

Da ich die Bilder verwende, um zu einem bestimmten Zeitpunkt einen 10-Sekunden-Schnappschuss aus dem Video anzuzeigenes ist 10 Sekunden auswenn ich das erste erzeugte Bild nicht lösche. Wenn es aus irgendeinem Grund dannnichtErstellen Sie am Anfang ein Duplikat, eine andere Version oder was auch immer. Das Löschen des ersten Bildes würde dasselbe Problem verursachen.

Montage

(Falls es von Interesse ist, die Montagen werden ungefähr so ​​erstellt):

montage -tile 5x -geometry +0+0 -background none [file1  - file50 ]  montage01.jpg
montage -tile 5x -geometry +0+0 -background none [file51 - file100]  montage02.jpg
...

Befehl, den ich jetzt basierend auf der Antwort (Shell) verwende:

# Set on call or global:
file_in=sample.mp4
pix_fmt=yuvj420p
sec_snap_interval=10
nr_start=1
pfx_out=snap


ffmpeg \
    -loglevel warning \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$file_in" \
    -pix_fmt "$pix_fmt" \
    -filter:v "
        scale_cuda=
            w = -1 :
            h = 100,
        thumbnail_cuda = 2,
        hwdownload,
        format = nv12,
        select = 'bitor(
            gte(t - prev_selected_t, $sec_snap_interval),
            isnan(prev_selected_t)
        )'
    " \
    -vsync passthrough \
    -color_range 2 \
    -start_number "$nr_start" \
    "$pfx_out%09d.jpg"

Antwort1

Durch die Verwendung -r 0.1wird die Ausgabebildrate auf 0,1 Hz eingestellt, es ist jedoch nicht garantiert, dass genau alle 10 Sekunden ein Bild vom Eingabevideo erhalten wird (ich bin nicht sicher, warum).

Eine Möglichkeit zur Lösung dieses Problems ist die VerwendungFilter auswählen.

Beispiel (ohne GPU-Beschleunigung):

ffmpeg -i input.mp4 -vf "select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" -vsync 0 f%09d.jpg

  • bitor(gte(t-prev_selected_t\,10)ist 1, wenn der Unterschied zwischen den „bestandenen“ Zeitstempeln größer als 10 Sekunden ist.
    Wenn der Ausdruck als ausgewertet wird 1, wird der Frame „ausgewählt“ und an die Ausgabe übergeben.
  • bitormit isnan(prev_selected_t)übergibt den ersten Frame, wobei prev_selected_t( NaNkein Wert hat).
  • -vsync 0wendet „Passthrough“ an – Jeder Frame wird mit seinem Zeitstempel vom Demuxer an den Muxer übergeben.

Hier ist ein Beispiel mit scale_cudaund thumbnail_cuda:

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12,select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" \
    -vsync 0 \
    -color_range 2 \
    f%09d.jpg   
  • Aufgrund der Filterverwendung thumbnail_cudamüssen wir den selectFilter am Ende platzieren.

Testen:
Synthetisches Video mit Frame-Zähler bei 10 fps erstellen:

ffmpeg -y -f lavfi -r 10 -i testsrc=size=128x72:rate=1:duration=1000 -vf setpts=N/10/TB -vcodec libx264 -pix_fmt yuv420p input.mp4

Ausgabeframes nach Ausführung des obigen Befehls:

Bildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingeben
Bildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingeben

Wie Sie sehen, liegen die ausgewählten Frames genau alle 10 Sekunden.

verwandte Informationen