Отримайте загальну тривалість відеофайлів у каталозі


30

У мене є список .tsфайлів:

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

Як я можу отримати загальну тривалість (час роботи) всіх цих файлів?


Як отримати тривалість одного файлу?
Хоуке Лагінг

Я теж не знаю, що це
k961

@Hauke ​​Laging Я знайшов цю програму "
медіаінфо

Це мультимедійні файли, ймовірно, відео.
slm

1
Будь-які медіа-файли (наприклад, MP4, ASF & .264 ...) матимуть попередньо визначену стандартну інформацію заголовка, ми можемо отримати інформацію з цього файлу, як роздільну здатність, частоту кадрів, кількість кадрів (GOP) та довжину файлу та тривалість медіа ...
Kantam Nagesh

Відповіді:


55

У мене немає .tsтут, але це працює для .mp4. Використовуйте ffprobe(частину ffmpeg), щоб отримати час у секундах, наприклад:

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

тому для всіх .mp4файлів у поточному режимі:

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

потім використовувати pasteдля передачі вихідних даних, щоб bcі отримати загальний час в секундах:

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

Отже, для .tsфайлів ви можете спробувати:

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

Інший інструмент, який працює для відеофайлів, які я маю тут exiftool, наприклад:

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

Загальна довжина для всіх .mp4файлів у поточному каталозі:

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

Ви також можете передати висновок в іншу команду для перетворення загальної суми DD:HH:MM:SS, дивіться відповіді тут .

Або використовуйте для цього exiftoolвнутрішній ConvertDuration(хоча вам потрібна відносно недавня версія):

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

Дуже приємно, не бачив цього трюку ffprobeраніше.
slm

1
Гарний трюк з pasteі bc! набагато чистіше, ніж awk, скажімо.
fduff

@fduff. Хоча це bcбуде робити довільну точність, один недолік полягає в тому, що ...| paste -sd+ - | bcвін досягне межі розміру рядка в деяких bcреалізаціях (наприклад, seq 429 | paste -sd+ - | bcне працює з OpenSolaris bc) або матиме потенціал використання всієї пам'яті в інших.
Стефан Шазелас

Чи можете ви це зробити (метод ffprobe) з чимось на зразок avconv? Я не можу знайти ffmpeg у своїх репортажах для Kubuntu 14.04 - значить, у мене також немає ffprobe? У мене є avprobe, але це аргументи не подобається.
Джо

@Joe - Ні avprobeв Arch repos (prolly тому, що це суперечить ffmpeg), тому не можна спробувати його в банкоматі, але він дає вам тривалість файлу, якщо ви запускаєте його так: avprobe -show_format_entry duration myfile.mp4або avprobe -loglevel quiet -show_format_entry duration myfile.mp4? Я думаю, що одна з цих команд повинна дати вам один рядок виводу з тривалістю файлу. Не впевнений, хоча.
don_crissti

6

При цьому використовується ffmpegі друкується час очікування в загальних секундах:

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

Пояснення:

for f in *.ts; do ітераціює кожен з файлів, який закінчується у ".ts"

ffmpeg -i "$f" 2>&1 перенаправляє вихід на stderr

grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' виділяє час

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Перетворює час у секунди

times+=("$_t") додає секунди до масиву

echo "${times[@]}" | sed 's/ /+/g' | bcрозширює кожен з аргументів і замінює пробіли та передає їх bcзагальному калькулятору Linux


1
Приємно! Також дивіться мою версію, яка базується на ваших ідеях.
MvG

Коротке та елегантне рішення
Neo

4

Упорядкуйте відповідь @ jmunsch , і використовуючи pasteщойно я дізнався з відповіді @ slm , ви можете закінчити щось подібне:

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

Так само, як це робив jmunsch, я використовую ffmpegдля друку тривалості, ігноруючи помилку про відсутній вихідний файл і замість цього шукаю вихід помилки для рядка тривалості. Я посилаюся ffmpegна всі аспекти локальної мови, примусової до стандартної мови C, так що мені не доведеться турбуватися про локалізовані вихідні повідомлення.

Далі я використовую сингл awkзамість його grep | grep | head | tr | awk. Це awkвиклик шукає (сподіваюся, унікальний) рядок, що містить Duration:. Використовуючи двокрапку як роздільник, ця мітка - це поле 1, години - це поле 2, хвилини подано 3, а поле секунди 4. Послідовна кома після секунд, схоже, не турбує мене awk, але якщо у когось є проблеми, він може включати tr -d ,в трубопровід між ffmpegі awk.

Тепер йде частина від slm: я використовую pasteдля заміни нових рядків знаками плюс, але не впливаючи на зворотний новий рядок (всупереч тому, що tr \\n +я мав у попередній версії цієї відповіді). Це дає суму вираження, до якої можна подати bc.

Натхненна ідеєю slm використовувати dateдля обробки форматів, схожих на час, ось версія, яка використовує його для форматування отриманих секунд у вигляді днів, годин, хвилин і секунд з дробовою частиною:

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

