format Go source code.

This commit is contained in:
Miroslav Šedivý 2021-02-14 14:40:17 +01:00
parent 732764991b
commit 45679f1b86
54 changed files with 445 additions and 447 deletions

View File

@ -5,8 +5,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"demodesk/neko" "demodesk/neko"
"demodesk/neko/modules"
"demodesk/neko/internal/config" "demodesk/neko/internal/config"
"demodesk/neko/modules"
) )
func init() { func init() {

View File

@ -3,8 +3,8 @@ package members
import ( import (
"net/http" "net/http"
"demodesk/neko/internal/utils"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils"
) )
type MemberDataPayload struct { type MemberDataPayload struct {
@ -29,11 +29,11 @@ func (h *MembersHandler) membersCreate(w http.ResponseWriter, r *http.Request) {
data := &MemberDataPayload{ data := &MemberDataPayload{
// default values // default values
MemberProfile: &types.MemberProfile{ MemberProfile: &types.MemberProfile{
IsAdmin: false, IsAdmin: false,
CanLogin: true, CanLogin: true,
CanConnect: true, CanConnect: true,
CanWatch: true, CanWatch: true,
CanHost: true, CanHost: true,
CanAccessClipboard: true, CanAccessClipboard: true,
}, },
} }

View File

@ -6,9 +6,9 @@ import (
"github.com/go-chi/chi" "github.com/go-chi/chi"
"demodesk/neko/internal/http/auth"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
"demodesk/neko/internal/http/auth"
) )
type key int type key int

View File

@ -3,14 +3,14 @@ package room
import ( import (
"net/http" "net/http"
"demodesk/neko/internal/utils"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message" "demodesk/neko/internal/types/message"
"demodesk/neko/internal/utils"
) )
type BroadcastStatusPayload struct { type BroadcastStatusPayload struct {
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
func (h *RoomHandler) broadcastStatus(w http.ResponseWriter, r *http.Request) { func (h *RoomHandler) broadcastStatus(w http.ResponseWriter, r *http.Request) {

View File

@ -6,8 +6,8 @@ import (
//"strings" //"strings"
"net/http" "net/http"
"demodesk/neko/internal/utils"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils"
) )
type ClipboardPayload struct { type ClipboardPayload struct {

View File

@ -5,15 +5,15 @@ import (
"github.com/go-chi/chi" "github.com/go-chi/chi"
"demodesk/neko/internal/http/auth"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message" "demodesk/neko/internal/types/message"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
"demodesk/neko/internal/http/auth"
) )
type ControlStatusPayload struct { type ControlStatusPayload struct {
HasHost bool `json:"has_host"` HasHost bool `json:"has_host"`
HostId string `json:"host_id,omitempty"` HostId string `json:"host_id,omitempty"`
} }
type ControlTargetPayload struct { type ControlTargetPayload struct {
@ -30,7 +30,7 @@ func (h *RoomHandler) controlStatus(w http.ResponseWriter, r *http.Request) {
} else { } else {
utils.HttpSuccess(w, ControlStatusPayload{ utils.HttpSuccess(w, ControlStatusPayload{
HasHost: true, HasHost: true,
HostId: host.ID(), HostId: host.ID(),
}) })
} }
} }

View File

@ -5,8 +5,8 @@ import (
"github.com/go-chi/chi" "github.com/go-chi/chi"
"demodesk/neko/internal/types"
"demodesk/neko/internal/http/auth" "demodesk/neko/internal/http/auth"
"demodesk/neko/internal/types"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
) )

View File

@ -3,8 +3,8 @@ package room
import ( import (
"net/http" "net/http"
"demodesk/neko/internal/utils"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils"
) )
type KeyboardMapData struct { type KeyboardMapData struct {
@ -24,11 +24,11 @@ func (h *RoomHandler) keyboardMapSet(w http.ResponseWriter, r *http.Request) {
} }
err := h.desktop.SetKeyboardMap(types.KeyboardMap{ err := h.desktop.SetKeyboardMap(types.KeyboardMap{
Layout: data.Layout, Layout: data.Layout,
Variant: data.Variant, Variant: data.Variant,
}) })
if err != nil{ if err != nil {
utils.HttpInternalServerError(w, "Unable to change keyboard map.") utils.HttpInternalServerError(w, "Unable to change keyboard map.")
return return
} }
@ -39,13 +39,13 @@ func (h *RoomHandler) keyboardMapSet(w http.ResponseWriter, r *http.Request) {
func (h *RoomHandler) keyboardMapGet(w http.ResponseWriter, r *http.Request) { func (h *RoomHandler) keyboardMapGet(w http.ResponseWriter, r *http.Request) {
data, err := h.desktop.GetKeyboardMap() data, err := h.desktop.GetKeyboardMap()
if err != nil{ if err != nil {
utils.HttpInternalServerError(w, "Unable to get keyboard map.") utils.HttpInternalServerError(w, "Unable to get keyboard map.")
return return
} }
utils.HttpSuccess(w, KeyboardMapData{ utils.HttpSuccess(w, KeyboardMapData{
Layout: data.Layout, Layout: data.Layout,
Variant: data.Variant, Variant: data.Variant,
}) })
} }
@ -57,7 +57,7 @@ func (h *RoomHandler) keyboardModifiersSet(w http.ResponseWriter, r *http.Reques
} }
h.desktop.SetKeyboardModifiers(types.KeyboardModifiers{ h.desktop.SetKeyboardModifiers(types.KeyboardModifiers{
NumLock: data.NumLock, NumLock: data.NumLock,
CapsLock: data.CapsLock, CapsLock: data.CapsLock,
}) })
utils.HttpSuccess(w) utils.HttpSuccess(w)
@ -67,7 +67,7 @@ func (h *RoomHandler) keyboardModifiersGet(w http.ResponseWriter, r *http.Reques
data := h.desktop.GetKeyboardModifiers() data := h.desktop.GetKeyboardModifiers()
utils.HttpSuccess(w, KeyboardModifiersData{ utils.HttpSuccess(w, KeyboardModifiersData{
NumLock: data.NumLock, NumLock: data.NumLock,
CapsLock: data.CapsLock, CapsLock: data.CapsLock,
}) })
} }

View File

@ -3,8 +3,8 @@ package room
import ( import (
"bytes" "bytes"
"image/jpeg" "image/jpeg"
"strconv"
"net/http" "net/http"
"strconv"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
@ -40,9 +40,9 @@ func (h *RoomHandler) screenConfigurationChange(w http.ResponseWriter, r *http.R
} }
if err := h.desktop.SetScreenSize(types.ScreenSize{ if err := h.desktop.SetScreenSize(types.ScreenSize{
Width: data.Width, Width: data.Width,
Height: data.Height, Height: data.Height,
Rate: data.Rate, Rate: data.Rate,
}); err != nil { }); err != nil {
utils.HttpUnprocessableEntity(w, err) utils.HttpUnprocessableEntity(w, err)
return return

View File

@ -1,12 +1,12 @@
package room package room
import ( import (
"os"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os"
"path" "path"
"strconv" "strconv"
"net/http"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
) )
@ -62,7 +62,7 @@ func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) {
defer srcFile.Close() defer srcFile.Close()
dstFile, err := os.OpenFile(path, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) dstFile, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
utils.HttpInternalServerError(w, err) utils.HttpInternalServerError(w, err)
return return
@ -125,7 +125,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
defer srcFile.Close() defer srcFile.Close()
dstFile, err := os.OpenFile(path, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) dstFile, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
utils.HttpInternalServerError(w, err) utils.HttpInternalServerError(w, err)
return return

View File

@ -7,10 +7,10 @@ import (
"demodesk/neko/internal/api/members" "demodesk/neko/internal/api/members"
"demodesk/neko/internal/api/room" "demodesk/neko/internal/api/room"
"demodesk/neko/internal/config"
"demodesk/neko/internal/http/auth" "demodesk/neko/internal/http/auth"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
"demodesk/neko/internal/config"
) )
type ApiManagerCtx struct { type ApiManagerCtx struct {

View File

@ -1,12 +1,12 @@
package api package api
import ( import (
"time"
"net/http" "net/http"
"time"
"demodesk/neko/internal/utils"
"demodesk/neko/internal/types"
"demodesk/neko/internal/http/auth" "demodesk/neko/internal/http/auth"
"demodesk/neko/internal/types"
"demodesk/neko/internal/utils"
) )
type SessionLoginPayload struct { type SessionLoginPayload struct {
@ -33,16 +33,16 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) {
} }
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "neko-id", Name: "neko-id",
Value: session.ID(), Value: session.ID(),
Expires: time.Now().Add(365 * 24 * time.Hour), Expires: time.Now().Add(365 * 24 * time.Hour),
HttpOnly: false, HttpOnly: false,
}) })
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "neko-secret", Name: "neko-secret",
Value: data.Secret, Value: data.Secret,
Expires: time.Now().Add(365 * 24 * time.Hour), Expires: time.Now().Add(365 * 24 * time.Hour),
HttpOnly: true, HttpOnly: true,
}) })
@ -55,16 +55,16 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) {
func (api *ApiManagerCtx) Logout(w http.ResponseWriter, r *http.Request) { func (api *ApiManagerCtx) Logout(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "neko-id", Name: "neko-id",
Value: "", Value: "",
Expires: time.Unix(0, 0), Expires: time.Unix(0, 0),
HttpOnly: false, HttpOnly: false,
}) })
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: "neko-secret", Name: "neko-secret",
Value: "", Value: "",
Expires: time.Unix(0, 0), Expires: time.Unix(0, 0),
HttpOnly: true, HttpOnly: true,
}) })

