to be continued

This commit is contained in:
TonyChyi 2022-10-12 11:23:14 +08:00
parent f1fe154e01
commit c64db4a7a5
4 changed files with 46 additions and 19 deletions

2
TODO
View File

@ -15,4 +15,4 @@
1. 视频设备延迟过高 1. 视频设备延迟过高
# 2022-10-11 # 2022-10-11
1. 解决视频设备延迟->丢帧机制或者使用tmpfs 1. 解决视频设备延迟->丢帧机制或者使用tmpfs->RGB到YUV时双for循环慢平均在30-50ms

View File

@ -5,8 +5,8 @@ import (
"image" "image"
"image/color" "image/color"
"io" "io"
"time"
"github.com/nfnt/resize"
"github.com/pion/mediadevices/pkg/frame" "github.com/pion/mediadevices/pkg/frame"
"github.com/pion/mediadevices/pkg/io/video" "github.com/pion/mediadevices/pkg/io/video"
"github.com/pion/mediadevices/pkg/prop" "github.com/pion/mediadevices/pkg/prop"
@ -17,10 +17,15 @@ import (
const DefaultFPS float32 = 60.0 const DefaultFPS float32 = 60.0
type Frame struct {
Time time.Time
Image io.ReadCloser
}
type PPMStreamDriver struct { type PPMStreamDriver struct {
Height, Width int Height, Width int
FPS float32 FPS float32
PPMImage <-chan io.ReadCloser PPMImage <-chan Frame
closed <-chan struct{} closed <-chan struct{}
cancel func() cancel func()
} }
@ -57,28 +62,45 @@ func (v *PPMStreamDriver) VideoRecord(p prop.Media) (video.Reader, error) {
image.Rect(0, 0, p.Width, p.Height), image.Rect(0, 0, p.Width, p.Height),
image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio420,
) )
Y, Cb, Cr := color.RGBToYCbCr(0, 0, 0)
for y := 0; y < canvas.Rect.Dy(); y++ {
for x := 0; x < canvas.Rect.Dx(); x++ {
canvas.Y[canvas.YOffset(x, y)] = Y
canvas.Cb[canvas.COffset(x, y)] = Cb
canvas.Cr[canvas.COffset(x, y)] = Cr
}
}
r := video.ReaderFunc(func() (img image.Image, release func(), err error) { r := video.ReaderFunc(func() (img image.Image, release func(), err error) {
select { select {
case <-v.closed: case <-v.closed:
return nil, func() {}, io.EOF return nil, func() {}, io.EOF
case ppmF := <-v.PPMImage: case ppmF := <-v.PPMImage:
defer ppmF.Close() defer ppmF.Image.Close()
img, _, err := image.Decode(ppmF) img, _, err := image.Decode(ppmF.Image)
if err != nil { if err != nil {
return nil, func() {}, err return nil, func() {}, err
} }
// resize image and draw it to canvas // resize image and draw it to canvas
resized := resize.Resize(uint(p.Width), uint(p.Height), img, resize.Bilinear) //resized := resize.Resize(uint(p.Width), uint(p.Height), img, resize.Bilinear)
for y := 0; y < resized.Bounds().Dy(); y++ { offsetX := (canvas.Rect.Dx() - img.Bounds().Dx()) / 2
for x := 0; x < resized.Bounds().Dx(); x++ { offsetY := (canvas.Rect.Dy() - img.Bounds().Dy()) / 2
r, g, b, _ := resized.At(x, y).RGBA()
for y := 0; y < img.Bounds().Dy(); y++ {
for x := 0; x < img.Bounds().Dx(); x++ {
r, g, b, _ := img.At(x, y).RGBA()
Y, Cb, Cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) Y, Cb, Cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b))
canvas.Y[canvas.YOffset(x, y)] = Y _x := offsetX + x
canvas.Cb[canvas.COffset(x, y)] = Cb _y := offsetY + y
canvas.Cr[canvas.COffset(x, y)] = Cr
canvas.Y[canvas.YOffset(_x, _y)] = Y
canvas.Cb[canvas.COffset(_x, _y)] = Cb
canvas.Cr[canvas.COffset(_x, _y)] = Cr
} }
} }
default: default:

View File

@ -43,7 +43,7 @@ func ExampleOptions() *Options {
Video: VideoOptions{ Video: VideoOptions{
Height: 768, Height: 768,
Width: 1024, Width: 1024,
FPS: 60, FPS: 30,
}, },
} }
} }

View File

@ -2,7 +2,6 @@ package qemuserver
import ( import (
"fmt" "fmt"
"io"
"net/url" "net/url"
"os" "os"
"time" "time"
@ -29,7 +28,7 @@ type Server struct {
qemu *qemu.Domain qemu *qemu.Domain
audioHeader chan *audio.WavHeader audioHeader chan *audio.WavHeader
pcm chan []byte pcm chan []byte
ppm chan io.ReadCloser ppm chan video.Frame
} }
var DefaultServer *Server var DefaultServer *Server
@ -39,7 +38,7 @@ func NewServer(o *Options) (*Server, error) {
options: o, options: o,
audioHeader: make(chan *audio.WavHeader, 1), audioHeader: make(chan *audio.WavHeader, 1),
pcm: make(chan []byte), pcm: make(chan []byte),
ppm: make(chan io.ReadCloser, 1), // to be configured ppm: make(chan video.Frame), // to be configured
} }
u, err := url.Parse(o.QmpAddress) u, err := url.Parse(o.QmpAddress)
@ -120,17 +119,21 @@ func (s *Server) Run() error {
defer ticker.Stop() defer ticker.Stop()
for { // to be configured for { // to be configured
now := time.Now()
ppm, err := s.qemu.ScreenDump() ppm, err := s.qemu.ScreenDump()
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
continue continue
} }
select { select {
case s.ppm <- ppm: case s.ppm <- video.Frame{
Time: now,
Image: ppm,
}:
<-ticker.C
case <-ticker.C: case <-ticker.C:
continue
} }
<-ticker.C
} }
}() }()
@ -156,12 +159,14 @@ func (s *Server) Run() error {
ticker := time.NewTicker(waveHeader.GetLatnecy(s.options.Audio.BufferSize)) ticker := time.NewTicker(waveHeader.GetLatnecy(s.options.Audio.BufferSize))
defer ticker.Stop() defer ticker.Stop()
for { for {
b := make([]byte, s.options.Audio.BufferSize) // to be configured b := make([]byte, s.options.Audio.BufferSize) // to be configured
if _, err := f.Read(b[:]); err != nil { if _, err := f.Read(b[:]); err != nil {
logrus.Error(err) logrus.Error(err)
} }
select { select {
case s.pcm <- b: case s.pcm <- b:
<-ticker.C
case <-ticker.C: case <-ticker.C:
} }
} }