replicas-computer 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ //go:build libyuv && capturetest
2
+
3
+ package capture
4
+
5
+ import (
6
+ "fmt"
7
+ "testing"
8
+ )
9
+
10
+ func TestPartialCapture(t *testing.T) {
11
+ for _, size := range [][2]int{{128, 96}, {94, 66}} {
12
+ for _, i420 := range []bool{true, false} {
13
+ for _, bt709 := range []bool{true, false} {
14
+ t.Run(fmt.Sprintf("%dx%d/i420=%t/bt709=%t", size[0], size[1], i420, bt709), func(t *testing.T) {
15
+ if err := checkPartialCapture(size[0], size[1], i420, bt709); err != nil {
16
+ t.Fatal(err)
17
+ }
18
+ })
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ package capture
2
+
3
+ type Options struct {
4
+ Width, Height, FPS, Threads, CRF, Kbps int
5
+ KeyframeMS, IntraRefresh, ChromaQP, Warmup, Payload, BT709 int
6
+ Preset, Profile, URL, SSRC string
7
+ }
@@ -0,0 +1,55 @@
1
+ //go:build libyuv
2
+
3
+ package capture
4
+
5
+ /*
6
+ #include <stdlib.h>
7
+ #include <X11/Xlib.h>
8
+ #include <libavutil/frame.h>
9
+ #include <libyuv/convert.h>
10
+ #include <libyuv/convert_from_argb.h>
11
+ #include <libyuv/row.h>
12
+ */
13
+ import "C"
14
+
15
+ import (
16
+ "errors"
17
+ "unsafe"
18
+ )
19
+
20
+ const hasLibyuv = true
21
+
22
+ type colorMatrix struct{ pointer *C.struct_ArgbConstants }
23
+
24
+ func newColorMatrix(bt709 bool) (colorMatrix, error) {
25
+ var pointer unsafe.Pointer
26
+ if C.posix_memalign(&pointer, 64, C.sizeof_struct_ArgbConstants) != 0 {
27
+ return colorMatrix{}, errors.New("cannot allocate color matrix")
28
+ }
29
+ m := colorMatrix{(*C.struct_ArgbConstants)(pointer)}
30
+ *m.pointer = C.kArgbI601Constants
31
+ if bt709 {
32
+ *m.pointer = C.kArgbH709Constants
33
+ }
34
+ for i := range m.pointer.kAddUV {
35
+ m.pointer.kAddUV[i] += 128
36
+ }
37
+ return m, nil
38
+ }
39
+
40
+ func (m colorMatrix) close() { C.free(unsafe.Pointer(m.pointer)) }
41
+
42
+ func (m colorMatrix) convert(im *C.XImage, f *C.AVFrame, x, y int) error {
43
+ chroma := 0
44
+ if f.format == C.AV_PIX_FMT_YUV420P {
45
+ chroma = 1
46
+ }
47
+ // The clipped, aligned region stays inside these native-owned image planes.
48
+ py := (*C.uint8_t)(unsafe.Add(unsafe.Pointer(f.data[0]), y*int(f.linesize[0])+x))
49
+ pu := (*C.uint8_t)(unsafe.Add(unsafe.Pointer(f.data[1]), (y>>chroma)*int(f.linesize[1])+(x>>chroma)))
50
+ pv := (*C.uint8_t)(unsafe.Add(unsafe.Pointer(f.data[2]), (y>>chroma)*int(f.linesize[2])+(x>>chroma)))
51
+ if chroma != 0 {
52
+ return avCheck(C.ARGBToI420Matrix((*C.uint8_t)(unsafe.Pointer(im.data)), im.bytes_per_line, py, f.linesize[0], pu, f.linesize[1], pv, f.linesize[2], m.pointer, im.width, im.height))
53
+ }
54
+ return avCheck(C.ARGBToI444Matrix((*C.uint8_t)(unsafe.Pointer(im.data)), im.bytes_per_line, py, f.linesize[0], pu, f.linesize[1], pv, f.linesize[2], m.pointer, im.width, im.height))
55
+ }
@@ -0,0 +1,23 @@
1
+ //go:build !libyuv
2
+
3
+ package capture
4
+
5
+ /*
6
+ #include <X11/Xlib.h>
7
+ #include <libavutil/frame.h>
8
+ */
9
+ import "C"
10
+
11
+ import "errors"
12
+
13
+ const hasLibyuv = false
14
+
15
+ type colorMatrix struct{}
16
+
17
+ func newColorMatrix(bool) (colorMatrix, error) {
18
+ return colorMatrix{}, errors.New("libyuv unavailable")
19
+ }
20
+ func (colorMatrix) close() {}
21
+ func (colorMatrix) convert(*C.XImage, *C.AVFrame, int, int) error {
22
+ return errors.New("libyuv unavailable")
23
+ }
package/native/main.go ADDED
@@ -0,0 +1,105 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "os/signal"
7
+ "syscall"
8
+
9
+ "github.com/replicas-group/replicas/computer-use/native/internal/capture"
10
+ )
11
+
12
+ func run(o capture.Options) error {
13
+ stop := make(chan os.Signal, 1)
14
+ signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
15
+ defer signal.Stop(stop)
16
+ stream, err := capture.Open(o)
17
+ if err != nil {
18
+ return err
19
+ }
20
+ defer stream.Close()
21
+ started := capture.NowMicros()
22
+ last, lastKeyframe, reportAt := started-1000000, started-1000000, started
23
+ interval := int64((1000000 + o.FPS - 1) / o.FPS)
24
+ busyUS, captureUS, scaleUS, encodeUS, previousPTS := int64(0), int64(0), int64(0), int64(0), int64(-1)
25
+ dirty := true
26
+ number, changedFrames, sentFrames := 0, 0, 0
27
+ for {
28
+ select {
29
+ case <-stop:
30
+ return nil
31
+ default:
32
+ }
33
+ changed, err := stream.Changed()
34
+ if err != nil {
35
+ return err
36
+ }
37
+ dirty = dirty || changed
38
+ now := capture.NowMicros()
39
+ idleDue := last + max(interval, 200000)
40
+ if o.IntraRefresh == 0 {
41
+ idleDue = min(idleDue, lastKeyframe+int64(o.KeyframeMS)*1000)
42
+ }
43
+ due := max(last+interval, idleDue)
44
+ if dirty || number <= o.Warmup || number < 3 {
45
+ due = last + interval
46
+ }
47
+ if now < due {
48
+ if err = stream.Wait(due - now); err != nil {
49
+ return err
50
+ }
51
+ continue
52
+ }
53
+ copied, converted := now, now
54
+ if dirty {
55
+ copied, converted, err = stream.Capture(number == 0)
56
+ if err != nil {
57
+ return err
58
+ }
59
+ changedFrames++
60
+ dirty = false
61
+ }
62
+ pts := max(previousPTS+1, (now-started)*90/1000)
63
+ previousPTS = pts
64
+ keyframe := number == 0 || (o.Warmup > 0 && number == o.Warmup) || (o.IntraRefresh == 0 && now-lastKeyframe >= int64(o.KeyframeMS)*1000)
65
+ if keyframe {
66
+ lastKeyframe = now
67
+ }
68
+ if err = stream.Send(pts, keyframe); err != nil {
69
+ return err
70
+ }
71
+ ended := capture.NowMicros()
72
+ captureUS += copied - now
73
+ scaleUS += converted - copied
74
+ encodeUS += ended - converted
75
+ busyUS += ended - now
76
+ sentFrames++
77
+ number++
78
+ if now-last < interval*2 {
79
+ last += interval
80
+ } else {
81
+ last = now
82
+ }
83
+ if ended-reportAt >= 1000000 {
84
+ _, err = fmt.Fprintf(os.Stdout, "{\"elapsedMs\":%.3f,\"busyMs\":%.3f,\"captureMs\":%.3f,\"scaleMs\":%.3f,\"encodeMs\":%.3f,\"frames\":%d,\"changedFrames\":%d}\n", float64(ended-reportAt)/1000, float64(busyUS)/1000, float64(captureUS)/1000, float64(scaleUS)/1000, float64(encodeUS)/1000, sentFrames, changedFrames)
85
+ if err != nil {
86
+ return err
87
+ }
88
+ reportAt = ended
89
+ busyUS, captureUS, scaleUS, encodeUS = 0, 0, 0, 0
90
+ changedFrames, sentFrames = 0, 0
91
+ }
92
+ }
93
+ }
94
+
95
+ func main() {
96
+ o, err := parseOptions()
97
+ if err != nil {
98
+ fmt.Fprintln(os.Stderr, "capture:", err)
99
+ os.Exit(2)
100
+ }
101
+ if err = run(o); err != nil {
102
+ fmt.Fprintln(os.Stderr, "capture:", err)
103
+ os.Exit(1)
104
+ }
105
+ }
@@ -0,0 +1,52 @@
1
+ package main
2
+
3
+ import (
4
+ "errors"
5
+ "flag"
6
+ "fmt"
7
+ "os"
8
+
9
+ "github.com/replicas-group/replicas/computer-use/native/internal/capture"
10
+ )
11
+
12
+ func parseOptions() (capture.Options, error) {
13
+ o := capture.Options{}
14
+ f := flag.NewFlagSet("capture", flag.ContinueOnError)
15
+ numbers := []struct {
16
+ name string
17
+ value *int
18
+ initial, low, high int
19
+ }{
20
+ {"width", &o.Width, 0, 2, 8192}, {"height", &o.Height, 0, 2, 8192},
21
+ {"fps", &o.FPS, 60, 1, 240}, {"threads", &o.Threads, 4, 1, 16},
22
+ {"crf", &o.CRF, 14, 0, 51}, {"kbps", &o.Kbps, 20000, 1, 1000000},
23
+ {"keyframe-ms", &o.KeyframeMS, 1000, 1, 10000}, {"intra-refresh", &o.IntraRefresh, 1, 0, 1},
24
+ {"chroma-qp", &o.ChromaQP, 0, -12, 12}, {"warmup", &o.Warmup, 0, 0, 60},
25
+ {"payload", &o.Payload, 96, 0, 127}, {"bt709", &o.BT709, 1, 0, 1},
26
+ }
27
+ for _, n := range numbers {
28
+ f.IntVar(n.value, n.name, n.initial, "")
29
+ }
30
+ f.StringVar(&o.Preset, "preset", "superfast", "")
31
+ f.StringVar(&o.Profile, "profile", "main", "")
32
+ f.StringVar(&o.URL, "url", "", "")
33
+ f.StringVar(&o.SSRC, "ssrc", "", "")
34
+ if err := f.Parse(os.Args[1:]); err != nil {
35
+ return o, err
36
+ }
37
+ var invalid error
38
+ f.Visit(func(f *flag.Flag) {
39
+ for _, n := range numbers {
40
+ if n.name == f.Name && (*n.value < n.low || *n.value > n.high) {
41
+ invalid = fmt.Errorf("invalid --%s", n.name)
42
+ }
43
+ }
44
+ })
45
+ if invalid != nil {
46
+ return o, invalid
47
+ }
48
+ if f.NArg() != 0 || o.Width == 0 || o.Height == 0 || o.URL == "" || o.SSRC == "" || o.Width%2 != 0 || o.Height%2 != 0 {
49
+ return o, errors.New("width, height, url and ssrc are required; dimensions must be even")
50
+ }
51
+ return o, nil
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-computer",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Replicas workspace computer-use runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,14 +13,21 @@
13
13
  "scripts/build-webrtc.sh",
14
14
  "patches/libdatachannel-dtls-bio.patch",
15
15
  "patches/libdatachannel-nack-duplicates.patch",
16
- "patches/libjuice-ice-role-presence.patch"
16
+ "patches/libjuice-ice-role-presence.patch",
17
+ "scripts/build-capture.sh",
18
+ "patches/libyuv-chroma-rounding.patch",
19
+ "native/go.mod",
20
+ "native/*.go",
21
+ "native/internal"
17
22
  ],
18
23
  "scripts": {
19
24
  "build": "bun run typecheck && tsup",
20
25
  "typecheck": "tsc --noEmit",
21
26
  "dev": "bun run src/index.ts",
22
27
  "dev-pack": "bun run build && npm pack --pack-destination /tmp",
23
- "prepublishOnly": "bun run build"
28
+ "prepublishOnly": "bun run build",
29
+ "build:capture": "bash scripts/build-capture.sh",
30
+ "test:capture": "bash scripts/build-capture.sh test"
24
31
  },
25
32
  "keywords": [
26
33
  "replicas",
@@ -0,0 +1,83 @@
1
+ diff --git a/source/row_gcc.cc b/source/row_gcc.cc
2
+ index 90d6786..f52e61b 100644
3
+ --- a/source/row_gcc.cc
4
+ +++ b/source/row_gcc.cc
5
+ @@ -1969,8 +1969,7 @@ void ARGBToUV444MatrixRow_SSSE3(const uint8_t* src_argb,
6
+ int width,
7
+ const struct ArgbConstants* c) {
8
+ asm volatile(
9
+ - "pcmpeqb %%xmm5,%%xmm5 \n" // 0x8000
10
+ - "psllw $15,%%xmm5 \n"
11
+ + "movdqa 0x80(%4),%%xmm5 \n"
12
+ "movdqa 0x20(%4),%%xmm3 \n" // kRGBToU
13
+ "movdqa 0x40(%4),%%xmm4 \n" // kRGBToV
14
+ "sub %1,%2 \n"
15
+ @@ -2042,8 +2041,7 @@ void ARGBToUV444MatrixRow_AVX2(const uint8_t* src_argb,
16
+ asm volatile(
17
+ "vmovdqa 0x20(%4),%%ymm3 \n" // kRGBToU
18
+ "vmovdqa 0x40(%4),%%ymm4 \n" // kRGBToV
19
+ - "vpcmpeqb %%ymm5,%%ymm5,%%ymm5 \n" // 0x8000
20
+ - "vpsllw $15,%%ymm5,%%ymm5 \n"
21
+ + "vmovdqa 0x80(%4),%%ymm5 \n"
22
+ "vmovdqa %5,%%ymm7 \n"
23
+ "sub %1,%2 \n"
24
+
25
+ @@ -2115,7 +2113,7 @@ void ARGBToUV444MatrixRow_AVX512BW(const uint8_t* src_argb,
26
+ "vbroadcasti32x4 0x20(%4),%%zmm3 \n" // kRGBToU
27
+ "vbroadcasti32x4 0x40(%4),%%zmm4 \n" // kRGBToV
28
+ "vpternlogd $0xff,%%zmm16,%%zmm16,%%zmm16 \n" // -1
29
+ - "vpsllw $15,%%zmm16,%%zmm5 \n" // 0x8000
30
+ + "vbroadcasti32x4 0x80(%4),%%zmm5 \n"
31
+ "vmovups %5,%%zmm17 \n"
32
+ "sub %1,%2 \n"
33
+
34
+ @@ -2135,8 +2133,8 @@ void ARGBToUV444MatrixRow_AVX512BW(const uint8_t* src_argb,
35
+ "vpmaddwd %%zmm16,%%zmm18,%%zmm18 \n"
36
+ "vpackssdw %%zmm1,%%zmm0,%%zmm0 \n" // mutates
37
+ "vpackssdw %%zmm18,%%zmm2,%%zmm2 \n"
38
+ - "vpsubw %%zmm5,%%zmm0,%%zmm0 \n"
39
+ - "vpsubw %%zmm5,%%zmm2,%%zmm2 \n"
40
+ + "vpaddw %%zmm5,%%zmm0,%%zmm0 \n"
41
+ + "vpaddw %%zmm5,%%zmm2,%%zmm2 \n"
42
+ "vpsrlw $0x8,%%zmm0,%%zmm0 \n"
43
+ "vpsrlw $0x8,%%zmm2,%%zmm2 \n"
44
+ "vpackuswb %%zmm2,%%zmm0,%%zmm0 \n" // mutates
45
+ @@ -2157,8 +2155,8 @@ void ARGBToUV444MatrixRow_AVX512BW(const uint8_t* src_argb,
46
+ "vpmaddwd %%zmm16,%%zmm18,%%zmm18 \n"
47
+ "vpackssdw %%zmm1,%%zmm0,%%zmm0 \n" // mutates
48
+ "vpackssdw %%zmm18,%%zmm2,%%zmm2 \n"
49
+ - "vpsubw %%zmm5,%%zmm0,%%zmm0 \n"
50
+ - "vpsubw %%zmm5,%%zmm2,%%zmm2 \n"
51
+ + "vpaddw %%zmm5,%%zmm0,%%zmm0 \n"
52
+ + "vpaddw %%zmm5,%%zmm2,%%zmm2 \n"
53
+ "vpsrlw $0x8,%%zmm0,%%zmm0 \n"
54
+ "vpsrlw $0x8,%%zmm2,%%zmm2 \n"
55
+ "vpackuswb %%zmm2,%%zmm0,%%zmm0 \n" // mutates
56
+ @@ -2230,8 +2228,7 @@ void ARGBToUVMatrixRow_SSSE3(const uint8_t* src_argb,
57
+ "pavgw %%xmm2,%%xmm1 \n"
58
+ "packuswb %%xmm1,%%xmm0 \n" // mutates
59
+
60
+ - "movdqa %%xmm6,%%xmm2 \n"
61
+ - "psllw $15,%%xmm2 \n" // 0x8000
62
+ + "movdqa 0x80(%5),%%xmm2 \n"
63
+ "movdqa %%xmm0,%%xmm1 \n"
64
+ "pmaddubsw %%xmm5,%%xmm1 \n" // 4 V
65
+ "pmaddubsw %%xmm4,%%xmm0 \n" // 4 U
66
+ @@ -2305,7 +2302,7 @@ void ARGBToUVMatrixRow_AVX2(const uint8_t* src_argb,
67
+ "vpackuswb %%ymm1,%%ymm0,%%ymm0 \n" // mutates
68
+ "vpermq $0xd8,%%ymm0,%%ymm0 \n" // 8 ARGB Pixels
69
+
70
+ - "vpsllw $15,%%ymm6,%%ymm2 \n" // 0x8000
71
+ + "vmovdqa 0x80(%5),%%ymm2 \n"
72
+ "vpmaddubsw %%ymm5,%%ymm0,%%ymm1 \n" // 8 V
73
+ "vpmaddubsw %%ymm4,%%ymm0,%%ymm0 \n" // 8 U
74
+ "vphaddw %%ymm1,%%ymm0,%%ymm0 \n" // uuuuvvvv uuuuvvvv
75
+ @@ -2362,7 +2359,7 @@ void ARGBToUVMatrixRow_AVX512BW(const uint8_t* src_argb,
76
+ "vbroadcasti32x4 0x40(%5),%%zmm5 \n" // RGBToV
77
+ "vpternlogd $0xff,%%zmm16,%%zmm16,%%zmm16 \n"
78
+ "vpabsb %%zmm16,%%zmm20 \n" // 0x0101
79
+ - "vpsllw $15,%%zmm16,%%zmm17 \n" // 0x8000
80
+ + "vbroadcasti32x4 0x80(%5),%%zmm17 \n"
81
+ "vbroadcasti32x4 %6,%%zmm21 \n" // kShuffleAARRGGBB
82
+ "vmovups %7,%%zmm18 \n" // kPermdARGBToY_AVX512BW
83
+ "vmovups %8,%%zmm19 \n" // kPermdARGBToUV_AVX512BW
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ package_dir="$(cd "$(dirname "$0")/.." && pwd)"
4
+ mkdir -p "$package_dir/dist"
5
+ export GOTOOLCHAIN=go1.27.1 CGO_ENABLED=1
6
+ build_tags=()
7
+ case "$("${CC:-cc}" -dumpmachine)" in
8
+ x86_64-*)
9
+ build_root="$(mktemp -d)"
10
+ trap 'rm -rf -- "$build_root"' EXIT
11
+ git init -q "$build_root/source"
12
+ git -C "$build_root/source" fetch --depth 1 https://chromium.googlesource.com/libyuv/libyuv 2dd4257364d39c38d79465c4ddc4b93137fe729b
13
+ git -C "$build_root/source" checkout -q --detach FETCH_HEAD
14
+ git -C "$build_root/source" apply "$package_dir/patches/libyuv-chroma-rounding.patch"
15
+ cmake -S "$build_root/source" -B "$build_root/build" -DCMAKE_BUILD_TYPE=Release -DLIBYUV_DISABLE_JPEG=ON -DUNIT_TEST=OFF
16
+ cmake --build "$build_root/build" --target yuv -j 2
17
+ export CGO_CFLAGS="${CGO_CFLAGS:--O2 -g} -I$build_root/source/include"
18
+ export CGO_LDFLAGS="${CGO_LDFLAGS:-} $build_root/build/libyuv.a -lstdc++ -lm"
19
+ build_tags=(-tags libyuv)
20
+ cp "$build_root/source/LICENSE" "$package_dir/dist/libyuv-LICENSE"
21
+ ;;
22
+ esac
23
+ cd "$package_dir/native"
24
+ if [[ "${1:-}" == test ]]; then
25
+ [[ ${#build_tags[@]} -gt 0 ]] || { echo 'Partial capture tests require x86-64 libyuv' >&2; exit 1; }
26
+ GOEXPERIMENT=cgocheck2 xvfb-run -a -s '-screen 0 128x96x24 -nolisten tcp' go test -count=1 -tags libyuv,capturetest ./...
27
+ exit
28
+ fi
29
+ go build -buildvcs=false -mod=readonly -trimpath -ldflags='-s -w' "${build_tags[@]}" -o "$package_dir/dist/replicas-capture.new" .
30
+ mv "$package_dir/dist/replicas-capture.new" "$package_dir/dist/replicas-capture"