Те же операции в ffmpeg, включая HEVC и HDR

Для пакетной обработки записей с iPhone удобнее терминал. Здесь готовые команды для macOS, Windows и Linux: замедление, ускорение, преобразование HDR в BT.709 и корректный поворот вертикального кадра.

Если файлов много, они очень большие или нужен скрипт, удобнее считать локально через ffmpeg. Ниже те же режимы, что и на сайте: дублирование, смешивание и интерполяция по движению. Замените имена файлов на свои.

Во всех командах F - кратность замедления (8 или 16), FPS - частота кадров результата (60 для x8, 30 для x16), а INPUT и OUTPUT - имена исходного и итогового файлов. В командах для Windows те же два файла называются IN и OUT: в PowerShell $input - зарезервированная системная переменная, и использовать её под своё имя нельзя.

Нажмите «Скопировать», чтобы забрать команду целиком.

Установка ffmpeg

Один раз ставим ffmpeg, дальше всё работает офлайн. Проверить установку можно командой ffmpeg -version.

macOS

brew install ffmpeg
ffmpeg -version
brew tap homebrew-ffmpeg/ffmpeg && brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-zimg

Сборка из Homebrew включает libx264, libx265 и videotoolbox, поэтому все режимы замедления работают сразу. Но zimg в неё НЕ входит: фильтра zscale там нет, и цепочка тонмаппинга HDR не запустится. Проверить: ffmpeg -hide_banner -filters | grep zscale. Если строки нет, а HDR обрабатывать надо, поставьте сборку из тапа третьей командой. Если Homebrew ещё нет, поставьте его по инструкции с brew.sh.

Windows

winget install Gyan.FFmpeg
ffmpeg -version
chcp 65001

После установки закройте терминал и откройте новый, иначе PATH не подхватится. Сборка Gyan.FFmpeg собрана с libx264, libx265, zimg и поддержкой nvenc, qsv и amf. Третья команда переключает cmd.exe на UTF-8: без неё русские и эмодзи в именах файлов превращаются в вопросительные знаки.

Linux

sudo apt update && sudo apt install ffmpeg
ffmpeg -version
curl -L -O https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && tar xf ffmpeg-release-amd64-static.tar.xz && sudo install -m 755 ffmpeg-*-amd64-static/ffmpeg ffmpeg-*-amd64-static/ffprobe /usr/local/bin/

В Debian и Ubuntu версия из репозитория часто отстаёт на год-полтора. Если ffmpeg -version показывает 4.x, ставьте статическую сборку johnvansickle.com третьей командой: она распаковывается в один каталог, ни с чем не конфликтует и включает libx264, libx265 и zimg. В Fedora dnf install ffmpeg, в Arch pacman -S ffmpeg.

Посмотреть параметры файла

Сначала смотрим кодек, разрешение, частоту кадров и поворот: от них зависят все следующие команды.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate,nb_frames,color_transfer:stream_tags=rotate -of json "$INPUT"

color_transfer со значением arib-std-b67 это HLG, smpte2084 это PQ. И то и другое - HDR, см. шаг про тонмаппинг.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate,nb_frames,color_transfer:stream_tags=rotate -of json "$IN"

color_transfer со значением arib-std-b67 это HLG, smpte2084 это PQ. И то и другое - HDR, см. шаг про тонмаппинг.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate,nb_frames,color_transfer:stream_tags=rotate -of json "$INPUT"

color_transfer со значением arib-std-b67 это HLG, smpte2084 это PQ. И то и другое - HDR, см. шаг про тонмаппинг.

Замедление дублированием кадров

Самый быстрый и надёжный способ: кадры повторяются, артефактов нет, движение выглядит ступенчатым.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

setpts обязан стоять ПЕРЕД fps. Поставите наоборот - fps сработает на исходных метках, setpts растянет уже прореженный поток, и замедления не будет вовсе.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"

setpts обязан стоять ПЕРЕД fps. Поставите наоборот - fps сработает на исходных метках, setpts растянет уже прореженный поток, и замедления не будет вовсе.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

setpts обязан стоять ПЕРЕД fps. Поставите наоборот - fps сработает на исходных метках, setpts растянет уже прореженный поток, и замедления не будет вовсе.

Замедление со смешиванием

Соседние кадры смешиваются, рывки превращаются в мягкий смаз. Считается почти так же быстро, как дублирование.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Порядок тот же: setpts, потом minterpolate. Параметр fps внутри minterpolate задаёт частоту результата, отдельный фильтр fps здесь не нужен.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"

