Назад в блог

RTSP: как протокол 1998 года остаётся основным способом забирать видео с IP-камер

Почему стандарт, созданный для потокового мультимедиа в конце 90-х, до сих пор лежит в основе захвата видео с IP-камер, какие компромиссы заложены в его архитектуру и какие особенности реального оборудования стоит учитывать. С отсылками к RFC и пометками о том, что нужно проверять на своих камерах.

Введение

Сегодня видеоплатформы доставляют зрителю видео через QUIC и WebTransport, запускают нейросети на Edge-устройствах и декодируют потоки в браузере средствами WebCodecs. При этом на первом шаге пути видео, там, где кадр впервые выходит из камеры, во многих системах по-прежнему стоит RTSP, созданный ещё в конце прошлого века.

RTSP (Real-Time Streaming Protocol) описан в RFC 2326, который вышел в апреле 1998 года. Это раньше, чем появились Google (основан в сентябре 1998-го) и YouTube (2005). Подавляющее большинство IP-камер и видеорегистраторов умеют отдавать видео по RTSP, часто наряду с другими способами (ONVIF, HTTP-потоки, облачные протоколы вендора). Универсальной замены именно для задачи «забрать поток с устройства» пока не видно. Поэтому понимание RTSP остаётся базовым навыком для видеоинженера.

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

О точности. Всё, что относится к стандартам (RFC, ONVIF), я сверял с первоисточниками и даю ссылки. То, что касается поведения конкретных камер, чаще всего опирается на форумы и рассылки, а не на документацию вендоров. Такие места я помечаю как ⚠ проверить: их стоит воспроизвести на вашей модели и прошивке, прежде чем закладываться на них в коде.

Для чего был создан RTSP

Хаос потокового видео 1990-х

Массовое потоковое аудио в интернете началось с RealAudio, вышедшего в апреле 1995 года. Рядом развивались и другие платформы. По обзору истории отрасли, над собственными решениями работали Vivo, Xing, VDOnet, VXtreme и Microsoft (Streaming Media Blog). Единого стандарта не было, у каждого вендора были свои клиент, сервер и формат:

  • RealNetworks (до 1997 года Progressive Networks) начинали с собственного протокола PNA. Позже в продуктах Real использовался RTSP для управления и собственный формат передачи данных RDT. Про RDT я нашёл упоминания только во вторичных источниках, поэтому ⚠ проверить.
  • Microsoft выпускала NetShow и протокол MMS (Microsoft Media Server). Позже Windows Media Server 9 получил поддержку RTSP (AppleInsider, 2009).
  • Apple выпустила QuickTime 4 и QuickTime Streaming Server с «настоящим» RTSP в 1999 году (там же).
  • Vivo, Xing, VDOnet предлагали собственные платформы.

Плеер одного вендора, как правило, не работал с сервером другого. Серверный софт был привязан к конкретной экосистеме.

Почему HTTP не подходил

В самом RFC 2326 перечислено, чем RTSP отличается от HTTP/1.1, на который он синтаксически похож (RFC 2326, §1.1): RTSP-серверу почти всегда нужно хранить состояние; запросы могут отправлять и клиент, и сервер; данные передаются вне основного соединения, другим протоколом. Отсюда практические причины, по которым простого GET для потока было мало:

  1. Состояние. Потоковая сессия имеет жизненный цикл: клиент подключился, настроил транспорт, запустил воспроизведение, поставил на паузу, отключился. HTTP/1.x не хранит состояние между запросами.
  2. Управление воспроизведением. В семантике HTTP нет операций PLAY, PAUSE, TEARDOWN и позиционирования по времени (в RTSP это заголовок Range).
  3. Multicast. В 1990-х для вещания активно применяли IP-multicast: одна копия потока на сеть, а не на каждого клиента. RTSP умеет объявлять multicast-группу в SETUP, а HTTP с ней не работает.
  4. Транспорт. Медиа передаётся отдельным протоколом (RTP) поверх UDP, TCP или multicast. HTTP/1.x жёстко привязан к TCP. (Сейчас есть HTTP/3 поверх QUIC, но к тому времени сложившиеся подходы к стримингу уже были другими.)

Создание RTSP

RFC 2326 написали три автора: Хеннинг Шульцринне (Columbia University), А. Рао (Netscape) и Роберт Лэнфиер (RealNetworks) — так указано в шапке документа (RFC 2326). Участие Netscape и RealNetworks согласуется и с историей отрасли: по данным AppleInsider, Real сотрудничала с Netscape над тем, что стало RTSP. Шульцринне также соавтор RTP и SIP. Задача протокола — универсальный «сетевой пульт» (в RFC так и написано: network remote control) для медиасерверов.

