debug needed
This commit is contained in:
7
servers/qemuserver/loginit.go
Normal file
7
servers/qemuserver/loginit.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package qemuserver
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
func init() {
|
||||
logrus.Info("qemu server loaded")
|
||||
}
|
38
servers/qemuserver/options.go
Normal file
38
servers/qemuserver/options.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package qemuserver
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
QmpAddress string `yaml:"address"`
|
||||
Timeout time.Duration `yaml:"timeout"`
|
||||
Name string `yaml:"name"`
|
||||
VNCAddress string `yaml:"vnc"`
|
||||
AudioPipe string `yaml:"audio_pipe"`
|
||||
AudioDevice string `yaml:"audio_device"`
|
||||
}
|
||||
|
||||
func ExampleOptions() *Options {
|
||||
return &Options{
|
||||
QmpAddress: (&url.URL{
|
||||
Scheme: "unix",
|
||||
Path: "/tmp/qemu.sock",
|
||||
}).String(),
|
||||
Timeout: time.Duration(60 * time.Second),
|
||||
Name: "ace-qemu",
|
||||
VNCAddress: "localhost:5900",
|
||||
AudioPipe: "/tmp/audio",
|
||||
AudioDevice: "snd0",
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) MakeFIFO() error {
|
||||
if err := os.Remove(o.AudioPipe); err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Mkfifo(o.AudioDevice, 0600)
|
||||
}
|
154
servers/qemuserver/server.go
Normal file
154
servers/qemuserver/server.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package qemuserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"git.sense-t.eu.org/ACE/ace/lib/audiodriver"
|
||||
"git.sense-t.eu.org/ACE/ace/lib/qemuconnection"
|
||||
"github.com/digitalocean/go-qemu/qemu"
|
||||
"github.com/digitalocean/go-qemu/qmp"
|
||||
"github.com/pion/mediadevices/pkg/driver"
|
||||
"github.com/pion/mediadevices/pkg/driver/vncdriver"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
options *Options
|
||||
QmpConnector struct {
|
||||
RX chan *qemuconnection.Event
|
||||
TX chan qemu.Status
|
||||
}
|
||||
}
|
||||
|
||||
var DefaultServer *Server
|
||||
|
||||
func NewServer(o *Options) *Server {
|
||||
server := &Server{
|
||||
options: o,
|
||||
}
|
||||
server.QmpConnector.RX = make(chan *qemuconnection.Event)
|
||||
server.QmpConnector.TX = make(chan qemu.Status)
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
logrus.Debug("qemu server running")
|
||||
defer logrus.Debug("qemu server exit")
|
||||
u, err := url.Parse(s.options.QmpAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var address string
|
||||
if u.Scheme == "unix" {
|
||||
address = u.Path
|
||||
} else {
|
||||
address = u.Host
|
||||
}
|
||||
logrus.Debugf("trying to connect qmp with %s://%s", u.Scheme, address)
|
||||
qmpConnection, err := qmp.NewSocketMonitor(u.Scheme, address, s.options.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer qmpConnection.Disconnect()
|
||||
|
||||
if err := qmpConnection.Connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debug("qmp connected")
|
||||
|
||||
qemu, err := qemu.NewDomain(qmpConnection, s.options.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer qemu.Close()
|
||||
|
||||
go s.startCapture(qemu)
|
||||
logrus.Debug("qemu capture start")
|
||||
|
||||
for ev := range s.QmpConnector.RX {
|
||||
if ev.Type == qemuconnection.QueryStatusEvent {
|
||||
status, err := qemu.Status()
|
||||
if err != nil {
|
||||
logrus.Error("get qemu status error: ", err)
|
||||
continue
|
||||
}
|
||||
s.QmpConnector.TX <- status
|
||||
continue
|
||||
}
|
||||
for _, cmd := range ev.ToQemuCommand() {
|
||||
_, err := qemu.Run(cmd)
|
||||
if err != nil {
|
||||
logrus.Error("run command error: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) startCapture(qemu *qemu.Domain) {
|
||||
if err := s.options.MakeFIFO(); err != nil {
|
||||
logrus.Fatal("failed to make pipe file: ", err)
|
||||
}
|
||||
if _, err := qemu.Run(qmp.Command{
|
||||
Execute: "human-monitor-command",
|
||||
Args: map[string]string{
|
||||
"command-line": fmt.Sprintf(
|
||||
"wavcapture %s %s",
|
||||
s.options.AudioPipe,
|
||||
s.options.AudioDevice,
|
||||
),
|
||||
},
|
||||
}); err != nil {
|
||||
logrus.Fatal("run audio command failed: ", err)
|
||||
}
|
||||
logrus.Debug("audio capture set")
|
||||
|
||||
if err := driver.GetManager().Register(
|
||||
audiodriver.New(s.options.AudioPipe),
|
||||
driver.Info{
|
||||
Label: "audioFifo",
|
||||
DeviceType: driver.Microphone,
|
||||
Priority: driver.PriorityNormal,
|
||||
},
|
||||
); err != nil {
|
||||
logrus.Fatal("audio initialize failed: ", err)
|
||||
}
|
||||
if err := driver.GetManager().Register(
|
||||
vncdriver.NewVnc(s.options.VNCAddress),
|
||||
driver.Info{
|
||||
Label: "vnc",
|
||||
DeviceType: driver.Camera,
|
||||
Priority: driver.PriorityNormal,
|
||||
},
|
||||
); err != nil {
|
||||
logrus.Fatal("video initialize failed: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Setup(o *Options) {
|
||||
DefaultServer = NewServer(o)
|
||||
go func() {
|
||||
if err := DefaultServer.Run(); err != nil {
|
||||
logrus.Fatal("cannot run qemuserver with error: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func SendEvent(b []byte) error {
|
||||
ev, err := qemuconnection.ParseEvent(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
DefaultServer.QmpConnector.RX <- ev
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStatus() qemu.Status {
|
||||
DefaultServer.QmpConnector.RX <- &qemuconnection.Event{
|
||||
Type: qemuconnection.QueryStatusEvent,
|
||||
}
|
||||
return <-DefaultServer.QmpConnector.TX
|
||||
}
|
70
servers/webserver/handlers.go
Normal file
70
servers/webserver/handlers.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Succeed bool `json:"succeed"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func (s *Server) getInstruction(c *gin.Context) {
|
||||
f, _ := os.ReadFile(s.options.InstructionFile)
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Succeed: true,
|
||||
Message: "",
|
||||
Data: string(f),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) exchangeSDP(c *gin.Context) {
|
||||
var offer *webrtc.SessionDescription
|
||||
if err := c.BindJSON(offer); err != nil {
|
||||
c.JSON(http.StatusBadRequest, Response{
|
||||
Succeed: false,
|
||||
Message: "bad request",
|
||||
Data: err.Error(),
|
||||
})
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
answer, err := s.rtcConnector.Regist(offer)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, Response{
|
||||
Succeed: false,
|
||||
Message: "internal error",
|
||||
Data: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Succeed: true,
|
||||
Message: "",
|
||||
Data: answer,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) getICEConfig(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Succeed: true,
|
||||
Message: "",
|
||||
Data: s.options.WebRTC.STUNServers,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) getName(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Succeed: true,
|
||||
Message: "",
|
||||
Data: s.options.Name,
|
||||
})
|
||||
}
|
7
servers/webserver/loginit.go
Normal file
7
servers/webserver/loginit.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package webserver
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
func init() {
|
||||
logrus.Info("web server loaded")
|
||||
}
|
21
servers/webserver/options.go
Normal file
21
servers/webserver/options.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"git.sense-t.eu.org/ACE/ace/lib/webrtcconnection"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Name string `yaml:"name"`
|
||||
Listen string `yaml:"listen"`
|
||||
InstructionFile string `yaml:"instruction_file"`
|
||||
WebRTC *webrtcconnection.Options `yaml:"webrtc"`
|
||||
}
|
||||
|
||||
func ExampleOptions() *Options {
|
||||
return &Options{
|
||||
Name: "ACE-test",
|
||||
Listen: "localhost:8080",
|
||||
InstructionFile: "",
|
||||
WebRTC: webrtcconnection.ExampleOptions(),
|
||||
}
|
||||
}
|
12
servers/webserver/routers.go
Normal file
12
servers/webserver/routers.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package webserver
|
||||
|
||||
import "github.com/gin-contrib/static"
|
||||
|
||||
func (s *Server) setupRoute() {
|
||||
s.webServer.
|
||||
Use(static.Serve("/", newFS())).
|
||||
POST("/api/v1/sdp", s.exchangeSDP).
|
||||
GET("/api/v1/instruction", s.getInstruction).
|
||||
GET("/api/v1/iceserver/url", s.getICEConfig).
|
||||
GET("/api/v1/name", s.getName)
|
||||
}
|
40
servers/webserver/server.go
Normal file
40
servers/webserver/server.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"git.sense-t.eu.org/ACE/ace/lib/webrtcconnection"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
options *Options
|
||||
webServer *gin.Engine
|
||||
rtcConnector *webrtcconnection.Connection
|
||||
}
|
||||
|
||||
var DefaultServer *Server
|
||||
|
||||
func NewServer(o *Options) *Server {
|
||||
s := &Server{
|
||||
options: o,
|
||||
webServer: gin.New(),
|
||||
rtcConnector: webrtcconnection.New(o.WebRTC),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
logrus.Debug("webserver running")
|
||||
defer logrus.Debug("webserver exit")
|
||||
s.setupRoute()
|
||||
return s.webServer.Run(s.options.Listen)
|
||||
}
|
||||
|
||||
func Setup(o *Options) {
|
||||
DefaultServer = NewServer(o)
|
||||
go func() {
|
||||
if err := DefaultServer.Run(); err != nil {
|
||||
logrus.Fatal("cannot run webserver with error: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
37
servers/webserver/static.go
Normal file
37
servers/webserver/static.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package webserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate cp -r ../../web/dist ./
|
||||
//go:embed dist
|
||||
var staticFiles embed.FS
|
||||
|
||||
type ginFS struct {
|
||||
fs http.FileSystem
|
||||
}
|
||||
|
||||
func newFS() *ginFS {
|
||||
f, _ := fs.Sub(staticFiles, "dist")
|
||||
return &ginFS{
|
||||
fs: http.FS(f),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ginFS) Exists(prefix string, filepath string) bool {
|
||||
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
|
||||
if _, err := s.fs.Open(p); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *ginFS) Open(name string) (http.File, error) {
|
||||
return s.fs.Open(name)
|
||||
}
|
Reference in New Issue
Block a user