it-swarm-vi.com

Làm cách nào tôi có thể sử dụng ffmpeg để chia video MPEG thành các đoạn 10 phút?

Thường có nhu cầu trong cộng đồng nhà phát triển nguồn mở hoặc hoạt động để xuất bản các phân đoạn video lớn trực tuyến. (Video gặp gỡ, cắm trại, nói chuyện về công nghệ ...) Tôi là một nhà phát triển và không phải là nhà quay phim, tôi không muốn bỏ thêm tiền vào tài khoản Vimeo cao cấp. Sau đó, làm cách nào để tôi có một video nói chuyện công nghệ MPEG 12,5 GB (1:20:00) và cắt nó thành các đoạn 00:10:00 để dễ dàng tải lên các trang chia sẻ video?

73
Gabriel
$ ffmpeg -i source-file.foo -ss 0 -t 600 first-10-min.m4v
$ ffmpeg -i source-file.foo -ss 600 -t 600 second-10-min.m4v
$ ffmpeg -i source-file.foo -ss 1200 -t 600 third-10-min.m4v
...

Gói nó thành một kịch bản để thực hiện nó trong một vòng lặp sẽ không khó.

Xin lưu ý rằng nếu bạn cố gắng tính số lần lặp dựa trên đầu ra thời lượng từ một cuộc gọi ffprobe thì đây là ước tính từ mức trung bình tốc độ bit khi bắt đầu clip và kích thước tệp của clip trừ khi bạn cho -count_frames đối số, làm chậm hoạt động của nó đáng kể.

Một điều khác cần lưu ý là vị trí của -ss tùy chọn trên dòng lệnh vấn đề . Nơi tôi có nó bây giờ là chậm nhưng chính xác. Phiên bản đầu tiên của câu trả lời này đã đưa ra nhanh nhưng không chính xác thay thế. Bài viết được liên kết cũng mô tả một sự thay thế hầu như nhanh chóng nhưng vẫn chính xác, mà bạn phải trả tiền với một chút phức tạp.

Ngoài ra, tôi không nghĩ bạn thực sự muốn cắt chính xác 10 phút cho mỗi clip. Điều đó sẽ đặt các vết cắt ngay giữa câu, thậm chí là từ. Tôi nghĩ bạn nên sử dụng trình chỉnh sửa video hoặc trình phát để tìm các điểm cắt tự nhiên chỉ cách nhau 10 phút.

Giả sử tệp của bạn ở định dạng mà YouTube có thể chấp nhận trực tiếp, bạn không phải mã hóa lại để có được phân khúc. Chỉ cần chuyển các điểm cắt điểm tự nhiên sang ffmpeg, yêu cầu nó chuyển A/V được mã hóa thông qua không bị chạm bằng cách sử dụng codec "sao chép":

$ ffmpeg -i source.m4v -ss 0 -t 593.3 -c copy part1.m4v
$ ffmpeg -i source.m4v -ss 593.3 -t 551.64 -c copy part2.m4v
$ ffmpeg -i source.m4v -ss 1144.94 -t 581.25 -c copy part3.m4v
...

Các -c copy argument bảo nó sao chép tất cả các luồng đầu vào (âm thanh, video và các tiềm năng khác, như phụ đề) vào đầu ra như hiện trạng. Đối với các chương trình A/V đơn giản, nó tương đương với các cờ dài hơn -c:v copy -c:a copy hoặc các cờ kiểu cũ -vcodec copy -acodec copy. Bạn sẽ sử dụng kiểu dài dòng hơn khi bạn chỉ muốn sao chép một trong các luồng, nhưng mã hóa lại luồng khác. Ví dụ, nhiều năm trước đây có một thực tế phổ biến với các tệp QuickTime để nén video bằng video H.264 nhưng để lại âm thanh là PCM không nén ; nếu bạn chạy qua một tệp như vậy ngày hôm nay, bạn có thể hiện đại hóa nó bằng -c:v copy -c:a aac để xử lý lại luồng âm thanh, khiến video không bị ảnh hưởng.

Điểm bắt đầu cho mọi lệnh ở trên sau lệnh đầu tiên là điểm bắt đầu của lệnh trước cộng với thời lượng của lệnh trước đó.

72
Warren Young

Đây là giải pháp một dòng :

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4

Xin lưu ý rằng điều này không cung cấp cho bạn các phân chia chính xác, nhưng phải phù hợp với nhu cầu của bạn. Thay vào đó, nó sẽ cắt ở khung đầu tiên sau thời gian được chỉ định sau segment_time, trong đoạn mã trên sẽ có sau 20 phút.

Nếu bạn thấy rằng chỉ có đoạn đầu tiên có thể phát được, hãy thử thêm -reset_timestamps 1 như đã đề cập trong các ý kiến.

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
68
Jon

Đối mặt với cùng một vấn đề trước đó và tập hợp một tập lệnh Python để làm việc đó (sử dụng FFMpeg). Có sẵn tại đây: https://github.com/c0decracker/video-splitter và dán bên dưới:

