diff --git a/server/cmd/serve.go b/server/cmd/serve.go index 5b1162d..890c83d 100644 --- a/server/cmd/serve.go +++ b/server/cmd/serve.go @@ -19,7 +19,8 @@ func init() { configs := []config.Config{ neko.Service.Server, neko.Service.WebRTC, - neko.Service.Remote, + neko.Service.Capture, + neko.Service.Desktop, neko.Service.Broadcast, neko.Service.WebSocket, } diff --git a/server/internal/broadcast/manager.go b/server/internal/broadcast/manager.go index aff55ec..3ab92cb 100644 --- a/server/internal/broadcast/manager.go +++ b/server/internal/broadcast/manager.go @@ -14,16 +14,16 @@ type BroadcastManager struct { mu sync.Mutex logger zerolog.Logger pipeline *gst.Pipeline - remote *config.Remote + capture *config.Capture config *config.Broadcast enabled bool url string } -func New(remote *config.Remote, config *config.Broadcast) *BroadcastManager { +func New(capture *config.Capture, config *config.Broadcast) *BroadcastManager { return &BroadcastManager{ - logger: log.With().Str("module", "remote").Logger(), - remote: remote, + logger: log.With().Str("module", "broadcast").Logger(), + capture: capture, config: config, enabled: config.Enabled, url: config.URL, @@ -42,8 +42,8 @@ func (manager *BroadcastManager) Start() error { var err error manager.pipeline, err = gst.CreateRTMPPipeline( - manager.remote.Device, - manager.remote.Display, + manager.capture.Device, + manager.capture.Display, manager.config.Pipeline, manager.url, ) @@ -54,8 +54,8 @@ func (manager *BroadcastManager) Start() error { } manager.logger.Info(). - Str("audio_device", manager.remote.Device). - Str("video_display", manager.remote.Display). + Str("audio_device", manager.capture.Device). + Str("video_display", manager.capture.Display). Str("rtmp_pipeline_src", manager.pipeline.Src). Msgf("RTMP pipeline is starting...") diff --git a/server/internal/remote/manager.go b/server/internal/capture/manager.go similarity index 67% rename from server/internal/remote/manager.go rename to server/internal/capture/manager.go index c5db764..934d0af 100644 --- a/server/internal/remote/manager.go +++ b/server/internal/capture/manager.go @@ -1,11 +1,11 @@ -package remote +package capture import ( "fmt" "time" + "m1k1o/neko/internal/desktop/xorg" "m1k1o/neko/internal/gst" - "m1k1o/neko/internal/remote/xorg" "m1k1o/neko/internal/types" "m1k1o/neko/internal/types/config" @@ -14,47 +14,41 @@ import ( "github.com/rs/zerolog/log" ) -type RemoteManager struct { +type CaptureManagerCtx struct { logger zerolog.Logger video *gst.Pipeline audio *gst.Pipeline - config *config.Remote + config *config.Capture broadcast types.BroadcastManager + desktop types.DesktopManager cleanup *time.Ticker shutdown chan bool emmiter events.EventEmmiter streaming bool } -func New(config *config.Remote, broadcast types.BroadcastManager) *RemoteManager { - return &RemoteManager{ - logger: log.With().Str("module", "remote").Logger(), +func New(desktop types.DesktopManager, broadcast types.BroadcastManager, config *config.Capture) *CaptureManagerCtx { + return &CaptureManagerCtx{ + logger: log.With().Str("module", "capture").Logger(), cleanup: time.NewTicker(1 * time.Second), shutdown: make(chan bool), emmiter: events.New(), config: config, broadcast: broadcast, + desktop: desktop, streaming: false, } } -func (manager *RemoteManager) VideoCodec() string { +func (manager *CaptureManagerCtx) VideoCodec() string { return manager.config.VideoCodec } -func (manager *RemoteManager) AudioCodec() string { +func (manager *CaptureManagerCtx) AudioCodec() string { return manager.config.AudioCodec } -func (manager *RemoteManager) Start() { - xorg.Display(manager.config.Display) - - if !xorg.ValidScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) { - manager.logger.Warn().Msgf("invalid screen option %dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) - } else if err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate); err != nil { - manager.logger.Warn().Err(err).Msg("unable to change screen size") - } - +func (manager *CaptureManagerCtx) Start() { manager.createPipelines() if err := manager.broadcast.Start(); err != nil { manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline") @@ -74,14 +68,14 @@ func (manager *RemoteManager) Start() { case sample := <-manager.audio.Sample: manager.emmiter.Emit("audio", sample) case <-manager.cleanup.C: - xorg.CheckKeys(time.Second * 10) + // TODO: refactor. } } }() } -func (manager *RemoteManager) Shutdown() error { - manager.logger.Info().Msgf("remote shutting down") +func (manager *CaptureManagerCtx) Shutdown() error { + manager.logger.Info().Msgf("capture shutting down") manager.video.Stop() manager.audio.Stop() manager.broadcast.Stop() @@ -91,19 +85,19 @@ func (manager *RemoteManager) Shutdown() error { return nil } -func (manager *RemoteManager) OnVideoFrame(listener func(sample types.Sample)) { +func (manager *CaptureManagerCtx) OnVideoFrame(listener func(sample types.Sample)) { manager.emmiter.On("video", func(payload ...interface{}) { listener(payload[0].(types.Sample)) }) } -func (manager *RemoteManager) OnAudioFrame(listener func(sample types.Sample)) { +func (manager *CaptureManagerCtx) OnAudioFrame(listener func(sample types.Sample)) { manager.emmiter.On("audio", func(payload ...interface{}) { listener(payload[0].(types.Sample)) }) } -func (manager *RemoteManager) StartStream() { +func (manager *CaptureManagerCtx) StartStream() { manager.createPipelines() manager.logger.Info(). @@ -113,7 +107,6 @@ func (manager *RemoteManager) StartStream() { Str("audio_codec", manager.config.AudioCodec). Str("audio_pipeline_src", manager.audio.Src). Str("video_pipeline_src", manager.video.Src). - Str("screen_resolution", fmt.Sprintf("%dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)). Msgf("Pipelines starting...") manager.video.Start() @@ -121,21 +114,21 @@ func (manager *RemoteManager) StartStream() { manager.streaming = true } -func (manager *RemoteManager) StopStream() { +func (manager *CaptureManagerCtx) StopStream() { manager.logger.Info().Msgf("Pipelines shutting down...") manager.video.Stop() manager.audio.Stop() manager.streaming = false } -func (manager *RemoteManager) Streaming() bool { +func (manager *CaptureManagerCtx) Streaming() bool { return manager.streaming } -func (manager *RemoteManager) createPipelines() { +func (manager *CaptureManagerCtx) createPipelines() { // handle maximum fps - rate := manager.config.ScreenRate - if manager.config.MaxFPS != 0 && manager.config.MaxFPS < manager.config.ScreenRate { + rate := int(manager.desktop.GetScreenSize().Rate) + if manager.config.MaxFPS != 0 && manager.config.MaxFPS < rate { rate = manager.config.MaxFPS } @@ -165,7 +158,7 @@ func (manager *RemoteManager) createPipelines() { } } -func (manager *RemoteManager) ChangeResolution(width int, height int, rate int) error { +func (manager *CaptureManagerCtx) ChangeResolution(width int, height int, rate int) error { if !xorg.ValidScreenSize(width, height, rate) { return fmt.Errorf("unknown configuration") } diff --git a/server/internal/desktop/clipboard.go b/server/internal/desktop/clipboard.go new file mode 100644 index 0000000..036a117 --- /dev/null +++ b/server/internal/desktop/clipboard.go @@ -0,0 +1,11 @@ +package desktop + +import "m1k1o/neko/internal/desktop/clipboard" + +func (manager *DesktopManagerCtx) ReadClipboard() string { + return clipboard.Read() +} + +func (manager *DesktopManagerCtx) WriteClipboard(data string) { + clipboard.Write(data) +} diff --git a/server/internal/remote/clipboard/clipboard.c b/server/internal/desktop/clipboard/clipboard.c similarity index 100% rename from server/internal/remote/clipboard/clipboard.c rename to server/internal/desktop/clipboard/clipboard.c diff --git a/server/internal/remote/clipboard/clipboard.go b/server/internal/desktop/clipboard/clipboard.go similarity index 100% rename from server/internal/remote/clipboard/clipboard.go rename to server/internal/desktop/clipboard/clipboard.go diff --git a/server/internal/remote/clipboard/clipboard.h b/server/internal/desktop/clipboard/clipboard.h similarity index 100% rename from server/internal/remote/clipboard/clipboard.h rename to server/internal/desktop/clipboard/clipboard.h diff --git a/server/internal/desktop/manager.go b/server/internal/desktop/manager.go new file mode 100644 index 0000000..2b950e3 --- /dev/null +++ b/server/internal/desktop/manager.go @@ -0,0 +1,65 @@ +package desktop + +import ( + "sync" + "time" + + "m1k1o/neko/internal/desktop/xorg" + "m1k1o/neko/internal/types" + "m1k1o/neko/internal/types/config" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type DesktopManagerCtx struct { + logger zerolog.Logger + wg sync.WaitGroup + shutdown chan struct{} + config *config.Desktop +} + +func New(config *config.Desktop, broadcast types.BroadcastManager) *DesktopManagerCtx { + return &DesktopManagerCtx{ + logger: log.With().Str("module", "desktop").Logger(), + shutdown: make(chan struct{}), + config: config, + } +} + +func (manager *DesktopManagerCtx) Start() { + xorg.Display(manager.config.Display) + + if !xorg.ValidScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) { + manager.logger.Warn().Msgf("invalid screen option %dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) + } else if err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate); err != nil { + manager.logger.Warn().Err(err).Msg("unable to change screen size") + } + + manager.wg.Add(1) + + go func() { + defer manager.wg.Done() + + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-manager.shutdown: + return + case <-ticker.C: + xorg.CheckKeys(time.Second * 10) + } + } + }() +} + +func (manager *DesktopManagerCtx) Shutdown() error { + manager.logger.Info().Msgf("desktop shutting down") + + close(manager.shutdown) + manager.wg.Wait() + + return nil +} diff --git a/server/internal/desktop/xorg.go b/server/internal/desktop/xorg.go new file mode 100644 index 0000000..e4cc2d2 --- /dev/null +++ b/server/internal/desktop/xorg.go @@ -0,0 +1,52 @@ +package desktop + +import ( + "os/exec" + + "m1k1o/neko/internal/desktop/xorg" + "m1k1o/neko/internal/types" +) + +func (manager *DesktopManagerCtx) Move(x, y int) { + xorg.Move(x, y) +} + +func (manager *DesktopManagerCtx) Scroll(x, y int) { + xorg.Scroll(x, y) +} + +func (manager *DesktopManagerCtx) ButtonDown(code int) error { + return xorg.ButtonDown(code) +} + +func (manager *DesktopManagerCtx) KeyDown(code uint64) error { + return xorg.KeyDown(code) +} + +func (manager *DesktopManagerCtx) ButtonUp(code int) error { + return xorg.ButtonUp(code) +} + +func (manager *DesktopManagerCtx) KeyUp(code uint64) error { + return xorg.KeyUp(code) +} + +func (manager *DesktopManagerCtx) ResetKeys() { + xorg.ResetKeys() +} + +func (manager *DesktopManagerCtx) ScreenConfigurations() map[int]types.ScreenConfiguration { + return xorg.ScreenConfigurations +} + +func (manager *DesktopManagerCtx) GetScreenSize() *types.ScreenSize { + return xorg.GetScreenSize() +} + +func (manager *DesktopManagerCtx) SetKeyboardLayout(layout string) { + _ = exec.Command("setxkbmap", layout).Run() +} + +func (manager *DesktopManagerCtx) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) { + xorg.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock) +} diff --git a/server/internal/remote/xorg/xorg.c b/server/internal/desktop/xorg/xorg.c similarity index 100% rename from server/internal/remote/xorg/xorg.c rename to server/internal/desktop/xorg/xorg.c diff --git a/server/internal/remote/xorg/xorg.go b/server/internal/desktop/xorg/xorg.go similarity index 100% rename from server/internal/remote/xorg/xorg.go rename to server/internal/desktop/xorg/xorg.go diff --git a/server/internal/remote/xorg/xorg.h b/server/internal/desktop/xorg/xorg.h similarity index 100% rename from server/internal/remote/xorg/xorg.h rename to server/internal/desktop/xorg/xorg.h diff --git a/server/internal/remote/clipboard.go b/server/internal/remote/clipboard.go deleted file mode 100644 index 9a7e325..0000000 --- a/server/internal/remote/clipboard.go +++ /dev/null @@ -1,11 +0,0 @@ -package remote - -import "m1k1o/neko/internal/remote/clipboard" - -func (manager *RemoteManager) ReadClipboard() string { - return clipboard.Read() -} - -func (manager *RemoteManager) WriteClipboard(data string) { - clipboard.Write(data) -} diff --git a/server/internal/remote/xorg.go b/server/internal/remote/xorg.go deleted file mode 100644 index 0722e27..0000000 --- a/server/internal/remote/xorg.go +++ /dev/null @@ -1,51 +0,0 @@ -package remote - -import ( - "m1k1o/neko/internal/remote/xorg" - "m1k1o/neko/internal/types" - "os/exec" -) - -func (manager *RemoteManager) Move(x, y int) { - xorg.Move(x, y) -} - -func (manager *RemoteManager) Scroll(x, y int) { - xorg.Scroll(x, y) -} - -func (manager *RemoteManager) ButtonDown(code int) error { - return xorg.ButtonDown(code) -} - -func (manager *RemoteManager) KeyDown(code uint64) error { - return xorg.KeyDown(code) -} - -func (manager *RemoteManager) ButtonUp(code int) error { - return xorg.ButtonUp(code) -} - -func (manager *RemoteManager) KeyUp(code uint64) error { - return xorg.KeyUp(code) -} - -func (manager *RemoteManager) ResetKeys() { - xorg.ResetKeys() -} - -func (manager *RemoteManager) ScreenConfigurations() map[int]types.ScreenConfiguration { - return xorg.ScreenConfigurations -} - -func (manager *RemoteManager) GetScreenSize() *types.ScreenSize { - return xorg.GetScreenSize() -} - -func (manager *RemoteManager) SetKeyboardLayout(layout string) { - _ = exec.Command("setxkbmap", layout).Run() -} - -func (manager *RemoteManager) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) { - xorg.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock) -} diff --git a/server/internal/session/manager.go b/server/internal/session/manager.go index b17d5d8..ed4e4b7 100644 --- a/server/internal/session/manager.go +++ b/server/internal/session/manager.go @@ -12,11 +12,11 @@ import ( "m1k1o/neko/internal/utils" ) -func New(remote types.RemoteManager) *SessionManager { +func New(capture types.CaptureManager) *SessionManager { return &SessionManager{ logger: log.With().Str("module", "session").Logger(), host: "", - remote: remote, + capture: capture, members: make(map[string]*Session), emmiter: events.New(), } @@ -26,7 +26,7 @@ type SessionManager struct { mu sync.Mutex logger zerolog.Logger host string - remote types.RemoteManager + capture types.CaptureManager members map[string]*Session emmiter events.EventEmmiter // TODO: Handle locks in sessions as flags. @@ -45,8 +45,8 @@ func (manager *SessionManager) New(id string, admin bool, socket types.WebSocket manager.mu.Lock() manager.members[id] = session - if !manager.remote.Streaming() && len(manager.members) > 0 { - manager.remote.StartStream() + if !manager.capture.Streaming() && len(manager.members) > 0 { + manager.capture.StartStream() } manager.mu.Unlock() @@ -160,8 +160,8 @@ func (manager *SessionManager) Destroy(id string) { err := session.destroy() delete(manager.members, id) - if manager.remote.Streaming() && len(manager.members) <= 0 { - manager.remote.StopStream() + if manager.capture.Streaming() && len(manager.members) <= 0 { + manager.capture.StopStream() } manager.mu.Unlock() diff --git a/server/internal/types/capture.go b/server/internal/types/capture.go new file mode 100644 index 0000000..21f23a7 --- /dev/null +++ b/server/internal/types/capture.go @@ -0,0 +1,14 @@ +package types + +type CaptureManager interface { + VideoCodec() string + AudioCodec() string + Start() + Shutdown() error + OnVideoFrame(listener func(sample Sample)) + OnAudioFrame(listener func(sample Sample)) + StartStream() + StopStream() + Streaming() bool + ChangeResolution(width int, height int, rate int) error +} diff --git a/server/internal/types/config/remote.go b/server/internal/types/config/capture.go similarity index 81% rename from server/internal/types/config/remote.go rename to server/internal/types/config/capture.go index 21bd6cd..690ec10 100644 --- a/server/internal/types/config/remote.go +++ b/server/internal/types/config/capture.go @@ -1,14 +1,11 @@ package config import ( - "regexp" - "strconv" - "github.com/spf13/cobra" "github.com/spf13/viper" ) -type Remote struct { +type Capture struct { Display string Device string AudioCodec string @@ -18,13 +15,10 @@ type Remote struct { VideoCodec string VideoParams string VideoBitrate uint - ScreenWidth int - ScreenHeight int - ScreenRate int MaxFPS int } -func (Remote) Init(cmd *cobra.Command) error { +func (Capture) Init(cmd *cobra.Command) error { cmd.PersistentFlags().String("display", ":99.0", "XDisplay to capture") if err := viper.BindPFlag("display", cmd.PersistentFlags().Lookup("display")); err != nil { return err @@ -55,11 +49,6 @@ func (Remote) Init(cmd *cobra.Command) error { return err } - cmd.PersistentFlags().String("screen", "1280x720@30", "default screen resolution and framerate") - if err := viper.BindPFlag("screen", cmd.PersistentFlags().Lookup("screen")); err != nil { - return err - } - cmd.PersistentFlags().Int("max_fps", 25, "maximum fps delivered via WebRTC, 0 is for no maximum") if err := viper.BindPFlag("max_fps", cmd.PersistentFlags().Lookup("max_fps")); err != nil { return err @@ -111,7 +100,7 @@ func (Remote) Init(cmd *cobra.Command) error { return nil } -func (s *Remote) Set() { +func (s *Capture) Set() { audioCodec := "Opus" if viper.GetBool("opus") { audioCodec = "Opus" @@ -146,24 +135,5 @@ func (s *Remote) Set() { s.VideoParams = viper.GetString("video") s.VideoBitrate = viper.GetUint("video_bitrate") - s.ScreenWidth = 1280 - s.ScreenHeight = 720 - s.ScreenRate = 30 - - r := regexp.MustCompile(`([0-9]{1,4})x([0-9]{1,4})@([0-9]{1,3})`) - res := r.FindStringSubmatch(viper.GetString("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.ScreenWidth = int(width) - s.ScreenHeight = int(height) - s.ScreenRate = int(rate) - } - } - s.MaxFPS = viper.GetInt("max_fps") } diff --git a/server/internal/types/config/desktop.go b/server/internal/types/config/desktop.go new file mode 100644 index 0000000..c7cc21b --- /dev/null +++ b/server/internal/types/config/desktop.go @@ -0,0 +1,51 @@ +package config + +import ( + "os" + "regexp" + "strconv" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type Desktop struct { + Display string + + ScreenWidth int + ScreenHeight int + ScreenRate int +} + +func (Desktop) Init(cmd *cobra.Command) error { + cmd.PersistentFlags().String("screen", "1280x720@30", "default screen resolution and framerate") + if err := viper.BindPFlag("screen", cmd.PersistentFlags().Lookup("screen")); err != nil { + return err + } + + return nil +} + +func (s *Desktop) Set() { + // Display is provided by env variable + s.Display = os.Getenv("DISPLAY") + + s.ScreenWidth = 1280 + s.ScreenHeight = 720 + s.ScreenRate = 30 + + r := regexp.MustCompile(`([0-9]{1,4})x([0-9]{1,4})@([0-9]{1,3})`) + res := r.FindStringSubmatch(viper.GetString("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.ScreenWidth = int(width) + s.ScreenHeight = int(height) + s.ScreenRate = int(rate) + } + } +} diff --git a/server/internal/types/remote.go b/server/internal/types/desktop.go similarity index 61% rename from server/internal/types/remote.go rename to server/internal/types/desktop.go index b437d10..ba05b95 100644 --- a/server/internal/types/remote.go +++ b/server/internal/types/desktop.go @@ -1,27 +1,21 @@ package types -type RemoteManager interface { - VideoCodec() string - AudioCodec() string +type DesktopManager interface { Start() Shutdown() error - OnVideoFrame(listener func(sample Sample)) - OnAudioFrame(listener func(sample Sample)) - StartStream() - StopStream() - Streaming() bool - ChangeResolution(width int, height int, rate int) error - GetScreenSize() *ScreenSize - ScreenConfigurations() map[int]ScreenConfiguration + // clipboard + ReadClipboard() string + WriteClipboard(data string) + // xorg Move(x, y int) Scroll(x, y int) ButtonDown(code int) error KeyDown(code uint64) error ButtonUp(code int) error KeyUp(code uint64) error - ReadClipboard() string - WriteClipboard(data string) ResetKeys() + ScreenConfigurations() map[int]ScreenConfiguration + GetScreenSize() *ScreenSize SetKeyboardLayout(layout string) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) } diff --git a/server/internal/webrtc/handle.go b/server/internal/webrtc/handle.go index c75fffb..f1e01bf 100644 --- a/server/internal/webrtc/handle.go +++ b/server/internal/webrtc/handle.go @@ -64,7 +64,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e return err } - manager.remote.Move(int(payload.X), int(payload.Y)) + manager.desktop.Move(int(payload.X), int(payload.Y)) case OP_SCROLL: payload := &PayloadScroll{} if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { @@ -77,7 +77,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e Str("y", strconv.Itoa(int(payload.Y))). Msg("scroll") - manager.remote.Scroll(int(payload.X), int(payload.Y)) + manager.desktop.Scroll(int(payload.X), int(payload.Y)) case OP_KEY_DOWN: payload := &PayloadKey{} if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { @@ -85,7 +85,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e } if payload.Key < 8 { - err := manager.remote.ButtonDown(int(payload.Key)) + err := manager.desktop.ButtonDown(int(payload.Key)) if err != nil { manager.logger.Warn().Err(err).Msg("button down failed") return nil @@ -93,7 +93,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e manager.logger.Debug().Msgf("button down %d", payload.Key) } else { - err := manager.remote.KeyDown(uint64(payload.Key)) + err := manager.desktop.KeyDown(uint64(payload.Key)) if err != nil { manager.logger.Warn().Err(err).Msg("key down failed") return nil @@ -109,7 +109,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e } if payload.Key < 8 { - err := manager.remote.ButtonUp(int(payload.Key)) + err := manager.desktop.ButtonUp(int(payload.Key)) if err != nil { manager.logger.Warn().Err(err).Msg("button up failed") return nil @@ -117,7 +117,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e manager.logger.Debug().Msgf("button up %d", payload.Key) } else { - err := manager.remote.KeyUp(uint64(payload.Key)) + err := manager.desktop.KeyUp(uint64(payload.Key)) if err != nil { manager.logger.Warn().Err(err).Msg("key up failed") return nil diff --git a/server/internal/webrtc/webrtc.go b/server/internal/webrtc/webrtc.go index 126d40b..266c780 100644 --- a/server/internal/webrtc/webrtc.go +++ b/server/internal/webrtc/webrtc.go @@ -18,10 +18,11 @@ import ( "m1k1o/neko/internal/types/config" ) -func New(sessions types.SessionManager, remote types.RemoteManager, config *config.WebRTC) *WebRTCManager { +func New(sessions types.SessionManager, capture types.CaptureManager, desktop types.DesktopManager, config *config.WebRTC) *WebRTCManager { return &WebRTCManager{ logger: log.With().Str("module", "webrtc").Logger(), - remote: remote, + capture: capture, + desktop: desktop, sessions: sessions, config: config, } @@ -34,30 +35,31 @@ type WebRTCManager struct { videoCodec webrtc.RTPCodecParameters audioCodec webrtc.RTPCodecParameters sessions types.SessionManager - remote types.RemoteManager + capture types.CaptureManager + desktop types.DesktopManager config *config.WebRTC api *webrtc.API } func (manager *WebRTCManager) Start() { var err error - manager.audioTrack, manager.audioCodec, err = manager.createTrack(manager.remote.AudioCodec()) + manager.audioTrack, manager.audioCodec, err = manager.createTrack(manager.capture.AudioCodec()) if err != nil { manager.logger.Panic().Err(err).Msg("unable to create audio track") } - manager.remote.OnAudioFrame(func(sample types.Sample) { + manager.capture.OnAudioFrame(func(sample types.Sample) { if err := manager.audioTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe { manager.logger.Warn().Err(err).Msg("audio pipeline failed to write") } }) - manager.videoTrack, manager.videoCodec, err = manager.createTrack(manager.remote.VideoCodec()) + manager.videoTrack, manager.videoCodec, err = manager.createTrack(manager.capture.VideoCodec()) if err != nil { manager.logger.Panic().Err(err).Msg("unable to create video track") } - manager.remote.OnVideoFrame(func(sample types.Sample) { + manager.capture.OnVideoFrame(func(sample types.Sample) { if err := manager.videoTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe { manager.logger.Warn().Err(err).Msg("video pipeline failed to write") } diff --git a/server/internal/websocket/control.go b/server/internal/websocket/control.go index 2f10016..c24bc3a 100644 --- a/server/internal/websocket/control.go +++ b/server/internal/websocket/control.go @@ -131,7 +131,7 @@ func (h *MessageHandler) controlClipboard(id string, session types.Session, payl return nil } - h.remote.WriteClipboard(payload.Text) + h.desktop.WriteClipboard(payload.Text) return nil } @@ -144,7 +144,7 @@ func (h *MessageHandler) controlKeyboard(id string, session types.Session, paylo // change layout if payload.Layout != nil { - h.remote.SetKeyboardLayout(*payload.Layout) + h.desktop.SetKeyboardLayout(*payload.Layout) } // set num lock @@ -177,6 +177,6 @@ func (h *MessageHandler) controlKeyboard(id string, session types.Session, paylo Int("ScrollLock", ScrollLock). Msg("setting keyboard modifiers") - h.remote.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock) + h.desktop.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock) return nil } diff --git a/server/internal/websocket/handler.go b/server/internal/websocket/handler.go index 4dec306..e92832d 100644 --- a/server/internal/websocket/handler.go +++ b/server/internal/websocket/handler.go @@ -16,7 +16,8 @@ type MessageHandler struct { logger zerolog.Logger sessions types.SessionManager webrtc types.WebRTCManager - remote types.RemoteManager + desktop types.DesktopManager + capture types.CaptureManager broadcast types.BroadcastManager banned map[string]string // IP -> session ID (that banned it) locked map[string]string // resource name -> session ID (that locked it) diff --git a/server/internal/websocket/screen.go b/server/internal/websocket/screen.go index 44e6af1..d1648f5 100644 --- a/server/internal/websocket/screen.go +++ b/server/internal/websocket/screen.go @@ -12,7 +12,7 @@ func (h *MessageHandler) screenSet(id string, session types.Session, payload *me return nil } - if err := h.remote.ChangeResolution(payload.Width, payload.Height, payload.Rate); err != nil { + if err := h.capture.ChangeResolution(payload.Width, payload.Height, payload.Rate); err != nil { h.logger.Warn().Err(err).Msgf("unable to change screen size") return err } @@ -33,7 +33,7 @@ func (h *MessageHandler) screenSet(id string, session types.Session, payload *me } func (h *MessageHandler) screenResolution(id string, session types.Session) error { - if size := h.remote.GetScreenSize(); size != nil { + if size := h.desktop.GetScreenSize(); size != nil { if err := session.Send(message.ScreenResolution{ Event: event.SCREEN_RESOLUTION, Width: size.Width, @@ -56,7 +56,7 @@ func (h *MessageHandler) screenConfigurations(id string, session types.Session) if err := session.Send(message.ScreenConfigurations{ Event: event.SCREEN_CONFIGURATIONS, - Configurations: h.remote.ScreenConfigurations(), + Configurations: h.desktop.ScreenConfigurations(), }); err != nil { h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_CONFIGURATIONS) return err diff --git a/server/internal/websocket/websocket.go b/server/internal/websocket/websocket.go index d5b56de..372f9ff 100644 --- a/server/internal/websocket/websocket.go +++ b/server/internal/websocket/websocket.go @@ -20,7 +20,7 @@ import ( const CONTROL_PROTECTION_SESSION = "by_control_protection" -func New(sessions types.SessionManager, remote types.RemoteManager, broadcast types.BroadcastManager, webrtc types.WebRTCManager, conf *config.WebSocket) *WebSocketHandler { +func New(sessions types.SessionManager, desktop types.DesktopManager, capture types.CaptureManager, broadcast types.BroadcastManager, webrtc types.WebRTCManager, conf *config.WebSocket) *WebSocketHandler { logger := log.With().Str("module", "websocket").Logger() locks := make(map[string]string) @@ -45,7 +45,8 @@ func New(sessions types.SessionManager, remote types.RemoteManager, broadcast ty shutdown: make(chan interface{}), conf: conf, sessions: sessions, - remote: remote, + desktop: desktop, + upgrader: websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true @@ -53,7 +54,8 @@ func New(sessions types.SessionManager, remote types.RemoteManager, broadcast ty }, handler: &MessageHandler{ logger: logger.With().Str("subsystem", "handler").Logger(), - remote: remote, + desktop: desktop, + capture: capture, broadcast: broadcast, sessions: sessions, webrtc: webrtc, @@ -73,7 +75,7 @@ type WebSocketHandler struct { shutdown chan interface{} upgrader websocket.Upgrader sessions types.SessionManager - remote types.RemoteManager + desktop types.DesktopManager conf *config.WebSocket handler *MessageHandler @@ -175,7 +177,7 @@ func (ws *WebSocketHandler) Start() { ws.wg.Done() }() - current := ws.remote.ReadClipboard() + current := ws.desktop.ReadClipboard() for { select { @@ -188,7 +190,7 @@ func (ws *WebSocketHandler) Start() { continue } - text := ws.remote.ReadClipboard() + text := ws.desktop.ReadClipboard() if text == current { continue } diff --git a/server/neko.go b/server/neko.go index 43dc949..9920417 100644 --- a/server/neko.go +++ b/server/neko.go @@ -7,8 +7,9 @@ import ( "runtime" "m1k1o/neko/internal/broadcast" + "m1k1o/neko/internal/capture" + "m1k1o/neko/internal/desktop" "m1k1o/neko/internal/http" - "m1k1o/neko/internal/remote" "m1k1o/neko/internal/session" "m1k1o/neko/internal/types/config" "m1k1o/neko/internal/webrtc" @@ -61,7 +62,8 @@ func init() { }, Root: &config.Root{}, Server: &config.Server{}, - Remote: &config.Remote{}, + Capture: &config.Capture{}, + Desktop: &config.Desktop{}, Broadcast: &config.Broadcast{}, WebRTC: &config.WebRTC{}, WebSocket: &config.WebSocket{}, @@ -100,7 +102,8 @@ func (i *Version) Details() string { type Neko struct { Version *Version Root *config.Root - Remote *config.Remote + Capture *config.Capture + Desktop *config.Desktop Broadcast *config.Broadcast Server *config.Server WebRTC *config.WebRTC @@ -109,7 +112,8 @@ type Neko struct { logger zerolog.Logger server *http.Server sessionManager *session.SessionManager - remoteManager *remote.RemoteManager + captureManager *capture.CaptureManagerCtx + desktopManager *desktop.DesktopManagerCtx broadcastManager *broadcast.BroadcastManager webRTCManager *webrtc.WebRTCManager webSocketHandler *websocket.WebSocketHandler @@ -120,17 +124,20 @@ func (neko *Neko) Preflight() { } func (neko *Neko) Start() { - broadcastManager := broadcast.New(neko.Remote, neko.Broadcast) + broadcastManager := broadcast.New(neko.Capture, neko.Broadcast) - remoteManager := remote.New(neko.Remote, broadcastManager) - remoteManager.Start() + desktopManager := desktop.New(neko.Desktop, broadcastManager) + desktopManager.Start() - sessionManager := session.New(remoteManager) + captureManager := capture.New(desktopManager, broadcastManager, neko.Capture) + captureManager.Start() - webRTCManager := webrtc.New(sessionManager, remoteManager, neko.WebRTC) + sessionManager := session.New(captureManager) + + webRTCManager := webrtc.New(sessionManager, captureManager, desktopManager, neko.WebRTC) webRTCManager.Start() - webSocketHandler := websocket.New(sessionManager, remoteManager, broadcastManager, webRTCManager, neko.WebSocket) + webSocketHandler := websocket.New(sessionManager, desktopManager, captureManager, broadcastManager, webRTCManager, neko.WebSocket) webSocketHandler.Start() server := http.New(neko.Server, webSocketHandler) @@ -138,7 +145,8 @@ func (neko *Neko) Start() { neko.broadcastManager = broadcastManager neko.sessionManager = sessionManager - neko.remoteManager = remoteManager + neko.captureManager = captureManager + neko.desktopManager = desktopManager neko.webRTCManager = webRTCManager neko.webSocketHandler = webSocketHandler neko.server = server @@ -150,8 +158,11 @@ func (neko *Neko) Shutdown() { err = neko.broadcastManager.Shutdown() neko.logger.Err(err).Msg("broadcast manager shutdown") - err = neko.remoteManager.Shutdown() - neko.logger.Err(err).Msg("remote manager shutdown") + err = neko.desktopManager.Shutdown() + neko.logger.Err(err).Msg("desktop manager shutdown") + + err = neko.captureManager.Shutdown() + neko.logger.Err(err).Msg("capture manager shutdown") err = neko.webRTCManager.Shutdown() neko.logger.Err(err).Msg("webrtc manager shutdown")