Extraiga fotogramas con precisión cada N segundos usando FFmpeg

Extraiga fotogramas con precisión cada N segundos usando FFmpeg

El uso

Extraigo imágenes de vídeos usando ffmpeg.

Descargo una imagen reducida cada 10 segundos, inclusive, que combino en montajes con imagemagick. Estos montajes nuevamente se utilizan para mostrar una vista previa del video cuando se coloca el cursor sobre un reproductor de video basado en la web. (Calculando qué imagen del montaje mostrar).

El comando

Después de jugar, terminé con el siguiente comando donde la idea es velocidad sobre calidad:

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

Esto pareció funcionar bien. Los disparos están desviados entre ± 0,5 y 1 segundo aquí y allá, pero eso es soportable.

El problema

El problema es que ffmpegproduce una imagen adicional al inicio de los videos. Por ejemplo, los archivos son:

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

A veces, el primero y el segundo tienen una diferencia de unos pocos milisegundos.

Tal como lo sé (ahora), puedo simplemente eliminar la primera imagen y continuar con el resto, pero no estoy seguro de por qué sucede esto y si se trata de un error o algo más.

Dicho de otra manera: necesito saber si el "efecto" de los dos primeros fotogramas esconfiablepara poder eliminarlo ffmpegtambién en otras versiones.

Como uso las imágenes para mostrar 10 seg. instantánea del vídeo en un momento específicoestá apagado por 10 segundossi no elimino la primera imagen generada. Si por alguna razón debería entoncesnocree una copia al inicio, otra versión o lo que sea, eliminar la primera imagen crearía el mismo problema.

Montaje

(Si es de interés los montajes se crean algo así como):

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

Comando que uso ahora según la respuesta (shell):

# 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"

Respuesta1

El uso -r 0.1establece la velocidad de fotogramas de salida en 0,1 Hz, pero no se garantiza que se obtengan fotogramas del vídeo de entrada exactamente cada 10 segundos (no estoy seguro de por qué).

Una forma de resolverlo es usandoseleccionar filtro.

Ejemplo (sin aceleración de GPU):

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)es 1cuando la diferencia entre las marcas de tiempo "aprobadas" es mayor que 10 segundos.
    Cuando la expresión se evalúa como 1, el marco se "selecciona" y se pasa a la salida.
  • bitorwith isnan(prev_selected_t)pasa el primer fotograma, donde prev_selected_testá NaN(no tiene valor).
  • -vsync 0aplica "paso a través": cada cuadro se pasa con su marca de tiempo del demuxer al muxer.

Aquí hay un ejemplo con scale_cuday 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   
  • Debido al uso de thumbnail_cudafiltro, tenemos que colocar el selectfiltro al final.

Pruebas:
cree vídeo sintético con contador de fotogramas a 10 fps:

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

Marcos de salida después de ejecutar el comando anterior:

ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí
ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí

Como puede ver, los cuadros seleccionados son exactamente cada 10 segundos.

información relacionada