Получить общую продолжительность видео файлов в каталоге


30

У меня есть список .tsфайлов:

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

Как я могу получить общую продолжительность (время выполнения) всех этих файлов?


Как вы получаете продолжительность одного файла?
Хауке Лагинг

я тоже этого не знаю
k961

@ Hauke ​​Laging Я нашел эту программу "mediainfo"
k961

Это медиа-файлы, скорее всего видео.
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+ - | bcOpenSolaris произойдет сбой bc), либо будет возможно использование всей памяти в других.
Стефан Шазелас

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

@Joe - Нет avprobeв репозиториях Arch (возможно, потому что он конфликтует с ffmpeg), поэтому не может попробовать его в ATM, но даст ли он вам продолжительность файла, если вы запустите его следующим образом: 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.

Теперь приходит часть из слх: Я использую 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 года. Результирующая «дата» форматируется как день года, время и наносекунды. С того дня года мы вычитаем один, так как ввод нулевых секунд уже приводит к 1-му дню 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.