MPEG videosunu 10 dakikalık parçalara bölmek için ffmpeg'i nasıl kullanabilirim?


63

Açık kaynak veya aktif geliştirici topluluğunda, büyük video bölümlerini çevrimiçi yayınlamak için genellikle bir ihtiyaç vardır. (Toplantı videoları, kamplar, teknik konuşmalar ...) Ben bir geliştirici olduğum ve videografçı olmadığım için, premium bir Vimeo hesabına fazladan çizik atma arzum yok. Öyleyse, video paylaşım sitelerine kolay yükleme yapmak için 12,5 GB (1:20:00) MPEG teknoloji konuşma videosunu alıp 00:10:00 segmentlerine ayırırım?


Yeni etiketleri barındıran @StevenD'ye özel teşekkürler.
Gabriel,

Yanıtlar:


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

Bunu bir döngü içinde yapmak için bir komut dosyasına sığdırmak zor olmaz.

Dikkat bu bir gelen süre çıkışına dayalı yineleme sayısını hesaplamak için çalışırsanız ffprobebu olduğunu çağrısı tahmin Eğer vermedikçe klibin başlangıç ve klibin dosya boyutunda ortalama bit oranından -count_framesfaaliyete ölçüde yavaşlatır argüman, .

Dikkat edilmesi gereken bir diğer husus, -ssseçeneğin komut satırındaki konumunun önemli olduğudur . Şimdi sahip olduğum yer yavaş ama doğru. Bu cevabın ilk versiyonu hızlı fakat yanlış bir alternatif verdi . Bağlantılı makale ayrıca, biraz karmaşık bir şekilde ödediğiniz, çoğunlukla hızlı ancak hala doğru olan bir alternatifi açıklamaktadır.

Tüm bunlar bir yana, her klip için tam olarak 10 dakikada bir kesim yapmak istediğinizi sanmıyorum. Bu tam olarak cümlelerin ortasına kesecek, hatta kelimeler. 10 dakikalık aralıklarla utangaç doğal noktaları bulmak için bir video editörü veya oynatıcı kullanmanız gerektiğini düşünüyorum.

Dosyanızın YouTube’un doğrudan kabul edebileceği bir biçimde olduğunu varsayarsak, bölümleri almak için yeniden kodlamanız gerekmez. Doğal kesme noktası ofsetlerini ffmpeg, "kopya" kodeki kullanılarak dokunulmamış olan A / V kodunu geçmesini söyleyerek iletmeniz yeterlidir:

$ 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 copyArgümanı olduğu gibi çıkış içine (potansiyel olarak ses, video ve alt yazılar gibi diğerleri) tüm giriş akışlarını kopyalamak söyler. Basit A ​​/ V programları için daha ayrıntılı bayraklara -c:v copy -c:a copyveya eski stil bayraklarına eşdeğerdir -vcodec copy -acodec copy. Akışlardan yalnızca birini kopyalamak istediğinizde daha ayrıntılı bir stil kullanır, ancak diğerini yeniden kodlarsınız. Örneğin, yıllar önce, videoyu H.264 video ile sıkıştırmak, ancak sesi sıkıştırılmamış PCM olarak bırakmak için QuickTime dosyalarıyla ortak bir uygulama yapıldı ; Bugün böyle bir dosyayla karşılaştıysanız -c:v copy -c:a aac, videoyu dokunmadan bırakarak yalnızca ses akışını yeniden işlemek için modernize edebilirsiniz .

İlk komuttan sonraki her komut için başlangıç ​​noktası, önceki komutun başlangıç ​​noktası ve önceki komutun süresidir.


1
"Her klip için tam olarak 10 dakikada kesme" iyi bir nokta.
Chris

belki -show_packets paramını kullanarak daha doğru hale getirebilirsiniz.
rogerdpack

nasıl bir döngü içine koymak için?
kRazzy R

i Bu cmd'yi kullanın ya da sağa bölünür, ancak Şimdi video ve ses SYNC'de değil, herhangi bir yardımda değil
Sunil Chaudhary

@ SununChaudhary: A / V dosya formatları karmaşıktır ve tüm yazılımlar aynı yöntemi uygulamaz, böylece bilginin ffmpegses ve video akışları arasında senkronizasyonu sürdürmesi gerekir kaynak dosyada mevcut olmayabilir. Bu size oluyorsa , sorunu önleyen daha karmaşık bölme yöntemleri vardır .
Warren Young,

