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.txt
a.tvshow
c.tvshow
e.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