2022-09-26 03:04:07 +00:00
|
|
|
package qemuserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
2022-09-27 02:21:06 +00:00
|
|
|
"os"
|
|
|
|
"time"
|
2022-09-26 03:04:07 +00:00
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
"git.sense-t.eu.org/ACE/ace/drivers/audio"
|
|
|
|
"git.sense-t.eu.org/ACE/ace/drivers/video"
|
2022-09-26 03:04:07 +00:00
|
|
|
"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/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
const waveHeaderSize = audio.FmtHeaderSizeDefault
|
2022-09-27 06:36:25 +00:00
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
var waveHeader *audio.WavHeader
|
2022-09-27 08:59:47 +00:00
|
|
|
|
|
|
|
func init() {
|
2022-09-30 04:24:32 +00:00
|
|
|
waveHeader = audio.DefaultHeader()
|
2022-09-27 06:36:25 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 03:04:07 +00:00
|
|
|
type Server struct {
|
2022-09-27 06:36:25 +00:00
|
|
|
options *Options
|
2022-09-27 02:21:06 +00:00
|
|
|
qemu *qemu.Domain
|
2022-09-30 04:24:32 +00:00
|
|
|
audioHeader chan *audio.WavHeader
|
|
|
|
pcm chan []byte
|
2022-10-12 03:23:14 +00:00
|
|
|
ppm chan video.Frame
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var DefaultServer *Server
|
|
|
|
|
2022-09-26 08:07:25 +00:00
|
|
|
func NewServer(o *Options) (*Server, error) {
|
2022-09-26 03:04:07 +00:00
|
|
|
server := &Server{
|
2022-09-27 02:21:06 +00:00
|
|
|
options: o,
|
2022-09-30 04:24:32 +00:00
|
|
|
audioHeader: make(chan *audio.WavHeader, 1),
|
|
|
|
pcm: make(chan []byte),
|
2022-10-12 03:23:14 +00:00
|
|
|
ppm: make(chan video.Frame), // to be configured
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 08:07:25 +00:00
|
|
|
u, err := url.Parse(o.QmpAddress)
|
2022-09-26 03:04:07 +00:00
|
|
|
if err != nil {
|
2022-09-26 08:07:25 +00:00
|
|
|
return nil, err
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
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)
|
2022-09-26 08:07:25 +00:00
|
|
|
qmpConnection, err := qmp.NewSocketMonitor(u.Scheme, address, o.Timeout)
|
2022-09-26 03:04:07 +00:00
|
|
|
if err != nil {
|
2022-09-26 08:07:25 +00:00
|
|
|
return nil, err
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := qmpConnection.Connect(); err != nil {
|
2022-09-26 08:07:25 +00:00
|
|
|
return nil, err
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
logrus.Debug("qmp connected")
|
|
|
|
|
2022-09-26 08:07:25 +00:00
|
|
|
qemu, err := qemu.NewDomain(qmpConnection, o.Name)
|
2022-09-26 03:04:07 +00:00
|
|
|
if err != nil {
|
2022-09-26 08:07:25 +00:00
|
|
|
return nil, err
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
2022-09-26 08:07:25 +00:00
|
|
|
server.qemu = qemu
|
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
audio := &audio.PCMStreamDriver{
|
|
|
|
PCM: server.pcm,
|
|
|
|
WaveHeader: waveHeader,
|
2022-10-02 11:29:57 +00:00
|
|
|
BufferSizeByBytes: o.Audio.BufferSize, // to be configured
|
2022-09-30 04:24:32 +00:00
|
|
|
}
|
2022-09-26 08:07:25 +00:00
|
|
|
if err := driver.GetManager().Register(
|
2022-09-27 02:21:06 +00:00
|
|
|
audio,
|
2022-09-26 08:07:25 +00:00
|
|
|
driver.Info{
|
|
|
|
Label: "audioFifo",
|
|
|
|
DeviceType: driver.Microphone,
|
|
|
|
Priority: driver.PriorityNormal,
|
|
|
|
},
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-09-27 02:21:06 +00:00
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
video := &video.PPMStreamDriver{
|
2022-10-02 11:29:57 +00:00
|
|
|
Height: o.Video.Height,
|
|
|
|
Width: o.Video.Width,
|
|
|
|
FPS: o.Video.FPS,
|
2022-09-30 04:24:32 +00:00
|
|
|
PPMImage: server.ppm,
|
|
|
|
}
|
2022-09-26 08:07:25 +00:00
|
|
|
if err := driver.GetManager().Register(
|
2022-09-30 04:24:32 +00:00
|
|
|
video,
|
2022-09-26 08:07:25 +00:00
|
|
|
driver.Info{
|
|
|
|
Label: "vnc",
|
|
|
|
DeviceType: driver.Camera,
|
|
|
|
Priority: driver.PriorityNormal,
|
|
|
|
},
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return server, nil
|
|
|
|
}
|
2022-09-26 03:04:07 +00:00
|
|
|
|
2022-09-26 08:07:25 +00:00
|
|
|
func (s *Server) Run() error {
|
|
|
|
logrus.Debug("qemu server running")
|
2022-10-02 11:29:57 +00:00
|
|
|
path, err := s.options.MakeFIFO()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
logrus.Debug("screen capture start")
|
|
|
|
defer close(s.ppm)
|
2022-10-10 11:12:26 +00:00
|
|
|
|
|
|
|
ticker := time.NewTicker(time.Second / time.Duration(s.options.Video.FPS))
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
for { // to be configured
|
2022-10-12 03:23:14 +00:00
|
|
|
now := time.Now()
|
2022-10-02 11:29:57 +00:00
|
|
|
ppm, err := s.qemu.ScreenDump()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
continue
|
|
|
|
}
|
2022-10-12 03:23:14 +00:00
|
|
|
|
2022-10-10 11:12:26 +00:00
|
|
|
select {
|
2022-10-12 03:23:14 +00:00
|
|
|
case s.ppm <- video.Frame{
|
|
|
|
Time: now,
|
|
|
|
Image: ppm,
|
|
|
|
}:
|
|
|
|
<-ticker.C
|
2022-10-10 11:12:26 +00:00
|
|
|
case <-ticker.C:
|
|
|
|
}
|
2022-10-02 11:29:57 +00:00
|
|
|
}
|
|
|
|
}()
|
2022-09-26 03:04:07 +00:00
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
go func() {
|
2022-10-02 11:29:57 +00:00
|
|
|
f, err := os.Open(path)
|
2022-09-30 04:24:32 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
logrus.Debug("start reading fifo")
|
|
|
|
|
|
|
|
logrus.Debug("skip wave headers, to the PCM!")
|
|
|
|
// skip to pcm data, for 44 bytes.
|
2022-10-02 11:29:57 +00:00
|
|
|
var _dataChunkHeader [audio.FmtHeaderOffset +
|
|
|
|
audio.FmtHeaderIDSize + audio.FmtHeaderChunkSizeSize + waveHeaderSize +
|
|
|
|
audio.DataChunkIDSize + audio.DataChunkSizeSize]byte
|
2022-09-30 04:24:32 +00:00
|
|
|
if _, err := f.Read(_dataChunkHeader[:]); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2022-09-30 05:46:58 +00:00
|
|
|
logrus.Debug("start reading PCM")
|
2022-09-30 04:24:32 +00:00
|
|
|
defer close(s.pcm)
|
2022-10-10 11:12:26 +00:00
|
|
|
ticker := time.NewTicker(waveHeader.GetLatnecy(s.options.Audio.BufferSize))
|
|
|
|
defer ticker.Stop()
|
2022-09-30 04:24:32 +00:00
|
|
|
for {
|
2022-10-12 03:23:14 +00:00
|
|
|
|
2022-10-03 00:15:43 +00:00
|
|
|
b := make([]byte, s.options.Audio.BufferSize) // to be configured
|
2022-09-30 04:24:32 +00:00
|
|
|
if _, err := f.Read(b[:]); err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case s.pcm <- b:
|
2022-10-12 03:23:14 +00:00
|
|
|
<-ticker.C
|
2022-10-10 11:12:26 +00:00
|
|
|
case <-ticker.C:
|
2022-09-30 04:24:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2022-09-27 04:32:23 +00:00
|
|
|
|
2022-09-27 02:21:06 +00:00
|
|
|
go func() {
|
2022-09-27 06:36:25 +00:00
|
|
|
logrus.Debug("setting audio capture")
|
2022-09-27 02:21:06 +00:00
|
|
|
if _, err := s.qemu.Run(qmp.Command{
|
|
|
|
Execute: "human-monitor-command",
|
|
|
|
Args: map[string]string{
|
|
|
|
"command-line": fmt.Sprintf(
|
2022-09-27 06:36:25 +00:00
|
|
|
"wavcapture %s %s %d %d %d",
|
2022-10-02 11:29:57 +00:00
|
|
|
path,
|
|
|
|
s.options.Audio.Device,
|
2022-09-27 06:36:25 +00:00
|
|
|
waveHeader.SampleRate,
|
|
|
|
waveHeader.BitsPerSample,
|
|
|
|
waveHeader.NumChannels,
|
2022-09-27 02:21:06 +00:00
|
|
|
),
|
|
|
|
},
|
|
|
|
}); err != nil {
|
|
|
|
logrus.Fatal("run audio command failed: ", err)
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
2022-09-27 02:21:06 +00:00
|
|
|
logrus.Debug("audio capture set")
|
2022-09-26 03:04:07 +00:00
|
|
|
}()
|
2022-09-27 06:36:25 +00:00
|
|
|
|
2022-09-30 04:24:32 +00:00
|
|
|
select {}
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
|
|
|
|
2022-09-27 02:21:06 +00:00
|
|
|
func (s *Server) SendEvent(b []byte) error {
|
2022-09-26 03:04:07 +00:00
|
|
|
ev, err := qemuconnection.ParseEvent(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-09-27 06:36:25 +00:00
|
|
|
for _, cmd := range ev.ToQemuCommand() {
|
|
|
|
_, err := s.qemu.Run(cmd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2022-09-26 03:04:07 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-09-27 02:21:06 +00:00
|
|
|
func (s *Server) GetStatus() qemu.Status {
|
2022-09-27 06:36:25 +00:00
|
|
|
status, err := s.qemu.Status()
|
|
|
|
if err != nil {
|
|
|
|
return qemu.StatusIOError
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|
2022-09-27 06:36:25 +00:00
|
|
|
return status
|
2022-09-26 03:04:07 +00:00
|
|
|
}
|