mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-26 20:31:11 +08:00
225 lines
6.0 KiB
Go
225 lines
6.0 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
"github.com/AlexxIT/go2rtc/pkg/xnet"
|
|
"github.com/pion/ice/v4"
|
|
"github.com/pion/interceptor"
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
// ReceiveMTU = Ethernet MTU (1500) - IP Header (20) - UDP Header (8)
|
|
// https://ffmpeg.org/ffmpeg-all.html#Muxer
|
|
const ReceiveMTU = 1472
|
|
|
|
func NewAPI() (*webrtc.API, error) {
|
|
return NewServerAPI("", "", nil)
|
|
}
|
|
|
|
type Filters struct {
|
|
Candidates []string `yaml:"candidates"`
|
|
Loopback bool `yaml:"loopback"`
|
|
Interfaces []string `yaml:"interfaces"`
|
|
IPs []string `yaml:"ips"`
|
|
Networks []string `yaml:"networks"`
|
|
UDPPorts []uint16 `yaml:"udp_ports"`
|
|
}
|
|
|
|
func NewServerAPI(network, address string, filters *Filters) (*webrtc.API, error) {
|
|
// for debug logs add to env: `PION_LOG_DEBUG=all`
|
|
m := &webrtc.MediaEngine{}
|
|
//if err := m.RegisterDefaultCodecs(); err != nil {
|
|
// return nil, err
|
|
//}
|
|
if err := RegisterDefaultCodecs(m); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
i := &interceptor.Registry{}
|
|
if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := webrtc.SettingEngine{}
|
|
|
|
// fix https://github.com/pion/webrtc/pull/2407
|
|
s.SetDTLSInsecureSkipHelloVerify(true)
|
|
|
|
if filters != nil && filters.Loopback {
|
|
s.SetIncludeLoopbackCandidate(true)
|
|
}
|
|
|
|
var interfaceFilter func(name string) bool
|
|
if filters != nil && filters.Interfaces != nil {
|
|
interfaceFilter = func(name string) bool {
|
|
return core.Contains(filters.Interfaces, name)
|
|
}
|
|
} else {
|
|
// default interfaces - all, except loopback
|
|
}
|
|
s.SetInterfaceFilter(interfaceFilter)
|
|
|
|
var ipFilter func(ip net.IP) bool
|
|
if filters != nil && filters.IPs != nil {
|
|
ipFilter = func(ip net.IP) bool {
|
|
return core.Contains(filters.IPs, ip.String())
|
|
}
|
|
} else {
|
|
// try filter all Docker-like interfaces
|
|
ipFilter = func(ip net.IP) bool {
|
|
return !xnet.Docker.Contains(ip)
|
|
}
|
|
// if there are no such interfaces - disable the filter
|
|
// the user will need to enable port forwarding
|
|
if nets, _ := xnet.IPNets(ipFilter); len(nets) == 0 {
|
|
ipFilter = nil
|
|
}
|
|
}
|
|
s.SetIPFilter(ipFilter)
|
|
|
|
var networkTypes []webrtc.NetworkType
|
|
if filters != nil && filters.Networks != nil {
|
|
for _, s := range filters.Networks {
|
|
if networkType, err := webrtc.NewNetworkType(s); err == nil {
|
|
networkTypes = append(networkTypes, networkType)
|
|
}
|
|
}
|
|
} else {
|
|
// default network types - all
|
|
networkTypes = []webrtc.NetworkType{
|
|
webrtc.NetworkTypeUDP4, webrtc.NetworkTypeUDP6,
|
|
webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6,
|
|
}
|
|
}
|
|
s.SetNetworkTypes(networkTypes)
|
|
|
|
if filters != nil && len(filters.UDPPorts) == 2 {
|
|
_ = s.SetEphemeralUDPPortRange(filters.UDPPorts[0], filters.UDPPorts[1])
|
|
}
|
|
|
|
//if len(hosts) != 0 {
|
|
// // support only: host, srflx
|
|
// if candidateType, err := webrtc.NewICECandidateType(hosts[0]); err == nil {
|
|
// s.SetNAT1To1IPs(hosts[1:], candidateType)
|
|
// } else {
|
|
// s.SetNAT1To1IPs(hosts, 0) // 0 = host
|
|
// }
|
|
//}
|
|
|
|
if address != "" {
|
|
if network == "" || network == "tcp" {
|
|
if ln, err := net.Listen("tcp", address); err == nil {
|
|
tcpMux := webrtc.NewICETCPMux(nil, ln, 8)
|
|
s.SetICETCPMux(tcpMux)
|
|
}
|
|
}
|
|
|
|
if network == "" || network == "udp" {
|
|
// UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead
|
|
var udpMux ice.UDPMux
|
|
if port := xnet.ParseUnspecifiedPort(address); port != 0 {
|
|
var networks []ice.NetworkType
|
|
for _, ntype := range networkTypes {
|
|
networks = append(networks, ice.NetworkType(ntype))
|
|
}
|
|
|
|
udpMux, _ = ice.NewMultiUDPMuxFromPort(
|
|
port,
|
|
ice.UDPMuxFromPortWithInterfaceFilter(interfaceFilter),
|
|
ice.UDPMuxFromPortWithIPFilter(ipFilter),
|
|
ice.UDPMuxFromPortWithNetworks(networks...),
|
|
)
|
|
} else if ln, err := net.ListenPacket("udp", address); err == nil {
|
|
udpMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{UDPConn: ln})
|
|
}
|
|
s.SetICEUDPMux(udpMux)
|
|
}
|
|
}
|
|
|
|
return webrtc.NewAPI(
|
|
webrtc.WithMediaEngine(m),
|
|
webrtc.WithInterceptorRegistry(i),
|
|
webrtc.WithSettingEngine(s),
|
|
), nil
|
|
}
|
|
|
|
func RegisterDefaultCodecs(m *webrtc.MediaEngine) error {
|
|
for _, codec := range []webrtc.RTPCodecParameters{
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "minptime=10;useinbandfec=1",
|
|
},
|
|
PayloadType: 101, //111,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypePCMU, ClockRate: 8000,
|
|
},
|
|
PayloadType: 0,
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypePCMA, ClockRate: 8000,
|
|
},
|
|
PayloadType: 8,
|
|
},
|
|
} {
|
|
if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeAudio); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
videoRTCPFeedback := []webrtc.RTCPFeedback{
|
|
{"goog-remb", ""},
|
|
{"ccm", "fir"},
|
|
{"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: 96, // Chrome v110 - PayloadType: 102
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 97, // Chrome v110 - PayloadType: 106
|
|
},
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH264,
|
|
ClockRate: 90000,
|
|
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032",
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 98, // Chrome v110 - PayloadType: 112
|
|
},
|
|
// macOS Safari 15.1
|
|
{
|
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
MimeType: webrtc.MimeTypeH265,
|
|
ClockRate: 90000,
|
|
RTCPFeedback: videoRTCPFeedback,
|
|
},
|
|
PayloadType: 100,
|
|
},
|
|
} {
|
|
if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|