Камера на складе разговаривает по RTSP. Браузер сотрудника в офисе RTSP не понимает и понимать не собирается: ему нужна страница, JavaScript и WebRTC-соединение. Между ними нужен переводчик, и на роль такого шлюза хорошо подходит MediaMTX. Но «переводчик» тут слово обманчивое: он переводит способ доставки, а не всё содержимое сразу. Разберём путь одного видеопотока до одного закрытого кабинета и посмотрим, в каких местах он может оборваться.

Условный сценарий на всю статью такой: одна IP-камера, один сервер-шлюз, один веб-кабинет, куда заходят только сотрудники склада. Это учебная схема, а не рассказ о реальном внедрении и не результат замеров.

Путь потока: камера, шлюз, браузер

Начнём с самой простой картинки. Камера отдаёт поток по RTSP. MediaMTX забирает его сам: в документации это называется static source, то есть источник, который сервер подтягивает с камеры или другого сервера. Дальше поток живёт внутри MediaMTX под именем пути, например sklad-vhod. Браузер подключается к этому пути уже по WebRTC. По документации MediaMTX принимает и отдаёт потоки через несколько протоколов, причём протокол публикации и протокол чтения могут различаться (публикация и чтение потоков).

Отсюда важное следствие для архитектуры. Камере не нужно знать про браузеры, а браузеру про камеру. Камера держит одно соединение со шлюзом, а не по соединению на каждого зрителя. Дешёвым камерам это нередко принципиально: многие плохо переносят несколько одновременных клиентов. Шлюз превращается в буфер между хрупким устройством и любопытными людьми.

Есть и вторая выгода, чисто сетевая. Камера не должна быть доступна из интернета. Её адрес остаётся во внутренней сети или за VPN, а наружу смотрит только сервер с понятным адресом. Если сервер арендован у провайдера VPS, то у него есть публичный IP, root-доступ и предсказуемое окружение. Для одного потока от такого шлюза обычно не нужно много: стартовать разумно с небольшого плана вроде Ba (1 vCPU, 2 ГБ RAM, 40 ГБ NVMe) или Uab (2 vCPU, 4 ГБ), а дальше смотреть на реальную загрузку. Ресурсы тарифа — отправная точка для теста, а не обещание, сколько зрителей выдержит сервер. Кстати, если вы раньше жили с nginx-rtmp и думаете о смене, отдельно о цене такого перехода написано в материале про MediaMTX вместо nginx-rtmp.

Один нюанс по схеме. Шлюз должен дотягиваться до камеры. Если камера в локальной сети склада, а сервер в дата-центре, между ними нужен защищённый маршрут: VPN, туннель или проброс, который вы готовы поддерживать и защищать. Прямая публикация порта камеры в интернет ради удобства — плохая сделка: у таких устройств обновления прошивок редки, а пароли по умолчанию распространены.

Кодек: вопрос, который задают раньше сети

Самая распространённая ошибка новичка выглядит так: «MediaMTX умеет WebRTC, значит любой поток с камеры в нём заиграет». Это не так. Смена транспорта и смена кодека — два разных действия. В документации по перекодированию это сказано прямо: сама по себе смена транспортного протокола не обеспечивает совместимости видеокодека с браузером, а для изменения кодека и сжатия предлагаются FFmpeg или GStreamer вместе с MediaMTX (перекодирование в MediaMTX).

Поэтому правильный порядок такой: сначала выясняем, что именно кодирует камера, и только потом занимаемся сетью и страницей. Иначе вы будете полдня отлаживать порты, а виновата окажется камера, выдающая видео в формате, который выбранный браузерный путь не воспроизводит.

Что надо узнать о камере до первого запуска:

  • какой видеокодек использует основной поток и какой — дополнительный (у многих камер есть оба, и они часто настроены по-разному);
  • есть ли звук и каким кодеком он закодирован: бывает, что видео подходит, а звуковая дорожка нет;
  • какие профили и параметры поток предлагает в веб-интерфейсе камеры;
  • можно ли в настройках камеры выбрать другой кодек, не трогая шлюз.

