debug needed

This commit is contained in:
Sense T
2022-09-26 03:04:07 +00:00
parent 0ce8374276
commit 4c80f8a25e
39 changed files with 8233 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
package qemuserver
import "github.com/sirupsen/logrus"
func init() {
logrus.Info("qemu server loaded")
}

View 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)
}

View 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
}

View 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,
})
}

View File

@@ -0,0 +1,7 @@
package webserver
import "github.com/sirupsen/logrus"
func init() {
logrus.Info("web server loaded")
}

View 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(),
}
}

View 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)
}

View 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)
}
}()
}

View 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)
}