Eu tenho uma lista de .ts
arquivos:
out1.ts ... out749.ts out8159.ts out8818.ts
Como posso obter a duração total (tempo de execução) de todos esses arquivos?
Eu tenho uma lista de .ts
arquivos:
out1.ts ... out749.ts out8159.ts out8818.ts
Como posso obter a duração total (tempo de execução) de todos esses arquivos?
Respostas:
Eu não tenho .ts
aqui, mas isso funciona para .mp4
. Use ffprobe
(parte de ffmpeg
) para obter o tempo em segundos, por exemplo:
ffprobe -v quiet -of csv=p=0 -show_entries format=duration Inception.mp4
275.690000
assim, para todos os .mp4
arquivos no diretório atual:
find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \;
149.233333
130.146667
275.690000
em seguida, usar paste
para passar a saída para bc
e obter o tempo total em segundos:
find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
555.070000
Portanto, para .ts
arquivos, você pode tentar:
find . -maxdepth 1 -iname '*.ts' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
Outra ferramenta que funciona para os arquivos de vídeo que tenho aqui é exiftool
, por exemplo:
exiftool -S -n Inception.mp4 | grep ^Duration
Duration: 275.69
exiftool -q -p '$Duration#' Inception.mp4
275.69
Comprimento total para todos os .mp4
arquivos no diretório atual:
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
Você também pode canalizar a saída para outro comando para o qual converter o total DD:HH:MM:SS
, veja as respostas aqui .
Ou use exiftool
interno ConvertDuration
para isso (você precisa de uma versão relativamente recente):
exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)
}' ./*.mp4| tail -n1
0:09:15
ffprobe
antes.
paste
e bc
! muito mais limpo do que com awk
, digamos.
bc
faça precisão arbitrária, uma desvantagem é que ...| paste -sd+ - | bc
atingirá o limite de tamanho da linha em algumas bc
implementações (por exemplo, seq 429 | paste -sd+ - | bc
falha no OpenSolaris bc
) ou terá o potencial de usar toda a memória em outras.
avprobe
no repositório do Arch (prolly porque entra em conflito com ffmpeg
), portanto, não é possível experimentá-lo em caixas eletrônicos, mas fornece a duração do arquivo se você o executar dessa maneira: avprobe -show_format_entry duration myfile.mp4
ou avprobe -loglevel quiet -show_format_entry duration myfile.mp4
? Eu acho que um desses comandos deve fornecer uma única linha de saída com a duração do arquivo. Não tenho certeza.
Isso usa ffmpeg
e imprime o tempo limite em segundos totais:
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
Explicação:
for f in *.ts; do
itera cada um dos arquivos que termina em ".ts"
ffmpeg -i "$f" 2>&1
redireciona a saída para o stderr
grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' '
isola o tempo
awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }'
Converte o tempo em segundos
times+=("$_t")
adiciona os segundos a uma matriz
echo "${times[@]}" | sed 's/ /+/g' | bc
expande cada um dos argumentos e substitui os espaços e os canaliza para bc
uma calculadora linux comum
Simplificando a resposta de @ jmunsch e usando o paste
que acabei de aprender com a resposta de @ slm , você pode terminar com algo assim:
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
Assim como jmunsch, estou usando ffmpeg
para imprimir a duração, ignorando o erro sobre um arquivo de saída ausente e procurando a linha de duração na saída de erro. Chamo ffmpeg
com todos os aspectos do código do idioma forçados para o código C padrão, para que não precise me preocupar com mensagens de saída localizadas.
Em seguida, estou usando um único em awk
vez do dele grep | grep | head | tr | awk
. Essa awk
invocação procura a linha (espero que única) que contém Duration:
. Usando dois pontos como separador, esse rótulo é o campo 1, as horas são o campo 2, os minutos são arquivados 3 e o campo de segundos 4. A vírgula final após os segundos não parece incomodar o meu awk
, mas se alguém tiver problemas, ele pode incluir um tr -d ,
no pipeline entre ffmpeg
e awk
.
Agora vem a parte do slm: estou usando paste
para substituir novas linhas por sinais de mais, mas sem afetar a nova linha à direita (ao contrário do que tr \\n +
eu tinha na versão anterior desta resposta). Isso fornece a expressão sum que pode ser fornecida bc
.
Inspirada na idéia do slm de usar date
para manipular formatos semelhantes ao tempo, aqui está uma versão que o usa para formatar os segundos resultantes como dias, horas, minutos e segundos com parte fracionária:
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]*$//'
A parte interna $(…)
é exatamente como antes. Utilizando o @
caractere como indicação, usamos esse número como o número de segundos desde 1º de janeiro de 1970. A "data" resultante é formatada como dia do ano, hora e nanossegundos. A partir desse dia do ano, subtraímos um, uma vez que uma entrada de zero segundos já leva ao dia 1 daquele ano de 1970. Não acho que haja uma maneira de obter as contagens do dia do ano começando em zero.
A final sed
se livra de zeros à direita extras. A TZ
definição deve esperamos forçar o uso de UTC, de modo que o horário de verão não vai interferir com realmente coleções de vídeo grandes. Se você tiver mais de um ano de vídeo, essa abordagem ainda não funcionará.
Não estou familiarizado com a .ts
extensão, mas supondo que eles sejam algum tipo de arquivo de vídeo que você possa usar ffmpeg
para identificar a duração de um arquivo da seguinte forma:
$ ffmpeg -i some.mp4 2>&1 | grep Dura
Duration: 00:23:17.01, start: 0.000000, bitrate: 504 kb/s
Podemos então dividir essa saída, selecionando apenas o tempo de duração.
$ ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"
00:23:17.01
Portanto, agora precisamos apenas de uma maneira de percorrer nossos arquivos e coletar esses valores de duração.
$ 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: Aqui para o meu exemplo, eu simplesmente copiou o meu arquivo de amostra some.mp4
e nomeou-o 1.mp4
, 2.mp4
e 3.mp4
.
O seguinte snippet pega as durações de cima e as converte em 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
Isso leva nossas durações e as coloca em uma variável $dur
, conforme percorremos os arquivos. O date
comando é então usado para calcular o número de segundos sine a época do Unix (01/01/1970). Aqui está o date
comando acima dividido, para que seja mais fácil ver:
$ date -ud "1970/01/01 00:23:17.01" +%s
1397
NOTA: O uso date
dessa maneira funcionará apenas se todos os seus arquivos tiverem uma duração inferior a 24 horas (ou seja, 86400 segundos). Se você precisar de algo que possa lidar com durações maiores, use-o como uma alternativa:
sed 's/^/((/; s/:/)*60+/g' | bc
Exemplo
$ echo 44:29:36.01 | sed 's/^/((/; s/:/)*60+/g' | bc
160176.01
Podemos então pegar a saída do nosso for
loop e executá-lo em um paste
comando que incorporará +
sinais entre cada número, da seguinte forma:
$ 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, executamos isso na calculadora da linha de comando, bc
para resumir:
$ 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 na duração total de todos os arquivos, em segundos. Obviamente, isso pode ser convertido para outro formato, se necessário.
date
choke poder se ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"
retorna algo como 26:33:21.68
(isto é, duração ≥ 24 horas / 86400 segundos)
paste
is my favorite command 8-)
Saindo da resposta aceita e usando a ferramenta clássica de polimento reverso 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
Ou seja: Appening +
e, em p
seguida, canalizando isso dc
e você receberá sua soma.
$ 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'
Certifique-se de ter o MPlayer instalado.
Bem, todas essas soluções precisam de um pouco de trabalho, o que eu fiz foi muito simples, 1)
foi para a pasta desejada e clique com o botão direito do mouse -> abrir com outro aplicativo
Em seguida, selecione VLC media player,
aqui está um exemplo
Você pode ver logo abaixo da barra de ferramentas, a lista de reprodução [10:35:51] gravada, para que a pasta contenha 10 horas e 35 minutos e 51 segundos de duração do total de vídeos