2020-10-23 03:54:50 +13:00
package config
import (
2024-07-19 07:48:09 +12:00
"encoding/json"
2020-10-23 03:54:50 +13:00
"strconv"
"strings"
2023-05-16 05:29:39 +12:00
"time"
2020-10-23 03:54:50 +13:00
2021-03-11 01:15:18 +13:00
"github.com/rs/zerolog/log"
2020-10-23 03:54:50 +13:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
2021-02-02 11:50:18 +13:00
2022-07-14 10:58:22 +12:00
"github.com/demodesk/neko/pkg/types"
"github.com/demodesk/neko/pkg/utils"
2020-10-23 03:54:50 +13:00
)
2021-09-03 07:37:24 +12:00
// default stun server
const defStunSrv = "stun:stun.l.google.com:19302"
2023-05-16 05:29:39 +12:00
type WebRTCEstimator struct {
Enabled bool
Passive bool
Debug bool
InitialBitrate int
// how often to read and process bandwidth estimation reports
ReadInterval time . Duration
// how long to wait for stable connection (only neutral or upward trend) before upgrading
StableDuration time . Duration
// how long to wait for unstable connection (downward trend) before downgrading
UnstableDuration time . Duration
// how long to wait for stalled connection (neutral trend with low bandwidth) before downgrading
StalledDuration time . Duration
// how long to wait before downgrading again after previous downgrade
DowngradeBackoff time . Duration
// how long to wait before upgrading again after previous upgrade
UpgradeBackoff time . Duration
// how bigger the difference between estimated and stream bitrate must be to trigger upgrade/downgrade
DiffThreshold float64
}
2020-10-23 03:54:50 +13:00
type WebRTC struct {
2023-03-28 05:34:24 +13:00
ICELite bool
ICETrickle bool
ICEServersFrontend [ ] types . ICEServer
ICEServersBackend [ ] types . ICEServer
EphemeralMin uint16
EphemeralMax uint16
TCPMux int
UDPMux int
2021-03-11 01:15:18 +13:00
NAT1To1IPs [ ] string
IpRetrievalUrl string
2023-02-27 09:54:10 +13:00
2023-05-16 05:29:39 +12:00
Estimator WebRTCEstimator
2020-10-23 03:54:50 +13:00
}
func ( WebRTC ) Init ( cmd * cobra . Command ) error {
2021-03-17 03:24:58 +13:00
cmd . PersistentFlags ( ) . Bool ( "webrtc.icelite" , false , "configures whether or not the ICE agent should be a lite agent" )
if err := viper . BindPFlag ( "webrtc.icelite" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.icelite" ) ) ; err != nil {
2020-10-23 03:54:50 +13:00
return err
}
2021-03-17 03:24:58 +13:00
cmd . PersistentFlags ( ) . Bool ( "webrtc.icetrickle" , true , "configures whether cadidates should be sent asynchronously using Trickle ICE" )
if err := viper . BindPFlag ( "webrtc.icetrickle" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.icetrickle" ) ) ; err != nil {
2020-10-23 03:54:50 +13:00
return err
}
2024-02-21 06:16:02 +13:00
// Looks like this is conflicting with the frontend and backend ICE servers since latest versions
//cmd.PersistentFlags().String("webrtc.iceservers", "[]", "Global STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys")
//if err := viper.BindPFlag("webrtc.iceservers", cmd.PersistentFlags().Lookup("webrtc.iceservers")); err != nil {
// return err
//}
2020-10-23 03:54:50 +13:00
2023-03-28 05:34:24 +13:00
cmd . PersistentFlags ( ) . String ( "webrtc.iceservers.frontend" , "[]" , "Frontend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys" )
if err := viper . BindPFlag ( "webrtc.iceservers.frontend" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.iceservers.frontend" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . String ( "webrtc.iceservers.backend" , "[]" , "Backend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys" )
if err := viper . BindPFlag ( "webrtc.iceservers.backend" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.iceservers.backend" ) ) ; err != nil {
return err
}
2022-02-10 09:45:03 +13:00
cmd . PersistentFlags ( ) . String ( "webrtc.epr" , "" , "limits the pool of ephemeral ports that ICE UDP connections can allocate from" )
if err := viper . BindPFlag ( "webrtc.epr" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.epr" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Int ( "webrtc.tcpmux" , 0 , "single TCP mux port for all peers" )
if err := viper . BindPFlag ( "webrtc.tcpmux" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.tcpmux" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Int ( "webrtc.udpmux" , 0 , "single UDP mux port for all peers, replaces EPR" )
if err := viper . BindPFlag ( "webrtc.udpmux" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.udpmux" ) ) ; err != nil {
return err
}
2021-03-17 03:24:58 +13:00
cmd . PersistentFlags ( ) . StringSlice ( "webrtc.nat1to1" , [ ] string { } , "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used" )
if err := viper . BindPFlag ( "webrtc.nat1to1" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.nat1to1" ) ) ; err != nil {
2020-10-23 03:54:50 +13:00
return err
}
2021-03-17 03:24:58 +13:00
cmd . PersistentFlags ( ) . String ( "webrtc.ip_retrieval_url" , "https://checkip.amazonaws.com" , "URL address used for retrieval of the external IP address" )
if err := viper . BindPFlag ( "webrtc.ip_retrieval_url" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.ip_retrieval_url" ) ) ; err != nil {
2021-03-11 01:15:18 +13:00
return err
}
2023-02-27 09:54:10 +13:00
// bandwidth estimator
cmd . PersistentFlags ( ) . Bool ( "webrtc.estimator.enabled" , false , "enables the bandwidth estimator" )
if err := viper . BindPFlag ( "webrtc.estimator.enabled" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.enabled" ) ) ; err != nil {
return err
}
2023-03-14 05:55:52 +13:00
cmd . PersistentFlags ( ) . Bool ( "webrtc.estimator.passive" , false , "passive estimator mode, when it does not switch pipelines, only estimates" )
if err := viper . BindPFlag ( "webrtc.estimator.passive" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.passive" ) ) ; err != nil {
return err
}
2023-05-16 05:29:39 +12:00
cmd . PersistentFlags ( ) . Bool ( "webrtc.estimator.debug" , false , "enables debug logging for the bandwidth estimator" )
if err := viper . BindPFlag ( "webrtc.estimator.debug" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.debug" ) ) ; err != nil {
return err
}
2023-02-27 09:54:10 +13:00
cmd . PersistentFlags ( ) . Int ( "webrtc.estimator.initial_bitrate" , 1_000_000 , "initial bitrate for the bandwidth estimator" )
if err := viper . BindPFlag ( "webrtc.estimator.initial_bitrate" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.initial_bitrate" ) ) ; err != nil {
return err
}
2023-05-16 05:29:39 +12:00
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.read_interval" , 2 * time . Second , "how often to read and process bandwidth estimation reports" )
if err := viper . BindPFlag ( "webrtc.estimator.read_interval" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.read_interval" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.stable_duration" , 12 * time . Second , "how long to wait for stable connection (upward or neutral trend) before upgrading" )
if err := viper . BindPFlag ( "webrtc.estimator.stable_duration" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.stable_duration" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.unstable_duration" , 6 * time . Second , "how long to wait for stalled connection (neutral trend with low bandwidth) before downgrading" )
if err := viper . BindPFlag ( "webrtc.estimator.unstable_duration" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.unstable_duration" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.stalled_duration" , 24 * time . Second , "how long to wait for stalled bandwidth estimation before downgrading" )
if err := viper . BindPFlag ( "webrtc.estimator.stalled_duration" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.stalled_duration" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.downgrade_backoff" , 10 * time . Second , "how long to wait before downgrading again after previous downgrade" )
if err := viper . BindPFlag ( "webrtc.estimator.downgrade_backoff" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.downgrade_backoff" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Duration ( "webrtc.estimator.upgrade_backoff" , 5 * time . Second , "how long to wait before upgrading again after previous upgrade" )
if err := viper . BindPFlag ( "webrtc.estimator.upgrade_backoff" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.upgrade_backoff" ) ) ; err != nil {
return err
}
cmd . PersistentFlags ( ) . Float64 ( "webrtc.estimator.diff_threshold" , 0.15 , "how bigger the difference between estimated and stream bitrate must be to trigger upgrade/downgrade" )
if err := viper . BindPFlag ( "webrtc.estimator.diff_threshold" , cmd . PersistentFlags ( ) . Lookup ( "webrtc.estimator.diff_threshold" ) ) ; err != nil {
return err
}
2020-10-23 03:54:50 +13:00
return nil
}
2024-07-19 07:48:09 +12:00
func ( WebRTC ) InitV2 ( cmd * cobra . Command ) error {
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . String ( "epr" , "" , "V2: limits the pool of ephemeral ports that ICE UDP connections can allocate from" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "epr" , cmd . PersistentFlags ( ) . Lookup ( "epr" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . StringSlice ( "nat1to1" , [ ] string { } , "V2: sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "nat1to1" , cmd . PersistentFlags ( ) . Lookup ( "nat1to1" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . Int ( "tcpmux" , 0 , "V2: single TCP mux port for all peers" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "tcpmux" , cmd . PersistentFlags ( ) . Lookup ( "tcpmux" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . Int ( "udpmux" , 0 , "V2: single UDP mux port for all peers" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "udpmux" , cmd . PersistentFlags ( ) . Lookup ( "udpmux" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . String ( "ipfetch" , "" , "V2: automatically fetch IP address from given URL when nat1to1 is not present" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "ipfetch" , cmd . PersistentFlags ( ) . Lookup ( "ipfetch" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . Bool ( "icelite" , false , "V2: configures whether or not the ice agent should be a lite agent" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "icelite" , cmd . PersistentFlags ( ) . Lookup ( "icelite" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . StringSlice ( "iceserver" , [ ] string { } , "V2: describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "iceserver" , cmd . PersistentFlags ( ) . Lookup ( "iceserver" ) ) ; err != nil {
return err
}
2024-07-19 07:54:53 +12:00
cmd . PersistentFlags ( ) . String ( "iceservers" , "" , "V2: describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer" )
2024-07-19 07:48:09 +12:00
if err := viper . BindPFlag ( "iceservers" , cmd . PersistentFlags ( ) . Lookup ( "iceservers" ) ) ; err != nil {
return err
}
return nil
}
2020-10-23 03:54:50 +13:00
func ( s * WebRTC ) Set ( ) {
2021-03-17 03:24:58 +13:00
s . ICELite = viper . GetBool ( "webrtc.icelite" )
s . ICETrickle = viper . GetBool ( "webrtc.icetrickle" )
2021-03-18 03:47:49 +13:00
2023-03-28 05:34:24 +13:00
// parse frontend ice servers
if err := viper . UnmarshalKey ( "webrtc.iceservers.frontend" , & s . ICEServersFrontend , viper . DecodeHook (
utils . JsonStringAutoDecode ( [ ] types . ICEServer { } ) ,
2021-03-18 03:47:49 +13:00
) ) ; err != nil {
2023-03-28 05:34:24 +13:00
log . Warn ( ) . Err ( err ) . Msgf ( "unable to parse frontend ICE servers" )
}
// parse backend ice servers
if err := viper . UnmarshalKey ( "webrtc.iceservers.backend" , & s . ICEServersBackend , viper . DecodeHook (
utils . JsonStringAutoDecode ( [ ] types . ICEServer { } ) ,
) ) ; err != nil {
log . Warn ( ) . Err ( err ) . Msgf ( "unable to parse backend ICE servers" )
}
if s . ICELite && len ( s . ICEServersBackend ) > 0 {
log . Warn ( ) . Msgf ( "ICE Lite is enabled, but backend ICE servers are configured. Backend ICE servers will be ignored." )
2021-03-18 03:47:49 +13:00
}
2023-03-28 05:34:24 +13:00
// if no frontend or backend ice servers are configured
if len ( s . ICEServersFrontend ) == 0 && len ( s . ICEServersBackend ) == 0 {
// parse global ice servers
var iceServers [ ] types . ICEServer
if err := viper . UnmarshalKey ( "webrtc.iceservers" , & iceServers , viper . DecodeHook (
utils . JsonStringAutoDecode ( [ ] types . ICEServer { } ) ,
) ) ; err != nil {
log . Warn ( ) . Err ( err ) . Msgf ( "unable to parse global ICE servers" )
}
// add default stun server if none are configured
if len ( iceServers ) == 0 {
iceServers = append ( iceServers , types . ICEServer {
URLs : [ ] string { defStunSrv } ,
} )
}
s . ICEServersFrontend = append ( s . ICEServersFrontend , iceServers ... )
s . ICEServersBackend = append ( s . ICEServersBackend , iceServers ... )
2021-03-18 03:47:49 +13:00
}
2020-10-23 03:54:50 +13:00
2022-02-10 09:45:03 +13:00
s . TCPMux = viper . GetInt ( "webrtc.tcpmux" )
s . UDPMux = viper . GetInt ( "webrtc.udpmux" )
epr := viper . GetString ( "webrtc.epr" )
if epr != "" {
ports := strings . SplitN ( epr , "-" , - 1 )
if len ( ports ) > 1 {
min , err := strconv . ParseUint ( ports [ 0 ] , 10 , 16 )
if err != nil {
log . Panic ( ) . Err ( err ) . Msgf ( "unable to parse ephemeral min port" )
}
max , err := strconv . ParseUint ( ports [ 1 ] , 10 , 16 )
if err != nil {
log . Panic ( ) . Err ( err ) . Msgf ( "unable to parse ephemeral max port" )
}
s . EphemeralMin = uint16 ( min )
s . EphemeralMax = uint16 ( max )
}
if s . EphemeralMin > s . EphemeralMax {
log . Panic ( ) . Msgf ( "ephemeral min port cannot be bigger than max" )
}
}
if epr == "" && s . TCPMux == 0 && s . UDPMux == 0 {
// using default epr range
s . EphemeralMin = 59000
s . EphemeralMax = 59100
log . Warn ( ) .
Uint16 ( "min" , s . EphemeralMin ) .
Uint16 ( "max" , s . EphemeralMax ) .
Msgf ( "no TCP, UDP mux or epr specified, using default epr range" )
}
2021-03-17 03:24:58 +13:00
s . NAT1To1IPs = viper . GetStringSlice ( "webrtc.nat1to1" )
s . IpRetrievalUrl = viper . GetString ( "webrtc.ip_retrieval_url" )
2021-03-11 01:15:18 +13:00
if s . IpRetrievalUrl != "" && len ( s . NAT1To1IPs ) == 0 {
ip , err := utils . HttpRequestGET ( s . IpRetrievalUrl )
2020-10-23 03:54:50 +13:00
if err == nil {
s . NAT1To1IPs = append ( s . NAT1To1IPs , ip )
2021-03-11 01:15:18 +13:00
} else {
log . Warn ( ) . Err ( err ) . Msgf ( "IP retrieval failed" )
2020-10-23 03:54:50 +13:00
}
}
2023-02-27 09:54:10 +13:00
// bandwidth estimator
2023-05-16 05:29:39 +12:00
s . Estimator . Enabled = viper . GetBool ( "webrtc.estimator.enabled" )
s . Estimator . Passive = viper . GetBool ( "webrtc.estimator.passive" )
s . Estimator . Debug = viper . GetBool ( "webrtc.estimator.debug" )
s . Estimator . InitialBitrate = viper . GetInt ( "webrtc.estimator.initial_bitrate" )
s . Estimator . ReadInterval = viper . GetDuration ( "webrtc.estimator.read_interval" )
s . Estimator . StableDuration = viper . GetDuration ( "webrtc.estimator.stable_duration" )
s . Estimator . UnstableDuration = viper . GetDuration ( "webrtc.estimator.unstable_duration" )
s . Estimator . StalledDuration = viper . GetDuration ( "webrtc.estimator.stalled_duration" )
s . Estimator . DowngradeBackoff = viper . GetDuration ( "webrtc.estimator.downgrade_backoff" )
s . Estimator . UpgradeBackoff = viper . GetDuration ( "webrtc.estimator.upgrade_backoff" )
s . Estimator . DiffThreshold = viper . GetFloat64 ( "webrtc.estimator.diff_threshold" )
2020-10-23 03:54:50 +13:00
}
2024-07-19 07:48:09 +12:00
func ( s * WebRTC ) SetV2 ( ) {
if viper . IsSet ( "nat1to1" ) {
s . NAT1To1IPs = viper . GetStringSlice ( "nat1to1" )
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_NAT1TO1' which is deprecated, please use 'NEKO_WEBRTC_NAT1TO1' instead" )
}
if viper . IsSet ( "tcpmux" ) {
s . TCPMux = viper . GetInt ( "tcpmux" )
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_TCPMUX' which is deprecated, please use 'NEKO_WEBRTC_TCPMUX' instead" )
}
if viper . IsSet ( "udpmux" ) {
s . UDPMux = viper . GetInt ( "udpmux" )
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_UDPMUX' which is deprecated, please use 'NEKO_WEBRTC_UDPMUX' instead" )
}
if viper . IsSet ( "icelite" ) {
s . ICELite = viper . GetBool ( "icelite" )
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_ICELITE' which is deprecated, please use 'NEKO_WEBRTC_ICELITE' instead" )
}
if viper . IsSet ( "iceservers" ) {
iceServers := [ ] types . ICEServer { }
iceServersJson := viper . GetString ( "iceservers" )
if iceServersJson != "" {
err := json . Unmarshal ( [ ] byte ( iceServersJson ) , & iceServers )
if err != nil {
log . Panic ( ) . Err ( err ) . Msg ( "failed to process iceservers" )
}
}
s . ICEServersFrontend = iceServers
s . ICEServersBackend = iceServers
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_ICESERVERS' which is deprecated, please use 'NEKO_WEBRTC_ICESERVERS_FRONTEND' and/or 'NEKO_WEBRTC_ICESERVERS_BACKEND' instead" )
}
if viper . IsSet ( "iceserver" ) {
iceServerSlice := viper . GetStringSlice ( "iceserver" )
if len ( iceServerSlice ) > 0 {
s . ICEServersFrontend = append ( s . ICEServersFrontend , types . ICEServer { URLs : iceServerSlice } )
s . ICEServersBackend = append ( s . ICEServersBackend , types . ICEServer { URLs : iceServerSlice } )
}
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_ICESERVER' which is deprecated, please use 'NEKO_WEBRTC_ICESERVERS_FRONTEND' and/or 'NEKO_WEBRTC_ICESERVERS_BACKEND' instead" )
}
if viper . IsSet ( "ipfetch" ) {
if len ( s . NAT1To1IPs ) == 0 {
ipfetch := viper . GetString ( "ipfetch" )
ip , err := utils . HttpRequestGET ( ipfetch )
if err != nil {
log . Panic ( ) . Err ( err ) . Str ( "ipfetch" , ipfetch ) . Msg ( "failed to fetch ip address" )
}
s . NAT1To1IPs = append ( s . NAT1To1IPs , ip )
}
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_IPFETCH' which is deprecated, please use 'NEKO_WEBRTC_IP_RETRIEVAL_URL' instead" )
}
if viper . IsSet ( "epr" ) {
min := uint16 ( 59000 )
max := uint16 ( 59100 )
epr := viper . GetString ( "epr" )
ports := strings . SplitN ( epr , "-" , - 1 )
if len ( ports ) > 1 {
start , err := strconv . ParseUint ( ports [ 0 ] , 10 , 16 )
if err == nil {
min = uint16 ( start )
}
end , err := strconv . ParseUint ( ports [ 1 ] , 10 , 16 )
if err == nil {
max = uint16 ( end )
}
}
if min > max {
s . EphemeralMin = max
s . EphemeralMax = min
} else {
s . EphemeralMin = min
s . EphemeralMax = max
}
log . Warn ( ) . Msg ( "you are using v2 configuration 'NEKO_EPR' which is deprecated, please use 'NEKO_WEBRTC_EPR' instead" )
}
}