FFmpeg: Làm thế nào để chia nhỏ video hiệu quả?


103

Tôi muốn chia một video avi lớn thành hai video nhỏ hơn liên tiếp. Tôi đang sử dụng ffmpeg.

Một cách là chạy ffmpeg hai lần:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi
ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

Nhưng theo manpage của ffmpeg, tôi có thể tạo nhiều hơn một tệp ouput từ một tệp đầu vào chỉ bằng một dòng:

ffmpeg -i input.avi -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 output1.avi \
   -vcodec copy -acodec copy -ss 00:30:00 -t 00:30:00 output2.avi

Câu hỏi của tôi là, phương pháp sau có tiết kiệm thời gian tính toán và bộ nhớ không?


3
khi sử dụng "-vcodec copy -acodec copy" thì rất nhanh :))
Savas Adar

@Antony, Tại sao bạn không bấm giờ cho cả hai phiên bản (và nhìn vào màn hình bộ nhớ chẳng hạn htop) và cho chúng tôi biết câu trả lời là gì?
AlcubierreDrive

Ví dụ đầu tiên thực hiện tuần tự, trong khi ví dụ thứ hai sử dụng các luồng. Cả hai sẽ làm điều tương tự, không có tốc độ tăng đáng chú ý nào xảy ra. Tuy nhiên, để đơn giản hóa mọi thứ, bạn có thể sử dụng ffmpeg dòng segmenter muxer: ffmpeg.org/...
Mladen B.

1
Ví dụ này có vẻ sai nghiêm trọng! Tài liệu ffmpeg nói về tùy chọn -ss: "Khi được sử dụng làm tùy chọn đầu vào (trước -i), tìm vị trí trong tệp đầu vào này. Khi được sử dụng làm tùy chọn đầu ra (trước tên tệp đầu ra), giải mã nhưng loại bỏ đầu vào cho đến khi dấu thời gian đạt đến vị trí. " Bạn đang sử dụng nó ở vị trí đầu ra, vì vậy dòng thứ hai của bạn lãng phí nỗ lực giải mã và loại bỏ 30 phút đầu tiên.
Lucian Wischik

1
@LucianWischik nhưng việc sử dụng -sslàm tùy chọn đầu vào trong phần tách thứ hai có thể không chính xác, vì vậy bạn có thể không nhận được phần tách rõ ràng. IMO ví dụ của OP là đúng hơn.
jiggunjer

Câu trả lời:


82

Trang wiki ffmpeg liên kết trở lại trang này liên quan đến "Cách tách video hiệu quả". Tôi không tin rằng trang này trả lời câu hỏi đó, vì vậy tôi đã làm như @AlcubierreDrive đề xuất…

echo "Two commands" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 -sn test1.mkv
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test2.mkv
echo "One command" 
time ffmpeg -v quiet -y -i input.ts -vcodec copy -acodec copy -ss 00:00:00 -t 00:30:00 \
  -sn test3.mkv -vcodec copy -acodec copy -ss 00:30:00 -t 01:00:00 -sn test4.mkv

Đầu ra nào ...

Two commands
real    0m16.201s
user    0m1.830s
sys 0m1.301s

real    0m43.621s
user    0m4.943s
sys 0m2.908s

One command
real    0m59.410s
user    0m5.577s
sys 0m3.939s

Tôi đã kiểm tra tệp SD & HD, sau một vài lần chạy và một chút phép toán.

Two commands SD 0m53.94 #2 wins  
One command  SD 0m49.63  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.60 #2 wins  
One command  SD 0m58.61 

Two commands SD 0m54.60  
One command  SD 0m50.51 #1 wins 

Two commands SD 0m53.94  
One command  SD 0m49.63 #1 wins  

Two commands SD 0m55.00  
One command  SD 0m52.26 #1 wins 

Two commands SD 0m58.71  
One command  SD 0m58.61 #1 wins

Two commands SD 0m54.63  
One command  SD 0m50.51 #1 wins  

Two commands SD 1m6.67s #2 wins  
One command  SD 1m20.18  

Two commands SD 1m7.67  
One command  SD 1m6.72 #1 wins

