Подробная инструкция: склейка нескольких WebM/видео файлов через FFmpeg

1. Когда это нужно

Типичные сценарии:

  • Запись встречи/лекции разбита на несколько файлов (например, при превышении лимита размера или при перезапуске записи).
  • Несколько коротких видео нужно собрать в одно.
  • Нужно склеить фрагменты записи экрана.

FFmpeg умеет это двумя принципиально разными способами:

  • Без перекодирования (-c copy) — быстро (секунды), но требует идентичных параметров всех файлов.
  • С перекодированием (фильтр concat) — медленнее, но работает всегда и даёт предсказуемый результат.

2. Подготовка

Проверить, что FFmpeg установлен

ffmpeg -version

Если команда не найдена:

  • Ubuntu/Debian: sudo apt install ffmpeg
  • Fedora: sudo dnf install ffmpeg
  • macOS (Homebrew): brew install ffmpeg
  • Windows: скачать с ffmpeg.org или winget install ffmpeg

Перейти в папку с файлами

cd ~/Загрузки          # Linux/macOS
cd %USERPROFILE%\Downloads   # Windows cmd
cd $env:USERPROFILE\Downloads # Windows PowerShell

Полезно: включить автодополнение имён файлов

Если в именах пробелы и кириллица (как в вашем случае) — набирать вручную неудобно. Используйте Tab для автодополнения или пишите имена в кавычках.


3. Шаг 1. Диагностика файлов

Это самый важный шаг. От него зависит, можно ли склеить быстро или нужно перекодировать.

3.1. Посмотреть ключевые параметры

ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate,sample_rate,channels -of default=noprint_wrappers=1 "файл1.webm"

Повторите для каждого файла. Сравните значения:

ПараметрЧто означаетВажно для -c copy
codec_name (видео)Кодек видеоДолжен совпадать
codec_name (аудио)Кодек аудиоДолжен совпадать
width / heightРазрешениеДолжно совпадать
r_frame_rateНоминальный fpsЖелательно совпадение
avg_frame_rateСредний fpsЕсли 0/0 — VFR, значит -c copy рискован
sample_rateЧастота дискретизации аудиоДолжна совпадать
channelsКол-во каналовДолжно совпадать

3.2. Как интерпретировать

  • Все параметры совпадают и avg_frame_rate не 0/0 → можно пробовать -c copy.
  • Параметры совпадают, но avg_frame_rate=0/0 (VFR) → -c copy может дать рассинхрон и битый звук. Лучше сразу перекодировать.
  • Параметры различаются → только перекодирование.

3.3. Быстрая проверка длительности

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1 "файл1.webm"

Пригодится потом для проверки суммы.


4. Шаг 2. Выбор способа склейки

Способ A — быстрое копирование (-c copy)

Плюсы: секунды, без потери качества.
Минусы: работает только при идентичных параметрах. При VFR часто ломается.

Способ B — перекодирование через фильтр concat

Плюсы: работает всегда, даёт стабильный результат.
Минусы: занимает время (зависит от кодека и CPU).

Правило: если сомневаетесь — берите способ B. Время дороже битого файла.


5. Шаг 3. Склейка

Способ A — без перекодирования

Создайте файл со списком (порядок строк = порядок в итоговом видео):

printf "file 'файл1.webm'\nfile 'файл2.webm'\nfile 'файл3.webm'\n" > list.txt

Затем:

ffmpeg -f concat -safe 0 -i list.txt -c copy "результат.webm"
  • -f concat — использовать concat-демультиплексор.
  • -safe 0 — разрешить абсолютные/нестандартные пути.
  • -c copy — не перекодировать, копировать потоки как есть.

Способ B — с перекодированием (универсальный)

B.1. В WebM (VP9 + Opus)

Медленно, но сохраняет формат WebM:

