better debouncing
This commit is contained in:
parent
5830f92637
commit
6c815b019d
@ -121,7 +121,7 @@ func init() {
|
|||||||
Keyboard[KEY_Q] = "q"
|
Keyboard[KEY_Q] = "q"
|
||||||
Keyboard[KEY_R] = "r"
|
Keyboard[KEY_R] = "r"
|
||||||
Keyboard[KEY_S] = "s"
|
Keyboard[KEY_S] = "s"
|
||||||
Keyboard[KEY_T] = "r"
|
Keyboard[KEY_T] = "t"
|
||||||
Keyboard[KEY_U] = "u"
|
Keyboard[KEY_U] = "u"
|
||||||
Keyboard[KEY_V] = "v"
|
Keyboard[KEY_V] = "v"
|
||||||
Keyboard[KEY_W] = "w"
|
Keyboard[KEY_W] = "w"
|
||||||
|
@ -3,6 +3,7 @@ package webrtc
|
|||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/pion/webrtc/v2"
|
"github.com/pion/webrtc/v2"
|
||||||
@ -20,29 +21,36 @@ func NewManager(password string) (*WebRTCManager, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gst.CreatePipeline(webrtc.VP8, []*webrtc.Track{video}, "ximagesrc show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert").Start()
|
|
||||||
engine.RegisterCodec(videoCodec)
|
engine.RegisterCodec(videoCodec)
|
||||||
// ximagesrc xid=0 show-pointer=true ! videoconvert ! queue | videotestsrc
|
|
||||||
|
|
||||||
audioCodec := webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000)
|
audioCodec := webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000)
|
||||||
audio, err := webrtc.NewTrack(webrtc.DefaultPayloadTypeOpus, rand.Uint32(), "stream", "stream", audioCodec)
|
audio, err := webrtc.NewTrack(webrtc.DefaultPayloadTypeOpus, rand.Uint32(), "stream", "stream", audioCodec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gst.CreatePipeline(webrtc.Opus, []*webrtc.Track{audio}, "pulsesrc device=auto_null.monitor ! audioconvert").Start()
|
|
||||||
engine.RegisterCodec(audioCodec)
|
engine.RegisterCodec(audioCodec)
|
||||||
|
|
||||||
|
videoPipeline := gst.CreatePipeline(webrtc.VP8, []*webrtc.Track{video}, "ximagesrc show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert")
|
||||||
|
// ximagesrc xid=0 show-pointer=true ! videoconvert ! queue | videotestsrc
|
||||||
|
|
||||||
|
audioPipeline := gst.CreatePipeline(webrtc.Opus, []*webrtc.Track{audio}, "pulsesrc device=auto_null.monitor ! audioconvert")
|
||||||
// pulsesrc device=auto_null.monitor ! audioconvert | audiotestsrc
|
// pulsesrc device=auto_null.monitor ! audioconvert | audiotestsrc
|
||||||
// gst-launch-1.0 -v pulsesrc device=auto_null.monitor ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
|
// gst-launch-1.0 -v pulsesrc device=auto_null.monitor ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
|
||||||
|
|
||||||
return &WebRTCManager{
|
return &WebRTCManager{
|
||||||
logger: log.With().Str("service", "webrtc").Logger(),
|
logger: log.With().Str("service", "webrtc").Logger(),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
api: webrtc.NewAPI(webrtc.WithMediaEngine(engine)),
|
api: webrtc.NewAPI(webrtc.WithMediaEngine(engine)),
|
||||||
video: video,
|
video: video,
|
||||||
audio: audio,
|
videoPipeline: videoPipeline,
|
||||||
controller: "",
|
audio: audio,
|
||||||
password: password,
|
audioPipeline: audioPipeline,
|
||||||
sessions: make(map[string]*session),
|
controller: "",
|
||||||
|
password: password,
|
||||||
|
sessions: make(map[string]*session),
|
||||||
|
debounce: make(map[int]time.Time),
|
||||||
|
cleanup: time.NewTicker(500 * time.Second),
|
||||||
|
shutdown: make(chan bool),
|
||||||
upgrader: websocket.Upgrader{
|
upgrader: websocket.Upgrader{
|
||||||
CheckOrigin: func(r *http.Request) bool {
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
return true
|
return true
|
||||||
@ -60,14 +68,45 @@ func NewManager(password string) (*WebRTCManager, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebRTCManager struct {
|
type WebRTCManager struct {
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
upgrader websocket.Upgrader
|
upgrader websocket.Upgrader
|
||||||
engine webrtc.MediaEngine
|
engine webrtc.MediaEngine
|
||||||
api *webrtc.API
|
api *webrtc.API
|
||||||
config webrtc.Configuration
|
config webrtc.Configuration
|
||||||
password string
|
password string
|
||||||
controller string
|
controller string
|
||||||
sessions map[string]*session
|
sessions map[string]*session
|
||||||
video *webrtc.Track
|
debounce map[int]time.Time
|
||||||
audio *webrtc.Track
|
shutdown chan bool
|
||||||
|
cleanup *time.Ticker
|
||||||
|
video *webrtc.Track
|
||||||
|
audio *webrtc.Track
|
||||||
|
videoPipeline *gst.Pipeline
|
||||||
|
audioPipeline *gst.Pipeline
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WebRTCManager) Start() error {
|
||||||
|
manager.videoPipeline.Start()
|
||||||
|
manager.audioPipeline.Start()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-manager.shutdown:
|
||||||
|
return
|
||||||
|
case <-manager.cleanup.C:
|
||||||
|
manager.checkKeys()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WebRTCManager) Shutdown() error {
|
||||||
|
manager.cleanup.Stop()
|
||||||
|
manager.shutdown <- true
|
||||||
|
manager.videoPipeline.Stop()
|
||||||
|
manager.audioPipeline.Stop()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-vgo/robotgo"
|
"github.com/go-vgo/robotgo"
|
||||||
"github.com/pion/webrtc/v2"
|
"github.com/pion/webrtc/v2"
|
||||||
@ -76,8 +77,6 @@ func (manager *WebRTCManager) createPeer(session *session, raw []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var debounce = map[int]bool{}
|
|
||||||
|
|
||||||
func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMessage) error {
|
func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMessage) error {
|
||||||
if manager.controller != session.id {
|
if manager.controller != session.id {
|
||||||
return nil
|
return nil
|
||||||
@ -101,27 +100,28 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
switch header.Event {
|
switch header.Event {
|
||||||
case 0x01: // MOUSE_MOVE
|
case 0x01: // MOUSE_MOVE
|
||||||
payload := &dataMouseMove{}
|
payload := &dataMouseMove{}
|
||||||
err := binary.Read(buffer, binary.LittleEndian, payload)
|
if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
robotgo.Move(int(payload.X), int(payload.Y))
|
robotgo.Move(int(payload.X), int(payload.Y))
|
||||||
break
|
break
|
||||||
case 0x02: // MOUSE_UP
|
case 0x02: // MOUSE_UP
|
||||||
payload := &dataMouseKey{}
|
payload := &dataMouseKey{}
|
||||||
err := binary.Read(buffer, binary.LittleEndian, payload)
|
if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if key, ok := keys.Mouse[int(payload.Key)]; ok {
|
code := int(payload.Key)
|
||||||
if !debounce[int(payload.Key)] {
|
if key, ok := keys.Mouse[code]; ok {
|
||||||
|
if _, ok := manager.debounce[code]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
debounce[int(payload.Key)] = false
|
delete(manager.debounce, code)
|
||||||
robotgo.MouseToggle("up", key)
|
robotgo.MouseToggle("up", key)
|
||||||
|
manager.logger.Debug().Msgf("MOUSE_UP key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown MOUSE_DOWN key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown MOUSE_UP key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 0x03: // MOUSE_DOWN
|
case 0x03: // MOUSE_DOWN
|
||||||
@ -131,15 +131,17 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if key, ok := keys.Mouse[int(payload.Key)]; ok {
|
code := int(payload.Key)
|
||||||
if debounce[int(payload.Key)] {
|
if key, ok := keys.Mouse[code]; ok {
|
||||||
|
if _, ok := manager.debounce[code]; ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
debounce[int(payload.Key)] = true
|
|
||||||
|
|
||||||
|
manager.debounce[code] = time.Now()
|
||||||
robotgo.MouseToggle("down", key)
|
robotgo.MouseToggle("down", key)
|
||||||
|
manager.logger.Debug().Msgf("MOUSE_DOWN key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown MOUSE_DOWN key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown MOUSE_DOWN key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 0x04: // MOUSE_CLK
|
case 0x04: // MOUSE_CLK
|
||||||
@ -149,8 +151,9 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if key, ok := keys.Mouse[int(payload.Key)]; ok {
|
code := int(payload.Key)
|
||||||
switch int(payload.Key) {
|
if key, ok := keys.Mouse[code]; ok {
|
||||||
|
switch code {
|
||||||
case keys.MOUSE_WHEEL_DOWN:
|
case keys.MOUSE_WHEEL_DOWN:
|
||||||
robotgo.Scroll(0, -1)
|
robotgo.Scroll(0, -1)
|
||||||
break
|
break
|
||||||
@ -166,8 +169,10 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
default:
|
default:
|
||||||
robotgo.Click(key, false)
|
robotgo.Click(key, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manager.logger.Debug().Msgf("MOUSE_CLK key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown MOUSE_CLK key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown MOUSE_CLK key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 0x05: // KEY_DOWN
|
case 0x05: // KEY_DOWN
|
||||||
@ -176,30 +181,35 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if key, ok := keys.Keyboard[int(payload.Key)]; ok {
|
|
||||||
if debounce[int(payload.Key)] {
|
code := int(payload.Key)
|
||||||
|
if key, ok := keys.Keyboard[code]; ok {
|
||||||
|
if _, ok := manager.debounce[code]; ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
debounce[int(payload.Key)] = true
|
manager.debounce[code] = time.Now()
|
||||||
robotgo.KeyToggle(key, "down")
|
robotgo.KeyToggle(key, "down")
|
||||||
|
manager.logger.Debug().Msgf("KEY_DOWN key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown KEY_DOWN key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown KEY_DOWN key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 0x06: // KEY_UP
|
case 0x06: // KEY_UP
|
||||||
payload := &dataKeyboardKey{}
|
payload := &dataKeyboardKey{}
|
||||||
err := binary.Read(buffer, binary.LittleEndian, payload)
|
if err := binary.Read(buffer, binary.LittleEndian, payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if key, ok := keys.Keyboard[int(payload.Key)]; ok {
|
|
||||||
if !debounce[int(payload.Key)] {
|
code := int(payload.Key)
|
||||||
|
if key, ok := keys.Keyboard[code]; ok {
|
||||||
|
if _, ok := manager.debounce[code]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
debounce[int(payload.Key)] = false
|
delete(manager.debounce, code)
|
||||||
robotgo.KeyToggle(key, "up")
|
robotgo.KeyToggle(key, "up")
|
||||||
|
manager.logger.Debug().Msgf("KEY_UP key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown KEY_UP key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown KEY_UP key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 0x07: // KEY_CLK
|
case 0x07: // KEY_CLK
|
||||||
@ -208,13 +218,49 @@ func (manager *WebRTCManager) onData(session *session, msg webrtc.DataChannelMes
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if key, ok := keys.Keyboard[int(payload.Key)]; ok {
|
|
||||||
|
code := int(payload.Key)
|
||||||
|
if key, ok := keys.Keyboard[code]; ok {
|
||||||
robotgo.KeyTap(key)
|
robotgo.KeyTap(key)
|
||||||
|
manager.logger.Debug().Msgf("KEY_CLK key: %v (%v)", code, key)
|
||||||
} else {
|
} else {
|
||||||
manager.logger.Warn().Msgf("Unknown KEY_CLK key: %v", payload.Key)
|
manager.logger.Warn().Msgf("Unknown KEY_CLK key: %v", code)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (manager *WebRTCManager) clearKeys() {
|
||||||
|
for code := range manager.debounce {
|
||||||
|
if key, ok := keys.Keyboard[code]; ok {
|
||||||
|
robotgo.MouseToggle(key, "up")
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, ok := keys.Mouse[code]; ok {
|
||||||
|
robotgo.KeyToggle(key, "up")
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(manager.debounce, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WebRTCManager) checkKeys() {
|
||||||
|
t := time.Now()
|
||||||
|
for code, start := range manager.debounce {
|
||||||
|
if t.Sub(start) < (time.Second * 10) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, ok := keys.Keyboard[code]; ok {
|
||||||
|
robotgo.MouseToggle(key, "up")
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, ok := keys.Mouse[code]; ok {
|
||||||
|
robotgo.KeyToggle(key, "up")
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(manager.debounce, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -162,6 +162,7 @@ func (manager *WebRTCManager) handleWS(session *session) {
|
|||||||
func (manager *WebRTCManager) destroy(session *session) {
|
func (manager *WebRTCManager) destroy(session *session) {
|
||||||
if manager.controller == session.id {
|
if manager.controller == session.id {
|
||||||
manager.controller = ""
|
manager.controller = ""
|
||||||
|
manager.clearKeys()
|
||||||
for id, sess := range manager.sessions {
|
for id, sess := range manager.sessions {
|
||||||
if id != session.id {
|
if id != session.id {
|
||||||
if err := sess.send(message{Event: "control/released"}); err != nil {
|
if err := sess.send(message{Event: "control/released"}); err != nil {
|
||||||
@ -181,6 +182,7 @@ func (manager *WebRTCManager) destroy(session *session) {
|
|||||||
func (manager *WebRTCManager) controlRelease(session *session) error {
|
func (manager *WebRTCManager) controlRelease(session *session) error {
|
||||||
if manager.controller == session.id {
|
if manager.controller == session.id {
|
||||||
manager.controller = ""
|
manager.controller = ""
|
||||||
|
manager.clearKeys()
|
||||||
|
|
||||||
if err := session.send(message{Event: "control/release"}); err != nil {
|
if err := session.send(message{Event: "control/release"}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -64,6 +64,7 @@ type Neko struct {
|
|||||||
Serve *config.Serve
|
Serve *config.Serve
|
||||||
Logger zerolog.Logger
|
Logger zerolog.Logger
|
||||||
http *http.Server
|
http *http.Server
|
||||||
|
manager *webrtc.WebRTCManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (neko *Neko) Preflight() {
|
func (neko *Neko) Preflight() {
|
||||||
@ -75,6 +76,10 @@ func (neko *Neko) Start() {
|
|||||||
|
|
||||||
manager, err := webrtc.NewManager(neko.Serve.Password)
|
manager, err := webrtc.NewManager(neko.Serve.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
neko.Logger.Panic().Err(err).Msg("Can not create webrtc manager")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := manager.Start(); err != nil {
|
||||||
neko.Logger.Panic().Err(err).Msg("Can not start webrtc manager")
|
neko.Logger.Panic().Err(err).Msg("Can not start webrtc manager")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +137,17 @@ func (neko *Neko) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
neko.http = server
|
neko.http = server
|
||||||
|
neko.manager = manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (neko *Neko) Shutdown() {
|
func (neko *Neko) Shutdown() {
|
||||||
|
if neko.manager != nil {
|
||||||
|
if err := neko.manager.Shutdown(); err != nil {
|
||||||
|
neko.Logger.Err(err).Msg("WebRTC manager shutdown with an error")
|
||||||
|
} else {
|
||||||
|
neko.Logger.Debug().Msg("WebRTC manager shutdown")
|
||||||
|
}
|
||||||
|
}
|
||||||
if neko.http != nil {
|
if neko.http != nil {
|
||||||
if err := neko.http.Shutdown(context.Background()); err != nil {
|
if err := neko.http.Shutdown(context.Background()); err != nil {
|
||||||
neko.Logger.Err(err).Msg("HTTP server shutdown with an error")
|
neko.Logger.Err(err).Msg("HTTP server shutdown with an error")
|
||||||
|
Reference in New Issue
Block a user