Последний пункт самый недооценённый. Если камера умеет выдавать поток в формате, подходящем браузеру, то проще всего переключить его в самой камере. Тогда шлюз вообще ничего не перекодирует и почти не расходует процессор. Перекодирование — запасной вариант, а не стартовая точка. Какой именно кодек подойдёт вашему браузерному клиенту, проверяйте на ваших браузерах и устройствах: общих гарантий «работает везде» здесь давать нельзя.

Практический способ различить причины сбоя: если страница открывается, соединение устанавливается, а картинки нет или она чёрная — подозревайте кодек и дорожки. Если соединение даже не устанавливается — это уже сеть или права. Эта граница потом сэкономит вам много времени.

Страница и право на видео — это два разных замка

Теперь про кабинет. Допустим, вы сделали страницу с видеоплеером, поставили её за логин, и сотрудники входят по паролю. Кажется, всё закрыто. Но задайте один вопрос: что будет, если человек из страницы вытащит адрес медиасоединения и откроет его напрямую, минуя кабинет? Страница закрыта, а вот поток — не обязательно.

Здесь легко споткнуться об идею «скрытой ссылки»: путь потока длинный, случайный, нигде не опубликован, значит, посторонний не найдёт. Но ссылка, которая видна клиентскому коду, перестаёт быть секретом в момент первого просмотра. Её можно переслать, она останется в истории и логах. Скрытый адрес — это неудобство для злоумышленника, а не авторизация.

Поэтому разделим две задачи:

  1. выдача страницы — кто вообще может зайти в кабинет;
  2. право получать медиа — кто может прочитать конкретный поток с шлюза.

MediaMTX решает вторую задачу собственными средствами. По документации предусмотрены внутренние пользователи, внешний HTTP-обработчик авторизации и JWT с JWKS. В правах (mediamtx_permissions) задаются действие и путь потока, а issuer и audience проверяются, если это включено в настройке (аутентификация в MediaMTX).

Как это выглядит на нашем примере. Три варианта, от простого к тонкому:

Способы закрыть чтение потока
СпособКогда подходитСлабое место
Внутренний пользователь с паролемОдин-два постоянных зрителя, например служебная панельПароль приходится передавать клиенту; смена пароля бьёт по всем сразу
Внешний HTTP-обработчикРешение «пускать или нет» принимает ваш кабинет при каждом подключенииОбработчик становится частью пути к видео: если он недоступен, видео тоже
JWT с JWKSКабинет выдаёт короткоживущий токен под конкретного человека и конкретный потокНужно правильно настроить проверку issuer и audience и следить за сроком токена

Для нашего кабинета логичнее всего третий вариант или второй. Идея в том, что кабинет, зная, кто вошёл, выпускает токен, в котором записано действие «читать» и путь sklad-vhod. Браузер приносит этот токен на шлюз, а шлюз проверяет подпись по публичным ключам и сверяет права. Тогда, даже если кто-то скопирует адрес, без действующего токена он ничего не получит. Что именно в правах записать и как настроить проверку, смотрите в документации той версии MediaMTX, которую вы ставите: точные имена параметров лучше сверять с ней, а не с памятью.

Заметьте, что токен должен быть привязан к конкретному потоку. Токен «читать всё» для сотрудника склада, которому нужна одна камера, — это та же ошибка в профиль: замок есть, но открывает все двери. Если завтра появится вторая камера в бухгалтерии, у человека со склада не должно появиться к ней доступа только потому, что она подключена к тому же серверу.

И ещё одна деталь про срок жизни. Токен, выданный на недели, ведёт себя почти как постоянная ссылка. Короткий срок неудобен тем, что плееру надо запрашивать новый токен, зато утёкшая копия быстро теряет ценность. Баланс выбирает владелец кабинета; важно просто сделать этот выбор осознанно.

