Skip to content

RTSP DVR

media.play.rtsp_dvr

Sapsan выдаёт записанный архив по RTSP. Клиент получает список доступных периодов записи, выбирает время по UTC и управляет воспроизведением в одной сессии. Чтение существующего архива не требует работающего входного источника.

Транспорт

RTSP 1.0 и RTP/RTCP interleaved в одном TCP-соединении. UDP не поддерживается: SETUP с UDP возвращает 461.

Поддерживаемые кодеки

  • H.264
  • HEVC / H.265
  • AV1
  • AAC
  • Opus
  • PCMA
  • PCMU

Список относится к пакетизации RTSP; клиент должен поддерживать выбранные кодеки.

AAC передаётся как MPEG4-GENERIC с AU-заголовками; AV1 — с RTP-фрагментацией и sequence header. Для MP4A-LATM и MPEG Audio (MPA, MP2/MP3) есть объявления в SDP, но полноценная соответствующая RTP-упаковка текущим выходным трактом не обеспечивается; они не включены в список выше. При необходимости преобразуйте звук в AAC или Opus. Наличие имени кодека в SDP само по себе не гарантирует совместимость.

Протокол и адрес

Управление использует RTSP 1.0: RFC 2326. Описание дорожек передаётся в SDP: RFC 4566. Медиа передаётся по RTP/RTCP: RFC 3550. Для H.264 применяется формат RTP из RFC 6184.

Список архивных периодов в одном атрибуте SDP и параметр position — расширения архивного интерфейса Sapsan. Их формат описан ниже. Поддерживаемый набор возможностей и известные расхождения приведены явно; наличие RTSP 1.0 само по себе не означает поддержки всех режимов этих RFC.

Для доступа нужны записанный архив DVR, настроенный RTSP listener и разрешённая лицензией функция media.play.rtsp_dvr.

rtsp://sapsan.example.com:554/camera?dvr=true

Адрес статичен: время передаётся в заголовке Range, а не в URL. Значение dvr=true обязательно; другие значения параметра отклоняются. Обычная ссылка без этого параметра предназначена для эфира и не принимает архивный Range: clock=.... Если требуется авторизация токеном, добавьте &token=<token>.

Лицензия

Функция media.play.rtsp_dvr использует отдельный лицензионный лимит media_play_rtsp_dvr. Обычный эфир по RTSP использует media_play_rtsp; разрешение эфира не даёт доступа к архиву.

Допуск проверяется до чтения архива, в том числе при прямом PLAY без предварительного DESCRIBE. Нулевой лимит запрещает доступ, а исчерпанный числовой лимит запрещает создание новых сессий: сервер отвечает 453 Not Enough Bandwidth.

Архивная сессия сохраняет свой лицензионный допуск после конца выбранного диапазона. Следующий PLAY в том же соединении использует ту же сессию. Освобождайте её через TEARDOWN или закрытие соединения; EOF сам по себе не освобождает занятый слот. Подробнее — лицензирование.

Доступные периоды записи

Клиент начинает с DESCRIBE:

DESCRIBE rtsp://sapsan.example.com:554/camera?dvr=true RTSP/1.0
CSeq: 1
Accept: application/sdp

В ответе 200 OK тело имеет тип application/sdp. На уровне сессии, до описаний дорожек m=, передаётся атрибут:

a=range:clock=20261019T080000Z-20261019T093000Z;clock=20261019T100000Z-

Это пример двух периодов записи с разрывом между ними. Формат времени — YYYYMMDDThhmmssZ, целые секунды UTC. Отсутствие времени после дефиса у последнего периода означает продолжающуюся запись.

  • Порядок. Периоды перечисляются по возрастанию времени.
  • Объединение. Соседние периоды объединяются; при построении списка также объединяются разрывы длительностью до 10 секунд включительно. Такой список не гарантирует наличие кадров в каждой секунде объединённого периода.
  • Количество. Передаётся не более 100 последних периодов. Более старую сохранившуюся запись можно запросить явным PLAY, даже если её нет в этом списке.
  • Пустой архив. Атрибут a=range отсутствует. Для описания дорожек сервер может использовать доступный эфир.

При наличии записи описание дорожек берётся из архива. Поэтому DESCRIBE и воспроизведение возможны после отключения входного источника.

