it-swarm-tr.com

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

Açık kaynak veya aktif geliştirici topluluğunda büyük video segmentlerini çevrimiçi yayınlamak için genellikle bir ihtiyaç vardır. (Buluşma videoları, kampanyalar, teknoloji konuşmaları ...) Bir kameraman değil, bir geliştirici olduğumdan, premium bir Vimeo hesabındaki ekstra çiziklerden kurtulmak istemiyorum. Daha sonra video paylaşım sitelerine kolay yükleme yapmak için 12,5 GB (1:20:00) MPEG teknik konuşma videosunu alıp 00:10:00 segmentlerine nasıl ayırırım?

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

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

Bir ffprobe çağrısından elde edilen süre çıktısını temel alarak yineleme sayısını hesaplamaya çalışırsanız, bunun ortalamadan tahmin edildiğine dikkat edin. işleyişini önemli ölçüde yavaşlatan -count_frames argümanını vermezseniz, klibin başlangıcında bit hızı ve klibin dosya boyutu.

Dikkat edilmesi gereken bir diğer şey, -ss Seçeneğinin komut satırındaki konumunun önemli olmasıdır. Şimdi sahip olduğum yer yavaş ama doğru. Bu cevabın ilk versiyonu hızlı fakat yanlış alternatifini verdi. Bağlantılı makalede, biraz karmaşıklıkla ödediğiniz çoğunlukla hızlı ama yine de doğru bir alternatif açıklanmaktadır.

Tüm bunlar bir yana, her klip için tam olarak 10 dakika kesmek istediğinizi düşünmüyorum. Bu, cümlelerin tam ortasına, kelimelere bile keser. 10 dakika arayla utangaç doğal noktaları bulmak için bir video düzenleyici 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, segmentler almak için yeniden kodlamanız gerekmez. Sadece doğal kesme noktası ofsetlerini ffmpeg değerine geçirin ve "kopya" kodekini kullanarak kodlanmış A/V'yi el değmeden geçirmesini söyleyin:

$ 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 copy Argümanı tüm girdi akışlarını (ses, video ve potansiyel olarak altyazılar gibi diğerleri) çıktıya olduğu gibi kopyalamasını söyler. Basit A ​​/ V programları için, daha ayrıntılı bayraklara -c:v copy -c:a copy Veya eski stil bayraklarına -vcodec copy -acodec copy Eşdeğerdir. Akışlardan yalnızca birini kopyalamak, ancak diğerini yeniden kodlamak istediğinizde daha ayrıntılı stili kullanırsı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 yaygın bir uygulama vardı. bugün böyle bir dosyayla karşılaşırsanız, videoyu el değmeden bırakarak yalnızca ses akışını yeniden işlemek için -c:v copy -c:a aac ile modernleştirebilirsiniz.

İlk komuttan sonra yukarıdaki her komutun başlangıç ​​noktası, önceki komutun başlangıç ​​noktası artı önceki komutun süresidir.

72
Warren Young

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

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

Bunun size doğru bölünmeler sağlamadığını, ancak ihtiyaçlarınızı karşılaması gerektiğini lütfen unutmayın. Bunun yerine ilk karede segment_time Öğesinden sonra belirtilen süreden sonra kesilecek, yukarıdaki kodda 20 dakikalık işaretten sonra olacaktır.

Yalnızca ilk yığının çalınabilir olduğunu fark ederseniz, yorumlarda belirtildiği gibi -reset_timestamps 1 Eklemeyi deneyin.

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

Daha önce aynı sorunla karşılaştı ve bunu yapmak için basit bir Python komut dosyası (FFMpeg kullanarak). Burada kullanılabilir: https://github.com/c0decracker/video-splitter ve aşağı yapıştırıldı:

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

Alternatif daha fazla okunabilir yol

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 Sık kullanılan FFmpeg komutlarının kaynağı ve listesi.

4
Niket Pathak

Gerçekten aynı oluşturmak istiyorsanız Topaklar ffmpeg'i her parçanın ilk karesinde i-kare oluşturmaya zorlamalı, böylece bu komutu 0,5 saniye yığın 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
4

Alternatif biçimin noktalama işaretinin -ss mm:ss.xxx. Saatlerce sezgisel ama yanlış kullanmaya çalıştım mm:ss:xx kullanılabilir.

$ man ffmpeg | grep -C1 position

-ss konumu
Saniye cinsinden verilen zaman pozisyonunu arayın. "ss: dd: ss [.xxx]" sözdizimi de desteklenir.

Kaynaklar burada ve burada .

3
Mark Hudson

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 işlem yaklaşık 2 saniyelik aynalara bölünür, ilgili anahtar karelere bölünür ve cam_out_h2640001.mp4, cam_out_h2640002.mp4, vb. Dosyalara çıktı verilir.

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