Распределение битов внутри GOP

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

TL;DR

Группа кадров (GOP) – это единица, которую современный энкодер обрабатывает целиком: один I-кадр и связанная с ним цепочка P- и B-кадров. Распределение битов – это процесс, в ходе которого определяется, как разделить общий битовый бюджет GOP между кадрами, чтобы обеспечить максимально стабильное качество изображения при минимальных затратах. Тип кадра, его позиция в иерархической B-пирамиде и частота использования в качестве опорного – все эти факторы влияют на то, сколько битов получит каждый кадр: I-кадры получают наибольший приоритет, опорные B-кадры – меньший, а «одноразовые» листовые B-кадры – минимальное количество. При правильном распределении на типичном VOD-каталоге можно сэкономить 20–40% битрейта при неизменной визуальной чёткости; при неправильной настройке картинка начинает пульсировать каждые две секунды – в момент появления нового I-кадра.

Почему это важно

Распределение битов внутри GOP – это слой, который превращает целевое значение rate-control «5 Мбит/с в среднем» в точный квантователь для каждого кадра. Продакт-менеджер, понимающий этот механизм, перестанет требовать от инженеров «просто дать I-кадрам больше битов» – современный битрайт-распределитель уже делает это автоматически, а попытки ручной настройки только ухудшают результат. Стриминг-инженер, умеющий читать логи x264 и оценивать, корректно ли сработал MB-tree, способен выявлять регрессии качества ещё до попадания в продакшн. Основатель, выбирающий сервис транскодирования, будет знать, какие вопросы задавать: глубина иерархической B-пирамиды, размер lookahead, включены ли MB-tree или CU-tree, соблюдает ли энкодер границы VBV/HRD по строкам или только по кадрам. Эта статья подробно разбирает реальный механизм распределения – соотношение типов кадров, QP-офсеты по временным уровням, масштабирование по сложности и алгоритмы, учитывающие распространение информации (MB-tree, CU-tree, TPL), – и показывает настройки, которые реально обсуждаются в продакшене.

Что такое распределение битов и где оно находится

Внутри энкодера друг над другом работают три цикла. Внешний цикл – rate control: он определяет цель (битрейт, среднее значение или фактор качества) и решает, сколько битов выделено на весь файл или на следующую секунду. Внутренний цикл – принятие решений о режиме (mode decision): для одного блока он выбирает наиболее эффективный способ кодирования при заданном соотношении «качество против битов» (это соотношение называется лямбда, λ). Между ними находится распределение битов. Распределитель берёт бюджет, установленный внешним циклом, распределяет его по кадрам в ближайшей группе кадров, а затем преобразует долю каждого кадра в параметр квантования (QP), который затем использует mode decision.

Распределение – отдельная задача, потому что кадры внутри GOP не одинаковы. I-кадр кодируется с нуля и служит опорой, от которой зависит всё остальное. P-кадр для большей части данных использует предыдущий якорь. B-кадр смотрит и назад, и вперёд, а в современных кодеках сами B-кадры образуют пирамиду, где одни B-кадры становятся опорой для других. Если задать всем кадрам одинаковый QP, опорные кадры недополучат битов, а каждый последующий кадр, копирующий с них данные, унаследует артефакты сжатия. Распределитель – это цикл, который решает эту проблему.

Полезная аналогия. Бюджет автопутешествия не делится поровну между днями. На ночь в отеле в пункте назначения вы тратите больше, чем на чашку кофе на заправке, потому что именно этот пункт – цель всей поездки. Распределение битов работает так же: больше ресурсов выделяется на кадры, от которых зависит остальная часть GOP.

Рис. 1. Три вложенных цикла внутри современного энкодера. Распределение битов находится между rate control и mode decision и преобразует цель по битрейту в QP для каждого кадра.

Первое разделение: по типу кадра

