Uzyskaj całkowity czas trwania plików wideo w katalogu


30

Mam listę .tsplików:

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

Jak mogę uzyskać całkowity czas trwania (czas działania) wszystkich tych plików?


Jak uzyskać czas trwania pojedynczego pliku?
Hauke ​​Laging,

też tego nie wiem
k961

@Hauke ​​Laging znalazłem ten program „
mediainfo

Są to pliki multimedialne, prawdopodobnie wideo.
slm

1
Wszelkie pliki multimedialne (np. MP4, ASF i .264 ...) będą miały predefiniowane standardowe informacje nagłówka, możemy uzyskać informacje z tego pliku, takie jak rozdzielczość, liczba klatek na sekundę, liczba klatek (GOP) oraz długość i czas trwania pliku media ...
Kantam Nagesh

Odpowiedzi:


55

Nie mam .tstutaj, ale to działa .mp4. Użyj ffprobe(część ffmpeg), aby uzyskać czas w sekundach, np .:

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

więc dla wszystkich .mp4plików w bieżącym katalogu:

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

następnie użyć pasteaby przekazać wyjście do bci uzyskać całkowity czas w sekundach:

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

W przypadku .tsplików możesz spróbować:

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

Innym narzędziem, które działa dla plików wideo, które tu mam, jest exiftoolnp .:

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

Całkowita długość wszystkich .mp4plików w bieżącym katalogu:

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

Możesz również przesłać dane wyjściowe do innego polecenia, aby przekonwertować sumę DD:HH:MM:SS, zobacz odpowiedzi tutaj .

Lub użyj do tego exiftoolwewnętrznego ConvertDuration(potrzebujesz jednak stosunkowo nowej wersji):

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

Bardzo fajnie, nie widziałem tej sztuczki ffprobewcześniej.
slm

1
Niezła sztuczka z pastei bc! znacznie czystsze niż awkpowiedzmy.
fduff,

@fduff. Chociaż bcbędzie to miało dowolną precyzję, jedną wadą jest to, że ...| paste -sd+ - | bcalbo osiągnie limit rozmiaru linii w niektórych bcimplementacjach (na przykład seq 429 | paste -sd+ - | bczawiedzie w OpenSolarisa bc), albo może potencjalnie wykorzystać całą pamięć w innych.
Stéphane Chazelas,

Czy możesz to zrobić (metoda ffprobe) za pomocą czegoś takiego jak avconv? Nie mogę znaleźć ffmpeg w moich repozytoriach dla Kubuntu 14.04 - więc też nie mam ffprobe? Mam avprobe, ale nie podoba mi się te argumenty.
Joe

@Joe - Nie avprobew repozytoriach Arch (prolly, ponieważ powoduje konflikt ffmpeg), więc nie można wypróbować bankomatu, ale czy daje on czas trwania pliku, jeśli uruchomisz go w ten sposób: avprobe -show_format_entry duration myfile.mp4lub avprobe -loglevel quiet -show_format_entry duration myfile.mp4? Myślę, że jedno z tych poleceń powinno dać ci jeden wiersz wyniku z czasem trwania pliku. Nie jestem pewien.
don_crissti

6

Spowoduje to użycie ffmpegi wydrukowanie limitu czasu w całkowitych sekundach:

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

Wyjaśnienie:

for f in *.ts; do iteruje każdy z plików, który kończy się na „.ts”

ffmpeg -i "$f" 2>&1 przekierowuje wyjście do stderr

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

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Konwertuje czas na sekundy

times+=("$_t") dodaje sekundy do tablicy

echo "${times[@]}" | sed 's/ /+/g' | bcrozwija każdy z argumentów i zastępuje spacje i potokuje je do bcwspólnego kalkulatora linux


1
Miły! Zobacz także moją wersję, która jest w dużej mierze oparta na twoich pomysłach.
MvG,

Krótkie i eleganckie rozwiązanie
Neo

4

Usprawniając odpowiedź @ jmunsch i korzystając z odpowiedzi , pastektórą właśnie nauczyłem się z @ slm , możesz otrzymać coś takiego:

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

Podobnie jak jmunsch, używam ffmpegdo drukowania czasu trwania, ignorując błąd dotyczący brakującego pliku wyjściowego i zamiast tego szukam w wierszu czasu trwania błędu. Inwokuję ffmpegze wszystkimi aspektami ustawień narodowych wymuszonymi na standardowe ustawienia regionalne C, aby nie musiałem się martwić o zlokalizowane komunikaty wyjściowe.

Następnie używam singla awkzamiast jego grep | grep | head | tr | awk. To awkwywołanie szuka wiersza zawierającego (miejmy nadzieję unikatowego) Duration:. Używając dwukropka jako separatora, etykieta jest polem 1, godziny są polem 2, minutami zapisanymi 3 i polem sekund 4. Przecinek końcowy po sekundach nie przeszkadza mi awk, ale jeśli ktoś ma problemy, on może zawierać tr -d ,w potoku między ffmpegi awk.

Teraz zaczyna się od SLM: Używam pastezastąpić newlines z znaki plus, ale bez wpływu na nowej linii spływu (w przeciwieństwie do tr \\n +miałem w poprzedniej wersji tej odpowiedzi). To daje sumę wyrażenia, którą można podać bc.

Zainspirowana pomysłem SLM dotyczącym dateobsługi formatów podobnych do czasu, oto wersja, która używa go do formatowania wynikowych sekund jako dni, godzin, minut i sekund z częścią ułamkową:

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]*$//'

