FFmpeg kullanarak videodan birden çok kesimi nasıl kaldırabilirim?


51

FFmpeg kullanarak bir videonun birkaç bölümünü silmeye çalışıyorum.

Örneğin, televizyonda bir gösteri kaydettiğinizi ve reklamları kesmek istediğinizi hayal edin. Bu bir GUI video editörü ile basittir; kaldırılacak her klibin başlangıcını ve bitişini işaretlemeniz yeterlidir ve sil seçeneğini seçin. Aynı şeyi FFmpeg ile komut satırından da yapmaya çalışıyorum.

Bunun gibi yeni bir video için tek bir kesimi nasıl keseceğimi biliyorum :

ffmpeg -i input.avi -ss 00:00:20 -t 00:00:05 -map 0 -codec copy output.avi

Bu, beş saniyelik bir klibi keser ve yeni bir video dosyası olarak kaydeder, ancak tam tersini yapabilir ve tüm videoyu belirtilen klip olmadan kaydedebilir ve kaldırılacak birden fazla klibi nasıl belirleyebilirim?

Örneğin, videom ABCDEFG tarafından gösterilebiliyorsa, ACDFG'den oluşacak yeni bir tane oluşturmak istiyorum.



1
İstediğim şey bu değil, hepsini bir "bölüm" içinde almak istiyorum, ancak orijinal videodan farklı bölümleri olacaktı.
Matias

@Matias, videodan bir kaç klip kesmeyi ve geri kalanı olduğu gibi bırakmayı soruyor olsaydı, bu bir şey olurdu, ama birkaç klip çekmek ve bunları diğer videolardan kliplerle birleştirmek istiyorsunuz. Bu değil ayrı ve eşsiz bir soru. Diğer soruların ayrı bölümleri almak için istediklerini yapmalı ve sonra bunları birleştirmelisiniz.
Synetech

1
@ Synetech cevaplarınız için teşekkürler. Onları diğer videolardan kliplerle birleştirmek istemiyorum. Sadece videolardan bazı kısımları kaldırmak istiyorum. Örneğin, videom ABCDEFG tarafından gösterilebiliyorsa, ACDFG'den oluşacak yeni bir tane oluşturmak istiyorum.
Matias

@ Synetech O ben değildim, yanlış anlaşılmış olması gereken Tog'dı.
Matias

Yanıtlar:


47

Bunun için hala trimfiltreyi kullanabilirsiniz . İşte bir örnek, 30-40 sn (10 sn) ve 50-80 sn (30 sn) segmentleri kesmek istediğinizi varsayalım:

ffmpeg -i in.ts -filter_complex \
"[0:v]trim=duration=30[a]; \
 [0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[b]; \
 [a][b]concat[c]; \
 [0:v]trim=start=80,setpts=PTS-STARTPTS[d]; \
 [c][d]concat[out1]" -map [out1] out.ts

Burada ne yaptım? İlk önce 30 saniye, 40-50 saniye ve 80 saniye sonuna kadar kırptım ve sonra bunları filtreyle akış out1halinde birleştirdim concat.

Setpts hakkında: buna ihtiyacımız var çünkü trim resim görüntüleme süresini değiştirmiyor ve 10 sn. Kesinti yaptığımızda dekoder sayacı bu 10 sn.

Siz de ses sahibi olmak istiyorsanız, ses akışları için de aynısını yapmanız gerekir. Yani komut şöyle olmalı:

ffmpeg -i utv.ts -filter_complex \
"[0:v]trim=duration=30[av];[0:a]atrim=duration=30[aa];\
 [0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[bv];\
 [0:a]atrim=start=40:end=50,asetpts=PTS-STARTPTS[ba];\
 [av][bv]concat[cv];[aa][ba]concat=v=0:a=1[ca];\
 [0:v]trim=start=80,setpts=PTS-STARTPTS[dv];\
 [0:a]atrim=start=80,asetpts=PTS-STARTPTS[da];\
 [cv][dv]concat[outv];[ca][da]concat=v=0:a=1[outa]" -map [outv] -map [outa] out.ts

Muhteşem bir tane! Neden ayarlara ihtiyacımız var? Açıklama ekler misiniz?
Rajib

Tamam, güncellenmiş cevabı gör.
ptQa

4
+1 Bu gerçekten cevap olarak seçilmelidir. "Çoklu bölümler" için daha uygundur ve birbiri ardına çoklu komut verme zorunluluğunu ortadan kaldırır. (başka bir yol daha verimli değilse)
Knossos

1
Çerçeveleri atlarken altyazı verilerini korumaya nasıl devam edersiniz?
Andrew Scagnelli

1
Bu çok insancıl değil.
golopot

15

