Ottieni la durata totale dei file video in una directory


30

Ho un elenco di .tsfile:

out1.ts ... out749.ts   out8159.ts  out8818.ts

Come posso ottenere la durata totale (tempo di esecuzione) di tutti questi file?


Come si ottiene la durata di un singolo file?
Hauke ​​Laging,

non lo so anche
io

@Hauke ​​Laging ho trovato questo programma "mediainfo"
k961,

Questi sono file multimediali, probabilmente video.
slm

1
Qualsiasi file multimediale (ad esempio: MP4, ASF e .264 ...) avrà informazioni di intestazione standard predefinite, possiamo ottenere le informazioni da quel file come risoluzione, frequenza dei fotogrammi, numero di fotogrammi (GOP) e lunghezza e durata del file media ...
Kantam Nagesh,

Risposte:


55

Non ho .tsqui ma questo funziona per .mp4. Utilizzare ffprobe(parte di ffmpeg) per ottenere il tempo in secondi, ad esempio:

ffprobe -v quiet -of csv=p=0 -show_entries format=duration Inception.mp4 
275.690000

quindi per tutti i .mp4file nella directory corrente:

find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \;
149.233333
130.146667
275.690000

quindi utilizzare pasteper passare l'output a bce ottenere il tempo totale in secondi:

find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
555.070000

Quindi, per i .tsfile potresti provare:

find . -maxdepth 1 -iname '*.ts' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc

Un altro strumento che funziona per i file video che ho qui è exiftool, ad esempio:

exiftool -S -n Inception.mp4 | grep ^Duration
Duration: 275.69
exiftool -q -p '$Duration#' Inception.mp4
275.69

Lunghezza totale per tutti i .mp4file nella directory corrente:

