mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-11 00:05:36 +02:00
- Also added new latency control parameter, not super visible differences, but it's cool :)
218 lines
6.7 KiB
Go
218 lines
6.7 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/pion/interceptor/pkg/nack"
|
|
"log/slog"
|
|
"strconv"
|
|
|
|
"github.com/libp2p/go-reuseport"
|
|
"github.com/pion/ice/v4"
|
|
"github.com/pion/interceptor"
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
var globalWebRTCAPI *webrtc.API
|
|
var globalWebRTCConfig = webrtc.Configuration{
|
|
ICETransportPolicy: webrtc.ICETransportPolicyAll,
|
|
BundlePolicy: webrtc.BundlePolicyBalanced,
|
|
SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
|
|
}
|
|
|
|
func InitWebRTCAPI() error {
|
|
var err error
|
|
flags := GetFlags()
|
|
|
|
// Media engine
|
|
mediaEngine := &webrtc.MediaEngine{}
|
|
|
|
// Register our extensions
|
|
if err = RegisterExtensions(mediaEngine); err != nil {
|
|
return fmt.Errorf("failed to register extensions: %w", err)
|
|
}
|
|
|
|
// Register codecs
|
|
for _, codec := range []webrtc.RTPCodecParameters{
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "minptime=10;useinbandfec=1"},
|
|
PayloadType: 111,
|
|
},
|
|
} {
|
|
if err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
videoRTCPFeedback := []webrtc.RTCPFeedback{{"nack", ""}, {"nack", "pli"}}
|
|
for _, codec := range []webrtc.RTPCodecParameters{
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 102,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 104,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 106,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 108,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 127,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 39,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH265,
|
|
ClockRate: 90000,
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 116,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeAV1, ClockRate: 90000, RTCPFeedback: videoRTCPFeedback},
|
|
PayloadType: 45,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=0", RTCPFeedback: videoRTCPFeedback},
|
|
PayloadType: 98,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=2", RTCPFeedback: videoRTCPFeedback},
|
|
PayloadType: 100,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264, ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 112,
|
|
},
|
|
} {
|
|
if err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Interceptor registry
|
|
interceptorRegistry := &interceptor.Registry{}
|
|
|
|
// Register our interceptors..
|
|
nackGenFactory, err := nack.NewGeneratorInterceptor()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
interceptorRegistry.Add(nackGenFactory)
|
|
nackRespFactory, err := nack.NewResponderInterceptor()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
interceptorRegistry.Add(nackRespFactory)
|
|
|
|
if err = webrtc.ConfigureRTCPReports(interceptorRegistry); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Setting engine
|
|
settingEngine := webrtc.SettingEngine{}
|
|
|
|
// New in v4, reduces CPU usage and latency when enabled
|
|
settingEngine.EnableSCTPZeroChecksum(true)
|
|
|
|
nat11IP := GetFlags().NAT11IP
|
|
if len(nat11IP) > 0 {
|
|
settingEngine.SetNAT1To1IPs([]string{nat11IP}, webrtc.ICECandidateTypeHost)
|
|
slog.Info("Using NAT 1:1 IP for WebRTC", "nat11_ip", nat11IP)
|
|
}
|
|
|
|
muxPort := GetFlags().UDPMuxPort
|
|
if muxPort > 0 {
|
|
// Use reuseport to allow multiple listeners on the same port
|
|
pktListener, err := reuseport.ListenPacket("udp", ":"+strconv.Itoa(muxPort))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create WebRTC muxed UDP listener: %w", err)
|
|
}
|
|
|
|
mux := ice.NewMultiUDPMuxDefault(ice.NewUDPMuxDefault(ice.UDPMuxParams{
|
|
UDPConn: pktListener,
|
|
}))
|
|
slog.Info("Using UDP Mux for WebRTC", "port", muxPort)
|
|
settingEngine.SetICEUDPMux(mux)
|
|
}
|
|
|
|
if flags.WebRTCUDPStart > 0 && flags.WebRTCUDPEnd > 0 && flags.WebRTCUDPStart < flags.WebRTCUDPEnd {
|
|
// Set the UDP port range used by WebRTC
|
|
err = settingEngine.SetEphemeralUDPPortRange(uint16(flags.WebRTCUDPStart), uint16(flags.WebRTCUDPEnd))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
slog.Info("Using WebRTC UDP Port Range", "start", flags.WebRTCUDPStart, "end", flags.WebRTCUDPEnd)
|
|
}
|
|
|
|
// Improves speed when sending offers to browsers (https://github.com/pion/webrtc/issues/3174)
|
|
settingEngine.SetIncludeLoopbackCandidate(true)
|
|
|
|
// Create a new API object with our customized settings
|
|
globalWebRTCAPI = webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithSettingEngine(settingEngine), webrtc.WithInterceptorRegistry(interceptorRegistry))
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreatePeerConnection sets up a new peer connection
|
|
func CreatePeerConnection(onClose func()) (*webrtc.PeerConnection, error) {
|
|
pc, err := globalWebRTCAPI.NewPeerConnection(globalWebRTCConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Log connection state changes and handle failed/disconnected connections
|
|
pc.OnConnectionStateChange(func(connectionState webrtc.PeerConnectionState) {
|
|
// Close PeerConnection in cases
|
|
if connectionState == webrtc.PeerConnectionStateFailed ||
|
|
connectionState == webrtc.PeerConnectionStateDisconnected ||
|
|
connectionState == webrtc.PeerConnectionStateClosed {
|
|
err = pc.Close()
|
|
if err != nil {
|
|
slog.Error("Failed to close PeerConnection", "err", err)
|
|
}
|
|
onClose()
|
|
}
|
|
})
|
|
|
|
return pc, nil
|
|
}
|