forked from kirides/screencapture
-
Notifications
You must be signed in to change notification settings - Fork 0
/
screencapture.go
122 lines (108 loc) · 2.59 KB
/
screencapture.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
package main
import (
"context"
"errors"
"fmt"
"image"
"io"
"os/exec"
"runtime"
"screen-share/d3d"
"time"
"github.com/kbinani/screenshot"
)
func captureScreenTranscode(ctx context.Context, n int, framerate int) {
max := screenshot.NumActiveDisplays()
if n >= max {
fmt.Printf("Not enough displays\n")
return
}
screenBounds := screenshot.GetDisplayBounds(n)
transcoder := newVideotranscoder(fmt.Sprintf("screen_%d.mp4", n), screenBounds.Dx(), screenBounds.Dy(), float32(framerate))
// Keep this thread, so windows/d3d11/dxgi can use their threadlocal caches, if any
runtime.LockOSThread()
// Setup D3D11 stuff
device, deviceCtx, err := d3d.NewD3D11Device()
if err != nil {
fmt.Printf("Could not create D3D11 Device. %v\n", err)
return
}
defer device.Release()
defer deviceCtx.Release()
ddup, err := d3d.NewIDXGIOutputDuplication(device, deviceCtx, uint(n))
if err != nil {
fmt.Printf("Err NewIDXGIOutputDuplication: %v\n", err)
return
}
defer ddup.Release()
limiter := newFrameLimiter(framerate)
// Create image that can contain the wanted output (desktop)
imgBuf := image.NewRGBA(screenBounds)
defer transcoder.Close()
t1 := time.Now()
numFrames := 0
for {
if time.Since(t1).Seconds() >= 1 {
fmt.Printf("%d: written %d frames in 1s\n", n, numFrames)
t1 = time.Now()
numFrames = 0
}
select {
case <-ctx.Done():
return
default:
limiter.Wait()
}
// Grab an image.RGBA from the current output presenter
err = ddup.GetImage(imgBuf, 0)
if err != nil && !errors.Is(err, d3d.ErrNoImageYet) {
fmt.Printf("Err ddup.GetImage: %v\n", err)
return
}
numFrames++
n, err := transcoder.Write(imgBuf.Pix)
if err != nil || n != len(imgBuf.Pix) {
fmt.Printf("Failed to write image: %v\n", err)
return
}
}
}
type videotranscoder struct {
cmd *exec.Cmd
in io.WriteCloser
}
func newVideotranscoder(filePath string, width, height int, framerate float32) *videotranscoder {
cmd := exec.Command("ffmpeg",
"-y",
"-vsync", "0",
"-f", "rawvideo",
"-video_size", fmt.Sprintf("%dx%d", width, height),
"-pixel_format", "rgba",
"-framerate", fmt.Sprintf("%f", framerate),
"-i", "-",
// "-vf", "scale=-1:1080",
"-c:v", "libx264", "-preset", "ultrafast",
"-crf", "26",
"-tune", "zerolatency",
filePath,
)
wc, err := cmd.StdinPipe()
if err != nil {
panic(err)
}
if err := cmd.Start(); err != nil {
panic(err)
}
return &videotranscoder{
cmd: cmd,
in: wc,
}
}
func (v *videotranscoder) Write(buf []byte) (int, error) {
return v.in.Write(buf)
}
func (v *videotranscoder) Close() error {
// v.out.Close()
v.in.Close()
return nil
}