Пайплайн транскодинга вглубь: decode → filter → encode → mux

Автор: Николай СапуновОбновлено: август 202623 мин чтения
Содержание статьи +

TL;DR

Пайплайн транскодинга – это конвейер из четырёх стадий, преобразующий видеофайл из одного формата в другой: demux, decode, filter, encode и mux. Каждая стадия выполняет свою задачу, передаёт результат следующей и в современных инструментах вроде FFmpeg 7 работает в отдельном потоке. Большинство багов в продакшене – рассинхронизация звука и изображения, зависшие превью, резкий рост нагрузки на CPU – вызваны путаницей в том, за что отвечает каждая стадия. В этой статье мы пройдём пайплайн от начала до конца: разберём математику, типичные сценарии сбоев и плейбук оператора на 2026 год.

Зачем это нужно

За каждым видеосервисом, которым вы когда-либо пользовались – Netflix, YouTube, Zoom, дашборды камер видеонаблюдения, порталы e-learning – стоит пайплайн транскодинга. Когда вы загружаете 4K-файл с телефона, а он без сбоев воспроизводится у коллеги на ноутбуке с медленным интернетом – четыре стадии работают последовательно. А если звук отстаёт от изображения на секунду или счёт в облаке удваивается после обычного деплоя – значит, где-то на одной из стадий произошла ошибка. Эта статья предназначена для продакт-менеджера, основателя или операционного руководителя, которому важно понимать пайплайн достаточно хорошо, чтобы общаться с инженерами, оценивать проект и разбираться в отчётах об инцидентах – не становясь при этом видеоинженером.

Пайплайн за один проход

Каждый транскодер – от Raspberry Pi с FFmpeg до AWS Elemental MediaConvert на сотнях ядер – следует одному и тому же пятишаговому рецепту.

Пять шагов по порядку: demux, decode, filter, encode и mux. Слово «транскодинг» – краткое обозначение всех пяти, выполняемых подряд, но большинство команд описывают пайплайн как четыре стадии, поскольку на практике demux практически неотделим от decode. Мы используем обе формулировки: в заголовке пайплайн представлен как decode → filter → encode → mux, а demux при этом разбираем отдельно – как «парадную дверь».

Полезная аналогия – кухня ресторана. Файл-контейнер (MP4, MKV) – это запечатанная коробка с продуктами; этап demux её распаковывает. Decoder – повар, который превращает упакованные ингредиенты обратно в сырьё. Filters – сама готовка: нарезка, обжарка, сервировка. Encoder – повар на линии, упаковывающий каждое блюдо в контейнер для доставки. Muxer – курьер, собирающий всё в один пакет для клиента. Если любой из них ошибётся во времени – приготовитель задержится, линейный повар забудет блюдо, курьер разольёт суп в сумке – заказ придёт испорченным.

Рисунок 1. Пайплайн транскодинга от начала до конца. Сжатые пакеты поступают в декодер; необработанные кадры проходят через фильтры и попадают в энкодер; сжатые пакеты направляются в мультиплексор. Тип данных изменяется ровно два раза.

Стадия 0 – Demux: открыть коробку

Видеофайл, который вы скачиваете, – это не просто поток пикселей. Это контейнер: MP4, MKV, MOV, WebM, MPEG-TS, – в котором хранятся один или несколько сжатых потоков (видео, аудио, субтитры), синхронизированных с помощью тайм-кодов и метаданных. Demuxer анализирует этот контейнер и извлекает каждый поток отдельно – пакет за пакетом.

Packet – фундаментальная единица сжатого медиа. В модели данных FFmpeg это AVPacket. Обычно один packet содержит один сжатый видеокадр или короткий фрагмент сжатого аудио, а также две метки времени: decode timestamp (DTS), которая указывает декодеру, когда обрабатывать пакет, и presentation timestamp (PTS), которая сообщает плееру, когда отображать или воспроизводить результат. Для простого аудио эти метки совпадают, но в случае видео они всегда различаются, если кодек использует B-кадры – кадры, для декодирования которых требуются будущие кадры, то есть порядок воспроизведения и порядок декодирования не совпадают.

Если вы когда-нибудь задумывались, почему плеер «заикается» в начале воспроизведения – вот одна из причин. Декодеру нужно несколько пакетов в буфере в порядке DTS, прежде чем он сможет выдать первый кадр в порядке PTS. Стадия демультиплексирования – это первое место, где тайм-коды должны быть абсолютно точными. Потеря даже миллисекунды на этом этапе – и позже звук начнёт отставать от изображения.