В протокол заложили четыре решения, которые во многом объясняют его долгую жизнь:

  • Разделение управления и данных. RTSP отвечает за сессию, а медиа едет по RTP (RFC 1889, январь 1996; сейчас действует RFC 3550). RTSP не знает, что именно в пакетах: кодек описывается в SDP и в спецификации payload format (для H.264 это RFC 6184, для H.265 — RFC 7798). Поэтому один и тот же RTSP пережил смену кодеков.
  • Синтаксис HTTP. Те же текстовые методы, заголовки и коды ответов. Для реализаторов это упрощало жизнь: многое можно было переиспользовать.
  • Состояние сессии. Сервер связывает клиента с сессией через заголовок Session. Состояния: Init, Ready, Playing (и Recording). Отсюда и аналогия с видеомагнитофоном.
  • Независимость от транспорта. В одном SETUP клиент и сервер договариваются, как доставлять данные: RTP по UDP (unicast), по TCP внутри RTSP-соединения (interleaved), либо multicast.

Дальше вышел RTSP 2.0 (RFC 7826, декабрь 2016). Он заменяет RFC 2326, но, как сказано в самом документе, не совместим с 1.0, кроме механизма согласования версии (RFC 7826, Appendix I). Среди изменений: убраны RECORD и ANNOUNCE, убран RTSP по UDP, добавлены поддержка IPv6, конвейерная отправка запросов (Pipelined-Requests), асинхронный PLAY_NOTIFY, а также схема rtsps для RTSP поверх TLS.

На практике 1.0 остался основным. Почему так вышло, документально не зафиксировано. Вот причины, которые выглядят правдоподобно (это мои выводы, а не цитаты из первоисточников):

  1. Несовместимость. Новый клиент не мог бы просто «заговорить» со старой камерой, а старые NVR не понимали бы новые камеры. Для миллионов уже установленных устройств этот барьер высок.
  2. Библиотеки. В 2016 году автор Live555 Росс Финлейсон писал в рассылке, что SRTP для RTSP определён только для RTSP 2.0, «которую почти никто, включая нас, не реализует» (live-devel, май 2016). Пример из свежего: новый GStreamer-элемент rtspsrc2 в списке возможностей указывает «RTSP 1.0 support» (документация GStreamer).
  3. ONVIF. Консорциум основан в 2008 году, то есть задолго до RFC 7826. ONVIF не заменяет RTSP: его спецификация стриминга прямо строится на RFC 2326 и описывает расширения RTSP/RTP (ONVIF Streaming Spec 19.06). Но то, что ONVIF описывает и обнаружение устройств, и профили, и управление, и поток через уже существующий RTSP, снижало потребность в новом протоколе. Подробнее об ONVIF — в отдельной статье.

А что с шифрованием? Схема rtsps формально определена в RTSP 2.0 (RFC 7826, §22.14.2). В мире RTSP 1.0 способы защиты канала у вендоров разные, и сводить их в один «стандарт де-факто» я бы не стал. Например, спецификация ONVIF Media2 предусматривает RTSPS и SRTP (это следует из WSDL Media2 и спецификации ONVIF Core), а автор Live555 примерно в 2014 году писал, что библиотека RTSP поверх SSL не поддерживает (live-devel; с тех пор ситуация могла измениться). Поддержку в конкретной камере и в конкретном клиенте нужно проверять отдельно — ⚠ проверить.

Причины долголетия RTSP

Долгая жизнь RTSP объясняется несколькими факторами:

  1. Огромная установленная база. Точной статистики именно по RTSP нет. Для масштаба: IHS Markit оценивал, что к 2021 году в мире будет установлено более 1 млрд камер видеонаблюдения (Security Today, 2019). Это не только IP-камеры, и не все они отдают RTSP. Но порядок величины понятен: переписывать прошивки ради нового протокола никто не станет.
  2. Достаточность для основной задачи. Для «забрать поток с камеры и отдать в видеосервер внутри локальной сети» RTSP работает хорошо, и его слабые места (NAT, работа через открытый интернет) здесь не мешают. Камера не обязана раздавать видео многим зрителям: её задача — донести поток до видеосервера (VMS), а масштабирование и шифрование для интернета делает уже он. Это моё архитектурное суждение, а не требование стандарта.
  3. Сетевой эффект. RTSP поддерживают библиотеки FFmpeg и GStreamer, многие VMS и NVR, а спецификации ONVIF строятся на нём. Ретрансляторы вроде go2rtc (его использует, например, Frigate) тоже принимают RTSP. Стоимость перехода всей индустрии на другой протокол на сегодня выше выгоды.

Последние 15 лет индустрия развивала в основном протоколы доставки видео от сервера к зрителю:

ПротоколГодЗаявленная цельСпецифика и ограничения
HLS2009 (RFC 8216 — 2017)Стриминг через обычные HTTP-серверыApple рекомендует сегменты по 6 с, а по спецификации плеер отступает от «живого края» не меньше чем на три длительности сегмента. Типичная задержка — десятки секунд.
MPEG-DASH2012 (ISO/IEC 23009-1)Адаптивное вещание по манифестамТа же идея «сегменты и манифест», что у HLS; не расширение HLS, а самостоятельный стандарт.
WebRTC2011 (первый релиз), 2021 (W3C Recommendation)Real-time связь между браузерамиЗадержка обычно доли секунды (зависит от сети и настроек). Нужен сигнальный сервер.
LL-HLS2019 (анонс на WWDC19)HLS с низкой задержкойЦелевая задержка — 1–2 с, нужна поддержка частичных сегментов на стороне CDN и источника.
SRT2017 (открытый код от Haivision)Надёжная доставка по UDP в нестабильных сетяхЧасто применяют для передачи видео между студиями и площадками.
WHIP2025 (RFC 9725)HTTP-сигнализация для отправки видео в WebRTCСтандартный способ «подать» поток на сервер по WebRTC.
MoQВ разработке (IETF)Pub/Sub доставка поверх QUIC и WebTransportНа 2026-10-01 это Internet-Draft draft-ietf-moq-transport-22, статус «WG Document», не стандарт.

Источники по таблице: Apple WWDC19, хронология HLS и DASH, Wikipedia: WebRTC, Wikipedia: SRT, datatracker: MoQ Transport, RFC 9725 (по обзору).

Строго говоря, ни один из этих протоколов не заточен под первичный съём потока с самого устройства-камеры, как это делает RTSP. Но нельзя сказать, что ingest — территория исключительно RTSP: SRT и WebRTC (через WHIP) используют для подачи потока в вещательные и облачные системы. Просто в IP-камерах поддержка у них пока редкость, а RTSP есть почти везде.

RTSP не нужно воспринимать как легаси, которое обязательно будут заменять. Это инфраструктурный слой с понятными границами. Если облачный сценарий требует другого, камеру обычно подключают через ретранслятор или Edge-ноду.

Структура RTSP: сигнализация, а не транспорт

RTSP не передаёт видео сам по себе. Это протокол управления медиасессиями (signaling). Передача медиа — задача RTP (Real-time Transport Protocol), а статистика и синхронизация — задача RTCP (RTP Control Protocol). В статье про RTP я разбираю, как устроен этот слой.

RTSP Signaling, RTP and RTCP Flow

Аналогия: RTSP — пульт от телевизора. Кнопки «включить» (PLAY), «переключить дорожку» (SETUP для другого trackID), «выключить» (TEARDOWN). Картинка же приходит по «HDMI-кабелю» (RTP), а не по инфракрасному лучу пульта. Аналогия неточна в одном: в режиме interleaved «кабель» и «пульт» используют один и тот же TCP-канал.

RTSP синтаксически похож на HTTP: текстовые методы, заголовки, коды ответов. Фундаментальное отличие в том, что у RTSP-сервера есть состояние сессии (Init → Ready → Playing), и он помнит его для каждого клиента (RFC 2326, §1.7 и приложение A).

Рукопожатие (handshake): минимум четыре шага до первого кадра

Классическое подключение к камере по RTSP — это последовательность запросов, где каждый следующий опирается на ответ предыдущего: OPTIONS, DESCRIBE, SETUP, PLAY. Но «четыре» — это скорее нижняя планка, чем жёсткое правило:

  • OPTIONS в стандарте необязателен: его можно отправить в любой момент, чтобы узнать поддерживаемые методы (RFC 2326, §10.1). Многие клиенты всё равно делают его первым.
  • SETUP выполняется для каждого трека отдельно: видео и аудио — это два запроса.
  • Если камера требует авторизацию, то первый запрос без учётных данных вернёт 401 Unauthorized с параметрами аутентификации, и клиент повторит его. Это ещё один круг.
  • Перед первым запросом нужно установить TCP-соединение (ещё один RTT).

Целиком параллельно цепочку не выполнить: для SETUP обычно нужен SDP из ответа на DESCRIBE (в нём адреса треков), а для PLAY нужен Session, который выдаёт ответ на SETUP. Но стандарт не запрещает конвейерную отправку (pipelining) независимых запросов в одном TCP-соединении: ответы приходят в том же порядке (RFC 2326, §9.1). В RTSP 2.0 для быстрого старта добавили заголовок Pipelined-Requests, чтобы несколько SETUP можно было отправить подряд ещё до того, как появился Session (RFC 7826, §18.33).

Для 1998 года такая схема была логичной. Современным требованиям к задержке она создаёт свои особенности:

  • Каждый запрос — это Round-Trip. Round-trip time (RTT) — время, за которое пакет доходит до камеры и возвращается с ответом. Считаем: TCP-соединение (1) + OPTIONS (1) + DESCRIBE (1) + два SETUP (2) + PLAY (1) = 6 RTT, а с запросом авторизации — 7. При пинге 100 мс на «согласование» уйдёт 0,6–0,7 с ещё до того, как камера начнёт слать RTP. Если пропустить OPTIONS, получится 5 RTT. Это арифметика на «чистом» сценарии; TLS, переадресации и медленная камера добавят своё.
  • Трафик текстовый. Сообщения читаются глазами и логируются без специальных инструментов. Но сами они небольшие, и задержку определяет не их размер, а число RTT.
  • Несколько потоков. В классическом варианте команды идут по TCP, видео — по UDP (RTP), метрики — по UDP (RTCP). Приходится следить за несколькими портами сразу. В режиме interleaved всё это едет по одному TCP-соединению.