Самый старый компонент распределения битов, присутствующий в каждом видеокодеке со времён MPEG-1, – это соотношение по типам кадров. Каждый тип получает множитель квантователя относительно P-кадра, основной рабочей единицы кодека. В x264 эти параметры называются ipratio (шаг квантователя I → P) и pbratio (шаг P → B). Значения по умолчанию – 1.40 и 1.30. Это означает: если энкодер выбрал QP 22 для P-кадра, соответствующий I-кадр кодируется при QP 22 − 6 × log₂(1.40) ≈ 22 − 2.9 ≈ 19, а соответствующий B-кадр – при QP 22 + 6 × log₂(1.30) ≈ 22 + 2.3 ≈ 24. Множитель 6 в формуле взят из правила семейства H.26x: «+6 QP» уменьшает битрейт вдвое.

Физическая причина таких значений по умолчанию такова: биты I-кадра используются всеми кадрами GOP в течение следующих примерно двух секунд, поэтому один бит, потраченный на I-кадр, обеспечивает примерно такое же качество, как четыре бита на P-кадре, который от него зависит. B-кадр – «одноразовый», с него никто больше не копирует, поэтому переплата за него не приносит пользы остальной части GOP. Понижение QP I-кадра примерно на 3 и повышение QP B-кадра примерно на 2 попадает в оптимум для естественного видео с точностью до нескольких процентов.

Численный пример для закрепления. Допустим, средний P-кадр в потоке 4 Мбит/с занимает 133 000 бит. При ipratio 1,40 целевой размер I-кадра – около 1,40 × 133 000 ≈ 186 000 бит, а B-кадра – примерно 133 000 / 1,30 ≈ 102 000 бит. В GOP из 30 кадров (1 I, 9 P и 20 B) суммарный расход составит 186 + 9 × 133 + 20 × 102 ≈ 3 423 кбит/с – уже близко к целевым 4 Мбит/с, ещё до работы покадрового распределителя битов.

Соотношения чувствительны к содержанию. Зернистое шумное кино выигрывает от более низких значений (ближе к 1.15 / 1.15), потому что в I-кадре много высокоэнтропийного шума, который стоит одинаково независимо от QP. Плоская анимация, напротив, терпит более высокие значения (ближе к 1.6 / 1.4), поскольку копирование и деформация в P-кадре покрывают почти всю картинку. Современные энкодеры адаптируют эти параметры автоматически – в частности, в x264 алгоритм macroblock-дерева заменяет постоянные соотношения, когда он включён (а он включён по умолчанию для любого пресета медленнее ultrafast).

Второе разделение: уровень в иерархической B-пирамиде

Современные кодеки не выстраивают B-кадры строго между P-якорями в линейной последовательности. Они используют пирамидальную структуру. Типичная 8-кадровая mini-GOP в порядке отображения выглядит так:

порядок отображения:  I   B   B   B   B   B   B   B   P
позиция:              0   1   2   3   4   5   6   7   8
временной уровень:    0   3   2   3   1   3   2   3   0

Кадры на уровне 0 (I и P) являются опорными для всех остальных. Кадр 4 на уровне 1 кодируется следующим и служит опорой для уровней 2 и 3. Кадры 2 и 6 на уровне 2 – опорные для уровня 3. Кадры 1, 3, 5, 7 на уровне 3 не используются ни одним другим кадром в качестве референса, и их можно сбросить, не затрагивая другие кадры. Декодер, которому нужна половина частоты кадров, просто пропускает уровень 3.

Каждый временной уровень получает собственный QP-офсет, и эти офсеты накапливаются в каскад. Уровень 0 имеет наименьший QP (то есть больше битов). Каждый последующий уровень добавляет положительный офсет. В референсной реализации HEVC стандартный каскад для 8-кадровой mini-GOP выглядит так: QP+1, QP+2, QP+3, QP+4 для уровней 0–3 соответственно, при этом возможны более тонкие настройки в зависимости от типа слайса. В SVT-AV1 каскад определяется данными из модели временных зависимостей (TPL) и зависит от пресета энкодера. Форма всегда одна: более глубокие уровни платят больше за каждый бит, поскольку их искажение не распространяется дальше.

