Obtener la duración total de los archivos de video en un directorio

30

Tengo una lista de .tsarchivos:

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

¿Cómo puedo obtener la duración total (tiempo de ejecución) de todos estos archivos?

k961
fuente
¿Cómo se obtiene la duración de un solo archivo?
Hauke ​​Laging
yo no sé eso también
k961
@Hauke ​​Laging encontré este programa "mediainfo"
k961
Estos son archivos multimedia, probablemente videos.
slm
1
Cualquier archivo multimedia (por ejemplo: MP4, ASF y .264 ...) tendrá información de encabezado estándar predefinida, podemos obtener la información de ese archivo como resolución, velocidad de cuadros, número de cuadros (GOP) y longitud y duración del archivo media ...
Kantam Nagesh

Respuestas:

55

No tengo .tsaquí pero esto funciona .mp4. Use ffprobe(parte de ffmpeg) para obtener el tiempo en segundos, por ejemplo:

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

así que para todos los .mp4archivos en el directorio actual:

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

a continuación, utilizar pastepara pasar la salida a bcy obtener el tiempo total en segundos:

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

Entonces, para los .tsarchivos que podrías probar:

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

Otra herramienta que funciona para los archivos de video que tengo aquí es exiftool, por ejemplo:

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

Longitud total de todos los .mp4archivos en el directorio actual:

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

También puede canalizar la salida a otro comando para convertir el total DD:HH:MM:SS, consulte las respuestas aquí .

O use exiftoolel interno ConvertDurationpara eso (aunque necesita una versión relativamente reciente):

exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)
                    }' ./*.mp4| tail -n1
0:09:15
don_crissti
fuente
Muy agradable, no había visto ese truco ffprobeantes.
slm
1
Buen truco con pastey bc! mucho más limpio que con awkdigamos.
fduff
@fduff. Si bien bchará una precisión arbitraria, un inconveniente es que ...| paste -sd+ - | bcalcanzará el límite de tamaño de línea en algunas bcimplementaciones (por ejemplo, seq 429 | paste -sd+ - | bcfalla con OpenSolaris bc) o tendrá el potencial de usar toda la memoria en otras.
Stéphane Chazelas
¿Puedes hacer esto (método ffprobe) con algo como avconv? No puedo encontrar ffmpeg en mis repositorios para Kubuntu 14.04, ¿entonces tampoco tengo ffprobe? Tengo avprobe, pero no me gustan esos argumentos.
Joe
@ Joe - No avprobeen los repositorios de Arch (prolly porque entra en conflicto con ffmpeg), así que no puedo probarlo en ATM, pero ¿te da la duración del archivo si lo ejecutas así: avprobe -show_format_entry duration myfile.mp4o avprobe -loglevel quiet -show_format_entry duration myfile.mp4? Creo que uno de estos comandos debería darle una sola línea de salida con la duración del archivo. Aunque no estoy seguro.
don_crissti
6

Esto usa ffmpege imprime el tiempo de espera en segundos totales:

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

Explicación:

for f in *.ts; do itera cada uno de los archivos que termina en ".ts"

ffmpeg -i "$f" 2>&1 redirige la salida a stderr

grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' aísla el tiempo

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Convierte el tiempo en segundos.

times+=("$_t") agrega los segundos a una matriz

echo "${times[@]}" | sed 's/ /+/g' | bcexpande cada uno de los argumentos y reemplaza los espacios y los canaliza a bcuna calculadora Linux común

jmunsch
fuente
1
¡Agradable! También vea mi versión que se basa en gran medida en sus ideas.
MvG
Solución corta y elegante
Neo
4

Simplificando la respuesta de @ jmunsch , y usando la respuestapaste que acabo de aprender de @ slm , podría terminar con algo como esto:

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

Al igual que jmunsch, estoy usando ffmpegpara imprimir la duración, ignorando el error sobre un archivo de salida faltante y en su lugar buscando la salida de error para la línea de duración. Yo invocoffmpeg con todos los aspectos de la configuración regional forzados a la configuración regional C estándar, para que no tenga que preocuparme por los mensajes de salida localizados.

Luego estoy usando un single en awklugar del suyo grep | grep | head | tr | awk. Esa awkinvocación busca la línea (con suerte única) que contiene Duration:. Usando dos puntos como separador, esa etiqueta es el campo 1, las horas son el campo 2, los minutos archivados 3 y los segundos campo 4. La coma final después de los segundos no parece molestarme awk, pero si alguien tiene problemas allí, él podría incluir un tr -d ,en la tubería entre ffmpegy awk.

Ahora viene la parte de slm: estoy usando pastepara reemplazar las nuevas líneas con signos más, pero sin afectar la nueva línea final (al contrario de lo tr \\n +que tenía en una versión anterior de esta respuesta). Eso da la expresión de suma que se puede alimentar bc.