Последовательность команд до первого кадра

1. OPTIONS: «Покажи меню»

Запрос показывает, какие методы поддерживает камера.

Запрос клиента:

OPTIONS rtsp://192.168.1.100:554/stream1 RTSP/1.0
CSeq: 1
User-Agent: MyVSaaS/1.0

Ответ камеры:

RTSP/1.0 200 OK
CSeq: 1
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

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

  • Узнать, что умеет камера. Особенно полезно, чтобы выбрать способ keep-alive: наличие GET_PARAMETER в Public — прямой сигнал, что его можно использовать. FFmpeg поступает именно так (см. раздел про keep-alive).
  • Совместимость. Встречаются сообщения, что отдельные камеры капризничают, если OPTIONS пропущен. Подтверждающих документов я не нашёл, поэтому ⚠ проверить на своей модели.
  • SET_PARAMETER. В RTSP 2.0 сервер обязан его поддерживать, а для keep-alive он рекомендован (RFC 7826, §10.5). В RTSP 1.0 это необязательный метод, и что именно можно менять через него, зависит от вендора.

2. DESCRIBE: «Что ты умеешь стримить?»

Клиент запрашивает «паспорт» медиапотока. Ответ приходит в виде SDP (Session Description Protocol).

Запрос клиента:

DESCRIBE rtsp://192.168.1.100:554/stream1 RTSP/1.0
CSeq: 2
Accept: application/sdp

Если камера требует авторизацию, сначала придёт 401 Unauthorized с заголовком WWW-Authenticate (например, Digest realm="...", nonce="..."). Клиент повторит DESCRIBE с заголовком Authorization, и CSeq в повторе будет уже следующим.

Ответ камеры:

RTSP/1.0 200 OK
CSeq: 2
Content-Base: rtsp://192.168.1.100:554/stream1/
Content-Type: application/sdp
Content-Length: 517

v=0
o=- 1609459200 1609459200 IN IP4 192.168.1.100
s=Media Presentation
c=IN IP4 0.0.0.0
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H265/90000
a=fmtp:96 sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwBdlZgJ; sprop-sps=QgEBAWAAAAMAkAAAAwAAAwBdoAKAgC0WWVmkkyuagICAgg==; sprop-pps=RAHBcrRiQA==
a=control:trackID=1
m=audio 0 RTP/AVP 97
a=rtpmap:97 MPEG4-GENERIC/16000/1
a=fmtp:97 streamtype=5; profile-level-id=1; mode=AAC-hbr; sizeLength=13; indexLength=3; indexDeltaLength=3; config=1408
a=control:trackID=2

Значения sprop-* здесь условные, приведены для наглядности и не предназначены для декодирования. Content-Length посчитан в байтах с учётом CRLF (\r\n) в конце строк, как в настоящем сообщении.

Ключевые параметры:

  • Content-Base — база для относительных a=control:. Клиент собирает URL для SETUP из Content-Base и значения control: rtsp://192.168.1.100:554/stream1/ + trackID=1.
  • a=rtpmap:96 H265/90000 — динамический payload type 96 соответствует H.265 с тактовой частотой 90 кГц. Номера 96–127 не закреплены за кодеками, а задаются именно здесь, в SDP (RFC 3551). Правило для H.265 — часы 90000 (RFC 7798).
  • sprop-vps, sprop-sps, sprop-pps — параметры кодека в Base64. В теории их хватает, чтобы инициализировать декодер ещё до первого кадра. На практике параметры могут быть и в самом потоке (подробности ниже).
  • a=rtpmap:97 MPEG4-GENERIC/16000/1 и a=fmtp:97 ... mode=AAC-hbr ... config=1408 — аудио AAC. Строка config — hex-представление AudioSpecificConfig. Для AAC-LC, 16 кГц, моно это 1408: 5 бит тип объекта (2), 4 бита индекс частоты (8 = 16 кГц), 4 бита число каналов (1) и 3 нулевых бита (RFC 3640, §3.3.6).
  • trackID=1 и trackID=2 — отдельные дорожки (видео и аудио). SETUP нужно выполнять для каждой дорожки отдельно.

3. SETUP: «Договариваемся, куда слать данные»

Клиент сообщает, как он хочет получать медиа: по UDP на указанные порты или по TCP внутри RTSP-соединения.

Вариант А: UDP (раздельные каналы)
SETUP rtsp://192.168.1.100:554/stream1/trackID=1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=50000-50001
Вариант B: TCP Interleaved (мультиплексирование внутри RTSP-соединения)
SETUP rtsp://192.168.1.100:554/stream1/trackID=1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

Ответ камеры (на примере UDP):