Порядок тот же: setpts, потом minterpolate. Параметр fps внутри minterpolate задаёт частоту результата, отдельный фильтр fps здесь не нужен.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Порядок тот же: setpts, потом minterpolate. Параметр fps внутри minterpolate задаёт частоту результата, отдельный фильтр fps здесь не нужен.

Интерполяция по движению

Фильтр minterpolate достраивает промежуточные кадры по векторам движения. Результат лучший, но расчёт долгий, а на воде, листве и склейках возможны искажения.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Прогоните сначала десять секунд через -ss 0 -t 10, посмотрите на результат и только потом запускайте весь файл. Переделывать двухчасовой просчёт обидно.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"

Прогоните сначала десять секунд через -ss 0 -t 10, посмотрите на результат и только потом запускайте весь файл. Переделывать двухчасовой просчёт обидно.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Прогоните сначала десять секунд через -ss 0 -t 10, посмотрите на результат и только потом запускайте весь файл. Переделывать двухчасовой просчёт обидно.

Переменная частота кадров

Записи с экрана и видео с телефонов часто идут с плавающим FPS. Приводим их к постоянной частоте, иначе замедление разъедется со звуком.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "fps=30,setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Первое число 30 в fps=30 - это исходная частота из ffprobe, а не целевая. Целевая стоит внутри minterpolate. Их легко перепутать местами, и тогда файл просто не замедлится.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "fps=30,setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"

Первое число 30 в fps=30 - это исходная частота из ffprobe, а не целевая. Целевая стоит внутри minterpolate. Их легко перепутать местами, и тогда файл просто не замедлится.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "fps=30,setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=mci" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Первое число 30 в fps=30 - это исходная частота из ffprobe, а не целевая. Целевая стоит внутри minterpolate. Их легко перепутать местами, и тогда файл просто не замедлится.

HDR в SDR

Материал с iPhone в HLG или PQ в обычных плеерах выглядит блёкло. Преобразуем цвет в BT.709. Проверьте, что в вашей сборке есть фильтр zscale: в стандартной сборке Homebrew его нет.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv,format=yuv420p,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -hide_banner -filters | grep zscale

zscale есть только в сборках с --enable-libzimg. Проверьте так: ffmpeg -hide_banner -filters | grep zscale. Чаще всего его нет именно на macOS: стандартная сборка Homebrew идёт без zimg, а вот сборки Gyan для Windows, пакеты Debian и Ubuntu и статические сборки johnvansickle его содержат. Если фильтра нет, вариантов два. На машине с OpenCL можно взять tonemap_opencl (сложнее в настройке, нужен -init_hw_device opencl). Либо ограничиться format=yuv420p вместо всей цепочки: файл откроется везде, но цвета останутся неверными, светы уйдут в белое, а насыщенность просядет. Не молчите об этом в результате.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv,format=yuv420p,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"
ffmpeg -hide_banner -filters | Select-String zscale

zscale есть только в сборках с --enable-libzimg. Проверьте так: ffmpeg -hide_banner -filters | grep zscale. Чаще всего его нет именно на macOS: стандартная сборка Homebrew идёт без zimg, а вот сборки Gyan для Windows, пакеты Debian и Ubuntu и статические сборки johnvansickle его содержат. Если фильтра нет, вариантов два. На машине с OpenCL можно взять tonemap_opencl (сложнее в настройке, нужен -init_hw_device opencl). Либо ограничиться format=yuv420p вместо всей цепочки: файл откроется везде, но цвета останутся неверными, светы уйдут в белое, а насыщенность просядет. Не молчите об этом в результате.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv,format=yuv420p,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -hide_banner -filters | grep zscale

zscale есть только в сборках с --enable-libzimg. Проверьте так: ffmpeg -hide_banner -filters | grep zscale. Чаще всего его нет именно на macOS: стандартная сборка Homebrew идёт без zimg, а вот сборки Gyan для Windows, пакеты Debian и Ubuntu и статические сборки johnvansickle его содержат. Если фильтра нет, вариантов два. На машине с OpenCL можно взять tonemap_opencl (сложнее в настройке, нужен -init_hw_device opencl). Либо ограничиться format=yuv420p вместо всей цепочки: файл откроется везде, но цвета останутся неверными, светы уйдут в белое, а насыщенность просядет. Не молчите об этом в результате.

Поворот

Если картинка легла на бок, применяем поворот к пикселям и снимаем флаг из метаданных.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "transpose=1,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -metadata:s:v rotate=0 -an "$OUTPUT"

