Skip to content

RTSP DVR

media.play.rtsp_dvr

Sapsan serves recorded archives over RTSP. Clients discover available recording periods, select a UTC time, and control playback within one session. Reading an existing archive does not require an active input source.

Transport

RTSP 1.0 and interleaved RTP/RTCP share one TCP connection. UDP is unsupported: a UDP SETUP returns 461.

Supported codecs

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

This list describes RTSP packetization; the client must support the selected codecs.

AAC uses MPEG4-GENERIC with AU headers; AV1 uses RTP fragmentation and a sequence header. SDP declarations exist for MP4A-LATM and MPEG Audio (MPA, MP2/MP3), but the current output path does not provide their complete corresponding RTP packetization; they are excluded from the list above. Convert audio to AAC or Opus when needed. A codec name in SDP alone does not guarantee compatibility.

Protocol and URL

Control uses RTSP 1.0: RFC 2326. Track descriptions use SDP: RFC 4566. Media uses RTP/RTCP: RFC 3550. H.264 uses the RTP payload format in RFC 6184.

The list of archive periods in a single SDP attribute and the position parameter are extensions of the Sapsan archive interface. Their formats are described below. The supported capabilities and known differences are stated explicitly; RTSP 1.0 support alone does not imply support for every mode in these RFCs.

Access requires a recorded DVR archive, a configured RTSP listener, and a license that permits media.play.rtsp_dvr.

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

The URL is static: time is passed in the Range header, not in the URL. The value dvr=true is required; other values are rejected. A regular URL without this parameter serves live playback and does not accept an archive Range: clock=.... For token authorization, append &token=<token>.

License

The media.play.rtsp_dvr feature uses the separate license limit media_play_rtsp_dvr. Live RTSP uses media_play_rtsp; permission to play live media does not grant archive access.

Admission is checked before reading the archive, including a direct PLAY without an earlier DESCRIBE. A zero limit prohibits access, while an exhausted numeric limit prevents new sessions: the server returns 453 Not Enough Bandwidth.

The archive session retains its licensed admission after the selected range ends. The next PLAY on the same connection uses that session. Release it with TEARDOWN or by closing the connection; EOF alone does not release the occupied slot. See licensing.

Available recording periods

The client starts with DESCRIBE:

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

The 200 OK response has an application/sdp body. It includes the following session-level attribute before the m= track descriptions:

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

This example describes two recording periods separated by a gap. Time uses YYYYMMDDThhmmssZ, in whole UTC seconds. A missing end time after the hyphen in the last period indicates ongoing recording.

  • Order. Periods are listed in ascending time order.
  • Merging. Adjacent periods are merged; gaps of up to and including 10 seconds are also merged when building the list. Such a period does not guarantee frames for every second it covers.
  • Count. At most the 100 most recent periods are advertised. Older recordings that still exist can be requested with an explicit PLAY, even when absent from this list.
  • Empty archive. The a=range attribute is omitted. The server may use an available live source to describe the tracks.

When recordings exist, track descriptions come from the archive. DESCRIBE and playback therefore remain available after the input source disconnects.

Transport setup

Send SETUP for each required track using its identifier from the SDP. RTP/RTCP is carried within the same TCP connection as RTSP — interleaved TCP. In this example, <track-url> denotes the selected track URL:

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

Relevant response headers:

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

Use the returned Session in subsequent requests. Choose a different interleaved channel pair for the next track. UDP transport is rejected with 461.

Examples show the relevant exchange fields. On the wire, lines end with CRLF, a blank line separates headers from the body, and Content-Length specifies the body length in bytes.

Time selection and seeking

To play an interval, send PLAY to the session URL:

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

Essential response fields:

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

The RTP-Info response contains seq and rtptime for the configured tracks being delivered, including initial archive playback and seeking. On resumption after a pause, rtptime matches the actual next frame, including a jump across a recording gap.

PLAY request Sapsan behavior
Range: clock=<from>-<to> Start at an independent fragment and deliver frames with DTS below <to>
Range: clock=<from>- Select existing fragments from the specified time without an upper bound
A new Range: clock=... in the same session Seek immediately; no new SETUP is needed
Range: npt=now- Switch to live playback
No Range, with a source already selected Continue playback, including after PAUSE
No Range, on initial playback Select the live source
Open numeric NPT, such as npt=10- 451 Parameter Not Understood; existing playback continues

The response Range reports the selected boundaries. If <from> falls inside an independent fragment, playback starts at its beginning, which may precede the request. The backward search is limited to 60 seconds; if no suitable start is found, the next independent fragment is selected. The end is limited by the requested <to> and the available recording. Frames with DTS equal to or greater than <to> are not delivered.