RTSP/1.0 200 OK
CSeq: 3
Session: 7A3B9C2D;timeout=60
Transport: RTP/AVP;unicast;client_port=50000-50001;server_port=6970-6971;ssrc=A1B2C3D4

Критически важный заголовок:

  • Session: 7A3B9C2D;timeout=60 — камера выдала идентификатор сессии и сообщила таймаут (60 секунд). Параметр timeout допустим только в ответах, а если его нет, действует значение по умолчанию — те же 60 секунд (RFC 2326, §12.37). С этого момента идентификатор нужно указывать во всех последующих запросах.

Второй SETUP для аудио отправляется уже с Session. Иначе камера создаст отдельную сессию:

SETUP rtsp://192.168.1.100:554/stream1/trackID=2 RTSP/1.0
CSeq: 4
Session: 7A3B9C2D
Transport: RTP/AVP;unicast;client_port=50002-50003

(Для варианта B будет interleaved=2-3.) Какие номера каналов использовать, выбирает клиент: числа 0–1 для первой дорожки и 2–3 для второй — просто общепринятая схема, а не требование.

4. PLAY: «Поехали!»

Финальная команда запускает передачу.

Запрос клиента:

PLAY rtsp://192.168.1.100:554/stream1/ RTSP/1.0
CSeq: 5
Session: 7A3B9C2D
Range: npt=now-

Для живого потока с камеры часто пишут Range: npt=0.000-, но в грамматике RFC 2326 для «текущего момента» есть значение now; в примерах спецификации ONVIF для живых потоков используется как раз npt=now- (RFC 2326, §3.6, ONVIF Streaming Spec, §5.2.1.1.2). Некоторые камеры игнорируют этот заголовок — ⚠ проверить.

Ответ камеры:

RTSP/1.0 200 OK
CSeq: 5
Session: 7A3B9C2D;timeout=60
Range: npt=0.000-
RTP-Info: url=rtsp://192.168.1.100:554/stream1/trackID=1;seq=21345;rtptime=1904583350,url=rtsp://192.168.1.100:554/stream1/trackID=2;seq=48122;rtptime=2511873

Заголовок RTP-Info сообщает для каждой дорожки начальный seq и rtptime (RFC 2326, §12.33). Он нужен для синхронизации аудио и видео, и мы вернёмся к нему в статье про RTP. После ответа камера начинает слать RTP-пакеты.

Картинка появится не в миллисекунду ответа. Декодеру нужен первый IDR-кадр (ключевой кадр) и параметры кодека. Если камера отправила IDR прямо перед PLAY, а следующий придёт через 2 секунды, зритель будет ждать эти 2 секунды. Поэтому интервал I-кадров на камере влияет на время старта: в документации Frigate рекомендуют ставить его равным частоте кадров и избегать «умных» режимов вроде H.264+/H.265+, так как длинный интервал задерживает первый декодируемый кадр (Frigate: go2rtc troubleshooting).

Выбор транспорта передачи данных в RTSP

UDP: быстро, но хрупко

RTP-пакеты уходят в отдельные UDP-сокеты: один порт для данных (RTP), соседний для статистики (RTCP). Чётный порт для RTP и нечётный для RTCP — давняя рекомендация, но не обязательное правило (RFC 3550, §11).

  • Когда хорошо работает: камера и сервер в одной стабильной проводной сети (LAN). Потери невелики, задержка минимальна, на пути нет преград.
  • Когда ломается:
    • NAT и межсетевые экраны. Клиент указывает в SETUP свои UDP-порты, камера шлёт на них пакеты. Если клиент за NAT или файрволом, который не пропускает «чужие» входящие UDP, поток не дойдёт. В корпоративных сетях такие правила обычны.
    • Потери на ненадёжных каналах (например, Wi-Fi). Любая потеря RTP-пакета означает артефакты или замирание до следующего IDR. Какая потеря типична, зависит от конкретной сети, и общей цифры я приводить не буду: измеряйте RTCP-статистикой на своей площадке.

TCP Interleaved: надёжно, но с нюансами

RTP-пакеты упаковываются в то же TCP-соединение, по которому идут RTSP-команды. Блоки отделяются символом $ (0x24):

$ [channel: 1 байт] [length: 2 байта, big-endian] [RTP- или RTCP-пакет]

Это описано в RFC 2326, §10.12. Любопытная деталь: стандарт говорит, что режим следует использовать только по TCP и «в целом избегать, пока он не необходим». В реальных системах необходимость возникает почти сразу (NAT, файрвол), поэтому он повсеместно поддержан. Например, спецификация ONVIF требует от устройства поддержки RTP по UDP и RTSP-туннеля по HTTP, а interleaved по TCP указывает как желательный (should) (ONVIF Streaming Spec, §5.1.1).

