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

30

Eu tenho uma lista de .tsarquivos:

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

Como posso obter a duração total (tempo de execução) de todos esses arquivos?

k961
fonte
Como você obtém a duração de um único arquivo?
Hauke ​​Laging
Eu não sei o que também
k961
@Hauke ​​Laging eu encontrei este programa "
mediainfo
Esses são arquivos de mídia, provavelmente vídeo.
slm
1
Quaisquer arquivos de mídia (por exemplo: MP4, ASF & .264 ...) terão informações de cabeçalho padrão predefinidas; podemos obter informações desse arquivo, como resolução, taxa de quadros, número de quadros (GOP) e comprimento e duração do arquivo. media ...
Kantam Nagesh

Respostas:

55

Eu não tenho .tsaqui, 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 .mp4arquivos 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 pastepara passar a saída para bce 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 .tsarquivos, 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 .mp4arquivos 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 exiftoolinterno ConvertDurationpara isso (você precisa de uma versão relativamente recente):

exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)
                    }' ./*.mp4| tail -n1
0:09:15
don_crissti
fonte
Muito bom, não tinha visto esse truque com ffprobeantes.
slm
1
Truque agradável com pastee bc! muito mais limpo do que com awk, digamos.
fduff
@fduff. Embora bcfaça precisão arbitrária, uma desvantagem é que ...| paste -sd+ - | bcatingirá o limite de tamanho da linha em algumas bcimplementações (por exemplo, seq 429 | paste -sd+ - | bcfalha no OpenSolaris bc) ou terá o potencial de usar toda a memória em outras.
Stéphane Chazelas
Você pode fazer isso (método ffprobe) com algo como avconv? Não consigo encontrar o ffmpeg nos meus repositórios do Kubuntu 14.04 - então também não tenho o ffprobe? Eu tenho avprobe, mas não gosta desses argumentos.
Joe
@ Joe - Não avprobeno 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.mp4ou 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.
31514 Don_crissti
6

Isso usa ffmpege 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' | bcexpande cada um dos argumentos e substitui os espaços e os canaliza para bcuma calculadora linux comum

jmunsch
fonte
1
Agradável! Veja também minha versão, que é fortemente baseada em suas idéias.
MvG 02/12
Solução curta e elegante
Neo
4

Simplificando a resposta de @ jmunsch e usando o pasteque 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 ffmpegpara 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 ffmpegcom 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 awkvez do dele grep | grep | head | tr | awk. Essa awkinvocaçã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 ffmpege awk.

Agora vem a parte do slm: estou usando pastepara 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 datepara 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 sedse livra de zeros à direita extras. A TZdefiniçã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á.

MvG
fonte
3

Não estou familiarizado com a .tsextensão, mas supondo que eles sejam algum tipo de arquivo de vídeo que você possa usar ffmpegpara 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.mp4e nomeou-o 1.mp4, 2.mp4e 3.mp4.

Convertendo tempos em segundos

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 datecomando é então usado para calcular o número de segundos sine a época do Unix (01/01/1970). Aqui está o datecomando acima dividido, para que seja mais fácil ver:

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

NOTA: O uso datedessa 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

Totalizando os tempos

Podemos então pegar a saída do nosso forloop e executá-lo em um pastecomando 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, bcpara 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.

slm
fonte
@ DamenSalvatore - não há problema, espero que ele mostre como você pode dividir a tarefa em várias etapas, para que você possa personalizá-la conforme necessário.
slm
@slm - Gostaria de usar uma outra maneira de converter a duração do vídeo para segundos como datechoke 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)
don_crissti
@don_crissti - obrigado, eu não o havia testado há mais de 20 horas ao testá-lo. Vou adicionar uma nota mostrando um método alternativo.
slm
Obrigado pela sua resposta! Não só inspirou o meu , mas também me chamou pastea atenção. Eu acho que java -classpath $(find -name \*.jar | paste -sd:)vai ser muito útil para mim, considerando os hacks que eu usei para isso no passado.
MvG 02/12
@MvG - pasteis my favorite command 8-)
slm
1

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 pseguida, canalizando isso dce você receberá sua soma.

AT
fonte
2
bcrecebe muito amor. Você é tudo colocando +sinais entre cada linha (juntando on +), enquanto que com reverso-polonês você pode simplesmente atirar um +no fim e print-lo;)
AT
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.

ryanmjacobs
fonte
ele não me dar qualquer saída
k961
você tem o mplayer e o perl instalados?
Ryanmjacobs
sim eu instalado mplayer e perl já foi instalado
k961
1
Desculpe, não sei por que não está funcionando; mas você já tem respostas decentes suficientes de qualquer maneira. :)
ryanmjacobs 4/04
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

Fortemente baseado em idéias MvG

Ahed Eid
fonte
0

Bem, todas essas soluções precisam de um pouco de trabalho, o que eu fiz foi muito simples, 1)

  1. foi para a pasta desejada e clique com o botão direito do mouse -> abrir com outro aplicativo

  2. Em seguida, selecione VLC media player,

  3. isso começará a reproduzir um dos vídeos, mas depois
  4. pressione ctrl + L e você verá a lista de reprodução dos vídeos e, em algum lugar no canto superior esquerdo, verá a duração total

aqui está um exemplo

1 Item da lista

2)insira a descrição da imagem aqui

3)insira a descrição da imagem aqui

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

Patel Ujjval Rajeshbhai
fonte
0

Eu tinha subdiretórios na pasta atual, então tive que calcular recursivamente a duração:

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

iMitwe
fonte