exiftool -S -n ./*.mp4 | awk '/^Duration/ {print $2}' | paste -sd+ -| bc
555.070000000000
exiftool -q -p '$Duration#' ./*.mp4 | awk '{sum += $0}; END{print sum}'
555.070000000000

È inoltre possibile reindirizzare l'output a un altro comando per convertire il totale in DD:HH:MM:SS, vedere le risposte qui .

O usa exiftooll'interno ConvertDurationper quello (hai bisogno di una versione relativamente recente):

exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)
                    }' ./*.mp4| tail -n1
0:09:15

Molto bello, non avevo mai visto quel trucco ffprobeprima.
slm

1
Bel trucco con pastee bc! molto più pulito che con awkdiciamo.
fduff,

@fduff. Mentre bcfarà una precisione arbitraria, uno svantaggio è che ...| paste -sd+ - | bcraggiungerà il limite della dimensione della linea in alcune bcimplementazioni (per esempio seq 429 | paste -sd+ - | bcfallisce con OpenSolaris bc) o avrà il potenziale di esaurire tutta la memoria in altre.
Stéphane Chazelas,

Puoi farlo (metodo ffprobe) con qualcosa come avconv? Non riesco a trovare ffmpeg nei miei repository per Kubuntu 14.04 - quindi non ho neanche ffprobe? Ho avprobe, ma non mi piacciono quegli argomenti.
Joe,

@Joe - No avprobenei repository Arch (principalmente perché è in conflitto con ffmpeg), quindi non puoi provarlo ATM ma ti dà la durata del file se lo esegui in questo modo: avprobe -show_format_entry duration myfile.mp4oppure avprobe -loglevel quiet -show_format_entry duration myfile.mp4? Penso che uno di questi comandi dovrebbe darti una sola riga di output con la durata del file. Non sono sicuro però.
don_crissti,

6

Questo utilizza ffmpege stampa il timeout in secondi totali:

times=()
for f in *.ts; do
    _t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
    times+=("$_t")
done
echo "${times[@]}" | sed 's/ /+/g' | bc

Spiegazione:

for f in *.ts; do scorre ogni file che termina con ".ts"

ffmpeg -i "$f" 2>&1 reindirizza l'output su stderr

grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' isola il tempo

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Converte il tempo in secondi

times+=("$_t") aggiunge i secondi a un array

echo "${times[@]}" | sed 's/ /+/g' | bcespande ciascuno degli argomenti e sostituisce gli spazi e lo convoglia a bcun comune calcolatore linux


1
Bello! Vedi anche la mia versione che è fortemente basata sulle tue idee.
MvG

Soluzione breve ed elegante
Neo

4

Ottimizzando la risposta di @ jmunsch e usando la risposta che pasteho appena appreso da @ slm , potresti finire con qualcosa del genere:

for i in *.ts; do LC_ALL=C ffmpeg -i "$i" 2>&1 | \
awk -F: '/Duration:/{print $2*3600+$3*60+$4}'; done | paste -sd+ | bc

Proprio come ha fatto jmunsch, sto usando ffmpegper stampare la durata, ignorando l'errore su un file di output mancante e invece cercando l'output dell'errore per la linea di durata. Invocoffmpeg con tutti gli aspetti della locale forzati alla locale C standard, in modo da non dovermi preoccupare dei messaggi di output localizzati.

Quindi sto usando un singolo al awkposto del suo grep | grep | head | tr | awk. Quell'invocazione awkcerca la riga (si spera unica) contenente Duration:. Usando i due punti come separatore, quell'etichetta è il campo 1, le ore sono il campo 2, i minuti archiviati 3 e il campo dei secondi 4. La virgola finale dopo i secondi non sembra disturbare il mio awk, ma se qualcuno ha problemi lì, lui potrebbe includere a tr -d ,nella pipeline tra ffmpegeawk .

Ora arriva la parte da slm: sto usando pasteper sostituire le nuove righe con segni più, ma senza influenzare la nuova riga finale (contrariamente a quanto tr \\n +avevo in una versione precedente di questa risposta). Ciò fornisce l'espressione somma a cui può essere fornita bc.

Ispirato dall'idea di slm di utilizzare dateper gestire i formati simili al tempo, ecco una versione che lo utilizza per formattare i secondi risultanti come giorni, ore, minuti e secondi con parte frazionaria:

TZ=UTC+0 date +'%j %T.%N' --date=@$(for i in *.ts; do LC_ALL=C \
ffmpeg -i "$i" 2>&1 | awk -F: '/Duration:/{print $2*3600+$3*60+$4}'; done \
| paste -sd+ | bc) | awk '{print $1-1 "d",$2}' | sed 's/[.0]*$//'

La parte interna $(…)è esattamente come prima. Usando il @carattere come indicazione, usiamo questo come numero di secondi dal 1 ° gennaio 1970. La "data" risultante viene formattata come giorno dell'anno, ora e nanosecondi. Da quel giorno dell'anno ne sottraggiamo uno, poiché un input di zero secondi porta già al primo giorno di quell'anno 1970. Non credo che ci sia un modo per ottenere i conteggi del giorno dell'anno a partire da zero.

Il finale sedsi sbarazza degli zeri finali in più. Si TZspera che l'impostazione forzare l'uso di UTC, in modo che l'ora legale non interferisca con raccolte video molto grandi. Se hai più di un anno di video, questo approccio non funzionerà comunque.


3

Non ho familiarità con l' .tsestensione, ma supponendo che siano un tipo di file video che puoi usare ffmpegper identificare la durata di un file in questo modo:

$ ffmpeg -i some.mp4 2>&1 | grep Dura
  Duration: 00:23:17.01, start: 0.000000, bitrate: 504 kb/s

Possiamo quindi suddividere questo output, selezionando solo il tempo di durata.

$ ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"
00:23:17.01

Quindi ora abbiamo solo bisogno di un modo per scorrere i nostri file e raccogliere questi valori di durata.

$ for i in *.mp4; do
    ffmpeg -i "$i" 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"; done
00:23:17.01
00:23:17.01
00:23:17.01

NOTA: qui per il mio esempio ho semplicemente copiato il mio file di esempio some.mp4e la chiamò 1.mp4, 2.mp4e 3.mp4.

Convertire i tempi in secondi

Il frammento seguente prenderà le durate dall'alto e le convertirà in secondi.

$ for i in *.mp4; do 
    dur=$(ffmpeg -i "$i" 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)");
    date -ud "1970/01/01 $dur" +%s; done
1397
1397
1397

Questo richiede le nostre durate e le inserisce in una variabile $dur, mentre eseguiamo il ciclo tra i file. Il datecomando viene quindi utilizzato per calcolare il numero di secondi sin dall'epoca Unix (1970/01/01). Ecco il datecomando sopra suddiviso, quindi è più facile da vedere:

$ date -ud "1970/01/01 00:23:17.01" +%s
1397

NOTA: l' utilizzo datein questo modo funziona solo se tutti i file hanno una durata inferiore a 24 ore (ovvero 86400 secondi). Se hai bisogno di qualcosa in grado di gestire durate più grandi, puoi usarlo come alternativa:

sed 's/^/((/; s/:/)*60+/g' | bc
Esempio
$ echo 44:29:36.01 | sed 's/^/((/; s/:/)*60+/g' | bc
160176.01

Sommando i tempi

Possiamo quindi prendere l'output del nostro forloop ed eseguirlo in un pastecomando che incorporerà i +segni tra ogni numero, in questo modo:

$ for i in *.mp4; do 
    dur=$(ffmpeg -i "$i" 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)");
    date -ud "1970/01/01 $dur" +%s; done | paste -s -d+
1397+1397+1397

Alla fine lo eseguiamo nel calcolatore della riga di comando, bcper riassumere:

$ for i in *.mp4; do 
    dur=$(ffmpeg -i "$i" 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)");
    date -ud "1970/01/01 $dur" +%s; done | paste -s -d+ | bc
4191

Con conseguente durata totale di tutti i file, in secondi. Questo può ovviamente essere convertito in qualche altro formato, se necessario.


@DamenSalvatore: nessun problema, si spera che ti mostri come suddividere l'attività in vari passaggi, in modo da poterla personalizzare secondo necessità.
slm

@slm - Vorrei usare un altro modo per convertire la durata del video in secondi, in quanto datepotrebbe soffocare se ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"restituisce qualcosa del tipo 26:33:21.68(ovvero durata ≥ 24 ore / 86400 secondi)
don_crissti

@don_crissti - grazie, non l'ho provato oltre 20 ore durante il test. Aggiungerò una nota che mostra un metodo alternativo.
slm

Grazie per la tua risposta! Non solo ha ispirato il mio , ma ha anche pasteattirato la mia attenzione. Immagino java -classpath $(find -name \*.jar | paste -sd:)che sarà molto utile per me, considerando gli hack che avevo usato per questo in passato.
MvG

@MvG - pasteè un ordine preferito 8-)
SLM

1

Disattivazione della risposta accettata e utilizzo del classico strumento di lucidatura inversa UNIX:

{ find . -maxdepth 2 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 \
         -show_entries format=duration {} \; ; printf '+\n60\n*\np'; } | dc

783.493000

Vale a dire: aprendo +e ppoi convogliandolo dce otterrai la tua somma.


2
bcottiene troppo amore. Stai tutti mettendo dei +segni tra ogni riga (unendoti +), mentre con la levigatura inversa puoi semplicemente buttare un +a alla fine e ptirarlo fuori;)
AL

0
$ find -iname '*.ts' -print0 |\
xargs -0  mplayer -vo dummy -ao dummy -identify 2>/dev/null |\
perl -nle '/ID_LENGTH=([0-9\.]+)/ && ($t += $1) && printf "%02d:%02d:%02d:%02d\n",$t/86400,$t/3600%24,$t/60%60,$t%60'

Assicurati di aver installato MPlayer .


non mi dà alcun output
k961

hai installato mplayer e perl?
ryanmjacobs,

sì, ho installato mplayer e perl era già installato
k961,

1
Scusa, non so perché non funziona; ma hai già già abbastanza risposte decenti. :)
ryanmjacobs il

0

Come Ubuntu spedisce libav invece di ffmpeg:

#!/bin/sh
for f in *.mp4; do
    avprobe -v quiet -show_format_entry duration "$f"
done | paste -sd+ | bc

Fortemente basato sulle idee MvG


0

Bene, tutte queste soluzioni richiedono un po 'di lavoro, quello che ho fatto è stato molto semplice, 1)

  1. è andato alla cartella desiderata e fare clic con il tasto destro -> apri con un'altra applicazione

  2. Quindi seleziona VLC media player,

  3. questo inizierà a riprodurre uno dei video, ma poi
  4. premi ctrl + L e vedrai la playlist dei video e da qualche parte nell'angolo in alto a sinistra vedrai la durata totale

ecco un esempio

1. Voce di elenco

2.inserisci qui la descrizione dell'immagine

3.inserisci qui la descrizione dell'immagine

Puoi vedere proprio sotto la barra degli strumenti, c'è una playlist [10:35:51] scritta, quindi la cartella contiene 10 ore e 35 minuti e 51 secondi di durata dei video totali


0

Avevo delle sottodirectory nella cartella corrente, quindi ho dovuto calcolare ricorsivamente la durata:

find . -iname '*.mp4' -print0 | xargs --null exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' | tail -n1

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.