RTSP Receiver

MediaX can receive video from an RTSP server such as an IP camera. RTSP (RFC 2326) only negotiates and controls the session. The video itself travels as ordinary RTP, so the existing codec depayloaders do the depacketising and decoding.

Scope

This is a receiver (client) only. RTSPS (TLS), audio tracks, SRTP and an RTSP server are not implemented. The Qt6 player example can additionally show HLS, MJPEG and JPEG snapshot web streams, see Examples. Decoding currently uses the OpenH264 software decoder (H.264). H.265 and AV1 sessions are parsed, but need a decoder backend that supports external transport (see Decoder backends).

Quick start

#include "rtsp/rtsp_depayloader.h"

mediax::rtsp::RtspDepayloader rx;
rx.SetUrl("rtsp://192.168.1.10:554/stream1");
rx.SetCredentials("user", "password");

if (!rx.Open()) {
  std::cerr << rx.GetLastError() << "\n";
  return 1;
}
rx.Start();

mediax::rtp::RtpFrameData frame;
while (rx.Receive(&frame, 1000)) {
  // frame.cpu_buffer holds RGB24 pixels, frame.resolution the size
}
rx.Close();

RtspDepayloader derives from mediax::rtp::RtpDepayloader, so it can be used wherever another depayloader is used: Receive(), RegisterCallback() (register before Open()), SetOutputMode() and SetRecordingFilename() for recording to Matroska all work the same way.

Transports

Mode RtspTransportMode Notes
Automatic kAuto (default) UDP unicast first, TCP interleaved if the server rejects UDP or no media arrives
UDP kUdp RTP/RTCP on an even/odd port pair, optionally restricted with rtp_port_min/rtp_port_max
Multicast kMulticast IPv4 only
TCP kTcp RTP interleaved on the RTSP connection, the best choice through firewalls and NAT
mediax::rtsp::RtspClientConfig config;
config.transport = mediax::rtsp::RtspTransportMode::kTcp;
config.media_timeout_ms = 5000;
rx.SetClientConfig(config);

Authentication

Basic and Digest (MD5) authentication are supported. Credentials can be given with SetCredentials() or in the URL. Credentials are only sent to the host in the URL and are never logged. Basic sends the password unprotected, so prefer Digest or a trusted network.

Reliability

  • RTCP receiver reports are sent every rtcp_interval_ms (default 5 s).
  • A keep-alive (GET_PARAMETER or OPTIONS) is sent at half the session timeout.
  • If the connection drops, or no media arrives for media_timeout_ms, the client reconnects with exponential backoff (reconnect_initial_delay_ms up to reconnect_max_delay_ms, at most max_reconnect_attempts tries). Register a state callback to follow kReconnecting, kPlaying and kError.
  • GetStatistics() returns packets received and lost, jitter and the number of reconnects.

Checking whether a stream is live

mediax::rtsp::ProbeRtspStream() answers "is this stream up?" without opening a decoder or delivering frames:

#include "rtsp/rtsp_probe.h"

mediax::rtsp::RtspProbeOptions options;
options.level = mediax::rtsp::RtspProbeLevel::kMedia;  // or kDescribe, the default
options.timeout_ms = 5000;

auto result = mediax::rtsp::ProbeRtspStream("rtsp://camera/stream", options);
if (result.IsLive()) {
  // result.tracks, result.first_packet_ms, result.transport
} else {
  std::cerr << result.error << "\n";  // never contains credentials
}
Level What it does Proves
kDescribe (default) Connect, OPTIONS, DESCRIBE Server reachable, credentials accepted, stream exists. Never touches the stream
kMedia Also SETUP and PLAY the first video track, wait for one RTP packet, TEARDOWN Media is actually flowing

The result also reports reachable, described, media_flowing, the track list and timings, so a stream that is described but silent can be told apart from one that is missing. kAuto transport probes over TCP interleaved first (it works through firewalls) and falls back to UDP if the server refuses TCP.

The Qt6 player has the same check on the command line, for RTSP and also for HLS, MJPEG and JPEG snapshot URLs. It prints one LIVE or DEAD line per URL and exits with status 0 only if all are live:

./build/bin/qt6-rtsp-player --probe rtsp://camera/stream https://host/live/playlist.m3u8

For HLS it checks that the playlist is not a finished recording and that the newest segment can be downloaded.

Encoded access units

To receive the compressed access units as well (for example to forward or store them), register an encoded frame callback before Open():

rx.RegisterEncodedFrameCallback([](const mediax::rtp::EncodedFrameData& au) {
  // au.data / au.size, au.is_keyframe, au.pts_us
});

