Bagaimana saya bisa menggunakan ffmpeg untuk membagi video MPEG menjadi potongan 10 menit?

63

Sering ada kebutuhan di komunitas open source atau komunitas pengembang aktif untuk menerbitkan segmen video besar secara online. (Video pertemuan, perkemahan, pembicaraan teknologi ...) Karena saya adalah seorang pengembang dan bukan seorang videografer, saya tidak punya keinginan untuk membayar ekstra pada akun Vimeo premium. Bagaimana cara saya mengambil video pembicaraan teknologi MPEG 12,5 GB (1:20:00) dan mengirisnya menjadi segmen 00:10:00 agar mudah diunggah ke situs berbagi video?

Gabriel
sumber
Terima kasih khusus kepada @StevenD untuk menampung tag baru.
Gabriel

Jawaban:

69
$ 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
...

Membungkus ini menjadi sebuah skrip untuk melakukannya dalam satu lingkaran tidak akan sulit.

Berhati-hatilah bahwa jika Anda mencoba menghitung jumlah iterasi berdasarkan output durasi dari ffprobepanggilan yang diperkirakan dari bit rate rata-rata pada awal klip dan ukuran file klip kecuali Anda memberikan -count_framesargumen, yang memperlambat operasinya secara signifikan. .

Hal lain yang perlu diperhatikan adalah posisi -ssopsi pada baris perintah penting . Di mana saya memilikinya sekarang lambat tapi akurat. Versi pertama dari jawaban ini memberikan alternatif yang cepat tetapi tidak akurat . Artikel tertaut juga menjelaskan sebagian besar alternatif yang cepat-tapi-masih-akurat, yang Anda bayar dengan sedikit kerumitan.

Selain itu, saya tidak berpikir Anda benar-benar ingin memotong tepat 10 menit untuk setiap klip. Itu akan memotong tepat di tengah kalimat, bahkan kata-kata. Saya pikir Anda harus menggunakan editor video atau pemutar untuk menemukan titik potong alami yang hanya berjarak 10 menit.

Dengan asumsi file Anda dalam format yang dapat diterima YouTube secara langsung, Anda tidak perlu memasukkan ulang kode untuk mendapatkan segmen. Lewati offset cut point natural ffmpeg, katakan untuk meneruskan A / V yang disandikan agar tidak tersentuh dengan menggunakan codec "copy":

$ 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
...

The -c copyArgumen mengatakan itu untuk menyalin semua aliran input (audio, video, dan berpotensi lainnya, seperti sub judul) menjadi output sebagai-adalah. Untuk program A / V sederhana, itu setara dengan bendera yang lebih verbose -c:v copy -c:a copyatau bendera gaya lama -vcodec copy -acodec copy. Anda akan menggunakan gaya yang lebih verbose ketika Anda ingin menyalin hanya satu aliran, tetapi menyandikan ulang yang lain. Misalnya, beberapa tahun yang lalu ada praktik umum dengan file QuickTime untuk mengompres video dengan video H.264 tetapi meninggalkan audio sebagai PCM yang tidak terkompresi ; jika Anda berlari melintasi file seperti itu hari ini, Anda dapat memodernkannya dengan -c:v copy -c:a aachanya memproses ulang streaming audio, membiarkan video tidak tersentuh.

Titik awal untuk setiap perintah di atas setelah yang pertama adalah titik awal perintah sebelumnya ditambah durasi perintah sebelumnya.

Warren Young
sumber
1
"memotong tepat 10 menit untuk setiap klip" adalah poin yang bagus.
Chris
mungkin dengan menggunakan param -show_packets Anda bisa membuatnya lebih akurat.
rogerdpack
bagaimana cara menempatkan di atas dalam satu lingkaran?
kRazzy R
i Gunakan cmd ini ya itu dibagi menjadi benar tapi Sekarang video dan audio tidak ada di SYNC bantuan
Sunil Chaudhary
@ SunilChaudhary: Format file A / V kompleks dan tidak semua perangkat lunak mengimplementasikannya dengan cara yang sama, sehingga informasi yang ffmpegdiperlukan untuk menjaga sinkronisasi antara stream audio dan video mungkin tidak ada dalam file sumber. Jika ini terjadi pada Anda, ada metode pemisahan yang lebih rumit yang menghindari masalah.
Warren Young
53

Inilah solusi satu baris :

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

Harap dicatat bahwa ini tidak memberikan Anda pembagian yang akurat, tetapi harus sesuai dengan kebutuhan Anda. Sebagai gantinya akan dipotong pada frame pertama setelah waktu yang ditentukan setelah segment_time, dalam kode di atas akan setelah 20 menit.