Это и есть самая большая одиночная победа по качеству за последние 15 лет в инженерии кодеков. Иерархические B-кадры с QP-каскадом обеспечили прирост примерно в 0,5 дБ по Y-PSNR по сравнению с плоским B-кодированием на тестовых последовательностях MPEG, использовавшихся при стандартизации HEVC. На естественном контенте при фиксированном битрейте это эквивалентно бесплатному полушагу в качестве – без изменения кодека, просто за счёт перераспределения битов в пользу тех кадров, от которых зависят остальные.

Рис. 2. 8-кадровая иерархическая B-пирамида. Кадры уровня 0 (I, P) – опорные для всей последовательности; каждый последующий уровень имеет QP на 1 выше, поскольку кадры в нём используются реже, а их искажения не передаются дальше.

Третье разделение: сложность каждого кадра

Соотношения по типам кадров и временные сдвиги – статичны. Они не учитывают, что реально содержится в следующих 8 кадрах. Распределитель, управляемый сложностью, – учитывает: он оценивает, насколько трудно закодировать каждый предстоящий кадр, и перераспределяет бюджет GOP так, чтобы более сложные кадры получили больше битов.

Каноническая формула масштабирования в x264 – прямая цитата из ratecontrol.txt Лорена Мерритта:

target_bits[i] = complexity[i] ** qcomp × scale

где complexity[i] – стоимость кадра i в битах при референсном QP (берётся из первого прохода в двухпроходном режиме или из лукахеда на половинном разрешении в однопроходном), qcomp – параметр от 0 до 1, а scale – коэффициент, обеспечивающий выполнение бюджета rate-control. При qcomp = 0 все кадры получают одинаковое количество битов (постоянный битрейт, большие колебания качества). При qcomp = 1 биты распределяются строго пропорционально сложности кадра (постоянный квантовый параметр, большие колебания битрейта). Значение по умолчанию – 0.60 – эмпирически найденный оптимум для восприятия человеком на естественном видео: на сложных сценах оно тратит чуть меньше, чем пропорционально (глаз маскирует артефакты в движении), а на простых – чуть больше, чем поровну (чтобы статичные фоны не демонстрировали бэндинг).

Численный пример, чтобы сделать распределение по сложности конкретным. Пусть в GOP три кадра со стоимостями первого прохода при QP 22 равны 80, 200 и 50 кбит, а целевой бюджет GOP – 270 кбит. Равное распределение даст каждому по 90 кбит – катастрофически недокормит средний кадр на 200 кбит. Строго пропорциональное распределение: 80/(80+200+50) × 270 = 65, 162 и 41 кбит – близко к исходным, но равномерно сжато. Дефолтное поведение x264 при qcomp 0.60 даёт 80^0.6, 200^0.6, 50^0.6 (= 14.6, 24.6, 10.5 в условных единицах), нормированных к сумме 270: 81, 137 и 58 кбит. Сложный кадр получает меньше своей строгой доли (137 < 162), потому что перцептивная маскировка прощает потери, а простым кадрам дают небольшую надбавку, чтобы их статичный контент оставался чистым.

В однопроходном режиме оценка сложности берётся из lookahead x264: он выполняет быструю оценку движения на половинном разрешении в скользящем окне (по умолчанию – 40 кадров, до 250 с --rc-lookahead), фиксирует стоимость остаточной ошибки (SATD – сумма абсолютных значений преобразованных разностей) и использует её как показатель сложности. В двухпроходном режиме оценка lookahead заменяется реальной стоимостью первого прохода – более точная, но удваивающая время кодирования.

Четвёртое разделение: распределение с учётом распространения (MB-Tree, CU-Tree, TPL)

Соотношения по типам кадров и сложность по кадру ограничены уровнем отдельного кадра. Они не учитывают, что блок (10, 5) в кадре 4 станет опорным для блока (10, 5) в кадрах 5, 6, 7 и 8 – а значит, дополнительные биты, потраченные на него, повысят качество сразу пяти кадров. Распределение, учитывающее распространение, закрывает эту проблему: оно распределяет биты на уровне блока, взвешивая их в зависимости от того, сколько будущих блоков скопируют данные с этого блока.

