Я пытаюсь удалить несколько фрагментов видео с помощью FFmpeg.
Например, представьте, что вы записали шоу на телевидении и хотите вырезать рекламу. Это просто сделать с помощью графического видеоредактора; вы просто отмечаете начало и конец каждого клипа, который нужно удалить, и выбираете «Удалить». Я пытаюсь сделать то же самое из командной строки с помощью FFmpeg.
Я знаю, как разрезать один сегмент нановыйвидео вроде этого:
ffmpeg -i input.avi -ss 00:00:20 -t 00:00:05 -map 0 -codec copy output.avi
Это вырезает пятисекундный клип и сохраняет его как новый видеофайл, но как сделать наоборот и сохранить все видео?безуказанный клип, и как я могу указать несколько клипов для удаления?
Например, если мое видео можно представить как 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 сек).
О настройках: это нам нужно, потому что обрезка не изменяет время отображения картинки, и когда мы вырезаем 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
для получения видео каждого нужного вам сегмента, а затем в объединении всех частей для получения финальной версии.
Допустим, у вас есть 6 сегментов ABCDEF, каждый длиной 5 секунд, и вы хотите, чтобы 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'
Обратите внимание на слово «file» в начале и замаскированное имя файла после него.
Затем команда:
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.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
Я сделал скрипт для ускорения редактирования записанного ТВ. Скрипт запрашивает время начала и окончания сегментов, которые вы хотите сохранить, и разбивает их на файлы. Он дает вам опции, вы можете:
- Возьмите один или несколько сегментов.
- Вы можете объединить сегменты в один результирующий файл.
- После присоединения вы можете сохранить или удалить файлы деталей.
- Вы можете сохранить исходный файл или заменить его новым файлом.
Дайте мне знать, что вы думаете.
#!/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