Obtenha a duração total dos arquivos de vídeo em um diretório

21

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?

    
por k961 02.12.2014 / 05:24

7 respostas

43

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

para todos os arquivos .mp4 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

use paste para passe a saída para bc e obtenha 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

Então, 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 de todos os arquivos .mp4 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 enviar a saída para outro comando para converter o total para DD:HH:MM:SS , veja as respostas aqui .

Ou use exiftool interno de 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
    
por 02.12.2014 / 07:48
5

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 terminam em ".ts"

ffmpeg -i "$f" 2>&1 redireciona a saída para stderr

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

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Converte 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 canaliza para bc uma calculadora comum de linux

    
por 02.12.2014 / 07:09
4

Simplificando a resposta do @jmunsch e usando o paste que acabei de aprender de @ slm's answer , você pode acabar 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 o jmunsch fez, estou usando ffmpeg para imprimir a duração, ignorando o erro sobre um arquivo de saída ausente e, em vez disso, pesquisando a saída de erro para a linha de duração. Eu invoco ffmpeg com todos os aspectos do código de idioma forçados para o código C padrão, para que eu não tenha que me preocupar com mensagens de saída localizadas.

Em seguida, estou usando um único awk em vez de seu grep | grep | head | tr | awk . Essa invocação de awk procura a linha (esperançosamente exclusiva) contendo Duration: . Usando dois pontos como separador, esse rótulo é o campo 1, as horas são o campo 2, os minutos arquivados 3 e o segundo campo 4. A vírgula final após os segundos não parece incomodar meu awk , mas se alguém tiver problemas lá, ele poderia incluir um tr -d , no pipeline entre ffmpeg e awk .

Agora vem a parte de slm: Estou usando paste para substituir novas linhas por sinais de mais, mas sem afetar a nova linha à direita (ao contrário do tr \n + que eu tinha em uma versão anterior desta resposta). Isso dá a expressão sum que pode ser alimentada para bc .

Inspirado pela idéia do slm de usar date para lidar com formatos semelhantes ao tempo, aqui está uma versão que o usa para formatar os segundos resultantes como dias, horas, minutos e segundos com a 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 dentro de $(…) é exatamente como antes. Usando o caractere @ como uma indicação, usamos isso 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, já que uma entrada de zero segundo já leva ao dia 1 daquele ano de 1970. Não acho que haja uma maneira de começar o dia do ano começando em zero.

O% final sed se livra dos zeros extras à direita. A configuração TZ deve forçar o uso do UTC, de modo que o horário de verão não interfira em grandes coleções de vídeo realmente . Se você tiver mais de um ano de vídeo, essa abordagem ainda não funcionará.

    
por 02.12.2014 / 09:31
3

Eu não estou familiarizado com a extensão .ts , mas assumindo que eles são algum tipo de arquivo de vídeo, você pode 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 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 só precisamos de uma maneira de iterar 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

OBSERVAÇÃO: Aqui, no meu exemplo, simplesmente copiei meu arquivo de amostra some.mp4 e nomeei-o como 1.mp4 , 2.mp4 e 3.mp4 .

Convertendo tempos em segundos

O snippet a seguir levará as durações acima e as converterá 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 comando date é então usado para calcular o número de segundos do período do Unix (1970/01/01). Aqui está o comando date acima quebrado para facilitar a visualização:

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

OBSERVAÇÃO: Usar date dessa maneira só funcionará se todos os seus arquivos tiverem uma duração que seja < 24 horas (ou seja, 86400 segundos). Se você precisar de algo que possa lidar com durações maiores, você pode usar isso como uma alternativa:

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

Totalizando os tempos

Podemos, então, obter a saída do nosso loop for e executá-lo em um comando paste , que incorporará + sinais entre cada número, assim:

$ 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

Por fim, colocamos isso na calculadora da linha de comando, bc para resumi-los:

$ 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. Isto pode ser convertido para outro formato, se necessário.

    
por 02.12.2014 / 07:19
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'

Certifique-se de ter o MPlayer instalado.

    
por 02.12.2014 / 06:30
0

Como o ubuntu envia libav em vez de ffmpeg:

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

Baseado strongmente em MvG ideias

    
por 27.12.2016 / 17:15
0

Saindo da resposta aceita e usando a ferramenta clássica de polimento reverso do 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

Ie: Apresen- tando + e p , em seguida, inserindo isso em dc e você Vou pegar sua soma.

    
por 09.02.2017 / 14:56