Три реализации, с которыми вы столкнётесь в продакшене:

Macroblock-Tree (MB-Tree) – алгоритм x264, разработанный Джейсоном Гарреттом-Глейзером и Лореном Мерриттом в 2009 году. Энкодер использует lookahead, чтобы оценить, как часто каждый макроблок размером 16×16 будет использоваться в качестве референсного другими блоками в последующих кадрах в пределах окна просмотра. Макроблоки, которые используются часто, получают снижение QP (до 6–10 пунктов) – так называемая поблочная скидка; те, что быстро перезаписываются, – небольшой штраф. Например, неподвижный фон, копируемый в течение двух секунд, может получить максимальную скидку. На естественном контенте MB-Tree позволяет сэкономить 5–10% битрейта при неизменном SSIM по сравнению с равномерным покадровым распределением QP. При этом его использование практически не требует дополнительных затрат: необходимый lookahead уже работает для размещения B-кадров и обнаружения смены сцен.

CU-Tree – HEVC-аналог x265. Принцип работы идентичен: вместо макроблоков 16×16 используются единицы кодирования (CU) переменного размера, применяемые в HEVC (до 64×64 в ранних версиях и до 128×128 с расширением screen-content). Статья Пуррезы и соавторов 2018 года («Optimize x265 Rate Control: An Exploration of Lookahead in Frame Bit Allocation and Slice Type Decision») показала выигрыш в 2–8 % по BD-рейту (BD-rate, дельта битрейта по Бьонтегаарду) на общих тестовых последовательностях MPEG, в зависимости от пресета.

Temporal dependency model (TPL) – трекер распространения эры AV1, встроенный в libaom и принятый SVT-AV1 в версии 0.9 (2021). TPL формализует то, что MB-дерево делало эмпирически: он решает линейную программу по всей GOP, минимизируя сумму искажений, взвешенных по значению «r0» каждого блока (доле его rate-distortion-стоимости, которая передаётся в будущие блоки). В реальных потоках AV1 выигрыш составляет 5–12% по BD-рейту относительно базового варианта без TPL, а максимальный эффект наблюдается на контенте с высокоструктурированным движением – например, в спорте и анимации.

Общий лейтмотив: распределение, учитывающее распространение, – самая ценная техника на основе lookahead в современных энкодерах. Отключите её – и вы потеряете 5–15% битрейта при том же качестве на всём, кроме малоподвижных «говорящих голов» (где распространять нечего).

Рис. 3. Распределение битов с учётом распространения. Энкодер определяет для каждого блока текущего кадра, сколько будущих блоков в окне lookahead ссылаются на него. Часто используемые блоки получают снижение QP (скидку), а те, что быстро перезаписываются, – небольшое повышение QP (штраф).

Как современные энкодеры распределяют работу – сравнение

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

ЭнкодерРучки типов кадровРучки временного уровняРаспределитель по сложностиТрекер распространенияLookahead по умолчанию
x264 (H.264)--ipratio 1.40, --pbratio 1.30неявно через B-пирамиду + MB-tree--qcomp 0.60MB-tree (включён по умолчанию)40 (--rc-lookahead)
x265 (HEVC)--ipratio 1.40, --pbratio 1.30--bframe-bias, таблица офсетов--qcomp 0.60CU-tree (включён по умолчанию)20 (--rc-lookahead)
libaom-AV1настраивается по проходу, скрытоиерархическая, 5 уровней по умолчаниюстатистика первого прохода + сегментный qTPL (включён для --good)--lag-in-frames 19
SVT-AV1настраивается по пресету, скрыто5 или 6 уровней, управляется пресетомзначения TPL r0TPL (включён по умолчанию)управляется пресетом
VVenC (H.266/VVC)управляется профилемтаблица QP-офсетов по уровнямкарта стоимостей rate-distortionлагранжиан по уровням16 по умолчанию
libvpx-VP9похоже на libaomдо 3 уровней иерархиистатистика первого проходабазовое распространение (без TPL)--lag-in-frames 25