Demuxing – самый дешёвый этап в пайплайне. По сути, это задача парсинга: никакой математики над пикселями, никакой работы с кодеками, просто чтение структуры контейнера. На современном CPU демультиплексор обрабатывает тысячи пакетов в секунду на поток.

Стадия 1 – Decode: пакеты превращаются в пиксели

Декодер получает сжатые пакеты от демуксера и преобразует их обратно в несжатые кадры. Здесь впервые меняется тип данных: на входе – AVPacket (сжатые байты), на выходе – AVFrame (сырые пиксели для видео, сырые сэмплы для аудио).

На декодирование уходит большая часть CPU, если фильтрация не применяется. Поток H.264 в разрешении 1080p, декодируемый программно со скоростью 60 кадров в секунду на современном CPU ноутбука, потребляет около 10–15% одного ядра. Та же задача на аппаратном декодере GPU (NVIDIA NVDEC, Intel Quick Sync, Apple VideoToolbox, AMD VCN) практически не нагружает CPU – фиксированный блок GPU выполняет её в выделенном кремнии, потребляя около 5–15 ватт.

Здесь возникает одно критическое архитектурное решение: где хранятся декодированные кадры? Программный декодер выдаёт кадры в системную память (RAM), а аппаратный – в память GPU (VRAM). Если следующая стадия обработки – фильтрация или кодирование – также выполняется на GPU, логично держать кадр в VRAM. Перемещение 4K-кадра между VRAM и RAM и обратно обходится дороже, чем сам процесс декодирования. FFmpeg решает эту задачу с помощью параметров -hwaccel cuda -hwaccel_output_format cuda, которые обеспечивают хранение кадров в CUDA-памяти на всём протяжении пайплайна. Это одна из самых распространённых десятистрочных оптимизаций в аппаратно-ускоренных транскодерах: многие команды в продакшене теряют ускорение в 3–5 раз, оставляя формат вывода по умолчанию nv12 вместо cuda.

Третье архитектурное решение на этапе декодирования – stream copy. Если задача сводится лишь к смене контейнера – например, конвертации MP4 в MPEG-TS для аппаратного декодера, поддерживающего только формат TS, – вообще не нужно выполнять декодирование. Достаточно выполнить демультиплексирование, а затем мультиплексирование в новый контейнер, полностью пропустив этапы декодирования, фильтрации и кодирования. Такой подход называется remuxing или stream copy и в FFmpeg реализуется с помощью флага -c copy. Он практически не требует ресурсов, поскольку пиксели не обрабатываются. Remuxing подходит примерно в 30% реальных пайплайнов и почти всегда упускается на первой итерации разработки.

Стадия 2 – Filter: этап, который действительно меняет изображение

Filter – это место, где пайплайн делает то, что виден пользователю. Уменьшить 4K-мастер до 720p-рендиции, сделать deinterlace для бродкаст-захвата, вшить субтитры, наложить лого, убрать шум на тёмном клипе, сконвертировать pixel format из yuv422p10le в yuv420p, чтобы потребительский энкодер его принял, – всё это фильтры.

FFmpeg моделирует это как filtergraph – направленный ациклический граф из узлов-фильтров, соединённых рёбрами. Каждый узел принимает один или несколько кадров, выполняет над ними преобразование и передаёт результат дальше. Простая цепочка – yadif,scale=1280:720,format=yuv420p – означает: «деинтерлейс, затем масштабирование до 720p, затем конвертация в цветовое пространство 4:2:0». Сложный filtergraph (-filter_complex) может разбить один входной кадр на три ветви, отмасштабировать каждую в свой размер и подать одновременно в три разных энкодера. Этот паттерн составляет техническую основу ABR-лестниц (adaptive bitrate).

Классическая цепочка фильтров для VOD ABR-лестницы строится сверху вниз: сначала – деинтерлейсинг, если исходный материал чересстрочный, затем – нормализация цветовых первичных величин и функции передачи для конвертации HDR в SDR или наоборот, масштабирование до целевого разрешения, установка формата субдискретизации хроминанса, соответствующего ожиданиям энкодера, и наложение брендового оверлея. Аудиофильтры обрабатываются параллельно: изменение частоты дискретизации до целевой, преобразование в нужный каналовый формат, нормализация громкости по стандарту вещания, например ITU-R BS.1770 или EBU R128.

Filters – второй по затратам CPU потребитель в пайплайне. Чистый scale на CPU стоит примерно столько же, сколько decode для того же разрешения; конверсия chroma subsampling – бесплатна; серьёзный денойзер (nlmeans, bm3d) может стоить в 10 раз больше, чем decode. Hardware-ускоренные фильтры есть у каждого GPU-вендора – scale_ npp и scale_ cuda у NVIDIA, scale_qsv у Intel, scale_vt у Apple – и их стоит использовать, когда соседние стадии тоже выполняются на том же GPU.

