Nhận tổng thời lượng của các tệp video trong một thư mục


30

Tôi có một danh sách các .tstập tin:

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

Làm cách nào tôi có thể nhận được tổng thời lượng (thời gian chạy) của tất cả các tệp này?


Làm thế nào để bạn có được thời lượng của một tập tin?
Hauke ​​Laging

tôi cũng không biết điều đó
k961

@Hauke ​​Laging tôi tìm thấy chương trình này "mediainfo"
k961

Đây là các tập tin phương tiện, video có khả năng.
SLM

1
Bất kỳ tệp phương tiện nào (ví dụ: MP4, ASF & .264 ...) sẽ có thông tin tiêu đề chuẩn được xác định trước, chúng tôi có thể lấy thông tin từ tệp đó như độ phân giải, tốc độ khung hình, số khung (GOP) & thời lượng tệp & thời lượng của phương tiện truyền thông ...
Kantam Nagesh

Câu trả lời:


55

Tôi không có .tsở đây nhưng điều này làm việc cho .mp4. Sử dụng ffprobe(một phần ffmpeg) để lấy thời gian tính bằng giây, ví dụ:

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

vì vậy đối với tất cả .mp4các tệp trong thư mục hiện tại:

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

sau đó sử dụng pasteđể chuyển đầu ra đến bcvà nhận tổng thời gian tính bằng giây:

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

Vì vậy, đối với .tscác tệp bạn có thể thử:

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

Một công cụ khác hoạt động cho các tập tin video tôi có ở đây là exiftool , ví dụ:

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

Tổng chiều dài cho tất cả .mp4 các tệp trong thư mục hiện tại:

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

Bạn cũng có thể chuyển đầu ra sang một lệnh khác để chuyển đổi tổng thành DD:HH:MM:SS , xem câu trả lời ở đây .

Hoặc sử dụng exiftoolnội bộ ConvertDurationcho điều đó (mặc dù bạn cần một phiên bản tương đối gần đây):

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

Rất đẹp, chưa từng thấy mánh khóe ffprobeđó trước đây.
slm

1
Thủ thuật hay với pastebc! sạch sẽ hơn nhiều so với awkgiả sử.
fduff

@fduff. Mặc dù bcsẽ có độ chính xác tùy ý, một nhược điểm là ...| paste -sd+ - | bcsẽ đạt đến giới hạn kích thước dòng trong một số bctriển khai (ví dụ như seq 429 | paste -sd+ - | bcthất bại với OpenSolaris bc) hoặc có khả năng sử dụng hết bộ nhớ trong các bộ nhớ khác.
Stéphane Chazelas

Bạn có thể làm điều này (phương pháp ffprobe) với một cái gì đó như avconv? Tôi không thể tìm thấy ffmpeg trong repos của mình cho Kubfox 14.04 - vì vậy tôi cũng không có ffprobe? Tôi có avprobe, nhưng nó không thích những tranh luận đó.
Joe

@Joe - Không có avprobetrong Arch repos (prolly vì nó xung đột với ffmpeg) vì vậy không thể dùng thử ATM nhưng nó có cung cấp cho bạn thời lượng của tệp nếu bạn chạy nó như thế này: avprobe -show_format_entry duration myfile.mp4hay avprobe -loglevel quiet -show_format_entry duration myfile.mp4? Tôi nghĩ rằng một trong những lệnh này sẽ cung cấp cho bạn một dòng đầu ra duy nhất với thời lượng của tệp. Không chắc chắn mặc dù.
don_crissti

6

Điều này sử dụng ffmpegvà in thời gian ra trong tổng số giây:

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

Giải trình:

for f in *.ts; do lặp lại từng tệp kết thúc bằng ".ts"

ffmpeg -i "$f" 2>&1 chuyển hướng đầu ra cho thiết bị lỗi chuẩn

grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' cô lập thời gian

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Chuyển đổi thời gian thành giây

times+=("$_t") thêm giây vào một mảng

echo "${times[@]}" | sed 's/ /+/g' | bcmở rộng từng đối số và thay thế khoảng trắng và chuyển nó sang bcmột máy tính linux phổ biến


1
Tốt đẹp! Cũng xem phiên bản của tôi dựa nhiều vào ý tưởng của bạn.
MvG

Giải pháp ngắn gọn và thanh lịch
Neo

4

