Декодер IP потоков

В нашем захолустье проживает 260-270 тысяч населения, что значит – цивилизация сюда идёт с большими задержками. Но идёт неизбежно. И вот, в один какой-то момент, местные интернет провайдеры начали гонку вооружений городских видеокамер. Начали мериться у кого больше, да у кого жирнее сеть. На этот карнавал решила зайти и наша телекомпания. Ну как зайти… поучаствовать. Ну и как начинаются все мои истории, вызывает меня руководство и говорит: «мы тут уговор свели с одним провайдером. Они нам потоки своих камер, а мы их в эфир. И им пиар и нам картинка живая, чтобы погоду показывать там, ну или как колесницы на дорогах бьются. А ты изобрети нам железку колдунскую, что бы картинку из потока вынимать, да в комплекс телевизионный подавать. Да что б не дорого!». Ну мне такие задачи не в новинку, достал из загашников компьютер не молодой, засунул в него плату SDI вывода, да погрузил все эти беснования в машинный зал. А что бы железяка бесовская работала поднял на ней VLC плеер в консольном режиме, да с веб-интерфейсом. И, казалось бы, вот и сказочке конец. И жили все долго и сч… да нет конечно! Через полгода пришел на поклон второй провайдер. И говорит: «тоже хочу вам картинки со своих камер показать, да на тех же условиях!». И в тот момент начались сложности: дело в том, что VLC не умеет накладывать на каждый поток свой логотип. т.е. сменить лого на лету да по веб-интерфейсу невозможно. Но тут вопрос авторских прав и т.д. Не можем же мы выдать в эфир чужую картинку под своим брендом. А тут ещё и разрешения у камер разные и подрезать изображения нужно на каждой по-своему. Это необходимо было по тому, что на разных камерах частенько встречались временные штампы или наложенное название камеры, а провайдер не мог их отключать по тем или иным причинам. В общем уперлись в тупик с этим плеером. Делать нечего, придется ваять свою программу, с блэк-джеком и шлю…

Глава 1. …Ух как навыдумываю сейчас!

Вообще идея написания своего ПО для декодера потоков у меня крутилась в голове давно. По сути нужно было взять части кода от проекта ComDev, добавить часть кода от AutoRecorder, перемешать, посыпать кусочками новых плюшек основного монстра FFMPEG и вуа-ля. А после того, как я открыл для себя его увлекательный и широченный мир, мне только и нужен был маленький пинок к действию. Им и стала проблема функционала VLC. И так, поигравшись с командной строкой ffmpeg и поняв, как им принимать поток и отдавать в мою плату decklink, я определился с функционалом:

  • редактируемый список потоков. В начале хотел просто читать из плейлиста, типа m3u или xspf, но потом отказался от этой идеи в пользу собственного списка в xml, куда можно засунуть и все настройки программы, и все логотипы за одно.
  • управление по веб-интерфейсу. Это необходимость, т.к. сам декодер находится в машинном зале, а инженер на своем рабочем месте. Всякие RDP и иже с ними – моветон!
  • разделённый функционал. Т.е. в зависимости от прав доступа (юзер ты или админ) можно или управлять камерами в эфире или просто посмотреть скриншот с неё. Ну а что? шеф-редактор, например, должен же иметь возможность выбирать камеру для эфира.
  • разделённое брендирование. Для каждого потока нужно ставить свой логотип.
  • разделённая обработка. Ну т.е. каждую картинку нужно отдельно подрезать, подкрасить и т.д.
  • Логирование. Это понятно. Что бы понимать, что происходило в тот или иной момент времени, все ходы записываем.

По мере эксплуатации может и ещё что придумается, но пока остановился на таком минимуме. И если вопросы с логотипом и обработкой решал наш очень гибкий и мощный бро ffmpeg и его комплексные фильтры, то вот остальное придется отдать нашей оболочке. Но вначале поговорим о нём.

Глава 2. Комплексные фильтры – наши друзья

В поисках примеров и русскоязычных мануалов в интернете, я наткнулся на статью aNNiMON о графах фильтров. Статья очень простая, для понимания и я прям благодарен этому автору. В двух словах: там грамотно и коротко описан синтаксис написания комплекса фильтров в строке ffmpeg’а. Почитав статью я выстрадал свой комплексный фильтр:

охренеть непонятно, да? Давайте разберём это заклинание. И так:

  • -filter_complex – так мы говорим ffmpeg’у, что хотим сделать комплексный фильтр. далее описываем его в кавычках. Обратите внимание, что все фильтры в комплексе разделяются точкой с запятой.
  • [0] и [1] – это индексы наших источников, указанных в строке под ключом –i.
  • [0]crop= in_w:in_h-50:in_w:in_h[crop]; – сообщаем, что к источнику [0] (наш поток видео) нужно применить фильтр подрезки кадра с параметрами входной ширины (in_w), от входной высоты отрезаем 50 пикселей (in_h-50) и позиционируем картинку на координатах входной ширины и высоты (in_w:in_h). И говорим, что выход нашего фильтра будет под названием [crop].
  • [1][crop]scale2ref=(1920/1080)*ih/sar:ih[logo][vid]; – Согласно статье, некоторые фильтры имеют более одного входа и выхода. Как в этом случае. К фильтру scale2ref (масштабирование) подключаем наш второй источник [1] (т.е. логотип) и подрезанный предыдущим фильтром поток [crop]. И задаём ему параметры масштаба под наше выходное разрешение. Так сделано для того, чтобы логотип не прыгал по экрану и не менял своих размеров в зависимости от разрешения камеры. И указываем названия выходных потоков [logo][vid].
  • [vid][logo]overlay=0:0 – Подключаем фильтр наложения, сообщаем ему источники [vid] и [logo], и т.к. логотип у нас – это картинка 1920х1080 с альфа – каналом, устанавливаем её на координаты 0:0

Единственное что я подбирал под каждую камеру индивидуально, это параметр подрезки. Благо, что разрешения камер были выше нашего вещательного 1920х1080, так что качество картинки практически не страдало.

И так берем наш комплекс фильтров и засовываем в строку. получаем что-то вроде:

Портянка хоть и длинная, но после разъяснений уже не такая страшная, правда? Тут стоит только пояснить про –ss 5 – это команда отбрасывает первые пять секунд ввода. Некоторые потоки падают с ошибками буферизации, ключевых кадров, и т.д. если таким образом не подождать ответа от сервера потоков. Ну и длинная строка после ключа –f – это всё относится к выходному формату, который поддерживает наша плата вывода. В нашем случае это плата DeckLink Studio 2. И тут нужно оговориться отдельно:

FFMPEG – бесплатный продукт и по умолчанию не поддерживает такие платы как decklink. Для того, чтобы наши параметры проглотились и плата заработала, нужно скомпилировать ffmpeg.exe с поддержкой данных плат из исходников самостоятельно. Ну или искать на просторах интернета версию с пометкой nonfree в названии.

Версия, использованная мной, была скомпилирована проектом FFmpegGUI о котором я упоминал в статье «Автоматизация записи эфира».

Глава 3. Заворачиваемся в красивую обёртку

Теперь давайте вникнем в топологию нашей оболочки. Все функции, переменные и т.д. мы разделим на 3 категории:

  • Первая – категория настроек и всё что к ним относится. Эти части программы мы соберем в отдельный проект и скомпилируем в библиотеку dll.
  • Вторая – категория редактора эти самых настроек. Всё что будет относиться к редактированию, будет в отдельном приложении.
  • Третья – непосредственно сервис web и управления.

И так, настройки.

У нас есть готовая длиннющая строка для запуска ffmpeg, которую мы можем условно поделить на две части – параметры источника и параметры выхода.

Значит, нам придется засунуть эти параметры в разные переменные, которыми мы и будем оперировать в дальнейшем. А также есть куча настроек Веб-сервера, перечислять которые нам не хватит никаких статей. Проще говоря, берем все эти пироги и засовываем в наш класс сериализатора, который уже не однократно был проверен годами.

Немного об авторизации веб-интерфейса. Я решил разделить права таким образом:

  • user – пользователь который может посмотреть какая камера сейчас в эфире, а кликнув по любой кнопке камеры, может получить скриншот выбранной камеры. и… собственно всё. Смысл этого в том, что сети пользователей и камер, это чаще всего разные сети, и данный пользователь не может получить доступ к камере напрямую. Эта учетка предполагалась для шеф-редактора, который перед эфиром принимает решение какую камеру будут показывать в прямом эфире.
  • moder – данный пользователь подразумевается, как человек, имеющий доступ к обеим сетям. Сети камер и сети где расположен наш веб-интерфейс. Соответственно, модератор так же может посмотреть какая камера сейчас в эфире, и кликнув по любой кнопке камеры, может получить прямую ссылку на камеру, что бы открыть её у себя на компьютере каким ни будь плеером уже без посредника – сервера.
  • admin – это наш царь и бог в этом интерфейсе. Он как раз и запускает нужную камеру на выход декодера и останавливает её по своей воле. Это учетная запись непосредственно для инженера, который участвует в эфире, как вы могли догадаться.

Естественно все пароли наших пользователей должны шифроваться. Для этого в нашей оболочке предусмотрен отдельный класс. И да, знаю, сейчас в меня полетят тапки от знающих людей с криками «алё! пароли нужно шифровать ДО отправки на сервер». И да, они будут правы, но данная оболочка подразумевает использование только в закрытых не публичных сетях, так что силой моего радиуса кривизны рук, было принято решение исправить это в дальнейших версиях. Сейчас была задача «что б работало!».

А пока давайте взглянем на утилиту редактирования настроек:

интерфейс программы IPdecoder

