mirror of
https://github.com/amitbet/vnc2video.git
synced 2025-08-15 19:23:26 +00:00
added video codecs, works with mjpeg & h264-ffmpeg
This commit is contained in:
parent
16f897ddfe
commit
a855abcb6c
84
encoders/dv8-enc.go
Normal file
84
encoders/dv8-enc.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"vnc2webm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DV8ImageEncoder struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
input io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *DV8ImageEncoder) Init(videoFileName string) {
|
||||||
|
fileExt := ".webm"
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
binary := "./ffmpeg"
|
||||||
|
cmd := exec.Command(binary,
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-vcodec", "ppm",
|
||||||
|
//"-r", strconv.Itoa(framerate),
|
||||||
|
"-r", "3",
|
||||||
|
//"-i", "pipe:0",
|
||||||
|
"-i", "-",
|
||||||
|
"-vcodec", "libvpx", //"libvpx",//"libvpx-vp9"//"libx264"
|
||||||
|
"-b:v", "2M",
|
||||||
|
"-threads", "8",
|
||||||
|
//"-speed", "0",
|
||||||
|
//"-lossless", "1", //for vpx
|
||||||
|
// "-tile-columns", "6",
|
||||||
|
//"-frame-parallel", "1",
|
||||||
|
// "-an", "-f", "webm",
|
||||||
|
"-cpu-used", "-16",
|
||||||
|
|
||||||
|
"-preset", "ultrafast",
|
||||||
|
"-deadline", "realtime",
|
||||||
|
//"-cpu-used", "-5",
|
||||||
|
"-maxrate", "2.5M",
|
||||||
|
"-bufsize", "10M",
|
||||||
|
"-g", "6",
|
||||||
|
|
||||||
|
//"-rc_lookahead", "16",
|
||||||
|
//"-profile", "0",
|
||||||
|
"-qmax", "51",
|
||||||
|
"-qmin", "11",
|
||||||
|
//"-slices", "4",
|
||||||
|
//"-vb", "2M",
|
||||||
|
|
||||||
|
videoFileName,
|
||||||
|
)
|
||||||
|
//cmd := exec.Command("/bin/echo")
|
||||||
|
|
||||||
|
//io.Copy(cmd.Stdout, os.Stdout)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
encInput, err := cmd.StdinPipe()
|
||||||
|
enc.input = encInput
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("can't get ffmpeg input pipe")
|
||||||
|
}
|
||||||
|
enc.cmd = cmd
|
||||||
|
}
|
||||||
|
func (enc *DV8ImageEncoder) Run() {
|
||||||
|
logger.Debugf("launching binary: %v", enc.cmd.Args)
|
||||||
|
err := enc.cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while launching ffmpeg:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *DV8ImageEncoder) Encode(img image.Image) {
|
||||||
|
err := encodePPM(enc.input, img)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while encoding image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *DV8ImageEncoder) Close() {
|
||||||
|
|
||||||
|
}
|
84
encoders/dv9-enc.go
Normal file
84
encoders/dv9-enc.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"vnc2webm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DV9ImageEncoder struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
input io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *DV9ImageEncoder) Init(videoFileName string) {
|
||||||
|
fileExt := ".webm"
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
binary := "./ffmpeg"
|
||||||
|
cmd := exec.Command(binary,
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-vcodec", "ppm",
|
||||||
|
//"-r", strconv.Itoa(framerate),
|
||||||
|
"-r", "3",
|
||||||
|
//"-i", "pipe:0",
|
||||||
|
"-i", "-",
|
||||||
|
"-vcodec", "libvpx-vp9", //"libvpx",//"libvpx-vp9"//"libx264"
|
||||||
|
"-b:v", "2M",
|
||||||
|
"-threads", "8",
|
||||||
|
//"-speed", "0",
|
||||||
|
//"-lossless", "1", //for vpx
|
||||||
|
// "-tile-columns", "6",
|
||||||
|
//"-frame-parallel", "1",
|
||||||
|
// "-an", "-f", "webm",
|
||||||
|
"-cpu-used", "-16",
|
||||||
|
|
||||||
|
"-preset", "ultrafast",
|
||||||
|
"-deadline", "realtime",
|
||||||
|
//"-cpu-used", "-5",
|
||||||
|
"-maxrate", "2.5M",
|
||||||
|
"-bufsize", "10M",
|
||||||
|
"-g", "6",
|
||||||
|
|
||||||
|
//"-rc_lookahead", "16",
|
||||||
|
//"-profile", "0",
|
||||||
|
"-qmax", "51",
|
||||||
|
"-qmin", "11",
|
||||||
|
//"-slices", "4",
|
||||||
|
//"-vb", "2M",
|
||||||
|
|
||||||
|
videoFileName,
|
||||||
|
)
|
||||||
|
//cmd := exec.Command("/bin/echo")
|
||||||
|
|
||||||
|
//io.Copy(cmd.Stdout, os.Stdout)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
encInput, err := cmd.StdinPipe()
|
||||||
|
enc.input = encInput
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("can't get ffmpeg input pipe")
|
||||||
|
}
|
||||||
|
enc.cmd = cmd
|
||||||
|
}
|
||||||
|
func (enc *DV9ImageEncoder) Run() {
|
||||||
|
logger.Debugf("launching binary: %v", enc.cmd.Args)
|
||||||
|
err := enc.cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while launching ffmpeg:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *DV9ImageEncoder) Encode(img image.Image) {
|
||||||
|
err := encodePPM(enc.input, img)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while encoding image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *DV9ImageEncoder) Close() {
|
||||||
|
|
||||||
|
}
|
43
encoders/image-enc.go
Normal file
43
encoders/image-enc.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodePPM(w io.Writer, img image.Image) error {
|
||||||
|
maxvalue := 255
|
||||||
|
size := img.Bounds()
|
||||||
|
// write ppm header
|
||||||
|
_, err := fmt.Fprintf(w, "P6\n%d %d\n%d\n", size.Dx(), size.Dy(), maxvalue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the bitmap
|
||||||
|
colModel := color.RGBAModel
|
||||||
|
row := make([]uint8, size.Dx()*3)
|
||||||
|
for y := size.Min.Y; y < size.Max.Y; y++ {
|
||||||
|
i := 0
|
||||||
|
for x := size.Min.X; x < size.Max.X; x++ {
|
||||||
|
color := colModel.Convert(img.At(x, y)).(color.RGBA)
|
||||||
|
row[i] = color.R
|
||||||
|
row[i+1] = color.G
|
||||||
|
row[i+2] = color.B
|
||||||
|
i += 3
|
||||||
|
}
|
||||||
|
if _, err := w.Write(row); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageEncoder interface {
|
||||||
|
Init(string)
|
||||||
|
Run()
|
||||||
|
Encode(image.Image)
|
||||||
|
Close()
|
||||||
|
}
|
56
encoders/mjpeg-enc.go
Normal file
56
encoders/mjpeg-enc.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"strings"
|
||||||
|
"vnc2webm/logger"
|
||||||
|
|
||||||
|
"github.com/icza/mjpeg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MJPegImageEncoder struct {
|
||||||
|
avWriter mjpeg.AviWriter
|
||||||
|
Quality int
|
||||||
|
Framerate int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *MJPegImageEncoder) Init(videoFileName string) {
|
||||||
|
fileExt := ".avi"
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
if enc.Framerate <= 0 {
|
||||||
|
enc.Framerate = 5
|
||||||
|
}
|
||||||
|
avWriter, err := mjpeg.New(videoFileName, 1024, 768, enc.Framerate)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error during mjpeg init: ", err)
|
||||||
|
}
|
||||||
|
enc.avWriter = avWriter
|
||||||
|
}
|
||||||
|
func (enc *MJPegImageEncoder) Run() {
|
||||||
|
}
|
||||||
|
func (enc *MJPegImageEncoder) Encode(img image.Image) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
jOpts := &jpeg.Options{Quality: enc.Quality}
|
||||||
|
if enc.Quality <= 0 {
|
||||||
|
jOpts = nil
|
||||||
|
}
|
||||||
|
err := jpeg.Encode(buf, img, jOpts)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error while creating jpeg: ", err)
|
||||||
|
}
|
||||||
|
err = enc.avWriter.AddFrame(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error while adding frame to mjpeg: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func (enc *MJPegImageEncoder) Close() {
|
||||||
|
err := enc.avWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error while closing mjpeg: ", err)
|
||||||
|
}
|
||||||
|
}
|
92
encoders/x264-enc.go
Normal file
92
encoders/x264-enc.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"vnc2webm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type X264ImageEncoder struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
binaryPath string
|
||||||
|
input io.WriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *X264ImageEncoder) Init(videoFileName string) {
|
||||||
|
fileExt := ".mp4"
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
//binary := "./ffmpeg"
|
||||||
|
cmd := exec.Command(enc.binaryPath,
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-vcodec", "ppm",
|
||||||
|
//"-r", strconv.Itoa(framerate),
|
||||||
|
"-r", "4",
|
||||||
|
//"-i", "pipe:0",
|
||||||
|
"-i", "-",
|
||||||
|
"-vcodec", "libx264", //"libvpx",//"libvpx-vp9"//"libx264"
|
||||||
|
"-b:v", "2M",
|
||||||
|
"-threads", "8",
|
||||||
|
//"-speed", "0",
|
||||||
|
//"-lossless", "1", //for vpx
|
||||||
|
// "-tile-columns", "6",
|
||||||
|
//"-frame-parallel", "1",
|
||||||
|
// "-an", "-f", "webm",
|
||||||
|
"-cpu-used", "-16",
|
||||||
|
|
||||||
|
"-preset", "ultrafast",
|
||||||
|
"-deadline", "realtime",
|
||||||
|
//"-cpu-used", "-5",
|
||||||
|
"-maxrate", "2.5M",
|
||||||
|
"-bufsize", "10M",
|
||||||
|
"-g", "6",
|
||||||
|
|
||||||
|
//"-rc_lookahead", "16",
|
||||||
|
//"-profile", "0",
|
||||||
|
"-qmax", "51",
|
||||||
|
"-qmin", "11",
|
||||||
|
//"-slices", "4",
|
||||||
|
//"-vb", "2M",
|
||||||
|
|
||||||
|
videoFileName,
|
||||||
|
)
|
||||||
|
//cmd := exec.Command("/bin/echo")
|
||||||
|
|
||||||
|
//io.Copy(cmd.Stdout, os.Stdout)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
encInput, err := cmd.StdinPipe()
|
||||||
|
enc.input = encInput
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("can't get ffmpeg input pipe")
|
||||||
|
}
|
||||||
|
enc.cmd = cmd
|
||||||
|
}
|
||||||
|
func (enc *X264ImageEncoder) Run(encoderFilePath string, videoFileName string) {
|
||||||
|
if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) {
|
||||||
|
logger.Error("encoder file doesn't exist in path:", encoderFilePath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.binaryPath = encoderFilePath
|
||||||
|
enc.Init(videoFileName)
|
||||||
|
logger.Infof("launching binary: %v", enc.cmd)
|
||||||
|
err := enc.cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *X264ImageEncoder) Encode(img image.Image) {
|
||||||
|
err := encodePPM(enc.input, img)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while encoding image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (enc *X264ImageEncoder) Close() {
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@ package vnc2webm
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"image"
|
"image"
|
||||||
|
"image/draw"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,6 +79,10 @@ var bPool = sync.Pool{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Renderer interface {
|
||||||
|
SetTargetImage(draw.Image)
|
||||||
|
}
|
||||||
|
|
||||||
// Encoding represents interface for vnc encoding
|
// Encoding represents interface for vnc encoding
|
||||||
type Encoding interface {
|
type Encoding interface {
|
||||||
Type() EncodingType
|
Type() EncodingType
|
||||||
|
@ -12,8 +12,6 @@ import (
|
|||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"vnc2webm/logger"
|
"vnc2webm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,9 +77,9 @@ func getTightColor(c io.Reader, pf *PixelFormat) (*color.RGBA64, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rgb := color.RGBA64{
|
rgb := color.RGBA64{
|
||||||
R: uint16(tbytes[0]), //byte(col >> pf.RedShift & int32(pf.RedMax)),
|
R: uint16(tbytes[0]),
|
||||||
G: uint16(tbytes[1]), //byte(col >> pf.GreenShift & int32(pf.GreenMax)),
|
G: uint16(tbytes[1]),
|
||||||
B: uint16(tbytes[2]), //byte(col >> pf.BlueShift & int32(pf.BlueMax)),
|
B: uint16(tbytes[2]),
|
||||||
A: uint16(1),
|
A: uint16(1),
|
||||||
}
|
}
|
||||||
return &rgb, nil
|
return &rgb, nil
|
||||||
@ -114,24 +112,9 @@ func getTightColor(c io.Reader, pf *PixelFormat) (*color.RGBA64, error) {
|
|||||||
B: uint16((pixel >> pf.BlueShift) & uint32(pf.BlueMax)),
|
B: uint16((pixel >> pf.BlueShift) & uint32(pf.BlueMax)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// else {
|
|
||||||
// *clr = clr.cm[pixel]
|
|
||||||
// clr.cmIndex = pixel
|
|
||||||
// }
|
|
||||||
return &rgb, nil
|
return &rgb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func getTightColor(bytes []byte, pf *PixelFormat) color.RGBA {
|
|
||||||
// col := (int32(bytes[0])&0xff)<<16 | (int32(bytes[1])&0xff)<<8 | int32(bytes[2])&0xff
|
|
||||||
// rgb := color.RGBA{
|
|
||||||
// R: byte(col >> pf.RedShift & int32(pf.RedMax)),
|
|
||||||
// G: byte(col >> pf.GreenShift & int32(pf.GreenMax)),
|
|
||||||
// B: byte(col >> pf.BlueShift & int32(pf.BlueMax)),
|
|
||||||
// A: byte(1),
|
|
||||||
// }
|
|
||||||
// return rgb
|
|
||||||
// }
|
|
||||||
|
|
||||||
func calcTightBytePerPixel(pf *PixelFormat) int {
|
func calcTightBytePerPixel(pf *PixelFormat) int {
|
||||||
bytesPerPixel := int(pf.BPP / 8)
|
bytesPerPixel := int(pf.BPP / 8)
|
||||||
|
|
||||||
@ -153,16 +136,19 @@ func (enc *TightEncoding) resetDecoders(compControl uint8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (enc *TightEncoding) SetTargetImage(img draw.Image) {
|
||||||
|
enc.Image = img
|
||||||
|
}
|
||||||
|
|
||||||
var counter int = 0
|
var counter int = 0
|
||||||
|
|
||||||
func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {
|
func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {
|
||||||
|
|
||||||
var out *os.File
|
|
||||||
var err error
|
var err error
|
||||||
////////////
|
////////////
|
||||||
if counter > 40 {
|
// if counter > 40 {
|
||||||
os.Exit(1)
|
// os.Exit(1)
|
||||||
}
|
// }
|
||||||
////////////
|
////////////
|
||||||
pixelFmt := c.PixelFormat()
|
pixelFmt := c.PixelFormat()
|
||||||
bytesPixel := calcTightBytePerPixel(&pixelFmt)
|
bytesPixel := calcTightBytePerPixel(&pixelFmt)
|
||||||
@ -180,15 +166,16 @@ func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {
|
|||||||
compctl, err := ReadUint8(c)
|
compctl, err := ReadUint8(c)
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
if out == nil {
|
// var out *os.File
|
||||||
out, err = os.Create("./output" + strconv.Itoa(counter) + "-" + strconv.Itoa(int(compctl)) + ".jpg")
|
// if out == nil {
|
||||||
if err != nil {
|
// out, err = os.Create("./output" + strconv.Itoa(counter) + "-" + strconv.Itoa(int(compctl)) + ".jpg")
|
||||||
fmt.Println(err)
|
// if err != nil {
|
||||||
os.Exit(1)
|
// fmt.Println(err)
|
||||||
}
|
// os.Exit(1)
|
||||||
}
|
// }
|
||||||
defer func() { counter++ }()
|
// }
|
||||||
defer jpeg.Encode(out, enc.Image, nil)
|
// defer func() { counter++ }()
|
||||||
|
// defer jpeg.Encode(out, enc.Image, nil)
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -213,15 +200,7 @@ func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//logger.Debugf("bytesPixel= %d, compctl= %d, color= %v", bytesPixel, compctl, rectColor)
|
|
||||||
|
|
||||||
//imgRect := image.Rect(0, 0, int(c.Width()), int(c.Height()))
|
|
||||||
// if enc.Image == nil {
|
|
||||||
// enc.Image = image.NewRGBA(imgRect)
|
|
||||||
// }
|
|
||||||
|
|
||||||
c1 := color.RGBAModel.Convert(rectColor).(color.RGBA)
|
c1 := color.RGBAModel.Convert(rectColor).(color.RGBA)
|
||||||
|
|
||||||
dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA)
|
dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA)
|
||||||
var x, y int
|
var x, y int
|
||||||
|
|
||||||
@ -232,14 +211,8 @@ func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error {
|
|||||||
dst.Pix[offset+1] = c1.G
|
dst.Pix[offset+1] = c1.G
|
||||||
dst.Pix[offset+2] = c1.B
|
dst.Pix[offset+2] = c1.B
|
||||||
dst.Pix[offset+3] = c1.A
|
dst.Pix[offset+3] = c1.A
|
||||||
|
|
||||||
//dst.Set(int(x), int(y), c1)
|
|
||||||
//dst.Pix[y*uint16(dst.Bounds().Max.Y)+x] = []uint8{rectColor.R, rectColor.G, rectColor.B}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enc.Image = dst
|
|
||||||
|
|
||||||
//draw.Draw(dst, imgRect, &image.Uniform{rectColor}, image.ZP, draw.Src)
|
|
||||||
|
|
||||||
if bytesPixel != 3 {
|
if bytesPixel != 3 {
|
||||||
return fmt.Errorf("non tight bytesPerPixel format, should be 3 bytes")
|
return fmt.Errorf("non tight bytesPerPixel format, should be 3 bytes")
|
||||||
@ -337,27 +310,7 @@ func (enc *TightEncoding) handleTightFilters(compCtl uint8, pixelFmt *PixelForma
|
|||||||
}
|
}
|
||||||
//logger.Errorf("handleTightFilters: got tight data: %v", tightBytes)
|
//logger.Errorf("handleTightFilters: got tight data: %v", tightBytes)
|
||||||
|
|
||||||
myImg := enc.Image.(draw.Image)
|
enc.drawTightPalette(rect, palette, tightBytes)
|
||||||
bytePos := 0
|
|
||||||
bitPos := 0
|
|
||||||
var palettePos int
|
|
||||||
for i := 0; i < int(rect.Height); i++ {
|
|
||||||
for j := 0; j < int(rect.Width); j++ {
|
|
||||||
if len(palette) == 2 {
|
|
||||||
currByte := tightBytes[bytePos]
|
|
||||||
palettePos = int(currByte&byte(math.Pow(2.0, float64(bitPos)))) >> uint(bitPos)
|
|
||||||
//logger.Debugf("palletPos=%d, bitpos=%d, bytepos=%d", palettePos, bitPos, bytePos)
|
|
||||||
bytePos = bytePos + int((bitPos+1.0)/8.0)
|
|
||||||
bitPos = (bitPos + 1) % 8
|
|
||||||
//logger.Debugf("next: bitpos=%d, bytepos=%d", bitPos, bytePos)
|
|
||||||
} else {
|
|
||||||
palettePos = int(tightBytes[bytePos])
|
|
||||||
bytePos++
|
|
||||||
}
|
|
||||||
myImg.Set(int(rect.X)+j, int(rect.Y)+i, palette[palettePos])
|
|
||||||
//logger.Debugf("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//enc.Image = myImg
|
//enc.Image = myImg
|
||||||
case TightFilterGradient: //GRADIENT_FILTER
|
case TightFilterGradient: //GRADIENT_FILTER
|
||||||
logger.Debugf("----GRADIENT_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter)
|
logger.Debugf("----GRADIENT_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter)
|
||||||
@ -387,6 +340,29 @@ func (enc *TightEncoding) handleTightFilters(compCtl uint8, pixelFmt *PixelForma
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
func (enc *TightEncoding) drawTightPalette(rect *Rectangle, palette color.Palette, tightBytes []byte) {
|
||||||
|
myImg := enc.Image.(draw.Image)
|
||||||
|
bytePos := 0
|
||||||
|
bitPos := 0
|
||||||
|
var palettePos int
|
||||||
|
for i := 0; i < int(rect.Height); i++ {
|
||||||
|
for j := 0; j < int(rect.Width); j++ {
|
||||||
|
if len(palette) == 2 {
|
||||||
|
currByte := tightBytes[bytePos]
|
||||||
|
palettePos = int(currByte&byte(math.Pow(2.0, float64(bitPos)))) >> uint(bitPos)
|
||||||
|
//logger.Debugf("palletPos=%d, bitpos=%d, bytepos=%d", palettePos, bitPos, bytePos)
|
||||||
|
bytePos = bytePos + int((bitPos+1.0)/8.0)
|
||||||
|
bitPos = (bitPos + 1) % 8
|
||||||
|
//logger.Debugf("next: bitpos=%d, bytepos=%d", bitPos, bytePos)
|
||||||
|
} else {
|
||||||
|
palettePos = int(tightBytes[bytePos])
|
||||||
|
bytePos++
|
||||||
|
}
|
||||||
|
myImg.Set(int(rect.X)+j, int(rect.Y)+i, palette[palettePos])
|
||||||
|
//logger.Debugf("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func (enc *TightEncoding) decodeGradData(rect *Rectangle, buffer []byte) {
|
func (enc *TightEncoding) decodeGradData(rect *Rectangle, buffer []byte) {
|
||||||
|
|
||||||
logger.Debugf("putting gradient size: %v on image: %v", rect, enc.Image.Bounds())
|
logger.Debugf("putting gradient size: %v on image: %v", rect, enc.Image.Bounds())
|
||||||
|
@ -3,11 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"image"
|
"image"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
vnc "vnc2webm"
|
vnc "vnc2webm"
|
||||||
|
"vnc2webm/encoders"
|
||||||
"vnc2webm/logger"
|
"vnc2webm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ func main() {
|
|||||||
// Establish TCP connection to VNC server.
|
// Establish TCP connection to VNC server.
|
||||||
nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second)
|
nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error connecting to VNC host. %v", err)
|
logger.Fatalf("Error connecting to VNC host. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debugf("starting up the client, connecting to: %s", os.Args[1])
|
logger.Debugf("starting up the client, connecting to: %s", os.Args[1])
|
||||||
@ -41,31 +41,54 @@ func main() {
|
|||||||
|
|
||||||
cc, err := vnc.Connect(context.Background(), nc, ccfg)
|
cc, err := vnc.Connect(context.Background(), nc, ccfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error negotiating connection to VNC host. %v", err)
|
logger.Fatalf("Error negotiating connection to VNC host. %v", err)
|
||||||
}
|
}
|
||||||
|
// out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
//vcodec := &encoders.MJPegImageEncoder{Quality: 60, Framerate: 6}
|
||||||
|
vcodec := &encoders.X264ImageEncoder{}
|
||||||
|
counter := 0
|
||||||
|
//vcodec.Init("./output" + strconv.Itoa(counter))
|
||||||
|
go vcodec.Run("./ffmpeg", "./output.mp4")
|
||||||
|
|
||||||
|
screenImage := image.NewRGBA(image.Rect(0, 0, int(cc.Width()), int(cc.Height())))
|
||||||
|
for _, enc := range ccfg.Encodings {
|
||||||
|
myRenderer, ok := enc.(vnc.Renderer)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
myRenderer.SetTargetImage(screenImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// var out *os.File
|
||||||
|
|
||||||
logger.Debugf("connected to: %s", os.Args[1])
|
logger.Debugf("connected to: %s", os.Args[1])
|
||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
cc.SetEncodings([]vnc.EncodingType{vnc.EncTight})
|
cc.SetEncodings([]vnc.EncodingType{vnc.EncTight})
|
||||||
rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height()))
|
//rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height()))
|
||||||
screenImage := image.NewRGBA64(rect)
|
//screenImage := image.NewRGBA64(rect)
|
||||||
// Process messages coming in on the ServerMessage channel.
|
// Process messages coming in on the ServerMessage channel.
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <-errorCh:
|
case err := <-errorCh:
|
||||||
panic(err)
|
panic(err)
|
||||||
case msg := <-cchClient:
|
case msg := <-cchClient:
|
||||||
log.Printf("Received client message type:%v msg:%v\n", msg.Type(), msg)
|
logger.Debugf("Received client message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
case msg := <-cchServer:
|
case msg := <-cchServer:
|
||||||
log.Printf("Received server message type:%v msg:%v\n", msg.Type(), msg)
|
logger.Debugf("Received server message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
myRenderer, ok := msg.(vnc.Renderer)
|
|
||||||
|
|
||||||
if ok {
|
// out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg")
|
||||||
err = myRenderer.Render(screenImage)
|
// if err != nil {
|
||||||
if err != nil {
|
// fmt.Println(err)
|
||||||
log.Printf("Received server message type:%v msg:%v\n", msg.Type(), msg)
|
// os.Exit(1)
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
counter++
|
||||||
|
//jpeg.Encode(out, screenImage, nil)
|
||||||
|
vcodec.Encode(screenImage)
|
||||||
reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()}
|
reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()}
|
||||||
reqMsg.Write(cc)
|
reqMsg.Write(cc)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
vnc "vnc2webm"
|
vnc "vnc2webm"
|
||||||
|
"vnc2webm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
@ -189,7 +189,7 @@ func (auth *AuthVNCHTTP) Auth(c vnc.Conn) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get auth data: %s", err.Error())
|
return fmt.Errorf("failed to get auth data: %s", err.Error())
|
||||||
}
|
}
|
||||||
log.Printf("http auth: %s\n", buf.Bytes())
|
logger.Infof("http auth: %s\n", buf.Bytes())
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
data := strings.Split(buf.String(), " ")
|
data := strings.Split(buf.String(), " ")
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
@ -223,12 +223,12 @@ func (*AuthVNCHTTP) SubType() vnc.SecuritySubType {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go func() {
|
go func() {
|
||||||
log.Println(http.ListenAndServe(":6060", nil))
|
logger.Info(http.ListenAndServe(":6060", nil))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", ":6900")
|
ln, err := net.Listen("tcp", ":6900")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error listen. %v", err)
|
logger.Fatalf("Error listen. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
schClient := make(chan vnc.ClientMessage)
|
schClient := make(chan vnc.ClientMessage)
|
||||||
|
@ -4,17 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"log"
|
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
vnc "vnc2webm"
|
vnc "vnc2webm"
|
||||||
|
"vnc2webm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ln, err := net.Listen("tcp", ":5900")
|
ln, err := net.Listen("tcp", ":5900")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error listen. %v", err)
|
logger.Fatalf("Error listen. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chServer := make(chan vnc.ClientMessage)
|
chServer := make(chan vnc.ClientMessage)
|
||||||
@ -50,12 +50,12 @@ func main() {
|
|||||||
case msg := <-chClient:
|
case msg := <-chClient:
|
||||||
switch msg.Type() {
|
switch msg.Type() {
|
||||||
default:
|
default:
|
||||||
log.Printf("11 Received message type:%v msg:%v\n", msg.Type(), msg)
|
logger.Debugf("11 Received message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
}
|
}
|
||||||
case msg := <-chServer:
|
case msg := <-chServer:
|
||||||
switch msg.Type() {
|
switch msg.Type() {
|
||||||
default:
|
default:
|
||||||
log.Printf("22 Received message type:%v msg:%v\n", msg.Type(), msg)
|
logger.Debugf("22 Received message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package logger
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var simpleLogger = SimpleLogger{LogLevelDebug}
|
var simpleLogger = SimpleLogger{LogLevelInfo}
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Debug(v ...interface{})
|
Debug(v ...interface{})
|
||||||
|
10
messages.go
10
messages.go
@ -3,7 +3,6 @@ package vnc2webm
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/draw"
|
|
||||||
"vnc2webm/logger"
|
"vnc2webm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,10 +82,6 @@ type ServerMessage interface {
|
|||||||
Supported(Conn) bool
|
Supported(Conn) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Renderer interface {
|
|
||||||
Render(draw.Image) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
||||||
type FramebufferUpdate struct {
|
type FramebufferUpdate struct {
|
||||||
_ [1]byte // pad
|
_ [1]byte // pad
|
||||||
@ -99,11 +94,6 @@ func (msg *FramebufferUpdate) String() string {
|
|||||||
return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects)
|
return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *FramebufferUpdate) Render(draw.Image) error {
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *FramebufferUpdate) Supported(c Conn) bool {
|
func (msg *FramebufferUpdate) Supported(c Conn) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user