Two commands SD 1m4.92  
One command  SD 1m2.24 #1 wins

Two commands SD 1m1.73  
One command  SD 0m59.72 #1 wins

Two commands HD 4m23.20  
One command  HD 3m40.02 #1 wins

Two commands SD 1m1.30  
One command  SD 0m59.59 #1 wins  

Two commands HD 3m47.89  
One command  HD 3m29.59 #1 wins  

Two commands SD 0m59.82  
One command  SD 0m59.41 #1 wins  

Two commands HD 3m51.18  
One command  HD 3m30.79 #1 wins  

Tệp SD = 1,35GB Luồng truyền tải DVB
Tập tin HD = 3,14 GB Luồng truyền tải DVB

Phần kết luận

Lệnh đơn sẽ tốt hơn nếu bạn đang xử lý HD, nó đồng ý với các nhận xét hướng dẫn sử dụng -ss sau tệp đầu vào để thực hiện 'tìm kiếm chậm'. Các tệp SD có sự khác biệt không đáng kể.

Phiên bản hai lệnh sẽ nhanh hơn bằng cách thêm một -ss khác vào trước tệp đầu vào cho 'tìm kiếm nhanh' sau đó là tìm kiếm chậm chính xác hơn.


Nếu TS chứa nhiều chương trình (các chương trình TV được thu bởi DVB-T), làm thế nào có thể tách nó ra? Ví dụ: tôi có một tệp TS chứa bóng đá + nấu ăn + phim hoạt hình. Làm cách nào để chia tệp TS này thành 3 tệp mpg?
Dr.jacky

Tôi đã sử dụng câu trả lời của bạn trong câu hỏi của tôi ở đây ( stackoverflow.com/questions/41983940/… ) nhưng nó không hoạt động, bạn nghĩ tôi đang làm gì sai?
utdev

1
Tệp kết quả không có video (chỉ có âm thanh) trong trường hợp của tôi. Sau một số thử nghiệm, tôi tìm thấy linh hồn. Nó cần phải chỉ định khóa cho "không sửa đổi chất lượng" như thế này -q:v 0.
oklas

19

Đây là một tập lệnh hữu ích, nó giúp bạn phân chia tự động: Một tập lệnh để chia nhỏ video bằng ffmpeg

#!/bin/bash
 
# Written by Alexis Bezverkhyy <alexis@grapsus.net> in 2011
# This is free and unencumbered software released into the public domain.
# For more information, please refer to <http://unlicense.org/>
 
function usage {
        echo "Usage : ffsplit.sh input.file chunk-duration [output-filename-format]"
        echo -e "\t - input file may be any kind of file reconginzed by ffmpeg"
        echo -e "\t - chunk duration must be in seconds"
        echo -e "\t - output filename format must be printf-like, for example myvideo-part-%04d.avi"
        echo -e "\t - if no output filename format is given, it will be computed\
 automatically from input filename"
}
 
IN_FILE="$1"
OUT_FILE_FORMAT="$3"
typeset -i CHUNK_LEN
CHUNK_LEN="$2"
 
DURATION_HMS=$(ffmpeg -i "$IN_FILE" 2>&1 | grep Duration | cut -f 4 -d ' ')
DURATION_H=$(echo "$DURATION_HMS" | cut -d ':' -f 1)
DURATION_M=$(echo "$DURATION_HMS" | cut -d ':' -f 2)
DURATION_S=$(echo "$DURATION_HMS" | cut -d ':' -f 3 | cut -d '.' -f 1)
let "DURATION = ( DURATION_H * 60 + DURATION_M ) * 60 + DURATION_S"
 
if [ "$DURATION" = '0' ] ; then
        echo "Invalid input video"
        usage
        exit 1
fi
 
if [ "$CHUNK_LEN" = "0" ] ; then
        echo "Invalid chunk size"
        usage
        exit 2
fi
 
if [ -z "$OUT_FILE_FORMAT" ] ; then
        FILE_EXT=$(echo "$IN_FILE" | sed 's/^.*\.\([a-zA-Z0-9]\+\)$/\1/')
        FILE_NAME=$(echo "$IN_FILE" | sed 's/^\(.*\)\.[a-zA-Z0-9]\+$/\1/')
        OUT_FILE_FORMAT="${FILE_NAME}-%03d.${FILE_EXT}"
        echo "Using default output file format : $OUT_FILE_FORMAT"