Я разделил настройки на «основные» и на «источники». В основных мы видим:

  • порт сервера – порт по которому общается наш веб-интерфейс. Его следует указывать в строке браузера через двоеточие. Например, http://127.0.0.1:8888
  • количество подключений – т.е. сколько пользователей одновременно могут подключаться к нашему серверу.
  • размер логов – это количество килобайт, которое не должен превышать наш файл журналирования.
  • Имя системы – название, передаваемое пользователю в шапке веб-интерфейса. На случай, если у нас будет не один декодер. Ну что бы не запутаться.
  • пароли пользователей – в строках они такой длины всегда, потому что хранятся закодированные. По умолчанию я сделал по классике: логин = паролю.
  • строка скриншота – это строка подставляется в ffmpeg в качестве параметра выхода, когда камеру вызывает пользователь user. По умолчанию тут команда получающая скриншот выбранного источника т.е. камеры.
  • строка эфира – это строка подставляется в ffmpeg в качестве параметра выхода, когда камеру вызывает пользователь admin. По умолчанию я указал выходные параметры для выхода на плату DeckLink Studio 2.

А вот с вкладкой источников всё немного иначе:

интерфейс программы IPdecoder

Тут я сделал (как и в случае с планировщиком AutoRecorder) список источников (камер), состоящий из входной строки, куда мы вписываем ту часть строки ffmpeg, что отвечает за параметры источника (в т.ч. наш комплексный фильтр), и названия источника, которое отображается непосредственно в веб-интерфейсе. Кликнув любую строку из списка, мы увидим её в полях редактирования сверху. Изменив какие-либо параметры в строке, мы можем либо нажать «изменить», что отредактирует текущую запись, либо нажать «добавить» – это создаст новую строку на основе выбранной. Это удобно, если у нас много повторяющихся строк с минимальными отличиями. Ну и не забываем про кнопки «открыть» и «сохранить». Их я сделал на случай, если файл с настройками хранится в другой директории.

Ну что же, осталось заглянуть в основной сервис:

интерфейс программы IPdecoder

По сути за основу я взял свой старый проект ComDev, выкинув всё лишнее и дописав нужное. Долго ползать по коду не имеет смысла. Заострим внимание на классе open. По сути он – сердце нашей задумки:

Как я и говорил выше: в зависимости от уровня авторизации пользователя, который вызывает команду open, данный класс принимает решение что ему сделать:

  1. вызвать процесс ffmpeg со строкой получения скриншота и сохранить его в папку для дальнейшего доступа из веб-интерфейса.
  2. вырезать адрес камеры из строки источника по шаблону «от ключа –i до кавычки» и передать его в файл m3u для дальнейшего доступа из веб-интерфейса. Так шаблон реализован, исходя из правил написания строки ffmpeg.
  3. вызвать процесс ffmpeg со строкой выхода на плату, предварительно завершив все остальные процессы, что бы ничего не мешало эфиру (эфир прежде всего!).

Собственно, на стороне сервера мы закончили.

Глава 4. А что же на другой стороне?

А вот на стороне клиента, после этого долгого тернистого пути, мы увидим это:

Web interface

В общем то оформление интерфейса я так же взял из старого проекта, немного его переписав. Вверху слева мы увидим с каким уровнем доступа мы авторизовались. Далее увидим название системы, в которой мы находимся или (если данный сервер в эфире) название камеры которая сейчас в эфире. Кликнув по этому названию, мы остановим эфир. Так же текущая дата и время, которые передаются с сервера. По ним можно ориентироваться есть ли связь с сервером. Пока время идёт – всё в порядке, а вот если время остановилось, значит с системой что-то не так. Ну и конечно же список камер. Так же справа прикручены всплывающие сообщения, которые транслируют последнюю запись из логов.

Весь интерфейс – это примитивный html с элементами JS и AJAX. В данных языках я не силён, так что углубляться туда мы не станем, что бы ваш покорных слуга не выглядел идиотом.

Заключение

Что по итогу мы получили на выходе? По сути мы получили возможность управлять очень мощным, гибким и в то же время бесплатным инструментом ffmpeg по сети, с другого компьютера, так же бесплатно. Конечно до идеала код нужно дорабатывать. Но в данном случае, я считаю, это хорошая альтернатива профессиональным аналогичным системам, которые (как и всё в мире broadcasting’а) стоят не малых денег. И как обычно, всё необходимое по ссылке ниже. А так же: пользуйтесь своими мозгами правильно.

ФАЙЛЫ ПРОЕКТА

2 комментария для “Декодер IP потоков

  1. Мужчина, тебе надо двигаться в сторону программирования… ты столько возможностей и бабла теряешь даря текущему работодателю такой софт и свою голову почти даром.

    Респект тебе конечно! тема серьёзная, мне даже чтобы прочитать пришлось мозги напрячь!

    PS. Кстати, nonfree можно юзать в комм целях?

    1. Насколько знаю, она остается GPL. Так что да, можно)
      Что до возможностей: нет спроса на меня. Печально, но факт – не получается у меня войти в нишу программирования…

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

Ваш адрес электронной почты не будет опубликован.Обязательные поля помечены *

* Все комментарии проходят предмодерацию.