Рисунок 2. Один 4K-вход преобразуется в трёхступенчатый ABR-ladder с помощью единого filtergraph и трёх параллельных энкодеров. Разделение происходит после декодирования – один раз на каждый входной кадр, после чего каждая версия кодируется независимо.

Стадия 3 – Encode: пиксели снова превращаются в пакеты

Encoder – это инверсия декодера. На вход подаётся необработанный AVFrame, на выходе получаются сжатые AVPacket, каждый с метками PTS и DTS, готовые к мультиплексированию в контейнер. Encoder – самая ресурсоёмкая стадия любого пайплайна. Правило большого пальца: кодирование одного и того же контента требует в 5–50 раз больше CPU-ресурсов, чем декодирование – в зависимости от кодека и пресета. 60-секундный клип в разрешении 1080p декодируется за 2–3 секунды, а кодируется – за 30 секунд через x264 на пресете slow или за 90–180 секунд через SVT-AV1 на пресете 5 (имплементации энкодеров подробно сравнивают семь основных энкодеров).

Два решения на этапе кодирования определяют большую часть финансовых затрат. Первое – выбор кодека и энкодера. H.264 с x264 – для совместимости, H.265 с x265 – для HDR-контента, AV1 с SVT-AV1 – для новых пайплайнов, где база декодеров поддерживает AV1. Второе – режим управления битрейтом (rate control mode): CRF – для стабильного качества, CBR – для фиксированного битрейта, VBR – для ограниченного переменного, capped-CRF – для современной оптимальной точки ABR. Подробно мы разбираем это в rate control: CBR, VBR, CRF; с точки зрения работы пайплайна важно, что именно энкодер – единственная стадия, которая взаимодействует с бюджетом битрейта. Демультиплексирование, декодирование и фильтрация к этому бюджету «слепы».

Hardware-энкодеры заслуживают отдельного внимания. NVIDIA NVENC, Intel Quick Sync, Apple VideoToolbox, AMD AMF и ASIC-ускорители вроде NETINT кодируют в 10–50 раз быстрее программного обеспечения, но при этом качество при одинаковом битрейте немного снижается. Обновление NVIDIA в январе 2026 года расширило режим ultra-high-quality (UHQ) до AV1 на архитектуре Blackwell, сократив разрыв в качестве с программным кодированием AV1 до нескольких процентов по VMAF при трёхкратной производительности. Для сервисов, транслирующих миллионы потоков, hardware-энкодеры больше не компромисс – они стали стандартом. Подробная матрица вендоров – в статье Hardware-ускорение: NVENC, VPU, ASIC.

Стадия 4 – Mux: собрать посылку

Muxer – финальная стадия. Он берёт сжатые пакеты от одного или нескольких энкодеров, перемежает их в порядке доставки, записывает заголовки контейнера (атом moov в MP4, индекс сегмента в HLS) и формирует выходной файл или поток. Как и демультиплексор, мультиплексор мало нагружает CPU, но у него есть две задачи, которые легко выполнить неправильно.

Первая – выравнивание тайм-кодов. Каждый выходной пакет от каждого энкодера должен находиться на согласованной временной шкале. Формат MP4 требует, чтобы PTS строго возрастали, без возвратов назад. Некоторые плееры на стороне приёмника отклоняют любой поток с отрицательным DTS. Мультиплексор применяет глобальный сдвиг, чтобы привести тайм-коды каждого потока в положительный диапазон, а затем перемежает пакеты по порядку DTS, чтобы стриминговый плеер мог декодировать их в порядке поступления. Ошибётесь на 100 миллисекунд – получите слышимую рассинхронизацию. Ошибётесь на 5 секунд – плеер откажется воспроизводить.

Вторая задача – packaging, то есть фрагментация видеопотока на сегменты для доставки по HTTP. В обычном VOD MP4 содержится один атом moov и один непрерывный блок mdat. В streaming-готовом фрагментированном MP4 (fMP4 или CMAF) также присутствует один moov, но вместо одного блока mdat – множество пар moof + mdat, каждая длиной 2–6 секунд. При этом используется тот же выход энкодера и тот же энкодер – разница лишь в том, как muxer выполняет фрагментацию. CMAF станет стандартом по умолчанию в 2026 году, поскольку одни и те же fMP4-сегменты можно использовать как для HLS, так и для DASH, не создавая отдельные файлы для каждого протокола (см. Контейнеры: MP4, fMP4, MKV, WebM, MOV, MPEG-TS).

