¿Cómo obtener la marca de tiempo del fotograma clave más cercano antes de una marca de tiempo dada con FFmpeg?

18

Quiero un comando de búsqueda FFmpeg tan rápido y preciso. Encontré esto .

La solución es que aplicamos -sstanto para la entrada (búsqueda rápida) como para la salida (búsqueda precisa). Pero: si la búsqueda de entrada no es precisa, ¿cómo podemos estar seguros de que la posición de búsqueda es precisa?


Por ejemplo: si quisiéramos buscar 00:03:00, el comando es algo como:

ffmpeg -ss 00:02:30 -i <INPUT> ... -ss 00:00:30 <OUTPUT>

El primero -ssbuscará otro lugar, no 00:02:30, digamos 00:02:31. Y después de aplicar la segunda búsqueda, el resultado final sería 00:03:01, no lo que queremos. ¿Es eso correcto?

¿Dónde -ssbusca el primero ? ¿Busca el fotograma clave más cercano 00:02:30?

Si es así, aquí está mi pensamiento: corríjame si me equivoco: después de la primera búsqueda, obtenemos la marca de tiempo del resultado (en este ejemplo:) 00:02:31, luego aplicamos la segunda búsqueda con el tiempo apropiado, en este caso 00:00:29.

La pregunta es: ¿cómo obtenemos la marca de tiempo del primer resultado de búsqueda?

Jackode
fuente

Respuestas:

18

Para responder literalmente a la pregunta de su título: Puede obtener una lista de I-frames con

ffprobe -select_streams v -show_frames <INPUT> 

Puede limitar esto a la salida necesaria agregando -show_entries frame=pkt_pts_time,pict_type.

Para ver qué fotograma está más cerca (viene después) de una determinada marca de tiempo, primero debe encontrar todas las marcas de tiempo de los fotogramas clave, por ejemplo con awk.

Primero, defina el tiempo que desea buscar, por ejemplo, 2: 30m que equivale a 150s.

ffprobe -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet in.mp4 | 
awk -F= ' 
  /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } } 
  /pkt_pts_time/ { if (i && ($2 >= 150)) print $2; }  
' | head -n 1

Por ejemplo, esto volvería 150.400000.


Tenga en cuenta que cuando se usa -ssantes -i, FFmpeg localizará el fotograma clave anterior al punto de búsqueda, luego asignará valores PTS negativos a todos los siguientes cuadros hasta que alcance el punto de búsqueda. Un jugador debe decodificar pero no mostrar cuadros con PTS negativo, y el video debe comenzar con precisión.

Algunos jugadores no respetan esto correctamente y mostrarán video negro o basura. En este caso, el script anterior se puede usar para encontrar el PTS del fotograma clave después de su punto de búsqueda, y usarlo para comenzar a buscar desde el fotograma clave. Esto, sin embargo, no será exacto.

Tenga en cuenta que si desea ser súper preciso mientras busca, y mantener la compatibilidad con muchos reproductores, probablemente debería convertir el video a cualquier formato sin pérdida, intra-único, donde podría cortar en cualquier momento y luego volver a codificarlo. Pero esto no será rápido.

slhck
fuente
1
gracias, no estoy haciendo un editor de video, pero quiero tener una búsqueda de video precisa en la cual la brecha debe ser inferior a 0,5 segundos.
jackode
1
Probablemente puedas hacer malabares con el PTS de ffprobe. De lo contrario, cualquier formato intermedio funcionaría, por ejemplo, ProRes 422, DNxHD, que son visualmente sin pérdidas y solo dentro del marco. O usa algo como HuffYUV, etc. Pero, por supuesto, volvería a perder el aspecto "rápido".
slhck
¿Qué versión de ffprobe usaste para el comando, porque la mía dijoUnrecognized option 'select_streams'
Jackode
2
Estabas cerca, la select_streamsopción se agregó en octubre de 2012 . :) Podrías prescindir de eso, pero también obtendrías información para los cuadros de audio, mezclados entre ellos.
slhck
2
Tenga en cuenta que puede agregar esto a la línea ffmpeg para dejar que solo muestre los 2 campos necesarios, en lugar de muchas cosas que desecha awk: -show_entries frame = pkt_pts_time, pict_type
Jannes
7

Entiendo que esta pregunta tiene varios años, pero la última versión de ffprobe tiene la capacidad de omitir marcos . Puede pasar -skip_frame nokeypara informar información solo en los fotogramas clave (I-frames). ¡Esto puede ahorrarle mucho tiempo! En un archivo MP4 de 2GB y 1080p, solía tomar 4 minutos sin omitir fotogramas. Agregar el parámetro de omisión solo lleva 20 segundos.

Mando:

ffprobe -select_streams v -skip_frame nokey -show_frames -show_entries frame = pkt_pts_time, pict_type D: \ test.mp4

Resultados:

[FRAME]
pkt_pts_time=0.000000
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=3.753750
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=7.507500
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=11.261250
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=15.015000
pict_type=I
[/FRAME]

Por lo tanto, los resultados solo contendrán información sobre los fotogramas clave.

Hind-D
fuente
1

Basándose en la respuesta de slhck , aquí hay una función bash que devolverá el fotograma clave más cercano que ocurre ANTES de los Nsegundos.

Esto también se utiliza -read_intervalspara garantizar que ffprobe solo comience a buscar su fotograma clave 25 segundos antes que Nsegundos. Este truco y tener una salida awk cuando se encuentra la marca de tiempo acelera enormemente las cosas.

function ffnearest() {
  STIME=$2; export STIME;
  ffprobe -read_intervals $[$STIME-25]% -select_streams v -show_frames -show_entries frame=pkt_pts_time,pict_type -v quiet "$1" |
  awk -F= '
    /pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } }
    /pkt_pts_time/ { if (i && ($2 <= ENVIRON["STIME"])) print $2; }
    /pkt_pts_time/ { if (i && ($2 > ENVIRON["STIME"])) exit 0; }
  ' | tail -n 1
}

ejemplo de uso:

➜ ffnearest input.mkv 30
23.941000

Lo uso para recortar archivos de video sin volver a codificarlos. Como no puede agregar nuevos fotogramas clave sin volver a codificar, solía ffnearestbuscar el fotograma clave antes de querer cortar. Aquí hay un ejemplo:

ffmpeg  -i input.mkv -ss 00:00:$(echo "$(ffnearest input.mkv 30) - 0.5" | bc)  -c copy -y output.mkv;

Tenga en cuenta que para ese ejemplo, es posible que deba cambiar el formato de lo que se pasa en el -ssparámetro si está buscando más de los primeros 60 segundos.

(molesto, decirle a ffmpeg que busque exactamente la marca de tiempo del fotograma clave parece hacer que ffmpeg excluya ese fotograma clave en la salida, pero restar 0,5 segundos de la marca de tiempo real del fotograma clave hace el truco. Para bash debe usar bcpara evaluar expresiones con decimales , pero en zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]funciona.)

Chris
fuente
Esto le dará el siguiente tiempo de cuadro después de que marco.
Ehsan Chavoshi
0

si desea obtener información de los cuadros I, puede usar

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
shuaihanhungry
fuente