Skip to content

feat: SRTLA receiver support#5811

Open
thejoeejoee wants to merge 18 commits into
bluenviron:mainfrom
thejoeejoee:feat/srtla-support
Open

feat: SRTLA receiver support#5811
thejoeejoee wants to merge 18 commits into
bluenviron:mainfrom
thejoeejoee:feat/srtla-support

Conversation

@thejoeejoee

@thejoeejoee thejoeejoee commented May 29, 2026

Copy link
Copy Markdown

Adds an SRTLA receiver server that accepts bonded connections from BELABOX-compatible encoders and proxies aggregated traffic to the local SRT server.

What

  • Full SRTLA protocol implementation: REG1/REG2/REG3 handshake, keepalive, multi-connection aggregation with per-connection ACKs
  • SRTLA↔SRT correlation: when SRT accepts a connection from the SRTLA backend, the group is labeled with the stream path
  • Path-labeled Prometheus metrics: srtla_groups, srtla_groups_conns_active, srtla_groups_bytes_received, srtla_groups_bytes_forwarded
  • Enriched lifecycle logging with group short ID in all events
  • IPv6/dual-stack support with family-aware loopback resolution
  • Configuration via srtla / srtlaAddress in mediamtx.yml

Config

srtla: yes
srtlaAddress: :5000

Closes #3029

@thejoeejoee

Copy link
Copy Markdown
Author
  • is SRT-SRTLA linker worth it? adds a lot of correlation code, but adds debugging/metrics benefits
  • receiver only, no sender — follow-up if needed
  • group timeout 30s hardcoded — should probably be configurable, but fine for now
  • address-based correlation is fragile behind NAT — if SRT conn comes from different IP than SRTLA reg, it won't match. in practice this works because both go through the same UDP socket on localhost, but worth knowing
  • no max groups/conns limit — could be DoS vector in theory

@thejoeejoee

thejoeejoee commented May 29, 2026

