获取目录中视频文件的总时长

k96*_*961 39 video shell-script

我有一个.ts文件列表:

out1.ts ... out749.ts   out8159.ts  out8818.ts
Run Code Online (Sandbox Code Playgroud)

如何获得所有这些文件的总持续时间(运行时间)?

don*_*sti 76

.ts这里没有,但这适用于.mp4. 使用ffprobe(part of ffmpeg) 获取以秒为单位的时间,例如:

ffprobe -v quiet -of csv=p=0 -show_entries format=duration Inception.mp4 
275.690000
Run Code Online (Sandbox Code Playgroud)

所以对于.mp4当前目录中的所有文件:

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

然后使用paste输出传递bc并获得秒的总时间:

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

因此,对于.ts文件,您可以尝试:

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

另一个适用于我这里的视频文件的工具是exiftool,例如:

exiftool -S -n Inception.mp4 | grep ^Duration
Duration: 275.69
exiftool -q -p '$Duration#' Inception.mp4
275.69
Run Code Online (Sandbox Code Playgroud)

.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
Run Code Online (Sandbox Code Playgroud)

您还可以将输出通过管道传输到另一个命令以将总数转换为DD:HH:MM:SS,请参阅此处的答案。

或者为此使用exiftool's internal ConvertDuration(不过您需要一个相对较新的版本):

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


jmu*_*sch 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
Run Code Online (Sandbox Code Playgroud)

解释:

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 计算器


MvG*_*MvG 5

简化@jmunsch\的答案,并使用paste我刚刚从@slm\的答案中学到的,你最终可能会得到这样的结果:

\n\n
for i in *.ts; do LC_ALL=C ffmpeg -i "$i" 2>&1 | \\\nawk -F: \'/Duration:/{print $2*3600+$3*60+$4}\'; done | paste -sd+ | bc\n
Run Code Online (Sandbox Code Playgroud)\n\n

就像 jmunsch 所做的那样,我用来ffmpeg打印持续时间,忽略有关丢失输出文件的错误,而是在错误输出中搜索持续时间行。我将ffmpeg语言环境的所有方面强制为标准 C 语言环境进行调用,这样我就不必担心本地化的输出消息。

\n\n

接下来我将使用单个awk而不是他的grep | grep | head | tr | awk. 该awk调用会查找包含 的(希望是唯一的)行Duration:。使用冒号作为分隔符,该标签是字段 1,小时是字段 2,分钟是字段 3,秒是字段 4。秒后面的逗号似乎不会打扰我awk,但如果有人在那里遇到问题,他可以在和tr -d ,之间添加一个管道。ffmpegawk

\n\n

现在来自 slm 的部分:我正在用paste加号替换换行符,但不会影响尾随换行符(与tr \\\\n +我在这个答案的先前版本中的相反)。这给出了可以输入到 的总和表达式bc

\n\n

受到 slm 用于date处理类似时间格式的想法的启发,这里有一个版本,它确实使用它来将结果秒格式化为带小数部分的天、小时、分钟和秒:

\n\n
TZ=UTC+0 date +\'%j %T.%N\' --date=@$(for i in *.ts; do LC_ALL=C \\\nffmpeg -i "$i" 2>&1 | awk -F: \'/Duration:/{print $2*3600+$3*60+$4}\'; done \\\n| paste -sd+ | bc) | awk \'{print $1-1 "d",$2}\' | sed \'s/[.0]*$//\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

里面的部分$(\xe2\x80\xa6)和以前一模一样。使用该@字符作为指示,我们将其用作自 1970 年 1 月 1 日以来的秒数。生成的 \xe2\x80\x9cdate\xe2\x80\x9d 被格式化为一年中的日期、时间和纳秒。从一年中的这一天我们减去一,因为输入零秒已经导致 1970 年的第一天。我认为没有办法让一年中的某一天从零开始计数。

\n\n

最后的结果sed去掉了多余的尾随零。该TZ设置有望强制使用 UTC,以便夏令时不会干扰真正的大型视频集合。不过,如果您有超过一年的视频,这种方法仍然行不通。

\n