mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
move shared code to pkg.
This commit is contained in:
6
pkg/types/api.go
Normal file
6
pkg/types/api.go
Normal file
@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type ApiManager interface {
|
||||
Route(r Router)
|
||||
AddRouter(path string, router func(Router))
|
||||
}
|
163
pkg/types/capture.go
Normal file
163
pkg/types/capture.go
Normal file
@ -0,0 +1,163 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/PaesslerAG/gval"
|
||||
"github.com/pion/webrtc/v3/pkg/media"
|
||||
|
||||
"gitlab.com/demodesk/neko/server/pkg/types/codec"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCapturePipelineAlreadyExists = errors.New("capture pipeline already exists")
|
||||
)
|
||||
|
||||
type Sample media.Sample
|
||||
|
||||
type BroadcastManager interface {
|
||||
Start(url string) error
|
||||
Stop()
|
||||
Started() bool
|
||||
Url() string
|
||||
}
|
||||
|
||||
type ScreencastManager interface {
|
||||
Enabled() bool
|
||||
Started() bool
|
||||
Image() ([]byte, error)
|
||||
}
|
||||
|
||||
type StreamSinkManager interface {
|
||||
Codec() codec.RTPCodec
|
||||
|
||||
AddListener(listener *func(sample Sample)) error
|
||||
RemoveListener(listener *func(sample Sample)) error
|
||||
MoveListenerTo(listener *func(sample Sample), targetStream StreamSinkManager) error
|
||||
|
||||
ListenersCount() int
|
||||
Started() bool
|
||||
}
|
||||
|
||||
type StreamSrcManager interface {
|
||||
Codec() codec.RTPCodec
|
||||
|
||||
Start(codec codec.RTPCodec) error
|
||||
Stop()
|
||||
Push(bytes []byte)
|
||||
|
||||
Started() bool
|
||||
}
|
||||
|
||||
type CaptureManager interface {
|
||||
Start()
|
||||
Shutdown() error
|
||||
|
||||
Broadcast() BroadcastManager
|
||||
Screencast() ScreencastManager
|
||||
Audio() StreamSinkManager
|
||||
Video(videoID string) (StreamSinkManager, bool)
|
||||
VideoIDs() []string
|
||||
|
||||
Webcam() StreamSrcManager
|
||||
Microphone() StreamSrcManager
|
||||
}
|
||||
|
||||
type VideoConfig struct {
|
||||
Width string `mapstructure:"width"` // expression
|
||||
Height string `mapstructure:"height"` // expression
|
||||
Fps string `mapstructure:"fps"` // expression
|
||||
GstPrefix string `mapstructure:"gst_prefix"` // pipeline prefix, starts with !
|
||||
GstEncoder string `mapstructure:"gst_encoder"` // gst encoder name
|
||||
GstParams map[string]string `mapstructure:"gst_params"` // map of expressions
|
||||
GstSuffix string `mapstructure:"gst_suffix"` // pipeline suffix, starts with !
|
||||
GstPipeline string `mapstructure:"gst_pipeline"` // whole pipeline as a string
|
||||
}
|
||||
|
||||
func (config *VideoConfig) GetPipeline(screen ScreenSize) (string, error) {
|
||||
values := map[string]interface{}{
|
||||
"width": screen.Width,
|
||||
"height": screen.Height,
|
||||
"fps": screen.Rate,
|
||||
}
|
||||
|
||||
language := []gval.Language{
|
||||
gval.Function("round", func(args ...interface{}) (interface{}, error) {
|
||||
return (int)(math.Round(args[0].(float64))), nil
|
||||
}),
|
||||
}
|
||||
|
||||
// get fps pipeline
|
||||
fpsPipeline := "! video/x-raw ! videoconvert ! queue"
|
||||
if config.Fps != "" {
|
||||
eval, err := gval.Full(language...).NewEvaluable(config.Fps)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
val, err := eval.EvalFloat64(context.Background(), values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fpsPipeline = fmt.Sprintf("! video/x-raw,framerate=%d/100 ! videoconvert ! queue", int(val*100))
|
||||
}
|
||||
|
||||
// get scale pipeline
|
||||
scalePipeline := ""
|
||||
if config.Width != "" && config.Height != "" {
|
||||
eval, err := gval.Full(language...).NewEvaluable(config.Width)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
w, err := eval.EvalInt(context.Background(), values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
eval, err = gval.Full(language...).NewEvaluable(config.Height)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
h, err := eval.EvalInt(context.Background(), values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
scalePipeline = fmt.Sprintf("! videoscale ! video/x-raw,width=%d,height=%d ! queue", w, h)
|
||||
}
|
||||
|
||||
// get encoder pipeline
|
||||
encPipeline := fmt.Sprintf("! %s", config.GstEncoder)
|
||||
for key, expr := range config.GstParams {
|
||||
if expr == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := gval.Evaluate(expr, values, language...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
encPipeline += fmt.Sprintf(" %s=%v", key, val)
|
||||
} else {
|
||||
encPipeline += fmt.Sprintf(" %s=%s", key, expr)
|
||||
}
|
||||
}
|
||||
|
||||
// join strings with space
|
||||
return strings.Join([]string{
|
||||
fpsPipeline,
|
||||
scalePipeline,
|
||||
config.GstPrefix,
|
||||
encPipeline,
|
||||
config.GstSuffix,
|
||||
}[:], " "), nil
|
||||
}
|
183
pkg/types/codec/codecs.go
Normal file
183
pkg/types/codec/codecs.go
Normal file
@ -0,0 +1,183 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
|
||||
func ParseRTC(codec webrtc.RTPCodecParameters) (RTPCodec, bool) {
|
||||
codecName := strings.Split(codec.RTPCodecCapability.MimeType, "/")[1]
|
||||
return ParseStr(codecName)
|
||||
}
|
||||
|
||||
func ParseStr(codecName string) (codec RTPCodec, ok bool) {
|
||||
ok = true
|
||||
|
||||
switch strings.ToLower(codecName) {
|
||||
case VP8().Name:
|
||||
codec = VP8()
|
||||
case VP9().Name:
|
||||
codec = VP9()
|
||||
case H264().Name:
|
||||
codec = H264()
|
||||
case Opus().Name:
|
||||
codec = Opus()
|
||||
case G722().Name:
|
||||
codec = G722()
|
||||
case PCMU().Name:
|
||||
codec = PCMU()
|
||||
case PCMA().Name:
|
||||
codec = PCMA()
|
||||
default:
|
||||
ok = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type RTPCodec struct {
|
||||
Name string
|
||||
PayloadType webrtc.PayloadType
|
||||
Type webrtc.RTPCodecType
|
||||
Capability webrtc.RTPCodecCapability
|
||||
Pipeline string
|
||||
}
|
||||
|
||||
func (codec *RTPCodec) Register(engine *webrtc.MediaEngine) error {
|
||||
return engine.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: codec.Capability,
|
||||
PayloadType: codec.PayloadType,
|
||||
}, codec.Type)
|
||||
}
|
||||
|
||||
func VP8() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "vp8",
|
||||
PayloadType: 96,
|
||||
Type: webrtc.RTPCodecTypeVideo,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP8,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html
|
||||
// gstreamer1.0-plugins-good
|
||||
Pipeline: "vp8enc cpu-used=16 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=15 static-threshold=20",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Profile ID.
|
||||
func VP9() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "vp9",
|
||||
PayloadType: 98,
|
||||
Type: webrtc.RTPCodecTypeVideo,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeVP9,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "profile-id=0",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html
|
||||
// gstreamer1.0-plugins-good
|
||||
Pipeline: "vp9enc cpu-used=16 threads=4 deadline=1 keyframe-max-dist=15 static-threshold=20",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Profile ID.
|
||||
func H264() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "h264",
|
||||
PayloadType: 102,
|
||||
Type: webrtc.RTPCodecTypeVideo,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH264,
|
||||
ClockRate: 90000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/x264/index.html
|
||||
// gstreamer1.0-plugins-ugly
|
||||
Pipeline: "video/x-raw,format=I420 ! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream",
|
||||
// https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html
|
||||
// gstreamer1.0-plugins-bad
|
||||
//Pipeline: "openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 ! video/x-h264,stream-format=byte-stream",
|
||||
}
|
||||
}
|
||||
|
||||
func Opus() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "opus",
|
||||
PayloadType: 111,
|
||||
Type: webrtc.RTPCodecTypeAudio,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeOpus,
|
||||
ClockRate: 48000,
|
||||
Channels: 2,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/opus/opusenc.html
|
||||
// gstreamer1.0-plugins-base
|
||||
Pipeline: "opusenc bitrate=128000",
|
||||
}
|
||||
}
|
||||
|
||||
func G722() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "g722",
|
||||
PayloadType: 9,
|
||||
Type: webrtc.RTPCodecTypeAudio,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeG722,
|
||||
ClockRate: 8000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html
|
||||
// gstreamer1.0-libav
|
||||
Pipeline: "avenc_g722",
|
||||
}
|
||||
}
|
||||
|
||||
func PCMU() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "pcmu",
|
||||
PayloadType: 0,
|
||||
Type: webrtc.RTPCodecTypeAudio,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMU,
|
||||
ClockRate: 8000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html
|
||||
// gstreamer1.0-plugins-good
|
||||
Pipeline: "audio/x-raw, rate=8000 ! mulawenc",
|
||||
}
|
||||
}
|
||||
|
||||
func PCMA() RTPCodec {
|
||||
return RTPCodec{
|
||||
Name: "pcma",
|
||||
PayloadType: 8,
|
||||
Type: webrtc.RTPCodecTypeAudio,
|
||||
Capability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypePCMA,
|
||||
ClockRate: 8000,
|
||||
Channels: 0,
|
||||
SDPFmtpLine: "",
|
||||
RTCPFeedback: []webrtc.RTCPFeedback{},
|
||||
},
|
||||
// https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html
|
||||
// gstreamer1.0-plugins-good
|
||||
Pipeline: "audio/x-raw, rate=8000 ! alawenc",
|
||||
}
|
||||
}
|
90
pkg/types/desktop.go
Normal file
90
pkg/types/desktop.go
Normal file
@ -0,0 +1,90 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
|
||||
type CursorImage struct {
|
||||
Width uint16
|
||||
Height uint16
|
||||
Xhot uint16
|
||||
Yhot uint16
|
||||
Serial uint64
|
||||
Image *image.RGBA
|
||||
}
|
||||
|
||||
type ScreenSize struct {
|
||||
Width int
|
||||
Height int
|
||||
Rate int16
|
||||
}
|
||||
|
||||
type ScreenConfiguration struct {
|
||||
Width int
|
||||
Height int
|
||||
Rates map[int]int16
|
||||
}
|
||||
|
||||
type KeyboardModifiers struct {
|
||||
NumLock *bool
|
||||
CapsLock *bool
|
||||
}
|
||||
|
||||
type KeyboardMap struct {
|
||||
Layout string
|
||||
Variant string
|
||||
}
|
||||
|
||||
type ClipboardText struct {
|
||||
Text string
|
||||
HTML string
|
||||
}
|
||||
|
||||
type DesktopManager interface {
|
||||
Start()
|
||||
Shutdown() error
|
||||
OnBeforeScreenSizeChange(listener func())
|
||||
OnAfterScreenSizeChange(listener func())
|
||||
|
||||
// xorg
|
||||
Move(x, y int)
|
||||
GetCursorPosition() (int, int)
|
||||
Scroll(x, y int)
|
||||
ButtonDown(code uint32) error
|
||||
KeyDown(code uint32) error
|
||||
ButtonUp(code uint32) error
|
||||
KeyUp(code uint32) error
|
||||
KeyPress(codes ...uint32) error
|
||||
ResetKeys()
|
||||
ScreenConfigurations() map[int]ScreenConfiguration
|
||||
SetScreenSize(ScreenSize) error
|
||||
GetScreenSize() *ScreenSize
|
||||
SetKeyboardMap(KeyboardMap) error
|
||||
GetKeyboardMap() (*KeyboardMap, error)
|
||||
SetKeyboardModifiers(mod KeyboardModifiers)
|
||||
GetKeyboardModifiers() KeyboardModifiers
|
||||
GetCursorImage() *CursorImage
|
||||
GetScreenshotImage() *image.RGBA
|
||||
|
||||
// xevent
|
||||
OnCursorChanged(listener func(serial uint64))
|
||||
OnClipboardUpdated(listener func())
|
||||
OnFileChooserDialogOpened(listener func())
|
||||
OnFileChooserDialogClosed(listener func())
|
||||
OnEventError(listener func(error_code uint8, message string, request_code uint8, minor_code uint8))
|
||||
|
||||
// clipboard
|
||||
ClipboardGetText() (*ClipboardText, error)
|
||||
ClipboardSetText(data ClipboardText) error
|
||||
ClipboardGetBinary(mime string) ([]byte, error)
|
||||
ClipboardSetBinary(mime string, data []byte) error
|
||||
ClipboardGetTargets() ([]string, error)
|
||||
|
||||
// drop
|
||||
DropFiles(x int, y int, files []string) bool
|
||||
|
||||
// filechooser
|
||||
HandleFileChooserDialog(uri string) error
|
||||
CloseFileChooserDialog()
|
||||
IsFileChooserDialogOpened() bool
|
||||
}
|
74
pkg/types/event/events.go
Normal file
74
pkg/types/event/events.go
Normal file
@ -0,0 +1,74 @@
|
||||
package event
|
||||
|
||||
const (
|
||||
SYSTEM_INIT = "system/init"
|
||||
SYSTEM_ADMIN = "system/admin"
|
||||
SYSTEM_LOGS = "system/logs"
|
||||
SYSTEM_DISCONNECT = "system/disconnect"
|
||||
)
|
||||
|
||||
const (
|
||||
SIGNAL_REQUEST = "signal/request"
|
||||
SIGNAL_RESTART = "signal/restart"
|
||||
SIGNAL_OFFER = "signal/offer"
|
||||
SIGNAL_ANSWER = "signal/answer"
|
||||
SIGNAL_PROVIDE = "signal/provide"
|
||||
SIGNAL_CANDIDATE = "signal/candidate"
|
||||
SIGNAL_VIDEO = "signal/video"
|
||||
SIGNAL_CLOSE = "signal/close"
|
||||
)
|
||||
|
||||
const (
|
||||
SESSION_CREATED = "session/created"
|
||||
SESSION_DELETED = "session/deleted"
|
||||
SESSION_PROFILE = "session/profile"
|
||||
SESSION_STATE = "session/state"
|
||||
SESSION_CURSORS = "session/cursors"
|
||||
)
|
||||
|
||||
const (
|
||||
CONTROL_HOST = "control/host"
|
||||
CONTROL_RELEASE = "control/release"
|
||||
CONTROL_REQUEST = "control/request"
|
||||
// mouse
|
||||
CONTROL_MOVE = "control/move" // TODO: New. (fallback)
|
||||
CONTROL_SCROLL = "control/scroll" // TODO: New. (fallback)
|
||||
// keyboard
|
||||
CONTROL_KEYPRESS = "control/keypress"
|
||||
CONTROL_KEYDOWN = "control/keydown"
|
||||
CONTROL_KEYUP = "control/keyup"
|
||||
// actions
|
||||
CONTROL_CUT = "control/cut"
|
||||
CONTROL_COPY = "control/copy"
|
||||
CONTROL_PASTE = "control/paste"
|
||||
CONTROL_SELECT_ALL = "control/select_all"
|
||||
)
|
||||
|
||||
const (
|
||||
SCREEN_UPDATED = "screen/updated"
|
||||
SCREEN_SET = "screen/set"
|
||||
)
|
||||
|
||||
const (
|
||||
CLIPBOARD_UPDATED = "clipboard/updated"
|
||||
CLIPBOARD_SET = "clipboard/set"
|
||||
)
|
||||
|
||||
const (
|
||||
KEYBOARD_MODIFIERS = "keyboard/modifiers"
|
||||
KEYBOARD_MAP = "keyboard/map"
|
||||
)
|
||||
|
||||
const (
|
||||
BORADCAST_STATUS = "broadcast/status"
|
||||
)
|
||||
|
||||
const (
|
||||
SEND_UNICAST = "send/unicast"
|
||||
SEND_BROADCAST = "send/broadcast"
|
||||
)
|
||||
|
||||
const (
|
||||
FILE_CHOOSER_DIALOG_OPENED = "file_chooser_dialog/opened"
|
||||
FILE_CHOOSER_DIALOG_CLOSED = "file_chooser_dialog/closed"
|
||||
)
|
28
pkg/types/http.go
Normal file
28
pkg/types/http.go
Normal file
@ -0,0 +1,28 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type RouterHandler func(w http.ResponseWriter, r *http.Request) error
|
||||
type MiddlewareHandler func(w http.ResponseWriter, r *http.Request) (context.Context, error)
|
||||
|
||||
type Router interface {
|
||||
Group(fn func(Router))
|
||||
Route(pattern string, fn func(Router))
|
||||
Get(pattern string, fn RouterHandler)
|
||||
Post(pattern string, fn RouterHandler)
|
||||
Put(pattern string, fn RouterHandler)
|
||||
Delete(pattern string, fn RouterHandler)
|
||||
With(fn MiddlewareHandler) Router
|
||||
WithBypass(fn func(next http.Handler) http.Handler) Router
|
||||
Use(fn MiddlewareHandler)
|
||||
UseBypass(fn func(next http.Handler) http.Handler)
|
||||
ServeHTTP(w http.ResponseWriter, req *http.Request)
|
||||
}
|
||||
|
||||
type HttpManager interface {
|
||||
Start()
|
||||
Shutdown() error
|
||||
}
|
43
pkg/types/member.go
Normal file
43
pkg/types/member.go
Normal file
@ -0,0 +1,43 @@
|
||||
package types
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrMemberAlreadyExists = errors.New("member already exists")
|
||||
ErrMemberDoesNotExist = errors.New("member does not exist")
|
||||
ErrMemberInvalidPassword = errors.New("invalid password")
|
||||
)
|
||||
|
||||
type MemberProfile struct {
|
||||
Name string `json:"name"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
CanLogin bool `json:"can_login"`
|
||||
CanConnect bool `json:"can_connect"`
|
||||
CanWatch bool `json:"can_watch"`
|
||||
CanHost bool `json:"can_host"`
|
||||
CanShareMedia bool `json:"can_share_media"`
|
||||
CanAccessClipboard bool `json:"can_access_clipboard"`
|
||||
SendsInactiveCursor bool `json:"sends_inactive_cursor"`
|
||||
CanSeeInactiveCursors bool `json:"can_see_inactive_cursors"`
|
||||
}
|
||||
|
||||
type MemberProvider interface {
|
||||
Connect() error
|
||||
Disconnect() error
|
||||
|
||||
Authenticate(username string, password string) (string, MemberProfile, error)
|
||||
|
||||
Insert(username string, password string, profile MemberProfile) (string, error)
|
||||
Select(id string) (MemberProfile, error)
|
||||
SelectAll(limit int, offset int) (map[string]MemberProfile, error)
|
||||
UpdateProfile(id string, profile MemberProfile) error
|
||||
UpdatePassword(id string, password string) error
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
type MemberManager interface {
|
||||
MemberProvider
|
||||
|
||||
Login(username string, password string) (Session, string, error)
|
||||
Logout(id string) error
|
||||
}
|
177
pkg/types/message/messages.go
Normal file
177
pkg/types/message/messages.go
Normal file
@ -0,0 +1,177 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"github.com/pion/webrtc/v3"
|
||||
|
||||
"gitlab.com/demodesk/neko/server/pkg/types"
|
||||
)
|
||||
|
||||
/////////////////////////////
|
||||
// System
|
||||
/////////////////////////////
|
||||
|
||||
type SystemWebRTC struct {
|
||||
Videos []string `json:"videos"`
|
||||
}
|
||||
|
||||
type SystemInit struct {
|
||||
SessionId string `json:"session_id"`
|
||||
ControlHost ControlHost `json:"control_host"`
|
||||
ScreenSize ScreenSize `json:"screen_size"`
|
||||
Sessions map[string]SessionData `json:"sessions"`
|
||||
ImplicitHosting bool `json:"implicit_hosting"`
|
||||
InactiveCursors bool `json:"inactive_cursors"`
|
||||
ScreencastEnabled bool `json:"screencast_enabled"`
|
||||
WebRTC SystemWebRTC `json:"webrtc"`
|
||||
}
|
||||
|
||||
type SystemAdmin struct {
|
||||
ScreenSizesList []ScreenSize `json:"screen_sizes_list"`
|
||||
BroadcastStatus BroadcastStatus `json:"broadcast_status"`
|
||||
}
|
||||
|
||||
type SystemLogs = []SystemLog
|
||||
|
||||
type SystemLog struct {
|
||||
Level string `json:"level"`
|
||||
Fields map[string]interface{} `json:"fields"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type SystemDisconnect struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Signal
|
||||
/////////////////////////////
|
||||
|
||||
type SignalProvide struct {
|
||||
SDP string `json:"sdp"`
|
||||
ICEServers []types.ICEServer `json:"iceservers"`
|
||||
Video string `json:"video"`
|
||||
}
|
||||
|
||||
type SignalCandidate struct {
|
||||
webrtc.ICECandidateInit
|
||||
}
|
||||
|
||||
type SignalDescription struct {
|
||||
SDP string `json:"sdp"`
|
||||
}
|
||||
|
||||
type SignalVideo struct {
|
||||
Video string `json:"video"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Session
|
||||
/////////////////////////////
|
||||
|
||||
type SessionID struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type MemberProfile struct {
|
||||
ID string `json:"id"`
|
||||
types.MemberProfile
|
||||
}
|
||||
|
||||
type SessionState struct {
|
||||
ID string `json:"id"`
|
||||
types.SessionState
|
||||
}
|
||||
|
||||
type SessionData struct {
|
||||
ID string `json:"id"`
|
||||
Profile types.MemberProfile `json:"profile"`
|
||||
State types.SessionState `json:"state"`
|
||||
}
|
||||
|
||||
type SessionCursors struct {
|
||||
ID string `json:"id"`
|
||||
Cursors []types.Cursor `json:"cursors"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Control
|
||||
/////////////////////////////
|
||||
|
||||
type ControlHost struct {
|
||||
HasHost bool `json:"has_host"`
|
||||
HostID string `json:"host_id,omitempty"`
|
||||
}
|
||||
|
||||
// TODO: New.
|
||||
type ControlMove struct {
|
||||
X uint16 `json:"x"`
|
||||
Y uint16 `json:"y"`
|
||||
}
|
||||
|
||||
// TODO: New.
|
||||
type ControlScroll struct {
|
||||
X int16 `json:"x"`
|
||||
Y int16 `json:"y"`
|
||||
}
|
||||
|
||||
type ControlKey struct {
|
||||
Keysym uint32 `json:"keysym"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Screen
|
||||
/////////////////////////////
|
||||
|
||||
type ScreenSize struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Rate int16 `json:"rate"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Clipboard
|
||||
/////////////////////////////
|
||||
|
||||
type ClipboardData struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Keyboard
|
||||
/////////////////////////////
|
||||
|
||||
type KeyboardMap struct {
|
||||
Layout string `json:"layout"`
|
||||
Variant string `json:"variant"`
|
||||
}
|
||||
|
||||
type KeyboardModifiers struct {
|
||||
CapsLock *bool `json:"capslock"`
|
||||
NumLock *bool `json:"numlock"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Broadcast
|
||||
/////////////////////////////
|
||||
|
||||
type BroadcastStatus struct {
|
||||
IsActive bool `json:"is_active"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Send (opaque comunication channel)
|
||||
/////////////////////////////
|
||||
|
||||
type SendUnicast struct {
|
||||
Sender string `json:"sender"`
|
||||
Receiver string `json:"receiver"`
|
||||
Subject string `json:"subject"`
|
||||
Body interface{} `json:"body"`
|
||||
}
|
||||
|
||||
type SendBroadcast struct {
|
||||
Sender string `json:"sender"`
|
||||
Subject string `json:"subject"`
|
||||
Body interface{} `json:"body"`
|
||||
}
|
81
pkg/types/session.go
Normal file
81
pkg/types/session.go
Normal file
@ -0,0 +1,81 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSessionNotFound = errors.New("session not found")
|
||||
ErrSessionAlreadyExists = errors.New("session already exists")
|
||||
ErrSessionAlreadyConnected = errors.New("session is already connected")
|
||||
ErrSessionLoginDisabled = errors.New("session login disabled")
|
||||
)
|
||||
|
||||
type Cursor struct {
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
}
|
||||
|
||||
type SessionState struct {
|
||||
IsConnected bool `json:"is_connected"`
|
||||
IsWatching bool `json:"is_watching"`
|
||||
}
|
||||
|
||||
type Session interface {
|
||||
ID() string
|
||||
Profile() MemberProfile
|
||||
State() SessionState
|
||||
IsHost() bool
|
||||
|
||||
// cursor
|
||||
SetCursor(cursor Cursor)
|
||||
|
||||
// websocket
|
||||
SetWebSocketPeer(websocketPeer WebSocketPeer)
|
||||
SetWebSocketConnected(websocketPeer WebSocketPeer, connected bool)
|
||||
GetWebSocketPeer() WebSocketPeer
|
||||
Send(event string, payload interface{})
|
||||
|
||||
// webrtc
|
||||
SetWebRTCPeer(webrtcPeer WebRTCPeer)
|
||||
SetWebRTCConnected(webrtcPeer WebRTCPeer, connected bool)
|
||||
GetWebRTCPeer() WebRTCPeer
|
||||
}
|
||||
|
||||
type SessionManager interface {
|
||||
Create(id string, profile MemberProfile) (Session, string, error)
|
||||
Update(id string, profile MemberProfile) error
|
||||
Delete(id string) error
|
||||
Get(id string) (Session, bool)
|
||||
GetByToken(token string) (Session, bool)
|
||||
List() []Session
|
||||
|
||||
SetHost(host Session)
|
||||
GetHost() Session
|
||||
ClearHost()
|
||||
|
||||
SetCursor(cursor Cursor, session Session)
|
||||
PopCursors() map[Session][]Cursor
|
||||
|
||||
Broadcast(event string, payload interface{}, exclude interface{})
|
||||
AdminBroadcast(event string, payload interface{}, exclude interface{})
|
||||
InactiveCursorsBroadcast(event string, payload interface{}, exclude interface{})
|
||||
|
||||
OnCreated(listener func(session Session))
|
||||
OnDeleted(listener func(session Session))
|
||||
OnConnected(listener func(session Session))
|
||||
OnDisconnected(listener func(session Session))
|
||||
OnProfileChanged(listener func(session Session))
|
||||
OnStateChanged(listener func(session Session))
|
||||
OnHostChanged(listener func(session Session))
|
||||
|
||||
ImplicitHosting() bool
|
||||
InactiveCursors() bool
|
||||
CookieEnabled() bool
|
||||
MercifulReconnect() bool
|
||||
|
||||
CookieSetToken(w http.ResponseWriter, token string)
|
||||
CookieClearToken(w http.ResponseWriter, r *http.Request)
|
||||
Authenticate(r *http.Request) (Session, error)
|
||||
}
|
42
pkg/types/webrtc.go
Normal file
42
pkg/types/webrtc.go
Normal file
@ -0,0 +1,42 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrWebRTCVideoNotFound = errors.New("webrtc video not found")
|
||||
ErrWebRTCDataChannelNotFound = errors.New("webrtc data channel not found")
|
||||
ErrWebRTCConnectionNotFound = errors.New("webrtc connection not found")
|
||||
)
|
||||
|
||||
type ICEServer struct {
|
||||
URLs []string `mapstructure:"urls" json:"urls"`
|
||||
Username string `mapstructure:"username" json:"username,omitempty"`
|
||||
Credential string `mapstructure:"credential" json:"credential,omitempty"`
|
||||
}
|
||||
|
||||
type WebRTCPeer interface {
|
||||
CreateOffer(ICERestart bool) (*webrtc.SessionDescription, error)
|
||||
CreateAnswer() (*webrtc.SessionDescription, error)
|
||||
SetOffer(sdp string) error
|
||||
SetAnswer(sdp string) error
|
||||
SetCandidate(candidate webrtc.ICECandidateInit) error
|
||||
|
||||
SetVideoID(videoID string) error
|
||||
SendCursorPosition(x, y int) error
|
||||
SendCursorImage(cur *CursorImage, img []byte) error
|
||||
|
||||
Destroy()
|
||||
}
|
||||
|
||||
type WebRTCManager interface {
|
||||
Start()
|
||||
Shutdown() error
|
||||
|
||||
ICEServers() []ICEServer
|
||||
|
||||
CreatePeer(session Session, videoID string) (*webrtc.SessionDescription, error)
|
||||
}
|
28
pkg/types/websocket.go
Normal file
28
pkg/types/websocket.go
Normal file
@ -0,0 +1,28 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebSocketMessage struct {
|
||||
Event string `json:"event"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
|
||||
type WebSocketHandler func(Session, WebSocketMessage) bool
|
||||
|
||||
type CheckOrigin func(r *http.Request) bool
|
||||
|
||||
type WebSocketPeer interface {
|
||||
Send(event string, payload interface{})
|
||||
Ping() error
|
||||
Destroy(reason string)
|
||||
}
|
||||
|
||||
type WebSocketManager interface {
|
||||
Start()
|
||||
Shutdown() error
|
||||
AddHandler(handler WebSocketHandler)
|
||||
Upgrade(checkOrigin CheckOrigin) RouterHandler
|
||||
}
|
Reference in New Issue
Block a user