ffmpeg -i "файл1.webm" -i "файл2.webm" \
  -filter_complex "\
[0:v]fps=30,setpts=PTS-STARTPTS[v0];\
[1:v]fps=30,setpts=PTS-STARTPTS[v1];\
[0:a]aresample=48000,asetpts=PTS-STARTPTS[a0];\
[1:a]aresample=48000,asetpts=PTS-STARTPTS[a1];\
[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" \
  -c:v libvpx-vp9 -b:v 2M -cpu-used 5 -row-mt 1 -threads 0 \
  -c:a libopus -b:a 128k \
  "результат.webm"

B.2. В MP4 (H.264 + AAC) — рекомендуется для встреч/скринкастов

Быстро, совместимо со всем:

ffmpeg -i "файл1.webm" -i "файл2.webm" \
  -filter_complex "[0:v]fps=30,setpts=PTS-STARTPTS[v0];[1:v]fps=30,setpts=PTS-STARTPTS[v1];[0:a]asetpts=PTS-STARTPTS[a0];[1:a]asetpts=PTS-STARTPTS[a1];[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" \
  -c:v libx264 -preset veryfast -crf 23 \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  "результат.mp4"

B.3. Для трёх и более файлов

Добавьте входы и блоки в filter_complex:

ffmpeg -i "файл1.webm" -i "файл2.webm" -i "файл3.webm" \
  -filter_complex "\
[0:v]fps=30,setpts=PTS-STARTPTS[v0];[0:a]asetpts=PTS-STARTPTS[a0];\
[1:v]fps=30,setpts=PTS-STARTPTS[v1];[1:a]asetpts=PTS-STARTPTS[a1];\
[2:v]fps=30,setpts=PTS-STARTPTS[v2];[2:a]asetpts=PTS-STARTPTS[a2];\
[v0][a0][v1][a1][v2][a2]concat=n=3:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" \
  -c:v libx264 -preset veryfast -crf 23 \
  -c:a aac -b:a 128k -movflags +faststart \
  "результат.mp4"

Обратите внимание: concat=n=3 — число файлов, и количество пар [vN][aN] должно совпадать с числом входов.


6. Шаг 4. Проверка результата

4.1. Длительность

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1 "результат.mp4"

Должна быть ≈ сумме длительностей исходников (допустимая погрешность — доли секунды).

4.2. Параметры потоков

ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,sample_rate,channels -of default=noprint_wrappers=1 "результат.mp4"

4.3. Визуальная проверка стыка (обязательно)

Откройте результат в плеере и проматайте на момент стыка — примерно на длительность первого файла от начала. Проверьте:

  • звук не «убегает» от видео;
  • картинка не замирает;
  • нет резких скачков яркости/размера (если исходники писались в разное время).

4.4. Проверка в конце

Проматайте ближе к концу файла — убедитесь, что синхрон сохраняется на всём протяжении. Если к концу накапливается рассинхрон — значит, в одном из исходников были проблемы, и поможет только покадровая обработка (уже не «просто склейка»).


7. Готовые шаблоны команд

Универсальный шаблон (MP4, H.264)

ffmpeg -i "ЧАСТЬ1" -i "ЧАСТЬ2" \
  -filter_complex "[0:v]fps=30,setpts=PTS-STARTPTS[v0];[1:v]fps=30,setpts=PTS-STARTPTS[v1];[0:a]asetpts=PTS-STARTPTS[a0];[1:a]asetpts=PTS-STARTPTS[a1];[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" \
  -c:v libx264 -preset veryfast -crf 23 \
  -c:a aac -b:a 128k -movflags +faststart \
  "РЕЗУЛЬТАТ.mp4"

Универсальный шаблон (WebM, VP9)

ffmpeg -i "ЧАСТЬ1" -i "ЧАСТЬ2" \
  -filter_complex "[0:v]fps=30,setpts=PTS-STARTPTS[v0];[1:v]fps=30,setpts=PTS-STARTPTS[v1];[0:a]aresample=48000,asetpts=PTS-STARTPTS[a0];[1:a]aresample=48000,asetpts=PTS-STARTPTS[a1];[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]" \
  -map "[v]" -map "[a]" \
  -c:v libvpx-vp9 -b:v 2M -cpu-used 5 -row-mt 1 -threads 0 \
  -c:a libopus -b:a 128k \
  "РЕЗУЛЬТАТ.webm"

