split remote to desktop and capture.

This commit is contained in:
Miroslav Šedivý 2022-09-12 22:12:47 +02:00
parent 0bca8c9d02
commit de4f6b45e5
26 changed files with 299 additions and 194 deletions

View File

@ -19,7 +19,8 @@ func init() {
configs := []config.Config{ configs := []config.Config{
neko.Service.Server, neko.Service.Server,
neko.Service.WebRTC, neko.Service.WebRTC,
neko.Service.Remote, neko.Service.Capture,
neko.Service.Desktop,
neko.Service.Broadcast, neko.Service.Broadcast,
neko.Service.WebSocket, neko.Service.WebSocket,
} }

View File

@ -14,16 +14,16 @@ type BroadcastManager struct {
mu sync.Mutex mu sync.Mutex
logger zerolog.Logger logger zerolog.Logger
pipeline *gst.Pipeline pipeline *gst.Pipeline
remote *config.Remote capture *config.Capture
config *config.Broadcast config *config.Broadcast
enabled bool enabled bool
url string url string
} }
func New(remote *config.Remote, config *config.Broadcast) *BroadcastManager { func New(capture *config.Capture, config *config.Broadcast) *BroadcastManager {
return &BroadcastManager{ return &BroadcastManager{
logger: log.With().Str("module", "remote").Logger(), logger: log.With().Str("module", "broadcast").Logger(),
remote: remote, capture: capture,
config: config, config: config,
enabled: config.Enabled, enabled: config.Enabled,
url: config.URL, url: config.URL,
@ -42,8 +42,8 @@ func (manager *BroadcastManager) Start() error {
var err error var err error
manager.pipeline, err = gst.CreateRTMPPipeline( manager.pipeline, err = gst.CreateRTMPPipeline(
manager.remote.Device, manager.capture.Device,
manager.remote.Display, manager.capture.Display,
manager.config.Pipeline, manager.config.Pipeline,
manager.url, manager.url,
) )
@ -54,8 +54,8 @@ func (manager *BroadcastManager) Start() error {
} }
manager.logger.Info(). manager.logger.Info().
Str("audio_device", manager.remote.Device). Str("audio_device", manager.capture.Device).
Str("video_display", manager.remote.Display). Str("video_display", manager.capture.Display).
Str("rtmp_pipeline_src", manager.pipeline.Src). Str("rtmp_pipeline_src", manager.pipeline.Src).
Msgf("RTMP pipeline is starting...") Msgf("RTMP pipeline is starting...")

View File

@ -1,11 +1,11 @@
package remote package capture
import ( import (
"fmt" "fmt"
"time" "time"
"m1k1o/neko/internal/desktop/xorg"
"m1k1o/neko/internal/gst" "m1k1o/neko/internal/gst"
"m1k1o/neko/internal/remote/xorg"
"m1k1o/neko/internal/types" "m1k1o/neko/internal/types"
"m1k1o/neko/internal/types/config" "m1k1o/neko/internal/types/config"
@ -14,47 +14,41 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
type RemoteManager struct { type CaptureManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
video *gst.Pipeline video *gst.Pipeline
audio *gst.Pipeline audio *gst.Pipeline
config *config.Remote config *config.Capture
broadcast types.BroadcastManager broadcast types.BroadcastManager
desktop types.DesktopManager
cleanup *time.Ticker cleanup *time.Ticker
shutdown chan bool shutdown chan bool
emmiter events.EventEmmiter emmiter events.EventEmmiter
streaming bool streaming bool
} }
func New(config *config.Remote, broadcast types.BroadcastManager) *RemoteManager { func New(desktop types.DesktopManager, broadcast types.BroadcastManager, config *config.Capture) *CaptureManagerCtx {
return &RemoteManager{ return &CaptureManagerCtx{
logger: log.With().Str("module", "remote").Logger(), logger: log.With().Str("module", "capture").Logger(),
cleanup: time.NewTicker(1 * time.Second), cleanup: time.NewTicker(1 * time.Second),
shutdown: make(chan bool), shutdown: make(chan bool),
emmiter: events.New(), emmiter: events.New(),
config: config, config: config,
broadcast: broadcast, broadcast: broadcast,
desktop: desktop,
streaming: false, streaming: false,
} }
} }
func (manager *RemoteManager) VideoCodec() string { func (manager *CaptureManagerCtx) VideoCodec() string {
return manager.config.VideoCodec return manager.config.VideoCodec
} }
func (manager *RemoteManager) AudioCodec() string { func (manager *CaptureManagerCtx) AudioCodec() string {
return manager.config.AudioCodec return manager.config.AudioCodec
} }
func (manager *RemoteManager) Start() { func (manager *CaptureManagerCtx) 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.createPipelines() manager.createPipelines()
if err := manager.broadcast.Start(); err != nil { if err := manager.broadcast.Start(); err != nil {
manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline") manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline")
@ -74,14 +68,14 @@ func (manager *RemoteManager) Start() {
case sample := <-manager.audio.Sample: case sample := <-manager.audio.Sample:
manager.emmiter.Emit("audio", sample) manager.emmiter.Emit("audio", sample)
case <-manager.cleanup.C: case <-manager.cleanup.C:
xorg.CheckKeys(time.Second * 10) // TODO: refactor.
} }
} }
}() }()
} }
func (manager *RemoteManager) Shutdown() error { func (manager *CaptureManagerCtx) Shutdown() error {
manager.logger.Info().Msgf("remote shutting down") manager.logger.Info().Msgf("capture shutting down")
manager.video.Stop() manager.video.Stop()
manager.audio.Stop() manager.audio.Stop()
manager.broadcast.Stop() manager.broadcast.Stop()
@ -91,19 +85,19 @@ func (manager *RemoteManager) Shutdown() error {
return nil 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{}) { manager.emmiter.On("video", func(payload ...interface{}) {
listener(payload[0].(types.Sample)) 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{}) { manager.emmiter.On("audio", func(payload ...interface{}) {
listener(payload[0].(types.Sample)) listener(payload[0].(types.Sample))
}) })
} }
func (manager *RemoteManager) StartStream() { func (manager *CaptureManagerCtx) StartStream() {
manager.createPipelines() manager.createPipelines()
manager.logger.Info(). manager.logger.Info().
@ -113,7 +107,6 @@ func (manager *RemoteManager) StartStream() {
Str("audio_codec", manager.config.AudioCodec). Str("audio_codec", manager.config.AudioCodec).
Str("audio_pipeline_src", manager.audio.Src). Str("audio_pipeline_src", manager.audio.Src).
Str("video_pipeline_src", manager.video.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...") Msgf("Pipelines starting...")
manager.video.Start() manager.video.Start()
@ -121,21 +114,21 @@ func (manager *RemoteManager) StartStream() {
manager.streaming = true manager.streaming = true
} }
func (manager *RemoteManager) StopStream() { func (manager *CaptureManagerCtx) StopStream() {
manager.logger.Info().Msgf("Pipelines shutting down...") manager.logger.Info().Msgf("Pipelines shutting down...")
manager.video.Stop() manager.video.Stop()
manager.audio.Stop() manager.audio.Stop()
manager.streaming = false manager.streaming = false
} }
func (manager *RemoteManager) Streaming() bool { func (manager *CaptureManagerCtx) Streaming() bool {
return manager.streaming return manager.streaming
} }
func (manager *RemoteManager) createPipelines() { func (manager *CaptureManagerCtx) createPipelines() {
// handle maximum fps // handle maximum fps
rate := manager.config.ScreenRate rate := int(manager.desktop.GetScreenSize().Rate)
if manager.config.MaxFPS != 0 && manager.config.MaxFPS < manager.config.ScreenRate { if manager.config.MaxFPS != 0 && manager.config.MaxFPS < rate {
rate = manager.config.MaxFPS 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) { if !xorg.ValidScreenSize(width, height, rate) {
return fmt.Errorf("unknown configuration") return fmt.Errorf("unknown configuration")
} }

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -12,11 +12,11 @@ import (
"m1k1o/neko/internal/utils" "m1k1o/neko/internal/utils"
) )
func New(remote types.RemoteManager) *SessionManager { func New(capture types.CaptureManager) *SessionManager {
return &SessionManager{ return &SessionManager{
logger: log.With().Str("module", "session").Logger(), logger: log.With().Str("module", "session").Logger(),
host: "", host: "",
remote: remote, capture: capture,
members: make(map[string]*Session), members: make(map[string]*Session),
emmiter: events.New(), emmiter: events.New(),
} }
@ -26,7 +26,7 @@ type SessionManager struct {
mu sync.Mutex mu sync.Mutex
logger zerolog.Logger logger zerolog.Logger
host string host string
remote types.RemoteManager capture types.CaptureManager
members map[string]*Session members map[string]*Session
emmiter events.EventEmmiter emmiter events.EventEmmiter
// TODO: Handle locks in sessions as flags. // 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.mu.Lock()
manager.members[id] = session manager.members[id] = session
if !manager.remote.Streaming() && len(manager.members) > 0 { if !manager.capture.Streaming() && len(manager.members) > 0 {
manager.remote.StartStream() manager.capture.StartStream()
} }
manager.mu.Unlock() manager.mu.Unlock()
@ -160,8 +160,8 @@ func (manager *SessionManager) Destroy(id string) {
err := session.destroy() err := session.destroy()
delete(manager.members, id) delete(manager.members, id)
if manager.remote.Streaming() && len(manager.members) <= 0 { if manager.capture.Streaming() && len(manager.members) <= 0 {
manager.remote.StopStream() manager.capture.StopStream()
} }
manager.mu.Unlock() manager.mu.Unlock()

View File

@ -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
}

View File

@ -1,14 +1,11 @@
package config package config
import ( import (
"regexp"
"strconv"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type Remote struct { type Capture struct {
Display string Display string
Device string Device string
AudioCodec string AudioCodec string
@ -18,13 +15,10 @@ type Remote struct {
VideoCodec string VideoCodec string
VideoParams string VideoParams string
VideoBitrate uint VideoBitrate uint
ScreenWidth int
ScreenHeight int
ScreenRate int
MaxFPS 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") cmd.PersistentFlags().String("display", ":99.0", "XDisplay to capture")
if err := viper.BindPFlag("display", cmd.PersistentFlags().Lookup("display")); err != nil { if err := viper.BindPFlag("display", cmd.PersistentFlags().Lookup("display")); err != nil {
return err return err
@ -55,11 +49,6 @@ func (Remote) Init(cmd *cobra.Command) error {
return err 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") 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 { if err := viper.BindPFlag("max_fps", cmd.PersistentFlags().Lookup("max_fps")); err != nil {
return err return err
@ -111,7 +100,7 @@ func (Remote) Init(cmd *cobra.Command) error {
return nil return nil
} }
func (s *Remote) Set() { func (s *Capture) Set() {
audioCodec := "Opus" audioCodec := "Opus"
if viper.GetBool("opus") { if viper.GetBool("opus") {
audioCodec = "Opus" audioCodec = "Opus"
@ -146,24 +135,5 @@ func (s *Remote) Set() {
s.VideoParams = viper.GetString("video") s.VideoParams = viper.GetString("video")
s.VideoBitrate = viper.GetUint("video_bitrate") 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") s.MaxFPS = viper.GetInt("max_fps")
} }

View File

@ -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)
}
}
}

View File

@ -1,27 +1,21 @@
package types package types
type RemoteManager interface { type DesktopManager interface {
VideoCodec() string
AudioCodec() string
Start() Start()
Shutdown() error Shutdown() error
OnVideoFrame(listener func(sample Sample)) // clipboard
OnAudioFrame(listener func(sample Sample)) ReadClipboard() string
StartStream() WriteClipboard(data string)
StopStream() // xorg
Streaming() bool
ChangeResolution(width int, height int, rate int) error
GetScreenSize() *ScreenSize
ScreenConfigurations() map[int]ScreenConfiguration
Move(x, y int) Move(x, y int)
Scroll(x, y int) Scroll(x, y int)
ButtonDown(code int) error ButtonDown(code int) error
KeyDown(code uint64) error KeyDown(code uint64) error
ButtonUp(code int) error ButtonUp(code int) error
KeyUp(code uint64) error KeyUp(code uint64) error
ReadClipboard() string
WriteClipboard(data string)
ResetKeys() ResetKeys()
ScreenConfigurations() map[int]ScreenConfiguration
GetScreenSize() *ScreenSize
SetKeyboardLayout(layout string) SetKeyboardLayout(layout string)
SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int) SetKeyboardModifiers(NumLock int, CapsLock int, ScrollLock int)
} }

View File

@ -64,7 +64,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
return err return err
} }
manager.remote.Move(int(payload.X), int(payload.Y)) manager.desktop.Move(int(payload.X), int(payload.Y))
case OP_SCROLL: case OP_SCROLL:
payload := &PayloadScroll{} payload := &PayloadScroll{}
if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { 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))). Str("y", strconv.Itoa(int(payload.Y))).
Msg("scroll") Msg("scroll")
manager.remote.Scroll(int(payload.X), int(payload.Y)) manager.desktop.Scroll(int(payload.X), int(payload.Y))
case OP_KEY_DOWN: case OP_KEY_DOWN:
payload := &PayloadKey{} payload := &PayloadKey{}
if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil { 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 { if payload.Key < 8 {
err := manager.remote.ButtonDown(int(payload.Key)) err := manager.desktop.ButtonDown(int(payload.Key))
if err != nil { if err != nil {
manager.logger.Warn().Err(err).Msg("button down failed") manager.logger.Warn().Err(err).Msg("button down failed")
return nil 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) manager.logger.Debug().Msgf("button down %d", payload.Key)
} else { } else {
err := manager.remote.KeyDown(uint64(payload.Key)) err := manager.desktop.KeyDown(uint64(payload.Key))
if err != nil { if err != nil {
manager.logger.Warn().Err(err).Msg("key down failed") manager.logger.Warn().Err(err).Msg("key down failed")
return nil return nil
@ -109,7 +109,7 @@ func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) e
} }
if payload.Key < 8 { if payload.Key < 8 {
err := manager.remote.ButtonUp(int(payload.Key)) err := manager.desktop.ButtonUp(int(payload.Key))
if err != nil { if err != nil {
manager.logger.Warn().Err(err).Msg("button up failed") manager.logger.Warn().Err(err).Msg("button up failed")
return nil 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) manager.logger.Debug().Msgf("button up %d", payload.Key)
} else { } else {
err := manager.remote.KeyUp(uint64(payload.Key)) err := manager.desktop.KeyUp(uint64(payload.Key))
if err != nil { if err != nil {
manager.logger.Warn().Err(err).Msg("key up failed") manager.logger.Warn().Err(err).Msg("key up failed")
return nil return nil

View File

@ -18,10 +18,11 @@ import (
"m1k1o/neko/internal/types/config" "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{ return &WebRTCManager{
logger: log.With().Str("module", "webrtc").Logger(), logger: log.With().Str("module", "webrtc").Logger(),
remote: remote, capture: capture,
desktop: desktop,
sessions: sessions, sessions: sessions,
config: config, config: config,
} }
@ -34,30 +35,31 @@ type WebRTCManager struct {
videoCodec webrtc.RTPCodecParameters videoCodec webrtc.RTPCodecParameters
audioCodec webrtc.RTPCodecParameters audioCodec webrtc.RTPCodecParameters
sessions types.SessionManager sessions types.SessionManager
remote types.RemoteManager capture types.CaptureManager
desktop types.DesktopManager
config *config.WebRTC config *config.WebRTC
api *webrtc.API api *webrtc.API
} }
func (manager *WebRTCManager) Start() { func (manager *WebRTCManager) Start() {
var err error 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 { if err != nil {
manager.logger.Panic().Err(err).Msg("unable to create audio track") 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 { 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.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 { if err != nil {
manager.logger.Panic().Err(err).Msg("unable to create video track") 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 { if err := manager.videoTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe {
manager.logger.Warn().Err(err).Msg("video pipeline failed to write") manager.logger.Warn().Err(err).Msg("video pipeline failed to write")
} }

View File

@ -131,7 +131,7 @@ func (h *MessageHandler) controlClipboard(id string, session types.Session, payl
return nil return nil
} }
h.remote.WriteClipboard(payload.Text) h.desktop.WriteClipboard(payload.Text)
return nil return nil
} }
@ -144,7 +144,7 @@ func (h *MessageHandler) controlKeyboard(id string, session types.Session, paylo
// change layout // change layout
if payload.Layout != nil { if payload.Layout != nil {
h.remote.SetKeyboardLayout(*payload.Layout) h.desktop.SetKeyboardLayout(*payload.Layout)
} }
// set num lock // set num lock
@ -177,6 +177,6 @@ func (h *MessageHandler) controlKeyboard(id string, session types.Session, paylo
Int("ScrollLock", ScrollLock). Int("ScrollLock", ScrollLock).
Msg("setting keyboard modifiers") Msg("setting keyboard modifiers")
h.remote.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock) h.desktop.SetKeyboardModifiers(NumLock, CapsLock, ScrollLock)
return nil return nil
} }

View File

@ -16,7 +16,8 @@ type MessageHandler struct {
logger zerolog.Logger logger zerolog.Logger
sessions types.SessionManager sessions types.SessionManager
webrtc types.WebRTCManager webrtc types.WebRTCManager
remote types.RemoteManager desktop types.DesktopManager
capture types.CaptureManager
broadcast types.BroadcastManager broadcast types.BroadcastManager
banned map[string]string // IP -> session ID (that banned it) banned map[string]string // IP -> session ID (that banned it)
locked map[string]string // resource name -> session ID (that locked it) locked map[string]string // resource name -> session ID (that locked it)

View File

@ -12,7 +12,7 @@ func (h *MessageHandler) screenSet(id string, session types.Session, payload *me
return nil 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") h.logger.Warn().Err(err).Msgf("unable to change screen size")
return err 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 { 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{ if err := session.Send(message.ScreenResolution{
Event: event.SCREEN_RESOLUTION, Event: event.SCREEN_RESOLUTION,
Width: size.Width, Width: size.Width,
@ -56,7 +56,7 @@ func (h *MessageHandler) screenConfigurations(id string, session types.Session)
if err := session.Send(message.ScreenConfigurations{ if err := session.Send(message.ScreenConfigurations{
Event: event.SCREEN_CONFIGURATIONS, Event: event.SCREEN_CONFIGURATIONS,
Configurations: h.remote.ScreenConfigurations(), Configurations: h.desktop.ScreenConfigurations(),
}); err != nil { }); err != nil {
h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_CONFIGURATIONS) h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_CONFIGURATIONS)
return err return err

View File

@ -20,7 +20,7 @@ import (
const CONTROL_PROTECTION_SESSION = "by_control_protection" 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() logger := log.With().Str("module", "websocket").Logger()
locks := make(map[string]string) locks := make(map[string]string)
@ -45,7 +45,8 @@ func New(sessions types.SessionManager, remote types.RemoteManager, broadcast ty
shutdown: make(chan interface{}), shutdown: make(chan interface{}),
conf: conf, conf: conf,
sessions: sessions, sessions: sessions,
remote: remote, desktop: desktop,
upgrader: websocket.Upgrader{ upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { CheckOrigin: func(r *http.Request) bool {
return true return true
@ -53,7 +54,8 @@ func New(sessions types.SessionManager, remote types.RemoteManager, broadcast ty
}, },
handler: &MessageHandler{ handler: &MessageHandler{
logger: logger.With().Str("subsystem", "handler").Logger(), logger: logger.With().Str("subsystem", "handler").Logger(),
remote: remote, desktop: desktop,
capture: capture,
broadcast: broadcast, broadcast: broadcast,
sessions: sessions, sessions: sessions,
webrtc: webrtc, webrtc: webrtc,
@ -73,7 +75,7 @@ type WebSocketHandler struct {
shutdown chan interface{} shutdown chan interface{}
upgrader websocket.Upgrader upgrader websocket.Upgrader
sessions types.SessionManager sessions types.SessionManager
remote types.RemoteManager desktop types.DesktopManager
conf *config.WebSocket conf *config.WebSocket
handler *MessageHandler handler *MessageHandler
@ -175,7 +177,7 @@ func (ws *WebSocketHandler) Start() {
ws.wg.Done() ws.wg.Done()
}() }()
current := ws.remote.ReadClipboard() current := ws.desktop.ReadClipboard()
for { for {
select { select {
@ -188,7 +190,7 @@ func (ws *WebSocketHandler) Start() {
continue continue
} }
text := ws.remote.ReadClipboard() text := ws.desktop.ReadClipboard()
if text == current { if text == current {
continue continue
} }

View File

@ -7,8 +7,9 @@ import (
"runtime" "runtime"
"m1k1o/neko/internal/broadcast" "m1k1o/neko/internal/broadcast"
"m1k1o/neko/internal/capture"
"m1k1o/neko/internal/desktop"
"m1k1o/neko/internal/http" "m1k1o/neko/internal/http"
"m1k1o/neko/internal/remote"
"m1k1o/neko/internal/session" "m1k1o/neko/internal/session"
"m1k1o/neko/internal/types/config" "m1k1o/neko/internal/types/config"
"m1k1o/neko/internal/webrtc" "m1k1o/neko/internal/webrtc"
@ -61,7 +62,8 @@ func init() {
}, },
Root: &config.Root{}, Root: &config.Root{},
Server: &config.Server{}, Server: &config.Server{},
Remote: &config.Remote{}, Capture: &config.Capture{},
Desktop: &config.Desktop{},
Broadcast: &config.Broadcast{}, Broadcast: &config.Broadcast{},
WebRTC: &config.WebRTC{}, WebRTC: &config.WebRTC{},
WebSocket: &config.WebSocket{}, WebSocket: &config.WebSocket{},
@ -100,7 +102,8 @@ func (i *Version) Details() string {
type Neko struct { type Neko struct {
Version *Version Version *Version
Root *config.Root Root *config.Root
Remote *config.Remote Capture *config.Capture
Desktop *config.Desktop
Broadcast *config.Broadcast Broadcast *config.Broadcast
Server *config.Server Server *config.Server
WebRTC *config.WebRTC WebRTC *config.WebRTC
@ -109,7 +112,8 @@ type Neko struct {
logger zerolog.Logger logger zerolog.Logger
server *http.Server server *http.Server
sessionManager *session.SessionManager sessionManager *session.SessionManager
remoteManager *remote.RemoteManager captureManager *capture.CaptureManagerCtx
desktopManager *desktop.DesktopManagerCtx
broadcastManager *broadcast.BroadcastManager broadcastManager *broadcast.BroadcastManager
webRTCManager *webrtc.WebRTCManager webRTCManager *webrtc.WebRTCManager
webSocketHandler *websocket.WebSocketHandler webSocketHandler *websocket.WebSocketHandler
@ -120,17 +124,20 @@ func (neko *Neko) Preflight() {
} }
func (neko *Neko) Start() { func (neko *Neko) Start() {
broadcastManager := broadcast.New(neko.Remote, neko.Broadcast) broadcastManager := broadcast.New(neko.Capture, neko.Broadcast)
remoteManager := remote.New(neko.Remote, broadcastManager) desktopManager := desktop.New(neko.Desktop, broadcastManager)
remoteManager.Start() 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() webRTCManager.Start()
webSocketHandler := websocket.New(sessionManager, remoteManager, broadcastManager, webRTCManager, neko.WebSocket) webSocketHandler := websocket.New(sessionManager, desktopManager, captureManager, broadcastManager, webRTCManager, neko.WebSocket)
webSocketHandler.Start() webSocketHandler.Start()
server := http.New(neko.Server, webSocketHandler) server := http.New(neko.Server, webSocketHandler)
@ -138,7 +145,8 @@ func (neko *Neko) Start() {
neko.broadcastManager = broadcastManager neko.broadcastManager = broadcastManager
neko.sessionManager = sessionManager neko.sessionManager = sessionManager
neko.remoteManager = remoteManager neko.captureManager = captureManager
neko.desktopManager = desktopManager
neko.webRTCManager = webRTCManager neko.webRTCManager = webRTCManager
neko.webSocketHandler = webSocketHandler neko.webSocketHandler = webSocketHandler
neko.server = server neko.server = server
@ -150,8 +158,11 @@ func (neko *Neko) Shutdown() {
err = neko.broadcastManager.Shutdown() err = neko.broadcastManager.Shutdown()
neko.logger.Err(err).Msg("broadcast manager shutdown") neko.logger.Err(err).Msg("broadcast manager shutdown")
err = neko.remoteManager.Shutdown() err = neko.desktopManager.Shutdown()
neko.logger.Err(err).Msg("remote manager 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() err = neko.webRTCManager.Shutdown()
neko.logger.Err(err).Msg("webrtc manager shutdown") neko.logger.Err(err).Msg("webrtc manager shutdown")