View File

@ -2,8 +2,8 @@ package capture
import ( import (
"fmt" "fmt"
"sync"
"strings" "strings"
"sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -12,20 +12,20 @@ import (
) )
type BroacastManagerCtx struct { type BroacastManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
mu sync.Mutex mu sync.Mutex
pipelineStr string pipelineStr string
pipeline *gst.Pipeline pipeline *gst.Pipeline
started bool started bool
url string url string
} }
func broadcastNew(pipelineStr string) *BroacastManagerCtx { func broadcastNew(pipelineStr string) *BroacastManagerCtx {
return &BroacastManagerCtx{ return &BroacastManagerCtx{
logger: log.With().Str("module", "capture").Str("submodule", "broadcast").Logger(), logger: log.With().Str("module", "capture").Str("submodule", "broadcast").Logger(),
pipelineStr: pipelineStr, pipelineStr: pipelineStr,
started: false, started: false,
url: "", url: "",
} }
} }

View File

@ -8,18 +8,18 @@ package gst
import "C" import "C"
import ( import (
"fmt" "fmt"
"time"
"sync" "sync"
"time"
"unsafe" "unsafe"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
) )
type Pipeline struct { type Pipeline struct {
Pipeline *C.GstElement Pipeline *C.GstElement
Sample chan types.Sample Sample chan types.Sample
Src string Src string
id int id int
} }
var pipelines = make(map[int]*Pipeline) var pipelines = make(map[int]*Pipeline)
@ -49,10 +49,10 @@ func CreatePipeline(pipelineStr string) (*Pipeline, error) {
} }
p := &Pipeline{ p := &Pipeline{
Pipeline: gstPipeline, Pipeline: gstPipeline,
Sample: make(chan types.Sample), Sample: make(chan types.Sample),
Src: pipelineStr, Src: pipelineStr,
id: len(pipelines), id: len(pipelines),
} }
pipelines[p.id] = p pipelines[p.id] = p
@ -96,7 +96,7 @@ func goHandlePipelineBuffer(buffer unsafe.Pointer, bufferLen C.int, duration C.i
if ok { if ok {
pipeline.Sample <- types.Sample{ pipeline.Sample <- types.Sample{
Data: C.GoBytes(buffer, bufferLen), Data: C.GoBytes(buffer, bufferLen),
Duration: time.Duration(duration), Duration: time.Duration(duration),
} }
} else { } else {

View File

@ -7,20 +7,20 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/config"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/codec" "demodesk/neko/internal/types/codec"
"demodesk/neko/internal/config"
) )
type CaptureManagerCtx struct { type CaptureManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
desktop types.DesktopManager desktop types.DesktopManager
streaming bool streaming bool
broadcast *BroacastManagerCtx broadcast *BroacastManagerCtx
screencast *ScreencastManagerCtx screencast *ScreencastManagerCtx
audio *StreamManagerCtx audio *StreamManagerCtx
videos map[string]*StreamManagerCtx videos map[string]*StreamManagerCtx
videoIDs []string videoIDs []string
} }
func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCtx { func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCtx {
@ -29,124 +29,124 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
broadcastPipeline := config.BroadcastPipeline broadcastPipeline := config.BroadcastPipeline
if broadcastPipeline == "" { if broadcastPipeline == "" {
broadcastPipeline = fmt.Sprintf( broadcastPipeline = fmt.Sprintf(
"flvmux name=mux ! rtmpsink location='{url} live=1' " + "flvmux name=mux ! rtmpsink location='{url} live=1' "+
"pulsesrc device=%s " + "pulsesrc device=%s "+
"! audio/x-raw,channels=2 " + "! audio/x-raw,channels=2 "+
"! audioconvert " + "! audioconvert "+
"! queue " + "! queue "+
"! voaacenc " + "! voaacenc "+
"! mux. " + "! mux. "+
"ximagesrc display-name=%s show-pointer=true use-damage=false " + "ximagesrc display-name=%s show-pointer=true use-damage=false "+
"! video/x-raw " + "! video/x-raw "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true byte-stream=true tune=zerolatency speed-preset=veryfast " + "! x264enc threads=4 bitrate=4096 key-int-max=15 byte-stream=true byte-stream=true tune=zerolatency speed-preset=veryfast "+
"! mux.", config.Device, config.Display, "! mux.", config.Device, config.Display,
) )
} }
screencastPipeline := config.ScreencastPipeline screencastPipeline := config.ScreencastPipeline
if screencastPipeline == "" { if screencastPipeline == "" {
screencastPipeline = fmt.Sprintf( screencastPipeline = fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=true use-damage=false " + "ximagesrc display-name=%s show-pointer=true use-damage=false "+
"! video/x-raw,framerate=%s " + "! video/x-raw,framerate=%s "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! jpegenc quality=%s " + "! jpegenc quality=%s "+
"! appsink name=appsink", config.Display, config.ScreencastRate, config.ScreencastQuality, "! appsink name=appsink", config.Display, config.ScreencastRate, config.ScreencastQuality,
) )
} }
return &CaptureManagerCtx{ return &CaptureManagerCtx{
logger: logger, logger: logger,
desktop: desktop, desktop: desktop,
streaming: false, streaming: false,
broadcast: broadcastNew(broadcastPipeline), broadcast: broadcastNew(broadcastPipeline),
screencast: screencastNew(config.Screencast, screencastPipeline), screencast: screencastNew(config.Screencast, screencastPipeline),
audio: streamNew(config.AudioCodec, func() string { audio: streamNew(config.AudioCodec, func() string {
if config.AudioPipeline != "" { if config.AudioPipeline != "" {
return config.AudioPipeline return config.AudioPipeline
} }
return fmt.Sprintf( return fmt.Sprintf(
"pulsesrc device=%s " + "pulsesrc device=%s "+
"! audio/x-raw,channels=2 " + "! audio/x-raw,channels=2 "+
"! audioconvert " + "! audioconvert "+
"! queue " + "! queue "+
"! %s " + "! %s "+
"! appsink name=appsink", config.Device, config.AudioCodec.Pipeline, "! appsink name=appsink", config.Device, config.AudioCodec.Pipeline,
) )
}), }),
videos: map[string]*StreamManagerCtx{ videos: map[string]*StreamManagerCtx{
"hd": streamNew(codec.VP8(), func() string { "hd": streamNew(codec.VP8(), func() string {
screen := desktop.GetScreenSize() screen := desktop.GetScreenSize()
bitrate := screen.Width * screen.Height * 12 bitrate := screen.Width * screen.Height * 12
return fmt.Sprintf( return fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=false use-damage=false " + "ximagesrc display-name=%s show-pointer=false use-damage=false "+
"! video/x-raw,framerate=25/1 " + "! video/x-raw,framerate=25/1 "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=6 max-quantizer=12 " + "! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=6 max-quantizer=12 "+
"! appsink name=appsink", config.Display, bitrate, "! appsink name=appsink", config.Display, bitrate,
) )
}), }),
"hq": streamNew(codec.VP8(), func() string { "hq": streamNew(codec.VP8(), func() string {
screen := desktop.GetScreenSize() screen := desktop.GetScreenSize()
width := int(math.Ceil(float64(screen.Width) / 6) * 5) width := int(math.Ceil(float64(screen.Width)/6) * 5)
height := int(math.Ceil(float64(screen.Height) / 6) * 5) height := int(math.Ceil(float64(screen.Height)/6) * 5)
bitrate := width * height * 12 bitrate := width * height * 12
return fmt.Sprintf( return fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=false use-damage=false " + "ximagesrc display-name=%s show-pointer=false use-damage=false "+
"! video/x-raw,framerate=25/1 " + "! video/x-raw,framerate=25/1 "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! videoscale " + "! videoscale "+
"! video/x-raw,width=%d,height=%d " + "! video/x-raw,width=%d,height=%d "+
"! queue " + "! queue "+
"! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=6 max-quantizer=12 " + "! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=6 max-quantizer=12 "+
"! appsink name=appsink", config.Display, width, height, bitrate, "! appsink name=appsink", config.Display, width, height, bitrate,
) )
}), }),
"mq": streamNew(codec.VP8(), func() string { "mq": streamNew(codec.VP8(), func() string {
screen := desktop.GetScreenSize() screen := desktop.GetScreenSize()
width := int(math.Ceil(float64(screen.Width) / 6) * 4) width := int(math.Ceil(float64(screen.Width)/6) * 4)
height := int(math.Ceil(float64(screen.Height) / 6) * 4) height := int(math.Ceil(float64(screen.Height)/6) * 4)
bitrate := width * height * 8 bitrate := width * height * 8
return fmt.Sprintf( return fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=false use-damage=false " + "ximagesrc display-name=%s show-pointer=false use-damage=false "+
"! video/x-raw,framerate=125/10 " + "! video/x-raw,framerate=125/10 "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! videoscale " + "! videoscale "+
"! video/x-raw,width=%d,height=%d " + "! video/x-raw,width=%d,height=%d "+
"! queue " + "! queue "+
"! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=12 max-quantizer=24 " + "! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=12 max-quantizer=24 "+
"! appsink name=appsink", config.Display, width, height, bitrate, "! appsink name=appsink", config.Display, width, height, bitrate,
) )
}), }),
"lq": streamNew(codec.VP8(), func() string { "lq": streamNew(codec.VP8(), func() string {
screen := desktop.GetScreenSize() screen := desktop.GetScreenSize()
width := int(math.Ceil(float64(screen.Width) / 6) * 3) width := int(math.Ceil(float64(screen.Width)/6) * 3)
height := int(math.Ceil(float64(screen.Height) / 6) * 3) height := int(math.Ceil(float64(screen.Height)/6) * 3)
bitrate := width * height * 4 bitrate := width * height * 4
return fmt.Sprintf( return fmt.Sprintf(
"ximagesrc display-name=%s show-pointer=false use-damage=false " + "ximagesrc display-name=%s show-pointer=false use-damage=false "+
"! video/x-raw,framerate=125/10 " + "! video/x-raw,framerate=125/10 "+
"! videoconvert " + "! videoconvert "+
"! queue " + "! queue "+
"! videoscale " + "! videoscale "+
"! video/x-raw,width=%d,height=%d " + "! video/x-raw,width=%d,height=%d "+
"! queue " + "! queue "+
"! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=12 max-quantizer=24 " + "! vp8enc target-bitrate=%d cpu-used=16 threads=4 deadline=100000 error-resilient=partitions keyframe-max-dist=15 auto-alt-ref=true min-quantizer=12 max-quantizer=24 "+
"! appsink name=appsink", config.Display, width, height, bitrate, "! appsink name=appsink", config.Display, width, height, bitrate,
) )
}), }),
}, },
videoIDs: []string{ "hd", "hq", "mq", "lq" }, videoIDs: []string{"hd", "hq", "mq", "lq"},
} }
} }

