mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
move server to server directory.
This commit is contained in:
246
server/internal/config/capture.go
Normal file
246
server/internal/config/capture.go
Normal file
@ -0,0 +1,246 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/demodesk/neko/pkg/types/codec"
|
||||
"github.com/demodesk/neko/pkg/utils"
|
||||
)
|
||||
|
||||
type Capture struct {
|
||||
Display string
|
||||
|
||||
VideoCodec codec.RTPCodec
|
||||
VideoIDs []string
|
||||
VideoPipelines map[string]types.VideoConfig
|
||||
|
||||
AudioDevice string
|
||||
AudioCodec codec.RTPCodec
|
||||
AudioPipeline string
|
||||
|
||||
BroadcastAudioBitrate int
|
||||
BroadcastVideoBitrate int
|
||||
BroadcastPreset string
|
||||
BroadcastPipeline string
|
||||
BroadcastUrl string
|
||||
|
||||
ScreencastEnabled bool
|
||||
ScreencastRate string
|
||||
ScreencastQuality string
|
||||
ScreencastPipeline string
|
||||
|
||||
WebcamEnabled bool
|
||||
WebcamDevice string
|
||||
WebcamWidth int
|
||||
WebcamHeight int
|
||||
|
||||
MicrophoneEnabled bool
|
||||
MicrophoneDevice string
|
||||
}
|
||||
|
||||
func (Capture) Init(cmd *cobra.Command) error {
|
||||
// audio
|
||||
cmd.PersistentFlags().String("capture.audio.device", "audio_output.monitor", "pulseaudio device to capture")
|
||||
if err := viper.BindPFlag("capture.audio.device", cmd.PersistentFlags().Lookup("capture.audio.device")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.audio.codec", "opus", "audio codec to be used")
|
||||
if err := viper.BindPFlag("capture.audio.codec", cmd.PersistentFlags().Lookup("capture.audio.codec")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.audio.pipeline", "", "gstreamer pipeline used for audio streaming")
|
||||
if err := viper.BindPFlag("capture.audio.pipeline", cmd.PersistentFlags().Lookup("capture.audio.pipeline")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// videos
|
||||
cmd.PersistentFlags().String("capture.video.codec", "vp8", "video codec to be used")
|
||||
if err := viper.BindPFlag("capture.video.codec", cmd.PersistentFlags().Lookup("capture.video.codec")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringSlice("capture.video.ids", []string{}, "ordered list of video ids")
|
||||
if err := viper.BindPFlag("capture.video.ids", cmd.PersistentFlags().Lookup("capture.video.ids")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.video.pipelines", "[]", "pipelines config in JSON used for video streaming")
|
||||
if err := viper.BindPFlag("capture.video.pipelines", cmd.PersistentFlags().Lookup("capture.video.pipelines")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// broadcast
|
||||
cmd.PersistentFlags().Int("capture.broadcast.audio_bitrate", 128, "broadcast audio bitrate in KB/s")
|
||||
if err := viper.BindPFlag("capture.broadcast.audio_bitrate", cmd.PersistentFlags().Lookup("capture.broadcast.audio_bitrate")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Int("capture.broadcast.video_bitrate", 4096, "broadcast video bitrate in KB/s")
|
||||
if err := viper.BindPFlag("capture.broadcast.video_bitrate", cmd.PersistentFlags().Lookup("capture.broadcast.video_bitrate")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.broadcast.preset", "veryfast", "broadcast speed preset for h264 encoding")
|
||||
if err := viper.BindPFlag("capture.broadcast.preset", cmd.PersistentFlags().Lookup("capture.broadcast.preset")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.broadcast.pipeline", "", "gstreamer pipeline used for broadcasting")
|
||||
if err := viper.BindPFlag("capture.broadcast.pipeline", cmd.PersistentFlags().Lookup("capture.broadcast.pipeline")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.broadcast.url", "", "initial URL for broadcasting, setting this value will automatically start broadcasting")
|
||||
if err := viper.BindPFlag("capture.broadcast.url", cmd.PersistentFlags().Lookup("capture.broadcast.url")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// screencast
|
||||
cmd.PersistentFlags().Bool("capture.screencast.enabled", false, "enable screencast")
|
||||
if err := viper.BindPFlag("capture.screencast.enabled", cmd.PersistentFlags().Lookup("capture.screencast.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.screencast.rate", "10/1", "screencast frame rate")
|
||||
if err := viper.BindPFlag("capture.screencast.rate", cmd.PersistentFlags().Lookup("capture.screencast.rate")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.screencast.quality", "60", "screencast JPEG quality")
|
||||
if err := viper.BindPFlag("capture.screencast.quality", cmd.PersistentFlags().Lookup("capture.screencast.quality")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.screencast.pipeline", "", "gstreamer pipeline used for screencasting")
|
||||
if err := viper.BindPFlag("capture.screencast.pipeline", cmd.PersistentFlags().Lookup("capture.screencast.pipeline")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// webcam
|
||||
cmd.PersistentFlags().Bool("capture.webcam.enabled", false, "enable webcam stream")
|
||||
if err := viper.BindPFlag("capture.webcam.enabled", cmd.PersistentFlags().Lookup("capture.webcam.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sudo apt install v4l2loopback-dkms v4l2loopback-utils
|
||||
// sudo apt-get install linux-headers-`uname -r` linux-modules-extra-`uname -r`
|
||||
// sudo modprobe v4l2loopback exclusive_caps=1
|
||||
cmd.PersistentFlags().String("capture.webcam.device", "/dev/video0", "v4l2sink device used for webcam")
|
||||
if err := viper.BindPFlag("capture.webcam.device", cmd.PersistentFlags().Lookup("capture.webcam.device")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Int("capture.webcam.width", 1280, "webcam stream width")
|
||||
if err := viper.BindPFlag("capture.webcam.width", cmd.PersistentFlags().Lookup("capture.webcam.width")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Int("capture.webcam.height", 720, "webcam stream height")
|
||||
if err := viper.BindPFlag("capture.webcam.height", cmd.PersistentFlags().Lookup("capture.webcam.height")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// microphone
|
||||
cmd.PersistentFlags().Bool("capture.microphone.enabled", true, "enable microphone stream")
|
||||
if err := viper.BindPFlag("capture.microphone.enabled", cmd.PersistentFlags().Lookup("capture.microphone.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("capture.microphone.device", "audio_input", "pulseaudio device used for microphone")
|
||||
if err := viper.BindPFlag("capture.microphone.device", cmd.PersistentFlags().Lookup("capture.microphone.device")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Capture) Set() {
|
||||
var ok bool
|
||||
|
||||
// Display is provided by env variable
|
||||
s.Display = os.Getenv("DISPLAY")
|
||||
|
||||
// video
|
||||
videoCodec := viper.GetString("capture.video.codec")
|
||||
s.VideoCodec, ok = codec.ParseStr(videoCodec)
|
||||
if !ok || !s.VideoCodec.IsVideo() {
|
||||
log.Warn().Str("codec", videoCodec).Msgf("unknown video codec, using Vp8")
|
||||
s.VideoCodec = codec.VP8()
|
||||
}
|
||||
|
||||
s.VideoIDs = viper.GetStringSlice("capture.video.ids")
|
||||
if err := viper.UnmarshalKey("capture.video.pipelines", &s.VideoPipelines, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode(s.VideoPipelines),
|
||||
)); err != nil {
|
||||
log.Warn().Err(err).Msgf("unable to parse video pipelines")
|
||||
}
|
||||
|
||||
// default video
|
||||
if len(s.VideoPipelines) == 0 {
|
||||
log.Warn().Msgf("no video pipelines specified, using defaults")
|
||||
|
||||
s.VideoCodec = codec.VP8()
|
||||
s.VideoPipelines = map[string]types.VideoConfig{
|
||||
"main": {
|
||||
Fps: "25",
|
||||
GstEncoder: "vp8enc",
|
||||
GstParams: map[string]string{
|
||||
"target-bitrate": "round(3072 * 650)",
|
||||
"cpu-used": "4",
|
||||
"end-usage": "cbr",
|
||||
"threads": "4",
|
||||
"deadline": "1",
|
||||
"undershoot": "95",
|
||||
"buffer-size": "(3072 * 4)",
|
||||
"buffer-initial-size": "(3072 * 2)",
|
||||
"buffer-optimal-size": "(3072 * 3)",
|
||||
"keyframe-max-dist": "25",
|
||||
"min-quantizer": "4",
|
||||
"max-quantizer": "20",
|
||||
},
|
||||
},
|
||||
}
|
||||
s.VideoIDs = []string{"main"}
|
||||
}
|
||||
|
||||
// audio
|
||||
s.AudioDevice = viper.GetString("capture.audio.device")
|
||||
s.AudioPipeline = viper.GetString("capture.audio.pipeline")
|
||||
|
||||
audioCodec := viper.GetString("capture.audio.codec")
|
||||
s.AudioCodec, ok = codec.ParseStr(audioCodec)
|
||||
if !ok || !s.AudioCodec.IsAudio() {
|
||||
log.Warn().Str("codec", audioCodec).Msgf("unknown audio codec, using Opus")
|
||||
s.AudioCodec = codec.Opus()
|
||||
}
|
||||
|
||||
// broadcast
|
||||
s.BroadcastAudioBitrate = viper.GetInt("capture.broadcast.audio_bitrate")
|
||||
s.BroadcastVideoBitrate = viper.GetInt("capture.broadcast.video_bitrate")
|
||||
s.BroadcastPreset = viper.GetString("capture.broadcast.preset")
|
||||
s.BroadcastPipeline = viper.GetString("capture.broadcast.pipeline")
|
||||
s.BroadcastUrl = viper.GetString("capture.broadcast.url")
|
||||
|
||||
// screencast
|
||||
s.ScreencastEnabled = viper.GetBool("capture.screencast.enabled")
|
||||
s.ScreencastRate = viper.GetString("capture.screencast.rate")
|
||||
s.ScreencastQuality = viper.GetString("capture.screencast.quality")
|
||||
s.ScreencastPipeline = viper.GetString("capture.screencast.pipeline")
|
||||
|
||||
// webcam
|
||||
s.WebcamEnabled = viper.GetBool("capture.webcam.enabled")
|
||||
s.WebcamDevice = viper.GetString("capture.webcam.device")
|
||||
s.WebcamWidth = viper.GetInt("capture.webcam.width")
|
||||
s.WebcamHeight = viper.GetInt("capture.webcam.height")
|
||||
|
||||
// microphone
|
||||
s.MicrophoneEnabled = viper.GetBool("capture.microphone.enabled")
|
||||
s.MicrophoneDevice = viper.GetString("capture.microphone.device")
|
||||
}
|
8
server/internal/config/config.go
Normal file
8
server/internal/config/config.go
Normal file
@ -0,0 +1,8 @@
|
||||
package config
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
type Config interface {
|
||||
Init(cmd *cobra.Command) error
|
||||
Set()
|
||||
}
|
91
server/internal/config/desktop.go
Normal file
91
server/internal/config/desktop.go
Normal file
@ -0,0 +1,91 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
)
|
||||
|
||||
type Desktop struct {
|
||||
Display string
|
||||
|
||||
ScreenSize types.ScreenSize
|
||||
|
||||
UseInputDriver bool
|
||||
InputSocket string
|
||||
|
||||
Unminimize bool
|
||||
UploadDrop bool
|
||||
FileChooserDialog bool
|
||||
}
|
||||
|
||||
func (Desktop) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().String("desktop.screen", "1280x720@30", "default screen size and framerate")
|
||||
if err := viper.BindPFlag("desktop.screen", cmd.PersistentFlags().Lookup("desktop.screen")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("desktop.input.enabled", true, "whether custom xf86 input driver should be used to handle touchscreen")
|
||||
if err := viper.BindPFlag("desktop.input.enabled", cmd.PersistentFlags().Lookup("desktop.input.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("desktop.input.socket", "/tmp/xf86-input-neko.sock", "socket path for custom xf86 input driver connection")
|
||||
if err := viper.BindPFlag("desktop.input.socket", cmd.PersistentFlags().Lookup("desktop.input.socket")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("desktop.unminimize", true, "automatically unminimize window when it is minimized")
|
||||
if err := viper.BindPFlag("desktop.unminimize", cmd.PersistentFlags().Lookup("desktop.unminimize")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("desktop.upload_drop", true, "whether drop upload is enabled")
|
||||
if err := viper.BindPFlag("desktop.upload_drop", cmd.PersistentFlags().Lookup("desktop.upload_drop")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("desktop.file_chooser_dialog", false, "whether to handle file chooser dialog externally")
|
||||
if err := viper.BindPFlag("desktop.file_chooser_dialog", cmd.PersistentFlags().Lookup("desktop.file_chooser_dialog")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Desktop) Set() {
|
||||
// Display is provided by env variable
|
||||
s.Display = os.Getenv("DISPLAY")
|
||||
|
||||
s.ScreenSize = types.ScreenSize{
|
||||
Width: 1280,
|
||||
Height: 720,
|
||||
Rate: 30,
|
||||
}
|
||||
|
||||
r := regexp.MustCompile(`([0-9]{1,4})x([0-9]{1,4})@([0-9]{1,3})`)
|
||||
res := r.FindStringSubmatch(viper.GetString("desktop.screen"))
|
||||
|
||||
if len(res) > 0 {
|
||||
width, err1 := strconv.ParseInt(res[1], 10, 64)
|
||||
height, err2 := strconv.ParseInt(res[2], 10, 64)
|
||||
rate, err3 := strconv.ParseInt(res[3], 10, 64)
|
||||
|
||||
if err1 == nil && err2 == nil && err3 == nil {
|
||||
s.ScreenSize.Width = int(width)
|
||||
s.ScreenSize.Height = int(height)
|
||||
s.ScreenSize.Rate = int16(rate)
|
||||
}
|
||||
}
|
||||
|
||||
s.UseInputDriver = viper.GetBool("desktop.input.enabled")
|
||||
s.InputSocket = viper.GetString("desktop.input.socket")
|
||||
s.Unminimize = viper.GetBool("desktop.unminimize")
|
||||
s.UploadDrop = viper.GetBool("desktop.upload_drop")
|
||||
s.FileChooserDialog = viper.GetBool("desktop.file_chooser_dialog")
|
||||
}
|
128
server/internal/config/member.go
Normal file
128
server/internal/config/member.go
Normal file
@ -0,0 +1,128 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/demodesk/neko/internal/member/file"
|
||||
"github.com/demodesk/neko/internal/member/multiuser"
|
||||
"github.com/demodesk/neko/internal/member/object"
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/demodesk/neko/pkg/utils"
|
||||
)
|
||||
|
||||
type Member struct {
|
||||
Provider string
|
||||
|
||||
// providers
|
||||
File file.Config
|
||||
Object object.Config
|
||||
Multiuser multiuser.Config
|
||||
}
|
||||
|
||||
func (Member) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().String("member.provider", "multiuser", "choose member provider")
|
||||
if err := viper.BindPFlag("member.provider", cmd.PersistentFlags().Lookup("member.provider")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// file provider
|
||||
cmd.PersistentFlags().String("member.file.path", "", "member file provider: storage path")
|
||||
if err := viper.BindPFlag("member.file.path", cmd.PersistentFlags().Lookup("member.file.path")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("member.file.hash", true, "member file provider: whether to hash passwords using sha256 (recommended)")
|
||||
if err := viper.BindPFlag("member.file.hash", cmd.PersistentFlags().Lookup("member.file.hash")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// object provider
|
||||
cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: users in JSON format")
|
||||
if err := viper.BindPFlag("member.object.users", cmd.PersistentFlags().Lookup("member.object.users")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// multiuser provider
|
||||
cmd.PersistentFlags().String("member.multiuser.user_password", "neko", "member multiuser provider: user password")
|
||||
if err := viper.BindPFlag("member.multiuser.user_password", cmd.PersistentFlags().Lookup("member.multiuser.user_password")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("member.multiuser.admin_password", "admin", "member multiuser provider: admin password")
|
||||
if err := viper.BindPFlag("member.multiuser.admin_password", cmd.PersistentFlags().Lookup("member.multiuser.admin_password")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("member.multiuser.user_profile", "{}", "member multiuser provider: user profile in JSON format")
|
||||
if err := viper.BindPFlag("member.multiuser.user_profile", cmd.PersistentFlags().Lookup("member.multiuser.user_profile")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("member.multiuser.admin_profile", "{}", "member multiuser provider: admin profile in JSON format")
|
||||
if err := viper.BindPFlag("member.multiuser.admin_profile", cmd.PersistentFlags().Lookup("member.multiuser.admin_profile")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Member) Set() {
|
||||
s.Provider = viper.GetString("member.provider")
|
||||
|
||||
// file provider
|
||||
s.File.Path = viper.GetString("member.file.path")
|
||||
s.File.Hash = viper.GetBool("member.file.hash")
|
||||
|
||||
// object provider
|
||||
if err := viper.UnmarshalKey("member.object.users", &s.Object.Users, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode(s.Object.Users),
|
||||
)); err != nil {
|
||||
log.Warn().Err(err).Msgf("unable to parse member object users")
|
||||
}
|
||||
|
||||
// multiuser provider
|
||||
s.Multiuser.UserPassword = viper.GetString("member.multiuser.user_password")
|
||||
s.Multiuser.AdminPassword = viper.GetString("member.multiuser.admin_password")
|
||||
|
||||
// default user profile
|
||||
s.Multiuser.UserProfile = types.MemberProfile{
|
||||
IsAdmin: false,
|
||||
CanLogin: true,
|
||||
CanConnect: true,
|
||||
CanWatch: true,
|
||||
CanHost: true,
|
||||
CanShareMedia: true,
|
||||
CanAccessClipboard: true,
|
||||
SendsInactiveCursor: true,
|
||||
CanSeeInactiveCursors: false,
|
||||
}
|
||||
|
||||
// override user profile
|
||||
if err := viper.UnmarshalKey("member.multiuser.user_profile", &s.Multiuser.UserProfile, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode(s.Multiuser.UserProfile),
|
||||
)); err != nil {
|
||||
log.Warn().Err(err).Msgf("unable to parse member multiuser user profile")
|
||||
}
|
||||
|
||||
// default admin profile
|
||||
s.Multiuser.AdminProfile = types.MemberProfile{
|
||||
IsAdmin: true,
|
||||
CanLogin: true,
|
||||
CanConnect: true,
|
||||
CanWatch: true,
|
||||
CanHost: true,
|
||||
CanShareMedia: true,
|
||||
CanAccessClipboard: true,
|
||||
SendsInactiveCursor: true,
|
||||
CanSeeInactiveCursors: true,
|
||||
}
|
||||
|
||||
// override admin profile
|
||||
if err := viper.UnmarshalKey("member.multiuser.admin_profile", &s.Multiuser.AdminProfile, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode(s.Multiuser.AdminProfile),
|
||||
)); err != nil {
|
||||
log.Warn().Err(err).Msgf("unable to parse member multiuser admin profile")
|
||||
}
|
||||
}
|
37
server/internal/config/plugins.go
Normal file
37
server/internal/config/plugins.go
Normal file
@ -0,0 +1,37 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Plugins struct {
|
||||
Enabled bool
|
||||
Dir string
|
||||
Required bool
|
||||
}
|
||||
|
||||
func (Plugins) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().Bool("plugins.enabled", false, "load plugins in runtime")
|
||||
if err := viper.BindPFlag("plugins.enabled", cmd.PersistentFlags().Lookup("plugins.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("plugins.dir", "./bin/plugins", "path to neko plugins to load")
|
||||
if err := viper.BindPFlag("plugins.dir", cmd.PersistentFlags().Lookup("plugins.dir")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("plugins.required", false, "if true, neko will exit if there is an error when loading a plugin")
|
||||
if err := viper.BindPFlag("plugins.required", cmd.PersistentFlags().Lookup("plugins.required")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Plugins) Set() {
|
||||
s.Enabled = viper.GetBool("plugins.enabled")
|
||||
s.Dir = viper.GetString("plugins.dir")
|
||||
s.Required = viper.GetBool("plugins.required")
|
||||
}
|
97
server/internal/config/root.go
Normal file
97
server/internal/config/root.go
Normal file
@ -0,0 +1,97 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Root struct {
|
||||
Config string
|
||||
|
||||
LogLevel zerolog.Level
|
||||
LogTime string
|
||||
LogJson bool
|
||||
LogNocolor bool
|
||||
LogDir string
|
||||
}
|
||||
|
||||
func (Root) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().StringP("config", "c", "", "configuration file path")
|
||||
if err := viper.BindPFlag("config", cmd.PersistentFlags().Lookup("config")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// just a shortcut
|
||||
cmd.PersistentFlags().BoolP("debug", "d", false, "enable debug mode")
|
||||
if err := viper.BindPFlag("debug", cmd.PersistentFlags().Lookup("debug")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("log.level", "info", "set log level (trace, debug, info, warn, error, fatal, panic, disabled)")
|
||||
if err := viper.BindPFlag("log.level", cmd.PersistentFlags().Lookup("log.level")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("log.time", "unix", "time format used in logs (unix, unixms, unixmicro)")
|
||||
if err := viper.BindPFlag("log.time", cmd.PersistentFlags().Lookup("log.time")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("log.json", false, "logs in JSON format")
|
||||
if err := viper.BindPFlag("log.json", cmd.PersistentFlags().Lookup("log.json")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("log.nocolor", false, "no ANSI colors in non-JSON output")
|
||||
if err := viper.BindPFlag("log.nocolor", cmd.PersistentFlags().Lookup("log.nocolor")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("log.dir", "", "logging directory to store logs")
|
||||
if err := viper.BindPFlag("log.dir", cmd.PersistentFlags().Lookup("log.dir")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Root) Set() {
|
||||
s.Config = viper.GetString("config")
|
||||
|
||||
logLevel := viper.GetString("log.level")
|
||||
level, err := zerolog.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
log.Warn().Msgf("unknown log level %s", logLevel)
|
||||
} else {
|
||||
s.LogLevel = level
|
||||
}
|
||||
|
||||
logTime := viper.GetString("log.time")
|
||||
switch logTime {
|
||||
case "unix":
|
||||
s.LogTime = zerolog.TimeFormatUnix
|
||||
case "unixms":
|
||||
s.LogTime = zerolog.TimeFormatUnixMs
|
||||
case "unixmicro":
|
||||
s.LogTime = zerolog.TimeFormatUnixMicro
|
||||
default:
|
||||
log.Warn().Msgf("unknown log time %s", logTime)
|
||||
}
|
||||
|
||||
s.LogJson = viper.GetBool("log.json")
|
||||
s.LogNocolor = viper.GetBool("log.nocolor")
|
||||
s.LogDir = viper.GetString("log.dir")
|
||||
|
||||
if viper.GetBool("debug") && s.LogLevel != zerolog.TraceLevel {
|
||||
s.LogLevel = zerolog.DebugLevel
|
||||
}
|
||||
|
||||
// support for NO_COLOR env variable: https://no-color.org/
|
||||
if os.Getenv("NO_COLOR") != "" {
|
||||
s.LogNocolor = true
|
||||
}
|
||||
}
|
103
server/internal/config/server.go
Normal file
103
server/internal/config/server.go
Normal file
@ -0,0 +1,103 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/demodesk/neko/pkg/utils"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Cert string
|
||||
Key string
|
||||
Bind string
|
||||
Proxy bool
|
||||
Static string
|
||||
PathPrefix string
|
||||
PProf bool
|
||||
Metrics bool
|
||||
CORS []string
|
||||
}
|
||||
|
||||
func (Server) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().String("server.bind", "127.0.0.1:8080", "address/port/socket to serve neko")
|
||||
if err := viper.BindPFlag("server.bind", cmd.PersistentFlags().Lookup("server.bind")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("server.cert", "", "path to the SSL cert used to secure the neko server")
|
||||
if err := viper.BindPFlag("server.cert", cmd.PersistentFlags().Lookup("server.cert")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("server.key", "", "path to the SSL key used to secure the neko server")
|
||||
if err := viper.BindPFlag("server.key", cmd.PersistentFlags().Lookup("server.key")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("server.proxy", false, "trust reverse proxy headers")
|
||||
if err := viper.BindPFlag("server.proxy", cmd.PersistentFlags().Lookup("server.proxy")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("server.static", "", "path to neko client files to serve")
|
||||
if err := viper.BindPFlag("server.static", cmd.PersistentFlags().Lookup("server.static")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("server.path_prefix", "/", "path prefix for HTTP requests")
|
||||
if err := viper.BindPFlag("server.path_prefix", cmd.PersistentFlags().Lookup("server.path_prefix")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("server.pprof", false, "enable pprof endpoint available at /debug/pprof")
|
||||
if err := viper.BindPFlag("server.pprof", cmd.PersistentFlags().Lookup("server.pprof")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("server.metrics", true, "enable prometheus metrics available at /metrics")
|
||||
if err := viper.BindPFlag("server.metrics", cmd.PersistentFlags().Lookup("server.metrics")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().StringSlice("server.cors", []string{}, "list of allowed origins for CORS, if empty CORS is disabled, if '*' is present all origins are allowed")
|
||||
if err := viper.BindPFlag("server.cors", cmd.PersistentFlags().Lookup("server.cors")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Set() {
|
||||
s.Cert = viper.GetString("server.cert")
|
||||
s.Key = viper.GetString("server.key")
|
||||
s.Bind = viper.GetString("server.bind")
|
||||
s.Proxy = viper.GetBool("server.proxy")
|
||||
s.Static = viper.GetString("server.static")
|
||||
s.PathPrefix = path.Join("/", path.Clean(viper.GetString("server.path_prefix")))
|
||||
s.PProf = viper.GetBool("server.pprof")
|
||||
s.Metrics = viper.GetBool("server.metrics")
|
||||
|
||||
s.CORS = viper.GetStringSlice("server.cors")
|
||||
in, _ := utils.ArrayIn("*", s.CORS)
|
||||
if len(s.CORS) == 0 || in {
|
||||
s.CORS = []string{"*"}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) HasCors() bool {
|
||||
return len(s.CORS) > 0
|
||||
}
|
||||
|
||||
func (s *Server) AllowOrigin(origin string) bool {
|
||||
// if CORS is disabled, allow all origins
|
||||
if len(s.CORS) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// if CORS is enabled, allow only origins in the list
|
||||
in, _ := utils.ArrayIn(origin, s.CORS)
|
||||
return in || s.CORS[0] == "*"
|
||||
}
|
114
server/internal/config/session.go
Normal file
114
server/internal/config/session.go
Normal file
@ -0,0 +1,114 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
File string
|
||||
|
||||
PrivateMode bool
|
||||
LockedLogins bool
|
||||
LockedControls bool
|
||||
ControlProtection bool
|
||||
ImplicitHosting bool
|
||||
InactiveCursors bool
|
||||
MercifulReconnect bool
|
||||
APIToken string
|
||||
|
||||
CookieEnabled bool
|
||||
CookieName string
|
||||
CookieExpiration time.Duration
|
||||
CookieSecure bool
|
||||
}
|
||||
|
||||
func (Session) Init(cmd *cobra.Command) error {
|
||||
cmd.PersistentFlags().String("session.file", "", "if sessions should be stored in a file, otherwise they will be stored only in memory")
|
||||
if err := viper.BindPFlag("session.file", cmd.PersistentFlags().Lookup("session.file")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.private_mode", false, "whether private mode should be enabled initially")
|
||||
if err := viper.BindPFlag("session.private_mode", cmd.PersistentFlags().Lookup("session.private_mode")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.locked_logins", false, "whether logins should be locked for users initially")
|
||||
if err := viper.BindPFlag("session.locked_logins", cmd.PersistentFlags().Lookup("session.locked_logins")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.locked_controls", false, "whether controls should be locked for users initially")
|
||||
if err := viper.BindPFlag("session.locked_controls", cmd.PersistentFlags().Lookup("session.locked_controls")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.control_protection", false, "users can gain control only if at least one admin is in the room")
|
||||
if err := viper.BindPFlag("session.control_protection", cmd.PersistentFlags().Lookup("session.control_protection")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.implicit_hosting", true, "allow implicit control switching")
|
||||
if err := viper.BindPFlag("session.implicit_hosting", cmd.PersistentFlags().Lookup("session.implicit_hosting")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.inactive_cursors", false, "show inactive cursors on the screen")
|
||||
if err := viper.BindPFlag("session.inactive_cursors", cmd.PersistentFlags().Lookup("session.inactive_cursors")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.merciful_reconnect", true, "allow reconnecting to websocket even if previous connection was not closed")
|
||||
if err := viper.BindPFlag("session.merciful_reconnect", cmd.PersistentFlags().Lookup("session.merciful_reconnect")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("session.api_token", "", "API token for interacting with external services")
|
||||
if err := viper.BindPFlag("session.api_token", cmd.PersistentFlags().Lookup("session.api_token")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cookie
|
||||
cmd.PersistentFlags().Bool("session.cookie.enabled", true, "whether cookies authentication should be enabled")
|
||||
if err := viper.BindPFlag("session.cookie.enabled", cmd.PersistentFlags().Lookup("session.cookie.enabled")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().String("session.cookie.name", "NEKO_SESSION", "name of the cookie that holds token")
|
||||
if err := viper.BindPFlag("session.cookie.name", cmd.PersistentFlags().Lookup("session.cookie.name")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Int("session.cookie.expiration", 365*24, "expiration of the cookie in hours")
|
||||
if err := viper.BindPFlag("session.cookie.expiration", cmd.PersistentFlags().Lookup("session.cookie.expiration")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PersistentFlags().Bool("session.cookie.secure", true, "use secure cookies")
|
||||
if err := viper.BindPFlag("session.cookie.secure", cmd.PersistentFlags().Lookup("session.cookie.secure")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) Set() {
|
||||
s.File = viper.GetString("session.file")
|
||||
|
||||
s.PrivateMode = viper.GetBool("session.private_mode")
|
||||
s.LockedLogins = viper.GetBool("session.locked_logins")
|
||||
s.LockedControls = viper.GetBool("session.locked_controls")
|
||||
s.ControlProtection = viper.GetBool("session.control_protection")
|
||||
s.ImplicitHosting = viper.GetBool("session.implicit_hosting")
|
||||
s.InactiveCursors = viper.GetBool("session.inactive_cursors")
|
||||
s.MercifulReconnect = viper.GetBool("session.merciful_reconnect")
|
||||
s.APIToken = viper.GetString("session.api_token")
|
||||
|
||||
s.CookieEnabled = viper.GetBool("session.cookie.enabled")
|
||||
s.CookieName = viper.GetString("session.cookie.name")
|
||||
s.CookieExpiration = time.Duration(viper.GetInt("session.cookie.expiration")) * time.Hour
|
||||
s.CookieSecure = viper.GetBool("session.cookie.secure")
|
||||
}
|
273
server/internal/config/webrtc.go
Normal file
273
server/internal/config/webrtc.go
Normal file
@ -0,0 +1,273 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/demodesk/neko/pkg/types"
|
||||
"github.com/demodesk/neko/pkg/utils"
|
||||
)
|
||||
|
||||
// default stun server
|
||||
const defStunSrv = "stun:stun.l.google.com:19302"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type WebRTC struct {
|
||||
ICELite bool
|
||||
ICETrickle bool
|
||||
ICEServersFrontend []types.ICEServer
|
||||
ICEServersBackend []types.ICEServer
|
||||
EphemeralMin uint16
|
||||
EphemeralMax uint16
|
||||
TCPMux int
|
||||
UDPMux int
|
||||
|
||||
NAT1To1IPs []string
|
||||
IpRetrievalUrl string
|
||||
|
||||
Estimator WebRTCEstimator
|
||||
}
|
||||
|
||||
func (WebRTC) Init(cmd *cobra.Command) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
//}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WebRTC) Set() {
|
||||
s.ICELite = viper.GetBool("webrtc.icelite")
|
||||
s.ICETrickle = viper.GetBool("webrtc.icetrickle")
|
||||
|
||||
// parse frontend ice servers
|
||||
if err := viper.UnmarshalKey("webrtc.iceservers.frontend", &s.ICEServersFrontend, viper.DecodeHook(
|
||||
utils.JsonStringAutoDecode([]types.ICEServer{}),
|
||||
)); err != nil {
|
||||
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.")
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
s.NAT1To1IPs = viper.GetStringSlice("webrtc.nat1to1")
|
||||
s.IpRetrievalUrl = viper.GetString("webrtc.ip_retrieval_url")
|
||||
if s.IpRetrievalUrl != "" && len(s.NAT1To1IPs) == 0 {
|
||||
ip, err := utils.HttpRequestGET(s.IpRetrievalUrl)
|
||||
if err == nil {
|
||||
s.NAT1To1IPs = append(s.NAT1To1IPs, ip)
|
||||
} else {
|
||||
log.Warn().Err(err).Msgf("IP retrieval failed")
|
||||
}
|
||||
}
|
||||
|
||||
// bandwidth estimator
|
||||
|
||||
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")
|
||||
}
|
Reference in New Issue
Block a user