Он такой… не постоянный

Часто возникает отправить файл в виде стрима в сеть или преобразовать (перекодировать, масштабировать) некий входной видео-поток. Естество наше просит обратиться к каким-либо «свободным» (бесплатным) продуктам… поскольку оно ж ничего не стоит.


И на поверхности тут лежит такое «явление» как ffmpeg.

Не будем греха таить, ffmpeg великолепный open-source продукт для медиа-траскодирования и стриминга. Он настолько хорош, что лежит в основе таких продуктов как CasparCG или fluesonic… и даже зашивается в железо бюджетных аппаратных стримеров,… например таких:

Нам доводилось пользоваться таким стримером и нам очень понравилось (как-то спокойнее работается чем с «софтовым» вариантом на базе PC или Raspberry). Но как говаривал Евстратий Палыч Мыльников своим филерам в «Статском советнике»: «А штуковина то в мелочах….»

И мелочь тут такая… ffmpeg ни при каких условиях не позволяет формировать файл или поток с постоянным битрейтом в кодеке h264 — то бишь в режиме CBR. Возможно кто-то скажет, что «это не правда» или «вы плохо читали форумы»… да, в настройках библиотеки x264 имеется ряд атрибутов, которые, вроде как (хотя на официальном сайте ffmpeg написано, что в их системе нет «native CBR mode»), должны устанавливать режим постоянного битрейта. В частности предлагается, например, использовать такую комбинацию параметров кодирования:

-x264opts nal-hrd=cbr:force-cfr=1 -b:v 2500k -minrate 2500k -maxrate 2500k -bufsize 100k

где:

nal-hrd=cbr — устанавливает эталонный декодера в режим CBR;
force-cfr=1 — принудительная генерация метки времени с постоянной частотой кадров;
-b:v — битрейт видео;
-minrate -maxrate — устанавливаются в то же значение что и общий битрейт;
-bufsize — размер буфера верификатора (VBV), рассчитывается для кодирования с ограниченным размером кадра по формуле (bufsize = bitrate / framerate).

Все это верно, но как ни старайся на статичных картинках или при затемнении между программами ffmpeg «жаждет» оптимизировать поток и снижает битрейт практически «до нуля». Кто-то резонно заметит «ну и Бог с ним…»

Так-то оно так, но при передаче потока на головную станцию кабельного тв желательно (а для некоторых моделей даже обязательно), чтобы битрейт был постоянный. Также при передаче IP-сетями по протоколу HTTP при помощи программы Cesbo Astra возникают «ложные обрывы» — срабатывание переключения Astra на резервный источник или выдачу Error'ов когда битрейт снижается ниже определенного уровня. Это «не есть хорошо» т.к. в эти моменты изображение или останавливается, или исчезает, или «рассыпается, чихает и заикается».

Что с этим делать?

По сути единственным разумным способом является наполнение потока стаффингами (NULL-TS пакетами), которые не содержат никакой полезной информации.

Закономерен вопрос: «Как наполнить поток стаффингами».

Вариант первый:…

Ряд сборок библиотеки x264 от ffmpeg имеет нативный атрибут -muxrate, который позволяет при мультиплексировании собирать поток с заданным битрейтом. Таким образом сам ffmpeg должен донаполнять сигнал NULL-TS пакетами. Однако очень многие сборки библиотек ffmpeg не обрабатывают атрибут -muxrate должным образом и требуемый эффект не достигается.

На помощь приходит второй подход:..

Данный метод требует наличия «на борту» медиасервера Cesbo Astra (последней бесплатной версии 4.* достаточно для решения данной задачи).

Суть подхода заключается в создании udp-кольца с наполнением потока стаффингами. Общую логическую схему можно изобразить таким образом:

где:

input — вход с ffmpeg'а;
Astra1 — первый рестример наполняющий поток стаффингами;
udp-output — промежуточный вывод фиктивного udp-потока с атрибутами sync&cbrX
Astra2 — второй рестример направляющий поток в необходимый формат (например HTTP);
input — выход в сеть;

Файл конфигурации первого рестримера будет иметь примерно следующее содержание:

make_channel({
name = "Make CBR",
input = {
"udp://192.168.1.1@239.0.0.1:1234", // входной поток без стаффингов
},
output = {
"udp://192.168.1.1@239.0.0.1:1235#sync&cbr=3", // «фиктивный» поток
}})

В свою очередь конфигурация второго рестримера может быть следующей:

make_channel({
name = "Channel CBR",
input = {
"udp://192.168.1.1@239.0.0.1:1235", // «фиктивный» поток
},
output = {
"http://0:8080/channel", // выходной поток со стаффингами
}})

Результатом работы данной цепочки будет медиапоток битрейт которого не будет опускаться ниже 3-х Мегабит в секунду и, соответственно, «ложных обрывов» на приеме быть не должно.

Кстати, для упрощения схемы, файлы конфигурации можно объединить и использовать одно запущенное приложение. Cesbo Astra отлично отрабатывает схему в виде петли, реализующей принцип «вещаю сам в себя»:

Важно помнить, что стаффинги идут в мультплексированном сигнале как отдельный элементарный поток со значением PID 8191. Поэтому в случае если вы производите переназначение меток программ или элементарных потоков (делаете «маппинг PID'ов») Cesbo Astra произведет отброс стаффингов и пересборку мультплекса без них, поэтому следует не забывать производить перезаполнение потока стаффингами после различных работ по модификации значений атрибутов мультиплексированных сигналов.