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

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

FFmpeg를 사용하여 동영상의 일부 섹션을 삭제하려고 합니다.

예를 들어, 텔레비전 프로그램을 녹화했는데 광고 부분을 잘라내고 싶다고 상상해 보세요. 이는 GUI 비디오 편집기를 사용하면 간단합니다. 제거할 각 클립의 시작과 끝을 표시하고 삭제를 선택하면 됩니다. FFmpeg를 사용하여 명령줄에서 동일한 작업을 수행하려고 합니다.

나는 단일 세그먼트를새로운다음과 같은 영상:

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

이렇게 하면 5초짜리 클립을 잘라서 새 동영상 파일로 저장하는데, 반대로 하면 어떻게 전체 동영상을 저장할 수 있나요?없이제거할 여러 클립을 지정하려면 어떻게 해야 합니까?

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

답변1

글쎄, 당신은 여전히 ​​​​을 사용할 수 있습니다trim그것을 필터링합니다. 다음은 예입니다. 비디오의 중간뿐만 아니라 처음과 끝 부분에서 세 개의 세그먼트를 잘라낸다고 가정해 보겠습니다.

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여과하고 30-40초(10초) 및 50-80초(30초)를 둡니다.

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

답변2

나는 ptQa의 솔루션을 작동시킬 수 없습니다. 대부분 필터의 오류가 무엇을 의미하는지 또는 이를 수정하는 방법을 알 수 없기 때문입니다. 내 솔루션은 혼란을 초래할 수 있기 때문에 조금 더 투박해 보이지만 스크립트에 적용하면 정리가 자동화될 수 있습니다. 또한 4단계에서 문제가 발생하면 1~3단계가 완료되므로 오류 복구가 좀 더 효율적이기 때문에 이 접근 방식을 좋아합니다.

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

각각 5초 길이의 6개 세그먼트 ABCDEF가 있고 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:00: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

그러면 모든 파일을 함께 연결하여 오디오 및 비디오 코덱을 복사하고 섹션 a, c 및 e로만 구성된 abe-files.txt파일을 만듭니다 . 그런 다음 , 및 을 삭제 ace.tvshow하는 것을 잊지 마세요 .ace-files.txta.tvshowc.tvshowe.tvshow

부인 성명: 이것이 다른 접근 방식과 비교하여 얼마나 (비)효율적인지는 모르겠지만 ffmpeg제 목적으로는 더 잘 작동합니다. 누군가에게 도움이 되기를 바랍니다.

답변3

ptQa의 접근 방식을 따르는 데 어려움을 겪는 사람들을 위해 좀 더 간소화된 방법이 있습니다. 각 단계를 연결하는 대신 마지막에 모두 수행하세요.

각 입력에 대해 A/V 쌍을 정의합니다.

//Input1:
[0:v]trim=start=10:end=20,setpts=PTS-STARTPTS,format=yuv420p[0v];
[0:a]atrim=start=10:end=20,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개의 입력을 사용하는 완전한 명령은 다음과 같습니다.

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

답변4

녹화된 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

관련 정보