transpose=1 это поворот на 90 градусов по часовой стрелке, transpose=2 - против часовой, для 180 берите transpose=1,transpose=1 или hflip,vflip. Нужное значение подскажет тег rotate из ffprobe: 90 - transpose=1, 270 - transpose=2. Ширина и высота при этом меняются местами.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "transpose=1,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -metadata:s:v rotate=0 -an "$OUT"

transpose=1 это поворот на 90 градусов по часовой стрелке, transpose=2 - против часовой, для 180 берите transpose=1,transpose=1 или hflip,vflip. Нужное значение подскажет тег rotate из ffprobe: 90 - transpose=1, 270 - transpose=2. Ширина и высота при этом меняются местами.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "transpose=1,setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -metadata:s:v rotate=0 -an "$OUTPUT"

transpose=1 это поворот на 90 градусов по часовой стрелке, transpose=2 - против часовой, для 180 берите transpose=1,transpose=1 или hflip,vflip. Нужное значение подскажет тег rotate из ffprobe: 90 - transpose=1, 270 - transpose=2. Ширина и высота при этом меняются местами.

Звук

По умолчанию дорожку проще убрать. Если звук нужен, растягиваем его цепочкой atempo, но на восьмикратном замедлении он превращается в гул.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend[v];[0:a]atempo=0.5,atempo=0.5,atempo=0.5[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=16*PTS,minterpolate=fps=30:mi_mode=blend[v];[0:a]atempo=0.5,atempo=0.5,atempo=0.5,atempo=0.5[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=0.125*PTS[v];[0:a]atempo=2.0,atempo=2.0,atempo=2.0[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"

Общий множитель цепочки atempo обязан равняться 1/F, иначе звук уедет от картинки. Проверяется умножением: 0.5 в кубе это 0.125, то есть 1/8. Три команды выше - это x8, x16 и ускорение x8; для других кратностей просто добавляйте или убирайте звенья.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -filter_complex "[0:v]setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend[v];[0:a]atempo=0.5,atempo=0.5,atempo=0.5[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUT"

Общий множитель цепочки atempo обязан равняться 1/F, иначе звук уедет от картинки. Проверяется умножением: 0.5 в кубе это 0.125, то есть 1/8. Три команды выше - это x8, x16 и ускорение x8; для других кратностей просто добавляйте или убирайте звенья.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=${F}*PTS,minterpolate=fps=${FPS}:mi_mode=blend[v];[0:a]atempo=0.5,atempo=0.5,atempo=0.5[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=16*PTS,minterpolate=fps=30:mi_mode=blend[v];[0:a]atempo=0.5,atempo=0.5,atempo=0.5,atempo=0.5[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"
ffmpeg -i "$INPUT" -filter_complex "[0:v]setpts=0.125*PTS[v];[0:a]atempo=2.0,atempo=2.0,atempo=2.0[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -c:a aac -b:a 192k "$OUTPUT"

Общий множитель цепочки atempo обязан равняться 1/F, иначе звук уедет от картинки. Проверяется умножением: 0.5 в кубе это 0.125, то есть 1/8. Три команды выше - это x8, x16 и ускорение x8; для других кратностей просто добавляйте или убирайте звенья.

Прогресс и время

Долгие задачи удобно запускать с выводом прогресса в файл, чтобы видеть, сколько осталось.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -progress pipe:1 -nostats -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -progress progress.txt -nostats -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Пример: исходник 12 секунд, F=8, FPS=60 даёт ceil(12 * 8 * 60) = 5760 кадров на выходе. Если frame= растёт на 20 в секунду, работы примерно на 5 минут. Вторая команда пишет то же самое в файл progress.txt, что удобно, когда ffmpeg запущен в фоне.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -progress pipe:1 -nostats -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUT"

Пример: исходник 12 секунд, F=8, FPS=60 даёт ceil(12 * 8 * 60) = 5760 кадров на выходе. Если frame= растёт на 20 в секунду, работы примерно на 5 минут. Вторая команда пишет то же самое в файл progress.txt, что удобно, когда ffmpeg запущен в фоне.

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -progress pipe:1 -nostats -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -progress progress.txt -nostats -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

Пример: исходник 12 секунд, F=8, FPS=60 даёт ceil(12 * 8 * 60) = 5760 кадров на выходе. Если frame= растёт на 20 в секунду, работы примерно на 5 минут. Вторая команда пишет то же самое в файл progress.txt, что удобно, когда ffmpeg запущен в фоне.

Какие кодировщики есть в системе

Сначала посмотрите, с чем собран ваш ffmpeg. Строка в списке ничего не гарантирует: кодировщик может быть собран, а железа под него нет. Надёжна только тестовая прогонка одного кадра.

macOS

ffmpeg -hide_banner -encoders | grep -E "videotoolbox|nvenc|qsv|amf|vaapi"
ffmpeg -hide_banner -f lavfi -i testsrc=size=1280x720:rate=30 -frames:v 1 -c:v h264_videotoolbox -f null -

Честный размен: libx264 -crf 18 остаётся значением по умолчанию, потому что при равном битрейте он даёт лучшую картинку. Аппаратный кодировщик - это опция "быстро", и качество при том же битрейте будет ниже, особенно на движении. И главное: в режиме motion узкое место не кодирование, а minterpolate, который считается на CPU. Там аппаратный кодировщик почти ничего не выигрывает, потому что кодировщик всё равно простаивает в ожидании кадров.

Windows

ffmpeg -hide_banner -encoders | Select-String "videotoolbox|nvenc|qsv|amf|vaapi"
ffmpeg -hide_banner -f lavfi -i testsrc=size=1280x720:rate=30 -frames:v 1 -c:v h264_nvenc -f null -

Честный размен: libx264 -crf 18 остаётся значением по умолчанию, потому что при равном битрейте он даёт лучшую картинку. Аппаратный кодировщик - это опция "быстро", и качество при том же битрейте будет ниже, особенно на движении. И главное: в режиме motion узкое место не кодирование, а minterpolate, который считается на CPU. Там аппаратный кодировщик почти ничего не выигрывает, потому что кодировщик всё равно простаивает в ожидании кадров.

Linux

ffmpeg -hide_banner -encoders | grep -E "videotoolbox|nvenc|qsv|amf|vaapi"
ffmpeg -hide_banner -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc=size=1280x720:rate=30 -frames:v 1 -vf "format=nv12,hwupload" -c:v h264_vaapi -f null -

Честный размен: libx264 -crf 18 остаётся значением по умолчанию, потому что при равном битрейте он даёт лучшую картинку. Аппаратный кодировщик - это опция "быстро", и качество при том же битрейте будет ниже, особенно на движении. И главное: в режиме motion узкое место не кодирование, а minterpolate, который считается на CPU. Там аппаратный кодировщик почти ничего не выигрывает, потому что кодировщик всё равно простаивает в ожидании кадров.

Аппаратное кодирование

Аппаратный кодировщик в разы быстрее, но при равном битрейте картинка хуже, особенно на движении. В режиме «По движению» узкое место всё равно minterpolate на процессоре, так что выигрыш там почти нулевой.

macOS

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_videotoolbox -b:v 12M -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v hevc_videotoolbox -tag:v hvc1 -b:v 8M -movflags +faststart -an "$OUTPUT"
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v libx265 -crf 20 -preset slow -tag:v hvc1 -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"

VideoToolbox не понимает -crf, качество задаётся битрейтом через -b:v или -q:v на Apple Silicon. Работает и на Intel, и на M-серии.

Windows

$IN="input.mp4"; $OUT="output.mp4"; $F=8; $FPS=60
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_nvenc -rc vbr -cq 23 -b:v 0 -pix_fmt yuv420p -movflags +faststart -an "$OUT"
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_qsv -global_quality 23 -pix_fmt nv12 -movflags +faststart -an "$OUT"
ffmpeg -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_amf -rc cqp -qp_i 22 -qp_p 24 -pix_fmt yuv420p -movflags +faststart -an "$OUT"

У nvenc вместо crf используется -cq при -rc vbr -b:v 0, у qsv это -global_quality, у amf это -rc cqp с -qp_i и -qp_p. Для AMD: ffmpeg -i "$IN" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_amf -rc cqp -qp_i 22 -qp_p 24 -pix_fmt yuv420p -movflags +faststart -an "$OUT"

Linux

INPUT="input.mp4"; OUTPUT="output.mp4"; F=8; FPS=60
ffmpeg -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS}" -c:v h264_nvenc -rc vbr -cq 23 -b:v 0 -pix_fmt yuv420p -movflags +faststart -an "$OUTPUT"
ffmpeg -vaapi_device /dev/dri/renderD128 -i "$INPUT" -vf "setpts=${F}*PTS,fps=${FPS},format=nv12,hwupload" -c:v h264_vaapi -b:v 12M -movflags +faststart -an "$OUTPUT"

VA-API требует явного устройства и загрузки кадров в память видеокарты: отсюда -vaapi_device /dev/dri/renderD128 и хвост format=nv12,hwupload в фильтре. Проверьте, что устройство существует: ls /dev/dri/. Для nvenc нужны проприетарные драйверы NVIDIA, открытый nouveau кодировать не умеет.

То же самое без браузера

Материал с iPhone требует двух дополнительных шагов, о которых легко забыть: тональное преобразование HLG в SDR и применение флага поворота к пикселям. В командах ниже оба шага уже учтены, поэтому результат не станет блёклым и не ляжет на бок.

Команды рассчитаны на ffmpeg 6 и новее. В более старых версиях названия некоторых фильтров отличаются.

Как это работает

Ни один байт вашего видео не уходит в сеть. Всё, что описано ниже, происходит внутри вкладки браузера.

  1. Файл открывается локально

    Вы выбираете видео, браузер читает его через File API как обычный файл на диске. Запроса на сервер с вашим файлом не происходит: страница отдаётся статикой, а дальше работает только ваше устройство.

  2. Контейнер разбирается на пакеты

    MP4 или MOV разбирается на видеопакеты, из заголовков достаются кодек, частота кадров, поворот и цветовое пространство. По ним строится план: сколько кадров получится и сколько это займёт.

  3. Кадры декодируются через WebCodecs

    VideoDecoder браузера превращает пакеты в кадры с аппаратным ускорением. Кадры живут в памяти вкладки и закрываются сразу после использования, чтобы не переполнить память.

  4. Между кадрами достраиваются новые

    Дублирование повторяет кадр, смешивание перекрёстно затухает соседние, режим по движению считает векторы движения на видеокарте через WebGPU. Именно эти новые кадры и делают движение плавным.

  5. Результат кодируется обратно в MP4

    VideoEncoder собирает H.264 с постоянной частотой кадров и ключевым кадром каждые две секунды, всё это упаковывается в MP4 и попадает к вам в загрузки. Нигде не остаётся копии.

Частые вопросы

Это бесплатно?

Да, полностью. Нет ни платных тарифов, ни лимита на количество файлов, ни водяных знаков. Вся нагрузка ложится на ваш компьютер, поэтому нам нечего продавать.

Это безопасно? Что происходит с моим видео?

Видео обрабатывается внутри вкладки и никуда не отправляется. Мы физически не можем его получить: на сервере лежат только HTML, CSS и JavaScript. Закройте вкладку, и в браузере не останется ничего.

Файл точно не загружается на сервер?

Точно. Проверить просто: откройте инструменты разработчика, вкладку «Сеть», и запустите обработку. Запросов с телом файла там не будет. После загрузки страницы можно вообще отключить интернет, обработка продолжит работать.

Работает ли это на iPhone и на MacBook?

Да. На MacBook в Safari, Chrome и Edge всё работает, включая HEVC с камеры. На iPhone работает Safari 17 и новее, но вкладку нужно держать на переднем плане: в фоне iOS останавливает расчёт. Длинные ролики на телефоне лучше сначала обрезать.

Какие браузеры поддерживаются?

Chrome, Edge и другие браузеры на Chromium от версии 94, Safari 17 и новее, Firefox с ограничениями. Нужен WebCodecs; если его нет, включается запасной путь на ffmpeg.wasm, он медленнее.

Почему в Firefox не открывается HEVC?

Firefox не декодирует HEVC через WebCodecs, это ограничение браузера, а не сайта. Видео с iPhone часто именно в HEVC. Откройте файл в Chrome, Edge или Safari либо перекодируйте его в H.264.

Почему вертикальное видео лежит на боку?

В MP4 поворот хранится отдельным флагом, и часть программ его игнорирует. Мы читаем этот флаг и разворачиваем картинку при кодировании, поэтому результат открывается правильно везде, включая старые плееры.

Какой максимальный размер файла?

Жёсткого предела нет, есть предел памяти вашего устройства. На настольном компьютере комфортны файлы до пары гигабайт, на телефоне лучше держаться в пределах нескольких сотен мегабайт. Если памяти не хватит, мы предупредим об этом до старта.

Почему результат такой длинный?

Замедление в 8 раз делает из 10 секунд 80 секунд, в 16 раз: больше двух минут. Кадров становится во столько же раз больше, поэтому растут и время расчёта, и размер файла. Если это лишнее, обрежьте исходник заранее.

А можно сделать то же самое без браузера?

Можно. На странице «Без браузера» собраны готовые команды ffmpeg для macOS, Windows и Linux: те же три режима, нормализация переменного FPS, HDR и поворот. Для пакетной обработки это удобнее.