Część wewnątrz $(…)jest dokładnie taka jak poprzednio. Używając tego @znaku jako wskaźnika, używamy go jako liczby sekund od 1 stycznia 1970 roku. Powstała „data” jest formatowana jako dzień roku, godzina i nanosekundy. Od tego dnia roku odejmujemy jeden, ponieważ wprowadzanie wartości zero sekund prowadzi już do dnia 1 tego roku 1970. Nie sądzę, że istnieje sposób, aby liczenie dnia roku zaczynało się od zera.

Finał sedpozbywa się dodatkowych zer końcowych. TZUstawienie powinno nadzieją wymusić użycie UTC, tak że czas letni nie będzie kolidować z naprawdę dużych zbiorów wideo. Jeśli masz wideo dłuższe niż rok, to podejście nadal nie zadziała.


3

Nie znam .tsrozszerzenia, ale zakładając, że jest to jakiś plik wideo, którego można użyć ffmpegdo określenia czasu trwania pliku:

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

Następnie możemy podzielić ten wynik, wybierając tylko czas trwania.

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

Teraz potrzebujemy tylko sposobu na iterację naszych plików i zebranie tych wartości czasu trwania.

$ 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

UWAGA: Tutaj dla mojego przykładu po prostu skopiowałem mój przykładowy plik some.mp4i nadałem mu nazwę 1.mp4,2.mp4 i 3.mp4.

Przeliczanie czasów na sekundy

Poniższy fragment zabierze czas trwania z góry i zamieni go na sekundy.

$ 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

To zajmuje nasze czasy trwania i umieszcza je w zmiennej, $durgdy przeglądamy pliki. datePolecenia te są następnie wykorzystywane do obliczenia liczby sekund sinusoidalnych epoki owych (1970/01/01). Oto powyższe datepolecenie, dzięki czemu łatwiej zobaczyć:

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

UWAGA: Używanie datew ten sposób będzie działać tylko wtedy, gdy wszystkie twoje pliki mają czas trwania <24 godziny (tj. 86400 sekund). Jeśli potrzebujesz czegoś, co poradzi sobie z dłuższym czasem trwania, możesz użyć tego jako alternatywy:

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

Sumując czasy

Następnie możemy pobrać dane wyjściowe z naszej forpętli i uruchomić je w pastepoleceniu, które będzie zawierało +znaki między każdą liczbą, w następujący sposób:

$ 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

Na koniec uruchamiamy to w kalkulatorze wiersza poleceń, bcaby je podsumować:

$ 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

Łączny czas trwania wszystkich plików, w sekundach. W razie potrzeby można to oczywiście przekonwertować na inny format.


@DamenSalvatore - nie ma problemu, mam nadzieję, że pokazuje, w jaki sposób możesz podzielić zadanie na różne etapy, dzięki czemu możesz go dostosować w razie potrzeby.
slm

@slm - Chciałbym użyć innego sposobu, aby przekonwertować czas trwania filmu na sekundy, ponieważ datemoże się udusić, jeśli ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"zwróci coś podobnego 26:33:21.68(to znaczy czas trwania ≥ 24 godziny / 86400 sekund)
don_crissti

@don_crissti - dzięki, nie wypróbowałem go dłużej niż 20 godzin podczas testowania. Dodam notatkę pokazującą alternatywną metodę.
slm

Dzięki za odpowiedź! Nie tylko zainspirowało mnie , ale także pastezwróciło moją uwagę. Myślę, że przyda mi java -classpath $(find -name \*.jar | paste -sd:)się bardzo , biorąc pod uwagę włamania, których użyłem w przeszłości.
MvG,

@MvG - pasteto moje ulubione polecenie 8-)
slm

1

Wyjście z zaakceptowanej odpowiedzi i użycie klasycznego narzędzia do polerowania w systemie 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

Tj .: dodawanie, +a pnastępnie wpuszczanie tego do, dca otrzymasz swoją sumę.


2
bcdostaje o wiele za dużo miłości. Wszyscy stawiacie +znaki między każdą linią (łączenie się +), podczas gdy przy polerowaniu wstecznym można po prostu rzucić a +na końcu i pzrzucić to;)
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'

Upewnij się, że masz zainstalowany MPlayer .


nie daje mi żadnych wyników
k961

czy masz zainstalowany mplayer i perl?
ryanmjacobs,

tak, zainstalowałem mplayera, a perl został już zainstalowany
k961

1
Przepraszam, nie wiem, dlaczego to nie działa; ale i tak masz już dość przyzwoitych odpowiedzi. :)
ryanmjacobs

0

Jako Ubuntu wysyłamy libav zamiast ffmpeg:

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

Mocno oparty na pomysłach MvG


0

Cóż, te wszystkie rozwiązania wymagają trochę pracy, to co zrobiłem było bardzo proste, 1)

  1. poszedł do żądanego folderu i kliknij prawym przyciskiem myszy -> otwórz za pomocą innej aplikacji

  2. Następnie wybierz odtwarzacz multimedialny VLC,

  3. zacznie się odtwarzanie jednego z filmów, ale potem
  4. naciśnij ctrl + L , a zobaczysz listę odtwarzania filmów, a gdzieś w lewym górnym rogu zobaczysz całkowity czas trwania

oto przykład

1. Element listy

2)wprowadź opis zdjęcia tutaj

3)wprowadź opis zdjęcia tutaj

Możesz zobaczyć tuż pod paskiem narzędzi, napisana jest lista odtwarzania [10:35:51] , więc folder zawiera 10 godzin 35 minut i 51 sekund czasu trwania wszystkich filmów


0

Miałem podkatalogi w bieżącym folderze, więc musiałem rekurencyjnie obliczać czas trwania:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.