Я пытаюсь динамически обрезать видео с помощью фильтра sendcmd
FFmpeg на основе координат, указанных в текстовом файле, но команды обрезки, похоже, не действуют. Вот формат команд, которые я пробовал, и соответствующая команда FFmpeg, которую я использую.
Следуя документации https://ffmpeg.org/ffmpeg-filters.html#sendcmd_002c-asendcmd, команды в текстовом файле (coordinates.txt
) выглядят следующим образом:
0.05 [enter] crop w=607:h=1080:x=0:y=0;
0.11 [enter] crop w=607:h=1080:x=0:y=0;
...
Команда ffmpeg:
ffmpeg -i '10s.mp4' -filter_complex "[0:v]sendcmd=f=coordinates.txt" -c:v libx264 -c:a copy -r 30 output.mp4
Кажется, это ничего не дает.
А с командами в текстовом файле (coordinates.txt
) вот так:
0.05 crop w 607, crop h 1080, crop x 0, crop y 0;
0.11 crop w 607, crop h 1080, crop x 0, crop y 0;
...
Команда ffmpeg:
ffmpeg -i '10s.mp4' -filter_complex "[0:v]sendcmd=f=coordinates.txt,crop" -c:v libx264 -c:a copy -r 30 output.mp4
(после этого ответа https://stackoverflow.com/a/67508233/1967110)
Этот что-то делает, но что-то очень грязное. Похоже, что он обрезает правильный x, но не учитывает y, w или h и помещает обрезку в правую часть входного видео.
Редактировать: я пытаюсь создать видео 607x1080 (портретный формат, 9:16) из видео 1920x1080 с параметром x, меняющимся во времени (представьте, что кадр 9:16 скользит по горизонтали по видео 16:9) . Итак, фиксированные w, h и y, меняется только x.
Я использую эту версию ffmpeg:
ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
configuration: --prefix=/home/ffmpeg-builder/release --pkg-config-flags=--static --extra-libs=-lm --disable-doc --disable-debug --disable-shared --disable-ffprobe --enable-static --enable-gpl --enable-version3 --enable-runtime-cpudetect --enable-avfilter --enable-filters --enable-nvenc --enable-nvdec --enable-cuvid --toolchain=hardened --disable-stripping --enable-opengl --pkgconfigdir=/home/ffmpeg-builder/release/lib/pkgconfig --extra-cflags='-I/home/ffmpeg-builder/release/include -static-libstdc++ -static-libgcc ' --extra-ldflags='-L/home/ffmpeg-builder/release/lib -fstack-protector -static-libstdc++ -static-libgcc ' --extra-cxxflags=' -static-libstdc++ -static-libgcc ' --extra-libs='-ldl -lrt -lpthread' --enable-ffnvcodec --enable-gmp --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libfdk-aac --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libkvazaar --enable-libmp3lame --enable-libopus --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libshine --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtheora --enable-libvidstab --ld=g++ --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-libzimg --enable-openssl --enable-zlib --enable-nonfree --extra-libs=-lpthread --enable-pthreads --extra-libs=-lgomp
@БаярГончижапов первый или второй формат я упоминаю?
например: 0 crop w 640, crop h 640, crop x 0, crop y 0; 2 crop x 100; 4 crop x 200; 6 crop x 300; 8 crop x 400;
. не обязательно писать w и h в каждой строке
@БаярГончижапов, а ты запускаешь этот файл с ffmpeg -i 'input.mp4' -filter_complex "[0:v]sendcmd=f=coordinates.txt,crop" -c:v libx264 -c:a copy -r 29.97 output.mp4
(с ,crop
в конце) ? Если да, можете ли вы опубликовать полный пример, я не могу заставить эту команду работать, она либо не работает (без ,crop
), либо обрезает с неприятным результатом (с ,crop
)
Итак, единственная команда, которая у меня пока работает, взята из https://video.stackexchange.com/posts/19403/revisions, это:
ffmpeg -i in.mp4 -filter_complex_script file.txt -map "[out]" output.mp4
с file.txt
вот так
nullsrc=WxH:r=FPS[cv];
[cv][0]overlay=-X0:-Y0:shortest=1:enable='eq(n\,0)'[b0];
[b0][0]overlay=-X1:-Y1:shortest=1:enable='eq(n\,1)'[b1];
[b1][0]overlay=-X2:-Y2:shortest=1:enable='eq(n\,2)'[b2];
...
[bm-1][0]overlay=-Xm:-Ym:shortest=1:enable='eq(n\,m)'[out]
для обрезки видео 1920x1080, 5 с, 30 кадров в секунду до видео 606x1080 требуется около 40 секунд (в Google Colab, поэтому процессор низкого уровня и без графического процессора). НО это также вызывает ошибки «Недостаточно памяти», как только необходимо обрезать более 900 кадров на компьютере с ОЗУ 12 ГБ (см. https://superuser.com/questions/1839180/ffmpeg-out-of-memory-issues -with-filter-complex-script).
Еще я попробовал сначала разложить видео на каждый кадр, обрезать каждый кадр и собрать видео заново. Вот код:
def decompose_video(video_path, frames_dir):
if not os.path.exists(frames_dir):
os.makedirs(frames_dir)
command = [
'ffmpeg', '-i', video_path,
os.path.join(frames_dir, 'frame_%04d.png')
]
subprocess.run(command, check=True)
def read_crop_values(crop_file):
with open(crop_file, 'r') as file:
crops = [line.strip().split(',') for line in file]
return crops
def apply_crop(frames_dir, crops):
cropped_frames_dir = frames_dir + '_cropped'
if not os.path.exists(cropped_frames_dir):
os.makedirs(cropped_frames_dir)
for i, crop in enumerate(crops, 1):
x, y, w, h = crop
input_frame = os.path.join(frames_dir, f'frame_{i:04d}.png')
output_frame = os.path.join(cropped_frames_dir, f'frame_{i:04d}.png')
command = [
'ffmpeg', '-i', input_frame,
'-filter:v', f'crop = {w}:{h}:{x}:{y}',
output_frame
]
subprocess.run(command, check=True)
def recreate_video(cropped_frames_dir, output_video_path):
command = [
'ffmpeg', '-y', '-loglevel', 'verbose', '-r', '29.97', '-f', 'image2', '-i',
os.path.join(cropped_frames_dir, 'frame_%04d.png'),
'-vcodec', 'libx264', '-crf', '23', '-pix_fmt', 'yuv420p',
output_video_path
]
try:
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print("FFmpeg failed with error:")
print(e.stderr.decode())
video_path = 'input.mp4'
frames_dir = 'frames'
crop_file = 'crop_values.txt'
output_video_path = 'output.mp4'
decompose_video(video_path, frames_dir)
crops = read_crop_values(crop_file)
apply_crop(frames_dir, crops)
recreate_video(frames_dir + '_cropped', output_video_path)
и в crop_values.txt
просто ставишь x, y, w, h:
0,0,606,1080
8,0,606,1080
17,0,606,1080
Это тоже работает, но немного медленнее (1 минута 40 минут для того же видео).
проверенный вариант:
#!/bin/bash
echo "#test crop
0 crop w 607, crop h 1080, crop x 0, crop y 0;
1 crop x 200;
2 crop x 400;
3 crop x 600;
" > coordinates.txt
ffmpeg -loop 1 -t 4 -r 30000/1001 -i "1920x1080.jpg" -filter_complex "sendcmd=f=coordinates.txt,crop" /tmp/out.mp4 -y
ffplay /tmp/out.mp4
-pix_fmt ...
эта опция сбрасывает выходное разрешение на входное.
в моем случае это работает правильно, если команды начинаются с 0 (а не 0,05) и имеют одинаковые значения w и h во всех строках