Introduces the subsystem layer that sits alongside api.API and wires
the M1 core/webrtc primitives into the per-process restream lifecycle.
app/webrtc/subsystem.go:
- Subsystem struct holding the global WebRTC config, core PeerFactory,
per-process stream map, and logger
- New(config.DataWebRTC, logger) constructor
- Enabled(), Hooks(), Close(), lookup() methods
app/webrtc/lifecycle.go:
- onProcessStart: allocates an adjacent UDP port pair, binds two
Pion Sources (video on V, audio on V+1), registers them under the
process id, and returns the two RTP output legs to append to the
FFmpeg command.
- onProcessStop: tears down the pair.
- allocAdjacentPair: retries up to 10 times to find a free (V, V+1)
pair since the kernel's ephemeral picker can hand us an odd port.
- splitRTPLegs: converts BuildArgs' flat []string into two ConfigIO
entries by splitting on the second -map token.
core/webrtc/peer.go + forward.go:
- Adds PeerFactory.CreatePeerFromSources for the M2 two-source
forwarding mode (video and audio on separate UDP ports, no
payload-type sniffing). Leaves CreatePeer intact for the M1 PoC.
- Adds forwardRTPSplit companion goroutine.
config/data.go:
- Promote anonymous WebRTC struct to named type DataWebRTC so
app/webrtc can accept it by value.
62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
// forwardRTP reads packets from sub and writes them to the correct track
|
|
// based on payload type (H.264 → video, Opus → audio). Used by the M1
|
|
// single-source PoC where FFmpeg emits both video and audio RTP to the
|
|
// same UDP port.
|
|
func forwardRTP(done <-chan struct{}, sub <-chan *rtp.Packet,
|
|
video, audio *webrtc.TrackLocalStaticRTP) {
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
case pkt, ok := <-sub:
|
|
if !ok {
|
|
return
|
|
}
|
|
// Pion default H.264 PT = 102, Opus PT = 111. If the publisher
|
|
// uses different PTs we'll revisit in M2 — for M1 PoC we
|
|
// configure FFmpeg to these values explicitly in the publisher
|
|
// script.
|
|
switch pkt.PayloadType {
|
|
case 102:
|
|
_ = video.WriteRTP(pkt)
|
|
case 111:
|
|
_ = audio.WriteRTP(pkt)
|
|
default:
|
|
// Unknown PT — drop. Log in M3.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// forwardRTPSplit is the M2 variant: it reads from two independent
|
|
// per-track channels (one video, one audio) and writes each to its
|
|
// own Pion track. This is the mode used when the restream manager
|
|
// emits two FFmpeg RTP legs on separate UDP ports. Either channel
|
|
// closing or done firing terminates the loop.
|
|
func forwardRTPSplit(done <-chan struct{},
|
|
videoSub <-chan *rtp.Packet, audioSub <-chan *rtp.Packet,
|
|
video, audio *webrtc.TrackLocalStaticRTP) {
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
case pkt, ok := <-videoSub:
|
|
if !ok {
|
|
return
|
|
}
|
|
_ = video.WriteRTP(pkt)
|
|
case pkt, ok := <-audioSub:
|
|
if !ok {
|
|
return
|
|
}
|
|
_ = audio.WriteRTP(pkt)
|
|
}
|
|
}
|
|
}
|