fi
 
N='1'
OFFSET='0'
let 'N_FILES = DURATION / CHUNK_LEN + 1'
 
while [ "$OFFSET" -lt "$DURATION" ] ; do
        OUT_FILE=$(printf "$OUT_FILE_FORMAT" "$N")
        echo "writing $OUT_FILE ($N/$N_FILES)..."
        ffmpeg -i "$IN_FILE" -vcodec copy -acodec copy -ss "$OFFSET" -t "$CHUNK_LEN" "$OUT_FILE"
        let "N = N + 1"
        let "OFFSET = OFFSET + CHUNK_LEN"
done

2
Nhận xét tương tự cho bạn cũng như cho SEARAS. Việc ghi các tệp hàng loạt rất hữu ích khi thực sự cần thiết và khi bạn không có tùy chọn nào khác vì nó không có tính di động. Ví dụ: bạn sẽ không thể chạy cùng một tập lệnh trên Windows. Khi có nhiều cách chung chung / di động hơn, nên tránh các tập lệnh hàng loạt, để tiết kiệm thời gian cần thiết để chuyển chúng. Vì ffmpeg có nhiều cách để giải quyết vấn đề này, nên cách thích hợp sẽ là sử dụng ffmpeg một mình mà không cần sự trợ giúp của script.
Mladen B.

Kịch bản hay. Bạn đã viết nó à? Nó có trên GitHub không? Tôi muốn đưa ra một số đề xuất :)
giọng nói

cho mp4, cảnh bị hư hỏng. tốt hơn thay thế bản sao bằng: -vcodec libx264 -acodec aac
kishu

8

Theo kinh nghiệm của tôi, không sử dụng ffmpeg để tách / nối. MP4Box, nhanh hơn và nhẹ hơn ffmpeg. Hãy thử. Ví dụ: nếu bạn muốn chia một tệp MP4 1400mb thành hai phần với 700mb, bạn có thể sử dụng cmdl sau: MP4Box -splits 716800 input.mp4 ví dụ: để nối hai tệp, bạn có thể sử dụng:

MP4Box -cat file1.mp4 -cat file2.mp4 output.mp4

Hoặc nếu bạn cần phân chia theo thời gian, hãy sử dụng -splitx StartTime:EndTime:

MP4Box -add input.mp4 -splitx 0:15 -new split.mp4


Đây chính xác là những gì tôi cần! Khi tôi cắt bằng ffmpeg, các clip không bắt đầu trên khung hình chính và có hiện tượng đóng băng kỳ lạ ở phần cuối. Sử dụng MP4Box -splitz đã thành công!
matt

1
làm cách nào để cắt video bằng Mp4Box với nhiều số? Ví dụ tôi cần có các clip phụ tính bằng giây là: 5 - 8, 21 - 30, 12 - 18 vì vậy tôi cần ba tệp video đầu ra.
mhndev

Và dễ sử dụng EndTimehơn nhiều -t.
vmassuchetto,


2

cách tiếp cận sau có tiết kiệm thời gian tính toán và bộ nhớ không?

Không có sự khác biệt lớn giữa hai ví dụ mà bạn đã cung cấp. Ví dụ đầu tiên cắt video theo tuần tự, theo 2 bước, trong khi ví dụ thứ hai làm điều đó cùng một lúc (sử dụng các chuỗi). Sẽ không có sự tăng tốc cụ thể nào đáng chú ý. Bạn có thể đọc thêm về cách tạo nhiều đầu ra với FFmpeg

Hơn nữa, những gì bạn có thể sử dụng (trong FFmpeg gần đây) là muxer phân đoạn luồng có thể:

xuất các luồng ra một số tệp riêng biệt có thời lượng gần như cố định. Mẫu tên tệp đầu ra có thể được đặt theo kiểu tương tự như image2 .


2