Подготовка транспорта

Для каждой нужной дорожки выполните SETUP, используя её идентификатор из SDP. Поддерживается RTP/RTCP внутри того же TCP-соединения, что и RTSP — interleaved TCP. В примере <track-url> обозначает URL выбранной дорожки:

SETUP <track-url> RTSP/1.0
CSeq: 2
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

Существенные заголовки ответа:

RTSP/1.0 200 OK
CSeq: 2
Session: example-session;timeout=60
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

В дальнейших запросах используйте полученный Session. Для следующей дорожки выбирайте другую пару interleaved-каналов. Транспорт UDP отклоняется кодом 461.

Примеры показывают существенные поля обмена. В сети строки заканчиваются CRLF, заголовки отделяются от тела пустой строкой, а Content-Length содержит длину тела в байтах.

Выбор времени и перемотка

Для воспроизведения интервала отправьте PLAY на адрес всей сессии:

PLAY rtsp://sapsan.example.com:554/camera?dvr=true RTSP/1.0
CSeq: 3
Session: example-session
Range: clock=20261019T081000Z-20261019T081500Z
Scale: 1

Существенные поля ответа:

RTSP/1.0 200 OK
CSeq: 3
Session: example-session;timeout=60
Range: clock=20261019T081000Z-20261019T081500Z
Scale: 1

В ответе RTP-Info содержит seq и rtptime для выдаваемых подготовленных дорожек, включая первый архивный запуск и перемотку. При продолжении после паузы rtptime соответствует фактическому следующему кадру, в том числе после разрыва записи.

Запрос PLAY Поведение Sapsan
Range: clock=<from>-<to> Начать с независимого фрагмента и выдавать кадры с DTS меньше <to>
Range: clock=<from>- Выбрать существующие фрагменты начиная с указанного времени без верхней границы
Новый Range: clock=... в той же сессии Сразу перемотать; повторный SETUP не нужен
Range: npt=now- Переключиться на эфир
Без Range, источник уже выбран Продолжить выдачу, в том числе после PAUSE
Без Range, первый запуск Выбрать эфир
Открытый числовой NPT, например npt=10- 451 Parameter Not Understood; текущая выдача продолжается

Ответный Range сообщает выбранные границы. Если <from> находится внутри независимого фрагмента, воспроизведение начинается с его начала, которое может быть раньше запроса. Поиск назад ограничен 60 секундами; если подходящего начала нет, выбирается следующий независимый фрагмент. Конец ограничивается запрошенным <to> и доступной записью. Кадры с DTS, равным <to> или больше него, не выдаются.

Дефис сохраняется и при открытом конце. Запрос принимает UTC с целыми секундами, но фактическая граница в ответе может содержать миллисекунды, например clock=20261019T081000.250Z-20261019T081500Z.

Скорость — обычная вперёд. Scale можно не передавать или указать 1; на Scale: 1 сервер возвращает Scale: 1. Другие скорости и обратная форма clock=-<to> возвращают 451 Parameter Not Understood, сохраняя текущую выдачу.

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

Открытый диапазон читает выборку фрагментов на момент PLAY и завершается на её конце. Новые фрагменты не подхватываются, автоматического перехода к эфиру нет. Для эфира нужен явный PLAY с npt=now-.

Текущая позиция

Чтобы получить позицию выдачи, отправьте GET_PARAMETER с телом position:

GET_PARAMETER rtsp://sapsan.example.com:554/camera?dvr=true RTSP/1.0
CSeq: 4
Session: example-session
Content-Type: text/parameters
Content-Length: 10

position

Здесь тело — position с завершающим CRLF, всего 10 байт. Ответ:

RTSP/1.0 200 OK
CSeq: 4
Session: example-session;timeout=60
Range: clock=20261019T081012Z
Supported: position

Время находится в заголовке, а не в теле ответа. Это одна метка без дефиса, в отличие от диапазона в ответе PLAY. Значение соответствует DTS последнего выданного кадра с точностью до секунды, а не моменту его показа на экране клиента.

До первого выданного кадра выбранного источника, в том числе после перемотки, position возвращает 503 Service Unavailable без Range, с Supported: position. Во время паузы и после конца архива возвращаются 200 OK и последняя подтверждённая позиция. По одному коду 200 нельзя определить, продолжается ли передача кадров. Неизвестный параметр в непустом теле даёт 200 OK и Supported: position, без Range.

