FFmpeg를 사용하여 비디오에서 여러 세그먼트를 제거하려면 어떻게해야합니까?


51

FFmpeg를 사용하여 비디오의 일부 섹션을 삭제하려고합니다.

예를 들어 TV에서 프로그램을 녹화하고 광고를 중단하려고한다고 가정 해보십시오. 이것은 GUI 비디오 편집기로 간단합니다. 제거 할 각 클립의 시작과 끝을 표시하고 삭제를 선택하면됩니다. FFmpeg를 사용하여 명령 줄에서 동일한 작업을 수행하려고합니다.

단일 세그먼트를 새로운 비디오 로 자르는 방법을 알고 있습니다 .

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

이렇게하면 5 초짜리 클립이 잘려서 새 비디오 파일로 저장되지만 , 지정된 클립 없이 반대 방향으로 전체 비디오 저장하는 방법과 제거 할 여러 클립을 지정하는 방법은 무엇입니까?

예를 들어 ABCDEFG로 비디오를 표현할 수 있으면 ACDFG로 구성된 새 비디오를 만들고 싶습니다.



1
그것은 내가 요구하는 것이 아니며, 하나의 "에피소드"로 모든 것을 얻고 싶지만 이것은 원래 비디오와 다른 섹션을 가질 것입니다.
Matias

@Matias, 비디오에서 몇 개의 클립을 잘라 내고 나머지는 그대로 두는 방법을 묻는다면 그중 하나가 될 것입니다.하지만 몇 개의 클립을 가져 와서 다른 비디오의 클립과 결합하고 싶습니다. 이것은 별개의 독특한 질문 이 아닙니다 . 별도의 세그먼트를 얻기 위해 다른 질문에서 수행 한 작업을 수행 한 다음 결합해야합니다.
Synetech

1
@Synetech 귀하의 답변에 감사드립니다. 다른 비디오의 클립과 결합하고 싶지 않습니다. 비디오에서 일부 부분을 제거하고 싶습니다. 예를 들어 ABCDEFG로 비디오를 표현할 수 있으면 ACDFG로 구성된 새 비디오를 만들고 싶습니다.
Matias

@Synetech 그건 내가 아니에요, 오해 한 사람은 Tog였습니다.
Matias

답변:


47

그래도 여전히 trim필터를 사용할 수 있습니다 . 다음은 30-40 초 (10 초) 및 50-80 초 (30 초) 세그먼트를 잘라 내고 싶다고 가정하는 예입니다.

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

내가 여기서 무엇을 했습니까? 처음 30 초, 40-50 초 및 80 초를 트리밍 한 다음 필터 out1와 함께 스트림 으로 결합했습니다 concat.

setpts 정보 : 트림은 영상 표시 시간을 수정하지 않기 때문에 필요하며 10 초를 잘라 내면 10 초 동안 디코더 카운터에 프레임이 표시되지 않습니다.

오디오도 함께하려면 오디오 스트림에 대해서도 동일하게 수행해야합니다. 따라서 명령은 다음과 같아야합니다.

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

대단해! 왜 우리는 setpt가 필요합니까? 설명을 추가해 주시겠습니까?
Rajib

좋아, 업데이트 된 답변을 참조하십시오.
ptQa

4
+1 이것은 실제로 답변으로 선택되어야합니다. "다중 세그먼트"에 더 적합하며 여러 명령을 차례로 수행 할 필요가 없습니다. (다른 방법이 더 속도 효율적이지 않으면)
Knossos

1
프레임을 건너 뛸 때 자막 데이터를 유지하는 방법은 무엇입니까?
Andrew Scagnelli

1
이것은 매우 비인간적입니다.
golopot

15

ptQa의 솔루션을 작동시킬 수는 없습니다. 주로 필터의 오류의 의미 또는 해결 방법을 알 수 없기 때문입니다. 내 솔루션은 혼란스러워 할 수 있기 때문에 약간 복잡해 보이지만 스크립트에 던지면 정리가 자동화 될 수 있습니다. 4 단계에서 문제가 발생하면 1-3 단계를 완료하여 오류를 복구하는 것이 좀 더 효율적이기 때문에이 방법이 마음에 듭니다.

기본 전략은 원하는 각 세그먼트의 비디오를 사용 -t하고 -ss얻은 다음 최종 버전의 모든 부분을 결합하는 것입니다.

5 초 길이의 ABCDEF 6 개 세그먼트가 있고 A (0-5 초), C (10-15 초) 및 E (20-25 초)를 원한다고 가정합니다.

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