Номера каналов выбирает клиент в SETUP (параметр interleaved=0-1). Обычная схема: канал 0 — RTP видео, канал 1 — RTCP видео, 2 — RTP аудио, 3 — RTCP аудио. По умолчанию RTCP использует канал на единицу выше канала RTP.

  • Плюсы: проходит через NAT и файрволы, использует один сокет, а TCP доставляет данные по порядку.
  • Минусы: Head-of-Line blocking. Если потерян один TCP-сегмент, приложение не получит следующие данные, пока тот не будет передан повторно. Для живого видео это означает скачки задержки. Величина зависит от RTT и настроек TCP, и стабильной «50–200 мс» для всех сетей назвать нельзя. Кроме того, контроль перегрузок TCP может замедлять передачу при потерях.

В последние годы для задач доставки обсуждают протокол MoQ (Media over QUIC), который работает поверх QUIC и WebTransport с независимыми потоками. Он задуман так, чтобы потеря данных в одном потоке не задерживала остальные. Но на сегодня это Internet-Draft (draft-ietf-moq-transport-22), а не стандарт, и он не решает задачу съёма потока с камеры. Упоминаю его как направление, а не как готовое решение проблемы TCP.

Практическое правило

Для старта подключитесь через TCP Interleaved: он проще в эксплуатации и работает в большем числе сетей. На UDP имеет смысл переходить, когда вы (а) измерили задержку, (б) убедились, что потерь по RTCP нет, и (в) уверены, что на пути нет NAT и файрволов.

СценарийРекомендация
Топология и состояние сети неизвестныTCP Interleaved
Edge-нода за NAT или в другом зданииTCP Interleaved
Ненадёжный канал (например, Wi-Fi с потерями)TCP Interleaved
Стабильная проводная LAN, задержка некритичнаTCP Interleaved (проще)
Стабильная проводная LAN, критична минимальная задержкаUDP, после проверки потерь

Для справки: в FFmpeg транспорт задаётся параметром rtsp_transport (udp, tcp, udp_multicast, http). Если его не указать, по коду FFmpeg перебирает разрешённые варианты по очереди, начиная с UDP (исходники). Поэтому лучше задавать транспорт явно, а не рассчитывать на поведение по умолчанию.

Особенности реальных камер

В этом разделе собрано то, с чем сталкиваются при интеграции с реальным оборудованием. Я не буду называть долю китайских камер в проектах как факт: оценки разнятся, а доля зависит от региона и сегмента. Для ориентира: по оценке Mordor Intelligence, Hikvision и Dahua вместе занимали около 39% мирового рынка камер видеонаблюдения в 2025 году (источник, вторичный). Это не значит, что проблемы возникают только у них: реализации стандарта отличаются у всех производителей. Подтверждённые документами факты я отделяю от случаев «по сообщениям», для которых нужна проверка.

Особенность 1: SDP описывает состояние на момент DESCRIBE

SDP, полученный в ответе на DESCRIBE, соответствует настройкам камеры на этот момент. Стандарт не гарантирует, что кодек не изменится, пока сессия жива (например, из-за смены настроек в веб-интерфейсе). Сообщения о том, что в некоторых прошивках после такой смены в RTP уже идёт другой кодек, а SDP остаётся прежним, обсуждаются в сообществе, но публичного подтверждения по конкретным моделям я не нашёл — ⚠ проверить.

Практический вывод не зависит от вендора: считайте SDP ожиданием, а не гарантией. Проверяйте реальные данные: типы NAL-юнитов в потоке, наличие параметров, размеры кадров. При несоответствии переподключайтесь и заново запрашивайте DESCRIBE. Как различать H.264 и H.265 по NAL-заголовку, я подробно разбираю в статье про RTP.

Особенность 2: параметры кодека (VPS/SPS/PPS): в SDP, в потоке или и там и там

Спецификации допускают оба способа: параметры могут быть в SDP (sprop-parameter-sets для H.264 по RFC 6184, sprop-vps/sps/pps для H.265 по RFC 7798) и/или передаваться в самом потоке (RFC 6184, RFC 7798). Поэтому клиент должен уметь работать с обоими вариантами: брать параметры из SDP и обновлять их, если в потоке приходят новые. Конкретные случаи вроде «Dahua не присылает sprop-vps» или «у Hikvision кривой Base64-паддинг» я в публичных источниках не нашёл, так что отношусь к ним как к гипотезам — ⚠ проверить.

Не запускайте декодирование, пока нет комплекта параметров (VPS/SPS/PPS для H.265, SPS/PPS для H.264) и IDR-кадра. Кешируйте начало потока, пока не соберёте всё необходимое.

Особенность 3: адреса потоков у разных производителей