PtQa'nın çözümünü asla çalıştıramam çünkü çoğunlukla filtrelerdeki hataların ne anlama geldiğini veya nasıl düzeltileceğini asla çözemiyorum. Benim çözümüm biraz clunkier görünüyor, çünkü bir karışıklık bırakabilir, ancak onu bir betiğe fırlatırsanız, temizlik otomatik hale getirilebilir. Bu yaklaşımı da seviyorum, çünkü 4. adımda bir şeyler ters giderse, 1-3 adımlarını tamamlamış olursunuz, böylece hatalardan kurtulmak biraz daha verimli olur.

Temel strateji kullanmak -tve -ssistediğiniz her bölümün videosunu almak, ardından son sürümünüz için tüm parçaları birleştirin.

Diyelim ki her 5 saniyede bir 6 ABCDEF segmentiniz var ve bunu A (0-5 saniye), C (10-15 saniye) ve E (20-25 saniye) istediğinizde yapın:

ffmpeg -i abcdef.tvshow -t 5 a.tvshow -ss 10 -t 5 c.tvshow -ss 20 -t 5 e.tvshow

veya

ffmpeg -i abcdef.tvshow -t 0:00:05 a.tvshow -ss 0:00:10 -t 0:00:05 c.tvshow -ss 0:00:20 -t 0:00:05 e.tvshow

Bu dosyaları a.tvshow, c.tvshow ve e.tvshow yapacaktır. -tHer klip ne kadar süre diyor, yani c 30 saniyelik kısmını zamandır 30 ya 0:00:30 yılında geçebileceği. -ssSeçenek her zaman dosyanın başlangıcına göre yani, kaynak videonun içine atlamak için ne kadar diyor.

Sonra bir sürü video dosyanız olduğunda, şöyle bir dosya ace-files.txthazırlarım:

file 'a.tvshow'
file 'c.tvshow'
file 'e.tvshow'

Başında "dosya" ve bundan sonra kaçan dosya adını not edin.

Sonra komut:

ffmpeg -f concat -i ace-files.txt -c copy ace.tvshow

Bu, tüm dosyaları bir abe-files.txtaraya toplayarak ses ve video kodeklerini kopyalar ve ace.tvshowsadece a, c ve e bölümleri olması gereken bir dosya oluşturur. Sonra sadece silmeyi unutmayın ace-files.txt, a.tvshow, c.tvshowve e.tvshow.

Feragatname : Bunun ne kadar verimli (ve) verimli olduğu konusunda diğer yaklaşımlarla kıyaslandığına dair hiçbir fikrim yok, ffmpegancak amaçlarıma göre daha iyi çalışıyor. Umarım birine yardımcı olur.


4
bu benim gibi yeni başlayanlar için çok anlaşılabilir bir durum, teşekkürler
juanpastas

4
Eğer bash kullanıyorsanız, bir dosya oluşturmaktan kaçınabilirsiniz ( ace-files.txtörneğinizde):ffmpeg -f concat -i <(printf "file '%s'\n" $(pwd)/prefix_*.tvshow) -c copy output.tvshow
bufh

Bu gerçekten yardımcı oldu, ancak tek tırnak tırnak içine dosya adlarını kaldırmak zorunda kaldı veya işe yaramadı.
tchaymore

Videoları birleştirirken, kısa bir siyah video ve aralarında susturulmuş bir ses var, yaklaşık 25 ms. Bundan kurtulmak için bir fikrin var mı?
Antonio Bonifati 'Farmboy',

Hmm ... Zamanını bozup atladığında matematiğini kontrol ederdim ve 25ms boşluk bırakmadığından emin ol ... Bunu yaşamamıştım.
xbakesx

5

Kayıtlı TV düzenlemeyi hızlandırmak için bir senaryo hazırladım. Komut dosyası, saklamak istediğiniz bölümlerin başlangıç ​​ve bitiş zamanlarını ister ve bunları dosyalara böler. Size seçenekler sunar, şunları yapabilirsiniz:

  • Bir veya daha fazla segmenti alın.
  • Segmentleri sonuçta elde edilen tek bir dosyada birleştirebilirsiniz.
  • Katıldıktan sonra parça dosyalarını saklayabilir veya silebilirsiniz.
  • Orijinal dosyayı saklayabilir veya yeni dosyanızla değiştirebilirsiniz.

Eylemdeki video: burada

Ne düşündüğü söyle.

 #!/bin/bash
/bin/date >>segmenter.log