Пауза и завершение сессии

PAUSE на адрес сессии приостанавливает выдачу с сохранением позиции внутри фрагмента. Следующий PLAY без Range продолжает с оставшихся кадров. Новый Range: clock=... задаёт немедленную перемотку, в том числе при конвейерной отправке нескольких PLAY; очередь интервалов не создаётся.

Во время паузы поддерживайте сессию пустым GET_PARAMETER. Сервер объявляет timeout=60; отправляйте keepalive чаще этого интервала. Входящие RTSP-запросы и RTCP-пакеты учитываются как активность.

При нормальном исчерпании диапазона, по которому уже передавалось RTP-медиа, сервер отправляет RTCP BYE по подготовленным дорожкам и завершает текущую выдачу. Соединение и сессия остаются открытыми: можно запросить другой диапазон без повторного SETUP.

Для освобождения сессии отправьте TEARDOWN с её Session. После ответа 200 OK соединение закрывается и лицензионный слот освобождается.

Медиа и границы совместимости

Для H.264 параметры декодера передаются в SDP, а недостающие SPS/PPS добавляются перед ключевыми кадрами. При смене конфигурации архивных фрагментов параметры обновляются. RTP timestamp формируется из архивного DTS; RTCP Sender Report связывает его со временем NTP.

RTCP Sender Report содержит накопленные количества RTP-пакетов и октетов полезной нагрузки. Заголовки RTP и обёртка interleaved в октеты не входят. Перемотка с тем же SSRC не сбрасывает счётчики.

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

  • Транспорт. Только interleaved TCP; UDP возвращает 461.
  • Скорость. Только обычное воспроизведение вперёд; другие значения Scale возвращают 451.
  • Шкала времени. Для архивного позиционирования используйте clock с целыми секундами UTC. Числовой NPT и SMPTE не поддерживаются. Дробные секунды допустимы только в ответе.
  • Последовательность диапазонов. Каждый новый архивный PLAY сразу перематывает; очереди интервалов нет.
  • Продолжающаяся запись. Открытый диапазон не читает фрагменты, появившиеся после его запуска.

Коды ответов

Код Причина
400 Bad Request Некорректный dvr или URL; отсутствует Transport в SETUP
403 Forbidden Отказ авторизации либо зашифрованный поток, недоступный для RTSP-выдачи
404 Not Found Не найден стрим, архив или дорожка
451 Parameter Not Understood Неподдержанный Range или Scale; запрос архива через обычную live-ссылку
453 Not Enough Bandwidth Функция запрещена лицензией или исчерпан лимит сессий
454 Session Not Found Нет контекста сессии, необходимого для запроса
457 Invalid Range Пустое или обратное окно; нет подходящих архивных фрагментов
461 Unsupported Transport Запрошен транспорт, отличный от interleaved TCP
503 Service Unavailable Источник недоступен; position запрошен до первого кадра выбранного источника. EOF архива сам по себе не даёт 503 для position

Проверка клиента

Проверяйте интеграцию на записи с известными временными границами и разрывом:

  1. Получите SDP и сопоставьте a=range с периодами записи с учётом объединения коротких разрывов.
  2. Выполните SETUP для нужных дорожек. Проверьте пары interleaved-каналов и идентификатор сессии.
  3. Запросите position до первого кадра: ожидается 503 без Range.
  4. Запустите PLAY с началом внутри фрагмента и Scale: 1. Проверьте выбранный Range, ответный Scale и соответствие rtptime/seq первому RTP-пакету. Кадров с DTS >= <to> быть не должно.
  5. Сделайте паузу посреди фрагмента и продолжите через PLAY без Range. Проверьте сохранение оставшихся кадров и позиции.
  6. Проверьте 451 для неподдержанной скорости и числового NPT: текущая выдача должна продолжаться.
  7. Дождитесь EOF и RTCP BYE, затем запросите другой диапазон в том же соединении. Проверьте позицию после EOF и накопленные счётчики Sender Report.
  8. Проверьте лицензионный отказ 453 при нулевом или исчерпанном лимите и завершите сессию через TEARDOWN.