split remote to desktop and capture.
This commit is contained in:
parent
0bca8c9d02
commit
de4f6b45e5
@ -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,
|
||||
}
|
||||
|
@ -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...")
|
||||
|
||||
|
@ -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")
|
||||
}
|
11
server/internal/desktop/clipboard.go
Normal file
11
server/internal/desktop/clipboard.go
Normal 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)
|
||||
}
|
65
server/internal/desktop/manager.go
Normal file
65
server/internal/desktop/manager.go
Normal 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
|
||||
}
|
52
server/internal/desktop/xorg.go
Normal file
52
server/internal/desktop/xorg.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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()
|
||||
|
||||
|
14
server/internal/types/capture.go
Normal file
14
server/internal/types/capture.go
Normal 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
|
||||
}
|
@ -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")
|
||||
}
|
51
server/internal/types/config/desktop.go
Normal file
51
server/internal/types/config/desktop.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
Reference in New Issue
Block a user