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

Оглавление

  1. Когда это нужно
  2. Подготовка
  3. Шаг 1. Диагностика файлов
  4. Шаг 2. Выбор способа склейки
  5. Шаг 3. Склейка
  6. Шаг 4. Проверка результата
  7. Готовые шаблоны команд
  8. Частые ошибки и что они значат
  9. Шпаргалка по параметрам

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 — сразу переходить к перекодированию.

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

Извлечение аудио из видео с помощью ffmpeg

Базовая команда

bash

ffmpeg -i video.mp4 -vn -c:a copy audio.m4a
  • -i video.mp4 — входной файл
  • -vn — не включать видео (no video)
  • -c:a copy — копировать аудиопоток без перекодирования (быстро, без потери качества)
  • audio.m4a — выходной файл

Формат контейнера нужно подбирать под кодек аудио из видео, иначе copy не сработает.

Как узнать, какой аудиокодек в видео

bash

ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of default=nw=1 video.mp4

Или просто посмотреть всё:

bash

ffprobe video.mp4

Найдите строку Stream #0:1: Audio: ... — например aac, mp3, ac3, opus, vorbis.

Подбор выходного формата под кодек

Аудиокодек в видеоКоманда
AACffmpeg -i video.mp4 -vn -c:a copy audio.m4a
MP3ffmpeg -i video.mp4 -vn -c:a copy audio.mp3
AC3 / EAC3ffmpeg -i video.mkv -vn -c:a copy audio.ac3
Opusffmpeg -i video.webm -vn -c:a copy audio.opus
Vorbisffmpeg -i video.ogv -vn -c:a copy audio.ogg
FLACffmpeg -i video.mkv -vn -c:a copy audio.flac
PCMffmpeg -i video.mov -vn -c:a copy audio.wav

Если нужно перекодировать (например, в MP3)

bash

ffmpeg -i video.mp4 -vn -c:a libmp3lame -q:a 2 audio.mp3
  • -q:a 2 — переменный битрейт, качество ~190 kbps (0 — лучшее, 9 — худшее).

Или с постоянным битрейтом:

bash

ffmpeg -i video.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3

В AAC:

bash

ffmpeg -i video.mp4 -vn -c:a aac -b:a 192k audio.m4a

Полезные дополнения

  • Выбрать конкретную аудиодорожку (если их несколько):bashffmpeg -i video.mkv -vn -map 0:a:1 -c:a copy audio.m4aгде 1 — индекс аудиопотока (0 — первая дорожка, 1 — вторая и т.д.).
  • Обрезать фрагмент (с 30-й секунды, длительностью 60 сек):bashffmpeg -ss 00:00:30 -i video.mp4 -t 60 -vn -c:a copy audio.m4a
  • Пакетно для всех mp4 в папке:bashfor f in *.mp4; do ffmpeg -i «$f» -vn -c:a copy «${f%.mp4}.m4a»; done
  • Перезаписать файл молча (-y) или не перезаписывать (-n):bashffmpeg -y -i video.mp4 -vn -c:a copy audio.m4a

Проверка результата

bash

ffprobe audio.m4a

Если при -c:a copy ffmpeg ругается на несовместимость контейнера — просто смените расширение выхода на подходящее из таблицы выше или перекодируйте (-c:a aac/libmp3lame).

Meld

Meld — это удобный графический инструмент для сравнения файлов и папок, который наглядно показывает все различия. В отличие от консольных утилит, здесь можно не только увидеть отличия, но и сразу их объединять.

Установить Meld в Ubuntu очень просто одной командой:

sudo apt install meld

🚀 Запуск и начало работы

После установки вы можете запустить Meld из меню приложений или из терминала.

Способы запуска из командной строки:

КомандаЧто делает
meldЗапускает программу с пустым окном
meld папка1 папка2Сравнивает две папки
meld файл1 файл2Сравнивает два файла
meld файл1 файл2 файл3Трёхстороннее сравнение файлов
meld папка1 папка2 папка3Трёхстороннее сравнение папок
meld .Сравнивает текущую папку с её версией в Git

При запуске без параметров откроется окно выбора режима: File сравнение (для файлов), Directory сравнение (для папок) или Version Control (для Git).

📁 Сравнение папок

Для сравнения двух папок укажите пути к ним и нажмите Compare.
Обе папки отобразятся в виде деревьев. Цветовая индикация подскажет, в каком состоянии находится каждый файл:

СтатусВнешний видЗначение
ИзменёнСиний и жирныйФайл есть в обеих папках, но отличается
НовыйЗелёный и жирныйФайл есть только в этой папке
ОтсутствуетСерый зачёркнутый текстФайла нет в этой папке
ОшибкаЯрко-красный на жёлтом фонеПроблема с доступом к файлу