Copy link
Copy Markdown
Author
2026/05/29 14:29:32 INF MediaMTX dev, darwin, arm64
2026/05/29 14:29:32 INF [RTSP] listener opened on :8554 (TCP/RTSP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
2026/05/29 14:29:32 INF [RTMP] listener opened on :1935 (TCP/RTMP)
2026/05/29 14:29:32 INF [HLS] listener opened on :8888 (TCP/HTTP)
2026/05/29 14:29:32 INF [WebRTC] listener opened on :8889 (TCP/HTTP), :8189 (UDP/ICE)
2026/05/29 14:29:32 INF [SRT] listener opened on :8890 (UDP)
2026/05/29 14:29:32 INF [SRTLA] listener opened on :8891 (UDP)
^[2026/05/29 14:33:18 INF [SRT] [conn [::1]:52107] opened
2026/05/29 14:33:18 INF [path mystream] stream is available and online, 2 tracks (H264, MPEG-4 Audio)
2026/05/29 14:33:18 INF [SRT] [conn [::1]:52107] is publishing to path 'mystream'
2026/05/29 14:33:46 INF [RTMP] [conn [::1]:49652] opened
2026/05/29 14:33:46 INF [RTMP] [conn [::1]:49652] is reading from path 'mystream', 2 tracks (H264, MPEG-4 Audio)
$ srtla_send 5000 127.0.0.1 8891 ./ips
ffmpeg -re -f lavfi -i testsrc=size=1280x720:rate=30 -f lavfi -i sine=frequency=1000 -c:v libx264 -preset ultrafast -tune zerolatency -c:a aac -f mpegts "srt://127.0.0.1:5000?streamid=publish:mystream&pkt_size=1316"
Input #0, lavfi, from 'testsrc=size=1280x720:rate=30':
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0: Video: wrapped_avframe, rgb24, 1280x720 [SAR 1:1 DAR 16:9], 30 fps, 30 tbr, 30 tbn
Input #1, lavfi, from 'sine=frequency=1000':
  Duration: N/A, start: 0.000000, bitrate: 705 kb/s
  Stream #1:0: Audio: pcm_s16le, 44100 Hz, mono, s16, 705 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (wrapped_avframe (native) -> h264 (libx264))
  Stream #1:0 -> #0:1 (pcm_s16le (native) -> aac (native))
Press [q] to stop, [?] for help
[libx264 @ 0x155e07df0] using SAR=1/1
[libx264 @ 0x155e07df0] using cpu capabilities: ARMv8 NEON DotProd I8MM
[libx264 @ 0x155e07df0] profile High 4:4:4 Predictive, level 3.1, 4:4:4, 8-bit
Output #0, mpegts, to 'srt://127.0.0.1:5000?streamid=publish:mystream&pkt_size=1316':
  Metadata:
    encoder         : Lavf62.12.101
  Stream #0:0: Video: h264, yuv444p(tv, progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 30 fps, 90k tbn
    Metadata:
      encoder         : Lavc62.28.101 libx264
    Side data:
      CPB properties: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
  Stream #0:1: Audio: aac (LC), 44100 Hz, mono, fltp, 69 kb/s
    Metadata:
      encoder         : Lavc62.28.101 aac
frame= 4411 fps= 30 q=15.0 size=    7522KiB time=00:02:27.03 bitrate= 419.1kbits/s speed=   1x elapsed=0:02:26.51
❯ ffprobe "rtmp://localhost:1935/mystream"
Input #0, flv, from 'rtmp://localhost:1935/mystream':
  Duration: N/A, start: 171.061000, bitrate: N/A
  Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 30.30 fps, 30 tbr, 1k tbn, start 175.000000
  Stream #0:1: Audio: aac (LC), 44100 Hz, mono, fltp, start 171.061000
# SRTLA groups
srtla_groups{id="21...",path="mystream"} 1
srtla_groups_conns_active{id="21...",path="mystream"} 1
srtla_groups_bytes_received{id="21...",path="mystream"} 843368
srtla_groups_bytes_forwarded{id="21...",path="mystream"} 843368

@thejoeejoee thejoeejoee marked this pull request as ready for review May 29, 2026 15:00
@thejoeejoee thejoeejoee changed the title feat: SRTLA support feat: SRTLA receiver support May 29, 2026
Implements SRTLA (SRT Link Aggregation) protocol receiver as a UDP
proxy server that aggregates multiple sender connections into a
single SRT stream, enabling mobile IRL streaming over bonded
network links (BELABOX, IRL Pro, Moblin).

Resolves bluenviron#3029
Match BELABOX receiver behavior: broadcast SRT ACKs to all
connections, send other packets only to the most recently
active SRTLA address. Also adds config validation and matches
upstream maxConnsPerGroup=8.
Wire SRTLA groups to their corresponding SRT connections for:
- Per-path metrics (bytes_received, bytes_forwarded, conns_active)
- Enriched logging with stream path context
- Lifecycle coupling (SRT close triggers SRTLA group teardown)

Use interfaceIsEmpty() for nil-safe metrics check matching SRT pattern.
Address review findings:
- Lazy srtConn creation on first data packet (not during REG1)
- sendReg2/sendReg3 return errors with state rollback on failure
- IPv6 loopback resolution for dual-stack/IPv6-only setups
- Close() atomically clears maps, preventing post-close state leaks
- All network I/O moved outside mutex
- cleanup() repairs lastAddr when timed-out conn was current target
- All send errors logged with context
- Shutdown race fix: closed check + wg.Add inside mutex
- 13 regression tests (including IPv6 loopback resolution)
@xemles

xemles commented Jun 13, 2026

Copy link
Copy Markdown

you are a mad lad, works perfectly for me! timing couldn't have been better for an upcoming small live production

@xemles

xemles commented Jun 14, 2026

Copy link
Copy Markdown

I was trying to push MediaMTX a bit and managed to crash it.

What I had running:

  • An SRTLA source connected and streaming
  • vMix regularly trying to pull some SRT feeds that weren't actually connected / publishing

After a while, MediaMTX died with fatal error: index out of range

I'm not a Go expert so I can't say exactly what went wrong, but it might be some kind of race condition.

The crash happened in the SRT server (internal/servers/srt/server.go, around line 170).

To reproduce:

  1. Enable SRT + SRTLA in MediaMTX
  2. Connect an SRTLA publisher
  3. Have vMix keep retrying SRT paths that don't have a stream
  4. Wait, it crashed for me under that load.

I'm pretty sure it didn't even need to have a source connected.

fatal error: index out of range

goroutine 43 gp=0x3b5b3c377c20 m=20 mp=0x3b5b3c7b4808 [running]:
runtime.throw({0x7ff7712ab482?, 0x1?})
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/panic.go:1229 +0x4d fp=0x3b5b3c039848 sp=0x3b5b3c039818 pc=0x7ff76feba7ed
runtime.panicCheck1(0x7ff772813e07?, {0x7ff7712ab482, 0x12})
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/panic.go:59 +0x94 fp=0x3b5b3c039868 sp=0x3b5b3c039848 pc=0x7ff76fe7fd34
runtime.panicBounds64(0x7ff76fe975f0, 0x3b5b3c0398d8)
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/panic.go:218 +0x7e fp=0x3b5b3c0398c8 sp=0x3b5b3c039868 pc=0x7ff76fe7fe3e
runtime.panicBounds()
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/asm_amd64.s:2129 +0x68 fp=0x3b5b3c039968 sp=0x3b5b3c0398c8 pc=0x7ff76fec2b68
runtime.sellock({0x3b5b3c039e20, 0x7, 0x3b5b3c039a00?}, {0x3b5b3c039b32, 0x1000000007, 0x7ff76fe53de9?})
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/select.go:37 +0x90 fp=0x3b5b3c039990 sp=0x3b5b3c039968 pc=0x7ff76fe975f0
runtime.selectgo(0x3b5b3c039e20, 0x3b5b3c039b24, 0x3b5b3c0de160?, 0x0, 0x0?, 0x1)
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/select.go:354 +0xad9 fp=0x3b5b3c039ac0 sp=0x3b5b3c039990 pc=0x7ff76fe98299
github.com/bluenviron/mediamtx/internal/servers/srt.(*Server).run(0x3b5b3c2ff0e0)
        C:/Users/vMix/mediamtx-srtla/mediamtx/internal/servers/srt/server.go:170 +0x24b fp=0x3b5b3c039fc8 sp=0x3b5b3c039ac0 pc=0x7ff770b5762b
github.com/bluenviron/mediamtx/internal/servers/srt.(*Server).Initialize.gowrap1()
        C:/Users/vMix/mediamtx-srtla/mediamtx/internal/servers/srt/server.go:139 +0x17 fp=0x3b5b3c039fe0 sp=0x3b5b3c039fc8 pc=0x7ff770b571d7
runtime.goexit({})
        C:/Users/vMix/mediamtx-srtla/.tools/go/src/runtime/asm_amd64.s:1771 +0x1 fp=0x3b5b3c039fe8 sp=0x3b5b3c039fe0 pc=0x7ff76fec2701
created by github.com/bluenviron/mediamtx/internal/servers/srt.(*Server).Initialize in goroutine 1
        C:/Users/vMix/mediamtx-srtla/mediamtx/internal/servers/srt/server.go:139 +0x529

@thejoeejoee

Copy link
Copy Markdown
Author

@xemles thanks for the report! un/fortunately this looks like golang runtime issue on Windows, fixed with >=1.26.2: golang/go#77975 -- would you please try compiling under newer golang compiler? thx!

@xemles

xemles commented Jun 15, 2026

Copy link
Copy Markdown

Yup, seems like that was the issue, been running for 2hrs with no crash, whoops!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: SRTLA Support

2 participants