Lower level API

mediax::rtsp::RtspClient is the control plane on its own. It negotiates the session and hands each RTP packet to a handler, without decoding:

mediax::rtsp::RtspClient client;
client.SetUrl("rtsp://camera/stream");
client.RegisterRtpHandler(0, [](const uint8_t* rtp, size_t size) { /* one RTP packet */ });
client.Open([](const mediax::rtsp::RtspTrackInfo& t) { return t.media_type == "video"; });
client.Play();

RtspTrackInfo carries the codec, payload type, clock rate and the sprop-parameter-sets from the SDP.

Decoder backends

mediax::rtp::DepayloaderFactory selects the decoder for a track. A depayloader can be driven by RTSP when SupportsExternalTransport() returns true; it then receives packets through PushRtpPacket() instead of opening its own socket. Currently implemented by RtpH264OpenH264Depayloader (requires -DBUILD_OPENH264=ON). Use RtspDepayloader::SetDecoderFactory() to supply your own.

Qt6 and Python

  • Qt6: mediax::qt6::QtRtspDepayloader (-DBUILD_QT6=ON) is signal driven and never blocks the GUI thread: play()/stopPlayback(), properties (url, transport, state), frameReady(QImage), errorOccurred and an asynchronous probe(). See the Qt6 API reference.
  • Python: rtsp_depayloader module (-DBUILD_PYTHON=ON). Use Receive() to poll frames.

Examples

Qt6 player

qt6-rtsp-player (built with -DEXAMPLES=ON when Qt6 Widgets is installed) plays RTSP and web streams. Paste a URL (credentials may be included) into the bar at the top and press Play or Enter. Ctrl+F toggles full screen (Esc or double click also leave it), Ctrl+L focuses the URL bar. The RTSP transport can be chosen in the bar.

URL How it plays
rtsp:// MediaX RTSP receiver, H.264 (needs -DBUILD_OPENH264=ON)
http(s):// MJPEG stream Decoded in the player (needs Qt6 Network), Basic authentication supported in the URL
http(s):// single JPEG Snapshot camera, reloaded every 5 seconds (--refresh)
http(s)://...m3u8 HLS, decoded by ffmpeg, which must be installed. Only http(s) playlists and segments are allowed
./build/bin/qt6-rtsp-player                       # paste the URL in the window
./build/bin/qt6-rtsp-player --transport=tcp rtsp://192.168.1.10/stream1
./build/bin/qt6-rtsp-player http://192.168.1.20/video.mjpg
./build/bin/qt6-rtsp-player --fullscreen --show-seconds 30 https://host/live/playlist.m3u8
./build/bin/qt6-rtsp-player --probe rtsp://192.168.1.10/stream1       # is it live? no window
Option Description
[url] rtsp:// or http(s):// URL to play immediately
-f, --fullscreen Start in full screen
-n, --seconds <s> Exit after this many seconds from launch
--show-seconds <s> Exit this many seconds after the first video frame is shown. Slow streams (HLS can take 2 to 15 s to start) still get their full time. Gives up after 30 more seconds without video
-r, --refresh <s> Reload interval for snapshot cameras, default 5, 0 disables
-t, --transport <t> RTSP transport: auto, udp or tcp
--probe Check whether the URL(s) are live without playing them, print LIVE/DEAD lines, exit 0 only if all are live
--probe-timeout <s> Seconds to wait for each probe, default 8

Playlist script

example/scripts/rtsp-playlist.sh shows a list of streams one after another in full screen:

./example/scripts/rtsp-playlist.sh                                      # 12 built-in camera snapshots
./example/scripts/rtsp-playlist.sh -c -s 10 example/scripts/hls-playlist.txt   # live HLS feeds
./example/scripts/rtsp-playlist.sh -l my-cameras.txt                    # loop your own list
Option Description
-s <seconds> How long each stream is shown once its first frame appears, default 5
-c Probe every stream first and skip those that are not live
-l Loop forever
-p <path> Path to qt6-rtsp-player
FILE One URL per line, blank lines and # comments ignored

Console receiver

rtsp-receive is a console version (built with -DEXAMPLES=ON). The password is read from RTSP_PASSWORD so it does not appear in the process list:

RTSP_PASSWORD=secret ./build/bin/rtsp-receive --url=rtsp://192.168.1.10/stream1 --user=admin \
    --transport=tcp --record=capture.mkv --duration=30

Build option

RTSP support is built by default and can be disabled with -DBUILD_RTSP=OFF. It is not available on Windows.