Comment obtenir l'horodatage de l'image clé la plus proche avant un horodatage donné avec FFmpeg?

18

Je veux une commande de recherche FFmpeg aussi rapide et précise. J'ai trouvé ça .

La solution consiste à appliquer -ssà la fois l'entrée (recherche rapide) et la sortie (recherche précise). Mais: si la recherche d'entrée n'est pas précise, comment pouvons-nous être sûrs que la position de recherche est exacte?


Par exemple: si nous voulions chercher à 00:03:00, la commande est quelque chose comme:

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

Le premier -sscherchera ailleurs, non 00:02:30, disons 00:02:31. Et après avoir appliqué la deuxième recherche, le résultat final serait 00:03:01- pas ce que nous voulons. Est-ce exact?

Où cherche le premier -ss? Cherche-t-il l'image clé la plus proche 00:02:30?

Si c'est le cas, voici ma pensée - corrigez-moi si je me trompe: après la première recherche, nous obtenons l'horodatage du résultat (dans cet exemple:) 00:02:31, puis nous appliquons la deuxième recherche avec le temps approprié, dans ce cas 00:00:29.

La question est: comment obtenir l'horodatage du premier résultat de recherche?

jackode
la source

Réponses:

18

Pour répondre littéralement à la question de votre titre: vous pouvez obtenir une liste de cadres en I avec

ffprobe -select_streams v -show_frames <INPUT> 

Vous pouvez encore limiter cela à la sortie nécessaire en ajoutant -show_entries frame=pkt_pts_time,pict_type.

Pour voir quelle image est la plus proche (vient après) un certain horodatage, vous devez d'abord trouver tous les horodatages des images clés, par exemple avec awk.

Tout d'abord, définissez le temps que vous souhaitez rechercher, par exemple 2: 30m, ce qui équivaut à 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

Par exemple, cela reviendrait 150.400000.


Notez que lors de l'utilisation -ssprécédente -i, FFmpeg localisera l'image clé avant le point de recherche, puis affectera des valeurs PTS négatives à toutes les images suivantes jusqu'à ce qu'il atteigne le point de recherche. Un lecteur doit décoder mais ne pas afficher les images avec un PTS négatif, et la vidéo doit démarrer avec précision.

Certains joueurs ne respectent pas cela correctement et affichent des vidéos noires ou des ordures. Dans ce cas, le script ci-dessus peut être utilisé pour trouver le PTS de l'image clé après votre point de recherche et l'utiliser pour commencer la recherche à partir de l'image clé. Cependant, cela ne sera pas exact.

Notez que si vous voulez être super précis lors de la recherche et conserver la compatibilité avec de nombreux lecteurs, vous devez probablement convertir la vidéo dans n'importe quel format sans perte et intra uniquement, où vous pouvez couper à tout moment, puis la réencoder. Mais cela ne sera pas rapide.

slhck
la source
1
merci, je ne fais pas d'éditeur vidéo, mais je veux avoir une recherche vidéo précise dans laquelle l'écart devrait être inférieur à 0,5 seconde.
jackode
1
Vous pouvez probablement jongler avec le PTS de ffprobe. Sinon, n'importe quel format intermédiaire ferait l'affaire, par exemple ProRes 422, DNxHD, qui sont visuellement sans perte et intra-trame uniquement. Ou vous utilisez quelque chose comme HuffYUV, etc. Mais alors vous perdriez à nouveau l'aspect "rapide", bien sûr.
slhck
quelle version de ffprobe avez-vous utilisée pour la commande, car la mienne a ditUnrecognized option 'select_streams'
jackode
2
Vous étiez proche, l' select_streamsoption a été ajoutée en octobre 2012 . :) Vous pouvez vous en passer, mais vous obtiendrez également des informations sur les trames audio, mélangées entre les deux.
slhck
2
Notez que vous pouvez ajouter ceci la ligne ffmpeg pour ne laisser sortir que les 2 champs nécessaires, au lieu de beaucoup de choses qui sont jetées par awk: -show_entries frame = pkt_pts_time, pict_type
Jannes
7

Je comprends que cette question remonte à plusieurs années, mais la dernière version de ffprobe a la possibilité de sauter des images . Vous pouvez transmettre des -skip_frame nokeyinformations de rapport uniquement sur les images clés (images I). Cela peut vous faire gagner beaucoup de temps! Sur un fichier MP4 1080p de 2 Go, cela prenait 4 minutes sans sauter les images. L'ajout du paramètre de saut ne prend que 20 secondes.

Commander:

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

Résultats:

[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]

Ainsi, les résultats ne contiendront que des informations concernant les images clés.

Hind-D
la source
1

En s'appuyant sur la réponse de slhck , voici une fonction bash qui renverra l'image clé la plus proche qui se produit AVANT Nsecondes.

Cela permet également de -read_intervalss'assurer que ffprobe ne commence à rechercher votre image clé que 25 secondes avant les Nsecondes. Cette astuce et la sortie awk lorsque l'horodatage est trouvé accélèrent considérablement les choses.

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
}

exemple d'utilisation:

➜ ffnearest input.mkv 30
23.941000

Je l'utilise pour découper des fichiers vidéo sans les réencoder. Comme vous ne pouvez pas ajouter de nouvelles images clés sans réencodage, j'utilise ffnearestpour rechercher l'image clé avant de vouloir couper. Voici un exemple:

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

Notez que pour cet exemple, vous devrez peut-être modifier le format de ce qui est passé dans le -ssparamètre si vous recherchez plus de 60 secondes.

(énervant, dire à ffmpeg de chercher exactement à l'horodatage de l'image clé semble faire ffmpeg exclure cette image clé dans la sortie, mais soustraire 0,5 seconde de l'horodatage réel de l'image clé fait l'affaire. Pour bash, vous devez utiliser bcpour évaluer les expressions avec des décimales , mais dans zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]fonctionne.)

Chris
la source
Cela donnera le temps d'image suivant après l'image I.
Ehsan Chavoshi
0

si vous souhaitez obtenir des informations sur les images I, vous pouvez utiliser

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