Come ottenere il timestamp del fotogramma chiave più vicino prima di un determinato timestamp con FFmpeg?


18

Voglio un FFmpeg che cerchi un comando così veloce e preciso. Ho trovato questo .

La soluzione è che applichiamo -sssia l'input (ricerca rapida) sia l'output (ricerca accurata). Ma: se la ricerca di input non è accurata, come possiamo essere sicuri che la posizione di ricerca sia accurata?


Ad esempio: se volessimo cercare alle 00:03:00, il comando è qualcosa del tipo:

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

Il primo -sscercherà altrove, non 00:02:30dire 00:02:31. E dopo aver applicato la seconda ricerca, il risultato finale sarebbe 00:03:01- non quello che vogliamo. È corretto?

Dove -sscerca il primo ? Cerca il fotogramma chiave più vicino 00:02:30?

Se è così, ecco il mio pensiero: correggimi se sbaglio: dopo la prima ricerca, otteniamo il timestamp del risultato (in questo esempio:) 00:02:31, quindi applichiamo la seconda ricerca con il tempo appropriato, in questo caso 00:00:29.

La domanda è: come si ottiene il time stamp del primo risultato di ricerca?

Risposte:


18

Per rispondere letteralmente alla domanda del tuo titolo: puoi ottenere un elenco di I-frame con

ffprobe -select_streams v -show_frames <INPUT> 

È possibile limitare ulteriormente questo all'output necessario aggiungendo -show_entries frame=pkt_pts_time,pict_type.

Per vedere quale fotogramma è il più vicino (dopo) un determinato timestamp, dovresti prima scoprire tutti i timestamp dei fotogrammi chiave, ad esempio con awk.

Innanzitutto, definire il tempo che si desidera cercare, ad esempio 2: 30m che 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

Ad esempio, questo ritornerebbe 150.400000.


Si noti che quando si utilizza -ssprima -i, FFmpeg individuerà il fotogramma chiave precedente al punto di ricerca, quindi assegnerà valori PTS negativi a tutti i fotogrammi seguenti fino a raggiungere il punto di ricerca. Un giocatore dovrebbe decodificare ma non visualizzare i frame con PTS negativo e il video dovrebbe iniziare con precisione.

Alcuni giocatori non lo rispettano correttamente e visualizzeranno video o immondizia neri. In questo caso, lo script sopra può essere utilizzato per trovare il PTS del fotogramma chiave dopo il punto di ricerca e utilizzarlo per iniziare a cercare dal fotogramma chiave. Questo, tuttavia, non sarà accurato.

Tieni presente che se desideri essere estremamente preciso durante la ricerca e mantenere la compatibilità con molti lettori, dovresti probabilmente convertire il video in qualsiasi formato senza perdita e intra-solo, dove puoi tagliare in qualsiasi momento e quindi ricodificarlo. Ma questo non sarà veloce.


1
grazie, non sto realizzando un editor video, ma voglio avere una ricerca video precisa in cui il divario dovrebbe essere inferiore a 0,5 secondi.
jackode,

1
Probabilmente puoi destreggiarti con il PTS da ffprobe. In caso contrario, qualsiasi formato intermedio farebbe, ad esempio ProRes 422, DNxHD, che sono visivamente senza perdita e solo all'interno del frame. Oppure usi qualcosa come HuffYUV, ecc. Ma poi perderai di nuovo l'aspetto "veloce", ovviamente.
slhck,

quale versione di ffprobe hai usato per il comando, perché il mio dicevaUnrecognized option 'select_streams'
jackode

2
Eri vicino, l' select_streamsopzione è stata aggiunta nell'ottobre 2012 . :) Potresti farne a meno, ma poi otterrai anche informazioni per i frame audio, mescolati tra loro.
slhck,

2
Nota che puoi aggiungere questa riga ffmpeg per consentirne l'output solo dei 2 campi necessari, invece di molte cose che vengono eliminate da awk: -show_entries frame = pkt_pts_time, pict_type
Jannes

7

Capisco che questa domanda ha diversi anni, ma l'ultima versione di ffprobe ha la capacità di saltare i frame . È possibile passare -skip_frame nokeyper segnalare le informazioni solo sui fotogrammi chiave (I-frame). Questo può farti risparmiare un sacco di tempo! Su un file MP4 1080p da 2 GB, impiegava 4 minuti senza saltare i frame. L'aggiunta del parametro skip richiede solo 20 secondi.

Comando:

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

risultati:

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

Quindi i risultati conterranno solo informazioni relative ai fotogrammi chiave.


1

Sulla base della risposta di Slhck , ecco una funzione bash che restituirà il fotogramma chiave più vicino che si verifica PRIMA dei Nsecondi.

Questo serve anche -read_intervalsa garantire che ffprobe inizi a cercare il fotogramma chiave solo 25 secondi prima dei Nsecondi. Questo trucco e l'uscita di awk quando viene trovato il timestamp velocizzano notevolmente le cose.

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
}

esempio di utilizzo:

➜ ffnearest input.mkv 30
23.941000

Lo uso per tagliare i file video senza ricodificarli. Dal momento che non è possibile aggiungere nuovi fotogrammi chiave senza ricodifica, io uso ffnearestper cercare il fotogramma chiave prima di voler tagliare. Ecco un esempio:

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

Nota che per quell'esempio potresti dover cambiare il formato di ciò che è passato nel -ssparametro se stai cercando oltre i primi 60 secondi.

(fastidiosamente, dire a ffmpeg di cercare esattamente il timestamp del fotogramma chiave sembra fare in modo che ffmpeg escluda quel fotogramma chiave nell'output, ma sottraendo 0,5 secondi dal timestamp effettivo del fotogramma chiave fa il trucco. Per bash è necessario utilizzare bcper valutare le espressioni con i decimali , ma in zsh -ss 00:00:$[$(ffnearest input.mkv 28)-0.5]funziona.)


Questo darà il tempo successivo al fotogramma successivo.
Ehsan Chavoshi,

0

se si desidera ottenere informazioni sui frame I, è possibile utilizzare

ffprobe -i input.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type|grep -B 1 'pict_type=I'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.