Что проверить на этом этапе. Откройте поток без токена — должен быть отказ. Откройте с токеном другого пути — тоже отказ. Откройте с токеном нужного пути — видео идёт. Три простых проверки покажут, что замок на потоке стоит именно там, где вы думаете.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Медиасоединение: открытый HTTPS ничего не доказывает

Вот сцена, которую видели многие. Страница кабинета загружается, плеер на месте, кнопка работает, статус соединения мигает — и видео не появляется. Сайт по HTTPS открыт, сертификат в порядке, и кажется, что с сетью всё хорошо. Но у WebRTC несколько путей, и «открыт 443-й порт» — это про один из них.

В документации LiveKit про порты и firewall хорошо разведены эти пути: сигнализация и API, ICE по UDP и TCP, а также TURN используют разные сетевые маршруты. Открытый HTTPS сам по себе не доказывает, что медиатрафик дойдёт, а точные порты зависят от конфигурации (порты и firewall в LiveKit). Это документация другого продукта, но принцип у WebRTC общий: сначала стороны договариваются о соединении, а затем медиа идёт по отдельному пути, который тоже должен быть доступен.

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

Что из этого следует для нашего шлюза на VPS:

  • порты, которые использует ваша конфигурация MediaMTX для WebRTC-медиа, нужно открыть и в файрволе самого сервера, и в сетевых правилах на стороне хостинга, если они есть; конкретные номера берите из своего файла настроек, а не из чужих инструкций;
  • сотрудник за корпоративным файрволом или мобильной сетью может не пройти по тем путям, которые доступны вам при тестировании из дома;
  • ретрансляция через TURN нужна там, где прямой путь невозможен; это отдельный компонент со своими портами, и о нём надо думать заранее, а не после жалоб пользователей;
  • если шлюз стоит за NAT или за обратным прокси, адрес, который он объявляет клиенту, может отличаться от того, по которому его видят снаружи, — такие расхождения надо искать первыми.

Обратите внимание на третий пункт и тестирование «только изнутри». Если вы проверяли кабинет с того же компьютера, где сидит админ, и всё работало, это ничего не говорит о сотруднике на другом провайдере. Проверка из внешней сети — отдельный шаг, а не формальность. Возьмите телефон в мобильной сети, откройте кабинет и посмотрите, дойдёт ли картинка.

Для диагностики пригодится простая таблица симптомов. Она не заменяет логов, но помогает не гадать:

Симптом и вероятная область поиска
Что видит сотрудникКуда смотреть в первую очередь
Страница не открываетсяВеб-сервер кабинета, сертификат, авторизация входа
Страница есть, плеер пишет об отказе в доступеТокен, его срок, путь потока, issuer и audience
Доступ разрешён, соединение зависает на установкеПорты медиа, файрвол, NAT, необходимость TURN
Соединение установлено, картинки нет или она чёрнаяКодек и дорожки камеры, а не сеть
Видео идёт, но рывкамиКанал между камерой и шлюзом, нагрузка на процессор, параметры потока

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

Когда без перекодирования не обойтись

Допустим, вы проверили камеру и выяснили: основной поток закодирован так, что выбранный браузерный путь его не воспроизводит, а переключить кодек в настройках камеры нельзя. Или звук закодирован неподходящим форматом. Вот тогда, и только тогда, у шлюза появляется вторая роль — преобразователь.

MediaMTX сам по себе не превращает любой кодек в любой другой. Документация предлагает для этого внешние инструменты: FFmpeg или GStreamer рядом с MediaMTX (перекодирование в MediaMTX). Схема получается такая: камера отдаёт поток, внешний процесс его перекодирует и публикует обратно в MediaMTX как новый путь, а браузер читает уже этот преобразованный путь. Конкретные команды и параметры подбирайте по документации инструментов и по вашей камере: универсального рецепта, подходящего всем, нет.

