mirror of
https://github.com/amitbet/vnc2video.git
synced 2025-08-17 20:16:28 +00:00
frame rate to variable
This commit is contained in:
parent
5c4293d610
commit
4f65cf3972
@ -14,10 +14,14 @@ type VP8ImageEncoder struct {
|
|||||||
FFMpegBinPath string
|
FFMpegBinPath string
|
||||||
input io.WriteCloser
|
input io.WriteCloser
|
||||||
closed bool
|
closed bool
|
||||||
|
Framerate int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (enc *VP8ImageEncoder) Init(videoFileName string) {
|
func (enc *VP8ImageEncoder) Init(videoFileName string) {
|
||||||
fileExt := ".webm"
|
fileExt := ".webm"
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
if !strings.HasSuffix(videoFileName, fileExt) {
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
videoFileName = videoFileName + fileExt
|
videoFileName = videoFileName + fileExt
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,14 @@ type DV9ImageEncoder struct {
|
|||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
FFMpegBinPath string
|
FFMpegBinPath string
|
||||||
input io.WriteCloser
|
input io.WriteCloser
|
||||||
|
Framerate int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (enc *DV9ImageEncoder) Init(videoFileName string) {
|
func (enc *DV9ImageEncoder) Init(videoFileName string) {
|
||||||
fileExt := ".mp4"
|
fileExt := ".mp4"
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
if !strings.HasSuffix(videoFileName, fileExt) {
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
videoFileName = videoFileName + fileExt
|
videoFileName = videoFileName + fileExt
|
||||||
}
|
}
|
||||||
|
116
encoders/huffyuv-enc.go
Normal file
116
encoders/huffyuv-enc.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"vnc2video/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// this is a very common loseless encoder (but produces huge files)
|
||||||
|
type HuffYuvImageEncoder struct {
|
||||||
|
FFMpegBinPath string
|
||||||
|
cmd *exec.Cmd
|
||||||
|
input io.WriteCloser
|
||||||
|
closed bool
|
||||||
|
Framerate int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *HuffYuvImageEncoder) Init(videoFileName string) {
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
fileExt := ".avi"
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
//binary := "./ffmpeg"
|
||||||
|
cmd := exec.Command(enc.FFMpegBinPath,
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-vcodec", "ppm",
|
||||||
|
//"-r", strconv.Itoa(framerate),
|
||||||
|
"-r", "12",
|
||||||
|
|
||||||
|
//"-re",
|
||||||
|
//"-i", "pipe:0",
|
||||||
|
"-an", //no audio
|
||||||
|
//"-vsync", "2",
|
||||||
|
///"-probesize", "10000000",
|
||||||
|
"-y",
|
||||||
|
|
||||||
|
"-i", "-",
|
||||||
|
//"–s", "640×360",
|
||||||
|
"-vcodec", "huffyuv", //"libvpx",//"libvpx-vp9"//"libx264"
|
||||||
|
//"-b:v", "0.33M",
|
||||||
|
"-threads", "7",
|
||||||
|
///"-coder", "1",
|
||||||
|
///"-bf", "0",
|
||||||
|
///"-me_method", "hex",
|
||||||
|
//"-speed", "0",
|
||||||
|
//"-lossless", "1", //for vpx
|
||||||
|
// "-an", "-f", "webm",
|
||||||
|
"-preset", "veryfast",
|
||||||
|
//"-tune", "animation",
|
||||||
|
"-maxrate", "0.5M",
|
||||||
|
"-bufsize", "50M",
|
||||||
|
"-g", "250",
|
||||||
|
|
||||||
|
//"-crf", "0", //for lossless encoding!!!!
|
||||||
|
|
||||||
|
//"-rc_lookahead", "16",
|
||||||
|
//"-profile", "0",
|
||||||
|
"-crf", "34",
|
||||||
|
//"-qmax", "51",
|
||||||
|
//"-qmin", "7",
|
||||||
|
//"-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 *HuffYuvImageEncoder) Run(videoFileName string) error {
|
||||||
|
if _, err := os.Stat(enc.FFMpegBinPath); os.IsNotExist(err) {
|
||||||
|
logger.Error("encoder file doesn't exist in path:", enc.FFMpegBinPath)
|
||||||
|
return errors.New("encoder file doesn't exist in path" + videoFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.Init(videoFileName)
|
||||||
|
logger.Debugf("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)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (enc *HuffYuvImageEncoder) Encode(img image.Image) {
|
||||||
|
if enc.input == nil || enc.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := encodePPM(enc.input, img)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while encoding image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *HuffYuvImageEncoder) Close() {
|
||||||
|
enc.closed = true
|
||||||
|
//enc.cmd.Process.Kill()
|
||||||
|
}
|
@ -19,6 +19,9 @@ type MJPegImageEncoder struct {
|
|||||||
|
|
||||||
func (enc *MJPegImageEncoder) Init(videoFileName string) {
|
func (enc *MJPegImageEncoder) Init(videoFileName string) {
|
||||||
fileExt := ".avi"
|
fileExt := ".avi"
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
if !strings.HasSuffix(videoFileName, fileExt) {
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
videoFileName = videoFileName + fileExt
|
videoFileName = videoFileName + fileExt
|
||||||
}
|
}
|
||||||
|
115
encoders/qtrle-enc.go
Normal file
115
encoders/qtrle-enc.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package encoders
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"vnc2video/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QTRLEImageEncoder quick time rle is an efficient loseless codec, uses .mov extension
|
||||||
|
type QTRLEImageEncoder struct {
|
||||||
|
FFMpegBinPath string
|
||||||
|
cmd *exec.Cmd
|
||||||
|
input io.WriteCloser
|
||||||
|
closed bool
|
||||||
|
Framerate int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *QTRLEImageEncoder) Init(videoFileName string) {
|
||||||
|
fileExt := ".mov"
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
|
videoFileName = videoFileName + fileExt
|
||||||
|
}
|
||||||
|
//binary := "./ffmpeg"
|
||||||
|
cmd := exec.Command(enc.FFMpegBinPath,
|
||||||
|
"-f", "image2pipe",
|
||||||
|
"-vcodec", "ppm",
|
||||||
|
//"-r", strconv.Itoa(framerate),
|
||||||
|
"-r", "12",
|
||||||
|
|
||||||
|
//"-re",
|
||||||
|
//"-i", "pipe:0",
|
||||||
|
"-an", //no audio
|
||||||
|
//"-vsync", "2",
|
||||||
|
///"-probesize", "10000000",
|
||||||
|
"-y",
|
||||||
|
|
||||||
|
"-i", "-",
|
||||||
|
//"–s", "640×360",
|
||||||
|
"-vcodec", "qtrle", //"libvpx",//"libvpx-vp9"//"libx264"
|
||||||
|
//"-b:v", "0.33M",
|
||||||
|
"-threads", "7",
|
||||||
|
///"-coder", "1",
|
||||||
|
///"-bf", "0",
|
||||||
|
///"-me_method", "hex",
|
||||||
|
//"-speed", "0",
|
||||||
|
//"-lossless", "1", //for vpx
|
||||||
|
// "-an", "-f", "webm",
|
||||||
|
"-preset", "veryfast",
|
||||||
|
//"-tune", "animation",
|
||||||
|
"-maxrate", "0.5M",
|
||||||
|
"-bufsize", "50M",
|
||||||
|
"-g", "250",
|
||||||
|
|
||||||
|
//"-crf", "0", //for lossless encoding!!!!
|
||||||
|
|
||||||
|
//"-rc_lookahead", "16",
|
||||||
|
//"-profile", "0",
|
||||||
|
"-crf", "34",
|
||||||
|
//"-qmax", "51",
|
||||||
|
//"-qmin", "7",
|
||||||
|
//"-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 *QTRLEImageEncoder) Run(videoFileName string) error {
|
||||||
|
if _, err := os.Stat(enc.FFMpegBinPath); os.IsNotExist(err) {
|
||||||
|
logger.Error("encoder file doesn't exist in path:", enc.FFMpegBinPath)
|
||||||
|
return errors.New("encoder file doesn't exist in path" + videoFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.Init(videoFileName)
|
||||||
|
logger.Debugf("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)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (enc *QTRLEImageEncoder) Encode(img image.Image) {
|
||||||
|
if enc.input == nil || enc.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := encodePPM(enc.input, img)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error while encoding image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *QTRLEImageEncoder) Close() {
|
||||||
|
enc.closed = true
|
||||||
|
//enc.cmd.Process.Kill()
|
||||||
|
}
|
@ -15,10 +15,14 @@ type X264ImageEncoder struct {
|
|||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
input io.WriteCloser
|
input io.WriteCloser
|
||||||
closed bool
|
closed bool
|
||||||
|
Framerate int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (enc *X264ImageEncoder) Init(videoFileName string) {
|
func (enc *X264ImageEncoder) Init(videoFileName string) {
|
||||||
fileExt := ".mp4"
|
fileExt := ".mp4"
|
||||||
|
if enc.Framerate == 0 {
|
||||||
|
enc.Framerate = 12
|
||||||
|
}
|
||||||
if !strings.HasSuffix(videoFileName, fileExt) {
|
if !strings.HasSuffix(videoFileName, fileExt) {
|
||||||
videoFileName = videoFileName + fileExt
|
videoFileName = videoFileName + fileExt
|
||||||
}
|
}
|
||||||
|
@ -367,9 +367,8 @@ func (enc *TightEncoding) drawTightPalette(rect *Rectangle, palette color.Palett
|
|||||||
enc.Image.Set(int(rect.X)+x, int(rect.Y)+y, palette[palettePos])
|
enc.Image.Set(int(rect.X)+x, int(rect.Y)+y, palette[palettePos])
|
||||||
//logger.Tracef("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos])
|
//logger.Tracef("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos])
|
||||||
}
|
}
|
||||||
// if bitPos != 7 {
|
|
||||||
// bytePos++
|
// reset bit alignment to first bit in byte (msb)
|
||||||
// }
|
|
||||||
bitPos = 7
|
bitPos = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,18 @@ package vnc2video
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockWidth = 16
|
||||||
|
BlockHeight = 16
|
||||||
|
)
|
||||||
|
|
||||||
type VncCanvas struct {
|
type VncCanvas struct {
|
||||||
draw.Image
|
draw.Image
|
||||||
//DisplayBuff draw.Image
|
//DisplayBuff draw.Image
|
||||||
@ -20,6 +26,7 @@ type VncCanvas struct {
|
|||||||
CursorOffset *image.Point
|
CursorOffset *image.Point
|
||||||
CursorLocation *image.Point
|
CursorLocation *image.Point
|
||||||
DrawCursor bool
|
DrawCursor bool
|
||||||
|
Changed map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVncCanvas(width, height int) *VncCanvas {
|
func NewVncCanvas(width, height int) *VncCanvas {
|
||||||
@ -33,6 +40,23 @@ func NewVncCanvas(width, height int) *VncCanvas {
|
|||||||
return &canvas
|
return &canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *VncCanvas) SetChanged(rect *Rectangle) {
|
||||||
|
if c.Changed == nil {
|
||||||
|
c.Changed = make(map[string]bool)
|
||||||
|
}
|
||||||
|
for x := int(rect.X) / BlockWidth; x*BlockWidth < int(rect.X+rect.Width); x++ {
|
||||||
|
for y := int(rect.Y) / BlockHeight; y*BlockHeight < int(rect.Y+rect.Height); y++ {
|
||||||
|
key := fmt.Sprintf("%d,%d", x, y)
|
||||||
|
//fmt.Println("setting block: ", key)
|
||||||
|
c.Changed[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VncCanvas) Reset(rect *Rectangle) {
|
||||||
|
c.Changed = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *VncCanvas) RemoveCursor() image.Image {
|
func (c *VncCanvas) RemoveCursor() image.Image {
|
||||||
if c.Cursor == nil || c.CursorLocation == nil {
|
if c.Cursor == nil || c.CursorLocation == nil {
|
||||||
return c.Image
|
return c.Image
|
||||||
|
15
encoding_util_test.go
Normal file
15
encoding_util_test.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package vnc2video
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSetChanged(t *testing.T) {
|
||||||
|
canvas := &VncCanvas{}
|
||||||
|
rect := &Rectangle{X: 1, Y: 1, Width: 1024, Height: 64}
|
||||||
|
canvas.SetChanged(rect)
|
||||||
|
if canvas.Changed["64,0"] == false ||
|
||||||
|
canvas.Changed["64,1"] == false ||
|
||||||
|
canvas.Changed["64,4"] == false {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user