View File

@ -2,41 +2,41 @@ package capture
import ( import (
"fmt" "fmt"
"time"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/types"
"demodesk/neko/internal/capture/gst" "demodesk/neko/internal/capture/gst"
"demodesk/neko/internal/types"
) )
type ScreencastManagerCtx struct { type ScreencastManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
mu sync.Mutex mu sync.Mutex
pipelineStr string pipelineStr string
pipeline *gst.Pipeline pipeline *gst.Pipeline
enabled bool enabled bool
started bool started bool
emitStop chan bool emitStop chan bool
emitUpdate chan bool emitUpdate chan bool
expired int32 expired int32
sample chan types.Sample sample chan types.Sample
image types.Sample image types.Sample
} }
const screencastTimeout = 5 * time.Second const screencastTimeout = 5 * time.Second
func screencastNew(enabled bool, pipelineStr string) *ScreencastManagerCtx { func screencastNew(enabled bool, pipelineStr string) *ScreencastManagerCtx {
manager := &ScreencastManagerCtx{ manager := &ScreencastManagerCtx{
logger: log.With().Str("module", "capture").Str("submodule", "screencast").Logger(), logger: log.With().Str("module", "capture").Str("submodule", "screencast").Logger(),
pipelineStr: pipelineStr, pipelineStr: pipelineStr,
enabled: enabled, enabled: enabled,
started: false, started: false,
emitStop: make(chan bool), emitStop: make(chan bool),
emitUpdate: make(chan bool), emitUpdate: make(chan bool),
} }
go func() { go func() {
@ -146,7 +146,7 @@ func (manager *ScreencastManagerCtx) createPipeline() error {
manager.pipeline.Start() manager.pipeline.Start()
manager.sample = manager.pipeline.Sample manager.sample = manager.pipeline.Sample
manager.emitUpdate <-true manager.emitUpdate <- true
return nil return nil
} }

View File

@ -2,40 +2,40 @@ package capture
import ( import (
"fmt" "fmt"
"sync"
"reflect" "reflect"
"sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/capture/gst"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/codec" "demodesk/neko/internal/types/codec"
"demodesk/neko/internal/capture/gst"
) )
type StreamManagerCtx struct { type StreamManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
mu sync.Mutex mu sync.Mutex
codec codec.RTPCodec codec codec.RTPCodec
pipelineStr func() string pipelineStr func() string
pipeline *gst.Pipeline pipeline *gst.Pipeline
sample chan types.Sample sample chan types.Sample
listeners map[uintptr]*func(sample types.Sample) listeners map[uintptr]*func(sample types.Sample)
emitMu sync.Mutex emitMu sync.Mutex
emitUpdate chan bool emitUpdate chan bool
emitStop chan bool emitStop chan bool
started bool started bool
} }
func streamNew(codec codec.RTPCodec, pipelineStr func() string) *StreamManagerCtx { func streamNew(codec codec.RTPCodec, pipelineStr func() string) *StreamManagerCtx {
manager := &StreamManagerCtx{ manager := &StreamManagerCtx{
logger: log.With().Str("module", "capture").Str("submodule", "stream").Logger(), logger: log.With().Str("module", "capture").Str("submodule", "stream").Logger(),
codec: codec, codec: codec,
pipelineStr: pipelineStr, pipelineStr: pipelineStr,
listeners: map[uintptr]*func(sample types.Sample){}, listeners: map[uintptr]*func(sample types.Sample){},
emitUpdate: make(chan bool), emitUpdate: make(chan bool),
emitStop: make(chan bool), emitStop: make(chan bool),
started: false, started: false,
} }
go func() { go func() {
@ -146,7 +146,7 @@ func (manager *StreamManagerCtx) createPipeline() error {
manager.pipeline.Start() manager.pipeline.Start()
manager.sample = manager.pipeline.Sample manager.sample = manager.pipeline.Sample
manager.emitUpdate <-true manager.emitUpdate <- true
return nil return nil
} }

View File

@ -8,15 +8,15 @@ import (
) )
type Capture struct { type Capture struct {
Device string Device string
AudioCodec codec.RTPCodec AudioCodec codec.RTPCodec
AudioPipeline string AudioPipeline string
Display string Display string
//VideoCodec codec.RTPCodec //VideoCodec codec.RTPCodec
//VideoPipeline string //VideoPipeline string
BroadcastPipeline string BroadcastPipeline string
Screencast bool Screencast bool
ScreencastRate string ScreencastRate string

View File

@ -1,8 +1,8 @@
package desktop package desktop
import ( import (
"fmt"
"bytes" "bytes"
"fmt"
"os/exec" "os/exec"
"strings" "strings"
@ -58,7 +58,7 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr
stdin, err := cmd.StdinPipe() stdin, err := cmd.StdinPipe()
if err != nil { if err != nil {
return err return err
} }

View File

@ -8,7 +8,7 @@ import (
const ( const (
DROP_MOVE_REPEAT = 4 DROP_MOVE_REPEAT = 4
DROP_DELAY = 100 * time.Millisecond DROP_DELAY = 100 * time.Millisecond
) )
func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool { func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool {
@ -49,10 +49,10 @@ func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool {
go drop.OpenWindow(files) go drop.OpenWindow(files)
select { select {
case succeeded := <- finished: case succeeded := <-finished:
return succeeded return succeeded
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
drop.CloseWindow(); drop.CloseWindow()
return false return false
} }
} }

View File

@ -25,7 +25,7 @@ func OpenWindow(files []string) {
defer mu.Unlock() defer mu.Unlock()
size := C.int(len(files)) size := C.int(len(files))
urisUnsafe := C.dragUrisMake(size); urisUnsafe := C.dragUrisMake(size)
defer C.dragUrisFree(urisUnsafe, size) defer C.dragUrisFree(urisUnsafe, size)
for i, file := range files { for i, file := range files {

View File

@ -2,8 +2,8 @@ package desktop
import ( import (
"fmt" "fmt"
"time"
"os/exec" "os/exec"
"time"
) )
const ( const (
@ -19,19 +19,19 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
// TODO: Use native API. // TODO: Use native API.
err1 := exec.Command( err1 := exec.Command(
"xdotool", "xdotool",
"search", "--name", FILE_CHOOSER_DIALOG_NAME, "windowfocus", "search", "--name", FILE_CHOOSER_DIALOG_NAME, "windowfocus",
"sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP, "sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP,
"key", "--clearmodifiers", "ctrl+l", "key", "--clearmodifiers", "ctrl+l",
"type", "--args", "1", uri + "//", "type", "--args", "1", uri+"//",
"sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP, "sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP,
"key", "Delete", // remove autocomplete results "key", "Delete", // remove autocomplete results
"sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP, "sleep", FILE_CHOOSER_DIALOG_SHORT_SLEEP,
"key", "Return", "key", "Return",
"sleep", FILE_CHOOSER_DIALOG_LONG_SLEEP, "sleep", FILE_CHOOSER_DIALOG_LONG_SLEEP,
"key", "Down", "key", "Down",
"key", "--clearmodifiers", "ctrl+a", "key", "--clearmodifiers", "ctrl+a",
"key", "Return", "key", "Return",
"sleep", FILE_CHOOSER_DIALOG_LONG_SLEEP, "sleep", FILE_CHOOSER_DIALOG_LONG_SLEEP,
).Run() ).Run()
if err1 != nil { if err1 != nil {
@ -41,7 +41,7 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
// TODO: Use native API. // TODO: Use native API.
err2 := exec.Command( err2 := exec.Command(
"xdotool", "xdotool",
"search", "--name", FILE_CHOOSER_DIALOG_NAME, "search", "--name", FILE_CHOOSER_DIALOG_NAME,
).Run() ).Run()
// if last command didn't return error, consider dialog as still open // if last command didn't return error, consider dialog as still open
@ -61,7 +61,7 @@ func (manager *DesktopManagerCtx) CloseFileChooserDialog() {
// TODO: Use native API. // TODO: Use native API.
err := exec.Command( err := exec.Command(
"xdotool", "xdotool",
"search", "--name", FILE_CHOOSER_DIALOG_NAME, "windowfocus", "search", "--name", FILE_CHOOSER_DIALOG_NAME, "windowfocus",
).Run() ).Run()
if err != nil { if err != nil {
@ -92,7 +92,7 @@ func (manager *DesktopManagerCtx) IsFileChooserDialogOpened() bool {
// TODO: Use native API. // TODO: Use native API.
err := exec.Command( err := exec.Command(
"xdotool", "xdotool",
"search", "--name", FILE_CHOOSER_DIALOG_NAME, "search", "--name", FILE_CHOOSER_DIALOG_NAME,
).Run() ).Run()
return err == nil return err == nil

View File

@ -2,37 +2,37 @@ package desktop
import ( import (
"fmt" "fmt"
"time"
"sync" "sync"
"time"
"github.com/kataras/go-events" "github.com/kataras/go-events"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/config" "demodesk/neko/internal/config"
"demodesk/neko/internal/desktop/xorg"
"demodesk/neko/internal/desktop/xevent" "demodesk/neko/internal/desktop/xevent"
"demodesk/neko/internal/desktop/xorg"
) )
var mu = sync.Mutex{} var mu = sync.Mutex{}
type DesktopManagerCtx struct { type DesktopManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
cleanup *time.Ticker cleanup *time.Ticker
shutdown chan bool shutdown chan bool
emmiter events.EventEmmiter emmiter events.EventEmmiter
display string display string
config *config.Desktop config *config.Desktop
} }
func New(display string, config *config.Desktop) *DesktopManagerCtx { func New(display string, config *config.Desktop) *DesktopManagerCtx {
return &DesktopManagerCtx{ return &DesktopManagerCtx{
logger: log.With().Str("module", "desktop").Logger(), logger: log.With().Str("module", "desktop").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(),
display: display, display: display,
config: config, config: config,
} }
} }

View File

@ -8,9 +8,9 @@ package xevent
import "C" import "C"
import ( import (
"strings"
"time" "time"
"unsafe" "unsafe"
"strings"
"github.com/kataras/go-events" "github.com/kataras/go-events"
) )

View File

@ -2,11 +2,11 @@ package desktop
import ( import (
"image" "image"
"regexp"
"os/exec" "os/exec"
"regexp"
"demodesk/neko/internal/types"
"demodesk/neko/internal/desktop/xorg" "demodesk/neko/internal/desktop/xorg"
"demodesk/neko/internal/types"
) )
// TODO: Refactor. // TODO: Refactor.
@ -119,7 +119,7 @@ func (manager *DesktopManagerCtx) GetKeyboardModifiers() types.KeyboardModifiers
CapsLock := (modifiers & xorg.KBD_CAPS_LOCK) != 0 CapsLock := (modifiers & xorg.KBD_CAPS_LOCK) != 0
return types.KeyboardModifiers{ return types.KeyboardModifiers{
NumLock: &NumLock, NumLock: &NumLock,
CapsLock: &CapsLock, CapsLock: &CapsLock,
} }
} }

View File

@ -236,13 +236,13 @@ func GetCursorImage() *types.CursorImage {
height := uint16(cur.height) height := uint16(cur.height)
return &types.CursorImage{ return &types.CursorImage{
Width: width, Width: width,
Height: height, Height: height,
Xhot: uint16(cur.xhot), Xhot: uint16(cur.xhot),
Yhot: uint16(cur.yhot), Yhot: uint16(cur.yhot),
Serial: uint64(cur.cursor_serial), Serial: uint64(cur.cursor_serial),
// Xlib stores 32-bit data in longs, even if longs are 64-bits long. // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
Pixels: C.GoBytes(unsafe.Pointer(cur.pixels), C.int(width * height * 8)), Pixels: C.GoBytes(unsafe.Pointer(cur.pixels), C.int(width*height*8)),
} }
} }
@ -252,7 +252,7 @@ func GetScreenshotImage() *image.RGBA {
var w, h C.int var w, h C.int
pixelsUnsafe := C.XGetScreenshot(&w, &h) pixelsUnsafe := C.XGetScreenshot(&w, &h)
pixels := C.GoBytes(unsafe.Pointer(pixelsUnsafe), w * h * 3) pixels := C.GoBytes(unsafe.Pointer(pixelsUnsafe), w*h*3)
defer C.free(unsafe.Pointer(pixelsUnsafe)) defer C.free(unsafe.Pointer(pixelsUnsafe))
width := int(w) width := int(w)
@ -288,7 +288,7 @@ func goSetScreenRates(index C.int, rate_index C.int, rateC C.short) {
rate := int16(rateC) rate := int16(rateC)
// filter out all irrelevant rates // filter out all irrelevant rates
if rate > 60 || (rate > 30 && rate % 10 != 0){ if rate > 60 || (rate > 30 && rate%10 != 0) {
return return
} }

View File

@ -11,8 +11,8 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/types"
"demodesk/neko/internal/config" "demodesk/neko/internal/config"
"demodesk/neko/internal/types"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
) )
@ -29,7 +29,7 @@ func New(WebSocketManager types.WebSocketManager, ApiManager types.ApiManager, c
router := chi.NewRouter() router := chi.NewRouter()
router.Use(middleware.Recoverer) // Recover from panics without crashing server router.Use(middleware.Recoverer) // Recover from panics without crashing server
router.Use(cors.Handler(cors.Options{ router.Use(cors.Handler(cors.Options{
AllowOriginFunc: func(r *http.Request, origin string) bool { AllowOriginFunc: func(r *http.Request, origin string) bool {
return conf.AllowOrigin(origin) return conf.AllowOrigin(origin)
}, },
AllowedMethods: []string{"GET", "POST", "DELETE", "OPTIONS"}, AllowedMethods: []string{"GET", "POST", "DELETE", "OPTIONS"},
@ -39,7 +39,7 @@ func New(WebSocketManager types.WebSocketManager, ApiManager types.ApiManager, c
MaxAge: 300, // Maximum value not ignored by any of major browsers MaxAge: 300, // Maximum value not ignored by any of major browsers
})) }))
router.Use(middleware.RequestID) // Create a request ID for each request router.Use(middleware.RequestID) // Create a request ID for each request
router.Use(Logger) // Log API request calls using custom logger function router.Use(Logger) // Log API request calls using custom logger function
router.Route("/api", ApiManager.Route) router.Route("/api", ApiManager.Route)

View File

@ -8,7 +8,7 @@ func New() types.MembersDatabase {
return &MembersDatabaseCtx{} return &MembersDatabaseCtx{}
} }
type MembersDatabaseCtx struct {} type MembersDatabaseCtx struct{}
func (manager *MembersDatabaseCtx) Connect() error { func (manager *MembersDatabaseCtx) Connect() error {
return nil return nil

View File

@ -2,9 +2,9 @@ package file
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"fmt"
"sync" "sync"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"

View File

@ -1,11 +1,11 @@
package database package database
import ( import (
"demodesk/neko/internal/config"
"demodesk/neko/internal/session/database/dummy" "demodesk/neko/internal/session/database/dummy"
"demodesk/neko/internal/session/database/file" "demodesk/neko/internal/session/database/file"
"demodesk/neko/internal/session/database/object" "demodesk/neko/internal/session/database/object"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/config"
) )
func New(config *config.Session) types.MembersDatabase { func New(config *config.Session) types.MembersDatabase {

View File

@ -8,9 +8,9 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/config"
"demodesk/neko/internal/session/database" "demodesk/neko/internal/session/database"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/config"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
) )
@ -72,25 +72,25 @@ func (manager *SessionManagerCtx) Connect() error {
// create default admin account at startup // create default admin account at startup
_ = manager.add("admin", types.MemberProfile{ _ = manager.add("admin", types.MemberProfile{
Secret: manager.config.AdminPassword, Secret: manager.config.AdminPassword,
Name: "Administrator", Name: "Administrator",
IsAdmin: true, IsAdmin: true,
CanLogin: true, CanLogin: true,
CanConnect: true, CanConnect: true,
CanWatch: true, CanWatch: true,
CanHost: true, CanHost: true,
CanAccessClipboard: true, CanAccessClipboard: true,
}) })
// create default user account at startup // create default user account at startup
_ = manager.add("user", types.MemberProfile{ _ = manager.add("user", types.MemberProfile{
Secret: manager.config.Password, Secret: manager.config.Password,
Name: "User", Name: "User",
IsAdmin: false, IsAdmin: false,
CanLogin: true, CanLogin: true,
CanConnect: true, CanConnect: true,
CanWatch: true, CanWatch: true,
CanHost: true, CanHost: true,
CanAccessClipboard: true, CanAccessClipboard: true,
}) })

View File

@ -19,14 +19,14 @@ func (codec *RTPCodec) Register(engine *webrtc.MediaEngine) error {
func VP8() RTPCodec { func VP8() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "vp8", Name: "vp8",
PayloadType: 96, PayloadType: 96,
Type: webrtc.RTPCodecTypeVideo, Type: webrtc.RTPCodecTypeVideo,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP8, MimeType: webrtc.MimeTypeVP8,
ClockRate: 90000, ClockRate: 90000,
Channels: 0, Channels: 0,
SDPFmtpLine: "", SDPFmtpLine: "",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html // https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html
@ -38,14 +38,14 @@ func VP8() RTPCodec {
// TODO: Profile ID. // TODO: Profile ID.
func VP9() RTPCodec { func VP9() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "vp9", Name: "vp9",
PayloadType: 98, PayloadType: 98,
Type: webrtc.RTPCodecTypeVideo, Type: webrtc.RTPCodecTypeVideo,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP9, MimeType: webrtc.MimeTypeVP9,
ClockRate: 90000, ClockRate: 90000,
Channels: 0, Channels: 0,
SDPFmtpLine: "profile-id=0", SDPFmtpLine: "profile-id=0",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html // https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html
@ -57,14 +57,14 @@ func VP9() RTPCodec {
// TODO: Profile ID. // TODO: Profile ID.
func H264() RTPCodec { func H264() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "h264", Name: "h264",
PayloadType: 102, PayloadType: 102,
Type: webrtc.RTPCodecTypeVideo, Type: webrtc.RTPCodecTypeVideo,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeH264, MimeType: webrtc.MimeTypeH264,
ClockRate: 90000, ClockRate: 90000,
Channels: 0, Channels: 0,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/x264/index.html // https://gstreamer.freedesktop.org/documentation/x264/index.html
@ -78,14 +78,14 @@ func H264() RTPCodec {
func Opus() RTPCodec { func Opus() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "opus", Name: "opus",
PayloadType: 111, PayloadType: 111,
Type: webrtc.RTPCodecTypeAudio, Type: webrtc.RTPCodecTypeAudio,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeOpus, MimeType: webrtc.MimeTypeOpus,
ClockRate: 48000, ClockRate: 48000,
Channels: 2, Channels: 2,
SDPFmtpLine: "", SDPFmtpLine: "",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/opus/opusenc.html // https://gstreamer.freedesktop.org/documentation/opus/opusenc.html
@ -96,14 +96,14 @@ func Opus() RTPCodec {
func G722() RTPCodec { func G722() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "g722", Name: "g722",
PayloadType: 9, PayloadType: 9,
Type: webrtc.RTPCodecTypeAudio, Type: webrtc.RTPCodecTypeAudio,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeG722, MimeType: webrtc.MimeTypeG722,
ClockRate: 8000, ClockRate: 8000,
Channels: 0, Channels: 0,
SDPFmtpLine: "", SDPFmtpLine: "",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html // https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html
@ -114,14 +114,14 @@ func G722() RTPCodec {
func PCMU() RTPCodec { func PCMU() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "pcmu", Name: "pcmu",
PayloadType: 0, PayloadType: 0,
Type: webrtc.RTPCodecTypeAudio, Type: webrtc.RTPCodecTypeAudio,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypePCMU, MimeType: webrtc.MimeTypePCMU,
ClockRate: 8000, ClockRate: 8000,
Channels: 0, Channels: 0,
SDPFmtpLine: "", SDPFmtpLine: "",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html // https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html
@ -132,14 +132,14 @@ func PCMU() RTPCodec {
func PCMA() RTPCodec { func PCMA() RTPCodec {
return RTPCodec{ return RTPCodec{
Name: "pcma", Name: "pcma",
PayloadType: 8, PayloadType: 8,
Type: webrtc.RTPCodecTypeAudio, Type: webrtc.RTPCodecTypeAudio,
Capability: webrtc.RTPCodecCapability{ Capability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypePCMA, MimeType: webrtc.MimeTypePCMA,
ClockRate: 8000, ClockRate: 8000,
Channels: 0, Channels: 0,
SDPFmtpLine: "", SDPFmtpLine: "",
RTCPFeedback: nil, RTCPFeedback: nil,
}, },
// https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html // https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html

View File

@ -15,20 +15,20 @@ const (
) )
const ( const (
MEMBER_CREATED = "member/created" MEMBER_CREATED = "member/created"
MEMBER_DELETED = "member/deleted" MEMBER_DELETED = "member/deleted"
MEMBER_PROFILE = "member/profile" MEMBER_PROFILE = "member/profile"
MEMBER_STATE = "member/state" MEMBER_STATE = "member/state"
) )
const ( const (
CONTROL_HOST = "control/host" CONTROL_HOST = "control/host"
CONTROL_RELEASE = "control/release" CONTROL_RELEASE = "control/release"
CONTROL_REQUEST = "control/request" CONTROL_REQUEST = "control/request"
CONTROL_MOVE = "control/move" // TODO: New. (fallback) CONTROL_MOVE = "control/move" // TODO: New. (fallback)
CONTROL_SCROLL = "control/scroll" // TODO: New. (fallback) CONTROL_SCROLL = "control/scroll" // TODO: New. (fallback)
CONTROL_KEYDOWN = "control/keydown" // TODO: New. (fallback) CONTROL_KEYDOWN = "control/keydown" // TODO: New. (fallback)
CONTROL_KEYUP = "control/keyup" // TODO: New. (fallback) CONTROL_KEYUP = "control/keyup" // TODO: New. (fallback)
) )
const ( const (

View File

@ -25,9 +25,9 @@ type SystemInit struct {
} }
type SystemAdmin struct { type SystemAdmin struct {
Event string `json:"event,omitempty"` Event string `json:"event,omitempty"`
ScreenSizesList []ScreenSize `json:"screen_sizes_list"` ScreenSizesList []ScreenSize `json:"screen_sizes_list"`
BroadcastStatus BroadcastStatus `json:"broadcast_status"` BroadcastStatus BroadcastStatus `json:"broadcast_status"`
} }
type SystemDisconnect struct { type SystemDisconnect struct {
@ -96,9 +96,9 @@ type MemberData struct {
///////////////////////////// /////////////////////////////
type ControlHost struct { type ControlHost struct {
Event string `json:"event,omitempty"` Event string `json:"event,omitempty"`
HasHost bool `json:"has_host"` HasHost bool `json:"has_host"`
HostID string `json:"host_id,omitempty"` HostID string `json:"host_id,omitempty"`
} }
// TODO: New. // TODO: New.

View File

@ -2,10 +2,10 @@ package utils
import ( import (
"bytes" "bytes"
"encoding/base64"
"image" "image"
"image/color" "image/color"
"image/png" "image/png"
"encoding/base64"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
) )

View File

@ -1,10 +1,10 @@
package utils package utils
import ( import (
"io"
"fmt"
"net/http"
"encoding/json" "encoding/json"
"fmt"
"io"
"net/http"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )

View File

@ -9,11 +9,11 @@ import (
) )
const ( const (
OP_MOVE = 0x01 OP_MOVE = 0x01
OP_SCROLL = 0x02 OP_SCROLL = 0x02
OP_KEY_DOWN = 0x03 OP_KEY_DOWN = 0x03
OP_KEY_UP = 0x04 OP_KEY_UP = 0x04
OP_KEY_CLK = 0x05 OP_KEY_CLK = 0x05
) )
type PayloadHeader struct { type PayloadHeader struct {

View File

@ -8,17 +8,17 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
type nulllog struct {} type nulllog struct{}
func (l nulllog) Trace(msg string) {} func (l nulllog) Trace(msg string) {}
func (l nulllog) Tracef(format string, args ...interface{}) {} func (l nulllog) Tracef(format string, args ...interface{}) {}
func (l nulllog) Debug(msg string) {} func (l nulllog) Debug(msg string) {}
func (l nulllog) Debugf(format string, args ...interface{}) {} func (l nulllog) Debugf(format string, args ...interface{}) {}
func (l nulllog) Info(msg string) {} func (l nulllog) Info(msg string) {}
func (l nulllog) Infof(format string, args ...interface{}) {} func (l nulllog) Infof(format string, args ...interface{}) {}
func (l nulllog) Warn(msg string) {} func (l nulllog) Warn(msg string) {}
func (l nulllog) Warnf(format string, args ...interface{}) {} func (l nulllog) Warnf(format string, args ...interface{}) {}
func (l nulllog) Error(msg string) {} func (l nulllog) Error(msg string) {}
func (l nulllog) Errorf(format string, args ...interface{}) {} func (l nulllog) Errorf(format string, args ...interface{}) {}
type logger struct { type logger struct {

View File

@ -3,39 +3,39 @@ package webrtc
import ( import (
"fmt" "fmt"
"io" "io"
"strings"
"reflect" "reflect"
"strings"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media" "github.com/pion/webrtc/v3/pkg/media"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/config"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message" "demodesk/neko/internal/types/message"
"demodesk/neko/internal/config"
) )
func New(desktop types.DesktopManager, capture types.CaptureManager, config *config.WebRTC) *WebRTCManagerCtx { func New(desktop types.DesktopManager, capture types.CaptureManager, config *config.WebRTC) *WebRTCManagerCtx {
return &WebRTCManagerCtx{ return &WebRTCManagerCtx{
logger: log.With().Str("module", "webrtc").Logger(), logger: log.With().Str("module", "webrtc").Logger(),
desktop: desktop, desktop: desktop,
capture: capture, capture: capture,
config: config, config: config,
// TODO: Refactor. // TODO: Refactor.
curImgListeners: map[uintptr]*func(cur *types.CursorImage){}, curImgListeners: map[uintptr]*func(cur *types.CursorImage){},
curPosListeners: map[uintptr]*func(x, y int){}, curPosListeners: map[uintptr]*func(x, y int){},
} }
} }
type WebRTCManagerCtx struct { type WebRTCManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
audioTrack *webrtc.TrackLocalStaticSample audioTrack *webrtc.TrackLocalStaticSample
audioStop func() audioStop func()
desktop types.DesktopManager desktop types.DesktopManager
capture types.CaptureManager capture types.CaptureManager
config *config.WebRTC config *config.WebRTC
// TODO: Refactor. // TODO: Refactor.
curImgListeners map[uintptr]*func(cur *types.CursorImage) curImgListeners map[uintptr]*func(cur *types.CursorImage)
curPosListeners map[uintptr]*func(x, y int) curPosListeners map[uintptr]*func(x, y int)
@ -58,7 +58,7 @@ func (manager *WebRTCManagerCtx) Start() {
} }
audio.AddListener(&listener) audio.AddListener(&listener)
manager.audioStop = func(){ manager.audioStop = func() {
audio.RemoveListener(&listener) audio.RemoveListener(&listener)
} }
@ -234,10 +234,10 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
} }
peer := &WebRTCPeerCtx{ peer := &WebRTCPeerCtx{
api: api, api: api,
connection: connection, connection: connection,
changeVideo: changeVideo, changeVideo: changeVideo,
dataChannel: dataChannel, dataChannel: dataChannel,
} }
cursorChange := func(cur *types.CursorImage) { cursorChange := func(cur *types.CursorImage) {

View File

@ -3,15 +3,15 @@ package webrtc
import "github.com/pion/webrtc/v3" import "github.com/pion/webrtc/v3"
type WebRTCPeerCtx struct { type WebRTCPeerCtx struct {
api *webrtc.API api *webrtc.API
connection *webrtc.PeerConnection connection *webrtc.PeerConnection
dataChannel *webrtc.DataChannel dataChannel *webrtc.DataChannel
changeVideo func(videoID string) error changeVideo func(videoID string) error
} }
func (webrtc_peer *WebRTCPeerCtx) SignalAnswer(sdp string) error { func (webrtc_peer *WebRTCPeerCtx) SignalAnswer(sdp string) error {
return webrtc_peer.connection.SetRemoteDescription(webrtc.SessionDescription{ return webrtc_peer.connection.SetRemoteDescription(webrtc.SessionDescription{
SDP: sdp, SDP: sdp,
Type: webrtc.SDPTypeAnswer, Type: webrtc.SDPTypeAnswer,
}) })
} }

View File

@ -1,9 +1,9 @@
package webrtc package webrtc
import ( import (
"fmt"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/utils" "demodesk/neko/internal/utils"
@ -11,7 +11,7 @@ import (
const ( const (
OP_CURSOR_POSITION = 0x01 OP_CURSOR_POSITION = 0x01
OP_CURSOR_IMAGE = 0x02 OP_CURSOR_IMAGE = 0x02
) )
type PayloadCursorPosition struct { type PayloadCursorPosition struct {
@ -35,7 +35,7 @@ func (webrtc_peer *WebRTCPeerCtx) SendCursorPosition(x, y int) error {
data := PayloadCursorPosition{ data := PayloadCursorPosition{
PayloadHeader: PayloadHeader{ PayloadHeader: PayloadHeader{
Event: OP_CURSOR_POSITION, Event: OP_CURSOR_POSITION,
Length: 7, Length: 7,
}, },
X: uint16(x), X: uint16(x),
@ -62,13 +62,13 @@ func (webrtc_peer *WebRTCPeerCtx) SendCursorImage(cur *types.CursorImage) error
data := PayloadCursorImage{ data := PayloadCursorImage{
PayloadHeader: PayloadHeader{ PayloadHeader: PayloadHeader{
Event: OP_CURSOR_IMAGE, Event: OP_CURSOR_IMAGE,
Length: uint16(11 + len(img)), Length: uint16(11 + len(img)),
}, },
Width: cur.Width, Width: cur.Width,
Height: cur.Height, Height: cur.Height,
Xhot: cur.Xhot, Xhot: cur.Xhot,
Yhot: cur.Yhot, Yhot: cur.Yhot,
} }
buffer := &bytes.Buffer{} buffer := &bytes.Buffer{}

View File

@ -23,8 +23,8 @@ func (ws *WebSocketManagerCtx) fileChooserDialogEvents() {
file_chooser_dialog_member = host file_chooser_dialog_member = host
go ws.sessions.Broadcast(message.MemberID{ go ws.sessions.Broadcast(message.MemberID{
Event: event.FILE_CHOOSER_DIALOG_OPENED, Event: event.FILE_CHOOSER_DIALOG_OPENED,
ID: host.ID(), ID: host.ID(),
}, nil) }, nil)
}) })
@ -35,11 +35,10 @@ func (ws *WebSocketManagerCtx) fileChooserDialogEvents() {
file_chooser_dialog_member = nil file_chooser_dialog_member = nil
go ws.sessions.Broadcast(message.MemberID{ go ws.sessions.Broadcast(message.MemberID{
Event: event.FILE_CHOOSER_DIALOG_CLOSED, Event: event.FILE_CHOOSER_DIALOG_CLOSED,
}, nil) }, nil)
}) })
// when new user joins, and someone holds dialog, he shouldd be notified about it. // when new user joins, and someone holds dialog, he shouldd be notified about it.
ws.sessions.OnConnected(func(session types.Session) { ws.sessions.OnConnected(func(session types.Session) {
if file_chooser_dialog_member == nil { if file_chooser_dialog_member == nil {
@ -47,8 +46,8 @@ func (ws *WebSocketManagerCtx) fileChooserDialogEvents() {
} }
if err := session.Send(message.MemberID{ if err := session.Send(message.MemberID{
Event: event.FILE_CHOOSER_DIALOG_OPENED, Event: event.FILE_CHOOSER_DIALOG_OPENED,
ID: file_chooser_dialog_member.ID(), ID: file_chooser_dialog_member.ID(),
}); err != nil { }); err != nil {
ws.logger.Warn(). ws.logger.Warn().
Str("id", session.ID()). Str("id", session.ID()).

View File

@ -21,20 +21,20 @@ func New(
logger := log.With().Str("module", "handler").Logger() logger := log.With().Str("module", "handler").Logger()
return &MessageHandlerCtx{ return &MessageHandlerCtx{
logger: logger, logger: logger,
sessions: sessions, sessions: sessions,
desktop: desktop, desktop: desktop,
capture: capture, capture: capture,
webrtc: webrtc, webrtc: webrtc,
} }
} }
type MessageHandlerCtx struct { type MessageHandlerCtx struct {
logger zerolog.Logger logger zerolog.Logger
sessions types.SessionManager sessions types.SessionManager
webrtc types.WebRTCManager webrtc types.WebRTCManager
desktop types.DesktopManager desktop types.DesktopManager
capture types.CaptureManager capture types.CaptureManager
} }
func (h *MessageHandlerCtx) Message(session types.Session, raw []byte) bool { func (h *MessageHandlerCtx) Message(session types.Session, raw []byte) bool {

View File

@ -5,7 +5,6 @@ import (
"demodesk/neko/internal/types/message" "demodesk/neko/internal/types/message"
) )
func (h *MessageHandlerCtx) keyboardMap(session types.Session, payload *message.KeyboardMap) error { func (h *MessageHandlerCtx) keyboardMap(session types.Session, payload *message.KeyboardMap) error {
if !session.IsHost() { if !session.IsHost() {
h.logger.Debug().Str("id", session.ID()).Msg("is not the host") h.logger.Debug().Str("id", session.ID()).Msg("is not the host")
@ -13,7 +12,7 @@ func (h *MessageHandlerCtx) keyboardMap(session types.Session, payload *message.
} }
return h.desktop.SetKeyboardMap(types.KeyboardMap{ return h.desktop.SetKeyboardMap(types.KeyboardMap{
Layout: payload.Layout, Layout: payload.Layout,
Variant: payload.Variant, Variant: payload.Variant,
}) })
} }
@ -25,7 +24,7 @@ func (h *MessageHandlerCtx) keyboardModifiers(session types.Session, payload *me
} }
h.desktop.SetKeyboardModifiers(types.KeyboardModifiers{ h.desktop.SetKeyboardModifiers(types.KeyboardModifiers{
NumLock: payload.NumLock, NumLock: payload.NumLock,
CapsLock: payload.CapsLock, CapsLock: payload.CapsLock,
}) })
return nil return nil

View File

@ -13,9 +13,9 @@ func (h *MessageHandlerCtx) screenSet(session types.Session, payload *message.Sc
} }
if err := h.desktop.SetScreenSize(types.ScreenSize{ if err := h.desktop.SetScreenSize(types.ScreenSize{
Width: payload.Width, Width: payload.Width,
Height: payload.Height, Height: payload.Height,
Rate: payload.Rate, Rate: payload.Rate,
}); err != nil { }); err != nil {
return err return err
} }

View File

@ -2,8 +2,8 @@ package handler
import ( import (
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/message"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message"
) )
func (h *MessageHandlerCtx) sendUnicast(session types.Session, payload *message.SendUnicast) error { func (h *MessageHandlerCtx) sendUnicast(session types.Session, payload *message.SendUnicast) error {
@ -24,10 +24,10 @@ func (h *MessageHandlerCtx) sendUnicast(session types.Session, payload *message.
func (h *MessageHandlerCtx) sendBroadcast(session types.Session, payload *message.SendBroadcast) error { func (h *MessageHandlerCtx) sendBroadcast(session types.Session, payload *message.SendBroadcast) error {
h.sessions.Broadcast(message.SendBroadcast{ h.sessions.Broadcast(message.SendBroadcast{
Event: event.SEND_BROADCAST, Event: event.SEND_BROADCAST,
Sender: session.ID(), Sender: session.ID(),
Subject: payload.Subject, Subject: payload.Subject,
Body: payload.Body, Body: payload.Body,
}, []string{ session.ID() }) }, []string{session.ID()})
return nil return nil
} }

View File

@ -23,7 +23,7 @@ func (h *MessageHandlerCtx) SessionDeleted(session types.Session) error {
message.MemberID{ message.MemberID{
Event: event.MEMBER_DELETED, Event: event.MEMBER_DELETED,
ID: session.ID(), ID: session.ID(),
}, nil); }, nil)
return nil return nil
} }

View File

@ -64,7 +64,7 @@ func (h *MessageHandlerCtx) signalVideo(session types.Session, payload *message.
return session.Send( return session.Send(
message.SignalVideo{ message.SignalVideo{
Event: event.SIGNAL_VIDEO, Event: event.SIGNAL_VIDEO,
Video: payload.Video, Video: payload.Video,
}) })
} }

View File

@ -34,10 +34,10 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error {
return session.Send( return session.Send(
message.SystemInit{ message.SystemInit{
Event: event.SYSTEM_INIT, Event: event.SYSTEM_INIT,
MemberId: session.ID(), MemberId: session.ID(),
ControlHost: controlHost, ControlHost: controlHost,
ScreenSize: message.ScreenSize{ ScreenSize: message.ScreenSize{
Width: size.Width, Width: size.Width,
Height: size.Height, Height: size.Height,
Rate: size.Rate, Rate: size.Rate,

View File

@ -8,10 +8,10 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"demodesk/neko/internal/websocket/handler"
"demodesk/neko/internal/types" "demodesk/neko/internal/types"
"demodesk/neko/internal/types/event" "demodesk/neko/internal/types/event"
"demodesk/neko/internal/types/message" "demodesk/neko/internal/types/message"
"demodesk/neko/internal/websocket/handler"
) )
func New( func New(
@ -23,11 +23,11 @@ func New(
logger := log.With().Str("module", "websocket").Logger() logger := log.With().Str("module", "websocket").Logger()
return &WebSocketManagerCtx{ return &WebSocketManagerCtx{
logger: logger, logger: logger,
sessions: sessions, sessions: sessions,
desktop: desktop, desktop: desktop,
handler: handler.New(sessions, desktop, capture, webrtc), handler: handler.New(sessions, desktop, capture, webrtc),
handlers: []types.HandlerFunction{}, handlers: []types.HandlerFunction{},
} }
} }
@ -35,12 +35,12 @@ func New(
const pingPeriod = 60 * time.Second const pingPeriod = 60 * time.Second
type WebSocketManagerCtx struct { type WebSocketManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
sessions types.SessionManager sessions types.SessionManager
desktop types.DesktopManager desktop types.DesktopManager
handler *handler.MessageHandlerCtx handler *handler.MessageHandlerCtx
handlers []types.HandlerFunction handlers []types.HandlerFunction
shutdown chan bool shutdown chan bool
} }
func (ws *WebSocketManagerCtx) Start() { func (ws *WebSocketManagerCtx) Start() {

54
neko.go
View File

@ -6,14 +6,14 @@ import (
"os/signal" "os/signal"
"runtime" "runtime"
"demodesk/neko/internal/api"
"demodesk/neko/internal/capture"
"demodesk/neko/internal/config" "demodesk/neko/internal/config"
"demodesk/neko/internal/desktop" "demodesk/neko/internal/desktop"
"demodesk/neko/internal/capture"
"demodesk/neko/internal/webrtc"
"demodesk/neko/internal/session"
"demodesk/neko/internal/websocket"
"demodesk/neko/internal/api"
"demodesk/neko/internal/http" "demodesk/neko/internal/http"
"demodesk/neko/internal/session"
"demodesk/neko/internal/webrtc"
"demodesk/neko/internal/websocket"
"demodesk/neko/modules" "demodesk/neko/modules"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@ -62,12 +62,12 @@ func init() {
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}, },
Configs: &Configs{ Configs: &Configs{
Root: &config.Root{}, Root: &config.Root{},
Desktop: &config.Desktop{}, Desktop: &config.Desktop{},
Capture: &config.Capture{}, Capture: &config.Capture{},
WebRTC: &config.WebRTC{}, WebRTC: &config.WebRTC{},
Session: &config.Session{}, Session: &config.Session{},
Server: &config.Server{}, Server: &config.Server{},
}, },
} }
} }
@ -102,26 +102,26 @@ func (i *Version) Details() string {
} }
type Configs struct { type Configs struct {
Root *config.Root Root *config.Root
Desktop *config.Desktop Desktop *config.Desktop
Capture *config.Capture Capture *config.Capture
WebRTC *config.WebRTC WebRTC *config.WebRTC
Session *config.Session Session *config.Session
Server *config.Server Server *config.Server
} }
type Neko struct { type Neko struct {
Version *Version Version *Version
Configs *Configs Configs *Configs
logger zerolog.Logger logger zerolog.Logger
desktopManager *desktop.DesktopManagerCtx desktopManager *desktop.DesktopManagerCtx
captureManager *capture.CaptureManagerCtx captureManager *capture.CaptureManagerCtx
webRTCManager *webrtc.WebRTCManagerCtx webRTCManager *webrtc.WebRTCManagerCtx
sessionManager *session.SessionManagerCtx sessionManager *session.SessionManagerCtx
webSocketManager *websocket.WebSocketManagerCtx webSocketManager *websocket.WebSocketManagerCtx
apiManager *api.ApiManagerCtx apiManager *api.ApiManagerCtx
httpManager *http.HttpManagerCtx httpManager *http.HttpManagerCtx
} }
func (neko *Neko) Preflight() { func (neko *Neko) Preflight() {