Типы данных, передаваемых между стадиями

Весь пайплайн становится проще, как только вы запомните: между стадиями передаются ровно два типа данных. Packets (AVPacket в FFmpeg) – это сжатые байты с тайм-кодами. Frames (AVFrame) – это несжатые пиксели (в виде сеток для видео) или аудиосэмплы (для аудио).

Каждая стадия преобразует один тип в другой:

  • Demux: байты на входе (файл-контейнер) → пакеты на выходе.
  • Decode: пакеты на входе → кадры на выходе.
  • Filter: кадры на входе → кадры на выходе (тот же тип, другое содержимое).
  • Encode: кадры на входе → пакеты на выходе.
  • Mux: пакеты на входе → байты на выходе (файл-контейнер).

Когда вы описываете баг транскодинга инженеру, указание типа данных на стадии, где проявляется ошибка, сокращает время диагностики вдвое. «PTS портится на пакетах, выходящих из энкодера» – это точный отчёт о баге. «Видео сломано» – нет.

Threading и планировщик FFmpeg 7

Большую часть двадцатилетней истории FFmpeg пайплайн транскодирования работал как единый поток, последовательно передавая каждый пакет через все стадии. У декодеров и энкодеров был свой внутренний многопоточность, но внешний цикл оставался последовательным, и медленный энкодер блокировал всю дальнейшую обработку.

FFmpeg 7.0, вышедший в апреле 2024 года, внедрил полноценный многопоточный планировщик – его лидер-разработчик Антон Хирнов назвал это «одним из самых сложных рефакторингов FFmpeg CLI за десятилетия». Теперь каждый demuxer, decoder, filtergraph, encoder и muxer работает в отдельном потоке, а центральный планировщик передаёт пакеты и кадры между ними через ограниченные очереди. Это изменение открыло путь к параллелизму, ранее невозможному: одна команда может одновременно декодировать входной поток, разделять его на три ветки фильтрации, кодировать каждую версию на отдельных ядрах процессора и мультиплексировать результат, при этом ни одна стадия не блокирует остальные.

Практический эффект – более линейное масштабирование на многоядерных машинах. На 32-ядерном Threadripper трёхступенчатый ABR-транскод, который занимал 90 секунд в FFmpeg 6, теперь завершается за 35–40 секунд в FFmpeg 7 при той же команде. Процессор тот же, просто пайплайн больше не является однопоточным между этапами. Если у вас self-hosted транскодер-ферма и вы ещё не обновились с FFmpeg 6, апгрейд – самое выгодное изменение, которое можно сделать в 2026 году.

Рисунок 3. Шедулер FFmpeg 7 выполняет каждую стадию пайплайна в отдельном потоке, соединённом ограниченными очередями. На многоядерных машинах это обычно даёт ускорение по времени выполнения (wall-clock time) в 2–3 раза при кодировании ABR-лестниц.

Разобранный пример: математика 3-ступенчатого ABR-транскода

Возьмём конкретный пример: один 60-секундный 4K HDR mezzanine-файл, который транскодируется в трёхуровневую ABR-лестницу 1080p / 720p / 480p в формате H.264 SDR для доставки по протоколу HLS. На одной 16-ядерной машине с FFmpeg 7 и программными x264-энкодерами распределение CPU-ресурсов по этапам выглядит следующим образом.

СтадияCPU за секундуВсего на 60-секундный клип
Demux 4K HDR HEVC~0.5% одного ядра0.3 core-секунды
Decode 4K HDR HEVC (софт)~25% одного ядра15 core-секунд
Filter: tonemap + scale ×3~30% одного ядра (общий)18 core-секунд
Encode 1080p H.264 (x264 slow)~150% одного ядра90 core-секунд
Encode 720p H.264 (x264 slow)~70% одного ядра42 core-секунды
Encode 480p H.264 (x264 slow)~30% одного ядра18 core-секунд
Mux 3 выходов в fMP4 / HLS~0.5% одного ядра0.3 core-секунды
Итого~184 core-секунды

Читаем таблицу: кодирование занимает около 82% CPU. Декодирование и фильтрация – около 18%. Демультиплексирование и мультиплексирование – погрешность округления. Любой счёт за транскодинг следует этой пропорции. Если снизить стоимость энкодера вдвое – перейдя на hardware-энкодер, выбрав более низкий пресет или сократив количество битрейтов в ladder – вы снизите общую стоимость вдвое. Оптимизация demux – пустая трата времени.