The hyphen is retained even for an open end. Requests accept whole UTC seconds, but an actual response boundary may include milliseconds, for example clock=20261019T081000.250Z-20261019T081500Z.

Playback runs forward at normal speed. Omit Scale or set it to 1; a request with Scale: 1 receives Scale: 1. Other speeds and the reverse form clock=-<to> return 451 Parameter Not Understood while existing playback continues.

If a recording gap separates selected fragments, the server moves to the next available fragment without waiting for the entire gap duration. Media timestamps jump across the missing period.

An open range reads the fragment selection available when PLAY was received and ends when that selection is exhausted. It does not pick up new fragments or switch to live media automatically. Live playback requires an explicit PLAY with npt=now-.

Current position

To obtain the delivery position, send GET_PARAMETER with position in the body:

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

The body here is position followed by CRLF, totaling 10 bytes. Response:

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

Time is returned in a header, not in the response body. It is a single timestamp without a hyphen, unlike the range in a PLAY response. The value is the DTS of the last delivered frame, truncated to whole seconds, rather than its display time on the client.

Before the first delivered frame of the selected source, including after a seek, position returns 503 Service Unavailable without Range, with Supported: position. During a pause and after archive EOF, it returns 200 OK and the last confirmed position. A 200 status alone does not indicate whether frames are still being delivered. An unknown parameter in a nonempty body returns 200 OK and Supported: position, without Range.

Pausing and ending a session

PAUSE at the session URL suspends delivery while retaining the position within the fragment. The next PLAY without Range resumes the remaining frames. A new Range: clock=... seeks immediately, including when multiple PLAY requests are pipelined; no interval queue is created.

During a pause, keep the session alive with an empty GET_PARAMETER. The server advertises timeout=60; send keepalive requests more frequently than this interval. Incoming RTSP requests and RTCP packets count as activity.

When a range that has delivered RTP media reaches its natural end, the server sends RTCP BYE for the configured tracks and ends the current delivery. The connection and session stay open: another range can be requested without repeating SETUP.

To release the session, send TEARDOWN with its Session. After 200 OK, the connection closes and the license slot is released.

Media and compatibility limits

For H.264, decoder parameters are supplied in SDP, and missing SPS/PPS are inserted before keyframes. Parameters are updated when the archive fragment configuration changes. RTP timestamps are derived from archive DTS; RTCP Sender Reports map them to NTP time.

RTCP Sender Reports contain cumulative RTP packet and payload octet counts. RTP headers and interleaved framing are excluded from the octet count. A seek using the same SSRC does not reset the counters.

The supported profile has the following limits:

  • Transport. Interleaved TCP only; UDP returns 461.
  • Speed. Normal forward playback only; other Scale values return 451.
  • Time scale. Use clock with whole UTC seconds for archive seeking. Numeric NPT and SMPTE are unsupported. Fractional seconds are only allowed in responses.
  • Range sequencing. Each new archive PLAY seeks immediately; there is no interval queue.
  • Ongoing recording. An open range does not read fragments created after playback starts.

Response codes

Code Cause
400 Bad Request Invalid dvr or URL; missing Transport in SETUP
403 Forbidden Authorization denied, or an encrypted stream unavailable for RTSP delivery
404 Not Found Stream, archive, or track not found
451 Parameter Not Understood Unsupported Range or Scale; archive requested through a regular live URL
453 Not Enough Bandwidth Feature prohibited by the license or session limit reached
454 Session Not Found Required session context is absent
457 Invalid Range Empty or reversed window; no suitable archive fragments
461 Unsupported Transport A transport other than interleaved TCP was requested
503 Service Unavailable Source unavailable; position requested before the first frame of the selected source. Archive EOF alone does not cause 503 for position

Client verification

Check the integration using a recording with known time boundaries and a gap:

  1. Obtain the SDP and compare a=range with recording periods, accounting for the merging of short gaps.
  2. Send SETUP for the required tracks. Check interleaved channel pairs and the session identifier.
  3. Request position before the first frame: expect 503 without Range.
  4. Start PLAY inside a fragment with Scale: 1. Check the selected Range, returned Scale, and whether rtptime/seq match the first RTP packet. No frame may have DTS >= <to>.
  5. Pause within a fragment and resume with PLAY without Range. Verify that remaining frames and the position are retained.
  6. Check 451 for an unsupported speed and numeric NPT: existing playback must continue.
  7. Wait for EOF and RTCP BYE, then request another range on the same connection. Check the position after EOF and cumulative Sender Report counters.
  8. Check license refusal 453 for a zero or exhausted limit, and end the session with TEARDOWN.