Стандарт не определяет путь в URL: всё, что после хоста и порта, для клиента непрозрачно. Порт по умолчанию — 554 (RFC 2326, §3.2). Запись учётных данных в URL (rtsp://user:pass@ip/...) тоже не из RTSP, это принятая в клиентах условность. Помните, что такие URL оседают в логах и конфигурациях.

ПроизводительТипичный формат RTSP URLИсточник
Hikvisionrtsp://<ip>:554/Streaming/Channels/101 (основной поток), .../102 (субпоток)Hikvision Portal
Dahuartsp://<ip>:554/cam/realmonitor?channel=1&subtype=0VisioForge (вторичный)
Axisrtsp://<ip>/axis-media/media.ampVisionAI Docs (вторичный)
Reolinkrtsp://<ip>:554/h264Preview_01_main (встречаются и другие варианты, зависит от модели)Frigate / сообщество
Univiewrtsp://<ip>:554/media/video1не нашёл источника, ⚠ проверить
Прочие брендыпути вроде /live, /stream1зависит от модели, ⚠ проверить

Если камера поддерживает ONVIF, нужный адрес лучше спрашивать у неё самой: в ONVIF для этого есть GetStreamUri, и угадывать путь не нужно. Как это делается и где ONVIF тоже расходится у разных вендоров, я описал в статье про ONVIF.

Особенность 4: аутентификация

В RFC 2326 серверам рекомендовано (SHOULD) реализовать Basic и Digest (RFC 2326, §16).

  • Basic. Логин и пароль передаются в виде Base64 от user:pass (RFC 2617). Это не шифрование и не «открытый текст» в строгом смысле, но декодируется тривиально, поэтому без TLS Basic по сути равен передаче пароля как есть.
  • Digest. Сервер присылает nonce, клиент отвечает хешем. Исходный алгоритм — MD5. Современная редакция (RFC 7616) добавляет SHA-256, а MD5 оставляет только для совместимости и помечает NOT RECOMMENDED (RFC 7616). Какой алгоритм используют массовые камеры, статистики я не нашёл. Распространённое мнение, что подавляющее большинство камер по-прежнему на MD5, правдоподобно, но подтвердить его цифрой не могу.

Шифрование всего канала (rtsps) определено в RTSP 2.0, а в камерах с RTSP 1.0 способы и их поддержка отличаются — ⚠ проверить. Пока TLS не гарантирован, RTSP-поток в локальной сети передаётся в открытом виде. Изолируйте оборудование видеонаблюдения сегментацией сети (VLAN).

Особенность 5: нестабильные соединения

Здесь пример из публичных источников: в рассылке Live555 пользователь описывал, что сервер Axis через 60 секунд присылал RTCP BYE, если клиент не отправлял RTCP Receiver Reports (live-devel, 2008). Это согласуется со стандартом: RTCP-отчёты клиента считаются «признаком жизни» (см. следующий раздел). Другие истории (оборванный TCP без FIN у Hikvision, пустой payload у Reolink после перезагрузки) я не смог подтвердить документами или отчётами, поэтому не включаю их как факты — ⚠ проверить, если вы с ними столкнулись.

Независимо от причин стоит иметь watchdog на уровне приложения: если от камеры нет новых RTP-пакетов дольше заданного времени, закрыть сессию и переподключиться. Для типичных потоков 5–10 секунд — разумная отправная точка (это моё практическое правило, а не стандарт). Для камер с очень низким FPS и длинным интервалом между кадрами порог нужно делать больше. Не полагайтесь только на системные таймауты TCP.

Keep-alive: невидимая нить, которая держит сессию

Типичная ситуация при первой разработке:

  1. Соединение установлено, PLAY сработал, видео идёт.
  2. Приложение переключилось на обработку потока, декодирование или аналитику.
  3. Примерно через 60 секунд поток останавливается, а в консоли нет ошибок.

Причина. Сервер считает сессию неактивной. Стандарт описывает это так: сервер в состоянии Playing вправе закрыть сессию, если за интервал таймаута (по умолчанию минуту) не получил «признаков жизни» — RTSP-команд или RTCP-отчётов от клиента (RFC 2326, Appendix A). Сами RTP-пакеты с видео, которые идут от камеры к клиенту, в этот список не входят.

Спецификация стриминга ONVIF формулирует то же самое: для удержания сессии клиент должен вызвать любой RTSP-метод или отправлять RTCP Receiver Reports, а SET_PARAMETER рекомендован (ONVIF Streaming Spec, §5.2.1.1.1). RTSP 2.0 даёт тот же порядок предпочтений: RTCP, затем SET_PARAMETER, GET_PARAMETER и OPTIONS (RFC 7826, §10.5).

Решение. Периодически посылать keep-alive. На практике чаще всего используют GET_PARAMETER:

GET_PARAMETER rtsp://192.168.1.100:554/stream1/ RTSP/1.0
CSeq: 42
Session: 7A3B9C2D

Отправляйте его заметно раньше истечения таймаута. Берите значение timeout из ответа на SETUP, а при его отсутствии считайте 60 секунд. Хороший запас даёт половина таймаута, то есть примерно 30 секунд: именно так делает FFmpeg (libavformat/rtspdec.c). RFC 7826 не рекомендует таймауты короче 30 секунд, а клиенту советует обновлять сессию заранее.

Не все серверы поддерживают GET_PARAMETER, поэтому проверяйте Public в ответе на OPTIONS. FFmpeg выбирает так: если сервер перечислил GET_PARAMETER, он использует его, иначе OPTIONS. Метод SET_PARAMETER с пустым телом тоже используют: он рекомендован стандартами, а как его поймёт старая камера, зависит от прошивки.

Что делают популярные библиотеки. Утверждать, что они «не делают keep-alive», неверно, но есть нюансы:

  • FFmpeg отправляет keep-alive из цикла чтения пакетов, когда с последней команды прошла половина таймаута. Если ваше приложение перестало читать (например, из-за обратного давления в очереди), keep-alive тоже не уйдёт (исходник). Также FFmpeg отправляет RTCP Receiver Reports.
  • GStreamer (rtspsrc) имеет свойство do-rtsp-keep-alive, по умолчанию включено. По сообщению на форуме, при передаче по TCP keep-alive может не отправляться из-за того, что данные идут непрерывно (обсуждение). Это отчёт пользователя, а не документация, поэтому ⚠ проверить на вашей версии.
  • OpenCV обычно использует бэкенд FFmpeg или GStreamer, поэтому поведение определяется ими. Конкретную сборку нужно проверять.

Если вы пишете свой клиент, закладывайте keep-alive в собственный цикл, независимо от того, кто читает данные. Кроме того, отправка RTCP Receiver Reports заодно даёт камере обратную связь о потерях.

Корректное завершение: TEARDOWN

Когда поток больше не нужен, явно закройте сессию:

TEARDOWN rtsp://192.168.1.100:554/stream1/ RTSP/1.0
CSeq: 99
Session: 7A3B9C2D

Сессия RTSP не привязана к TCP-соединению. Это прямо отмечено в разделе безопасности RFC 2326: между транспортным соединением и сессией нет связи (RFC 2326, §16). Поэтому, если просто оборвать TCP, камера вправе держать сессию открытой до таймаута (по умолчанию 60 секунд).

На камерах бывают ограничения на число одновременных подключений. Конкретные цифры сильно различаются. Например, в документации одного открытого проекта указано «обычно 2–5» (NVIDIA live-vlm-webui, вторичный источник), а в даташитах конкретных моделей встречаются иные значения. Ищите лимит в документации своей модели. Если сервис перезапустился и не отправил TEARDOWN, «висячие» сессии могут занять доступные слоты, и новые подключения будут отклоняться, пока старые не истекут.

Что именно ответит камера при исчерпании лимита, зависит от прошивки. В RFC 2326 есть коды 503 Service Unavailable (для всех методов) и 453 Not Enough Bandwidth (для SETUP), но каким из них воспользуется ваша модель, я не нашёл в открытых источниках — ⚠ проверить. В любом случае, учитывайте такую ситуацию: повторяйте подключение с задержкой (backoff), а не в плотном цикле.

RTSP в современной экосистеме

RTSP занимает своё место в общей цепочке обработки видео:

RTSP в современной экосистеме

  • ONVIF — отдельный набор спецификаций (веб-сервисы на SOAP), а не «надстройка над RTSP». Он отвечает за обнаружение устройств (WS-Discovery), настройку, PTZ и события, а для самого видео возвращает RTSP-ссылку (GetStreamUri) и описывает расширения RTSP/RTP. Подробнее в статье про ONVIF.
  • WebRTC, HLS, DASH, MoQ — протоколы доставки медиа от сервера к зрителю (в браузер или приложение). Они начинаются там, где RTSP закончил свою работу. MoQ при этом пока остаётся черновиком IETF.
  • FFmpeg и GStreamer — библиотеки и фреймворки, которые скрывают рукопожатие RTSP. Для прототипа удобны, а в продакшене важно самим контролировать переподключения, keep-alive и особенности конкретных камер.

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

  • RTSP — протокол управления. Он отвечает за команды сессии, а видео едет по RTP.
  • Минимум четыре шага до первого кадра. Цикл OPTIONS → DESCRIBE → SETUP (на каждый трек) → PLAY. OPTIONS необязателен, авторизация добавляет круг, а после PLAY нужно дождаться IDR-кадра. Для примера с двумя дорожками это 6–7 RTT вместе с TCP-соединением.
  • Транспорт определяется задачей. Начинайте с TCP Interleaved, а на UDP переходите по измерениям в стабильной LAN.
  • SDP — это ожидание, а не гарантия. Сверяйте его с реальным RTP-потоком.
  • Keep-alive обязателен. Признаки жизни — RTSP-запросы и RTCP-отчёты клиента, а не входящий RTP. Проверьте, делает ли это ваша библиотека в вашем режиме транспорта.
  • TEARDOWN нужен. Сессия не привязана к TCP-соединению, и «висячие» сессии могут занять слоты камеры.
  • Не считайте слухи фактами. Всё, что я отметил как «проверить», стоит воспроизвести на своём оборудовании.

Источники

Стандарты и спецификации

Исходный код

История и рынок

Практика (форумы и документация проектов, не первоисточники)