53

İşte bir satır çözüm :

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

Lütfen bunun size kesin ayrılıklar sağlamadığını, ancak ihtiyaçlarınıza uygun olması gerektiğini unutmayın. Bunun yerine ilk karede belirtilen süreden sonra kesilecektir, segment_timeyukarıdaki kodda 20 dakikalık işaretten sonra olacaktır.

Yalnızca ilk parçanın oynanabilir olduğunu tespit ederseniz -reset_timestamps 1, yorumlarda belirtildiği gibi eklemeyi deneyin .

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

2
Video kalitesine değer veriyorsanız, aslında size çok hassas ayrılmalar veriyor . Belirli bir zamana göre bölmek yerine, istenen sürenin ardından en yakın ana kareye bölünür, böylece her yeni bölüm her zaman bir ana kare ile başlar.
Malvineous

3
birimler neler? 8'ler? 8dk? 8h?
user1133275

1
@ user1133275, ikinci
Jon

2
Mac'te bunun N çıkışlı video parçalarıyla sonuçlandığını buldum, ancak yalnızca 1 tanesi geçerli, görüntülenebilir bir MP4 idi. Diğer N-1 parçaları ses içermeyen boş video (tümü siyah) idi. Bunu yapabilmek için reset_timestamps bayrağını böyle eklemeliydim: ffmpeg -i input.mp4 -c copy -map 0 -segment_time 8 -f segment -reset_timestamps 1 çıkış% 03d.mp4.
jarmod

3
ekleyerek -reset_timestamps 1sorunu benim için düzeltti bulundu
jlarsch 30:17

6

Aynı problemi daha önce karşıladı ve sadece bunu yapmak için basit bir Python betiği hazırladı (FFMpeg kullanarak). Burada bulunabilir: https://github.com/c0decracker/video-splitter ve aşağı yapıştırılmış:

#!/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)

1
Bir dahaki sefere bunu lütfen yorum yapın. Yalnızca bağlantı yanıtları burada gerçekten beğenilmez ve sitenizi reklam verirseniz aynıdır. Kaynak kodu ile açık kaynaklı bir projedir, belki de bir istisnadır, ama artık benim inceleme ayrıcalıkları riske değil Cevabınız çıkarılması için oy. Ve evet, yorum gönderemezsiniz, ancak 5 oy topladıktan sonra (durumunuz çok hızlı görünüyor).
user259412

2
Merhaba ve siteye hoşgeldiniz. Lütfen bağlantı göndermeyin sadece cevaplar. Senaryonuzun kendisi muhtemelen harika bir cevap olsa da, ona verilen bağlantı bir cevap değildir. Bu bir cevabı işaret eden bir tabeladır. Burada daha fazlası . Nazikçe bağlantıyı verdiğinizden beri devam ettim ve senaryoyu cevabınızın gövdesine dahil ettim. Buna itiraz ederseniz, lütfen cevabı tamamen silin.
terdon

4

Gerçekten aynı Chunks oluşturmak istiyorsanız, her chunks'ın ilk karesinde i-frame'i oluşturmak için ffmpeg'i zorlamanız gerekir, böylece bu komutu 0.5 saniye chunk oluşturmak için kullanabilirsiniz.

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

Bu kabul edilen cevap olmalı. Arkadaşını paylaştığın için teşekkürler!
Stephan Ahlf

4

Bir Alternatif daha okunabilir bir yol olacağını

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

İşte Yaygın olarak kullanılan FFmpeg komutlarının kaynağı ve listesi.


3

Alternatif biçimin tam noktalama işaretine dikkat edin -ss mm:ss.xxx. Sezgisel-ama-yanlış kullanmaya çalışmak için saatlerce mm:ss:xxuğraştım.

$ man ffmpeg | grep -C1 position

-ss pozisyonu
Saniyeler içinde verilen zaman pozisyonunu arayın. "hh: mm: ss [.xxx]" sözdizimi de desteklenir.

Buraya ve buraya referanslar .


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

0

Tam olarak bunu yapmak için ffmpeg'de yerleşik olanı kullanın.

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

Bu, kabaca 2 saniye aynasına bölünecek, ilgili anahtar karelere bölünecek ve cam_out_h2640001.mp4, cam_out_h2640002.mp4, vb. Dosyalara çıkacaktır.


Bu cevabın neden oylandığını anladığımdan emin değilim. İyi çalışıyor gibi görünüyor.
Ağustos'ta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.