diff --git a/encoding_tight.go b/encoding_tight.go index ae127a9..9c13de8 100644 --- a/encoding_tight.go +++ b/encoding_tight.go @@ -249,7 +249,9 @@ func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error { if err != nil { logger.Error("problem while decoding jpeg:", err) } - enc.Image = img + dest := enc.Image.(draw.Image) + draw.Draw(dest, dest.Bounds(), img, image.Point{int(rect.X), int(rect.Y)}, draw.Src) + return nil default: diff --git a/example/client/main.go b/example/client/main.go index 571e2ec..dca9d67 100644 --- a/example/client/main.go +++ b/example/client/main.go @@ -93,9 +93,10 @@ func main() { //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() + //cc.Wait() } diff --git a/example/file-reader/main.go b/example/file-reader/main.go new file mode 100644 index 0000000..56df755 --- /dev/null +++ b/example/file-reader/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "image" + "os" + vnc "vnc2webm" + "vnc2webm/encoders" + "vnc2webm/logger" + "path/filepath" +) + +func main() { + + if len(os.Args) <= 1 { + logger.Errorf("please provide a fbs file name") + return + } + if _, err := os.Stat(os.Args[1]); os.IsNotExist(err) { + logger.Errorf("File doesn't exist", err) + return + } + encs := []vnc.Encoding{ + &vnc.RawEncoding{}, + &vnc.TightEncoding{}, + } + + fbs, err := vnc.NewFbsConn( + os.Args[1], + encs, + ) + if err != nil { + logger.Error("failed to open fbs reader:", err) + //return nil, err + } + + //launch video encoding process: + vcodec := &encoders.X264ImageEncoder{} + //vcodec := &encoders.DV8ImageEncoder{} + //vcodec := &encoders.DV9ImageEncoder{} + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + logger.Debugf("current dir: %s", dir) + go vcodec.Run("./ffmpeg", "./output.mp4") + + screenImage := image.NewRGBA(image.Rect(0, 0, int(fbs.Width()), int(fbs.Height()))) + for _, enc := range encs { + myRenderer, ok := enc.(vnc.Renderer) + + if ok { + myRenderer.SetTargetImage(screenImage) + } + } + + + msgReader := vnc.NewFBSPlayHelper(fbs) + + //loop over all messages, feed images to video codec: + for { + msgReader.ReadFbsMessage() + vcodec.Encode(screenImage) + } +} diff --git a/fbs-connection.go b/fbs-connection.go new file mode 100644 index 0000000..11e5cd6 --- /dev/null +++ b/fbs-connection.go @@ -0,0 +1,201 @@ +package vnc2webm + +import ( + "encoding/binary" + "net" + "vnc2webm/logger" + + "io" + "time" +) + +// Conn represents vnc conection +type FbsConn struct { + FbsReader + + protocol string + //c net.IServerConn + //config *ClientConfig + colorMap ColorMap + + // Encodings supported by the client. This should not be modified + // directly. Instead, SetEncodings should be used. + encodings []Encoding + + // Height of the frame buffer in pixels, sent from the server. + fbHeight uint16 + + // Width of the frame buffer in pixels, sent from the server. + fbWidth uint16 + desktopName string + // The pixel format associated with the connection. This shouldn't + // be modified. If you wish to set a new pixel format, use the + // SetPixelFormat method. + pixelFormat PixelFormat +} + +// func (c *FbsConn) Close() error { +// return c.fbs.Close() +// } + +// // Read reads data from conn +// func (c *FbsConn) Read(buf []byte) (int, error) { +// return c.fbs.Read(buf) +// } + +//dummy, no writing to this conn... +func (c *FbsConn) Write(buf []byte) (int, error) { + return len(buf), nil +} + +func (c *FbsConn) Conn() net.Conn { + return nil +} + +func (c *FbsConn) Config() interface{} { + return nil +} + +func (c *FbsConn) Protocol() string { + return "RFB 003.008" +} +func (c *FbsConn) PixelFormat() PixelFormat { + return c.pixelFormat +} + +func (c *FbsConn) SetPixelFormat(pf PixelFormat) error { + c.pixelFormat = pf + return nil +} + +func (c *FbsConn) ColorMap() ColorMap { return c.colorMap } +func (c *FbsConn) SetColorMap(cm ColorMap) { c.colorMap = cm } +func (c *FbsConn) Encodings() []Encoding { return c.encodings } +func (c *FbsConn) SetEncodings([]EncodingType) error { return nil } +func (c *FbsConn) Width() uint16 { return c.fbWidth } +func (c *FbsConn) Height() uint16 { return c.fbHeight } +func (c *FbsConn) SetWidth(w uint16) { c.fbWidth = w } +func (c *FbsConn) SetHeight(h uint16) { c.fbHeight = h } +func (c *FbsConn) DesktopName() []byte { return []byte(c.desktopName) } +func (c *FbsConn) SetDesktopName(d []byte) { c.desktopName = string(d) } +func (c *FbsConn) Flush() error { return nil } +func (c *FbsConn) Wait() {} +func (c *FbsConn) SetProtoVersion(string) {} +func (c *FbsConn) SetSecurityHandler(SecurityHandler) error { return nil } +func (c *FbsConn) SecurityHandler() SecurityHandler { return nil } +func (c *FbsConn) GetEncInstance(typ EncodingType) Encoding { + for _, enc := range c.encodings { + if enc.Type() == typ { + return enc + } + } + return nil + } + +type VncStreamFileReader interface { + io.Reader + CurrentTimestamp() int + ReadStartSession() (*ServerInit, error) + CurrentPixelFormat() *PixelFormat + Encodings() []Encoding +} + +type FBSPlayHelper struct { + Conn *FbsConn + //Fbs VncStreamFileReader + serverMessageMap map[uint8]ServerMessage + firstSegDone bool + startTime int +} + +func NewFbsConn(filename string, encs []Encoding) (*FbsConn, error) { + + fbs, err := NewFbsReader(filename) + if err != nil { + logger.Error("failed to open fbs reader:", err) + return nil, err + } + + + //NewFbsReader("/Users/amitbet/vncRec/recording.rbs") + initMsg, err := fbs.ReadStartSession() + if err != nil { + logger.Error("failed to open read fbs start session:", err) + return nil, err + } + fbsConn := &FbsConn{FbsReader: *fbs} + fbsConn.encodings = encs + fbsConn.SetPixelFormat(initMsg.PixelFormat) + fbsConn.SetHeight(initMsg.FBHeight) + fbsConn.SetWidth(initMsg.FBWidth) + fbsConn.SetDesktopName([]byte(initMsg.NameText)) + + return fbsConn, nil +} + +func NewFBSPlayHelper(r *FbsConn) *FBSPlayHelper { + h := &FBSPlayHelper{Conn: r} + h.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) + + h.serverMessageMap = make(map[uint8]ServerMessage) + h.serverMessageMap[0] = &FramebufferUpdate{} + h.serverMessageMap[1] = &SetColorMapEntries{} + h.serverMessageMap[2] = &Bell{} + h.serverMessageMap[3] = &ServerCutText{} + + return h +} + +// func (handler *FBSPlayHelper) Consume(seg *RfbSegment) error { + +// switch seg.SegmentType { +// case SegmentFullyParsedClientMessage: +// clientMsg := seg.Message.(ClientMessage) +// logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) got ClientMessage type=%s", clientMsg.Type()) +// switch clientMsg.Type() { + +// case FramebufferUpdateRequestMsgType: +// if !handler.firstSegDone { +// handler.firstSegDone = true +// handler.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) +// } +// handler.sendFbsMessage() +// } +// // server.MsgFramebufferUpdateRequest: +// } +// return nil +// } + +func (h *FBSPlayHelper) ReadFbsMessage() ServerMessage { + var messageType uint8 + //messages := make(map[uint8]ServerMessage) + fbs := h.Conn + //conn := h.Conn + err := binary.Read(fbs, binary.BigEndian, &messageType) + if err != nil { + logger.Error("TestServer.NewConnHandler: Error in reading FBS: ", err) + return nil + } + //IClientConn{} + //binary.Write(h.Conn, binary.BigEndian, messageType) + msg := h.serverMessageMap[messageType] + if msg == nil { + logger.Error("TestServer.NewConnHandler: Error unknown message type: ", messageType) + return nil + } + //read the actual message data + //err = binary.Read(fbs, binary.BigEndian, &msg) + parsedMsg, err := msg.Read(fbs) + if err != nil { + logger.Error("TestServer.NewConnHandler: Error in reading FBS message: ", err) + return nil + } + + timeSinceStart := int(time.Now().UnixNano()/int64(time.Millisecond)) - h.startTime + timeToSleep := fbs.CurrentTimestamp() - timeSinceStart + if timeToSleep > 0 { + time.Sleep(time.Duration(timeToSleep) * time.Millisecond) + } + + return parsedMsg +} diff --git a/fbs-reader.go b/fbs-reader.go new file mode 100644 index 0000000..f8fb5ca --- /dev/null +++ b/fbs-reader.go @@ -0,0 +1,190 @@ +package vnc2webm + +import ( + "bytes" + "encoding/binary" + "io" + "os" + //"vncproxy/common" + //"vncproxy/encodings" + "vnc2webm/logger" + //"vncproxy/encodings" + //"vncproxy/encodings" +) + +type FbsReader struct { + reader io.ReadCloser + buffer bytes.Buffer + currentTimestamp int + //pixelFormat *PixelFormat + //encodings []IEncoding +} + +func (fbs *FbsReader) Close() error { + return fbs.reader.Close() +} + +func (fbs *FbsReader) CurrentTimestamp() int { + return fbs.currentTimestamp +} + +func (fbs *FbsReader) Read(p []byte) (n int, err error) { + if fbs.buffer.Len() < len(p) { + seg, err := fbs.ReadSegment() + + if err != nil { + logger.Error("FBSReader.Read: error reading FBSsegment: ", err) + return 0, err + } + fbs.buffer.Write(seg.bytes) + fbs.currentTimestamp = int(seg.timestamp) + } + return fbs.buffer.Read(p) +} + +//func (fbs *FbsReader) CurrentPixelFormat() *PixelFormat { return fbs.pixelFormat } +//func (fbs *FbsReader) CurrentColorMap() *common.ColorMap { return &common.ColorMap{} } +//func (fbs *FbsReader) Encodings() []IEncoding { return fbs.encodings } + +func NewFbsReader(fbsFile string) (*FbsReader, error) { + + reader, err := os.OpenFile(fbsFile, os.O_RDONLY, 0644) + if err != nil { + logger.Error("NewFbsReader: can't open fbs file: ", fbsFile) + return nil, err + } + return &FbsReader{reader: reader}, //encodings: []IEncoding{ + // //&encodings.CopyRectEncoding{}, + // //&encodings.ZLibEncoding{}, + // //&encodings.ZRLEEncoding{}, + // //&encodings.CoRREEncoding{}, + // //&encodings.HextileEncoding{}, + // &TightEncoding{}, + // //&TightPngEncoding{}, + // //&EncCursorPseudo{}, + // &RawEncoding{}, + // //&encodings.RREEncoding{}, + //}, + nil + +} + +func (fbs *FbsReader) ReadStartSession() (*ServerInit, error) { + + initMsg := ServerInit{} + reader := fbs.reader + + var framebufferWidth uint16 + var framebufferHeight uint16 + var SecTypeNone uint32 + //read rfb header information (the only part done without the [size|data|timestamp] block wrapper) + //.("FBS 001.000\n") + bytes := make([]byte, 12) + _, err := reader.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init message - FBS file Version:", err) + return nil, err + } + + //read the version message into the buffer, it is written in the first fbs block + //RFB 003.008\n + bytes = make([]byte, 12) + _, err = fbs.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - RFB Version: ", err) + return nil, err + } + + //push sec type and fb dimensions + binary.Read(fbs, binary.BigEndian, &SecTypeNone) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - SecType: ", err) + } + + //read frame buffer width, height + binary.Read(fbs, binary.BigEndian, &framebufferWidth) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBWidth: ", err) + return nil, err + } + initMsg.FBWidth = framebufferWidth + + binary.Read(fbs, binary.BigEndian, &framebufferHeight) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBHeight: ", err) + return nil, err + } + initMsg.FBHeight = framebufferHeight + + //read pixel format + pixelFormat := &PixelFormat{} + binary.Read(fbs, binary.BigEndian, pixelFormat) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - Pixelformat: ", err) + return nil, err + } + + initMsg.PixelFormat = *pixelFormat + + //read desktop name + var desknameLen uint32 + binary.Read(fbs, binary.BigEndian, &desknameLen) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - deskname Len: ", err) + return nil, err + } + initMsg.NameLength = desknameLen + + bytes = make([]byte, desknameLen) + fbs.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - desktopName: ", err) + return nil, err + } + + initMsg.NameText = bytes + + return &initMsg, nil +} + +func (fbs *FbsReader) ReadSegment() (*FbsSegment, error) { + reader := fbs.reader + var bytesLen uint32 + + //read length + err := binary.Read(reader, binary.BigEndian, &bytesLen) + if err != nil { + logger.Error("FbsReader.ReadStartSession: read len, error reading rbs file: ", err) + return nil, err + } + + paddedSize := (bytesLen + 3) & 0x7FFFFFFC + + //read bytes + bytes := make([]byte, paddedSize) + _, err = reader.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadSegment: read bytes, error reading rbs file: ", err) + return nil, err + } + + //remove padding + actualBytes := bytes[:bytesLen] + + //read timestamp + var timeSinceStart uint32 + binary.Read(reader, binary.BigEndian, &timeSinceStart) + if err != nil { + logger.Error("FbsReader.ReadSegment: read timestamp, error reading rbs file: ", err) + return nil, err + } + + //timeStamp := time.Unix(timeSinceStart, 0) + seg := &FbsSegment{bytes: actualBytes, timestamp: timeSinceStart} + return seg, nil +} + +type FbsSegment struct { + bytes []byte + timestamp uint32 +}