function segment (){
while true; do
    echo "Would you like to cut out a segment ?"
    echo -e "1) Yes\n2) No\n3) Quit"
    read CHOICE
    if [ "$CHOICE" == "3" ]; then
        exit
    elif [ "$CHOICE" == "2" ]; then
        clear
        break
    elif [ "$CHOICE" == "1" ]; then
        clear
        ((segments++))
        echo "What time does segment $segments start ?"
        read SEGSTART
        clear
        echo -e "Segment $segments start set to $SEGSTART\n"  
        echo "What time does segment $segments end ?"
        read SEGEND
        clear
        echo -e "Segment $segments end set to $SEGEND\n"
        break
    else
        clear
        echo -e "Bad option"
        segment "$segments"
    fi
done
if [ "$CHOICE" == "1" ]; then
    echo "Cutting file $file video segment $segments starting at $SEGSTART and ending at $SEGEND"
    ffmpeg -i "$file" -ss $SEGSTART -to  $SEGEND -map 0:0 -map 0:1 -c:a copy -c:v copy  "$filename-part$segments.$extension"  >> segmenter.log 2>&1
    clear
    echo -e "Cut file $filename-part$segments.$extension starting at $SEGSTART and ending at $SEGEND\n"                             
    segment "$segments"
fi
}

file="$1"
filename="${file%.*}"
extension="${file##*.}"
clear
segments=0
segment "$segments"
clear
if (("$segments"==1)); then
mv $filename"-part1."$extension "$filename-segmented.$extension"
elif (("$segments">1)); then
echo "Would you like to join the segments into one file ?"      
       OPTIONS="Yes No Quit"
       select opt in $OPTIONS; do
       clear
        if [ "$opt" == "Quit" ]; then
            exit
        elif [ "$opt" == "Yes" ]; then
            clear
            echo "Joining segments"
            ffmpeg -f concat -i <(for f in $filename"-part"*$extension;         do echo "file '$(pwd)/$f'"; done) -c:a copy -c:v copy "$filename-segmented.$extension" >>         segmenter.log 2>&1
            clear
            echo "Would you like to delete the part files ?"
            select opt in $OPTIONS; do
            clear
            if [ "$opt" == "Quit" ]; then
                exit
            elif [ "$opt" == "Yes" ]; then
                for f in $filename"-part"*$extension; do rm $f; done
                break
            elif [ "$opt" == "No" ]; then
                break
            else
                clear
                echo -e "Bad option\n"
            fi
            done
            break
        clear
        elif [ "$opt" == "No" ]; then
            exit
        else
            clear
            echo -e "Bad option\n"
        fi
    done
fi
echo "Would you like to replace the original file with the result of your changes ?"
OPTIONS="Yes No Quit"
select opt in $OPTIONS; do
    clear
    if [ "$opt" == "Quit" ]; then
        exit
    elif [ "$opt" == "Yes" ]; then
        rm $file
        mv "$filename-segmented.$extension" $file
        break
    elif [ "$opt" == "No" ]; then
        break
    else
        clear
        echo -e "Bad option\n"
    fi
done

2
Bu senaryo harika! Birleştirme için mutlak yollar kullandığınızdan beri yalnızca bir değişiklik: add safe=0, yani ffmpeg -f concat -safe 0 -i ...Bkz. Ffmpeg.org/ffmpeg-all.html#Options-36
pkSML

2

PtQa'nın verdiği cevap işe yarıyor gibi görünse de, iyi çalıştığını kanıtlayan başka bir çözüm geliştirdim.

Temel olarak, yaptığım şey, sonucuma eklemek istediğim orijinal videonun her bölümü için bir video kesmektir. Daha sonra, onları burada açıklanan Concat Demuxer ile bir araya getirdim .

Çözüm, önce denedim ve senkronizasyon sorunları sunduğumla aynı. Eklediğim farklı videolar oluştururken -avoid_negative_ts 1 komutu . Bu çözümle senkronizasyon sorunları ortadan kalkar.


1

PtQa'nın yaklaşımını takip etmekte zorlananlar için, bu konuda biraz daha kolay bir yöntem var. Her bir adımı birleştirmek yerine, hepsini sonunda tamamlayın.

Her giriş için bir A / V çifti tanımlayın:

//Input1:
[0:v]trim=start=10:end=20,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10:end=10,asetpts=PTS-STARTPTS[0a];
//Input2:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[1a];
//Input3:
[0:v]trim=start=30:end=40,setpts=PTS-STARTPTS,format=yuv420p[2v];
[0:a]atrim=start=30:end=40,asetpts=PTS-STARTPTS[2a];

İhtiyacınız kadar çift tanımlayın, sonra hepsini bir seferde birleştirin, burada n = total giriş sayısı.

[0v][0a][1v][1a][2v][2a]concat=n=3:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4

Bu kolayca bir döngü içinde inşa edilebilir.

2 giriş kullanan eksiksiz bir komut şöyle görünebilir:

 -y -i in.mp4 -filter_complex 
[0:v]trim=start=10.0:end=15.0,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10.0:end=15.0,asetpts=PTS-STARTPTS[0a];
[0:v]trim=start=65.0:end=70.0,setpts=PTS-STARTPTS,format=yuv420p[1v];
[0:a]atrim=start=65.0:end=70.0,asetpts=PTS-STARTPTS[1a];[0v][0a][1v]
[1a]concat=n=2:v=1:a=1[outv][outa] -map [outv] -map [outa] out.mp4
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.