diff --git a/encoding_cursor.go b/encoding_cursor.go index fae0281..012f270 100644 --- a/encoding_cursor.go +++ b/encoding_cursor.go @@ -20,6 +20,10 @@ func (enc *CursorPseudoEncoding) SetTargetImage(img draw.Image) { enc.Image = img } +func (enc *CursorPseudoEncoding) Reset() error { + return nil +} + func (*CursorPseudoEncoding) Type() EncodingType { return EncCursorPseudo } func (enc *CursorPseudoEncoding) Read(c Conn, rect *Rectangle) error { diff --git a/encoding_hextile.go b/encoding_hextile.go index a0f7f95..6004ccc 100644 --- a/encoding_hextile.go +++ b/encoding_hextile.go @@ -1,8 +1,6 @@ package vnc2video import ( - "encoding/binary" - "errors" "image" "image/color" "image/draw" @@ -24,51 +22,32 @@ type HextileEncoding struct { Image draw.Image } -// Read unmarshal color from conn -func ReadColor(c io.Reader, pf *PixelFormat) (*color.RGBA, error) { - if pf.TrueColor == 0 { - return nil, errors.New("support for non true color formats was not implemented") - } - order := pf.order() - var pixel uint32 - - switch pf.BPP { - case 8: - var px uint8 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - case 16: - var px uint16 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - case 32: - var px uint32 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - } - - rgb := color.RGBA{ - R: uint8((pixel >> pf.RedShift) & uint32(pf.RedMax)), - G: uint8((pixel >> pf.GreenShift) & uint32(pf.GreenMax)), - B: uint8((pixel >> pf.BlueShift) & uint32(pf.BlueMax)), - A: 1, - } - - return &rgb, nil +func (enc *HextileEncoding) SetTargetImage(img draw.Image) { + enc.Image = img } -func (z *HextileEncoding) Type() int32 { - return 5 +func (*HextileEncoding) Supported(Conn) bool { + return true } + +func (enc *HextileEncoding) Reset() error { + //enc.decoders = make([]io.Reader, 4) + //enc.decoderBuffs = make([]*bytes.Buffer, 4) + return nil +} + +func (z *HextileEncoding) Type() EncodingType { + return EncHextile +} + func (z *HextileEncoding) WriteTo(w io.Writer) (n int, err error) { return w.Write(z.bytes) } + +func (enc *HextileEncoding) Write(c Conn, rect *Rectangle) error { + return nil +} + func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { //func (z *HextileEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { //bytesPerPixel := int(r.PixelFormat().BPP) / 8 @@ -83,7 +62,7 @@ func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { // defer func() { // z.bytes = r.EndByteCollection() // }() - + logger.Debugf("HextileEncoding.Read: got hextile rect: %v", rect) for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 { th := 16 if rect.Y+rect.Height-ty < 16 { @@ -106,19 +85,31 @@ func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { if (subencoding & HextileRaw) != 0 { rawEnc := r.GetEncInstance(EncRaw) - rawEnc.Read(r, &Rectangle{0, 0, uint16(tw), uint16(th), EncRaw, rawEnc}) - //ReadBytes(tw*th*bytesPerPixel, r) + rawEnc.Read(r, &Rectangle{X: uint16(tx), Y: uint16(ty), Width: uint16(tw), Height: uint16(th), EncType: EncRaw, Enc: rawEnc}) + //ReadBytes(tw*th*int(pf.BPP)/8, r) continue } if (subencoding & HextileBackgroundSpecified) != 0 { //ReadBytes(int(bytesPerPixel), r) bgCol, err = ReadColor(r, &pf) - rBounds := image.Rectangle{Min: image.Point{int(tx), int(ty)}, Max: image.Point{int(tw), int(th)}} - FillRect(z.Image, &rBounds, bgCol) + if err != nil { + logger.Errorf("HextileEncoding.Read: error in hextile bg color reader: %v", err) + return err + } + + //logger.Debugf("%v %v", rBounds, bgCol) } + rBounds := image.Rectangle{Min: image.Point{int(tx), int(ty)}, Max: image.Point{int(tx) + int(tw), int(ty) + int(th)}} + logger.Debugf("filling background rect: %v, col: %v", rBounds, bgCol) + FillRect(z.Image, &rBounds, bgCol) + if (subencoding & HextileForegroundSpecified) != 0 { fgCol, err = ReadColor(r, &pf) + if err != nil { + logger.Errorf("HextileEncoding.Read: error in hextile fg color reader: %v", err) + return err + } } if (subencoding & HextileAnySubrects) == 0 { //logger.Debug("hextile reader: no Subrects") @@ -136,7 +127,7 @@ func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { if colorSpecified { color, err = ReadColor(r, &pf) if err != nil { - logger.Error("Hextile decoder: problem reading color from connection: ", err) + logger.Error("HextileEncoding.Read: problem reading color from connection: ", err) return err } } else { @@ -146,20 +137,21 @@ func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { fgCol = color dimensions, err = ReadUint8(r) // bits 7-4 for x, bits 3-0 for y if err != nil { - logger.Error("Hextile decoder: problem reading dimensions from connection: ", err) + logger.Error("HextileEncoding.Read: problem reading dimensions from connection: ", err) return err } subtileX := dimensions >> 4 & 0x0f subtileY := dimensions & 0x0f dimensions, err = ReadUint8(r) // bits 7-4 for w, bits 3-0 for h if err != nil { - logger.Error("Hextile decoder: problem reading 2nd dimensions from connection: ", err) + logger.Error("HextileEncoding.Read: problem reading 2nd dimensions from connection: ", err) return err } subtileWidth := 1 + (dimensions >> 4 & 0x0f) subtileHeight := 1 + (dimensions & 0x0f) - subrectBounds := image.Rectangle{Min: image.Point{int(tx) + int(subtileX), int(ty) + int(subtileY)}, Max: image.Point{int(subtileWidth), int(subtileHeight)}} + subrectBounds := image.Rectangle{Min: image.Point{int(tx) + int(subtileX), int(ty) + int(subtileY)}, Max: image.Point{int(tx) + int(subtileX) + int(subtileWidth), int(ty) + int(subtileY) + int(subtileHeight)}} FillRect(z.Image, &subrectBounds, color) + //logger.Debugf("%v", subrectBounds) } } } diff --git a/encoding_util.go b/encoding_util.go index f989f46..521318d 100644 --- a/encoding_util.go +++ b/encoding_util.go @@ -1,19 +1,57 @@ package vnc2video import ( + "encoding/binary" + "errors" "image" "image/color" "image/draw" + "io" ) func FillRect(img draw.Image, rect *image.Rectangle, c color.Color) { - for x := 0; x < rect.Max.X; x++ { - for y := 0; y < rect.Max.Y; y++ { + for x := rect.Min.X; x < rect.Max.X; x++ { + for y := rect.Min.Y; y < rect.Max.Y; y++ { img.Set(x, y, c) } } } -func DrawLine(img draw.Image, rect *image.Rectangle, c color.Color) { +// Read unmarshal color from conn +func ReadColor(c io.Reader, pf *PixelFormat) (*color.RGBA, error) { + if pf.TrueColor == 0 { + return nil, errors.New("support for non true color formats was not implemented") + } + order := pf.order() + var pixel uint32 + switch pf.BPP { + case 8: + var px uint8 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + case 16: + var px uint16 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + case 32: + var px uint32 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + } + + rgb := color.RGBA{ + R: uint8((pixel >> pf.RedShift) & uint32(pf.RedMax)), + G: uint8((pixel >> pf.GreenShift) & uint32(pf.GreenMax)), + B: uint8((pixel >> pf.BlueShift) & uint32(pf.BlueMax)), + A: 1, + } + + return &rgb, nil } diff --git a/example/client/main.go b/example/client/main.go index 91f0e9f..fd94c4a 100644 --- a/example/client/main.go +++ b/example/client/main.go @@ -1,102 +1,108 @@ -package main - -import ( - "context" - "image" - "net" - "os" - "time" - vnc "vnc2video" - "vnc2video/encoders" - "vnc2video/logger" -) - -func main() { - - // Establish TCP connection to VNC server. - nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second) - if err != nil { - logger.Fatalf("Error connecting to VNC host. %v", err) - } - - logger.Debugf("starting up the client, connecting to: %s", os.Args[1]) - // Negotiate connection with the server. - cchServer := make(chan vnc.ServerMessage) - cchClient := make(chan vnc.ClientMessage) - errorCh := make(chan error) - - ccfg := &vnc.ClientConfig{ - SecurityHandlers: []vnc.SecurityHandler{ - //&vnc.ClientAuthATEN{Username: []byte(os.Args[2]), Password: []byte(os.Args[3])} - &vnc.ClientAuthVNC{Password: []byte("12345")}, - &vnc.ClientAuthNone{}, - }, - PixelFormat: vnc.PixelFormat32bit, - ClientMessageCh: cchClient, - ServerMessageCh: cchServer, - Messages: vnc.DefaultServerMessages, - Encodings: []vnc.Encoding{&vnc.RawEncoding{}, &vnc.TightEncoding{}}, - ErrorCh: errorCh, - } - - cc, err := vnc.Connect(context.Background(), nc, ccfg) - if err != nil { - 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{} - //vcodec := &encoders.DV8ImageEncoder{} - //vcodec := &encoders.DV9ImageEncoder{} - - //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]) - defer cc.Close() - - cc.SetEncodings([]vnc.EncodingType{vnc.EncTight}) - //rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height())) - //screenImage := image.NewRGBA64(rect) - // Process messages coming in on the ServerMessage channel. - for { - select { - case err := <-errorCh: - panic(err) - case msg := <-cchClient: - logger.Debugf("Received client message type:%v msg:%v\n", msg.Type(), msg) - case msg := <-cchServer: - logger.Debugf("Received server message type:%v msg:%v\n", msg.Type(), msg) - - // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") - // if err != nil { - // fmt.Println(err) - // os.Exit(1) - // } - if msg.Type() == vnc.FramebufferUpdateMsgType { - //counter++ - //jpeg.Encode(out, screenImage, nil) - vcodec.Encode(screenImage) - reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()} - //cc.ResetAllEncodings() - reqMsg.Write(cc) - } - } - } - //cc.Wait() -} +package main + +import ( + "context" + "image" + "net" + "os" + "time" + vnc "vnc2video" + "vnc2video/encoders" + "vnc2video/logger" +) + +func main() { + + // Establish TCP connection to VNC server. + nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second) + if err != nil { + logger.Fatalf("Error connecting to VNC host. %v", err) + } + + logger.Debugf("starting up the client, connecting to: %s", os.Args[1]) + // Negotiate connection with the server. + cchServer := make(chan vnc.ServerMessage) + cchClient := make(chan vnc.ClientMessage) + errorCh := make(chan error) + + ccfg := &vnc.ClientConfig{ + SecurityHandlers: []vnc.SecurityHandler{ + //&vnc.ClientAuthATEN{Username: []byte(os.Args[2]), Password: []byte(os.Args[3])} + &vnc.ClientAuthVNC{Password: []byte("12345")}, + &vnc.ClientAuthNone{}, + }, + PixelFormat: vnc.PixelFormat32bit, + ClientMessageCh: cchClient, + ServerMessageCh: cchServer, + Messages: vnc.DefaultServerMessages, + Encodings: []vnc.Encoding{ + &vnc.RawEncoding{}, + //&vnc.TightEncoding{}, + &vnc.HextileEncoding{}, + //&vnc.CursorPseudoEncoding{}, + }, + ErrorCh: errorCh, + } + + cc, err := vnc.Connect(context.Background(), nc, ccfg) + if err != nil { + 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{} + //vcodec := &encoders.DV8ImageEncoder{} + //vcodec := &encoders.DV9ImageEncoder{} + + //counter := 0 + //vcodec.Init("./output" + strconv.Itoa(counter)) + //go vcodec.Run("./ffmpeg", "./output.mp4") + go vcodec.Run("/Users/amitbet/Dropbox/go/src/vnc2webm/example/file-reader/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]) + defer cc.Close() + + cc.SetEncodings([]vnc.EncodingType{vnc.EncHextile}) + //rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height())) + //screenImage := image.NewRGBA64(rect) + // Process messages coming in on the ServerMessage channel. + for { + select { + case err := <-errorCh: + panic(err) + case msg := <-cchClient: + logger.Debugf("Received client message type:%v msg:%v\n", msg.Type(), msg) + case msg := <-cchServer: + logger.Debugf("Received server message type:%v msg:%v\n", msg.Type(), msg) + + // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") + // if err != nil { + // fmt.Println(err) + // os.Exit(1) + // } + if msg.Type() == vnc.FramebufferUpdateMsgType { + //counter++ + //jpeg.Encode(out, screenImage, nil) + vcodec.Encode(screenImage) + reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()} + //cc.ResetAllEncodings() + reqMsg.Write(cc) + } + } + } + //cc.Wait() +}