Частина всередині $(…)точно така, як і раніше. Використовуючи @символ як вказівку, ми використовуємо це як кількість секунд з 1 січня 1970 року. Отримана "дата" формується як день року, час та наносекунд. З цього дня року ми віднімаємо одне, оскільки введення нульових секунд вже призводить до першого дня 1970-го року. Я не думаю, що існує спосіб отримати число рахунків року, починаючи з нуля.

Фінал sedпозбавляється від зайвих нулів. TZСподіваємось, цей параметр повинен змусити використовувати UTC, щоб літній час не заважав дійсно великим колекціям відео. Якщо у вас є відеоролик, який коштує більше року, цей підхід все одно не буде працювати.


3

Я не знайомий з .tsрозширенням, але припускаючи, що це певний тип відеофайлу, який ви можете використовувати ffmpegдля визначення тривалості файлу, наприклад:

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

Потім ми можемо розділити цей результат вгору, вибравши лише тривалість часу.

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

Отже, зараз нам просто потрібен спосіб перебрати наші файли та зібрати ці значення тривалості.

$ 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

Примітка: Тут для мого прикладу я просто скопіював мій зразок файл some.mp4і назвав його 1.mp4, 2.mp4і 3.mp4.

Перетворення часу в секунди

Наступний фрагмент візьме тривалість зверху та перетворить їх на секунди.

$ 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

Це займає нашу тривалість і ставить їх у змінну $dur, коли ми перебираємо файли. Потім dateкоманда використовується для обчислення кількості секунд, що належать епосі Unix (1970/01/01). Ось наведена вище dateкоманда розбита, тому її легше побачити:

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

ПРИМІТКА. Використання dateцього способу працює лише в тому випадку, якщо всі ваші файли тривалістю <24 години (тобто 86400 секунд). Якщо вам потрібно щось, що може працювати з більшою тривалістю, ви можете використовувати це як альтернативу:

sed 's/^/((/; s/:/)*60+/g' | bc
Приклад
$ echo 44:29:36.01 | sed 's/^/((/; s/:/)*60+/g' | bc
160176.01

Підсумовування разів

Потім ми можемо взяти висновок нашого forциклу і запустити його в pasteкоманду, яка буде включати +знаки між кожним числом, наприклад:

$ 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

Нарешті ми запускаємо це в калькулятор командного рядка, bcщоб підсумувати їх:

$ 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

Результат загальної тривалості всіх файлів, в секундах. Звичайно, за потреби це можна перетворити на інший формат.


@DamenSalvatore - не проблема, сподіваємось, він показує, як ви можете розбити завдання на різні етапи, так що ви можете налаштувати його за потребою.
slm

@slm - я б скористався іншим способом перетворення тривалості відео у секунди, як це dateможе задихнутися, якщо ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"поверне щось подібне 26:33:21.68(тобто тривалість ≥ 24 години / 86400 секунд)
don_crissti

@don_crissti - спасибі, я не пробував це протягом 20 годин, коли тестував його. Я додам примітку із зазначенням альтернативного методу.
slm

Дякую за вашу відповідь! Це не тільки надихнуло мою , але й принесло pasteмою увагу. Гадаю, мені java -classpath $(find -name \*.jar | paste -sd:)це стане дуже корисним, враховуючи хаки, які я раніше використовував для цього.
MvG

@MvG - pasteмоя улюблена команда 8-)
slm

1

Вихід із прийнятої відповіді та використання класичного інструменту зворотної полірування 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

Тобто: Появившись, +а pпотім вставте це в нього, dcі ви отримаєте свою суму.


2
bcотримує занадто багато кохання. Ви все покласти +знаки між кожною лінією (вступ в +), в той час як зі зворотним полірують ви можете просто Чак +на кінці і pРінту його;)
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'

Будьте впевнені, що у вас встановлений MPlayer .


він не дає мені жодного результату
k961

у вас встановлені mplayer та perl?
ryanmjacobs

так, я вже встановив mplayer і perl вже встановлено
k961

1
Вибачте, я не знаю, чому це не працює; але, у вас уже є достатньо гідних відповідей. :)
ryanmjacobs

0

Як ubuntu корабель libav замість ffmpeg:

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

Сильно заснована на ідеях MvG


0

Ну, над цим рішенням потрібно трохи попрацювати, що я зробив було дуже просто, 1)

  1. перейшов у потрібну папку і клацніть правою кнопкою миші -> відкрити з іншим додатком

  2. Потім виберіть медіаплеєр VLC,

  3. це почне відтворення одного з відео, але потім
  4. натисніть ctrl + L , і ви побачите список відтворення відео, а десь у верхньому лівому куті ви побачите загальну тривалість

ось приклад

1. Елемент списку

2.введіть тут опис зображення

3.введіть тут опис зображення

Ви можете бачити прямо під панеллю інструментів, там написано Плейлист [10:35:51] , тому папка містить 10 годин 35 хвилин та загальну тривалість 51 сек.


0

Я мав підкаталоги в поточній папці, тому мені довелося рекурсивно обчислювати тривалість:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.