Jika ternyata hanya potongan pertama yang dapat dimainkan, coba tambahkan -reset_timestamps 1seperti yang disebutkan dalam komentar.

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4
Jon
sumber
2
Ini sebenarnya memberi Anda pemisahan yang sangat akurat, jika Anda menghargai kualitas video. Alih-alih pemisahan berdasarkan waktu tertentu, itu membagi pada kerangka kunci terdekat setelah waktu yang diminta, sehingga setiap segmen baru selalu dimulai dengan kerangka kunci.
Malvineous
3
apa saja unitnya? 8s? 8 menit? 8 jam?
user1133275
1
@ user1133275 yang kedua
Jon
2
Di Mac, saya menemukan bahwa ini menghasilkan potongan video keluaran N tetapi hanya yang ke-1 dari mereka adalah MP4 yang valid dan dapat dilihat. Potongan N-1 lainnya adalah video kosong (semuanya hitam) tanpa audio. Untuk membuatnya bekerja, saya perlu menambahkan flag reset_timestamps seperti: ffmpeg -i input.mp4 -c copy -map 0 -segment_time 8 -f segmen -reset_timestamps 1 output% 03d.mp4.
jarmod
3
menemukan bahwa menambahkan -reset_timestamps 1perbaikan masalah untuk saya
jlarsch
6

Menghadapi masalah yang sama sebelumnya dan mengumpulkan skrip Python sederhana untuk melakukan hal itu (menggunakan FFMpeg). Tersedia di sini: https://github.com/c0decracker/video-splitter , dan ditempel di bawah:

#!/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)
c0decracker
sumber
1
Lain kali lakukan ini, silakan dalam komentar. Jawaban hanya tautan tidak benar-benar disukai di sini, dan sama jika Anda mengiklankan situs Anda. Jika itu adalah proyek opensource dengan kode sumber, mungkin itu pengecualian, tapi sekarang saya mempertaruhkan hak ulasan saya dengan tidak memilih untuk menghapus jawaban Anda. Dan ya, Anda tidak dapat memposting komentar, tetapi setelah Anda mengumpulkan 5 upvotes (yang tampaknya sangat cepat dalam kasus Anda), Anda akan melakukannya.
user259412
2
Hai dan selamat datang di situs ini. Tolong jangan posting jawaban hanya tautan. Meskipun skrip Anda sendiri mungkin akan membuat jawaban yang bagus, tautan ke sana bukanlah jawaban. Ini adalah rambu yang menunjuk ke sebuah jawaban. Lebih lanjut tentang itu di sini . Karena Anda dengan baik hati memberikan tautannya, saya melanjutkan dan memasukkan skrip ke dalam tubuh jawaban Anda. Jika Anda keberatan dengan itu, harap hapus jawabannya sama sekali.
terdon
4

Jika Anda ingin membuat chunks yang benar-benar sama harus memaksa ffmpeg untuk membuat i-frame pada frame pertama setiap chunks sehingga Anda dapat menggunakan perintah ini untuk membuat chunk 0,5 detik.

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
alireza akbaribayat
sumber
Ini harus menjadi jawaban yang diterima. Terima kasih telah berbagi teman!
Stephan Ahlf
4

Cara alternatif yang lebih mudah dibaca adalah

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
*/

Inilah sumber dan daftar perintah FFmpeg yang umum digunakan.

Niket Pathak
sumber
3

Perhatikan tanda baca yang tepat dari format alternatif ini -ss mm:ss.xxx. Saya berjuang selama berjam-jam mencoba menggunakan intuitif-tetapi-salah tetapi mm:ss:xxtidak berhasil.

$ man ffmpeg | grep -C1 position

Posisi -ss
Mencari posisi waktu tertentu dalam detik. Sintaks "hh: mm: ss [.xxx]" juga didukung.

Referensi di sini dan di sini .

Mark Hudson
sumber
0
#!/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
Alex_Shilli
sumber
0

Cukup gunakan apa yang dibangun ke ffmpeg untuk melakukan hal ini.

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

Ini akan membaginya menjadi chuck sekitar 2 detik, membaginya di keyframes yang relevan, dan akan menghasilkan file cam_out_h2640001.mp4, cam_out_h2640002.mp4, dll.

John Allard
sumber
Tidak yakin saya mengerti mengapa jawaban ini tidak dipilih. Tampaknya berfungsi dengan baik.
Costin