-
Notifications
You must be signed in to change notification settings - Fork 0
/
encoder.go
181 lines (163 loc) · 3.81 KB
/
encoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package main
/**
* XXX: Ugly workaround for https://github.com/amitbet/vnc2video/issues/10. I've copied the file and build a
* X264ImageCustomEncoder. Once this is merged, we can drop the encoder.go file again.
*/
import (
"errors"
"fmt"
vnc "github.com/amitbet/vnc2video"
"github.com/amitbet/vnc2video/encoders"
"github.com/sirupsen/logrus"
"image"
"image/color"
"io"
"os"
"os/exec"
"strconv"
)
func encodePPMforRGBA(w io.Writer, img *image.RGBA) 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
}
if convImage == nil {
convImage = make([]uint8, size.Dy()*size.Dx()*3)
}
rowCount := 0
for i := 0; i < len(img.Pix); i++ {
if (i % 4) != 3 {
convImage[rowCount] = img.Pix[i]
rowCount++
}
}
if _, err := w.Write(convImage); err != nil {
return err
}
return nil
}
func encodePPMGeneric(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
}
var convImage []uint8
func encodePPM(w io.Writer, img image.Image) error {
if img == nil {
return errors.New("nil image")
}
img1, isRGBImage := img.(*vnc.RGBImage)
img2, isRGBA := img.(*image.RGBA)
if isRGBImage {
return encodePPMforRGBImage(w, img1)
} else if isRGBA {
return encodePPMforRGBA(w, img2)
}
return encodePPMGeneric(w, img)
}
func encodePPMforRGBImage(w io.Writer, img *vnc.RGBImage) 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
}
if _, err := w.Write(img.Pix); err != nil {
return err
}
return nil
}
type X264ImageCustomEncoder struct {
encoders.X264ImageEncoder
FFMpegBinPath string
cmd *exec.Cmd
input io.WriteCloser
closed bool
Framerate int
ConstantRateFactor int
}
func (enc *X264ImageCustomEncoder) Init(videoFileName string) {
if enc.Framerate == 0 {
enc.Framerate = 12
}
cmd := exec.Command(enc.FFMpegBinPath,
"-f", "image2pipe",
"-vcodec", "ppm",
"-r", strconv.Itoa(enc.Framerate),
"-an", // no audio
"-y",
"-i", "-",
"-vcodec", "libx264",
"-preset", "fast",
"-g", "250",
"-crf", strconv.Itoa(enc.ConstantRateFactor),
"-pix_fmt", "yuv420p10",
"-s", "1280x720",
videoFileName,
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
encInput, err := cmd.StdinPipe()
enc.input = encInput
if err != nil {
logrus.WithError(err).Error("can't get ffmpeg input pipe.")
}
enc.cmd = cmd
}
func (enc *X264ImageCustomEncoder) Run(videoFileName string) error {
if _, err := os.Stat(enc.FFMpegBinPath); os.IsNotExist(err) {
return err
}
enc.Init(videoFileName)
logrus.Infof("launching binary: %v", enc.cmd)
err := enc.cmd.Run()
if err != nil {
logrus.WithError(err).Errorf("error while launching ffmpeg: %v", enc.cmd.Args)
return err
}
return nil
}
func (enc *X264ImageCustomEncoder) Encode(img image.Image) {
if enc.input == nil || enc.closed {
return
}
err := encodePPM(enc.input, img)
if err != nil {
logrus.WithError(err).Error("error while encoding image.")
}
}
func (enc *X264ImageCustomEncoder) Close() {
if enc.closed {
return
}
enc.closed = true
err := enc.input.Close()
if err != nil {
logrus.WithError(err).Error("could not close input.")
}
}