Теперь про цену. Перекодирование видео — это работа процессора, и она заметно отличается от простой пересылки. Шлюз, который раньше почти ничего не делал, начинает считать. Здесь план сервера перестаёт быть формальностью: для транскодирования разумно взять больше ядер, например присмотреться к Heka (4 vCPU, 8 ГБ) или выше, чем для простой ретрансляции. Но «разумно присмотреться» не значит «хватит ровно столько-то потоков»: замеряйте на своём видео, своём разрешении и своей частоте кадров. GPU в предложении не заявлен, поэтому считайте, что нагрузка ляжет на процессор.

Несколько правил, которые помогают не превратить транскодирование в источник проблем:

  • Перекодируйте один раз. Если зрителей пятеро, преобразованный поток публикуется в MediaMTX единожды, а читают его все. Перекодировать отдельно под каждого — самый быстрый способ загрузить процессор.
  • Не трогайте то, что уже подходит. Иногда несовместим только звук. Тогда имеет смысл преобразовывать звуковую дорожку, а видео оставить как есть, если используемый инструмент позволяет так сделать. Экономия процессора будет ощутимой.
  • Уменьшайте поток там, где это оправдано. Для просмотра в кабинете полное разрешение камеры бывает не нужно. Но это решение о качестве, и принимает его тот, кто будет смотреть видео, а не тот, кто настраивает сервер.
  • Следите за самим процессом. Внешняя программа может упасть, и тогда преобразованный путь пропадёт, хотя MediaMTX продолжит работать. Нужен автоматический перезапуск и понятный признак того, что путь жив.

Отдельно стоит сказать про права. Преобразованный путь — это новый путь, и права на чтение для него нужно выдать так же внимательно, как для исходного. Типичная ошибка: закрыли sklad-vhod, а рядом появился sklad-vhod-web без ограничений, потому что про него забыли. Закрытый кабинет с открытым побочным путём остаётся открытым.

Один поток, один кабинет, один понятный маршрут

Соберём наш условный пример целиком и пройдём путь так, как его проходит видео.

Камера на складе отдаёт RTSP во внутренней сети. MediaMTX на VPS забирает поток как static source по защищённому маршруту; камера по-прежнему не видна из интернета. Если кодек подходит браузеру, поток просто становится доступным по WebRTC. Если не подходит — рядом работает FFmpeg или GStreamer, публикует преобразованный путь, и браузер читает именно его. Сотрудник входит в кабинет, кабинет проверяет его право и выдаёт токен на чтение единственного нужного пути. Браузер приносит токен шлюзу, шлюз сверяет права, и медиасоединение устанавливается по портам, которые заранее открыты и проверены из внешней сети.

У каждого преобразования здесь свой владелец и своё место:

  • RTSP в WebRTC — доставка, её делает MediaMTX;
  • один кодек в другой — отдельная работа внешнего инструмента, и нужна она только когда камера не может отдать совместимый поток;
  • «человек вошёл» в «человек может читать этот путь» — задача кабинета и токена;
  • «сервер слышит клиента» в «видео доходит до клиента» — задача сетевых правил, и проверять её нужно снаружи.

Когда эти четыре шага названы по именам, отладка перестаёт быть угадайкой. Чёрный экран — это второй пункт. Отказ в доступе — третий. Бесконечное подключение — четвёртый. Страница, которая не открылась, — вообще не про видео.

Начинать стоит с малого: один поток, один пользователь, одна проверка из мобильной сети. Для такой пробы подойдёт самый скромный план, а если появится транскодирование, всегда можно перейти на тариф побольше и сравнить загрузку процессора до и после. Видео должен видеть тот, кому оно предназначено, и не должен видеть никто другой, а в схеме не должно остаться ни одного преобразования, про которое вы не можете сказать, где оно происходит и кто за него отвечает.