This commit is contained in:
Miroslav Šedivý 2023-01-28 22:08:36 +01:00
parent dfe8b8b57d
commit ee13e40d4c
16 changed files with 135 additions and 103 deletions

View File

@ -177,9 +177,9 @@ 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),
Timestamp: time.Now(), Timestamp: time.Now(),
Duration: time.Duration(duration), Duration: time.Duration(duration),
} }
} else { } else {
log.Warn(). log.Warn().

View File

@ -18,7 +18,6 @@ type CaptureManagerCtx struct {
broadcast *BroacastManagerCtx broadcast *BroacastManagerCtx
audio *StreamSinkManagerCtx audio *StreamSinkManagerCtx
video *StreamSinkManagerCtx video *StreamSinkManagerCtx
} }
func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCtx { func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCtx {
@ -53,7 +52,8 @@ func (manager *CaptureManagerCtx) Start() {
go func() { go func() {
for { for {
_ = <- manager.desktop.GetBeforeScreenSizeChangeChannel() <-manager.desktop.GetBeforeScreenSizeChangeChannel()
if manager.video.Started() { if manager.video.Started() {
manager.video.destroyPipeline() manager.video.destroyPipeline()
} }
@ -66,7 +66,8 @@ func (manager *CaptureManagerCtx) Start() {
go func() { go func() {
for { for {
framerate := <- manager.desktop.GetAfterScreenSizeChangeChannel(); framerate := <-manager.desktop.GetAfterScreenSizeChangeChannel()
if manager.video.Started() { if manager.video.Started() {
manager.video.SetChangeFramerate(framerate) manager.video.SetChangeFramerate(framerate)
err := manager.video.createPipeline() err := manager.video.createPipeline()

View File

@ -107,26 +107,27 @@ func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc strin
pipelineStr = fmt.Sprintf(videoSrc+"vp9enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, display, fps, bitrate*1000) pipelineStr = fmt.Sprintf(videoSrc+"vp9enc target-bitrate=%d cpu-used=-5 threads=4 deadline=1 keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, display, fps, bitrate*1000)
case codec.AV1().Name: case codec.AV1().Name:
// https://gstreamer.freedesktop.org/documentation/aom/av1enc.html?gi-language=c // https://gstreamer.freedesktop.org/documentation/aom/av1enc.html?gi-language=c
// gstreamer1.0-plugins-bad // gstreamer1.0-plugins-bad
// av1enc usage-profile=1 // av1enc usage-profile=1
if err := gst.CheckPlugins([]string{"ximagesrc", "vpx"}); err != nil { // TODO: check for plugin.
return "", err if err := gst.CheckPlugins([]string{"ximagesrc", "vpx"}); err != nil {
} return "", err
}
pipelineStr = strings.Join([]string{ pipelineStr = strings.Join([]string{
fmt.Sprintf(videoSrc, display, fps), fmt.Sprintf(videoSrc, display, fps),
"av1enc", "av1enc",
fmt.Sprintf("target-bitrate=%d", bitrate*650), fmt.Sprintf("target-bitrate=%d", bitrate*650),
"cpu-used=4", "cpu-used=4",
"end-usage=cbr", "end-usage=cbr",
// "usage-profile=realtime", // "usage-profile=realtime",
"undershoot=95", "undershoot=95",
"keyframe-max-dist=25", "keyframe-max-dist=25",
"min-quantizer=4", "min-quantizer=4",
"max-quantizer=20", "max-quantizer=20",
pipelineStr, pipelineStr,
}, " ") }, " ")
case codec.H264().Name: case codec.H264().Name:
if err := gst.CheckPlugins([]string{"ximagesrc"}); err != nil { if err := gst.CheckPlugins([]string{"ximagesrc"}); err != nil {
return "", err return "", err

View File

@ -2,9 +2,9 @@ package capture
import ( import (
"errors" "errors"
"sync"
"regexp" "regexp"
"strconv" "strconv"
"sync"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -143,7 +143,7 @@ func (manager *StreamSinkManagerCtx) createPipeline() error {
if manager.changeFramerate > 0 && manager.adaptiveFramerate { if manager.changeFramerate > 0 && manager.adaptiveFramerate {
m1 := regexp.MustCompile(`framerate=\d+/1`) m1 := regexp.MustCompile(`framerate=\d+/1`)
pipelineStr = m1.ReplaceAllString(pipelineStr, "framerate=" + strconv.FormatInt(int64(manager.changeFramerate), 10) + "/1") pipelineStr = m1.ReplaceAllString(pipelineStr, "framerate="+strconv.FormatInt(int64(manager.changeFramerate), 10)+"/1")
} }
manager.logger.Info(). manager.logger.Info().
@ -180,7 +180,7 @@ func (manager *StreamSinkManagerCtx) destroyPipeline() {
manager.pipeline = nil manager.pipeline = nil
} }
func (manager *StreamSinkManagerCtx) GetSampleChannel() (chan types.Sample) { func (manager *StreamSinkManagerCtx) GetSampleChannel() chan types.Sample {
if manager.pipeline != nil { if manager.pipeline != nil {
return manager.pipeline.Sample return manager.pipeline.Sample
} }

View File

@ -11,12 +11,12 @@ import (
type Capture struct { type Capture struct {
// video // video
Display string Display string
VideoCodec codec.RTPCodec VideoCodec codec.RTPCodec
VideoHWEnc string // TODO: Pipeline builder. VideoHWEnc string // TODO: Pipeline builder.
VideoBitrate uint // TODO: Pipeline builder. VideoBitrate uint // TODO: Pipeline builder.
VideoMaxFPS int16 // TODO: Pipeline builder. VideoMaxFPS int16 // TODO: Pipeline builder.
VideoPipeline string VideoPipeline string
VideoAdaptiveFramerate bool VideoAdaptiveFramerate bool
// audio // audio
@ -60,7 +60,7 @@ func (Capture) Init(cmd *cobra.Command) error {
// DEPRECATED: video codec // DEPRECATED: video codec
cmd.PersistentFlags().Bool("av1", false, "DEPRECATED: use video_codec") cmd.PersistentFlags().Bool("av1", false, "DEPRECATED: use video_codec")
if err := viper.BindPFlag("av1", cmd.PersistentFlags().Lookup("av1")); err != nil { if err := viper.BindPFlag("av1", cmd.PersistentFlags().Lookup("av1")); err != nil {
return err return err
} }
// DEPRECATED: video codec // DEPRECATED: video codec

View File

@ -16,21 +16,21 @@ import (
var mu = sync.Mutex{} var mu = sync.Mutex{}
type DesktopManagerCtx struct { type DesktopManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
wg sync.WaitGroup wg sync.WaitGroup
shutdown chan struct{} shutdown chan struct{}
beforeScreenSizeChangeChannel chan bool beforeScreenSizeChangeChannel chan bool
afterScreenSizeChangeChannel chan int16 afterScreenSizeChangeChannel chan int16
config *config.Desktop config *config.Desktop
} }
func New(config *config.Desktop) *DesktopManagerCtx { func New(config *config.Desktop) *DesktopManagerCtx {
return &DesktopManagerCtx{ return &DesktopManagerCtx{
logger: log.With().Str("module", "desktop").Logger(), logger: log.With().Str("module", "desktop").Logger(),
shutdown: make(chan struct{}), shutdown: make(chan struct{}),
beforeScreenSizeChangeChannel: make (chan bool), beforeScreenSizeChangeChannel: make(chan bool),
afterScreenSizeChangeChannel: make (chan int16), afterScreenSizeChangeChannel: make(chan int16),
config: config, config: config,
} }
} }
@ -50,13 +50,13 @@ func (manager *DesktopManagerCtx) Start() {
go func() { go func() {
for { for {
desktopErrorMessage := <- xevent.EventErrorChannel msg := <-xevent.EventErrorChannel
manager.logger.Warn(). manager.logger.Warn().
Uint8("error_code", desktopErrorMessage.Error_code). Uint8("error_code", msg.Error_code).
Str("message", desktopErrorMessage.Message). Str("message", msg.Message).
Uint8("request_code", desktopErrorMessage.Request_code). Uint8("request_code", msg.Request_code).
Uint8("minor_code", desktopErrorMessage.Minor_code). Uint8("minor_code", msg.Minor_code).
Msg("X event error occurred") Msg("X event error occurred")
} }
}() }()
@ -79,11 +79,11 @@ func (manager *DesktopManagerCtx) Start() {
}() }()
} }
func (manager *DesktopManagerCtx) GetBeforeScreenSizeChangeChannel() (chan bool) { func (manager *DesktopManagerCtx) GetBeforeScreenSizeChangeChannel() chan bool {
return manager.beforeScreenSizeChangeChannel return manager.beforeScreenSizeChangeChannel
} }
func (manager *DesktopManagerCtx) GetAfterScreenSizeChangeChannel() (chan int16) { func (manager *DesktopManagerCtx) GetAfterScreenSizeChangeChannel() chan int16 {
return manager.afterScreenSizeChangeChannel return manager.afterScreenSizeChangeChannel
} }

View File

@ -5,22 +5,22 @@ import (
"m1k1o/neko/internal/types" "m1k1o/neko/internal/types"
) )
func (manager *DesktopManagerCtx) GetCursorChangedChannel() (chan uint64) { func (manager *DesktopManagerCtx) GetCursorChangedChannel() chan uint64 {
return xevent.CursorChangedChannel return xevent.CursorChangedChannel
} }
func (manager *DesktopManagerCtx) GetClipboardUpdatedChannel() (chan bool) { func (manager *DesktopManagerCtx) GetClipboardUpdatedChannel() chan bool {
return xevent.ClipboardUpdatedChannel return xevent.ClipboardUpdatedChannel
} }
func (manager *DesktopManagerCtx) GetFileChooserDialogOpenedChannel() (chan bool) { func (manager *DesktopManagerCtx) GetFileChooserDialogOpenedChannel() chan bool {
return xevent.FileChooserDialogOpenedChannel return xevent.FileChooserDialogOpenedChannel
} }
func (manager *DesktopManagerCtx) GetFileChooserDialogClosedChannel() (chan bool) { func (manager *DesktopManagerCtx) GetFileChooserDialogClosedChannel() chan bool {
return xevent.FileChooserDialogClosedChannel return xevent.FileChooserDialogClosedChannel
} }
func (manager *DesktopManagerCtx) GetEventErrorChannel() (chan types.DesktopErrorMessage) { func (manager *DesktopManagerCtx) GetEventErrorChannel() chan types.DesktopErrorMessage {
return xevent.EventErrorChannel return xevent.EventErrorChannel
} }

View File

@ -9,6 +9,7 @@ import "C"
import ( import (
"unsafe" "unsafe"
"m1k1o/neko/internal/types" "m1k1o/neko/internal/types"
) )
@ -25,20 +26,22 @@ func init() {
FileChooserDialogOpenedChannel = make(chan bool) FileChooserDialogOpenedChannel = make(chan bool)
EventErrorChannel = make(chan types.DesktopErrorMessage) EventErrorChannel = make(chan types.DesktopErrorMessage)
// Dummy goroutines since there is no consumer for the channel otherwise
go func() { go func() {
for { for {
_ = <-CursorChangedChannel // TODO: Unused.
<-CursorChangedChannel
} }
}() }()
go func() { go func() {
for { for {
_ = <-FileChooserDialogClosedChannel // TODO: Unused.
<-FileChooserDialogClosedChannel
} }
}() }()
go func() { go func() {
for { for {
_ = <-FileChooserDialogOpenedChannel // TODO: Unused.
<-FileChooserDialogOpenedChannel
} }
}() }()
} }
@ -72,7 +75,12 @@ func goXEventUnmapNotify(window C.Window) {
//export goXEventError //export goXEventError
func goXEventError(event *C.XErrorEvent, message *C.char) { func goXEventError(event *C.XErrorEvent, message *C.char) {
EventErrorChannel <- types.DesktopErrorMessage{ uint8(event.error_code), C.GoString(message), uint8(event.request_code), uint8(event.minor_code) } EventErrorChannel <- types.DesktopErrorMessage{
Error_code: uint8(event.error_code),
Message: C.GoString(message),
Request_code: uint8(event.request_code),
Minor_code: uint8(event.minor_code),
}
} }
//export goXEventActive //export goXEventActive

View File

@ -13,25 +13,25 @@ import (
func New(capture types.CaptureManager) *SessionManager { func New(capture types.CaptureManager) *SessionManager {
return &SessionManager{ return &SessionManager{
logger: log.With().Str("module", "session").Logger(), logger: log.With().Str("module", "session").Logger(),
host: "", host: "",
capture: capture, capture: capture,
sessionChannel: make(chan types.SessionInformation, 10), sessionChannel: make(chan types.SessionInformation, 10),
hostChannel: make(chan types.HostInformation, 10), hostChannel: make(chan types.HostInformation, 10),
members: make(map[string]*Session), members: make(map[string]*Session),
} }
} }
type SessionManager struct { type SessionManager struct {
mu sync.Mutex mu sync.Mutex
logger zerolog.Logger logger zerolog.Logger
host string host string
capture types.CaptureManager capture types.CaptureManager
members map[string]*Session members map[string]*Session
sessionChannel chan types.SessionInformation sessionChannel chan types.SessionInformation
hostChannel chan types.HostInformation hostChannel chan types.HostInformation
// TODO: Handle locks in sessions as flags. // TODO: Handle locks in sessions as flags.
controlLocked bool controlLocked bool
} }
func (manager *SessionManager) New(id string, admin bool, socket types.WebSocket) types.Session { func (manager *SessionManager) New(id string, admin bool, socket types.WebSocket) types.Session {
@ -50,11 +50,16 @@ func (manager *SessionManager) New(id string, admin bool, socket types.WebSocket
manager.capture.Video().AddListener() manager.capture.Video().AddListener()
manager.mu.Unlock() manager.mu.Unlock()
manager.sessionChannel <- types.SessionInformation{ "created", id, session } manager.sessionChannel <- types.SessionInformation{
Type: "created",
Id: id,
Session: session,
}
go func() { go func() {
for { for {
_ = <- manager.hostChannel // TODO: Unused.
<-manager.hostChannel
} }
}() }()
return session return session
@ -76,7 +81,10 @@ func (manager *SessionManager) SetHost(id string) error {
if ok { if ok {
manager.host = id manager.host = id
manager.hostChannel <- types.HostInformation{ "host", id } manager.hostChannel <- types.HostInformation{
Type: "host",
Id: id,
}
return nil return nil
} }
@ -94,7 +102,10 @@ func (manager *SessionManager) GetHost() (types.Session, bool) {
func (manager *SessionManager) ClearHost() { func (manager *SessionManager) ClearHost() {
id := manager.host id := manager.host
manager.host = "" manager.host = ""
manager.hostChannel <- types.HostInformation{ "host_cleared", id } manager.hostChannel <- types.HostInformation{
Type: "host_cleared",
Id: id,
}
} }
func (manager *SessionManager) Has(id string) bool { func (manager *SessionManager) Has(id string) bool {
@ -171,7 +182,11 @@ func (manager *SessionManager) Destroy(id string) {
manager.capture.Video().RemoveListener() manager.capture.Video().RemoveListener()
manager.mu.Unlock() manager.mu.Unlock()
manager.sessionChannel <- types.SessionInformation{ "destroyed", id, session } manager.sessionChannel <- types.SessionInformation{
Type: "destroyed",
Id: id,
Session: session,
}
manager.logger.Err(err).Str("session_id", id).Msg("destroying session") manager.logger.Err(err).Str("session_id", id).Msg("destroying session")
return return
} }
@ -229,10 +244,10 @@ func (manager *SessionManager) AdminBroadcast(v interface{}, exclude interface{}
return nil return nil
} }
func (manager *SessionManager) GetSessionChannel() (chan types.SessionInformation) { func (manager *SessionManager) GetSessionChannel() chan types.SessionInformation {
return manager.sessionChannel return manager.sessionChannel
} }
func (manager *SessionManager) GetHostChannel() (chan types.HostInformation) { func (manager *SessionManager) GetHostChannel() chan types.HostInformation {
return manager.hostChannel return manager.hostChannel
} }

View File

@ -78,7 +78,11 @@ func (session *Session) SetPeer(peer types.Peer) error {
func (session *Session) SetConnected(connected bool) error { func (session *Session) SetConnected(connected bool) error {
session.connected = connected session.connected = connected
if connected { if connected {
session.manager.sessionChannel <- types.SessionInformation{ "connected", session.id, session } session.manager.sessionChannel <- types.SessionInformation{
Type: "connected",
Id: session.id,
Session: session,
}
} }
return nil return nil
} }

View File

@ -25,7 +25,7 @@ type StreamSinkManager interface {
ListenersCount() int ListenersCount() int
Started() bool Started() bool
GetSampleChannel() (chan Sample) GetSampleChannel() chan Sample
SetChangeFramerate(rate int16) SetChangeFramerate(rate int16)
SetAdaptiveFramerate(allow bool) SetAdaptiveFramerate(allow bool)
} }

View File

@ -116,6 +116,7 @@ func H264() RTPCodec {
}, },
} }
} }
// TODO: Profile ID. // TODO: Profile ID.
func AV1() RTPCodec { func AV1() RTPCodec {
return RTPCodec{ return RTPCodec{

View File

@ -43,8 +43,8 @@ type DesktopErrorMessage struct {
type DesktopManager interface { type DesktopManager interface {
Start() Start()
Shutdown() error Shutdown() error
GetBeforeScreenSizeChangeChannel() (chan bool) GetBeforeScreenSizeChangeChannel() chan bool
GetAfterScreenSizeChangeChannel() (chan int16) GetAfterScreenSizeChangeChannel() chan int16
// clipboard // clipboard
ReadClipboard() string ReadClipboard() string
@ -72,9 +72,9 @@ type DesktopManager interface {
GetScreenshotImage() *image.RGBA GetScreenshotImage() *image.RGBA
// xevent // xevent
GetCursorChangedChannel() (chan uint64) GetCursorChangedChannel() chan uint64
GetClipboardUpdatedChannel() (chan bool) GetClipboardUpdatedChannel() chan bool
GetFileChooserDialogOpenedChannel() (chan bool) GetFileChooserDialogOpenedChannel() chan bool
GetFileChooserDialogClosedChannel() (chan bool) GetFileChooserDialogClosedChannel() chan bool
GetEventErrorChannel() (chan DesktopErrorMessage) GetEventErrorChannel() chan DesktopErrorMessage
} }

View File

@ -8,9 +8,9 @@ type Member struct {
} }
type SessionInformation struct { type SessionInformation struct {
Type string Type string
Id string Id string
Session Session Session Session
} }
type HostInformation struct { type HostInformation struct {
@ -57,6 +57,6 @@ type SessionManager interface {
Clear() error Clear() error
Broadcast(v interface{}, exclude interface{}) error Broadcast(v interface{}, exclude interface{}) error
AdminBroadcast(v interface{}, exclude interface{}) error AdminBroadcast(v interface{}, exclude interface{}) error
GetSessionChannel() (chan SessionInformation) GetSessionChannel() chan SessionInformation
GetHostChannel() (chan HostInformation) GetHostChannel() chan HostInformation
} }

View File

@ -62,7 +62,8 @@ func (manager *WebRTCManager) Start() {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
continue continue
} }
newSample := <- manager.capture.Audio().GetSampleChannel()
newSample := <-manager.capture.Audio().GetSampleChannel()
err := manager.audioTrack.WriteSample(media.Sample(newSample)) err := manager.audioTrack.WriteSample(media.Sample(newSample))
if err != nil && errors.Is(err, io.ErrClosedPipe) { if err != nil && errors.Is(err, io.ErrClosedPipe) {
manager.logger.Warn().Err(err).Msg("audio pipeline failed to write") manager.logger.Warn().Err(err).Msg("audio pipeline failed to write")
@ -87,7 +88,8 @@ func (manager *WebRTCManager) Start() {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
continue continue
} }
newSample := <- manager.capture.Video().GetSampleChannel()
newSample := <-manager.capture.Video().GetSampleChannel()
err := manager.videoTrack.WriteSample(media.Sample(newSample)) err := manager.videoTrack.WriteSample(media.Sample(newSample))
if err != nil && errors.Is(err, io.ErrClosedPipe) { if err != nil && errors.Is(err, io.ErrClosedPipe) {
manager.logger.Warn().Err(err).Msg("video pipeline failed to write") manager.logger.Warn().Err(err).Msg("video pipeline failed to write")

View File

@ -101,9 +101,9 @@ type WebSocketHandler struct {
} }
func (ws *WebSocketHandler) Start() { func (ws *WebSocketHandler) Start() {
go func () { go func() {
for { for {
channelMessage := <- ws.sessions.GetSessionChannel() channelMessage := <-ws.sessions.GetSessionChannel()
switch channelMessage.Type { switch channelMessage.Type {
case "created": case "created":
@ -189,7 +189,7 @@ func (ws *WebSocketHandler) Start() {
go func() { go func() {
for { for {
_ = <- ws.desktop.GetClipboardUpdatedChannel() _ = <-ws.desktop.GetClipboardUpdatedChannel()
session, ok := ws.sessions.GetHost() session, ok := ws.sessions.GetHost()
if !ok { if !ok {
return return