165 lines
3.6 KiB
Go
165 lines
3.6 KiB
Go
package desktop
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
type DesktopSession struct {
|
|
SessionID string
|
|
Port int
|
|
Config PipelineConfig
|
|
Pipeline *PipelineManager
|
|
Injector *XdotoolInjector
|
|
WebRTC *WebRTCServer
|
|
}
|
|
|
|
type Service struct {
|
|
sessions map[string]*DesktopSession
|
|
mu sync.Mutex
|
|
}
|
|
|
|
var (
|
|
instance *Service
|
|
once sync.Once
|
|
)
|
|
|
|
func GetService() *Service {
|
|
once.Do(func() {
|
|
instance = &Service{
|
|
sessions: make(map[string]*DesktopSession),
|
|
}
|
|
})
|
|
return instance
|
|
}
|
|
|
|
func (s *Service) StartSession(sessionID string, cfg PipelineConfig, iceServers []string) (*DesktopSession, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
cfg.Port = normalizeRTPPort(cfg.Port)
|
|
cfg.Display = ResolveDesktopDisplay(cfg.Display)
|
|
|
|
s.stopSessionByIDLocked(sessionID)
|
|
s.stopSessionsOnPortLocked(cfg.Port)
|
|
|
|
log.Printf("Starting Remote Desktop session: %s", sessionID)
|
|
|
|
// 1. Initialize input injector
|
|
injector := NewXdotoolInjector(cfg.Display)
|
|
if err := injector.Start(); err != nil {
|
|
return nil, fmt.Errorf("failed to start input injector: %w", err)
|
|
}
|
|
|
|
// 2. Initialize WebRTC server
|
|
webrtcSrv, err := NewWebRTCServer(injector)
|
|
if err != nil {
|
|
_ = injector.Close()
|
|
return nil, fmt.Errorf("failed to create WebRTC server: %w", err)
|
|
}
|
|
|
|
if err := webrtcSrv.InitPeerConnection(iceServers); err != nil {
|
|
_ = injector.Close()
|
|
return nil, fmt.Errorf("failed to init peer connection: %w", err)
|
|
}
|
|
|
|
// Start local UDP listener for GStreamer RTP packets
|
|
if err := webrtcSrv.StartRTPReceiver(cfg.Port); err != nil {
|
|
webrtcSrv.Close()
|
|
_ = injector.Close()
|
|
return nil, fmt.Errorf("failed to start RTP receiver: %w", err)
|
|
}
|
|
|
|
sess := &DesktopSession{
|
|
SessionID: sessionID,
|
|
Port: cfg.Port,
|
|
Config: cfg,
|
|
Pipeline: NewPipelineManager(),
|
|
Injector: injector,
|
|
WebRTC: webrtcSrv,
|
|
}
|
|
|
|
s.sessions[sessionID] = sess
|
|
return sess, nil
|
|
}
|
|
|
|
func (s *Service) StartCapture(sessionID string) error {
|
|
sess, err := s.GetSession(sessionID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sess.Pipeline == nil {
|
|
sess.Pipeline = NewPipelineManager()
|
|
}
|
|
if sess.Pipeline.IsRunning() {
|
|
return nil
|
|
}
|
|
if err := sess.Pipeline.Start(sess.Config); err != nil {
|
|
return fmt.Errorf("failed to start capture pipeline: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) GetSession(sessionID string) (*DesktopSession, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
sess, exists := s.sessions[sessionID]
|
|
if !exists {
|
|
return nil, fmt.Errorf("session %s not found", sessionID)
|
|
}
|
|
return sess, nil
|
|
}
|
|
|
|
func (s *Service) StopSession(sessionID string) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
s.stopSessionByIDLocked(sessionID)
|
|
}
|
|
|
|
func (s *Service) stopSessionByIDLocked(sessionID string) {
|
|
if sess, exists := s.sessions[sessionID]; exists {
|
|
s.stopSessionLocked(sess)
|
|
delete(s.sessions, sessionID)
|
|
}
|
|
}
|
|
|
|
func (s *Service) stopSessionsOnPortLocked(port int) {
|
|
if port <= 0 {
|
|
return
|
|
}
|
|
for sessionID, sess := range s.sessions {
|
|
if sess.Port != port {
|
|
continue
|
|
}
|
|
log.Printf("Stopping Remote Desktop session on RTP port %d: %s", port, sessionID)
|
|
s.stopSessionLocked(sess)
|
|
delete(s.sessions, sessionID)
|
|
}
|
|
}
|
|
|
|
func (s *Service) stopSessionLocked(sess *DesktopSession) {
|
|
log.Printf("Stopping Remote Desktop session: %s", sess.SessionID)
|
|
if sess.Pipeline != nil {
|
|
sess.Pipeline.Stop()
|
|
}
|
|
if sess.WebRTC != nil {
|
|
sess.WebRTC.Close()
|
|
}
|
|
if sess.Injector != nil {
|
|
_ = sess.Injector.Close()
|
|
}
|
|
}
|
|
|
|
func (s *Service) AddICECandidate(sessionID string, candidate webrtc.ICECandidateInit) error {
|
|
sess, err := s.GetSession(sessionID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sess.WebRTC.AddICECandidate(candidate)
|
|
}
|