Đây là một tệp Windows bat đơn giản để chia tệp đến thành 50 phần. Mỗi phần có độ dài 1 phút. Xin lỗi vì kịch bản ngu ngốc như vậy. Tôi hy vọng sẽ tốt hơn nếu có một tập lệnh windows câm thay vì không có nó. Có lẽ nó giúp một ai đó. (Dựa trên "tệp bat cho vòng lặp" từ trang web này.)

set var=0
@echo off
:start
set lz=
if %var% EQU 50 goto end
if %var% LEQ 9 set lz=0
echo part %lz%%var%
ffmpeg -ss 00:%lz%%var%:00 -t 00:01:00 -i %1 -acodec copy -vcodec copy %2_%lz%%var%.mp4
set /a var+=1
goto start

:end
echo var has reached %var%.
exit

Đây thực chất là đoạn mã đầu tiên trong câu hỏi ( ffmpegcác lệnh liên tiếp ) được viết lại dưới dạng một vòng lặp. Điều này không trả lời câu hỏi liệu cách tiếp cận đó hay một lệnh duy nhất sẽ "tiết kiệm thời gian tính toán và bộ nhớ".
BACON

1

Không kiểm tra ist, nhưng điều này có vẻ hứa hẹn:

Trình phân đoạn luồng cơ bản

Rõ ràng là nó đang chia AVI thành các phân đoạn có cùng kích thước, có nghĩa là các phân đoạn này không làm giảm chất lượng hoặc tăng bộ nhớ hoặc phải được tính toán lại.

Nó cũng sử dụng bản sao codec - điều đó có nghĩa là nó có thể xử lý các luồng rất lớn? Bởi vì đây là vấn đề của tôi, tôi muốn phá vỡ avi của mình để tôi có thể sử dụng bộ lọc để loại bỏ sự biến dạng. Nhưng toàn bộ avi chạy trong nhiều giờ.


-1

Đây là một cách hoàn hảo để chia nhỏ video. Tôi đã làm nó trước đây và nó hoạt động tốt cho tôi.

ffmpeg -i C:\xampp\htdocs\videoCutting\movie.mp4 -ss 00:00:00 -t 00:00:05 -async 1 C:\xampp\htdocs\videoCutting\SampleVideoNew.mp4 (Đối với cmd). shell_exec('ffmpeg -i C:\xampp\htdocs\videoCutting\movie.mp4 -ss 00:00:00 -t 00:00:05 -async 1 C:\xampp\htdocs\videoCutting\SampleVideoNew.mp4')(đối với php).

Hãy làm theo điều này và tôi chắc chắn rằng nó sẽ hoạt động hoàn hảo.


Tại sao đây là "cách hoàn hảo" để chia nhỏ video? Giống như dòng lệnh trong câu hỏi, bạn đang chuyển -itheo sau là -ss-t. Tuy nhiên, câu hỏi đặt ra là làm thế nào để trích xuất hiệu quả nhiều phân đoạn từ cùng một tệp đầu vào, trong khi bạn chỉ trích xuất một phân đoạn duy nhất. Tôi không hiểu làm thế nào điều này trả lời câu hỏi.
BACON

-2

một cách đơn giản là sử dụng tùy chọn trim với api dựa trên url publitio .

khi bạn tải video lên, chỉ cần đặt độ lệch đầu và độ lệch kết thúc trong url (so_2, eo_2) như sau:

    https://media.publit.io/file/so_2,eo_2/tummy.mp4

điều này sẽ tạo ngay video mới bắt đầu từ giây thứ 2 và có độ dài 2 giây. bạn có thể chia nhỏ video theo cách này theo cách nào bạn muốn.


Câu hỏi đặt ra là cách nào hiệu quả hơn trong hai cách tiếp cận để trích xuất nhiều phân đoạn từ một tệp đầu vào với ffmpeg. Trừ khi bạn có kết nối internet thực sự, thực sự nhanh, tôi nghĩ việc phải tải toàn bộ video lên dịch vụ đám mây và sau đó tải xuống kết quả sẽ làm tăng thời gian xử lý lên đáng kể. Mặc dù vậy, điều này vẫn không trả lời câu hỏi trong số đó tiếp cận vớiffmpeg hiệu quả hơn.
BACON
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.