
A customer wanted their shiny camera platform recording into a third-party NVR. Simple ask: pull the RTSP stream, point the recorder at it, walk away.
The recorder disagreed. Every stream it tried died on connect. No video, no error worth reading, just a connection that opened and then went cold.
I’d wired up a hundred of these. This one fought me.
The investigation
First instinct: bad URL. So I copied the stream address straight out of the platform and fed it to ffprobe, the universal “is this thing even alive” test.
ffprobe "rtsps://camera-host:7441/<stream-token>?enableSrtp"
[tls] Error: handshake failure
rtsps://camera-host:7441/...: Input/output error
There it is. rtsps://. Not rtsp://. That little s is the whole story.
The platform was advertising an encrypted stream — RTSP wrapped in TLS, with SRTP turned on for the media itself. A high port, a token in the path, the works. Very modern. Very secure.
And completely useless to a recorder that only speaks plain, unencrypted RTSP. The NVR reached out for a normal RTSP handshake and got a TLS negotiation shoved in its face. It had no idea what to do, so it hung up.
┌──────────────┐ rtsps:// + SRTP ┌──────────────┐
│ Camera │ ────────────────────────►│ NVR │
│ platform │ (TLS handshake) │ (plain RTSP) │
└──────────────┘ └──────┬───────┘
│
▼
✗ handshake failure
✗ no video recorded
Two devices, both fluent in “RTSP,” neither able to hold a conversation. The walled garden, working exactly as designed.
The “aha”
Here’s the thing about these platforms: the encrypted stream is what they show you. It’s rarely the only thing they offer.
I dug into the device settings and found a toggle — a separate, unencrypted RTSP endpoint on a different port. Plain rtsp://, basic credentials, no TLS, no SRTP. The boring old protocol the recorder actually wanted, sitting right there behind a checkbox nobody enabled by default.
The fix
Enable the plain endpoint, grab the new URL, and verify with ffprobe before touching the NVR config:
ffprobe -v error -show_streams \
"rtsp://<user>:<pass>@camera-host:7447/<stream-token>"
codec_name=h264
width=2688
height=1512
✓ Stream #0:0 Video: h264, 2688x1512, 15 fps
Green across the board. Point the recorder at that URL and it records.
If the platform won’t expose a plain endpoint, drop a restreamer in the middle — something like MediaMTX or a thin ffmpeg relay — to ingest the encrypted stream and re-publish it as plain RTSP on your own box:
ffmpeg -rtsp_transport tcp \
-i "rtsps://camera-host:7441/<stream-token>?enableSrtp" \
-c copy -f rtsp \
rtsp://127.0.0.1:8554/cam1
# verify the republished stream the NVR will actually consume
ffprobe "rtsp://127.0.0.1:8554/cam1"
The NVR talks to your restreamer. Your restreamer eats the TLS for breakfast. Everybody’s happy.
Why it happened
Vendors are encrypting streams by default now, and good for them — that’s the right call for traffic crossing untrusted networks. But “secure by default” and “interoperable” are different goals, and the camera folks optimized for the first one.
The encrypted URL is front and center. The plain one is buried, off, and undocumented. So the integration looks broken when it’s really just locked. The garden has a gate; you have to go find it.
Takeaways
- Read the scheme.
rtsps://on a high port with SRTP is a different animal thanrtsp://. The handshake error is your first clue, not a riddle. - Look for the plain-RTSP port. Most platforms still expose an unencrypted endpoint behind a toggle. Check device settings before you assume it can’t be done.
ffprobebefore you wire anything. If the probe can’t pull codec and resolution, the NVR won’t either.- Put a restreamer in the middle when the vendor refuses to deobfuscate — MediaMTX or
ffmpeg -c copyingests RTSPS and republishes clean RTSP, no re-encode. - A walled garden isn’t a wall. It’s a default. Find the gate the vendor left in the settings, and the lock-in mostly evaporates.