Быстрый шаблон (только для одинаковых файлов)

printf "file 'ЧАСТЬ1'\nfile 'ЧАСТЬ2'\n" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy "РЕЗУЛЬТАТ"

8. Частые ошибки и что они значат

Non-monotonic DTS; previous: X, current: Y

Таймстемпы второго файла конфликтуют с первым. Причина — разный fps или VFR. Решение: перекодирование с fps=30 и setpts=PTS-STARTPTS.

Error parsing Opus packet header

Повреждён аудиопоток на стыке — следствие той же проблемы. Решение: то же — перекодирование.

Unknown encoder 'libvpx-vp9'

Ваш FFmpeg собран без VP9. Решение: используйте libx264 (MP4) или переустановите FFmpeg с полным набором кодеков.

Unknown encoder 'libx264'

Аналогично — нет H.264. Решение: sudo apt install ffmpeg из официальных репозиториев обычно содержит libx264.

Invalid file index 2 in filtergraph description

В filter_complex указан вход, которого нет (например, [2:v], но задано только 2 файла с индексами 0 и 1). Решение: пересчитайте входы и concat=n=N.

Input link in1:v0 parameters (size X, SAR Y) do not match the corresponding output link

Разные разрешения или пропорции. Решение: добавьте scale в фильтр для каждого входа:

[0:v]fps=30,scale=1920:1080,setsar=1,setpts=PTS-STARTPTS[v0];

moov atom not found при воспроизведении результата

Файл записан некорректно (обычно из-за прерывания FFmpeg). Решение: запустите склейку заново, дождитесь завершения.


9. Шпаргалка по параметрам

Видео

ПараметрЗначениеКомментарий
-c:v libx264H.264 (MP4)Быстро, совместимо
-c:v libvpx-vp9VP9 (WebM)Медленно, но формат WebM
-preset veryfastСкорость x264ultrafast…veryslow
-crf 23Качество x264Меньше = лучше (18–28)
-b:v 2MБитрейт VP92 Мбит/с для 1080p встреч
-cpu-used 5Скорость VP90 (медл.)…8 (быстро)
fps=30Частота кадровДля скринкастов 30, для динамики 60
-movflags +faststartMP4Метаданные в начало файла

Аудио

ПараметрЗначениеКомментарий
-c:a aacAAC (MP4)Универсально
-c:a libopusOpus (WebM)Для WebM
-b:a 128kБитрейт128k достаточно для голоса
aresample=48000Частота48000 — стандарт для Opus

Фильтры

ФильтрЗачем
fps=30Приводит видео к постоянной частоте кадров
setpts=PTS-STARTPTSОбнуляет таймстемпы видео
asetpts=PTS-STARTPTSОбнуляет таймстемпы аудио
aresample=48000Приводит аудио к единой частоте
scale=1920:1080Приводит к единому разрешению
setsar=1Убирает разницу в aspect ratio
concat=n=N:v=1:a=1Собственно склейка N файлов

Памятка на будущее (короткая версия)

  1. Сравнить параметры файлов через ffprobe.
  2. Одинаковые и без VFR → -c copy (секунды).
  3. Разные или VFR → фильтр concat с fps=30 и setpts=PTS-STARTPTS.
  4. Формат не важен → MP4/H.264 (veryfast, crf 23) — быстрее всего.
  5. Обязателен WebM → VP9 + Opus, но долго.
  6. Всегда проверять стык на готовом файле.
  7. При ошибках Non-monotonic DTS / Opus packet header — сразу переходить к перекодированию.

Сохраните этот файл — в следующий раз сэкономите время на диагностику.

Добавить комментарий