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

Date: 2026-09-13
Author: Nikita Saytarov (RockEdge)
Source: https://www.rockedge.ru/blog/rtsp-protocol-overview/
Markdown: https://www.rockedge.ru/blog/rtsp-protocol-overview.md
Index: https://www.rockedge.ru/llms.txt
License: свободно использовать и развивать с указанием автора и источника

## Введение

Сегодня видеоплатформы доставляют зрителю видео через 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](https://www.streamingmediablog.com/2016/03/history-of-the-streaming-media-industry.html)). Единого стандарта не было, у каждого вендора были свои клиент, сервер и формат:

- **RealNetworks** (до 1997 года Progressive Networks) начинали с собственного протокола PNA. Позже в продуктах Real использовался RTSP для управления и собственный формат передачи данных RDT. Про RDT я нашёл упоминания только во вторичных источниках, поэтому **⚠ проверить**.
- **Microsoft** выпускала NetShow и протокол MMS (Microsoft Media Server). Позже Windows Media Server 9 получил поддержку RTSP ([AppleInsider, 2009](https://appleinsider.com/articles/09/07/08/apple_launches_http_live_streaming_standard_in_iphone_3_0.html)).
- **Apple** выпустила QuickTime 4 и QuickTime Streaming Server с «настоящим» RTSP в 1999 году (там же).
- **Vivo, Xing, VDOnet** предлагали собственные платформы.

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

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

В самом RFC 2326 перечислено, чем RTSP отличается от HTTP/1.1, на который он синтаксически похож ([RFC 2326, §1.1](https://www.rfc-editor.org/rfc/rfc2326)): 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](https://www.rfc-editor.org/rfc/rfc2326)). Участие Netscape и RealNetworks согласуется и с историей отрасли: по данным [AppleInsider](https://appleinsider.com/articles/09/07/08/apple_launches_http_live_streaming_standard_in_iphone_3_0.html), 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](https://www.rfc-editor.org/rfc/rfc7826)). Среди изменений: убраны `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](http://lists.live555.com/pipermail/live-devel/2016-May/020080.html)). Пример из свежего: новый GStreamer-элемент `rtspsrc2` в списке возможностей указывает «RTSP 1.0 support» ([документация GStreamer](https://origin.gstreamer.freedesktop.org/documentation/rsrtsp/index.html)).
3. **ONVIF.** Консорциум основан в 2008 году, то есть задолго до RFC 7826. ONVIF не заменяет RTSP: его спецификация стриминга прямо строится на RFC 2326 и описывает расширения RTSP/RTP ([ONVIF Streaming Spec 19.06](https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v1906.pdf)). Но то, что ONVIF описывает и обнаружение устройств, и профили, и управление, и поток через уже существующий RTSP, снижало потребность в новом протоколе. Подробнее об ONVIF — в [отдельной статье](/blog/onvif-protocol-overview/).

**А что с шифрованием?** Схема `rtsps` формально определена в RTSP 2.0 ([RFC 7826, §22.14.2](https://www.rfc-editor.org/rfc/rfc7826)). В мире RTSP 1.0 способы защиты канала у вендоров разные, и сводить их в один «стандарт де-факто» я бы не стал. Например, спецификация ONVIF Media2 предусматривает RTSPS и SRTP (это следует из WSDL Media2 и спецификации ONVIF Core), а автор Live555 примерно в 2014 году писал, что библиотека RTSP поверх SSL не поддерживает ([live-devel](https://live-devel.live555.narkive.com/tzxNjqbS/rtsp-over-ssl); с тех пор ситуация могла измениться). Поддержку в конкретной камере и в конкретном клиенте нужно проверять отдельно — **⚠ проверить**.


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

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

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

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

| Протокол | Год | Заявленная цель | Специфика и ограничения |
| :--- | :--- | :--- | :--- |
| **HLS** | 2009 (RFC 8216 — 2017) | Стриминг через обычные HTTP-серверы | Apple рекомендует сегменты по 6 с, а по спецификации плеер отступает от «живого края» не меньше чем на три длительности сегмента. Типичная задержка — десятки секунд. |
| **MPEG-DASH** | 2012 (ISO/IEC 23009-1) | Адаптивное вещание по манифестам | Та же идея «сегменты и манифест», что у HLS; не расширение HLS, а самостоятельный стандарт. |
| **WebRTC** | 2011 (первый релиз), 2021 (W3C Recommendation) | Real-time связь между браузерами | Задержка обычно доли секунды (зависит от сети и настроек). Нужен сигнальный сервер. |
| **LL-HLS** | 2019 (анонс на WWDC19) | HLS с низкой задержкой | Целевая задержка — 1–2 с, нужна поддержка частичных сегментов на стороне CDN и источника. |
| **SRT** | 2017 (открытый код от Haivision) | Надёжная доставка по UDP в нестабильных сетях | Часто применяют для передачи видео между студиями и площадками. |
| **WHIP** | 2025 (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](https://developer.apple.com/videos/play/wwdc2019/502/), [хронология HLS и DASH](https://ppc.land/hls/), [Wikipedia: WebRTC](https://en.wikipedia.org/wiki/WebRTC), [Wikipedia: SRT](https://en.wikipedia.org/wiki/Secure_Reliable_Transport), [datatracker: MoQ Transport](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/), [RFC 9725 (по обзору)](https://www.webrtc-developers.com/webrtc-streaming-with-whip-and-whep/).

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

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


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

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

![RTSP Signaling, RTP and RTCP Flow](https://www.rockedge.ru/images/rtsp-signaling-flow.svg)

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

RTSP синтаксически похож на HTTP: текстовые методы, заголовки, коды ответов. Фундаментальное отличие в том, что у RTSP-сервера есть **состояние сессии** (`Init → Ready → Playing`), и он помнит его для каждого клиента ([RFC 2326, §1.7 и приложение A](https://www.rfc-editor.org/rfc/rfc2326)).


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

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

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

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

Для 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: «Покажи меню»

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

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

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

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

```http
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](https://www.rfc-editor.org/rfc/rfc7826)). В RTSP 1.0 это необязательный метод, и что именно можно менять через него, зависит от вендора.

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

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

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

```http
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` в повторе будет уже следующим.

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

```http
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](https://www.rfc-editor.org/rfc/rfc3551)). Правило для H.265 — часы 90000 ([RFC 7798](https://www.rfc-editor.org/rfc/rfc7798)).
- `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](https://www.rfc-editor.org/rfc/rfc3640)).
- `trackID=1` и `trackID=2` — отдельные дорожки (видео и аудио). `SETUP` нужно выполнять **для каждой дорожки отдельно**.

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

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

##### Вариант А: UDP (раздельные каналы)

```http
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-соединения)

```http
SETUP rtsp://192.168.1.100:554/stream1/trackID=1 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
```

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

```http
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](https://www.rfc-editor.org/rfc/rfc2326)). С этого момента идентификатор нужно указывать во всех последующих запросах.

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

```http
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: «Поехали!»

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

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

```http
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](https://www.rfc-editor.org/rfc/rfc2326), [ONVIF Streaming Spec, §5.2.1.1.2](https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v1906.pdf)). Некоторые камеры игнорируют этот заголовок — **⚠ проверить**.

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

```http
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](https://www.rfc-editor.org/rfc/rfc2326)). Он нужен для синхронизации аудио и видео, и мы вернёмся к нему в статье про RTP. После ответа камера начинает слать RTP-пакеты.

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


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

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

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

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

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

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

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

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

Номера каналов выбирает клиент в `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 ([исходники](https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/rtsp.c)). Поэтому лучше задавать транспорт явно, а не рассчитывать на поведение по умолчанию.


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

В этом разделе собрано то, с чем сталкиваются при интеграции с реальным оборудованием. Я не буду называть долю китайских камер в проектах как факт: оценки разнятся, а доля зависит от региона и сегмента. Для ориентира: по оценке Mordor Intelligence, Hikvision и Dahua вместе занимали около 39% мирового рынка камер видеонаблюдения в 2025 году ([источник](https://www.mordorintelligence.com/industry-reports/surveillance-camera-market), вторичный). Это не значит, что проблемы возникают только у них: реализации стандарта отличаются у всех производителей. Подтверждённые документами факты я отделяю от случаев «по сообщениям», для которых нужна проверка.

### Особенность 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](https://www.rfc-editor.org/rfc/rfc6184), [RFC 7798](https://www.rfc-editor.org/rfc/rfc7798)). Поэтому клиент должен уметь работать с обоими вариантами: брать параметры из SDP и обновлять их, если в потоке приходят новые. Конкретные случаи вроде «Dahua не присылает `sprop-vps`» или «у Hikvision кривой Base64-паддинг» я в публичных источниках не нашёл, так что отношусь к ним как к гипотезам — **⚠ проверить**.

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

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

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

| Производитель | Типичный формат RTSP URL | Источник |
| :--- | :--- | :--- |
| **Hikvision** | `rtsp://<ip>:554/Streaming/Channels/101` (основной поток), `.../102` (субпоток) | [Hikvision Portal](https://supportusa.hikvision.com/support/solutions/articles/17000129022-do-you-have-an-example-showing-the-format-for-getting-a-rtsp-stream-from-a-camera-) |
| **Dahua** | `rtsp://<ip>:554/cam/realmonitor?channel=1&subtype=0` | [VisioForge](https://www.visioforge.com/help/docs/dotnet/camera-brands/dahua/) (вторичный) |
| **Axis** | `rtsp://<ip>/axis-media/media.amp` | [VisionAI Docs](https://docs.visionify.ai/overview/finding-rtsp-urls/) (вторичный) |
| **Reolink** | `rtsp://<ip>:554/h264Preview_01_main` (встречаются и другие варианты, зависит от модели) | [Frigate / сообщество](https://www.reddit.com/r/frigate_nvr/comments/1ak5j2q/frigate_with_reolink_cameras_using_rtsp_and/) |
| **Uniview** | `rtsp://<ip>:554/media/video1` | не нашёл источника, **⚠ проверить** |
| **Прочие бренды** | пути вроде `/live`, `/stream1` | зависит от модели, **⚠ проверить** |

Если камера поддерживает ONVIF, нужный адрес лучше спрашивать у неё самой: в ONVIF для этого есть `GetStreamUri`, и угадывать путь не нужно. Как это делается и где ONVIF тоже расходится у разных вендоров, я описал в [статье про ONVIF](/blog/onvif-protocol-overview/).

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

В RFC 2326 серверам рекомендовано (SHOULD) реализовать **Basic** и **Digest** ([RFC 2326, §16](https://www.rfc-editor.org/rfc/rfc2326)).

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

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

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

Здесь пример из публичных источников: в рассылке Live555 пользователь описывал, что сервер Axis через 60 секунд присылал RTCP `BYE`, если клиент не отправлял RTCP Receiver Reports ([live-devel, 2008](https://live-devel.live.narkive.com/ZH3rKcwv/rtsp-and-keeping-alive-connection-with-rtcp-packets)). Это согласуется со стандартом: 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](https://www.rfc-editor.org/rfc/rfc2326)). Сами RTP-пакеты с видео, которые идут от камеры к клиенту, в этот список не входят.

Спецификация стриминга ONVIF формулирует то же самое: для удержания сессии клиент **должен** вызвать любой RTSP-метод или отправлять RTCP Receiver Reports, а `SET_PARAMETER` рекомендован ([ONVIF Streaming Spec, §5.2.1.1.1](https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v1906.pdf)). RTSP 2.0 даёт тот же порядок предпочтений: RTCP, затем `SET_PARAMETER`, `GET_PARAMETER` и `OPTIONS` ([RFC 7826, §10.5](https://www.rfc-editor.org/rfc/rfc7826)).

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

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

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

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

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

- **FFmpeg** отправляет keep-alive из цикла чтения пакетов, когда с последней команды прошла половина таймаута. Если ваше приложение перестало читать (например, из-за обратного давления в очереди), keep-alive тоже не уйдёт ([исходник](https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/rtspdec.c)). Также FFmpeg отправляет RTCP Receiver Reports.
- **GStreamer** (`rtspsrc`) имеет свойство `do-rtsp-keep-alive`, по умолчанию включено. По сообщению на форуме, при передаче по TCP keep-alive может не отправляться из-за того, что данные идут непрерывно ([обсуждение](https://discourse.gstreamer.org/t/rtspsrc-keep-alive-behavior-with-tcp-vs-udp/4433)). Это отчёт пользователя, а не документация, поэтому **⚠ проверить** на вашей версии.
- **OpenCV** обычно использует бэкенд FFmpeg или GStreamer, поэтому поведение определяется ими. Конкретную сборку нужно проверять.

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


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

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

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

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

На камерах бывают ограничения на число одновременных подключений. Конкретные цифры сильно различаются. Например, в документации одного открытого проекта указано «обычно 2–5» ([NVIDIA live-vlm-webui](https://github.com/nvidia-ai-iot/live-vlm-webui/blob/main/docs/usage/rtsp-ip-cameras.md), вторичный источник), а в даташитах конкретных моделей встречаются иные значения. Ищите лимит в документации своей модели. Если сервис перезапустился и не отправил `TEARDOWN`, «висячие» сессии могут занять доступные слоты, и новые подключения будут отклоняться, пока старые не истекут.

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


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

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

![RTSP в современной экосистеме](https://www.rockedge.ru/images/rtsp-ecosystem-architecture.svg)

- **ONVIF** — отдельный набор спецификаций (веб-сервисы на SOAP), а не «надстройка над RTSP». Он отвечает за обнаружение устройств (`WS-Discovery`), настройку, PTZ и события, а для самого видео возвращает RTSP-ссылку (`GetStreamUri`) и описывает расширения RTSP/RTP. Подробнее в [статье про ONVIF](/blog/onvif-protocol-overview/).
- **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-соединению, и «висячие» сессии могут занять слоты камеры.
- **Не считайте слухи фактами.** Всё, что я отметил как «проверить», стоит воспроизвести на своём оборудовании.


## Источники

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

- [RFC 2326: Real Time Streaming Protocol (RTSP)](https://www.rfc-editor.org/rfc/rfc2326), апрель 1998.
- [RFC 7826: RTSP 2.0](https://www.rfc-editor.org/rfc/rfc7826), декабрь 2016.
- [RFC 3550: RTP](https://www.rfc-editor.org/rfc/rfc3550) и [RFC 1889](https://www.rfc-editor.org/rfc/rfc1889).
- [RFC 3551: RTP A/V Profile](https://www.rfc-editor.org/rfc/rfc3551), [RFC 6184: H.264 over RTP](https://www.rfc-editor.org/rfc/rfc6184), [RFC 7798: H.265 over RTP](https://www.rfc-editor.org/rfc/rfc7798), [RFC 3640: MPEG-4 over RTP](https://www.rfc-editor.org/rfc/rfc3640).
- [RFC 2617](https://www.rfc-editor.org/rfc/rfc2617) и [RFC 7616](https://www.rfc-editor.org/rfc/rfc7616): HTTP-аутентификация (Basic, Digest).
- [ONVIF Streaming Specification v19.06](https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v1906.pdf).
- [Internet-Draft: Media over QUIC Transport](https://datatracker.ietf.org/doc/draft-ietf-moq-transport/) (черновик).

**Исходный код**

- [FFmpeg: libavformat/rtspdec.c и rtsp.c](https://github.com/FFmpeg/FFmpeg/tree/master/libavformat).

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

- [История индустрии потокового видео (Streaming Media Blog)](https://www.streamingmediablog.com/2016/03/history-of-the-streaming-media-industry.html).
- [AppleInsider: HTTP Live Streaming в iPhone 3.0 (2009)](https://appleinsider.com/articles/09/07/08/apple_launches_http_live_streaming_standard_in_iphone_3_0.html).
- [Apple WWDC19, сессия 502: Low-Latency HLS](https://developer.apple.com/videos/play/wwdc2019/502/) и [хронология HLS](https://ppc.land/hls/).
- [IHS Markit через Security Today: 1 млрд камер](https://securitytoday.com/articles/2019/12/09/one-billion-surveillance-cameras-expected.aspx).
- [Mordor Intelligence: рынок камер видеонаблюдения](https://www.mordorintelligence.com/industry-reports/surveillance-camera-market) (вторичный).

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

- [Live555: SRTP и RTSP 2.0](http://lists.live555.com/pipermail/live-devel/2016-May/020080.html), [RTSP over SSL](https://live-devel.live555.narkive.com/tzxNjqbS/rtsp-over-ssl), [Axis и RTCP RR](https://live-devel.live.narkive.com/ZH3rKcwv/rtsp-and-keeping-alive-connection-with-rtcp-packets).
- [GStreamer rtspsrc2](https://origin.gstreamer.freedesktop.org/documentation/rsrtsp/index.html), [обсуждение keep-alive](https://discourse.gstreamer.org/t/rtspsrc-keep-alive-behavior-with-tcp-vs-udp/4433).
- [Frigate: go2rtc troubleshooting](https://docs.frigate.video/troubleshooting/go2rtc/).
- [Hikvision Portal: RTSP URL](https://supportusa.hikvision.com/support/solutions/articles/17000129022-do-you-have-an-example-showing-the-format-for-getting-a-rtsp-stream-from-a-camera-).