또는

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

그러면 a.tvshow, c.tvshow 및 e.tvshow 파일이 만들어집니다. 는 -t각 클립이 얼마나 오래 말한다, 그래서 C는 30 초입니다 경우 길이는 30 0시 0분 30초에 전달할 수 있습니다. 이 -ss옵션은 소스 비디오로 건너 뛰는 정도를 나타내므로 항상 파일 시작과 관련이 있습니다.

그런 다음 많은 비디오 파일 ace-files.txt이 있으면 다음과 같은 파일을 만듭니다 .

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

시작 부분에 "파일"이 있고 그 뒤에 이스케이프 된 파일 이름이 있습니다.

그런 다음 명령 :

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

모든 파일을 abe-files.txt하나로 묶어 오디오 및 비디오 코덱을 복사하고 ace.tvshow섹션 a, c 및 e 만 있는 파일 을 만듭니다 . 그럼 그냥 삭제 기억 ace-files.txt, a.tvshow, c.tvshowe.tvshow.

면책 조항 : 나는 이것이 다른 접근 방식과 비교하여 (비) 효율적이라고 생각하지 ffmpeg않지만 내 목적으로는 더 효과적입니다. 그것이 누군가를 돕기를 바랍니다.


4
이 하나, 덕분에 나 같은 초보자를위한 아주 이해할 수있다
juanpastas

4
bash를 사용하는 경우 다음과 같이 파일을 작성하지 ace-files.txtffmpeg -f concat -i <(printf "file '%s'\n" $(pwd)/prefix_*.tvshow) -c copy output.tvshow
않아도

이것은 실제로 도움이되었지만 파일 이름 주위에서 작은 따옴표를 제거해야하거나 작동하지 않았습니다.
tchaymore

비디오를 연결하면 약 25ms의 짧은 검은 색 비디오와 무음 오디오가 있습니다. 그것을 제거 할 아이디어가 있습니까?
Antonio Bonifati '농장'

흠 ... 시간 오프셋과 건너 뛰기에서 수학을 확인하고 25ms 간격을 남기지 않도록하십시오 ... 나는 그것을 경험하지 못했습니다.
xbakesx

5

녹화 된 TV 편집 속도를 높이기위한 스크립트를 만들었습니다. 스크립트는 유지하려는 세그먼트의 시작 및 종료 시간을 묻고 파일로 분할합니다. 다음과 같은 옵션을 제공합니다.

  • 하나 또는 여러 개의 세그먼트를 가져옵니다.
  • 세그먼트를 하나의 결과 파일로 결합 할 수 있습니다.
  • 가입 한 후 부품 파일을 유지하거나 삭제할 수 있습니다.
  • 원본 파일을 유지하거나 새 파일로 교체 할 수 있습니다.

실제 동영상 : 여기

당신이 무슨 생각을하는지 제게 알려주세요.

 #!/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
이 스크립트는 훌륭합니다! 당신이 연결에 대한 절대 경로를 사용하고 있기 때문에 딱 한 수정 : 추가 safe=0, 즉 ffmpeg -f concat -safe 0 -i ...참조 ffmpeg.org/ffmpeg-all.html#Options-36
pkSML

2

ptQa가 제공 한 답변이 효과가있는 것처럼 보이지만 잘 작동하는 다른 솔루션을 개발했습니다.

기본적으로 내가하는 일은 결과에 포함하려는 원본 비디오의 각 부분에 대해 하나의 비디오를 잘라내는 것입니다. 나중에 여기에 설명 된 Concat Demuxer와 연결합니다 .

해결책은 내가 먼저 시도하고 동기화 문제를 제시 한 것과 같습니다. 내가 추가 한 것은 다른 비디오를 생성 할 때 -avoid_negative_ts 1 명령 입니다. 이 솔루션을 사용하면 동기화 문제가 사라집니다.


1

ptQa의 접근 방식을 따르는 데 어려움이있는 사람들에게는 조금 더 간소화 된 방법이 있습니다. 각 단계를 연결하는 대신 끝 부분에서 모두 수행하십시오.

각 입력에 대해 A / V 쌍을 정의하십시오.

//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];

필요한만큼 쌍을 정의한 다음 한 번에 모두 연결합니다. 여기서 n = 총 입력 수입니다.

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

루프에서 쉽게 구성 할 수 있습니다.

2 개의 입력을 사용하는 완전한 명령은 다음과 같습니다.

 -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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.