Sắp xếp hợp lý câu trả lời của @ jmunsch và sử dụng câu trả lờipaste tôi vừa học được từ câu trả lời của @ slm , bạn có thể kết thúc bằng một câu như thế này:

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

Giống như jmunsch đã làm, tôi đang sử dụng ffmpegđể in thời lượng, bỏ qua lỗi về một tệp đầu ra bị thiếu và thay vào đó tìm kiếm đầu ra lỗi cho dòng thời lượng. Tôi gọiffmpeg với tất cả các khía cạnh của miền địa phương buộc phải đến miền địa phương C tiêu chuẩn, do đó tôi sẽ không phải lo lắng về các thông điệp đầu ra được bản địa hóa.

Tiếp theo tôi đang sử dụng một awkthay vì của mình grep | grep | head | tr | awk. Lời mời đó awktìm kiếm dòng (hy vọng duy nhất) có chứa Duration:. Sử dụng dấu hai chấm làm dấu phân cách, nhãn đó là trường 1, giờ là trường 2, phút nộp 3 và trường giây 4. Dấu phẩy sau giây dường như không làm phiền tôi awk, nhưng nếu ai đó có vấn đề ở đó, anh ta có thể bao gồm một tr -d ,trong các đường ống giữa ffmpegawk.

Bây giờ đến phần từ slm: Tôi đang sử dụng pasteđể thay thế dòng mới bằng dấu cộng, nhưng không ảnh hưởng đến dòng mới (trái với câu tr \\n +tôi đã có trong phiên bản trước của câu trả lời này). Điều đó đưa ra biểu thức tổng có thể được cung cấp chobc .

Lấy cảm hứng từ ý tưởng sử dụng slm dateđể xử lý các định dạng giống như thời gian, đây là phiên bản sử dụng nó để định dạng các giây kết quả là ngày, giờ, phút và giây với phần phân số:

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

Phần bên trong $(…)chính xác như trước. Sử dụng@ ký tự làm chỉ dẫn, chúng tôi sử dụng số này làm số giây kể từ ngày 1 tháng 1 năm 1970. Kết quả là ngày NGÀY được định dạng là ngày trong năm, thời gian và nano giây. Từ ngày đó trong năm, chúng tôi trừ đi một, vì đầu vào bằng 0 giây đã dẫn đến ngày 1 của năm 1970. Tôi không nghĩ có cách nào để có được số ngày trong năm bắt đầu từ 0.

Trận chung kết sedđược loại bỏ các số 0 thêm. Các TZthiết lập hy vọng sẽ buộc việc sử dụng UTC, do đó tiết kiệm thời gian ban ngày sẽ không gây trở ngại cho sự sưu tập video lớn. Tuy nhiên, nếu bạn có video có giá trị hơn một năm, thì phương pháp này vẫn không hiệu quả.


3

Tôi không quen thuộc với .tstiện ích mở rộng, nhưng giả sử chúng là một loại tệp video bạn có thể sử dụng ffmpegđể xác định thời lượng của tệp như vậy:

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

Sau đó chúng ta có thể tách đầu ra này lên, chỉ chọn thời lượng.

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

Vì vậy, bây giờ chúng ta chỉ cần một cách để lặp qua các tệp của mình và thu thập các giá trị thời lượng này.

$ 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

LƯU Ý: Ở đây ví dụ của tôi, tôi chỉ đơn giản là sao chép tập tin mẫu của tôi some.mp4và đặt tên cho nó 1.mp4, 2.mp43.mp4.

Chuyển đổi thời gian thành giây

Đoạn mã sau sẽ lấy thời lượng từ phía trên và chuyển đổi chúng thành giây.

$ 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

Điều này có thời lượng của chúng tôi và đặt chúng trong một biến $dur, khi chúng tôi lặp qua các tệp. Các datesau đó lệnh được sử dụng để tính toán số giây sin kỷ nguyên Unix (1970/01/01). Đây là datelệnh trên được chia ra để dễ nhìn hơn:

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

LƯU Ý: Sử dụng datetheo cách này sẽ chỉ hoạt động nếu tất cả các tệp của bạn có thời lượng <24 giờ (tức là 86400 giây). Nếu bạn cần một cái gì đó có thể xử lý thời lượng lớn hơn, bạn có thể sử dụng nó như là một thay thế:

sed 's/^/((/; s/:/)*60+/g' | bc
Thí dụ
$ echo 44:29:36.01 | sed 's/^/((/; s/:/)*60+/g' | bc
160176.01

Tổng số lần

Sau đó, chúng ta có thể lấy đầu ra của forvòng lặp của mình và chạy nó thành một pastelệnh sẽ kết hợp +các dấu hiệu ở giữa mỗi số, như vậy:

$ 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

Cuối cùng, chúng tôi chạy nó vào máy tính dòng lệnh, bcđể tổng hợp chúng:

$ 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

Kết quả là tổng thời lượng của tất cả các tệp, tính bằng giây. Điều này tất nhiên có thể được chuyển đổi sang một số định dạng khác nếu cần.


@DamenSalvatore - không có vấn đề gì, hy vọng nó cho bạn thấy làm thế nào bạn có thể chia nhiệm vụ thành nhiều bước khác nhau, để bạn có thể tùy chỉnh nó khi cần.
slm

@slm - Tôi sẽ sử dụng một cách khác để chuyển đổi thời lượng video thành giây vì datecó thể bị nghẹt nếu ffmpeg -i some.mp4 2>&1 | grep -oP "(?<=Duration: ).*(?=, start.*)"trả về một cái gì đó như 26:33:21.68(nghĩa là thời lượng ≥ 24 giờ / 86400 giây)
don_crissti

@don_crissti - cảm ơn, tôi đã không thử nó sau 20 giờ khi thử nghiệm nó. Tôi sẽ thêm một ghi chú cho thấy một phương pháp thay thế.
slm

Cảm ơn câu trả lời của bạn! Nó không chỉ truyền cảm hứng cho tôi mà còn khiến pastetôi chú ý. Tôi đoán java -classpath $(find -name \*.jar | paste -sd:)là sẽ rất hữu ích với tôi, xem xét các vụ hack tôi đã sử dụng cho việc này trong quá khứ.
MvG

@MvG - pastelà lệnh ưa thích của tôi 8-)
slm

1

Bỏ qua câu trả lời được chấp nhận và sử dụng công cụ đánh bóng ngược UNIX cổ điển:

{ find . -maxdepth 2 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 \
         -show_entries format=duration {} \; ; printf '+\n60\n*\np'; } | dc

783.493000

Tức là: Xuất hiện +psau đó chuyển nó vào dcvà bạn sẽ nhận được số tiền của mình.


2
bcnhận được quá nhiều tình yêu Tất cả các bạn đều đặt +các dấu hiệu giữa mỗi dòng (tham gia vào +), trong khi với đánh bóng ngược, bạn có thể chỉ cần tặc lưỡi +ở cuối và pxé nó ra;)
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'

Hãy chắc chắn rằng bạn đã cài đặt MPlayer .


nó không cho tôi bất kỳ đầu ra nào
k961

Bạn đã cài đặt mplayer và perl chưa?
ryanmjacobs

vâng tôi đã cài đặt mplayer và perl đã được cài đặt
k961

1
Xin lỗi, tôi không biết tại sao nó không hoạt động; nhưng, bạn đã có đủ câu trả lời đàng hoàng rồi. :)
ryanmjacobs

0

Như ub Ubuntu tàu libav thay vì ffmpeg:

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

Dựa trên những ý tưởng của MvG


0

Chà, tất cả những giải pháp này cần một chút công việc, những gì tôi đã làm rất đơn giản, 1)

  1. đi đến thư mục mong muốn và nhấp chuột phải -> mở với ứng dụng khác

  2. Sau đó chọn trình phát phương tiện VLC,

  3. điều này sẽ bắt đầu phát một trong các video, nhưng sau đó
  4. nhấn ctrl + L và bạn sẽ thấy danh sách phát của video và ở đâu đó ở góc trên cùng bên trái, bạn sẽ thấy tổng thời lượng

đây là ví dụ

1. Danh sách mục

2.nhập mô tả hình ảnh ở đây

3.nhập mô tả hình ảnh ở đây

Bạn có thể thấy ngay dưới thanh công cụ, có Playlist [10:35:51] được viết, vì vậy thư mục chứa 10 giờ 35 phút và thời lượng 51 giây của tổng số video


0

Tôi đã có các thư mục con trên thư mục hiện tại vì vậy tôi phải tính toán đệ quy thời lượng:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.