Два паттерна, на которые стоит обратить внимание. Во-первых, семейство H.26x (x264, x265) выставляет параметры наружу, чтобы оператор мог вручную настраивать кодирование. Семейство AOMedia (libaom, SVT-AV1) скрывает их за пресетами и TPL, поскольку модель работает на основе данных. Во-вторых, каждый современный энкодер включает трекинг распространения по умолчанию – MB-Tree, CU-Tree и TPL активны «из коробки». Если отключить их, вы просто тратите биты впустую.

Два прохода, один проход и что меняется внутри распределителя

В режиме двух проходов у распределителя – полная информация. На первом проходе создаётся stats-файл с точной стоимостью каждого кадра при placeholder-QP, а на втором проходе эти значения используются как входной параметр «сложности» в приведённой выше формуле. Распределитель может спланировать распределение по GOP ещё до кодирования хотя бы одного кадра второго прохода, и итоговый результат отклоняется от целевого битрейта не более чем на 0,5% на 90-минутном файле.

В однопроходном режиме у кодировщика есть только информация, доступная через lookahead. Чем больше lookahead, тем ближе качество однопроходного кодирования к двухпроходному – но ценой задержки: энкодер не может отправить кадр N, пока не проанализирует кадры N+1 … N+lookahead. Для задач VOD значение lookahead в 60 кадров – оптимальное (sweet spot): дальше кривая качества выходит на плато. В live-трансляциях lookahead ограничен общей задержкой конвейера: WebRTC-решения с задержкой менее секунды используют lookahead от 0 до 8 кадров, а HLS-конвейеры с задержкой до 3 секунд – от 16 до 40.

Распределитель битов в обоих режимах выполняет дополнительную компенсацию: после кодирования каждого кадра, когда становится известна его реальная битовая стоимость, параметры QP будущих кадров корректируются, чтобы компенсировать ошибку предсказания. Если распределитель предсказал 100 кбит для следующего P-кадра, а фактически было использовано 130 кбит, QP последующих кадров масштабируется на коэффициент 100/130, чтобы восполнить дефицит. Степень этой коррекции определяет разницу между жёстким VBV-CBR (дефицит возвращается строго, допускается колебание качества) и более гибким ABR (коррекция мягкая, допускается разброс битрейта ±10%).

Числа, которые реально показывает лог энкодера

Когда вы запускаете x264 с --verbose или x265 с --csv-log-level 2, в логе по каждому кадру отображается работа планировщика. Типичная строка x264:

[debug] frame=  243 QP=22.0 NAL=2 Slice:P Poc:486 I:174  P:1542 SKIP:684 size=10623 bytes

QP равен 22,0, но это среднее значение по кадру. Реальные значения QP для макроблоков варьируются из-за поблочной скидки, применяемой алгоритмом MB-tree. Энкодер также выводит однострочную гистограмму в конце, отображающую распределение QP:

[info] x264 [info]: frame I:23   Avg QP:17.85  size:113042
[info] x264 [info]: frame P:1085 Avg QP:21.21  size: 13420
[info] x264 [info]: frame B:2367 Avg QP:23.42  size:  4811

Три вещи, которые можно прочитать из этого. Во-первых, средний QP I-кадра (17,85) примерно на 3,4 QP ниже среднего QP P-кадра (21,21) – это в действии --ipratio 1,40 (6 × log₂(1,40) ≈ 2,9 плюс небольшая скидка от MB-дерева за распространение, которое создают эти I-кадры). Во-вторых, средний QP B-кадра (23,42) на 2,2 QP выше, чем у P-кадра – это в действии --pbratio 1,30 (6 × log₂(1,30) ≈ 2,3). В-третьих, соотношение среднего размера кадров (113 : 13 : 4,8 кбит) составляет примерно 23 : 3 : 1, что близко к правилу: I-кадр ~5× P-кадра, P-кадр ~2,5× B-кадра при одинаковом воспринимаемом качестве. Если в логах соотношения сильно отличаются от этих значений – например, I-кадр к P-кадру как 50:1 – энкодер сильно сжимает P/B-кадры, и изображение будет пульсировать на каждом ключевом кадре.