Теперь добавим железо. Та же нагрузка на той же 16-ядерной машине: NVDEC для декодирования, scale_npp для масштабирования и NVENC для всех трёх энкодеров.

СтадияCPU за секундуЗаметки
Demux~0.5% одного ядрабез изменений
Decode (NVDEC)~0% CPU, GPU ~10WGPU fixed-function
Filter (scale_npp ×3)~0% CPU, GPU ~5Wтот же GPU
Encode (NVENC ×3)~0% CPU, GPU ~30Wfixed-function
Mux~0.5% одного ядрабез изменений
Итого~1 core-сек + 45W GPUwall clock: 10–15 сек

Время выполнения падает с ~12 секунд (16 ядер загружены) до ~10–15 секунд, но теперь 16 ядер CPU свободны для транскодирования следующего клипа. Одна GPU-машина может обрабатывать ~20–40 таких клипов в минуту, тогда как чисто CPU-решение справляется с 5–8. В этом и заключается экономический смысл hardware-транскодирующих пайплайнов в 2026 году.

Типичные ошибки в пайплайне

«Ошибка: перекодируете там, где нужен был remux. Частая просьба – «переведи этот MKV в MP4» – на самом деле означает remux, а не транскод. Запуск команды ffmpeg -i in.mkv out.mp4 без флага -c copy приводит к декодированию и перекодированию всех потоков, что занимает в 10 раз больше времени и влечёт потерю качества. Правильный вариант – ffmpeg -i in.mkv -c copy out.mp4. Всегда уточняйте, что именно требуется: «сменить контейнер» (remux) или «сменить кодек, разрешение или битрейт» (transcode).»
«Ошибка: вы напрасно гоняете кадры между RAM и VRAM. Пайплайн, который декодирует на GPU, фильтрует на CPU и кодирует на GPU, тратит большую часть времени на копирование кадров через PCIe. Либо держите всё в VRAM с помощью -hwaccel cuda -hwaccel_output_format cuda и используйте GPU-фильтры, либо оставьте всё в RAM. Смешивать два подхода почти всегда медленнее, чем любой из чистых.»
«Ошибка: забываете про аудио. Типичный видеопоток включает один видео- и 1–6 аудиопотоков, а также опциональные субтитры. Если забыть указать -map 0:a, звук пропадёт без предупреждения, и результат будет проигрываться в тишине. Всегда проверяйте, что все ожидаемые потоки попадают в мультиплексор.»
«Ошибка: доверяете дефолтным тайм-кодам при live-ingest. RTMP и SRT live-ingests иногда выдают отрицательный DTS или «обёрнутые» временные метки после длительного времени работы. Если мультиплексор их отклоняет, поток останавливается. Безопасный вариант по умолчанию – -fflags +genpts -avoid_negative_ts make_zero, чтобы мультиплексор создал чистую монотонную шкалу времени.»

Где здесь Фора Софт

Фора Софт строит и эксплуатирует транскодинг-пайплайны с 2005 года в таких областях, как конференции, видеостриминг, OTT и IPTV, видеонаблюдение, e-learning, телемедицина и AR/VR. Основной вывод по 250+ проектам: пайплайн – это не источник багов для большинства команд, а место, где они становятся заметными. Камера с неправильными цветовыми первичными параметрами заставляет энкодер выдавать искажённую цветопередачу; энкодер с неверным режимом управления битрейтом заставляет мультиплексор формировать поток, который CDN отказывается кэшировать; CDN с некорректным ключом кэширования показывает зрителям чужие DRM-токены. Эффективная эксплуатация пайплайна означает глубокое знание каждой его стадии – настолько, чтобы по flame graph’у понимать, какая из них выдаёт ложную информацию.

Ключевые выводы

  • Пайплайн состоит из пяти стадий: demux, decode, filter, encode, mux – каждая выполняет одну конкретную задачу.
  • Между стадиями передаются только два типа данных: сжатые пакеты и несжатые кадры.
  • На кодирование уходит 80% и более CPU; demux и mux – это погрешность округления.
  • Шедулер FFmpeg 7 даёт бесплатное ускорение в 2–3 раза на многоядерных машинах.
  • Remuxing – правильный выбор примерно в 30% случаев, но его почти всегда пропускают на первой итерации.
  • Храните кадры на одном устройстве (RAM или VRAM) на всём протяжении пайплайна; копирование через PCIe убивает производительность.

Что читать дальше

Строите такую систему?

Подберём параметры кодирования под ваш контент и посчитаем стоимость доставки до старта разработки.