#!/usr/bin/env python
import subprocess
import re
import math
from optparse import OptionParser
length_regexp = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
re_length = re.compile(length_regexp)
def main():
    (filename, split_length) = parse_options()
    if split_length <= 0:
        print "Split length can't be 0"
        raise SystemExit
    output = subprocess.Popen("ffmpeg -i '"+filename+"' 2>&1 | grep 'Duration'",
                              Shell = True,
                              stdout = subprocess.PIPE
    ).stdout.read()
    print output
    matches = re_length.search(output)
    if matches:
        video_length = int(matches.group(1)) * 3600 + \
                       int(matches.group(2)) * 60 + \
                       int(matches.group(3))
        print "Video length in seconds: "+str(video_length)
    else:
        print "Can't determine video length."
        raise SystemExit
    split_count = int(math.ceil(video_length/float(split_length)))
    if(split_count == 1):
        print "Video length is less then the target split length."
        raise SystemExit
    split_cmd = "ffmpeg -i '"+filename+"' -vcodec copy "
    for n in range(0, split_count):
        split_str = ""
        if n == 0:
            split_start = 0
        else:
            split_start = split_length * n
            split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
                         " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                         "'"
    print "About to run: "+split_cmd+split_str
    output = subprocess.Popen(split_cmd+split_str, Shell = True, stdout =
                              subprocess.PIPE).stdout.read()
def parse_options():
    parser = OptionParser()
    parser.add_option("-f", "--file",
                      dest = "filename",
                      help = "file to split, for example sample.avi",
                      type = "string",
                      action = "store"
    )
    parser.add_option("-s", "--split-size",
                      dest = "split_size",
                      help = "split or chunk size in seconds, for example 10",
                      type = "int",
                      action = "store"
    )
    (options, args) = parser.parse_args()
    if options.filename and options.split_size:
        return (options.filename, options.split_size)
    else:
        parser.print_help()
        raise SystemExit
if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print "Exception occured running main():"
        print str(e)
7
c0decracker

Một cách Thay thế thêm có thể đọc được

ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy output1.mp4
ffmpeg -i input.mp4 -ss 00:10:00 -to 00:20:00 -c copy output2.mp4

/**
* -i  input file
* -ss start time in seconds or in hh:mm:ss
* -to end time in seconds or in hh:mm:ss
* -c codec to use
*/

Đây nguồn và danh sách các lệnh FFmpeg thường được sử dụng.

4
Niket Pathak

Nếu bạn muốn tạo Chunks thực sự giống nhau, buộc ffmpeg phải tạo khung i trên khung đầu tiên của mỗi khối để bạn có thể sử dụng lệnh này để tạo 0,5 giây.

ffmpeg -hide_banner  -err_detect ignore_err -i input.mp4 -r 24 -codec:v libx264  -vsync 1  -codec:a aac  -ac 2  -ar 48k  -f segment   -preset fast  -segment_format mpegts  -segment_time 0.5 -force_key_frames  "expr: gte(t, n_forced * 0.5)" out%d.mkv
4
alireza akbaribayat

Lưu ý dấu câu chính xác của định dạng thay thế là -ss mm:ss.xxx. Tôi đã vật lộn hàng giờ để cố gắng sử dụng trực quan nhưng sai mm:ss:xx vô ích.

$ man ffmpeg | grep -C1 position

-ss vị trí
[.__.] Tìm kiếm vị trí thời gian nhất định tính bằng giây. Cú pháp "hh: mm: ss [.xxx]" cũng được hỗ trợ.

Tài liệu tham khảo tại đâytại đây .

3
Mark Hudson

Chỉ cần sử dụng những gì được tích hợp vào ffmpeg để làm chính xác điều này.

ffmpeg -i invid.mp4 -threads 3 -vcodec copy -f segment -segment_time 2 cam_out_h264%04d.mp4

Điều này sẽ phân chia nó thành khoảng 2 giây, phân chia tại các khung hình chính có liên quan và sẽ xuất ra các tệp cam thừng_h2640001.mp4, cam thừng_h2640002.mp4, v.v.

1
John Allard
#!/bin/bash

if [ "X$1" == "X" ]; then
    echo "No file name for split, exiting ..."
    exit 1
fi

if [ ! -f "$1" ]; then
    echo "The file '$1' doesn't exist. exiting ..."
    exit 1
fi

duration=$(ffmpeg -i "$1" 2>&1 | grep Duration | sed 's/^.*Duration: \(.*\)\..., start.*$/\1/' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')      #'
split_time=${split_time:-55}
time=0
part=1

filename=${file%%.*}
postfix=${file##*.}

while [ ${time} -le ${duration} ]; do

echo    ffmpeg -i "$1" -vcodec copy -ss ${time} -t ${split_time} "${filename}-${part}.${postfix}"
    (( part++ ))
    (( time = time + split_time ))

done
0
Alex_Shilli