Распространённая ошибка: ручная настройка ipratio при включённом MB-Tree

Самая частая ошибка в продакшене – прочитать устаревший гайд, решить: «у меня зернистый источник, нужно понизить ipratio до 1.15» – и так и отгрузить. Проблема в том, что x264 с включённым MB-Tree (а это значение по умолчанию) игнорирует ipratio при распределении битов и полагается на веса распространения. Ручной ipratio применяется только в резервном режиме, который используется на первых нескольких кадрах, пока буфер предварительного просмотра не заполнится. Итог: оператор считает, что настраивает энкодер, а тот использует собственную модель, и результат ничем не отличается от настроек по умолчанию.

Правильное решение – одно из двух. Либо использовать дефолты MB-tree: они настраивались командой x264 и Лореном Мерриттом на тестовом наборе MPEG с 2009 года и дают выигрыш почти для любого входного сигнала. Либо, если есть веская причина переопределить поведение – например, при низколатентной прямой трансляции с --rc-lookahead 0, – явно передать --no-mbtree, чтобы распределитель перешёл на путь по соотношениям, и ваш тюнинг действительно начал работать. Та же логика применима к --no-ctu-tree в x265 и отключению TPL в libaom (--enable-tpl-model=0). Если вы случайно отключили трекер распространения – теряете 5–15% битрейта; если сделали это осознанно, из-за ограничений по задержке, запрещающих lookahead, – придётся вручную перетюнить параметры соотношений.

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

Распределение битов остаётся незамеченным, пока всё работает хорошо, но именно оно первым бросается в глаза, когда что-то идёт не так. В live-конвейерах, которые мы строили для клиентов – видеоконференций и видеостриминга, – настройка почти всегда сводится к компромиссу между задержкой и качеством: сколько lookahead может позволить себе конкретный use case, что напрямую определяет, насколько агрессивным может быть битовый распределитель внутри каждой GOP. Для OTT- и VOD-каталогов – телемедицинских архивов, e-learning-библиотек, записей с камер видеонаблюдения – мы используем двухпроходное распределение с полным трекингом распространения битов, потому что задержка здесь не критична, а экономия битрейта напрямую снижает расходы на CDN. Конфигурация, которую мы применяем, зависит от поколения кодека и возможностей клиентской цепочки декодирования, но базовое решение всегда одно: выбрать те параметры, которые можем себе позволить, остальное оставить по умолчанию, а затем проанализировать покадровый лог, чтобы убедиться, что битовый распределитель работает именно так, как задумано.

Главное

  • Битовый бюджет GOP распределяется по четырём критериям: типу кадра, временному уровню, сложности кадра и весу распространения блока.
  • Стандартные значения x264 ipratio 1.40 и pbratio 1.30 обеспечивают I-кадру скидку около 3 QP и B-кадру штраф около 2 QP по сравнению с P-кадром.
  • Иерархические B-кадры с QP-каскадом дали прирост примерно 0.5 дБ по Y-PSNR на тестовом наборе HEVC без дополнительных затрат.
  • MB-дерево, CU-дерево и TPL решают одну задачу – определить, насколько часто используется тот или иной блок, – и все три метода позволяют сэкономить 5–15% битрейта.
  • Двухпроходное распределение битов укладывается в отклонение от целевой скорости передачи данных не более чем на 0.5% при обработке полнометражного файла; одному проходу требуется lookahead в 40–60 кадров.
  • Ручная настройка ipratio при включённом MB-дереве не даёт эффекта; либо используйте значения по умолчанию, либо сначала отключите --no-mbtree.

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

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

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