Inspirado por la idea de slm de usar datepara manejar formatos similares al tiempo, aquí hay una versión que lo usa para formatear los segundos resultantes como días, horas, minutos y segundos con parte fraccional:

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 interior $(…)es exactamente como antes. Utilizando la@ carácter como una indicación, usamos esto como el número de segundos desde el 1 de enero de 1970. La "fecha" resultante se formatea como día del año, hora y nanosegundos. A partir de ese día del año, restamos uno, ya que una entrada de cero segundos ya conduce al día 1 de ese año 1970. No creo que haya una manera de obtener los recuentos del día del año comenzando en cero.

La final sedelimina los ceros finales adicionales. Con TZsuerte, la configuración debería forzar el uso de UTC, para que el horario de verano no interfiera con colecciones de videos realmente grandes. Sin embargo, si tiene más de un año de video, este enfoque aún no funcionará.

MvG
fuente
3

No estoy familiarizado con la .tsextensión, pero suponiendo que sean algún tipo de archivo de video que pueda usar ffmpegpara identificar la duración de un archivo de esta manera:

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

Luego podemos dividir esta salida, seleccionando solo el tiempo de duración.

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

Así que ahora solo necesitamos una forma de recorrer nuestros archivos y recopilar estos valores de duración.

$ 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: Aquí para mi ejemplo simplemente copiar mi archivo de muestra some.mp4y la llamó 1.mp4, 2.mp4y 3.mp4.

Convertir tiempos a segundos

El siguiente fragmento tomará las duraciones de arriba y las convertirá a segundos.

$ 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

Esto toma nuestras duraciones y las pone en una variable $dur, a medida que recorremos los archivos. El datecomando se usa para calcular el número de segundos sinusoidal de la época de Unix (01/01/1970). Aquí está el datecomando anterior desglosado para que sea más fácil de ver:

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

NOTA: El uso datede esta manera solo funcionará si todos sus archivos tienen una duración <24 horas (es decir, 86400 segundos). Si necesita algo que pueda manejar duraciones más largas, puede usar esto como una alternativa:

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

Sumando los tiempos

Luego podemos tomar la salida de nuestro forbucle y ejecutarlo en un pastecomando que incorporará +signos entre cada número, así:

$ 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

Finalmente ejecutamos esto en la calculadora de línea de comando, bcpara resumirlos:

$ 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

Resultando en la duración total de todos los archivos, en segundos. Por supuesto, esto se puede convertir a otro formato si es necesario.

slm
fuente
@DamenSalvatore: no hay problema, es de esperar que le muestre cómo puede dividir la tarea en varios pasos, para que pueda personalizarla según sea necesario.
slm
@slm: usaría otra forma de convertir la duración del video a segundos, ya que datepodría ahogarse si ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"devuelve algo como 26:33:21.68(es decir, duración ≥ 24 horas / 86400 segundos)
don_crissti
@don_crissti - gracias, no lo había probado más de 20 horas cuando lo probé. Agregaré una nota que muestra un método alternativo.
slm
¡Gracias por tu respuesta! No solo inspiró el mío , sino que también pasteme llamó la atención. Supongo java -classpath $(find -name \*.jar | paste -sd:)que va a ser muy útil para mí, teniendo en cuenta los trucos que usé para esto en el pasado.
MvG
@MvG - pastees mi comando favorito 8-)
slm
1

Saliendo de la respuesta aceptada y utilizando la herramienta clásica de pulido inverso 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

Es decir: apelando +y pluego canalizando eso dcy obtendrás tu suma.

A
fuente
2
bcse pone demasiado amor Todos están poniendo +señales entre cada línea (uniéndose +), mientras que con el pulido inverso puede simplemente tirar un +extremo y peliminarlo;)
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'

Asegúrese de tener instalado MPlayer .

ryanmjacobs
fuente
no me da ningún resultado
k961
¿tienes instalado mplayer y perl?
ryanmjacobs
sí, instalé mplayer y perl ya estaba instalado
k961
1
Lo siento, no sé por qué no funciona; pero ya tienes suficientes respuestas decentes de todos modos. :)
ryanmjacobs
0

Como ubuntu envía libav en lugar de ffmpeg:

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

Muy basado en ideas de MvG

Ahed Eid
fuente
0

Bueno, todas estas soluciones necesitan un poco de trabajo, lo que hice fue muy simple, 1)

  1. fue a la carpeta deseada y haga clic derecho -> abrir con otra aplicación

  2. Luego seleccione el reproductor multimedia VLC,

  3. esto comenzará a reproducir uno de los videos, pero luego
  4. presione Ctrl + L , y verá la lista de reproducción de los videos y en algún lugar en la esquina superior izquierda verá la duración total

aquí hay un ejemplo

1) Elemento de la lista

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

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

Puede ver justo debajo de la barra de herramientas, hay una lista de reproducción [10:35:51] escrita, por lo que la carpeta contiene 10 horas 35 minutos y 51 segundos de duración del total de videos

Patel Ujjval Rajeshbhai
fuente
0

Tenía subdirectorios en la carpeta actual, así que tuve que calcular recursivamente la duración:

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

iMitwe
fuente