Чтобы открыть два файла для детального сравнения, просто дважды кликните по любому файлу в списке — откроется новая вкладка с пофайловым сравнением.

Управление отображением:
На панели инструментов есть кнопки-фильтры:

  • Same — показать одинаковые файлы (по умолчанию включено, нажмите, чтобы скрыть)
  • New — показать новые файлы
  • Modified — показать изменённые файлы

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

📄 Сравнение файлов

Когда вы открываете два файла рядом, Meld подсвечивает различия:

  • Зелёным — добавленные строки
  • Красным — изменённые строки
  • Серым зачёркнутым — удалённые строки

Для объединения изменений используйте кнопки со стрелками между панелями:

  • Стрелка вправо → — скопировать выделенный фрагмент из левого файла в правый
  • Стрелка влево ← — скопировать из правого в левый

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

🎯 Настройка фильтров

Иногда нужно исключить из сравнения определённые типы файлов (например, .log, __pycache__ или .git).

Как добавить свой фильтр:

  1. Откройте меню Edit → Preferences → File Filters
  2. Нажмите + (плюс) для добавления нового правила
  3. Введите шаблон имени файла, используя специальные символы (*, ?)

Символы для шаблонов:

СимволЧто означаетПример
*Любое количество символов*.log — все файлы с расширением .log
?Один любой символfile?.txt — file1.txt, fileA.txt
[abc]Любой из перечисленных символов[0-9] — любая цифра
{cat,dog}Один из вариантов*.{jpg,png} — изображения

💡 Примечание о регистре имён

Meld по умолчанию различает заглавные и строчные буквы в именах файлов. Это значит, что README.md, Readme.md и readme.md будут считаться разными файлами.

Если вы хотите этого избежать (например, при сравнении папок с Windows), включите опцию View → Ignore Filename Case в меню.

Команда meld . из корня Git-проекта — особенно полезный трюк, который показывает все незакоммиченные изменения в одном окне. Нужна будет помощь с настройкой такого сравнения?

Сборка plv8 для PostgreSQL

sudo apt-get install -y \
    build-essential \
    postgresql-server-dev-16 \
    cmake \
    ninja-build \
    python3 \
    pkg-config \
    libtinfo5 \
    libc++-dev \
    libc++abi-dev \
    libv8-dev \
    curl \
    git \
    wget
# Скачайте и соберите plv8
git clone https://github.com/plv8/plv8.git
cd plv8
make
sudo make install

Если будет ошибка при запуске: https://github.com/plv8/plv8/issues/571

ERROR:  загрузить библиотеку "/usr/lib/postgresql/16/lib/plv8-3.2.4.so" не удалось: /usr/lib/postgresql/16/lib/plv8-3.2.4.so: undefined symbol: _ZTIN2v84base6ThreadE 

ОШИБКА: загрузить библиотеку "/usr/lib/postgresql/16/lib/plv8-3.2.4.so" не удалось: /usr/lib/postgresql/16/lib/plv8-3.2.4.so: undefined symbol: _ZTIN2v84base6ThreadE
SQL state: 58P01

Просмотр статистики активных запросов в PostresSQL

-- Показывает размеры таблиц, участвующих в запросах
WITH table_sizes AS (
    SELECT 
        schemaname,
        tablename,
        pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
    FROM pg_tables 
    WHERE schemaname NOT LIKE 'pg_%' AND schemaname != 'information_schema'
)
SELECT 
    a.pid,
    a.usename,
    a.datname,
    a.query,
    a.state,
    -- Можно добавить JOIN с table_sizes если знаете какие таблики используются
    now() - a.query_start as duration
FROM pg_stat_activity a
WHERE a.state = 'active' 
  AND a.query NOT LIKE '%pg_stat_activity%';

Восстановление grub загрузчика по умолчанию

На ПК были одновременно установлены Ubuntu 22.04 и Windows 10. После обновления Windows 10 до 11 загрузчик сломался и выдавал ошибку.

Примечание: скриншота нет под рукой, но ошибка говорила, что загрузка не возможна требуется восстановление (и это всё на синем экране windows).

Мне помогло следующее решение.

Читать далее «Восстановление grub загрузчика по умолчанию»

Практическое применение elasticsearch+fluent-bit

Данная связка будет применяться для хранения логов от приложений. Ниже пример compose файла:

version: "3.5"
services:
  elasticsearch:
    image: elasticsearch:8.19.3
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.type=single-node
      - xpack.security.enabled=false  # Disable security for development
    volumes:
      - ./elasticsearch/data:/usr/share/elasticsearch/data
      - ./elasticsearch/logs:/usr/share/elasticsearch/logs
  fluentbit:
    build: .
    ports:
      - "8888:8888"
      - "24224:24224"
      - "24224:24224/udp"
    depends_on:
      - elasticsearch
Читать далее «Практическое применение elasticsearch+fluent-bit»