detached 4c97002

Merge pull request 'feat: add a zero-allocation PipeWire video source' (#1) from feat/video-source into main

Reviewed-on: https://git.bnema.dev/bnema/purego-pipewire/pulls/1
commit
4c970021009f5965404bcd9709c1d32e25b9a378
author
bnema · · 2026-10-15 03:07 UTC
merge of
6f9eb59 + adcd7e5 · diff against the first parent
signature
SSH UnverifiedSHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivA The key is unknown
refs
maintag: v0.2.0
49 files · +7110 -61
A .github/workflows/ci.yml +39 -0
@@ -0,0 +1,39 @@
1+name: CI
2+on:
3+ push:
4+ branches: [main]
5+ pull_request:
6+permissions:
7+ contents: read
8+concurrency:
9+ group: ci-${{ github.ref }}
10+ cancel-in-progress: true
11+jobs:
12+ # Runs as root in an Arch container, on Forgejo and GitHub alike. Arch
13+ # ships the libpipewire the bindings are generated against; gcc is for the
14+ # race detector. The integration tests need a running PipeWire and are run
15+ # by hand (go test -tags integration ./integration/).
16+ check:
17+ runs-on: ubuntu-latest
18+ container:
19+ image: archlinux:latest
20+ options: --init
21+ steps:
22+ - run: pacman -Syu --noconfirm --needed git go gcc pipewire
23+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24+ with:
25+ persist-credentials: false
26+ - run: |
27+ git config --global --add safe.directory "$GITHUB_WORKSPACE"
28+ test "$(go env GOVERSION | cut -d. -f1-2)" = "go$(sed -n 's/^go \([0-9]*\.[0-9]*\).*/\1/p' go.mod)"
29+ - run: test -z "$(gofmt -l spa pipewire integration internal/core internal/ports)"
30+ - run: CGO_ENABLED=0 go vet ./...
31+ - run: CGO_ENABLED=0 go vet -tags integration ./integration/
32+ - run: go run honnef.co/go/tools/cmd/staticcheck@2026.2.2 ./...
33+ - run: CGO_ENABLED=0 go test -count=1 ./...
34+ - run: CGO_ENABLED=1 go test -race -count=1 ./...
35+ - name: generated code is current
36+ run: |
37+ go install github.com/vektra/mockery/v3@v3.8.1
38+ PATH="$HOME/go/bin:$PATH" GOWORK=off go generate ./...
39+ git diff --exit-code
M .github/workflows/dependabot-auto-merge.yml +20 -5
@@ -2,10 +2,25 @@ name: Dependabot Auto-Merge
22
33 on: pull_request
44
5+permissions:
6+ contents: write
7+ pull-requests: write
8+
59 jobs:
10+ # Dependabot runs on GitHub only. A plain job (no reusable workflow) lets
11+ # the `if` skip it on Forgejo, which cannot resolve GitHub-only workflows.
612 auto-merge:
7- if: github.event.pull_request.user.login == 'dependabot[bot]'
8- permissions:
9- contents: write
10- pull-requests: write
11- uses: bnema/gh-actions/.github/workflows/dependabot-auto-merge.yml@main
13+ runs-on: ubuntu-latest
14+ if: github.actor == 'dependabot[bot]'
15+ steps:
16+ - name: Dependabot metadata
17+ id: metadata
18+ uses: dependabot/fetch-metadata@v3
19+ with:
20+ github-token: "${{ secrets.GITHUB_TOKEN }}"
21+ - name: Auto-merge patch and minor updates
22+ if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
23+ run: gh pr merge --auto --squash "$PR_URL"
24+ env:
25+ PR_URL: ${{ github.event.pull_request.html_url }}
26+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
M .mockery.yml +8 -0
@@ -16,6 +16,9 @@ packages:
1616 InitAPI:
1717 LoopAPI:
1818 StreamOps:
19+ VideoAPI:
20+ SharedMemAPI:
21+ VideoStreamAPI:
1922 github.com/bnema/purego-pipewire/pipewire:
2023 config:
2124 all: false
@@ -24,3 +27,8 @@ packages:
2427 Runtime:
2528 MainLoop:
2629 Player:
30+ github.com/bnema/purego-pipewire/internal/ports/in:
31+ config:
32+ all: false
33+ interfaces:
34+ VideoHandler:
M README.md +78 -1
@@ -23,7 +23,7 @@ The binding layer is **generated** from the checked-in model in `gen/pipewire.js
2323 - **Inbound port interfaces** for the handwritten core layer
2424 - **Public API wrappers** in `pipewire/` and SPA constants/helpers in `spa/`
2525
26-A small hand-written core implements runtime init/deinit, player lifecycle, stream setup, and PCM buffer handoff for playback.
26+A small hand-written core implements runtime init/deinit, player lifecycle, stream setup, and PCM buffer handoff for playback, plus the zero-allocation `VideoSource` (loop, context, core, memfd video buffers). The per-frame C calls of the video path use `purego.Syscall6` instead of `RegisterLibFunc` wrappers, which allocate on every call.
2727
2828 Mocks for public interfaces and outbound ports are generated by [Mockery v3](https://vektra.github.io/mockery/) and shipped in `pipewire/mocks/` and `internal/ports/out/mocks/`.
2929
@@ -86,6 +86,83 @@ if err := player.Start(); err != nil {
8686 }
8787 ```
8888
89+### Video source (screen-cast style producer)
90+
91+`VideoSource` publishes a PipeWire `Video/Source` node whose frames you write
92+yourself into shared memory (memfd). It drives the graph, so there is no
93+process callback to wait for, and a steady-state frame performs no heap
94+allocation. DMA-BUF is not supported; buffers are always memfd.
95+
96+A `Loop`, the `Context` and `Core` created on it, and the `VideoSource` must be
97+used from a single goroutine locked to one OS thread. You own the loop: poll
98+`Loop.Fd()` together with your other file descriptors and call `Loop.Iterate()`
99+when it is readable. Stream callbacks (`VideoHandler`) run inside `Iterate`.
100+
101+```go
102+import (
103+ "runtime"
104+ "time"
105+
106+ "github.com/bnema/purego-pipewire/pipewire"
107+)
108+
109+runtime.LockOSThread()
110+defer runtime.UnlockOSThread()
111+
112+rt, err := pipewire.Init()
113+if err != nil {
114+ return err
115+}
116+defer rt.Deinit()
117+
118+loop, err := rt.NewLoop()
119+if err != nil {
120+ return err
121+}
122+defer loop.Destroy()
123+ctx, err := rt.NewContext(loop)
124+if err != nil {
125+ return err
126+}
127+defer ctx.Destroy()
128+core, err := ctx.Connect()
129+if err != nil {
130+ return err
131+}
132+defer core.Disconnect()
133+
134+src, err := core.NewVideoSource(pipewire.VideoConfig{
135+ Name: "my-screen",
136+ Width: 1920,
137+ Height: 1080,
138+ MaxFPS: 60,
139+ Formats: []pipewire.VideoFormat{pipewire.VideoFormatBGRx, pipewire.VideoFormatBGRA},
140+}, handler) // handler implements pipewire.VideoHandler
141+if err != nil {
142+ return err
143+}
144+defer src.Destroy()
145+
146+// handler.StateChanged reports the node id once the stream is PAUSED or
147+// STREAMING; hand it to the consumer (for example pipewiresrc path=<id>).
148+
149+// One frame: Dequeue, fill, Queue, then Trigger, in that order.
150+if b := src.Dequeue(); b != nil { // nil: no buffer is free, skip the frame
151+ // b.Data is a mapped memfd, b.Stride bytes per row.
152+ copy(b.Data, frame)
153+ src.Queue(b, seq, time.Since(start), false) // true flags the frame corrupted
154+ src.Trigger()
155+}
156+```
157+
158+Buffers that PipeWire removes while you still hold them (renegotiation,
159+`Resize`, `Destroy`, the consumer leaving) stay mapped until you hand them back
160+with `Queue` or `Release`. `Resize` fails with `ErrVideoBorrowed` while a buffer
161+is borrowed, and `Destroy` is best called when none is. Formats are BGRx and
162+BGRA. A consumer that is not a plain `pipewiresrc` may need to declare
163+`media.type=Video` (for example `pipewiresrc stream-properties=props,media.type=Video`)
164+for WirePlumber to link it.
165+
89166 ### Testing with mocks
90167
91168 The package ships pre-generated [Mockery v3](https://vektra.github.io/mockery/) mocks for the public interfaces in `github.com/bnema/purego-pipewire/pipewire/mocks`.
M gen/pipewire.json +20 -2
@@ -6,7 +6,8 @@
66 "groups": [
77 {"name": "init", "interface": "InitAPI", "package": "out", "symbols": ["pw_init", "pw_deinit"]},
88 {"name": "loop", "interface": "LoopAPI", "package": "out", "symbols": ["pw_main_loop_new", "pw_main_loop_destroy", "pw_main_loop_run", "pw_main_loop_quit", "pw_main_loop_get_loop"]},
9- {"name": "stream_playback", "interface": "StreamPlaybackAPI", "package": "out", "symbols": ["pw_stream_new_simple", "pw_stream_connect", "pw_stream_set_active", "pw_stream_disconnect", "pw_stream_dequeue_buffer", "pw_stream_queue_buffer", "pw_stream_add_listener", "pw_stream_destroy"]}
9+ {"name": "stream_playback", "interface": "StreamPlaybackAPI", "package": "out", "symbols": ["pw_stream_new_simple", "pw_stream_connect", "pw_stream_set_active", "pw_stream_disconnect", "pw_stream_dequeue_buffer", "pw_stream_queue_buffer", "pw_stream_add_listener", "pw_stream_destroy"]},
10+ {"name": "video", "interface": "VideoAPI", "package": "out", "symbols": ["pw_loop_new", "pw_loop_destroy", "pw_loop_get_fd", "pw_loop_enter", "pw_loop_leave", "pw_loop_iterate", "pw_context_new", "pw_context_connect", "pw_context_destroy", "pw_core_disconnect", "pw_properties_new_string", "pw_stream_new", "pw_stream_update_params", "pw_stream_get_node_id", "pw_stream_trigger_process", "pw_stream_is_driving", "pw_stream_return_buffer"]}
1011 ],
1112 "symbols": [
1213 {"name": "pw_init", "library": "pipewire", "group": "init", "signature": "func(argc *int32, argv ***byte)"},
@@ -23,7 +24,24 @@
2324 {"name": "pw_stream_dequeue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer) unsafe.Pointer"},
2425 {"name": "pw_stream_queue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer, buffer unsafe.Pointer) int32"},
2526 {"name": "pw_stream_add_listener", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer, listener unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) int32"},
26- {"name": "pw_stream_destroy", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer)"}
27+ {"name": "pw_stream_destroy", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer)"},
28+ {"name": "pw_loop_new", "library": "pipewire", "group": "video", "signature": "func(props unsafe.Pointer) unsafe.Pointer"},
29+ {"name": "pw_loop_destroy", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer)"},
30+ {"name": "pw_loop_get_fd", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer) int32"},
31+ {"name": "pw_loop_enter", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer)"},
32+ {"name": "pw_loop_leave", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer)"},
33+ {"name": "pw_loop_iterate", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer, timeout int32) int32"},
34+ {"name": "pw_context_new", "library": "pipewire", "group": "video", "signature": "func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer"},
35+ {"name": "pw_context_connect", "library": "pipewire", "group": "video", "signature": "func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer"},
36+ {"name": "pw_context_destroy", "library": "pipewire", "group": "video", "signature": "func(context unsafe.Pointer)"},
37+ {"name": "pw_core_disconnect", "library": "pipewire", "group": "video", "signature": "func(core unsafe.Pointer) int32"},
38+ {"name": "pw_properties_new_string", "library": "pipewire", "group": "video", "signature": "func(args *byte) unsafe.Pointer"},
39+ {"name": "pw_stream_new", "library": "pipewire", "group": "video", "signature": "func(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer"},
40+ {"name": "pw_stream_update_params", "library": "pipewire", "group": "video", "signature": "func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32"},
41+ {"name": "pw_stream_get_node_id", "library": "pipewire", "group": "video", "signature": "func(stream unsafe.Pointer) uint32"},
42+ {"name": "pw_stream_trigger_process", "library": "pipewire", "group": "video", "signature": "func(stream unsafe.Pointer) int32"},
43+ {"name": "pw_stream_is_driving", "library": "pipewire", "group": "video", "signature": "func(stream unsafe.Pointer) bool"},
44+ {"name": "pw_stream_return_buffer", "library": "pipewire", "group": "video", "signature": "func(stream unsafe.Pointer, buffer unsafe.Pointer) int32"}
2745 ],
2846 "callbacks": [
2947 {"name": "pw_stream_events", "signature": "struct{version uint32; destroy *func(stream unsafe.Pointer); state_changed *func(stream unsafe.Pointer, old uint32, state uint32, error *byte); control_info *func(stream unsafe.Pointer, id uint32, control unsafe.Pointer); io_changed *func(stream unsafe.Pointer, id uint32, area unsafe.Pointer, size uint32); param_changed *func(stream unsafe.Pointer, id uint32, param unsafe.Pointer); add_buffer *func(stream unsafe.Pointer, buffer unsafe.Pointer); remove_buffer *func(stream unsafe.Pointer, buffer unsafe.Pointer); process *func(stream unsafe.Pointer); drained *func(stream unsafe.Pointer); command *func(stream unsafe.Pointer, cmd unsafe.Pointer); trigger_done *func(stream unsafe.Pointer)}", "group": "stream_playback"}
M go.mod +2 -2
@@ -1,9 +1,9 @@
11 module github.com/bnema/purego-pipewire
22
3-go 1.26
3+go 1.27
44
55 require (
6- github.com/bnema/purego v0.11.0-bnema.3
6+ github.com/bnema/purego v0.13.0-bnema.1
77 github.com/stretchr/testify v1.11.1
88 )
99
M go.sum +2 -2 generated file: collapsed
A integration/helpers_test.go +53 -0
@@ -0,0 +1,53 @@
1+//go:build integration
2+
3+package integration
4+
5+import (
6+ "fmt"
7+ "os"
8+ "strings"
9+ "syscall"
10+ "time"
11+ "unsafe"
12+)
13+
14+type pollFd struct {
15+ fd int32
16+ events int16
17+ revents int16
18+}
19+
20+const pollIn = 0x1
21+
22+// pollPFD and pollTS are package-level because syscall.Syscall6 forces
23+// pointer arguments passed through uintptr(unsafe.Pointer(&x)) to the heap.
24+// Only the test goroutine (locked to its thread) uses them.
25+var (
26+ pollPFD pollFd
27+ pollTS syscall.Timespec
28+)
29+
30+// waitReadable waits up to timeout for fd to become readable (or for a
31+// signal). It does not allocate, so it can sit inside testing.AllocsPerRun.
32+func waitReadable(fd int, timeout time.Duration) {
33+ pollPFD = pollFd{fd: int32(fd), events: pollIn}
34+ pollTS = syscall.Timespec{Sec: int64(timeout / time.Second), Nsec: int64(timeout % time.Second)}
35+ _, _, _ = syscall.Syscall6(syscall.SYS_PPOLL, uintptr(unsafe.Pointer(&pollPFD)), 1, uintptr(unsafe.Pointer(&pollTS)), 0, 0, 0)
36+}
37+
38+// isMapped reports whether the address of p lies inside a mapping listed in
39+// /proc/self/maps.
40+func isMapped(p *byte) bool {
41+ addr := uint64(uintptr(unsafe.Pointer(p)))
42+ b, err := os.ReadFile("/proc/self/maps")
43+ if err != nil {
44+ return true
45+ }
46+ for _, line := range strings.Split(string(b), "\n") {
47+ var lo, hi uint64
48+ if _, err := fmt.Sscanf(line, "%x-%x", &lo, &hi); err == nil && addr >= lo && addr < hi {
49+ return true
50+ }
51+ }
52+ return false
53+}
A integration/race_off_test.go +5 -0
@@ -0,0 +1,5 @@
1+//go:build integration && !race
2+
3+package integration
4+
5+const raceEnabled = false
A integration/race_on_test.go +5 -0
@@ -0,0 +1,5 @@
1+//go:build integration && race
2+
3+package integration
4+
5+const raceEnabled = true
A integration/video_test.go +371 -0
@@ -0,0 +1,371 @@
1+//go:build integration
2+
3+package integration
4+
5+import (
6+ "context"
7+ "fmt"
8+ "os"
9+ "os/exec"
10+ "path/filepath"
11+ "runtime"
12+ "sync/atomic"
13+ "testing"
14+ "time"
15+
16+ "github.com/bnema/purego-pipewire/internal/ports/in/mocks"
17+ "github.com/bnema/purego-pipewire/pipewire"
18+ "github.com/bnema/purego-pipewire/spa"
19+ "github.com/stretchr/testify/mock"
20+)
21+
22+const (
23+ vidW = 320
24+ vidH = 240
25+)
26+
27+// videoEvents records what the source reports. Callbacks run on the loop
28+// thread; the fields the test reads are atomics.
29+type videoEvents struct {
30+ state atomic.Int32
31+ nodeID atomic.Uint32
32+ format atomic.Uint32
33+ width atomic.Int32
34+ height atomic.Int32
35+ stride atomic.Int32
36+ buffers atomic.Int32
37+ statesCh chan int32
38+}
39+
40+// newVideoHandler returns a generated VideoHandler mock that records every
41+// callback into the returned events.
42+func newVideoHandler(t *testing.T) (*mocks.MockVideoHandler, *videoEvents) {
43+ ev := &videoEvents{statesCh: make(chan int32, 64)}
44+ h := mocks.NewMockVideoHandler(t)
45+ h.EXPECT().StateChanged(mock.Anything, mock.Anything, mock.Anything).Run(func(state int32, nodeID uint32, _ string) {
46+ ev.state.Store(state)
47+ if nodeID != 0 {
48+ ev.nodeID.Store(nodeID)
49+ }
50+ select {
51+ case ev.statesCh <- state:
52+ default:
53+ }
54+ }).Maybe()
55+ h.EXPECT().FormatChanged(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(f spa.VideoFormat, w, hh, stride int) {
56+ ev.format.Store(uint32(f))
57+ ev.width.Store(int32(w))
58+ ev.height.Store(int32(hh))
59+ ev.stride.Store(int32(stride))
60+ }).Maybe()
61+ h.EXPECT().BuffersChanged().Run(func() { ev.buffers.Add(1) }).Maybe()
62+ return h, ev
63+}
64+
65+type videoEnv struct {
66+ rt pipewire.Runtime
67+ loop pipewire.Loop
68+ ctx pipewire.Context
69+ core pipewire.Core
70+ src *pipewire.VideoSource
71+ handler *videoEvents
72+}
73+
74+func (e *videoEnv) close() {
75+ if e.src != nil {
76+ e.src.Destroy()
77+ }
78+ if e.core != nil {
79+ e.core.Disconnect()
80+ }
81+ if e.ctx != nil {
82+ e.ctx.Destroy()
83+ }
84+ if e.loop != nil {
85+ e.loop.Destroy()
86+ }
87+ e.rt.Deinit()
88+}
89+
90+// pollIterate waits up to timeout for the loop fd and iterates once.
91+func pollIterate(loop pipewire.Loop, timeout time.Duration) {
92+ waitReadable(loop.Fd(), timeout)
93+ loop.Iterate()
94+}
95+
96+func skipUnlessPipeWire(t *testing.T) {
97+ t.Helper()
98+ dir := os.Getenv("XDG_RUNTIME_DIR")
99+ if dir == "" {
100+ t.Skip("XDG_RUNTIME_DIR not set")
101+ }
102+ if _, err := os.Stat(filepath.Join(dir, "pipewire-0")); err != nil {
103+ t.Skipf("no PipeWire socket: %v", err)
104+ }
105+}
106+
107+// newVideoEnv must be called from a goroutine locked to its OS thread.
108+func newVideoEnv(t *testing.T) *videoEnv {
109+ t.Helper()
110+ skipUnlessPipeWire(t)
111+ rt, err := pipewire.Init()
112+ if err != nil {
113+ t.Fatalf("Init: %v", err)
114+ }
115+ h, ev := newVideoHandler(t)
116+ e := &videoEnv{rt: rt, handler: ev}
117+ if e.loop, err = rt.NewLoop(); err != nil {
118+ e.close()
119+ t.Fatalf("NewLoop: %v", err)
120+ }
121+ if e.ctx, err = rt.NewContext(e.loop); err != nil {
122+ e.close()
123+ t.Fatalf("NewContext: %v", err)
124+ }
125+ if e.core, err = e.ctx.Connect(); err != nil {
126+ e.close()
127+ t.Skipf("cannot connect to PipeWire: %v", err)
128+ }
129+ e.src, err = e.core.NewVideoSource(pipewire.VideoConfig{
130+ Name: "purego-pipewire-test", Width: vidW, Height: vidH, MaxFPS: 60,
131+ Formats: []pipewire.VideoFormat{pipewire.VideoFormatBGRx},
132+ }, h)
133+ if err != nil {
134+ e.close()
135+ t.Fatalf("NewVideoSource: %v", err)
136+ }
137+ return e
138+}
139+
140+// waitNode iterates until the node id is known.
141+func (e *videoEnv) waitNode(t *testing.T) uint32 {
142+ t.Helper()
143+ deadline := time.Now().Add(5 * time.Second)
144+ for e.handler.nodeID.Load() == 0 {
145+ if time.Now().After(deadline) {
146+ t.Fatal("timed out waiting for the node id")
147+ }
148+ pollIterate(e.loop, 20*time.Millisecond)
149+ }
150+ return e.handler.nodeID.Load()
151+}
152+
153+// produce runs one Dequeue -> fill -> Queue -> Trigger cycle. It reports whether a
154+// buffer was available.
155+func (e *videoEnv) produce(seq uint64, fill byte) bool {
156+ b := e.src.Dequeue()
157+ if b == nil {
158+ return false
159+ }
160+ // Touch the first and last byte of the frame; filling everything would
161+ // dominate the allocation-free path being measured.
162+ b.Data[0] = fill
163+ b.Data[len(b.Data)-1] = fill
164+ e.src.Queue(b, seq, time.Duration(seq)*time.Second/60, false)
165+ e.src.Trigger()
166+ return true
167+}
168+
169+// gstMediaType makes pipewiresrc declare media.type=Video. Without it the
170+// stream registers as Stream/Input/Unknown and WirePlumber refuses to link it
171+// to a Video node ("target not found").
172+const gstMediaType = "stream-properties=props,media.type=Video"
173+
174+func startGst(t *testing.T, args ...string) *exec.Cmd {
175+ t.Helper()
176+ path, err := exec.LookPath("gst-launch-1.0")
177+ if err != nil {
178+ t.Skip("gst-launch-1.0 not found")
179+ }
180+ cmd := exec.Command(path, args...)
181+ cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
182+ if err := cmd.Start(); err != nil {
183+ t.Fatalf("start gst-launch-1.0: %v", err)
184+ }
185+ return cmd
186+}
187+
188+func TestVideoSource(t *testing.T) {
189+ runtime.LockOSThread()
190+ defer runtime.UnlockOSThread()
191+ skipUnlessPipeWire(t)
192+ if _, err := exec.LookPath("gst-launch-1.0"); err != nil {
193+ t.Skip("gst-launch-1.0 not found")
194+ }
195+ e := newVideoEnv(t)
196+ defer e.close()
197+ node := e.waitNode(t)
198+
199+ cmd := startGst(t, "-q", "pipewiresrc", fmt.Sprintf("path=%d", node), gstMediaType, "num-buffers=120", "!",
200+ fmt.Sprintf("video/x-raw,format=BGRx,width=%d,height=%d", vidW, vidH), "!", "fakesink")
201+ done := make(chan error, 1)
202+ go func() { done <- cmd.Wait() }()
203+ defer func() { _ = cmd.Process.Kill() }()
204+
205+ var seq, produced uint64
206+ next := time.Now()
207+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
208+ defer cancel()
209+ for {
210+ select {
211+ case err := <-done:
212+ if err != nil {
213+ t.Fatalf("gst-launch-1.0 failed: %v", err)
214+ }
215+ if produced < 120 {
216+ t.Fatalf("gst finished after only %d produced frames", produced)
217+ }
218+ if got := e.handler.width.Load(); got != vidW || e.handler.height.Load() != vidH || e.handler.stride.Load() != vidW*4 {
219+ t.Fatalf("negotiated %dx%d stride %d", got, e.handler.height.Load(), e.handler.stride.Load())
220+ }
221+ if e.handler.format.Load() != uint32(pipewire.VideoFormatBGRx) {
222+ t.Fatalf("negotiated format %d", e.handler.format.Load())
223+ }
224+ t.Logf("gst received 120 frames; produced %d", produced)
225+ return
226+ case <-ctx.Done():
227+ t.Fatalf("timed out; produced %d frames", produced)
228+ default:
229+ }
230+ // 60 Hz tick from the loop's poll timeout.
231+ wait := time.Until(next)
232+ if wait < 0 {
233+ wait = 0
234+ }
235+ pollIterate(e.loop, wait)
236+ if !time.Now().Before(next) {
237+ next = next.Add(time.Second / 60)
238+ if e.handler.state.Load() == pipewire.StreamStateStreaming && e.produce(seq, byte(seq)) {
239+ seq++
240+ produced++
241+ }
242+ }
243+ }
244+}
245+
246+func TestVideoSourceConsumerLeaves(t *testing.T) {
247+ runtime.LockOSThread()
248+ defer runtime.UnlockOSThread()
249+ skipUnlessPipeWire(t)
250+ if _, err := exec.LookPath("gst-launch-1.0"); err != nil {
251+ t.Skip("gst-launch-1.0 not found")
252+ }
253+ e := newVideoEnv(t)
254+ defer e.close()
255+ node := e.waitNode(t)
256+
257+ cmd := startGst(t, "-q", "pipewiresrc", fmt.Sprintf("path=%d", node), gstMediaType, "!", "fakesink")
258+ defer func() { _ = cmd.Process.Kill(); _ = cmd.Wait() }()
259+
260+ // Stream until the consumer has started streaming.
261+ deadline := time.Now().Add(10 * time.Second)
262+ var seq uint64
263+ for e.handler.state.Load() != pipewire.StreamStateStreaming {
264+ if time.Now().After(deadline) {
265+ t.Fatal("stream never reached STREAMING")
266+ }
267+ pollIterate(e.loop, 16*time.Millisecond)
268+ }
269+ var borrowed *pipewire.VideoBuffer
270+ for borrowed == nil {
271+ if time.Now().After(deadline) {
272+ t.Fatal("no buffer to borrow")
273+ }
274+ pollIterate(e.loop, 16*time.Millisecond)
275+ e.produce(seq, 1)
276+ seq++
277+ borrowed = e.src.Dequeue()
278+ }
279+ data := borrowed.Data
280+ buffersBefore := e.handler.buffers.Load()
281+
282+ // Kill the consumer while holding a buffer.
283+ _ = cmd.Process.Kill()
284+ _ = cmd.Wait()
285+ deadline = time.Now().Add(10 * time.Second)
286+ for st := e.handler.state.Load(); st == pipewire.StreamStateStreaming; st = e.handler.state.Load() {
287+ if time.Now().After(deadline) {
288+ t.Fatal("stream stayed STREAMING after the consumer left")
289+ }
290+ pollIterate(e.loop, 20*time.Millisecond)
291+ e.src.Trigger()
292+ }
293+ if st := e.handler.state.Load(); st != pipewire.StreamStatePaused && st != pipewire.StreamStateUnconnected {
294+ t.Fatalf("state after consumer left = %d, want PAUSED or UNCONNECTED", st)
295+ }
296+
297+ // The borrowed buffer must still be writable (no SIGBUS/SIGSEGV), even if
298+ // PipeWire removed it meanwhile.
299+ data[0] = 0xEE
300+ data[len(data)-1] = 0xEE
301+ if !isMapped(&data[0]) {
302+ t.Fatal("borrowed buffer was unmapped before Release")
303+ }
304+
305+ // Give the buffer back. If PipeWire removed it while borrowed, Release must
306+ // unmap it now; otherwise it goes back to PipeWire and stays mapped.
307+ removed := e.handler.buffers.Load() != buffersBefore
308+ e.src.Release(borrowed)
309+ if removed && isMapped(&data[0]) {
310+ t.Fatal("removed buffer still mapped after Release")
311+ }
312+ t.Logf("consumer left: removed while borrowed = %v", removed)
313+ e.src.Release(borrowed) // a second Release is ignored
314+}
315+
316+func TestVideoSourceFrameAllocations(t *testing.T) {
317+ runtime.LockOSThread()
318+ defer runtime.UnlockOSThread()
319+ skipUnlessPipeWire(t)
320+ if _, err := exec.LookPath("gst-launch-1.0"); err != nil {
321+ t.Skip("gst-launch-1.0 not found")
322+ }
323+ if raceEnabled {
324+ t.Skip("allocation counts are not meaningful under -race")
325+ }
326+ e := newVideoEnv(t)
327+ defer e.close()
328+ node := e.waitNode(t)
329+
330+ cmd := startGst(t, "-q", "pipewiresrc", fmt.Sprintf("path=%d", node), gstMediaType, "!", "fakesink", "sync=false")
331+ defer func() { _ = cmd.Process.Kill(); _ = cmd.Wait() }()
332+
333+ deadline := time.Now().Add(15 * time.Second)
334+ for e.handler.state.Load() != pipewire.StreamStateStreaming {
335+ if time.Now().After(deadline) {
336+ t.Fatal("stream never reached STREAMING")
337+ }
338+ pollIterate(e.loop, 16*time.Millisecond)
339+ }
340+ // Warm up so every buffer and every lazily initialised path has been used.
341+ var seq uint64
342+ for i := 0; i < 60; i++ {
343+ pollIterate(e.loop, 16*time.Millisecond)
344+ if e.produce(seq, 1) {
345+ seq++
346+ }
347+ }
348+ if seq == 0 {
349+ t.Fatal("no frame could be produced during warm-up")
350+ }
351+
352+ // Each run waits (without allocating) for the loop fd, so that the consumer
353+ // has returned a buffer and most runs queue and trigger a real frame.
354+ loopFd := e.loop.Fd() // read once, like a real poll loop does
355+ var produced int
356+ allocs := testing.AllocsPerRun(200, func() {
357+ waitReadable(loopFd, 17*time.Millisecond)
358+ e.loop.Iterate()
359+ if e.produce(seq, byte(seq)) {
360+ seq++
361+ produced++
362+ }
363+ })
364+ t.Logf("allocs per Dequeue/fill/Queue/Trigger/Iterate cycle: %v (%d frames queued)", allocs, produced)
365+ if allocs != 0 {
366+ t.Fatalf("allocs per cycle = %v, want 0", allocs)
367+ }
368+ if produced < 50 {
369+ t.Fatalf("only %d frames were produced inside the measured closure, want >= 50", produced)
370+ }
371+}
M internal/capi/adapters_gen.go +73 -0 generated file: collapsed
M internal/capi/register_all.go +2 -1
@@ -9,5 +9,6 @@ func registerAll(h loader.Handles) error {
99 registerLoop(h.PipeWire)
1010 registerLoopLock(h.PipeWire)
1111 registerStream_playback(h.PipeWire)
12- return nil
12+ registerVideo(h.PipeWire)
13+ return registerVideoFast(h.PipeWire)
1314 }
A internal/capi/video_fast.go +92 -0
@@ -0,0 +1,92 @@
1+package capi
2+
3+import (
4+ "unsafe"
5+
6+ "github.com/bnema/purego"
7+)
8+
9+// The generated bindings call C through purego.RegisterLibFunc, whose
10+// reflect-based wrappers allocate on every call. The calls made once per
11+// video frame (and once per loop iteration) must not allocate, so they go
12+// through purego.Syscall6 instead: the methods below shadow the generated
13+// adapter methods of the same name on defaultCAPI. Everything else keeps using
14+// the generated bindings.
15+//
16+// All hot-path functions take only pointers/integers and return an integer
17+// (or nothing), which is what Syscall6 supports.
18+var (
19+ symLoopGetFd uintptr
20+ symLoopEnter uintptr
21+ symLoopLeave uintptr
22+ symLoopIterate uintptr
23+ symStreamDequeueBuffer uintptr
24+ symStreamQueueBuffer uintptr
25+ symStreamReturnBuffer uintptr
26+ symStreamTrigger uintptr
27+)
28+
29+func registerVideoFast(handle uintptr) error {
30+ for _, s := range []struct {
31+ dst *uintptr
32+ name string
33+ }{
34+ {&symLoopGetFd, "pw_loop_get_fd"},
35+ {&symLoopEnter, "pw_loop_enter"},
36+ {&symLoopLeave, "pw_loop_leave"},
37+ {&symLoopIterate, "pw_loop_iterate"},
38+ {&symStreamDequeueBuffer, "pw_stream_dequeue_buffer"},
39+ {&symStreamQueueBuffer, "pw_stream_queue_buffer"},
40+ {&symStreamReturnBuffer, "pw_stream_return_buffer"},
41+ {&symStreamTrigger, "pw_stream_trigger_process"},
42+ } {
43+ addr, err := purego.Dlsym(handle, s.name)
44+ if err != nil {
45+ return err
46+ }
47+ *s.dst = addr
48+ }
49+ return nil
50+}
51+
52+// uptr converts a C pointer returned as an integer without a
53+// uintptr-to-unsafe.Pointer conversion (the memory is owned by C).
54+func uptr(v uintptr) unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&v)) }
55+
56+func (*defaultCAPI) PWLoopGetFd(loop unsafe.Pointer) int32 {
57+ r, _, _ := purego.Syscall6(symLoopGetFd, uintptr(loop), 0, 0, 0, 0, 0)
58+ return int32(r)
59+}
60+
61+func (*defaultCAPI) PWLoopEnter(loop unsafe.Pointer) {
62+ purego.Syscall6(symLoopEnter, uintptr(loop), 0, 0, 0, 0, 0)
63+}
64+
65+func (*defaultCAPI) PWLoopLeave(loop unsafe.Pointer) {
66+ purego.Syscall6(symLoopLeave, uintptr(loop), 0, 0, 0, 0, 0)
67+}
68+
69+func (*defaultCAPI) PWLoopIterate(loop unsafe.Pointer, timeout int32) int32 {
70+ r, _, _ := purego.Syscall6(symLoopIterate, uintptr(loop), uintptr(uint32(timeout)), 0, 0, 0, 0)
71+ return int32(r)
72+}
73+
74+func (*defaultCAPI) PWStreamDequeueBuffer(stream unsafe.Pointer) unsafe.Pointer {
75+ r, _, _ := purego.Syscall6(symStreamDequeueBuffer, uintptr(stream), 0, 0, 0, 0, 0)
76+ return uptr(r)
77+}
78+
79+func (*defaultCAPI) PWStreamQueueBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
80+ r, _, _ := purego.Syscall6(symStreamQueueBuffer, uintptr(stream), uintptr(buffer), 0, 0, 0, 0)
81+ return int32(r)
82+}
83+
84+func (*defaultCAPI) PWStreamReturnBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
85+ r, _, _ := purego.Syscall6(symStreamReturnBuffer, uintptr(stream), uintptr(buffer), 0, 0, 0, 0)
86+ return int32(r)
87+}
88+
89+func (*defaultCAPI) PWStreamTriggerProcess(stream unsafe.Pointer) int32 {
90+ r, _, _ := purego.Syscall6(symStreamTrigger, uintptr(stream), 0, 0, 0, 0, 0)
91+ return int32(r)
92+}
A internal/capi/video_gen.go +137 -0 generated file: collapsed
M internal/core/context.go +34 -8
@@ -3,27 +3,53 @@ package core
33 import (
44 "errors"
55 "unsafe"
6+
7+ portout "github.com/bnema/purego-pipewire/internal/ports/out"
68 )
79
810 var ErrContextCreate = errors.New("failed to create context")
911
12+// Context wraps a pw_context. Create it with Runtime.NewContext and destroy
13+// it before the Loop it was created on.
1014 type Context struct {
11- ptr unsafe.Pointer
15+ ptr unsafe.Pointer
16+ capi portout.VideoStreamAPI
17+ shm portout.SharedMemAPI
1218 }
1319
14-func (r *Runtime) NewContext() (*Context, error) {
15- // For now, return a placeholder - full implementation
16- // will require generated context methods
17- return nil, ErrContextCreate
20+// NewContext creates a pw_context that runs on loop. The loop must outlive
21+// the context.
22+func (r *Runtime) NewContext(loop *Loop) (*Context, error) {
23+ if loop == nil || loop.ptr == nil {
24+ return nil, ErrContextCreate
25+ }
26+ ptr := r.capi.PWContextNew(loop.ptr, nil, 0)
27+ if ptr == nil {
28+ return nil, ErrContextCreate
29+ }
30+ return &Context{ptr: ptr, capi: r.capi, shm: memfdShm{}}, nil
1831 }
1932
33+// Destroy destroys the context. All cores connected from it must have been
34+// disconnected first. Destroy is idempotent.
2035 func (c *Context) Destroy() {
21- // Placeholder for context destruction
36+ if c.ptr == nil {
37+ return
38+ }
39+ c.capi.PWContextDestroy(c.ptr)
40+ c.ptr = nil
2241 }
2342
43+// Connect connects to the PipeWire daemon.
2444 func (c *Context) Connect() (*Core, error) {
25- // Placeholder - will connect and return a Core
26- return nil, errors.New("not implemented")
45+ if c.ptr == nil {
46+ return nil, ErrCoreConnect
47+ }
48+ ptr := c.capi.PWContextConnect(c.ptr, nil, 0)
49+ if ptr == nil {
50+ return nil, ErrCoreConnect
51+ }
52+ return &Core{ptr: ptr, capi: c.capi, shm: c.shm}, nil
2753 }
2854
2955 func (c *Context) Ptr() unsafe.Pointer {
M internal/core/core.go +11 -1
@@ -3,6 +3,8 @@ package core
33 import (
44 "errors"
55 "unsafe"
6+
7+ portout "github.com/bnema/purego-pipewire/internal/ports/out"
68 )
79
810 var ErrCoreConnect = errors.New("failed to connect core")
@@ -10,11 +12,19 @@ var ErrRegistryUnavailable = errors.New("registry unavailable")
1012
1113 type Core struct {
1214 ptr unsafe.Pointer
15+ capi portout.VideoStreamAPI
16+ shm portout.SharedMemAPI
1317 registry *Registry
1418 }
1519
20+// Disconnect disconnects from the daemon. Streams created from the core must
21+// have been destroyed first. Disconnect is idempotent.
1622 func (c *Core) Disconnect() {
17- // Placeholder for core disconnection
23+ if c.ptr == nil || c.capi == nil {
24+ return
25+ }
26+ c.capi.PWCoreDisconnect(c.ptr)
27+ c.ptr = nil
1828 }
1929
2030 func (c *Core) Registry() (*Registry, error) {
M internal/core/loop.go +50 -0
@@ -33,3 +33,53 @@ func (ml *MainLoop) Destroy() {
3333 func (ml *MainLoop) Ptr() unsafe.Pointer {
3434 return ml.ptr
3535 }
36+
37+var ErrLoopCreate = errors.New("failed to create loop")
38+
39+// Loop wraps a pw_loop that the caller iterates itself, typically from a
40+// poll loop that waits on Fd. A Loop and everything created on it (Context,
41+// Core, streams) must only be used from one goroutine locked to one OS
42+// thread (runtime.LockOSThread); PipeWire loops are not thread safe.
43+type Loop struct {
44+ ptr unsafe.Pointer
45+ capi portout.VideoStreamAPI
46+}
47+
48+// NewLoop creates a pw_loop. The Runtime must be initialized.
49+func (r *Runtime) NewLoop() (*Loop, error) {
50+ ptr := r.capi.PWLoopNew(nil)
51+ if ptr == nil {
52+ return nil, ErrLoopCreate
53+ }
54+ return &Loop{ptr: ptr, capi: r.capi}, nil
55+}
56+
57+// Fd returns the pollable file descriptor of the loop. It becomes readable
58+// when Iterate has work to do.
59+func (l *Loop) Fd() int {
60+ return int(l.capi.PWLoopGetFd(l.ptr))
61+}
62+
63+// Iterate dispatches the pending events without blocking (pw_loop_enter,
64+// pw_loop_iterate with a zero timeout, pw_loop_leave). Stream callbacks run
65+// inside it. A negative result such as -EINTR is not fatal and is ignored:
66+// the next Iterate simply retries.
67+func (l *Loop) Iterate() {
68+ l.capi.PWLoopEnter(l.ptr)
69+ _ = l.capi.PWLoopIterate(l.ptr, 0)
70+ l.capi.PWLoopLeave(l.ptr)
71+}
72+
73+// Destroy destroys the loop. Contexts created on it must be destroyed first.
74+// Destroy is idempotent.
75+func (l *Loop) Destroy() {
76+ if l.ptr == nil {
77+ return
78+ }
79+ l.capi.PWLoopDestroy(l.ptr)
80+ l.ptr = nil
81+}
82+
83+func (l *Loop) Ptr() unsafe.Pointer {
84+ return l.ptr
85+}
A internal/core/shm.go +57 -0
@@ -0,0 +1,57 @@
1+package core
2+
3+import (
4+ "fmt"
5+ "syscall"
6+ "unsafe"
7+)
8+
9+const (
10+ mfdCloexec = 0x1
11+ mfdAllowSealing = 0x2
12+ fAddSeals = 0x409 // F_ADD_SEALS
13+ fSealSeal = 0x1
14+ fSealShrink = 0x2
15+ fSealGrow = 0x4
16+)
17+
18+// memfdShm is the production portout.SharedMemAPI backed by memfd_create.
19+type memfdShm struct{}
20+
21+func (memfdShm) NewSharedMem(name string, size int) (int, []byte, error) {
22+ if size <= 0 {
23+ return -1, nil, fmt.Errorf("memfd: invalid size %d", size)
24+ }
25+ cname, err := syscall.BytePtrFromString(name)
26+ if err != nil {
27+ return -1, nil, err
28+ }
29+ r, _, errno := syscall.Syscall(sysMemfdCreate, uintptr(unsafe.Pointer(cname)), mfdCloexec|mfdAllowSealing, 0)
30+ if errno != 0 {
31+ return -1, nil, fmt.Errorf("memfd_create: %w", errno)
32+ }
33+ fd := int(r)
34+ if err := syscall.Ftruncate(fd, int64(size)); err != nil {
35+ _ = syscall.Close(fd)
36+ return -1, nil, fmt.Errorf("memfd ftruncate: %w", err)
37+ }
38+ if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), fAddSeals, fSealShrink|fSealGrow|fSealSeal); errno != 0 {
39+ _ = syscall.Close(fd)
40+ return -1, nil, fmt.Errorf("memfd seal: %w", errno)
41+ }
42+ data, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
43+ if err != nil {
44+ _ = syscall.Close(fd)
45+ return -1, nil, fmt.Errorf("memfd mmap: %w", err)
46+ }
47+ return fd, data, nil
48+}
49+
50+func (memfdShm) FreeSharedMem(fd int, data []byte) {
51+ if data != nil {
52+ _ = syscall.Munmap(data)
53+ }
54+ if fd >= 0 {
55+ _ = syscall.Close(fd)
56+ }
57+}
A internal/core/shm_amd64.go +5 -0
@@ -0,0 +1,5 @@
1+package core
2+
3+// sysMemfdCreate is the memfd_create syscall number (not exported by package
4+// syscall).
5+const sysMemfdCreate = 319
A internal/core/shm_arm64.go +5 -0
@@ -0,0 +1,5 @@
1+package core
2+
3+// sysMemfdCreate is the memfd_create syscall number (not exported by package
4+// syscall).
5+const sysMemfdCreate = 279
A internal/core/shm_test.go +38 -0
@@ -0,0 +1,38 @@
1+package core
2+
3+import (
4+ "os"
5+ "strconv"
6+ "syscall"
7+ "testing"
8+)
9+
10+func TestMemfdShmRoundTrip(t *testing.T) {
11+ fd, data, err := memfdShm{}.NewSharedMem("pw-video-test", 4096)
12+ if err != nil {
13+ t.Skipf("memfd_create unavailable: %v", err)
14+ }
15+ if len(data) != 4096 {
16+ t.Fatalf("len = %d", len(data))
17+ }
18+ data[0], data[4095] = 1, 2
19+ // Re-map through the fd: the memory is shared and sealed against resizing.
20+ again, err := syscall.Mmap(fd, 0, 4096, syscall.PROT_READ, syscall.MAP_SHARED)
21+ if err != nil {
22+ t.Fatal(err)
23+ }
24+ if again[0] != 1 || again[4095] != 2 {
25+ t.Fatal("memfd is not shared")
26+ }
27+ if err := syscall.Ftruncate(fd, 8192); err == nil {
28+ t.Fatal("sealed memfd was resized")
29+ }
30+ _ = syscall.Munmap(again)
31+ memfdShm{}.FreeSharedMem(fd, data)
32+ if _, err := os.Stat("/proc/self/fd/" + strconv.Itoa(fd)); err == nil {
33+ t.Fatal("fd still open after FreeSharedMem")
34+ }
35+ if _, _, err := (memfdShm{}).NewSharedMem("x", 0); err == nil {
36+ t.Fatal("zero size accepted")
37+ }
38+}
A internal/core/video.go +634 -0
@@ -0,0 +1,634 @@
1+package core
2+
3+import (
4+ "errors"
5+ "fmt"
6+ "log/slog"
7+ "math"
8+ "strings"
9+ "sync"
10+ "sync/atomic"
11+ "time"
12+ "unsafe"
13+
14+ "github.com/bnema/purego"
15+
16+ portin "github.com/bnema/purego-pipewire/internal/ports/in"
17+ portout "github.com/bnema/purego-pipewire/internal/ports/out"
18+ "github.com/bnema/purego-pipewire/spa"
19+)
20+
21+// Video source limits. They are fixed so that nothing is allocated after
22+// NewVideoSource except the memfd mappings made in add_buffer.
23+const (
24+ videoMaxBuffers = 16 // Buffers param maximum, size of the buffer slot table
25+ videoMaxSources = 64 // size of the process-wide callback slot table
26+ videoPodSize = 512
27+
28+ pwStreamFlagDriver = 0x8
29+ pwStreamFlagAllocBuffers = 0x100
30+ pwStreamStateStreaming = 3
31+ pwStreamStatePaused = 2
32+
33+ defaultVideoFPS = 60
34+)
35+
36+var (
37+ ErrVideoConfig = errors.New("invalid video source config")
38+ ErrVideoTableFull = errors.New("too many video sources")
39+ ErrVideoStreamCreate = errors.New("failed to create video stream")
40+ ErrVideoConnect = errors.New("failed to connect video stream")
41+ ErrVideoResize = errors.New("failed to resize video stream")
42+ ErrVideoBorrowed = errors.New("video buffer still borrowed")
43+)
44+
45+// VideoConfig configures a VideoSource. MaxFPS defaults to 60 and Formats to
46+// BGRx. Only BGRx and BGRA are supported: buffers hold 4 bytes per pixel.
47+type VideoConfig struct {
48+ Name string
49+ Width, Height int
50+ MaxFPS int
51+ Formats []spa.VideoFormat
52+}
53+
54+// VideoBuffer is a buffer that was handed out by VideoSource.Dequeue. Data
55+// aliases a memfd mapping owned by the source: it stays valid until the buffer
56+// is given back with Queue or Release, and not afterwards. The structure is
57+// owned by the source; do not copy it, retain it or modify it.
58+type VideoBuffer struct {
59+ Index int
60+ Data []byte
61+ Stride, Width, Height int
62+ Format spa.VideoFormat
63+}
64+
65+// C layouts (amd64/arm64). Offsets are asserted in tests.
66+
67+// videoPWBuffer mirrors struct pw_buffer; UserData carries slot index+1.
68+type videoPWBuffer struct {
69+ Buffer *spaBuffer
70+ UserData uintptr
71+ Size uint64
72+}
73+
74+// spaMeta mirrors struct spa_meta.
75+type spaMeta struct {
76+ Type uint32
77+ Size uint32
78+ Data unsafe.Pointer
79+}
80+
81+// spaMetaHeader mirrors struct spa_meta_header.
82+type spaMetaHeader struct {
83+ Flags uint32
84+ Offset uint32
85+ PTS int64
86+ DTSOffset int64
87+ Seq uint64
88+}
89+
90+// videoEvents mirrors struct pw_stream_events (version 2).
91+type videoEvents struct {
92+ Version uint32
93+ Destroy uintptr
94+ StateChanged uintptr
95+ ControlInfo uintptr
96+ IOChanged uintptr
97+ ParamChanged uintptr
98+ AddBuffer uintptr
99+ RemoveBuf uintptr
100+ Process uintptr
101+ Drained uintptr
102+ Command uintptr
103+ TriggerDone uintptr
104+}
105+
106+type videoSlot struct {
107+ buf VideoBuffer
108+ pw unsafe.Pointer // struct pw_buffer; nil once removed
109+ hdr *spaMetaHeader // nil if the buffer has no header meta
110+ chunk *spaChunk
111+ fd int
112+ mem []byte
113+ live bool // slot holds a mapped buffer
114+ removed bool // PipeWire removed the buffer while it was borrowed
115+ borrowed bool
116+}
117+
118+// VideoSource is a PipeWire Video/Source node that drives the graph and
119+// exposes memfd-backed buffers. Every method, and every handler callback, runs
120+// on the loop thread: the goroutine (locked to its OS thread) that calls
121+// Loop.Iterate.
122+//
123+// Frames are produced with Dequeue, then writing Data, then Queue, then
124+// Trigger. The buffer must be queued before Trigger, because the process
125+// callback does nothing: PipeWire picks up queued buffers when triggered.
126+type VideoSource struct {
127+ capi portout.VideoStreamAPI
128+ shm portout.SharedMemAPI
129+ handler portin.VideoHandler
130+
131+ stream unsafe.Pointer
132+ table int // index in videoTable
133+ events videoEvents
134+ hook [8]uint64 // struct spa_hook, 48 bytes
135+ params [2]unsafe.Pointer
136+ enumPod [videoPodSize]byte
137+ buffPod [videoPodSize]byte
138+ metaPod [128]byte
139+ cfg VideoConfig
140+ formats [4]spa.VideoFormat
141+ nformats int
142+
143+ state int32
144+ nodeID uint32
145+ format spa.VideoFormat
146+ width int
147+ height int
148+ stride int
149+ negotiated bool
150+ closed bool
151+ warned bool
152+
153+ slots [videoMaxBuffers]videoSlot
154+}
155+
156+var (
157+ videoTable [videoMaxSources]atomic.Pointer[VideoSource]
158+
159+ videoCBOnce sync.Once
160+ videoCB struct {
161+ stateChanged, paramChanged, addBuffer, removeBuffer, process, triggerDone uintptr
162+ }
163+)
164+
165+// uptr turns a raw callback argument into a pointer without a
166+// uintptr-to-unsafe.Pointer conversion; the value is C memory or a small
167+// integer, never Go memory.
168+func uptr(v uintptr) unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&v)) }
169+
170+func videoLookup(a *purego.CallbackArgs) *VideoSource {
171+ idx := int(a.Int(0)) - 1
172+ if uint(idx) >= videoMaxSources {
173+ return nil
174+ }
175+ return videoTable[idx].Load()
176+}
177+
178+func registerVideoCallbacks() {
179+ videoCBOnce.Do(func() {
180+ videoCB.stateChanged = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
181+ if s := videoLookup(a); s != nil {
182+ s.onStateChanged(int32(a.Int(2)), uptr(a.Int(3)))
183+ }
184+ return 0
185+ })
186+ videoCB.paramChanged = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
187+ if s := videoLookup(a); s != nil {
188+ s.onParamChanged(uint32(a.Int(1)), uptr(a.Int(2)))
189+ }
190+ return 0
191+ })
192+ videoCB.addBuffer = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
193+ if s := videoLookup(a); s != nil {
194+ s.onAddBuffer(uptr(a.Int(1)))
195+ }
196+ return 0
197+ })
198+ videoCB.removeBuffer = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
199+ if s := videoLookup(a); s != nil {
200+ s.onRemoveBuffer(uptr(a.Int(1)))
201+ }
202+ return 0
203+ })
204+ // process and trigger_done intentionally do nothing: buffers are
205+ // queued by the owner before Trigger.
206+ videoCB.process = purego.NewCallbackInts(func(*purego.CallbackArgs) uintptr { return 0 })
207+ videoCB.triggerDone = purego.NewCallbackInts(func(*purego.CallbackArgs) uintptr { return 0 })
208+ })
209+}
210+
211+// NewVideoSource creates and connects a Video/Source stream on the core. The
212+// stream drives the graph (DRIVER) and allocates its own memfd buffers
213+// (ALLOC_BUFFERS). Call it, and every other method, on the loop thread.
214+func (c *Core) NewVideoSource(cfg VideoConfig, h portin.VideoHandler) (*VideoSource, error) {
215+ if c.ptr == nil || c.capi == nil {
216+ return nil, ErrVideoStreamCreate
217+ }
218+ if h == nil || !validVideoSize(cfg.Width, cfg.Height) {
219+ return nil, ErrVideoConfig
220+ }
221+ if cfg.MaxFPS <= 0 {
222+ cfg.MaxFPS = defaultVideoFPS
223+ }
224+ s := &VideoSource{capi: c.capi, shm: c.shm, handler: h, cfg: cfg, table: -1}
225+ if len(cfg.Formats) == 0 {
226+ s.formats[0], s.nformats = spa.VideoFormatBGRx, 1
227+ } else {
228+ if len(cfg.Formats) > len(s.formats) {
229+ return nil, ErrVideoConfig
230+ }
231+ for _, f := range cfg.Formats {
232+ if f != spa.VideoFormatBGRx && f != spa.VideoFormatBGRA {
233+ return nil, fmt.Errorf("%w: unsupported format %d", ErrVideoConfig, f)
234+ }
235+ }
236+ s.nformats = copy(s.formats[:], cfg.Formats)
237+ }
238+ s.cfg.Formats = nil // the slot-owned array is the source of truth
239+ name := cfg.Name
240+ if name == "" {
241+ name = "purego-pipewire"
242+ }
243+ if strings.ContainsAny(name, "\"\\") || strings.ContainsFunc(name, func(r rune) bool { return r < 0x20 || r == 0x7f }) {
244+ return nil, fmt.Errorf("%w: invalid name %q", ErrVideoConfig, name)
245+ }
246+ if _, err := spa.VideoEnumFormat(s.enumPod[:], cfg.Width, cfg.Height, cfg.MaxFPS, s.formats[:s.nformats]...); err != nil {
247+ return nil, fmt.Errorf("%w: %v", ErrVideoConfig, err)
248+ }
249+
250+ registerVideoCallbacks()
251+ for i := range videoTable {
252+ if videoTable[i].CompareAndSwap(nil, s) {
253+ s.table = i
254+ break
255+ }
256+ }
257+ if s.table < 0 {
258+ return nil, ErrVideoTableFull
259+ }
260+
261+ cname := append([]byte(name), 0)
262+ props := append([]byte(`media.class=Video/Source node.name="`+name+`"`), 0)
263+ propsPtr := c.capi.PWPropertiesNewString(&props[0])
264+ if propsPtr == nil {
265+ videoTable[s.table].Store(nil)
266+ return nil, ErrVideoStreamCreate
267+ }
268+ s.stream = c.capi.PWStreamNew(c.ptr, &cname[0], propsPtr) // takes ownership of props
269+ if s.stream == nil {
270+ videoTable[s.table].Store(nil)
271+ return nil, ErrVideoStreamCreate
272+ }
273+
274+ s.events = videoEvents{
275+ Version: 2,
276+ StateChanged: videoCB.stateChanged,
277+ ParamChanged: videoCB.paramChanged,
278+ AddBuffer: videoCB.addBuffer,
279+ RemoveBuf: videoCB.removeBuffer,
280+ Process: videoCB.process,
281+ TriggerDone: videoCB.triggerDone,
282+ }
283+ data := uptr(uintptr(s.table + 1))
284+ c.capi.PWStreamAddListener(s.stream, unsafe.Pointer(&s.hook[0]), unsafe.Pointer(&s.events), data)
285+
286+ s.params[0] = unsafe.Pointer(&s.enumPod[0])
287+ ret := c.capi.PWStreamConnect(s.stream, 1 /* PW_DIRECTION_OUTPUT */, spa.IDInvalid,
288+ pwStreamFlagDriver|pwStreamFlagAllocBuffers, unsafe.Pointer(&s.params[0]), 1)
289+ if ret < 0 {
290+ s.closed = true
291+ s.teardown()
292+ return nil, fmt.Errorf("%w: pw_stream_connect returned %d", ErrVideoConnect, ret)
293+ }
294+ return s, nil
295+}
296+
297+// NodeID returns the PipeWire node id, or 0 until the stream is paused or
298+// streaming.
299+func (s *VideoSource) NodeID() uint32 { return s.nodeID }
300+
301+// State returns the last stream state (see VideoHandler.StateChanged).
302+func (s *VideoSource) State() int32 { return s.state }
303+
304+// Dequeue returns a free buffer to draw into, or nil when none is available.
305+// The buffer is borrowed until Queue or Release. Buffers that could not be
306+// given a memfd are skipped (they stay out of the queue).
307+func (s *VideoSource) Dequeue() *VideoBuffer {
308+ if s.stream == nil || !s.negotiated {
309+ return nil
310+ }
311+ for i := 0; i < 2*videoMaxBuffers; i++ {
312+ p := s.capi.PWStreamDequeueBuffer(s.stream)
313+ if p == nil {
314+ return nil
315+ }
316+ idx := int((*videoPWBuffer)(p).UserData) - 1
317+ if uint(idx) >= videoMaxBuffers {
318+ continue // unusable buffer: never handed out
319+ }
320+ sl := &s.slots[idx]
321+ if !sl.live || sl.removed || sl.borrowed || sl.pw != p {
322+ continue
323+ }
324+ sl.borrowed = true
325+ return &sl.buf
326+ }
327+ return nil
328+}
329+
330+// slotOf validates b against the slot table.
331+func (s *VideoSource) slotOf(b *VideoBuffer) *videoSlot {
332+ if b == nil || uint(b.Index) >= videoMaxBuffers {
333+ return nil
334+ }
335+ sl := &s.slots[b.Index]
336+ if &sl.buf != b || !sl.live || !sl.borrowed {
337+ return nil
338+ }
339+ return sl
340+}
341+
342+// Queue hands a filled buffer back to PipeWire with its frame sequence and
343+// presentation time. When corrupted is set the frame is flagged corrupted and
344+// carries no data. Call Trigger after Queue: the buffer is queued before the
345+// graph cycle is triggered. Queue on a buffer PipeWire already removed only
346+// releases it.
347+func (s *VideoSource) Queue(b *VideoBuffer, seq uint64, pts time.Duration, corrupted bool) {
348+ sl := s.slotOf(b)
349+ if sl == nil {
350+ return
351+ }
352+ if sl.removed || s.stream == nil {
353+ s.freeSlot(sl)
354+ return
355+ }
356+ size := uint32(sl.buf.Stride * sl.buf.Height)
357+ if c := sl.chunk; c != nil {
358+ c.Offset, c.Size, c.Stride, c.Flags = 0, size, int32(sl.buf.Stride), 0
359+ if corrupted {
360+ c.Size, c.Flags = 0, spa.ChunkFlagCorrupted
361+ }
362+ }
363+ if h := sl.hdr; h != nil {
364+ h.Flags, h.Offset, h.PTS, h.DTSOffset, h.Seq = 0, 0, int64(pts), 0, seq
365+ if corrupted {
366+ h.Flags = spa.MetaHeaderFlagCorrupted
367+ }
368+ }
369+ sl.borrowed = false
370+ s.capi.PWStreamQueueBuffer(s.stream, sl.pw)
371+}
372+
373+// Release gives a dequeued buffer back without queueing a frame. If PipeWire
374+// removed the buffer in the meantime its memory is unmapped now. b.Data must
375+// not be used afterwards.
376+func (s *VideoSource) Release(b *VideoBuffer) {
377+ sl := s.slotOf(b)
378+ if sl == nil {
379+ return
380+ }
381+ if sl.removed || s.stream == nil {
382+ s.freeSlot(sl)
383+ return
384+ }
385+ sl.borrowed = false
386+ s.capi.PWStreamReturnBuffer(s.stream, sl.pw)
387+}
388+
389+// Trigger starts a graph cycle (pw_stream_trigger_process). Queue the filled
390+// buffer first.
391+func (s *VideoSource) Trigger() {
392+ if s.stream != nil {
393+ s.capi.PWStreamTriggerProcess(s.stream)
394+ }
395+}
396+
397+// validVideoSize reports whether a w×h frame of 4-byte pixels fits the
398+// int32 sizes SPA buffers carry.
399+func validVideoSize(w, h int) bool {
400+ return w > 0 && h > 0 && w <= 1<<15 && h <= 1<<15 && w*4*h <= math.MaxInt32
401+}
402+
403+// Resize renegotiates the stream with a new size. It fails with
404+// ErrVideoBorrowed while a buffer is borrowed: PipeWire replaces every
405+// buffer, and the replacements need the slots the borrowed ones hold.
406+func (s *VideoSource) Resize(w, h int) error {
407+ if s.stream == nil {
408+ return ErrVideoResize
409+ }
410+ if !validVideoSize(w, h) {
411+ return ErrVideoConfig
412+ }
413+ for i := range s.slots {
414+ if s.slots[i].borrowed {
415+ return ErrVideoBorrowed
416+ }
417+ }
418+ if _, err := spa.VideoEnumFormat(s.enumPod[:], w, h, s.cfg.MaxFPS, s.formats[:s.nformats]...); err != nil {
419+ return fmt.Errorf("%w: %v", ErrVideoResize, err)
420+ }
421+ s.cfg.Width, s.cfg.Height = w, h
422+ s.params[0] = unsafe.Pointer(&s.enumPod[0])
423+ if ret := s.capi.PWStreamUpdateParams(s.stream, unsafe.Pointer(&s.params[0]), 1); ret < 0 {
424+ return fmt.Errorf("%w: pw_stream_update_params returned %d", ErrVideoResize, ret)
425+ }
426+ return nil
427+}
428+
429+// Destroy disconnects and destroys the stream. PipeWire removes every buffer
430+// during the disconnect; buffers that are still borrowed keep their mapping
431+// until Release (or Queue). Call Destroy only when no buffer is borrowed,
432+// unless the caller releases them afterwards. No handler callback runs after
433+// Destroy starts. Destroy is idempotent.
434+func (s *VideoSource) Destroy() {
435+ s.closed = true
436+ s.teardown()
437+}
438+
439+func (s *VideoSource) teardown() {
440+ if s.stream != nil {
441+ stream := s.stream
442+ s.capi.PWStreamDisconnect(stream)
443+ s.capi.PWStreamDestroy(stream)
444+ s.stream = nil
445+ }
446+ if s.table >= 0 {
447+ videoTable[s.table].Store(nil)
448+ s.table = -1
449+ }
450+ for i := range s.slots {
451+ sl := &s.slots[i]
452+ if !sl.live {
453+ continue
454+ }
455+ sl.pw, sl.hdr, sl.chunk = nil, nil, nil
456+ if sl.borrowed {
457+ sl.removed = true
458+ } else {
459+ s.freeSlot(sl)
460+ }
461+ }
462+}
463+
464+// freeSlot unmaps the slot's memory and makes the slot reusable.
465+func (s *VideoSource) freeSlot(sl *videoSlot) {
466+ if sl.live {
467+ s.shm.FreeSharedMem(sl.fd, sl.mem)
468+ }
469+ *sl = videoSlot{}
470+}
471+
472+// --- stream event handlers (loop thread) ---
473+
474+func (s *VideoSource) onStateChanged(state int32, errp unsafe.Pointer) {
475+ s.state = state
476+ if state == pwStreamStatePaused || state == pwStreamStateStreaming {
477+ s.nodeID = s.capi.PWStreamGetNodeId(s.stream)
478+ }
479+ if state == pwStreamStateStreaming && !s.warned {
480+ s.warned = true
481+ if !s.capi.PWStreamIsDriving(s.stream) {
482+ slog.Warn("purego-pipewire: video stream is not driving the graph", "node", s.nodeID)
483+ }
484+ }
485+ if s.closed {
486+ return
487+ }
488+ msg := ""
489+ if state < 0 {
490+ msg = cString(errp)
491+ }
492+ s.handler.StateChanged(state, s.nodeID, msg)
493+}
494+
495+func cString(p unsafe.Pointer) string {
496+ if p == nil {
497+ return ""
498+ }
499+ n := 0
500+ for n < 512 && *(*byte)(unsafe.Add(p, n)) != 0 {
501+ n++
502+ }
503+ return string(unsafe.Slice((*byte)(p), n))
504+}
505+
506+func (s *VideoSource) onParamChanged(id uint32, param unsafe.Pointer) {
507+ if id != spa.ParamFormat || s.stream == nil {
508+ return
509+ }
510+ if param == nil {
511+ s.negotiated = false
512+ return
513+ }
514+ size := int(*(*uint32)(param))
515+ if size < 8 || size > 4096 {
516+ return
517+ }
518+ pod := unsafe.Slice((*byte)(param), 8+size)
519+ f, w, h, ok := spa.ParseVideoFormat(pod)
520+ if !ok {
521+ return
522+ }
523+ format := spa.VideoFormat(f)
524+ allowed := false
525+ for _, af := range s.formats[:s.nformats] {
526+ allowed = allowed || af == format
527+ }
528+ if !allowed {
529+ return
530+ }
531+ stride := w * 4
532+ buffers, err := spa.VideoBuffers(s.buffPod[:], stride*h, stride, 2, 8, videoMaxBuffers, 1<<spa.DataMemFd)
533+ if err != nil {
534+ return
535+ }
536+ meta, err := spa.MetaHeader(s.metaPod[:])
537+ if err != nil {
538+ return
539+ }
540+ s.format, s.width, s.height, s.stride, s.negotiated = format, w, h, stride, true
541+ s.params[0] = unsafe.Pointer(&buffers[0])
542+ s.params[1] = unsafe.Pointer(&meta[0])
543+ s.capi.PWStreamUpdateParams(s.stream, unsafe.Pointer(&s.params[0]), 2)
544+ if !s.closed {
545+ s.handler.FormatChanged(format, w, h, stride)
546+ }
547+}
548+
549+func (s *VideoSource) onAddBuffer(p unsafe.Pointer) {
550+ if p == nil {
551+ return
552+ }
553+ pb := (*videoPWBuffer)(p)
554+ pb.UserData = 0 // unusable until a slot is assigned
555+ sb := pb.Buffer
556+ if sb == nil || sb.NDatas == 0 || sb.Datas == nil || !s.negotiated {
557+ return
558+ }
559+ d := (*spaData)(sb.Datas)
560+ if d.Type&(1<<spa.DataMemFd) == 0 {
561+ return // DMA-BUF only: not supported
562+ }
563+ idx := -1
564+ for i := range s.slots {
565+ if !s.slots[i].live {
566+ idx = i
567+ break
568+ }
569+ }
570+ if idx < 0 {
571+ return
572+ }
573+ size := s.stride * s.height
574+ fd, mem, err := s.shm.NewSharedMem("pw-video", size)
575+ if err != nil || len(mem) < size {
576+ if err == nil {
577+ s.shm.FreeSharedMem(fd, mem)
578+ }
579+ return
580+ }
581+ d.Type, d.Flags, d.Fd = spa.DataMemFd, spa.DataFlagReadWrite, int64(fd)
582+ d.MapOffset, d.Maxsize, d.Data = 0, uint32(size), unsafe.Pointer(&mem[0])
583+ if d.Chunk != nil {
584+ d.Chunk.Offset, d.Chunk.Size, d.Chunk.Stride, d.Chunk.Flags = 0, uint32(size), int32(s.stride), 0
585+ }
586+ sl := &s.slots[idx]
587+ *sl = videoSlot{
588+ buf: VideoBuffer{Index: idx, Data: mem, Stride: s.stride, Width: s.width, Height: s.height, Format: s.format},
589+ pw: p,
590+ chunk: d.Chunk,
591+ fd: fd,
592+ mem: mem,
593+ live: true,
594+ }
595+ if sb.Metas != nil {
596+ metas := unsafe.Slice((*spaMeta)(sb.Metas), int(sb.NMetas))
597+ for i := range metas {
598+ if metas[i].Type == spa.MetaTypeHeader && metas[i].Size >= spa.MetaHeaderSize && metas[i].Data != nil {
599+ sl.hdr = (*spaMetaHeader)(metas[i].Data)
600+ break
601+ }
602+ }
603+ }
604+ pb.UserData = uintptr(idx + 1)
605+ if !s.closed {
606+ s.handler.BuffersChanged()
607+ }
608+}
609+
610+func (s *VideoSource) onRemoveBuffer(p unsafe.Pointer) {
611+ if p == nil {
612+ return
613+ }
614+ pb := (*videoPWBuffer)(p)
615+ idx := int(pb.UserData) - 1
616+ if uint(idx) >= videoMaxBuffers {
617+ return
618+ }
619+ sl := &s.slots[idx]
620+ if !sl.live || sl.pw != p {
621+ return
622+ }
623+ pb.UserData = 0
624+ if sl.borrowed {
625+ // The owner may still write into the mapping: keep it until Release.
626+ sl.removed = true
627+ sl.pw, sl.hdr, sl.chunk = nil, nil, nil
628+ } else {
629+ s.freeSlot(sl)
630+ }
631+ if !s.closed {
632+ s.handler.BuffersChanged()
633+ }
634+}
A internal/core/video_test.go +592 -0
@@ -0,0 +1,592 @@
1+package core
2+
3+import (
4+ "errors"
5+ "testing"
6+ "time"
7+ "unsafe"
8+
9+ "github.com/stretchr/testify/mock"
10+
11+ portinmocks "github.com/bnema/purego-pipewire/internal/ports/in/mocks"
12+ portmocks "github.com/bnema/purego-pipewire/internal/ports/out/mocks"
13+ "github.com/bnema/purego-pipewire/spa"
14+)
15+
16+func TestVideoStructLayouts(t *testing.T) {
17+ var pb videoPWBuffer
18+ if unsafe.Offsetof(pb.Buffer) != 0 || unsafe.Offsetof(pb.UserData) != 8 || unsafe.Offsetof(pb.Size) != 16 {
19+ t.Errorf("pw_buffer layout: user_data@%d size@%d", unsafe.Offsetof(pb.UserData), unsafe.Offsetof(pb.Size))
20+ }
21+ var sb spaBuffer
22+ if unsafe.Offsetof(sb.NMetas) != 0 || unsafe.Offsetof(sb.NDatas) != 4 || unsafe.Offsetof(sb.Metas) != 8 || unsafe.Offsetof(sb.Datas) != 16 {
23+ t.Errorf("spa_buffer layout wrong")
24+ }
25+ var m spaMeta
26+ if unsafe.Sizeof(m) != 16 || unsafe.Offsetof(m.Data) != 8 {
27+ t.Errorf("spa_meta layout: size %d", unsafe.Sizeof(m))
28+ }
29+ var h spaMetaHeader
30+ if unsafe.Sizeof(h) != 32 || unsafe.Offsetof(h.PTS) != 8 || unsafe.Offsetof(h.DTSOffset) != 16 || unsafe.Offsetof(h.Seq) != 24 {
31+ t.Errorf("spa_meta_header layout: size %d", unsafe.Sizeof(h))
32+ }
33+ var c spaChunk
34+ if unsafe.Sizeof(c) != 16 || unsafe.Offsetof(c.Stride) != 8 || unsafe.Offsetof(c.Flags) != 12 {
35+ t.Errorf("spa_chunk layout wrong")
36+ }
37+ var d spaData
38+ if unsafe.Sizeof(d) != 40 || unsafe.Offsetof(d.Fd) != 8 || unsafe.Offsetof(d.MapOffset) != 16 ||
39+ unsafe.Offsetof(d.Maxsize) != 20 || unsafe.Offsetof(d.Data) != 24 || unsafe.Offsetof(d.Chunk) != 32 {
40+ t.Errorf("spa_data layout: size %d", unsafe.Sizeof(d))
41+ }
42+ var e videoEvents
43+ for name, want := range map[string][2]uintptr{
44+ "state_changed": {unsafe.Offsetof(e.StateChanged), 16},
45+ "param_changed": {unsafe.Offsetof(e.ParamChanged), 40},
46+ "add_buffer": {unsafe.Offsetof(e.AddBuffer), 48},
47+ "remove_buffer": {unsafe.Offsetof(e.RemoveBuf), 56},
48+ "process": {unsafe.Offsetof(e.Process), 64},
49+ "trigger_done": {unsafe.Offsetof(e.TriggerDone), 88},
50+ } {
51+ if want[0] != want[1] {
52+ t.Errorf("pw_stream_events.%s at %d, want %d", name, want[0], want[1])
53+ }
54+ }
55+ var s VideoSource
56+ if unsafe.Sizeof(s.hook) < 48 {
57+ t.Errorf("spa_hook storage %d bytes, need 48", unsafe.Sizeof(s.hook))
58+ }
59+}
60+
61+// videoRig is a VideoSource wired to mocked C calls and mocked shared memory,
62+// with a fake negotiated format. C-side structures are Go memory, which is
63+// enough because the production code only reads them through pointers.
64+type videoRig struct {
65+ t *testing.T
66+ api *portmocks.MockVideoStreamAPI
67+ shm *portmocks.MockSharedMemAPI
68+ handler *portinmocks.MockVideoHandler
69+ src *VideoSource
70+ stream unsafe.Pointer
71+ streamB [8]byte
72+}
73+
74+const rigW, rigH = 4, 2 // stride 16, size 32
75+
76+func newRig(t *testing.T) *videoRig {
77+ t.Helper()
78+ r := &videoRig{
79+ t: t,
80+ api: portmocks.NewMockVideoStreamAPI(t),
81+ shm: portmocks.NewMockSharedMemAPI(t),
82+ handler: portinmocks.NewMockVideoHandler(t),
83+ }
84+ r.stream = unsafe.Pointer(&r.streamB[0])
85+ core := &Core{ptr: unsafe.Pointer(&r.streamB[4]), capi: r.api, shm: r.shm}
86+
87+ var gotProps *byte
88+ r.api.EXPECT().PWPropertiesNewString(mock.Anything).Run(func(args *byte) { gotProps = args }).Return(unsafe.Pointer(&r.streamB[1])).Once()
89+ r.api.EXPECT().PWStreamNew(core.ptr, mock.Anything, unsafe.Pointer(&r.streamB[1])).Return(r.stream).Once()
90+ var events *videoEvents
91+ r.api.EXPECT().PWStreamAddListener(r.stream, mock.Anything, mock.Anything, mock.Anything).
92+ Run(func(_, _, ev, _ unsafe.Pointer) { events = (*videoEvents)(ev) }).Return(0).Once()
93+ var connectFlags uint32
94+ r.api.EXPECT().PWStreamConnect(r.stream, int32(1), uint32(spa.IDInvalid), mock.Anything, mock.Anything, uint32(1)).
95+ Run(func(_ unsafe.Pointer, _ int32, _ uint32, flags uint32, _ unsafe.Pointer, _ uint32) {
96+ connectFlags = flags
97+ }).
98+ Return(0).Once()
99+
100+ src, err := core.NewVideoSource(VideoConfig{Name: "test-src", Width: rigW, Height: rigH}, r.handler)
101+ if err != nil {
102+ t.Fatal(err)
103+ }
104+ r.src = src
105+ t.Cleanup(func() {
106+ if r.src.table >= 0 {
107+ videoTable[r.src.table].Store(nil)
108+ }
109+ })
110+
111+ if connectFlags != pwStreamFlagDriver|pwStreamFlagAllocBuffers {
112+ t.Fatalf("connect flags = %#x, want DRIVER|ALLOC_BUFFERS", connectFlags)
113+ }
114+ if events == nil || events.Version != 2 || events.StateChanged == 0 || events.ParamChanged == 0 ||
115+ events.AddBuffer == 0 || events.RemoveBuf == 0 || events.Process == 0 || events.TriggerDone == 0 {
116+ t.Fatalf("events not fully registered: %+v", events)
117+ }
118+ if got := cString(unsafe.Pointer(gotProps)); got != `media.class=Video/Source node.name="test-src"` {
119+ t.Fatalf("props = %q", got)
120+ }
121+ if videoTable[src.table].Load() != src {
122+ t.Fatal("source not in callback slot table")
123+ }
124+ return r
125+}
126+
127+// negotiate feeds a Format param to the source, as PipeWire would.
128+func (r *videoRig) negotiate() {
129+ r.t.Helper()
130+ var dst [256]byte
131+ b := spa.NewPodBuilder(dst[:])
132+ b.Object(spa.TypeObjectFormat, spa.ParamFormat)
133+ b.Prop(spa.FormatKeyMediaType)
134+ b.Id(spa.MediaTypeVideo)
135+ b.Prop(spa.FormatKeyMediaSubtype)
136+ b.Id(spa.MediaSubtypeRaw)
137+ b.Prop(spa.FormatKeyVideoFormat)
138+ b.Id(uint32(spa.VideoFormatBGRx))
139+ b.Prop(spa.FormatKeyVideoSize)
140+ b.Rectangle(rigW, rigH)
141+ b.End()
142+ pod, err := b.Bytes()
143+ if err != nil {
144+ r.t.Fatal(err)
145+ }
146+ var params unsafe.Pointer
147+ r.api.EXPECT().PWStreamUpdateParams(r.stream, mock.Anything, uint32(2)).
148+ Run(func(_, p unsafe.Pointer, _ uint32) { params = p }).Return(0).Once()
149+ r.handler.EXPECT().FormatChanged(spa.VideoFormatBGRx, rigW, rigH, rigW*4).Once()
150+ r.src.onParamChanged(spa.ParamFormat, unsafe.Pointer(&pod[0]))
151+ if params != unsafe.Pointer(&r.src.params[0]) {
152+ r.t.Fatal("update_params not given the slot-owned params array")
153+ }
154+}
155+
156+// fakeBuf is a PipeWire-side buffer: pw_buffer + spa_buffer + data + chunk + header meta.
157+type fakeBuf struct {
158+ pw videoPWBuffer
159+ sb spaBuffer
160+ data spaData
161+ chunk spaChunk
162+ meta spaMeta
163+ hdr spaMetaHeader
164+}
165+
166+func newFakeBuf(dataType uint32) *fakeBuf {
167+ f := &fakeBuf{}
168+ f.data.Type = dataType
169+ f.data.Chunk = &f.chunk
170+ f.meta = spaMeta{Type: spa.MetaTypeHeader, Size: spa.MetaHeaderSize, Data: unsafe.Pointer(&f.hdr)}
171+ f.sb = spaBuffer{NMetas: 1, NDatas: 1, Metas: unsafe.Pointer(&f.meta), Datas: unsafe.Pointer(&f.data)}
172+ f.pw.Buffer = &f.sb
173+ return f
174+}
175+
176+func (f *fakeBuf) ptr() unsafe.Pointer { return unsafe.Pointer(&f.pw) }
177+
178+// add runs add_buffer with a memfd that the shm mock "creates".
179+func (r *videoRig) add(f *fakeBuf, fd int) []byte {
180+ r.t.Helper()
181+ mem := make([]byte, rigW*4*rigH)
182+ r.shm.EXPECT().NewSharedMem("pw-video", rigW*4*rigH).Return(fd, mem, nil).Once()
183+ r.handler.EXPECT().BuffersChanged().Once()
184+ r.src.onAddBuffer(f.ptr())
185+ return mem
186+}
187+
188+func TestVideoSourceNegotiationAndAddBuffer(t *testing.T) {
189+ r := newRig(t)
190+ r.negotiate()
191+ f := newFakeBuf(1 << spa.DataMemFd)
192+ mem := r.add(f, 42)
193+
194+ if f.data.Type != spa.DataMemFd || f.data.Flags != spa.DataFlagReadWrite || f.data.Fd != 42 ||
195+ f.data.MapOffset != 0 || f.data.Maxsize != uint32(len(mem)) || f.data.Data != unsafe.Pointer(&mem[0]) {
196+ t.Fatalf("spa_data not filled: %+v", f.data)
197+ }
198+ if f.chunk.Offset != 0 || f.chunk.Size != uint32(len(mem)) || f.chunk.Stride != rigW*4 {
199+ t.Fatalf("chunk = %+v", f.chunk)
200+ }
201+ if f.pw.UserData != 1 {
202+ t.Fatalf("user_data = %d, want slot 0 + 1", f.pw.UserData)
203+ }
204+
205+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
206+ b := r.src.Dequeue()
207+ if b == nil || len(b.Data) != len(mem) || &b.Data[0] != &mem[0] || b.Stride != rigW*4 || b.Width != rigW || b.Height != rigH || b.Format != spa.VideoFormatBGRx {
208+ t.Fatalf("Dequeue = %+v", b)
209+ }
210+
211+ var queued unsafe.Pointer
212+ r.api.EXPECT().PWStreamQueueBuffer(r.stream, f.ptr()).Run(func(_, p unsafe.Pointer) { queued = p }).Return(0).Once()
213+ r.src.Queue(b, 7, 1500*time.Millisecond, false)
214+ if queued != f.ptr() {
215+ t.Fatal("buffer not queued")
216+ }
217+ if f.chunk.Size != uint32(len(mem)) || f.chunk.Stride != rigW*4 || f.chunk.Flags != 0 {
218+ t.Fatalf("chunk after Queue = %+v", f.chunk)
219+ }
220+ if f.hdr.Seq != 7 || f.hdr.PTS != int64(1500*time.Millisecond) || f.hdr.Flags != 0 {
221+ t.Fatalf("header after Queue = %+v", f.hdr)
222+ }
223+
224+ // Second round, corrupted.
225+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
226+ b = r.src.Dequeue()
227+ if b == nil {
228+ t.Fatal("buffer not re-dequeued")
229+ }
230+ r.api.EXPECT().PWStreamQueueBuffer(r.stream, f.ptr()).Return(0).Once()
231+ r.src.Queue(b, 8, 0, true)
232+ if f.chunk.Size != 0 || f.chunk.Flags != spa.ChunkFlagCorrupted || f.hdr.Flags != spa.MetaHeaderFlagCorrupted || f.hdr.Seq != 8 {
233+ t.Fatalf("corrupted frame: chunk %+v hdr %+v", f.chunk, f.hdr)
234+ }
235+
236+ // Trigger is separate and goes straight to the C call.
237+ r.api.EXPECT().PWStreamTriggerProcess(r.stream).Return(0).Once()
238+ r.src.Trigger()
239+
240+ r.expectDestroy(1)
241+ r.src.Destroy()
242+}
243+
244+// expectDestroy expects the disconnect/destroy sequence and the free of n live slots.
245+func (r *videoRig) expectDestroy(n int) {
246+ r.api.EXPECT().PWStreamDisconnect(r.stream).Return(0).Once()
247+ r.api.EXPECT().PWStreamDestroy(r.stream).Once()
248+ if n > 0 {
249+ r.shm.EXPECT().FreeSharedMem(mock.Anything, mock.Anything).Times(n)
250+ }
251+}
252+
253+func TestVideoSourceMemfdFailureMakesBufferUnusable(t *testing.T) {
254+ r := newRig(t)
255+ r.negotiate()
256+
257+ bad := newFakeBuf(1 << spa.DataMemFd)
258+ r.shm.EXPECT().NewSharedMem("pw-video", rigW*4*rigH).Return(-1, nil, errors.New("memfd_create: boom")).Once()
259+ r.src.onAddBuffer(bad.ptr())
260+ if bad.pw.UserData != 0 {
261+ t.Fatalf("failed buffer got user_data %d", bad.pw.UserData)
262+ }
263+
264+ good := newFakeBuf(1 << spa.DataMemFd)
265+ mem := r.add(good, 7)
266+
267+ // Dequeue skips the unusable buffer and returns the good one.
268+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(bad.ptr()).Once()
269+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(good.ptr()).Once()
270+ b := r.src.Dequeue()
271+ if b == nil || &b.Data[0] != &mem[0] {
272+ t.Fatalf("Dequeue = %+v, want the good buffer", b)
273+ }
274+ r.api.EXPECT().PWStreamReturnBuffer(r.stream, good.ptr()).Return(0).Once()
275+ r.src.Release(b)
276+
277+ // A DMA-BUF-only buffer is also unusable and allocates nothing.
278+ dma := newFakeBuf(1 << spa.DataDmaBuf)
279+ r.src.onAddBuffer(dma.ptr())
280+ if dma.pw.UserData != 0 {
281+ t.Fatalf("dmabuf buffer got user_data %d", dma.pw.UserData)
282+ }
283+
284+ // Nothing is dequeueable and no buffer is available: nil, no panic.
285+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(nil).Once()
286+ if b := r.src.Dequeue(); b != nil {
287+ t.Fatalf("Dequeue = %+v, want nil", b)
288+ }
289+ r.expectDestroy(1)
290+ r.src.Destroy()
291+}
292+
293+func TestVideoSourceRemoveBorrowedKeepsMappingUntilRelease(t *testing.T) {
294+ r := newRig(t)
295+ r.negotiate()
296+ f := newFakeBuf(1 << spa.DataMemFd)
297+ mem := r.add(f, 9)
298+
299+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
300+ b := r.src.Dequeue()
301+ if b == nil {
302+ t.Fatal("no buffer")
303+ }
304+
305+ // PipeWire removes the buffer while the owner still holds it: no unmap yet
306+ // (FreeSharedMem has no expectation, so a call would fail the test).
307+ r.handler.EXPECT().BuffersChanged().Once()
308+ r.src.onRemoveBuffer(f.ptr())
309+ b.Data[0] = 0xAB // still writable
310+ if mem[0] != 0xAB || f.pw.UserData != 0 {
311+ t.Fatalf("mapping not kept: mem[0]=%x user_data=%d", mem[0], f.pw.UserData)
312+ }
313+
314+ // A removed slot is never returned by Dequeue (PipeWire no longer owns it,
315+ // so even if it showed up the source would not hand it out).
316+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
317+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(nil).Once()
318+ if got := r.src.Dequeue(); got != nil {
319+ t.Fatalf("removed buffer dequeued: %+v", got)
320+ }
321+
322+ // Queue on a removed slot must not call pw_stream_queue_buffer, and
323+ // releases the mapping.
324+ r.shm.EXPECT().FreeSharedMem(9, mem).Once()
325+ r.api.AssertNotCalled(t, "PWStreamQueueBuffer", mock.Anything, mock.Anything)
326+ r.src.Queue(b, 1, 0, false)
327+ r.api.AssertNotCalled(t, "PWStreamQueueBuffer", mock.Anything, mock.Anything)
328+
329+ // The slot is reusable afterwards.
330+ f2 := newFakeBuf(1 << spa.DataMemFd)
331+ r.add(f2, 10)
332+ if f2.pw.UserData != 1 {
333+ t.Fatalf("slot not reused, user_data = %d", f2.pw.UserData)
334+ }
335+ r.expectDestroy(1)
336+ r.src.Destroy()
337+}
338+
339+func TestVideoSourceRemoveBorrowedThenRelease(t *testing.T) {
340+ r := newRig(t)
341+ r.negotiate()
342+ f := newFakeBuf(1 << spa.DataMemFd)
343+ mem := r.add(f, 11)
344+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
345+ b := r.src.Dequeue()
346+
347+ r.handler.EXPECT().BuffersChanged().Once()
348+ r.src.onRemoveBuffer(f.ptr())
349+ r.shm.EXPECT().FreeSharedMem(11, mem).Once()
350+ r.src.Release(b)
351+ r.api.AssertNotCalled(t, "PWStreamReturnBuffer", mock.Anything, mock.Anything)
352+
353+ // A second Release/Queue of the same buffer is ignored.
354+ r.src.Release(b)
355+ r.src.Queue(b, 0, 0, false)
356+ r.expectDestroy(0)
357+ r.src.Destroy()
358+}
359+
360+func TestVideoSourceRemoveIdleUnmapsImmediately(t *testing.T) {
361+ r := newRig(t)
362+ r.negotiate()
363+ f := newFakeBuf(1 << spa.DataMemFd)
364+ mem := r.add(f, 12)
365+ r.shm.EXPECT().FreeSharedMem(12, mem).Once()
366+ r.handler.EXPECT().BuffersChanged().Once()
367+ r.src.onRemoveBuffer(f.ptr())
368+ r.expectDestroy(0)
369+ r.src.Destroy()
370+}
371+
372+func TestVideoSourceDestroyKeepsBorrowedMappingUntilRelease(t *testing.T) {
373+ r := newRig(t)
374+ r.negotiate()
375+ f := newFakeBuf(1 << spa.DataMemFd)
376+ mem := r.add(f, 13)
377+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
378+ b := r.src.Dequeue()
379+
380+ r.expectDestroy(0)
381+ r.src.Destroy()
382+ b.Data[1] = 1 // still writable after Destroy
383+ if mem[1] != 1 {
384+ t.Fatal("mapping lost on Destroy")
385+ }
386+ r.shm.EXPECT().FreeSharedMem(13, mem).Once()
387+ r.src.Release(b)
388+
389+ // Destroy is idempotent and Dequeue/Trigger after it are no-ops.
390+ r.src.Destroy()
391+ r.src.Trigger()
392+ if r.src.Dequeue() != nil {
393+ t.Fatal("Dequeue after Destroy returned a buffer")
394+ }
395+}
396+
397+func TestVideoSourceStateChanged(t *testing.T) {
398+ r := newRig(t)
399+ r.api.EXPECT().PWStreamGetNodeId(r.stream).Return(77).Twice()
400+ r.api.EXPECT().PWStreamIsDriving(r.stream).Return(false).Once()
401+ r.handler.EXPECT().StateChanged(int32(2), uint32(77), "").Once()
402+ r.src.onStateChanged(2, nil)
403+ r.handler.EXPECT().StateChanged(int32(3), uint32(77), "").Once()
404+ r.src.onStateChanged(3, nil)
405+ if r.src.NodeID() != 77 || r.src.State() != 3 {
406+ t.Fatalf("node %d state %d", r.src.NodeID(), r.src.State())
407+ }
408+
409+ msg := append([]byte("boom"), 0)
410+ r.handler.EXPECT().StateChanged(int32(-1), uint32(77), "boom").Once()
411+ r.src.onStateChanged(-1, unsafe.Pointer(&msg[0]))
412+
413+ r.expectDestroy(0)
414+ r.src.Destroy()
415+}
416+
417+func TestVideoSourceResize(t *testing.T) {
418+ r := newRig(t)
419+ var p unsafe.Pointer
420+ r.api.EXPECT().PWStreamUpdateParams(r.stream, mock.Anything, uint32(1)).Run(func(_, params unsafe.Pointer, _ uint32) { p = params }).Return(0).Once()
421+ if err := r.src.Resize(8, 6); err != nil {
422+ t.Fatal(err)
423+ }
424+ if p != unsafe.Pointer(&r.src.params[0]) {
425+ t.Fatal("params not slot-owned")
426+ }
427+ if err := r.src.Resize(0, 6); err == nil {
428+ t.Fatal("Resize(0, 6) accepted")
429+ }
430+ // A borrowed buffer holds a slot the replacements need.
431+ r.negotiate()
432+ f := newFakeBuf(1 << spa.DataMemFd)
433+ r.add(f, 42)
434+ r.api.EXPECT().PWStreamDequeueBuffer(r.stream).Return(f.ptr()).Once()
435+ b := r.src.Dequeue()
436+ if err := r.src.Resize(8, 6); !errors.Is(err, ErrVideoBorrowed) {
437+ t.Fatalf("Resize with a borrowed buffer: %v, want ErrVideoBorrowed", err)
438+ }
439+ r.api.EXPECT().PWStreamReturnBuffer(r.stream, f.ptr()).Return(0).Once()
440+ r.src.Release(b)
441+ r.api.EXPECT().PWStreamUpdateParams(r.stream, mock.Anything, uint32(1)).Return(-22).Once()
442+ if err := r.src.Resize(8, 6); err == nil {
443+ t.Fatal("Resize accepted a failing update_params")
444+ }
445+ r.expectDestroy(1)
446+ r.src.Destroy()
447+ if err := r.src.Resize(8, 6); err == nil {
448+ t.Fatal("Resize after Destroy accepted")
449+ }
450+}
451+
452+func TestNewVideoSourceErrors(t *testing.T) {
453+ api := portmocks.NewMockVideoStreamAPI(t)
454+ shm := portmocks.NewMockSharedMemAPI(t)
455+ h := portinmocks.NewMockVideoHandler(t)
456+ core := &Core{ptr: unsafe.Pointer(new(byte)), capi: api, shm: shm}
457+
458+ if _, err := core.NewVideoSource(VideoConfig{Width: 0, Height: 10}, h); !errors.Is(err, ErrVideoConfig) {
459+ t.Errorf("zero width: %v", err)
460+ }
461+ if _, err := core.NewVideoSource(VideoConfig{Width: 1 << 15, Height: 1 << 14}, h); !errors.Is(err, ErrVideoConfig) {
462+ t.Errorf("frame size over int32: %v", err)
463+ }
464+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10, Formats: []spa.VideoFormat{spa.VideoFormatBGRx, 18}}, h); !errors.Is(err, ErrVideoConfig) {
465+ t.Errorf("unsupported format: %v", err)
466+ }
467+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, nil); !errors.Is(err, ErrVideoConfig) {
468+ t.Errorf("nil handler: %v", err)
469+ }
470+ if _, err := core.NewVideoSource(VideoConfig{Name: `a"b`, Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoConfig) {
471+ t.Errorf("quote in name: %v", err)
472+ }
473+ if _, err := (&Core{}).NewVideoSource(VideoConfig{Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoStreamCreate) {
474+ t.Errorf("disconnected core: %v", err)
475+ }
476+
477+ // properties allocation failure
478+ api.EXPECT().PWPropertiesNewString(mock.Anything).Return(nil).Once()
479+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoStreamCreate) {
480+ t.Errorf("props failure: %v", err)
481+ }
482+
483+ // pw_stream_new failure
484+ props := unsafe.Pointer(new(byte))
485+ api.EXPECT().PWPropertiesNewString(mock.Anything).Return(props).Once()
486+ api.EXPECT().PWStreamNew(core.ptr, mock.Anything, props).Return(nil).Once()
487+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoStreamCreate) {
488+ t.Errorf("stream_new failure: %v", err)
489+ }
490+
491+ // pw_stream_connect failure tears the stream down and frees the slot
492+ stream := unsafe.Pointer(new(byte))
493+ api.EXPECT().PWPropertiesNewString(mock.Anything).Return(props).Once()
494+ api.EXPECT().PWStreamNew(core.ptr, mock.Anything, props).Return(stream).Once()
495+ api.EXPECT().PWStreamAddListener(stream, mock.Anything, mock.Anything, mock.Anything).Return(0).Once()
496+ api.EXPECT().PWStreamConnect(stream, int32(1), uint32(spa.IDInvalid), mock.Anything, mock.Anything, uint32(1)).Return(-1).Once()
497+ api.EXPECT().PWStreamDisconnect(stream).Return(0).Once()
498+ api.EXPECT().PWStreamDestroy(stream).Once()
499+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoConnect) {
500+ t.Errorf("connect failure: %v", err)
501+ }
502+ for i := range videoTable {
503+ if videoTable[i].Load() != nil {
504+ t.Errorf("callback slot %d leaked", i)
505+ }
506+ }
507+}
508+
509+func TestNewVideoSourceTableFull(t *testing.T) {
510+ api := portmocks.NewMockVideoStreamAPI(t)
511+ h := portinmocks.NewMockVideoHandler(t)
512+ core := &Core{ptr: unsafe.Pointer(new(byte)), capi: api, shm: portmocks.NewMockSharedMemAPI(t)}
513+ dummy := &VideoSource{}
514+ for i := range videoTable {
515+ videoTable[i].Store(dummy)
516+ }
517+ defer func() {
518+ for i := range videoTable {
519+ videoTable[i].Store(nil)
520+ }
521+ }()
522+ if _, err := core.NewVideoSource(VideoConfig{Width: 10, Height: 10}, h); !errors.Is(err, ErrVideoTableFull) {
523+ t.Fatalf("err = %v, want ErrVideoTableFull", err)
524+ }
525+}
526+
527+func TestLoopContextCore(t *testing.T) {
528+ api := portmocks.NewMockCAPI(t)
529+ r := NewRuntime(api)
530+
531+ loopPtr := unsafe.Pointer(new(byte))
532+ api.EXPECT().PWLoopNew(unsafe.Pointer(nil)).Return(nil).Once()
533+ if _, err := r.NewLoop(); !errors.Is(err, ErrLoopCreate) {
534+ t.Fatalf("NewLoop nil: %v", err)
535+ }
536+ api.EXPECT().PWLoopNew(unsafe.Pointer(nil)).Return(loopPtr).Once()
537+ loop, err := r.NewLoop()
538+ if err != nil {
539+ t.Fatal(err)
540+ }
541+
542+ api.EXPECT().PWLoopGetFd(loopPtr).Return(9).Once()
543+ if loop.Fd() != 9 {
544+ t.Fatal("Fd")
545+ }
546+ // Iterate: enter, iterate(0), leave in that order; a negative result is not fatal.
547+ var order []string
548+ api.EXPECT().PWLoopEnter(loopPtr).Run(func(unsafe.Pointer) { order = append(order, "enter") }).Once()
549+ api.EXPECT().PWLoopIterate(loopPtr, int32(0)).Run(func(unsafe.Pointer, int32) { order = append(order, "iterate") }).Return(-4).Once()
550+ api.EXPECT().PWLoopLeave(loopPtr).Run(func(unsafe.Pointer) { order = append(order, "leave") }).Once()
551+ loop.Iterate()
552+ if len(order) != 3 || order[0] != "enter" || order[1] != "iterate" || order[2] != "leave" {
553+ t.Fatalf("Iterate order = %v", order)
554+ }
555+
556+ if _, err := r.NewContext(nil); !errors.Is(err, ErrContextCreate) {
557+ t.Fatalf("NewContext(nil loop): %v", err)
558+ }
559+ api.EXPECT().PWContextNew(loopPtr, unsafe.Pointer(nil), uintptr(0)).Return(nil).Once()
560+ if _, err := r.NewContext(loop); !errors.Is(err, ErrContextCreate) {
561+ t.Fatalf("NewContext nil ctx: %v", err)
562+ }
563+ ctxPtr := unsafe.Pointer(new(byte))
564+ api.EXPECT().PWContextNew(loopPtr, unsafe.Pointer(nil), uintptr(0)).Return(ctxPtr).Once()
565+ ctx, err := r.NewContext(loop)
566+ if err != nil {
567+ t.Fatal(err)
568+ }
569+
570+ api.EXPECT().PWContextConnect(ctxPtr, unsafe.Pointer(nil), uintptr(0)).Return(nil).Once()
571+ if _, err := ctx.Connect(); !errors.Is(err, ErrCoreConnect) {
572+ t.Fatalf("Connect nil: %v", err)
573+ }
574+ corePtr := unsafe.Pointer(new(byte))
575+ api.EXPECT().PWContextConnect(ctxPtr, unsafe.Pointer(nil), uintptr(0)).Return(corePtr).Once()
576+ c, err := ctx.Connect()
577+ if err != nil {
578+ t.Fatal(err)
579+ }
580+ api.EXPECT().PWCoreDisconnect(corePtr).Return(0).Once()
581+ c.Disconnect()
582+ c.Disconnect() // idempotent
583+ api.EXPECT().PWContextDestroy(ctxPtr).Once()
584+ ctx.Destroy()
585+ ctx.Destroy()
586+ if _, err := ctx.Connect(); err == nil {
587+ t.Fatal("Connect after Destroy accepted")
588+ }
589+ api.EXPECT().PWLoopDestroy(loopPtr).Once()
590+ loop.Destroy()
591+ loop.Destroy()
592+}
A internal/ports/in/mocks/mock_VideoHandler.go +189 -0
@@ -0,0 +1,189 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "github.com/bnema/purego-pipewire/spa"
9+ mock "github.com/stretchr/testify/mock"
10+)
11+
12+// NewMockVideoHandler creates a new instance of MockVideoHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
13+// The first argument is typically a *testing.T value.
14+func NewMockVideoHandler(t interface {
15+ mock.TestingT
16+ Cleanup(func())
17+}) *MockVideoHandler {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
22+ mock := &MockVideoHandler{}
23+ mock.Mock.Test(t)
24+
25+ t.Cleanup(func() {
26+ if helper, ok := t.(interface{ Helper() }); ok {
27+ helper.Helper()
28+ }
29+ mock.AssertExpectations(t)
30+ })
31+
32+ return mock
33+}
34+
35+// MockVideoHandler is an autogenerated mock type for the VideoHandler type
36+type MockVideoHandler struct {
37+ mock.Mock
38+}
39+
40+type MockVideoHandler_Expecter struct {
41+ mock *mock.Mock
42+}
43+
44+func (_m *MockVideoHandler) EXPECT() *MockVideoHandler_Expecter {
45+ return &MockVideoHandler_Expecter{mock: &_m.Mock}
46+}
47+
48+// BuffersChanged provides a mock function for the type MockVideoHandler
49+func (_mock *MockVideoHandler) BuffersChanged() {
50+ _mock.Called()
51+ return
52+}
53+
54+// MockVideoHandler_BuffersChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuffersChanged'
55+type MockVideoHandler_BuffersChanged_Call struct {
56+ *mock.Call
57+}
58+
59+// BuffersChanged is a helper method to define mock.On call
60+func (_e *MockVideoHandler_Expecter) BuffersChanged() *MockVideoHandler_BuffersChanged_Call {
61+ return &MockVideoHandler_BuffersChanged_Call{Call: _e.mock.On("BuffersChanged")}
62+}
63+
64+func (_c *MockVideoHandler_BuffersChanged_Call) Run(run func()) *MockVideoHandler_BuffersChanged_Call {
65+ _c.Call.Run(func(args mock.Arguments) {
66+ run()
67+ })
68+ return _c
69+}
70+
71+func (_c *MockVideoHandler_BuffersChanged_Call) Return() *MockVideoHandler_BuffersChanged_Call {
72+ _c.Call.Return()
73+ return _c
74+}
75+
76+func (_c *MockVideoHandler_BuffersChanged_Call) RunAndReturn(run func()) *MockVideoHandler_BuffersChanged_Call {
77+ _c.Run(run)
78+ return _c
79+}
80+
81+// FormatChanged provides a mock function for the type MockVideoHandler
82+func (_mock *MockVideoHandler) FormatChanged(f spa.VideoFormat, w int, h int, stride int) {
83+ _mock.Called(f, w, h, stride)
84+ return
85+}
86+
87+// MockVideoHandler_FormatChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatChanged'
88+type MockVideoHandler_FormatChanged_Call struct {
89+ *mock.Call
90+}
91+
92+// FormatChanged is a helper method to define mock.On call
93+// - f spa.VideoFormat
94+// - w int
95+// - h int
96+// - stride int
97+func (_e *MockVideoHandler_Expecter) FormatChanged(f any, w any, h any, stride any) *MockVideoHandler_FormatChanged_Call {
98+ return &MockVideoHandler_FormatChanged_Call{Call: _e.mock.On("FormatChanged", f, w, h, stride)}
99+}
100+
101+func (_c *MockVideoHandler_FormatChanged_Call) Run(run func(f spa.VideoFormat, w int, h int, stride int)) *MockVideoHandler_FormatChanged_Call {
102+ _c.Call.Run(func(args mock.Arguments) {
103+ var arg0 spa.VideoFormat
104+ if args[0] != nil {
105+ arg0 = args[0].(spa.VideoFormat)
106+ }
107+ var arg1 int
108+ if args[1] != nil {
109+ arg1 = args[1].(int)
110+ }
111+ var arg2 int
112+ if args[2] != nil {
113+ arg2 = args[2].(int)
114+ }
115+ var arg3 int
116+ if args[3] != nil {
117+ arg3 = args[3].(int)
118+ }
119+ run(
120+ arg0,
121+ arg1,
122+ arg2,
123+ arg3,
124+ )
125+ })
126+ return _c
127+}
128+
129+func (_c *MockVideoHandler_FormatChanged_Call) Return() *MockVideoHandler_FormatChanged_Call {
130+ _c.Call.Return()
131+ return _c
132+}
133+
134+func (_c *MockVideoHandler_FormatChanged_Call) RunAndReturn(run func(f spa.VideoFormat, w int, h int, stride int)) *MockVideoHandler_FormatChanged_Call {
135+ _c.Run(run)
136+ return _c
137+}
138+
139+// StateChanged provides a mock function for the type MockVideoHandler
140+func (_mock *MockVideoHandler) StateChanged(state int32, nodeID uint32, err string) {
141+ _mock.Called(state, nodeID, err)
142+ return
143+}
144+
145+// MockVideoHandler_StateChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StateChanged'
146+type MockVideoHandler_StateChanged_Call struct {
147+ *mock.Call
148+}
149+
150+// StateChanged is a helper method to define mock.On call
151+// - state int32
152+// - nodeID uint32
153+// - err string
154+func (_e *MockVideoHandler_Expecter) StateChanged(state any, nodeID any, err any) *MockVideoHandler_StateChanged_Call {
155+ return &MockVideoHandler_StateChanged_Call{Call: _e.mock.On("StateChanged", state, nodeID, err)}
156+}
157+
158+func (_c *MockVideoHandler_StateChanged_Call) Run(run func(state int32, nodeID uint32, err string)) *MockVideoHandler_StateChanged_Call {
159+ _c.Call.Run(func(args mock.Arguments) {
160+ var arg0 int32
161+ if args[0] != nil {
162+ arg0 = args[0].(int32)
163+ }
164+ var arg1 uint32
165+ if args[1] != nil {
166+ arg1 = args[1].(uint32)
167+ }
168+ var arg2 string
169+ if args[2] != nil {
170+ arg2 = args[2].(string)
171+ }
172+ run(
173+ arg0,
174+ arg1,
175+ arg2,
176+ )
177+ })
178+ return _c
179+}
180+
181+func (_c *MockVideoHandler_StateChanged_Call) Return() *MockVideoHandler_StateChanged_Call {
182+ _c.Call.Return()
183+ return _c
184+}
185+
186+func (_c *MockVideoHandler_StateChanged_Call) RunAndReturn(run func(state int32, nodeID uint32, err string)) *MockVideoHandler_StateChanged_Call {
187+ _c.Run(run)
188+ return _c
189+}
A internal/ports/in/video.go +20 -0
@@ -0,0 +1,20 @@
1+package in
2+
3+import "github.com/bnema/purego-pipewire/spa"
4+
5+// VideoHandler receives video source stream events. All methods are called on
6+// the thread that drives the PipeWire loop (the goroutine that calls
7+// Loop.Iterate), from inside Iterate. Implementations must not block and
8+// should not allocate.
9+type VideoHandler interface {
10+ // StateChanged reports a stream state change. state is a pw_stream_state
11+ // value (-1 error, 0 unconnected, 1 connecting, 2 paused, 3 streaming).
12+ // nodeID is the PipeWire node id once known (0 before). err is the error
13+ // message when state is -1.
14+ StateChanged(state int32, nodeID uint32, err string)
15+ // FormatChanged reports the negotiated format.
16+ FormatChanged(f spa.VideoFormat, w, h, stride int)
17+ // BuffersChanged is called after a buffer was added or removed. Dequeue
18+ // may now return a buffer.
19+ BuffersChanged()
20+}
M internal/ports/out/capi_gen.go +1 -0 generated file: collapsed
M internal/ports/out/mocks/mock_CAPI.go +908 -16
@@ -16,10 +16,19 @@ func NewMockCAPI(t interface {
1616 mock.TestingT
1717 Cleanup(func())
1818 }) *MockCAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
1923 mock := &MockCAPI{}
2024 mock.Mock.Test(t)
2125
22- t.Cleanup(func() { mock.AssertExpectations(t) })
26+ t.Cleanup(func() {
27+ if helper, ok := t.(interface{ Helper() }); ok {
28+ helper.Helper()
29+ }
30+ mock.AssertExpectations(t)
31+ })
2332
2433 return mock
2534 }
@@ -37,6 +46,223 @@ func (_m *MockCAPI) EXPECT() *MockCAPI_Expecter {
3746 return &MockCAPI_Expecter{mock: &_m.Mock}
3847 }
3948
49+// PWContextConnect provides a mock function for the type MockCAPI
50+func (_mock *MockCAPI) PWContextConnect(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
51+ ret := _mock.Called(context, props, user_data_size)
52+
53+ if len(ret) == 0 {
54+ panic("no return value specified for PWContextConnect")
55+ }
56+
57+ var r0 unsafe.Pointer
58+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
59+ r0 = returnFunc(context, props, user_data_size)
60+ } else {
61+ r0 = ret.Get(0).(unsafe.Pointer)
62+ }
63+ return r0
64+}
65+
66+// MockCAPI_PWContextConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextConnect'
67+type MockCAPI_PWContextConnect_Call struct {
68+ *mock.Call
69+}
70+
71+// PWContextConnect is a helper method to define mock.On call
72+// - context unsafe.Pointer
73+// - props unsafe.Pointer
74+// - user_data_size uintptr
75+func (_e *MockCAPI_Expecter) PWContextConnect(context any, props any, user_data_size any) *MockCAPI_PWContextConnect_Call {
76+ return &MockCAPI_PWContextConnect_Call{Call: _e.mock.On("PWContextConnect", context, props, user_data_size)}
77+}
78+
79+func (_c *MockCAPI_PWContextConnect_Call) Run(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockCAPI_PWContextConnect_Call {
80+ _c.Call.Run(func(args mock.Arguments) {
81+ var arg0 unsafe.Pointer
82+ if args[0] != nil {
83+ arg0 = args[0].(unsafe.Pointer)
84+ }
85+ var arg1 unsafe.Pointer
86+ if args[1] != nil {
87+ arg1 = args[1].(unsafe.Pointer)
88+ }
89+ var arg2 uintptr
90+ if args[2] != nil {
91+ arg2 = args[2].(uintptr)
92+ }
93+ run(
94+ arg0,
95+ arg1,
96+ arg2,
97+ )
98+ })
99+ return _c
100+}
101+
102+func (_c *MockCAPI_PWContextConnect_Call) Return(v unsafe.Pointer) *MockCAPI_PWContextConnect_Call {
103+ _c.Call.Return(v)
104+ return _c
105+}
106+
107+func (_c *MockCAPI_PWContextConnect_Call) RunAndReturn(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockCAPI_PWContextConnect_Call {
108+ _c.Call.Return(run)
109+ return _c
110+}
111+
112+// PWContextDestroy provides a mock function for the type MockCAPI
113+func (_mock *MockCAPI) PWContextDestroy(context unsafe.Pointer) {
114+ _mock.Called(context)
115+ return
116+}
117+
118+// MockCAPI_PWContextDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextDestroy'
119+type MockCAPI_PWContextDestroy_Call struct {
120+ *mock.Call
121+}
122+
123+// PWContextDestroy is a helper method to define mock.On call
124+// - context unsafe.Pointer
125+func (_e *MockCAPI_Expecter) PWContextDestroy(context any) *MockCAPI_PWContextDestroy_Call {
126+ return &MockCAPI_PWContextDestroy_Call{Call: _e.mock.On("PWContextDestroy", context)}
127+}
128+
129+func (_c *MockCAPI_PWContextDestroy_Call) Run(run func(context unsafe.Pointer)) *MockCAPI_PWContextDestroy_Call {
130+ _c.Call.Run(func(args mock.Arguments) {
131+ var arg0 unsafe.Pointer
132+ if args[0] != nil {
133+ arg0 = args[0].(unsafe.Pointer)
134+ }
135+ run(
136+ arg0,
137+ )
138+ })
139+ return _c
140+}
141+
142+func (_c *MockCAPI_PWContextDestroy_Call) Return() *MockCAPI_PWContextDestroy_Call {
143+ _c.Call.Return()
144+ return _c
145+}
146+
147+func (_c *MockCAPI_PWContextDestroy_Call) RunAndReturn(run func(context unsafe.Pointer)) *MockCAPI_PWContextDestroy_Call {
148+ _c.Run(run)
149+ return _c
150+}
151+
152+// PWContextNew provides a mock function for the type MockCAPI
153+func (_mock *MockCAPI) PWContextNew(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
154+ ret := _mock.Called(loop, props, user_data_size)
155+
156+ if len(ret) == 0 {
157+ panic("no return value specified for PWContextNew")
158+ }
159+
160+ var r0 unsafe.Pointer
161+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
162+ r0 = returnFunc(loop, props, user_data_size)
163+ } else {
164+ r0 = ret.Get(0).(unsafe.Pointer)
165+ }
166+ return r0
167+}
168+
169+// MockCAPI_PWContextNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextNew'
170+type MockCAPI_PWContextNew_Call struct {
171+ *mock.Call
172+}
173+
174+// PWContextNew is a helper method to define mock.On call
175+// - loop unsafe.Pointer
176+// - props unsafe.Pointer
177+// - user_data_size uintptr
178+func (_e *MockCAPI_Expecter) PWContextNew(loop any, props any, user_data_size any) *MockCAPI_PWContextNew_Call {
179+ return &MockCAPI_PWContextNew_Call{Call: _e.mock.On("PWContextNew", loop, props, user_data_size)}
180+}
181+
182+func (_c *MockCAPI_PWContextNew_Call) Run(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockCAPI_PWContextNew_Call {
183+ _c.Call.Run(func(args mock.Arguments) {
184+ var arg0 unsafe.Pointer
185+ if args[0] != nil {
186+ arg0 = args[0].(unsafe.Pointer)
187+ }
188+ var arg1 unsafe.Pointer
189+ if args[1] != nil {
190+ arg1 = args[1].(unsafe.Pointer)
191+ }
192+ var arg2 uintptr
193+ if args[2] != nil {
194+ arg2 = args[2].(uintptr)
195+ }
196+ run(
197+ arg0,
198+ arg1,
199+ arg2,
200+ )
201+ })
202+ return _c
203+}
204+
205+func (_c *MockCAPI_PWContextNew_Call) Return(v unsafe.Pointer) *MockCAPI_PWContextNew_Call {
206+ _c.Call.Return(v)
207+ return _c
208+}
209+
210+func (_c *MockCAPI_PWContextNew_Call) RunAndReturn(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockCAPI_PWContextNew_Call {
211+ _c.Call.Return(run)
212+ return _c
213+}
214+
215+// PWCoreDisconnect provides a mock function for the type MockCAPI
216+func (_mock *MockCAPI) PWCoreDisconnect(core unsafe.Pointer) int32 {
217+ ret := _mock.Called(core)
218+
219+ if len(ret) == 0 {
220+ panic("no return value specified for PWCoreDisconnect")
221+ }
222+
223+ var r0 int32
224+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
225+ r0 = returnFunc(core)
226+ } else {
227+ r0 = ret.Get(0).(int32)
228+ }
229+ return r0
230+}
231+
232+// MockCAPI_PWCoreDisconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWCoreDisconnect'
233+type MockCAPI_PWCoreDisconnect_Call struct {
234+ *mock.Call
235+}
236+
237+// PWCoreDisconnect is a helper method to define mock.On call
238+// - core unsafe.Pointer
239+func (_e *MockCAPI_Expecter) PWCoreDisconnect(core any) *MockCAPI_PWCoreDisconnect_Call {
240+ return &MockCAPI_PWCoreDisconnect_Call{Call: _e.mock.On("PWCoreDisconnect", core)}
241+}
242+
243+func (_c *MockCAPI_PWCoreDisconnect_Call) Run(run func(core unsafe.Pointer)) *MockCAPI_PWCoreDisconnect_Call {
244+ _c.Call.Run(func(args mock.Arguments) {
245+ var arg0 unsafe.Pointer
246+ if args[0] != nil {
247+ arg0 = args[0].(unsafe.Pointer)
248+ }
249+ run(
250+ arg0,
251+ )
252+ })
253+ return _c
254+}
255+
256+func (_c *MockCAPI_PWCoreDisconnect_Call) Return(n int32) *MockCAPI_PWCoreDisconnect_Call {
257+ _c.Call.Return(n)
258+ return _c
259+}
260+
261+func (_c *MockCAPI_PWCoreDisconnect_Call) RunAndReturn(run func(core unsafe.Pointer) int32) *MockCAPI_PWCoreDisconnect_Call {
262+ _c.Call.Return(run)
263+ return _c
264+}
265+
40266 // PWDeinit provides a mock function for the type MockCAPI
41267 func (_mock *MockCAPI) PWDeinit() {
42268 _mock.Called()
@@ -84,7 +310,7 @@ type MockCAPI_PWInit_Call struct {
84310 // PWInit is a helper method to define mock.On call
85311 // - argc *int32
86312 // - argv ***byte
87-func (_e *MockCAPI_Expecter) PWInit(argc interface{}, argv interface{}) *MockCAPI_PWInit_Call {
313+func (_e *MockCAPI_Expecter) PWInit(argc any, argv any) *MockCAPI_PWInit_Call {
88314 return &MockCAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}
89315 }
90316
@@ -116,6 +342,285 @@ func (_c *MockCAPI_PWInit_Call) RunAndReturn(run func(argc *int32, argv ***byte)
116342 return _c
117343 }
118344
345+// PWLoopDestroy provides a mock function for the type MockCAPI
346+func (_mock *MockCAPI) PWLoopDestroy(loop unsafe.Pointer) {
347+ _mock.Called(loop)
348+ return
349+}
350+
351+// MockCAPI_PWLoopDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopDestroy'
352+type MockCAPI_PWLoopDestroy_Call struct {
353+ *mock.Call
354+}
355+
356+// PWLoopDestroy is a helper method to define mock.On call
357+// - loop unsafe.Pointer
358+func (_e *MockCAPI_Expecter) PWLoopDestroy(loop any) *MockCAPI_PWLoopDestroy_Call {
359+ return &MockCAPI_PWLoopDestroy_Call{Call: _e.mock.On("PWLoopDestroy", loop)}
360+}
361+
362+func (_c *MockCAPI_PWLoopDestroy_Call) Run(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopDestroy_Call {
363+ _c.Call.Run(func(args mock.Arguments) {
364+ var arg0 unsafe.Pointer
365+ if args[0] != nil {
366+ arg0 = args[0].(unsafe.Pointer)
367+ }
368+ run(
369+ arg0,
370+ )
371+ })
372+ return _c
373+}
374+
375+func (_c *MockCAPI_PWLoopDestroy_Call) Return() *MockCAPI_PWLoopDestroy_Call {
376+ _c.Call.Return()
377+ return _c
378+}
379+
380+func (_c *MockCAPI_PWLoopDestroy_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopDestroy_Call {
381+ _c.Run(run)
382+ return _c
383+}
384+
385+// PWLoopEnter provides a mock function for the type MockCAPI
386+func (_mock *MockCAPI) PWLoopEnter(loop unsafe.Pointer) {
387+ _mock.Called(loop)
388+ return
389+}
390+
391+// MockCAPI_PWLoopEnter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopEnter'
392+type MockCAPI_PWLoopEnter_Call struct {
393+ *mock.Call
394+}
395+
396+// PWLoopEnter is a helper method to define mock.On call
397+// - loop unsafe.Pointer
398+func (_e *MockCAPI_Expecter) PWLoopEnter(loop any) *MockCAPI_PWLoopEnter_Call {
399+ return &MockCAPI_PWLoopEnter_Call{Call: _e.mock.On("PWLoopEnter", loop)}
400+}
401+
402+func (_c *MockCAPI_PWLoopEnter_Call) Run(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopEnter_Call {
403+ _c.Call.Run(func(args mock.Arguments) {
404+ var arg0 unsafe.Pointer
405+ if args[0] != nil {
406+ arg0 = args[0].(unsafe.Pointer)
407+ }
408+ run(
409+ arg0,
410+ )
411+ })
412+ return _c
413+}
414+
415+func (_c *MockCAPI_PWLoopEnter_Call) Return() *MockCAPI_PWLoopEnter_Call {
416+ _c.Call.Return()
417+ return _c
418+}
419+
420+func (_c *MockCAPI_PWLoopEnter_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopEnter_Call {
421+ _c.Run(run)
422+ return _c
423+}
424+
425+// PWLoopGetFd provides a mock function for the type MockCAPI
426+func (_mock *MockCAPI) PWLoopGetFd(loop unsafe.Pointer) int32 {
427+ ret := _mock.Called(loop)
428+
429+ if len(ret) == 0 {
430+ panic("no return value specified for PWLoopGetFd")
431+ }
432+
433+ var r0 int32
434+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
435+ r0 = returnFunc(loop)
436+ } else {
437+ r0 = ret.Get(0).(int32)
438+ }
439+ return r0
440+}
441+
442+// MockCAPI_PWLoopGetFd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopGetFd'
443+type MockCAPI_PWLoopGetFd_Call struct {
444+ *mock.Call
445+}
446+
447+// PWLoopGetFd is a helper method to define mock.On call
448+// - loop unsafe.Pointer
449+func (_e *MockCAPI_Expecter) PWLoopGetFd(loop any) *MockCAPI_PWLoopGetFd_Call {
450+ return &MockCAPI_PWLoopGetFd_Call{Call: _e.mock.On("PWLoopGetFd", loop)}
451+}
452+
453+func (_c *MockCAPI_PWLoopGetFd_Call) Run(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopGetFd_Call {
454+ _c.Call.Run(func(args mock.Arguments) {
455+ var arg0 unsafe.Pointer
456+ if args[0] != nil {
457+ arg0 = args[0].(unsafe.Pointer)
458+ }
459+ run(
460+ arg0,
461+ )
462+ })
463+ return _c
464+}
465+
466+func (_c *MockCAPI_PWLoopGetFd_Call) Return(n int32) *MockCAPI_PWLoopGetFd_Call {
467+ _c.Call.Return(n)
468+ return _c
469+}
470+
471+func (_c *MockCAPI_PWLoopGetFd_Call) RunAndReturn(run func(loop unsafe.Pointer) int32) *MockCAPI_PWLoopGetFd_Call {
472+ _c.Call.Return(run)
473+ return _c
474+}
475+
476+// PWLoopIterate provides a mock function for the type MockCAPI
477+func (_mock *MockCAPI) PWLoopIterate(loop unsafe.Pointer, timeout int32) int32 {
478+ ret := _mock.Called(loop, timeout)
479+
480+ if len(ret) == 0 {
481+ panic("no return value specified for PWLoopIterate")
482+ }
483+
484+ var r0 int32
485+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, int32) int32); ok {
486+ r0 = returnFunc(loop, timeout)
487+ } else {
488+ r0 = ret.Get(0).(int32)
489+ }
490+ return r0
491+}
492+
493+// MockCAPI_PWLoopIterate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopIterate'
494+type MockCAPI_PWLoopIterate_Call struct {
495+ *mock.Call
496+}
497+
498+// PWLoopIterate is a helper method to define mock.On call
499+// - loop unsafe.Pointer
500+// - timeout int32
501+func (_e *MockCAPI_Expecter) PWLoopIterate(loop any, timeout any) *MockCAPI_PWLoopIterate_Call {
502+ return &MockCAPI_PWLoopIterate_Call{Call: _e.mock.On("PWLoopIterate", loop, timeout)}
503+}
504+
505+func (_c *MockCAPI_PWLoopIterate_Call) Run(run func(loop unsafe.Pointer, timeout int32)) *MockCAPI_PWLoopIterate_Call {
506+ _c.Call.Run(func(args mock.Arguments) {
507+ var arg0 unsafe.Pointer
508+ if args[0] != nil {
509+ arg0 = args[0].(unsafe.Pointer)
510+ }
511+ var arg1 int32
512+ if args[1] != nil {
513+ arg1 = args[1].(int32)
514+ }
515+ run(
516+ arg0,
517+ arg1,
518+ )
519+ })
520+ return _c
521+}
522+
523+func (_c *MockCAPI_PWLoopIterate_Call) Return(n int32) *MockCAPI_PWLoopIterate_Call {
524+ _c.Call.Return(n)
525+ return _c
526+}
527+
528+func (_c *MockCAPI_PWLoopIterate_Call) RunAndReturn(run func(loop unsafe.Pointer, timeout int32) int32) *MockCAPI_PWLoopIterate_Call {
529+ _c.Call.Return(run)
530+ return _c
531+}
532+
533+// PWLoopLeave provides a mock function for the type MockCAPI
534+func (_mock *MockCAPI) PWLoopLeave(loop unsafe.Pointer) {
535+ _mock.Called(loop)
536+ return
537+}
538+
539+// MockCAPI_PWLoopLeave_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopLeave'
540+type MockCAPI_PWLoopLeave_Call struct {
541+ *mock.Call
542+}
543+
544+// PWLoopLeave is a helper method to define mock.On call
545+// - loop unsafe.Pointer
546+func (_e *MockCAPI_Expecter) PWLoopLeave(loop any) *MockCAPI_PWLoopLeave_Call {
547+ return &MockCAPI_PWLoopLeave_Call{Call: _e.mock.On("PWLoopLeave", loop)}
548+}
549+
550+func (_c *MockCAPI_PWLoopLeave_Call) Run(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopLeave_Call {
551+ _c.Call.Run(func(args mock.Arguments) {
552+ var arg0 unsafe.Pointer
553+ if args[0] != nil {
554+ arg0 = args[0].(unsafe.Pointer)
555+ }
556+ run(
557+ arg0,
558+ )
559+ })
560+ return _c
561+}
562+
563+func (_c *MockCAPI_PWLoopLeave_Call) Return() *MockCAPI_PWLoopLeave_Call {
564+ _c.Call.Return()
565+ return _c
566+}
567+
568+func (_c *MockCAPI_PWLoopLeave_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockCAPI_PWLoopLeave_Call {
569+ _c.Run(run)
570+ return _c
571+}
572+
573+// PWLoopNew provides a mock function for the type MockCAPI
574+func (_mock *MockCAPI) PWLoopNew(props unsafe.Pointer) unsafe.Pointer {
575+ ret := _mock.Called(props)
576+
577+ if len(ret) == 0 {
578+ panic("no return value specified for PWLoopNew")
579+ }
580+
581+ var r0 unsafe.Pointer
582+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) unsafe.Pointer); ok {
583+ r0 = returnFunc(props)
584+ } else {
585+ r0 = ret.Get(0).(unsafe.Pointer)
586+ }
587+ return r0
588+}
589+
590+// MockCAPI_PWLoopNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopNew'
591+type MockCAPI_PWLoopNew_Call struct {
592+ *mock.Call
593+}
594+
595+// PWLoopNew is a helper method to define mock.On call
596+// - props unsafe.Pointer
597+func (_e *MockCAPI_Expecter) PWLoopNew(props any) *MockCAPI_PWLoopNew_Call {
598+ return &MockCAPI_PWLoopNew_Call{Call: _e.mock.On("PWLoopNew", props)}
599+}
600+
601+func (_c *MockCAPI_PWLoopNew_Call) Run(run func(props unsafe.Pointer)) *MockCAPI_PWLoopNew_Call {
602+ _c.Call.Run(func(args mock.Arguments) {
603+ var arg0 unsafe.Pointer
604+ if args[0] != nil {
605+ arg0 = args[0].(unsafe.Pointer)
606+ }
607+ run(
608+ arg0,
609+ )
610+ })
611+ return _c
612+}
613+
614+func (_c *MockCAPI_PWLoopNew_Call) Return(v unsafe.Pointer) *MockCAPI_PWLoopNew_Call {
615+ _c.Call.Return(v)
616+ return _c
617+}
618+
619+func (_c *MockCAPI_PWLoopNew_Call) RunAndReturn(run func(props unsafe.Pointer) unsafe.Pointer) *MockCAPI_PWLoopNew_Call {
620+ _c.Call.Return(run)
621+ return _c
622+}
623+
119624 // PWMainLoopDestroy provides a mock function for the type MockCAPI
120625 func (_mock *MockCAPI) PWMainLoopDestroy(loop unsafe.Pointer) {
121626 _mock.Called(loop)
@@ -129,7 +634,7 @@ type MockCAPI_PWMainLoopDestroy_Call struct {
129634
130635 // PWMainLoopDestroy is a helper method to define mock.On call
131636 // - loop unsafe.Pointer
132-func (_e *MockCAPI_Expecter) PWMainLoopDestroy(loop interface{}) *MockCAPI_PWMainLoopDestroy_Call {
637+func (_e *MockCAPI_Expecter) PWMainLoopDestroy(loop any) *MockCAPI_PWMainLoopDestroy_Call {
133638 return &MockCAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}
134639 }
135640
@@ -180,7 +685,7 @@ type MockCAPI_PWMainLoopGetLoop_Call struct {
180685
181686 // PWMainLoopGetLoop is a helper method to define mock.On call
182687 // - loop unsafe.Pointer
183-func (_e *MockCAPI_Expecter) PWMainLoopGetLoop(loop interface{}) *MockCAPI_PWMainLoopGetLoop_Call {
688+func (_e *MockCAPI_Expecter) PWMainLoopGetLoop(loop any) *MockCAPI_PWMainLoopGetLoop_Call {
184689 return &MockCAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}
185690 }
186691
@@ -231,7 +736,7 @@ type MockCAPI_PWMainLoopNew_Call struct {
231736
232737 // PWMainLoopNew is a helper method to define mock.On call
233738 // - props unsafe.Pointer
234-func (_e *MockCAPI_Expecter) PWMainLoopNew(props interface{}) *MockCAPI_PWMainLoopNew_Call {
739+func (_e *MockCAPI_Expecter) PWMainLoopNew(props any) *MockCAPI_PWMainLoopNew_Call {
235740 return &MockCAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}
236741 }
237742
@@ -282,7 +787,7 @@ type MockCAPI_PWMainLoopQuit_Call struct {
282787
283788 // PWMainLoopQuit is a helper method to define mock.On call
284789 // - loop unsafe.Pointer
285-func (_e *MockCAPI_Expecter) PWMainLoopQuit(loop interface{}) *MockCAPI_PWMainLoopQuit_Call {
790+func (_e *MockCAPI_Expecter) PWMainLoopQuit(loop any) *MockCAPI_PWMainLoopQuit_Call {
286791 return &MockCAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}
287792 }
288793
@@ -333,7 +838,7 @@ type MockCAPI_PWMainLoopRun_Call struct {
333838
334839 // PWMainLoopRun is a helper method to define mock.On call
335840 // - loop unsafe.Pointer
336-func (_e *MockCAPI_Expecter) PWMainLoopRun(loop interface{}) *MockCAPI_PWMainLoopRun_Call {
841+func (_e *MockCAPI_Expecter) PWMainLoopRun(loop any) *MockCAPI_PWMainLoopRun_Call {
337842 return &MockCAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}
338843 }
339844
@@ -355,7 +860,58 @@ func (_c *MockCAPI_PWMainLoopRun_Call) Return(n int32) *MockCAPI_PWMainLoopRun_C
355860 return _c
356861 }
357862
358-func (_c *MockCAPI_PWMainLoopRun_Call) RunAndReturn(run func(loop unsafe.Pointer) int32) *MockCAPI_PWMainLoopRun_Call {
863+func (_c *MockCAPI_PWMainLoopRun_Call) RunAndReturn(run func(loop unsafe.Pointer) int32) *MockCAPI_PWMainLoopRun_Call {
864+ _c.Call.Return(run)
865+ return _c
866+}
867+
868+// PWPropertiesNewString provides a mock function for the type MockCAPI
869+func (_mock *MockCAPI) PWPropertiesNewString(args *byte) unsafe.Pointer {
870+ ret := _mock.Called(args)
871+
872+ if len(ret) == 0 {
873+ panic("no return value specified for PWPropertiesNewString")
874+ }
875+
876+ var r0 unsafe.Pointer
877+ if returnFunc, ok := ret.Get(0).(func(*byte) unsafe.Pointer); ok {
878+ r0 = returnFunc(args)
879+ } else {
880+ r0 = ret.Get(0).(unsafe.Pointer)
881+ }
882+ return r0
883+}
884+
885+// MockCAPI_PWPropertiesNewString_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWPropertiesNewString'
886+type MockCAPI_PWPropertiesNewString_Call struct {
887+ *mock.Call
888+}
889+
890+// PWPropertiesNewString is a helper method to define mock.On call
891+// - args *byte
892+func (_e *MockCAPI_Expecter) PWPropertiesNewString(args any) *MockCAPI_PWPropertiesNewString_Call {
893+ return &MockCAPI_PWPropertiesNewString_Call{Call: _e.mock.On("PWPropertiesNewString", args)}
894+}
895+
896+func (_c *MockCAPI_PWPropertiesNewString_Call) Run(run func(args *byte)) *MockCAPI_PWPropertiesNewString_Call {
897+ _c.Call.Run(func(args mock.Arguments) {
898+ var arg0 *byte
899+ if args[0] != nil {
900+ arg0 = args[0].(*byte)
901+ }
902+ run(
903+ arg0,
904+ )
905+ })
906+ return _c
907+}
908+
909+func (_c *MockCAPI_PWPropertiesNewString_Call) Return(v unsafe.Pointer) *MockCAPI_PWPropertiesNewString_Call {
910+ _c.Call.Return(v)
911+ return _c
912+}
913+
914+func (_c *MockCAPI_PWPropertiesNewString_Call) RunAndReturn(run func(args *byte) unsafe.Pointer) *MockCAPI_PWPropertiesNewString_Call {
359915 _c.Call.Return(run)
360916 return _c
361917 }
@@ -387,7 +943,7 @@ type MockCAPI_PWStreamAddListener_Call struct {
387943 // - listener unsafe.Pointer
388944 // - events unsafe.Pointer
389945 // - data unsafe.Pointer
390-func (_e *MockCAPI_Expecter) PWStreamAddListener(stream interface{}, listener interface{}, events interface{}, data interface{}) *MockCAPI_PWStreamAddListener_Call {
946+func (_e *MockCAPI_Expecter) PWStreamAddListener(stream any, listener any, events any, data any) *MockCAPI_PWStreamAddListener_Call {
391947 return &MockCAPI_PWStreamAddListener_Call{Call: _e.mock.On("PWStreamAddListener", stream, listener, events, data)}
392948 }
393949
@@ -458,7 +1014,7 @@ type MockCAPI_PWStreamConnect_Call struct {
4581014 // - flags uint32
4591015 // - ports unsafe.Pointer
4601016 // - n_ports uint32
461-func (_e *MockCAPI_Expecter) PWStreamConnect(stream interface{}, direction interface{}, id interface{}, flags interface{}, ports interface{}, n_ports interface{}) *MockCAPI_PWStreamConnect_Call {
1017+func (_e *MockCAPI_Expecter) PWStreamConnect(stream any, direction any, id any, flags any, ports any, n_ports any) *MockCAPI_PWStreamConnect_Call {
4621018 return &MockCAPI_PWStreamConnect_Call{Call: _e.mock.On("PWStreamConnect", stream, direction, id, flags, ports, n_ports)}
4631019 }
4641020
@@ -534,7 +1090,7 @@ type MockCAPI_PWStreamDequeueBuffer_Call struct {
5341090
5351091 // PWStreamDequeueBuffer is a helper method to define mock.On call
5361092 // - stream unsafe.Pointer
537-func (_e *MockCAPI_Expecter) PWStreamDequeueBuffer(stream interface{}) *MockCAPI_PWStreamDequeueBuffer_Call {
1093+func (_e *MockCAPI_Expecter) PWStreamDequeueBuffer(stream any) *MockCAPI_PWStreamDequeueBuffer_Call {
5381094 return &MockCAPI_PWStreamDequeueBuffer_Call{Call: _e.mock.On("PWStreamDequeueBuffer", stream)}
5391095 }
5401096
@@ -574,7 +1130,7 @@ type MockCAPI_PWStreamDestroy_Call struct {
5741130
5751131 // PWStreamDestroy is a helper method to define mock.On call
5761132 // - stream unsafe.Pointer
577-func (_e *MockCAPI_Expecter) PWStreamDestroy(stream interface{}) *MockCAPI_PWStreamDestroy_Call {
1133+func (_e *MockCAPI_Expecter) PWStreamDestroy(stream any) *MockCAPI_PWStreamDestroy_Call {
5781134 return &MockCAPI_PWStreamDestroy_Call{Call: _e.mock.On("PWStreamDestroy", stream)}
5791135 }
5801136
@@ -625,7 +1181,7 @@ type MockCAPI_PWStreamDisconnect_Call struct {
6251181
6261182 // PWStreamDisconnect is a helper method to define mock.On call
6271183 // - stream unsafe.Pointer
628-func (_e *MockCAPI_Expecter) PWStreamDisconnect(stream interface{}) *MockCAPI_PWStreamDisconnect_Call {
1184+func (_e *MockCAPI_Expecter) PWStreamDisconnect(stream any) *MockCAPI_PWStreamDisconnect_Call {
6291185 return &MockCAPI_PWStreamDisconnect_Call{Call: _e.mock.On("PWStreamDisconnect", stream)}
6301186 }
6311187
@@ -652,6 +1208,171 @@ func (_c *MockCAPI_PWStreamDisconnect_Call) RunAndReturn(run func(stream unsafe.
6521208 return _c
6531209 }
6541210
1211+// PWStreamGetNodeId provides a mock function for the type MockCAPI
1212+func (_mock *MockCAPI) PWStreamGetNodeId(stream unsafe.Pointer) uint32 {
1213+ ret := _mock.Called(stream)
1214+
1215+ if len(ret) == 0 {
1216+ panic("no return value specified for PWStreamGetNodeId")
1217+ }
1218+
1219+ var r0 uint32
1220+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) uint32); ok {
1221+ r0 = returnFunc(stream)
1222+ } else {
1223+ r0 = ret.Get(0).(uint32)
1224+ }
1225+ return r0
1226+}
1227+
1228+// MockCAPI_PWStreamGetNodeId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamGetNodeId'
1229+type MockCAPI_PWStreamGetNodeId_Call struct {
1230+ *mock.Call
1231+}
1232+
1233+// PWStreamGetNodeId is a helper method to define mock.On call
1234+// - stream unsafe.Pointer
1235+func (_e *MockCAPI_Expecter) PWStreamGetNodeId(stream any) *MockCAPI_PWStreamGetNodeId_Call {
1236+ return &MockCAPI_PWStreamGetNodeId_Call{Call: _e.mock.On("PWStreamGetNodeId", stream)}
1237+}
1238+
1239+func (_c *MockCAPI_PWStreamGetNodeId_Call) Run(run func(stream unsafe.Pointer)) *MockCAPI_PWStreamGetNodeId_Call {
1240+ _c.Call.Run(func(args mock.Arguments) {
1241+ var arg0 unsafe.Pointer
1242+ if args[0] != nil {
1243+ arg0 = args[0].(unsafe.Pointer)
1244+ }
1245+ run(
1246+ arg0,
1247+ )
1248+ })
1249+ return _c
1250+}
1251+
1252+func (_c *MockCAPI_PWStreamGetNodeId_Call) Return(v uint32) *MockCAPI_PWStreamGetNodeId_Call {
1253+ _c.Call.Return(v)
1254+ return _c
1255+}
1256+
1257+func (_c *MockCAPI_PWStreamGetNodeId_Call) RunAndReturn(run func(stream unsafe.Pointer) uint32) *MockCAPI_PWStreamGetNodeId_Call {
1258+ _c.Call.Return(run)
1259+ return _c
1260+}
1261+
1262+// PWStreamIsDriving provides a mock function for the type MockCAPI
1263+func (_mock *MockCAPI) PWStreamIsDriving(stream unsafe.Pointer) bool {
1264+ ret := _mock.Called(stream)
1265+
1266+ if len(ret) == 0 {
1267+ panic("no return value specified for PWStreamIsDriving")
1268+ }
1269+
1270+ var r0 bool
1271+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) bool); ok {
1272+ r0 = returnFunc(stream)
1273+ } else {
1274+ r0 = ret.Get(0).(bool)
1275+ }
1276+ return r0
1277+}
1278+
1279+// MockCAPI_PWStreamIsDriving_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamIsDriving'
1280+type MockCAPI_PWStreamIsDriving_Call struct {
1281+ *mock.Call
1282+}
1283+
1284+// PWStreamIsDriving is a helper method to define mock.On call
1285+// - stream unsafe.Pointer
1286+func (_e *MockCAPI_Expecter) PWStreamIsDriving(stream any) *MockCAPI_PWStreamIsDriving_Call {
1287+ return &MockCAPI_PWStreamIsDriving_Call{Call: _e.mock.On("PWStreamIsDriving", stream)}
1288+}
1289+
1290+func (_c *MockCAPI_PWStreamIsDriving_Call) Run(run func(stream unsafe.Pointer)) *MockCAPI_PWStreamIsDriving_Call {
1291+ _c.Call.Run(func(args mock.Arguments) {
1292+ var arg0 unsafe.Pointer
1293+ if args[0] != nil {
1294+ arg0 = args[0].(unsafe.Pointer)
1295+ }
1296+ run(
1297+ arg0,
1298+ )
1299+ })
1300+ return _c
1301+}
1302+
1303+func (_c *MockCAPI_PWStreamIsDriving_Call) Return(b bool) *MockCAPI_PWStreamIsDriving_Call {
1304+ _c.Call.Return(b)
1305+ return _c
1306+}
1307+
1308+func (_c *MockCAPI_PWStreamIsDriving_Call) RunAndReturn(run func(stream unsafe.Pointer) bool) *MockCAPI_PWStreamIsDriving_Call {
1309+ _c.Call.Return(run)
1310+ return _c
1311+}
1312+
1313+// PWStreamNew provides a mock function for the type MockCAPI
1314+func (_mock *MockCAPI) PWStreamNew(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer {
1315+ ret := _mock.Called(core, name, props)
1316+
1317+ if len(ret) == 0 {
1318+ panic("no return value specified for PWStreamNew")
1319+ }
1320+
1321+ var r0 unsafe.Pointer
1322+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, *byte, unsafe.Pointer) unsafe.Pointer); ok {
1323+ r0 = returnFunc(core, name, props)
1324+ } else {
1325+ r0 = ret.Get(0).(unsafe.Pointer)
1326+ }
1327+ return r0
1328+}
1329+
1330+// MockCAPI_PWStreamNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamNew'
1331+type MockCAPI_PWStreamNew_Call struct {
1332+ *mock.Call
1333+}
1334+
1335+// PWStreamNew is a helper method to define mock.On call
1336+// - core unsafe.Pointer
1337+// - name *byte
1338+// - props unsafe.Pointer
1339+func (_e *MockCAPI_Expecter) PWStreamNew(core any, name any, props any) *MockCAPI_PWStreamNew_Call {
1340+ return &MockCAPI_PWStreamNew_Call{Call: _e.mock.On("PWStreamNew", core, name, props)}
1341+}
1342+
1343+func (_c *MockCAPI_PWStreamNew_Call) Run(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer)) *MockCAPI_PWStreamNew_Call {
1344+ _c.Call.Run(func(args mock.Arguments) {
1345+ var arg0 unsafe.Pointer
1346+ if args[0] != nil {
1347+ arg0 = args[0].(unsafe.Pointer)
1348+ }
1349+ var arg1 *byte
1350+ if args[1] != nil {
1351+ arg1 = args[1].(*byte)
1352+ }
1353+ var arg2 unsafe.Pointer
1354+ if args[2] != nil {
1355+ arg2 = args[2].(unsafe.Pointer)
1356+ }
1357+ run(
1358+ arg0,
1359+ arg1,
1360+ arg2,
1361+ )
1362+ })
1363+ return _c
1364+}
1365+
1366+func (_c *MockCAPI_PWStreamNew_Call) Return(v unsafe.Pointer) *MockCAPI_PWStreamNew_Call {
1367+ _c.Call.Return(v)
1368+ return _c
1369+}
1370+
1371+func (_c *MockCAPI_PWStreamNew_Call) RunAndReturn(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer) *MockCAPI_PWStreamNew_Call {
1372+ _c.Call.Return(run)
1373+ return _c
1374+}
1375+
6551376 // PWStreamNewSimple provides a mock function for the type MockCAPI
6561377 func (_mock *MockCAPI) PWStreamNewSimple(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) unsafe.Pointer {
6571378 ret := _mock.Called(context, name, props, events, data)
@@ -680,7 +1401,7 @@ type MockCAPI_PWStreamNewSimple_Call struct {
6801401 // - props unsafe.Pointer
6811402 // - events unsafe.Pointer
6821403 // - data unsafe.Pointer
683-func (_e *MockCAPI_Expecter) PWStreamNewSimple(context interface{}, name interface{}, props interface{}, events interface{}, data interface{}) *MockCAPI_PWStreamNewSimple_Call {
1404+func (_e *MockCAPI_Expecter) PWStreamNewSimple(context any, name any, props any, events any, data any) *MockCAPI_PWStreamNewSimple_Call {
6841405 return &MockCAPI_PWStreamNewSimple_Call{Call: _e.mock.On("PWStreamNewSimple", context, name, props, events, data)}
6851406 }
6861407
@@ -752,7 +1473,7 @@ type MockCAPI_PWStreamQueueBuffer_Call struct {
7521473 // PWStreamQueueBuffer is a helper method to define mock.On call
7531474 // - stream unsafe.Pointer
7541475 // - buffer unsafe.Pointer
755-func (_e *MockCAPI_Expecter) PWStreamQueueBuffer(stream interface{}, buffer interface{}) *MockCAPI_PWStreamQueueBuffer_Call {
1476+func (_e *MockCAPI_Expecter) PWStreamQueueBuffer(stream any, buffer any) *MockCAPI_PWStreamQueueBuffer_Call {
7561477 return &MockCAPI_PWStreamQueueBuffer_Call{Call: _e.mock.On("PWStreamQueueBuffer", stream, buffer)}
7571478 }
7581479
@@ -784,6 +1505,63 @@ func (_c *MockCAPI_PWStreamQueueBuffer_Call) RunAndReturn(run func(stream unsafe
7841505 return _c
7851506 }
7861507
1508+// PWStreamReturnBuffer provides a mock function for the type MockCAPI
1509+func (_mock *MockCAPI) PWStreamReturnBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
1510+ ret := _mock.Called(stream, buffer)
1511+
1512+ if len(ret) == 0 {
1513+ panic("no return value specified for PWStreamReturnBuffer")
1514+ }
1515+
1516+ var r0 int32
1517+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer) int32); ok {
1518+ r0 = returnFunc(stream, buffer)
1519+ } else {
1520+ r0 = ret.Get(0).(int32)
1521+ }
1522+ return r0
1523+}
1524+
1525+// MockCAPI_PWStreamReturnBuffer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamReturnBuffer'
1526+type MockCAPI_PWStreamReturnBuffer_Call struct {
1527+ *mock.Call
1528+}
1529+
1530+// PWStreamReturnBuffer is a helper method to define mock.On call
1531+// - stream unsafe.Pointer
1532+// - buffer unsafe.Pointer
1533+func (_e *MockCAPI_Expecter) PWStreamReturnBuffer(stream any, buffer any) *MockCAPI_PWStreamReturnBuffer_Call {
1534+ return &MockCAPI_PWStreamReturnBuffer_Call{Call: _e.mock.On("PWStreamReturnBuffer", stream, buffer)}
1535+}
1536+
1537+func (_c *MockCAPI_PWStreamReturnBuffer_Call) Run(run func(stream unsafe.Pointer, buffer unsafe.Pointer)) *MockCAPI_PWStreamReturnBuffer_Call {
1538+ _c.Call.Run(func(args mock.Arguments) {
1539+ var arg0 unsafe.Pointer
1540+ if args[0] != nil {
1541+ arg0 = args[0].(unsafe.Pointer)
1542+ }
1543+ var arg1 unsafe.Pointer
1544+ if args[1] != nil {
1545+ arg1 = args[1].(unsafe.Pointer)
1546+ }
1547+ run(
1548+ arg0,
1549+ arg1,
1550+ )
1551+ })
1552+ return _c
1553+}
1554+
1555+func (_c *MockCAPI_PWStreamReturnBuffer_Call) Return(n int32) *MockCAPI_PWStreamReturnBuffer_Call {
1556+ _c.Call.Return(n)
1557+ return _c
1558+}
1559+
1560+func (_c *MockCAPI_PWStreamReturnBuffer_Call) RunAndReturn(run func(stream unsafe.Pointer, buffer unsafe.Pointer) int32) *MockCAPI_PWStreamReturnBuffer_Call {
1561+ _c.Call.Return(run)
1562+ return _c
1563+}
1564+
7871565 // PWStreamSetActive provides a mock function for the type MockCAPI
7881566 func (_mock *MockCAPI) PWStreamSetActive(stream unsafe.Pointer, active bool) int32 {
7891567 ret := _mock.Called(stream, active)
@@ -809,7 +1587,7 @@ type MockCAPI_PWStreamSetActive_Call struct {
8091587 // PWStreamSetActive is a helper method to define mock.On call
8101588 // - stream unsafe.Pointer
8111589 // - active bool
812-func (_e *MockCAPI_Expecter) PWStreamSetActive(stream interface{}, active interface{}) *MockCAPI_PWStreamSetActive_Call {
1590+func (_e *MockCAPI_Expecter) PWStreamSetActive(stream any, active any) *MockCAPI_PWStreamSetActive_Call {
8131591 return &MockCAPI_PWStreamSetActive_Call{Call: _e.mock.On("PWStreamSetActive", stream, active)}
8141592 }
8151593
@@ -840,3 +1618,117 @@ func (_c *MockCAPI_PWStreamSetActive_Call) RunAndReturn(run func(stream unsafe.P
8401618 _c.Call.Return(run)
8411619 return _c
8421620 }
1621+
1622+// PWStreamTriggerProcess provides a mock function for the type MockCAPI
1623+func (_mock *MockCAPI) PWStreamTriggerProcess(stream unsafe.Pointer) int32 {
1624+ ret := _mock.Called(stream)
1625+
1626+ if len(ret) == 0 {
1627+ panic("no return value specified for PWStreamTriggerProcess")
1628+ }
1629+
1630+ var r0 int32
1631+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
1632+ r0 = returnFunc(stream)
1633+ } else {
1634+ r0 = ret.Get(0).(int32)
1635+ }
1636+ return r0
1637+}
1638+
1639+// MockCAPI_PWStreamTriggerProcess_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamTriggerProcess'
1640+type MockCAPI_PWStreamTriggerProcess_Call struct {
1641+ *mock.Call
1642+}
1643+
1644+// PWStreamTriggerProcess is a helper method to define mock.On call
1645+// - stream unsafe.Pointer
1646+func (_e *MockCAPI_Expecter) PWStreamTriggerProcess(stream any) *MockCAPI_PWStreamTriggerProcess_Call {
1647+ return &MockCAPI_PWStreamTriggerProcess_Call{Call: _e.mock.On("PWStreamTriggerProcess", stream)}
1648+}
1649+
1650+func (_c *MockCAPI_PWStreamTriggerProcess_Call) Run(run func(stream unsafe.Pointer)) *MockCAPI_PWStreamTriggerProcess_Call {
1651+ _c.Call.Run(func(args mock.Arguments) {
1652+ var arg0 unsafe.Pointer
1653+ if args[0] != nil {
1654+ arg0 = args[0].(unsafe.Pointer)
1655+ }
1656+ run(
1657+ arg0,
1658+ )
1659+ })
1660+ return _c
1661+}
1662+
1663+func (_c *MockCAPI_PWStreamTriggerProcess_Call) Return(n int32) *MockCAPI_PWStreamTriggerProcess_Call {
1664+ _c.Call.Return(n)
1665+ return _c
1666+}
1667+
1668+func (_c *MockCAPI_PWStreamTriggerProcess_Call) RunAndReturn(run func(stream unsafe.Pointer) int32) *MockCAPI_PWStreamTriggerProcess_Call {
1669+ _c.Call.Return(run)
1670+ return _c
1671+}
1672+
1673+// PWStreamUpdateParams provides a mock function for the type MockCAPI
1674+func (_mock *MockCAPI) PWStreamUpdateParams(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32 {
1675+ ret := _mock.Called(stream, params, n_params)
1676+
1677+ if len(ret) == 0 {
1678+ panic("no return value specified for PWStreamUpdateParams")
1679+ }
1680+
1681+ var r0 int32
1682+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uint32) int32); ok {
1683+ r0 = returnFunc(stream, params, n_params)
1684+ } else {
1685+ r0 = ret.Get(0).(int32)
1686+ }
1687+ return r0
1688+}
1689+
1690+// MockCAPI_PWStreamUpdateParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamUpdateParams'
1691+type MockCAPI_PWStreamUpdateParams_Call struct {
1692+ *mock.Call
1693+}
1694+
1695+// PWStreamUpdateParams is a helper method to define mock.On call
1696+// - stream unsafe.Pointer
1697+// - params unsafe.Pointer
1698+// - n_params uint32
1699+func (_e *MockCAPI_Expecter) PWStreamUpdateParams(stream any, params any, n_params any) *MockCAPI_PWStreamUpdateParams_Call {
1700+ return &MockCAPI_PWStreamUpdateParams_Call{Call: _e.mock.On("PWStreamUpdateParams", stream, params, n_params)}
1701+}
1702+
1703+func (_c *MockCAPI_PWStreamUpdateParams_Call) Run(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32)) *MockCAPI_PWStreamUpdateParams_Call {
1704+ _c.Call.Run(func(args mock.Arguments) {
1705+ var arg0 unsafe.Pointer
1706+ if args[0] != nil {
1707+ arg0 = args[0].(unsafe.Pointer)
1708+ }
1709+ var arg1 unsafe.Pointer
1710+ if args[1] != nil {
1711+ arg1 = args[1].(unsafe.Pointer)
1712+ }
1713+ var arg2 uint32
1714+ if args[2] != nil {
1715+ arg2 = args[2].(uint32)
1716+ }
1717+ run(
1718+ arg0,
1719+ arg1,
1720+ arg2,
1721+ )
1722+ })
1723+ return _c
1724+}
1725+
1726+func (_c *MockCAPI_PWStreamUpdateParams_Call) Return(n int32) *MockCAPI_PWStreamUpdateParams_Call {
1727+ _c.Call.Return(n)
1728+ return _c
1729+}
1730+
1731+func (_c *MockCAPI_PWStreamUpdateParams_Call) RunAndReturn(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32) *MockCAPI_PWStreamUpdateParams_Call {
1732+ _c.Call.Return(run)
1733+ return _c
1734+}
M internal/ports/out/mocks/mock_InitAPI.go +11 -2
@@ -14,10 +14,19 @@ func NewMockInitAPI(t interface {
1414 mock.TestingT
1515 Cleanup(func())
1616 }) *MockInitAPI {
17+ if helper, ok := t.(interface{ Helper() }); ok {
18+ helper.Helper()
19+ }
20+
1721 mock := &MockInitAPI{}
1822 mock.Mock.Test(t)
1923
20- t.Cleanup(func() { mock.AssertExpectations(t) })
24+ t.Cleanup(func() {
25+ if helper, ok := t.(interface{ Helper() }); ok {
26+ helper.Helper()
27+ }
28+ mock.AssertExpectations(t)
29+ })
2130
2231 return mock
2332 }
@@ -82,7 +91,7 @@ type MockInitAPI_PWInit_Call struct {
8291 // PWInit is a helper method to define mock.On call
8392 // - argc *int32
8493 // - argv ***byte
85-func (_e *MockInitAPI_Expecter) PWInit(argc interface{}, argv interface{}) *MockInitAPI_PWInit_Call {
94+func (_e *MockInitAPI_Expecter) PWInit(argc any, argv any) *MockInitAPI_PWInit_Call {
8695 return &MockInitAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}
8796 }
8897
M internal/ports/out/mocks/mock_LoopAPI.go +15 -6
@@ -16,10 +16,19 @@ func NewMockLoopAPI(t interface {
1616 mock.TestingT
1717 Cleanup(func())
1818 }) *MockLoopAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
1923 mock := &MockLoopAPI{}
2024 mock.Mock.Test(t)
2125
22- t.Cleanup(func() { mock.AssertExpectations(t) })
26+ t.Cleanup(func() {
27+ if helper, ok := t.(interface{ Helper() }); ok {
28+ helper.Helper()
29+ }
30+ mock.AssertExpectations(t)
31+ })
2332
2433 return mock
2534 }
@@ -50,7 +59,7 @@ type MockLoopAPI_PWMainLoopDestroy_Call struct {
5059
5160 // PWMainLoopDestroy is a helper method to define mock.On call
5261 // - loop unsafe.Pointer
53-func (_e *MockLoopAPI_Expecter) PWMainLoopDestroy(loop interface{}) *MockLoopAPI_PWMainLoopDestroy_Call {
62+func (_e *MockLoopAPI_Expecter) PWMainLoopDestroy(loop any) *MockLoopAPI_PWMainLoopDestroy_Call {
5463 return &MockLoopAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}
5564 }
5665
@@ -101,7 +110,7 @@ type MockLoopAPI_PWMainLoopGetLoop_Call struct {
101110
102111 // PWMainLoopGetLoop is a helper method to define mock.On call
103112 // - loop unsafe.Pointer
104-func (_e *MockLoopAPI_Expecter) PWMainLoopGetLoop(loop interface{}) *MockLoopAPI_PWMainLoopGetLoop_Call {
113+func (_e *MockLoopAPI_Expecter) PWMainLoopGetLoop(loop any) *MockLoopAPI_PWMainLoopGetLoop_Call {
105114 return &MockLoopAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}
106115 }
107116
@@ -152,7 +161,7 @@ type MockLoopAPI_PWMainLoopNew_Call struct {
152161
153162 // PWMainLoopNew is a helper method to define mock.On call
154163 // - props unsafe.Pointer
155-func (_e *MockLoopAPI_Expecter) PWMainLoopNew(props interface{}) *MockLoopAPI_PWMainLoopNew_Call {
164+func (_e *MockLoopAPI_Expecter) PWMainLoopNew(props any) *MockLoopAPI_PWMainLoopNew_Call {
156165 return &MockLoopAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}
157166 }
158167
@@ -203,7 +212,7 @@ type MockLoopAPI_PWMainLoopQuit_Call struct {
203212
204213 // PWMainLoopQuit is a helper method to define mock.On call
205214 // - loop unsafe.Pointer
206-func (_e *MockLoopAPI_Expecter) PWMainLoopQuit(loop interface{}) *MockLoopAPI_PWMainLoopQuit_Call {
215+func (_e *MockLoopAPI_Expecter) PWMainLoopQuit(loop any) *MockLoopAPI_PWMainLoopQuit_Call {
207216 return &MockLoopAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}
208217 }
209218
@@ -254,7 +263,7 @@ type MockLoopAPI_PWMainLoopRun_Call struct {
254263
255264 // PWMainLoopRun is a helper method to define mock.On call
256265 // - loop unsafe.Pointer
257-func (_e *MockLoopAPI_Expecter) PWMainLoopRun(loop interface{}) *MockLoopAPI_PWMainLoopRun_Call {
266+func (_e *MockLoopAPI_Expecter) PWMainLoopRun(loop any) *MockLoopAPI_PWMainLoopRun_Call {
258267 return &MockLoopAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}
259268 }
260269
A internal/ports/out/mocks/mock_SharedMemAPI.go +165 -0
@@ -0,0 +1,165 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ mock "github.com/stretchr/testify/mock"
9+)
10+
11+// NewMockSharedMemAPI creates a new instance of MockSharedMemAPI. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
12+// The first argument is typically a *testing.T value.
13+func NewMockSharedMemAPI(t interface {
14+ mock.TestingT
15+ Cleanup(func())
16+}) *MockSharedMemAPI {
17+ if helper, ok := t.(interface{ Helper() }); ok {
18+ helper.Helper()
19+ }
20+
21+ mock := &MockSharedMemAPI{}
22+ mock.Mock.Test(t)
23+
24+ t.Cleanup(func() {
25+ if helper, ok := t.(interface{ Helper() }); ok {
26+ helper.Helper()
27+ }
28+ mock.AssertExpectations(t)
29+ })
30+
31+ return mock
32+}
33+
34+// MockSharedMemAPI is an autogenerated mock type for the SharedMemAPI type
35+type MockSharedMemAPI struct {
36+ mock.Mock
37+}
38+
39+type MockSharedMemAPI_Expecter struct {
40+ mock *mock.Mock
41+}
42+
43+func (_m *MockSharedMemAPI) EXPECT() *MockSharedMemAPI_Expecter {
44+ return &MockSharedMemAPI_Expecter{mock: &_m.Mock}
45+}
46+
47+// FreeSharedMem provides a mock function for the type MockSharedMemAPI
48+func (_mock *MockSharedMemAPI) FreeSharedMem(fd int, data []byte) {
49+ _mock.Called(fd, data)
50+ return
51+}
52+
53+// MockSharedMemAPI_FreeSharedMem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FreeSharedMem'
54+type MockSharedMemAPI_FreeSharedMem_Call struct {
55+ *mock.Call
56+}
57+
58+// FreeSharedMem is a helper method to define mock.On call
59+// - fd int
60+// - data []byte
61+func (_e *MockSharedMemAPI_Expecter) FreeSharedMem(fd any, data any) *MockSharedMemAPI_FreeSharedMem_Call {
62+ return &MockSharedMemAPI_FreeSharedMem_Call{Call: _e.mock.On("FreeSharedMem", fd, data)}
63+}
64+
65+func (_c *MockSharedMemAPI_FreeSharedMem_Call) Run(run func(fd int, data []byte)) *MockSharedMemAPI_FreeSharedMem_Call {
66+ _c.Call.Run(func(args mock.Arguments) {
67+ var arg0 int
68+ if args[0] != nil {
69+ arg0 = args[0].(int)
70+ }
71+ var arg1 []byte
72+ if args[1] != nil {
73+ arg1 = args[1].([]byte)
74+ }
75+ run(
76+ arg0,
77+ arg1,
78+ )
79+ })
80+ return _c
81+}
82+
83+func (_c *MockSharedMemAPI_FreeSharedMem_Call) Return() *MockSharedMemAPI_FreeSharedMem_Call {
84+ _c.Call.Return()
85+ return _c
86+}
87+
88+func (_c *MockSharedMemAPI_FreeSharedMem_Call) RunAndReturn(run func(fd int, data []byte)) *MockSharedMemAPI_FreeSharedMem_Call {
89+ _c.Run(run)
90+ return _c
91+}
92+
93+// NewSharedMem provides a mock function for the type MockSharedMemAPI
94+func (_mock *MockSharedMemAPI) NewSharedMem(name string, size int) (int, []byte, error) {
95+ ret := _mock.Called(name, size)
96+
97+ if len(ret) == 0 {
98+ panic("no return value specified for NewSharedMem")
99+ }
100+
101+ var r0 int
102+ var r1 []byte
103+ var r2 error
104+ if returnFunc, ok := ret.Get(0).(func(string, int) (int, []byte, error)); ok {
105+ return returnFunc(name, size)
106+ }
107+ if returnFunc, ok := ret.Get(0).(func(string, int) int); ok {
108+ r0 = returnFunc(name, size)
109+ } else {
110+ r0 = ret.Get(0).(int)
111+ }
112+ if returnFunc, ok := ret.Get(1).(func(string, int) []byte); ok {
113+ r1 = returnFunc(name, size)
114+ } else {
115+ if ret.Get(1) != nil {
116+ r1 = ret.Get(1).([]byte)
117+ }
118+ }
119+ if returnFunc, ok := ret.Get(2).(func(string, int) error); ok {
120+ r2 = returnFunc(name, size)
121+ } else {
122+ r2 = ret.Error(2)
123+ }
124+ return r0, r1, r2
125+}
126+
127+// MockSharedMemAPI_NewSharedMem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewSharedMem'
128+type MockSharedMemAPI_NewSharedMem_Call struct {
129+ *mock.Call
130+}
131+
132+// NewSharedMem is a helper method to define mock.On call
133+// - name string
134+// - size int
135+func (_e *MockSharedMemAPI_Expecter) NewSharedMem(name any, size any) *MockSharedMemAPI_NewSharedMem_Call {
136+ return &MockSharedMemAPI_NewSharedMem_Call{Call: _e.mock.On("NewSharedMem", name, size)}
137+}
138+
139+func (_c *MockSharedMemAPI_NewSharedMem_Call) Run(run func(name string, size int)) *MockSharedMemAPI_NewSharedMem_Call {
140+ _c.Call.Run(func(args mock.Arguments) {
141+ var arg0 string
142+ if args[0] != nil {
143+ arg0 = args[0].(string)
144+ }
145+ var arg1 int
146+ if args[1] != nil {
147+ arg1 = args[1].(int)
148+ }
149+ run(
150+ arg0,
151+ arg1,
152+ )
153+ })
154+ return _c
155+}
156+
157+func (_c *MockSharedMemAPI_NewSharedMem_Call) Return(fd int, data []byte, err error) *MockSharedMemAPI_NewSharedMem_Call {
158+ _c.Call.Return(fd, data, err)
159+ return _c
160+}
161+
162+func (_c *MockSharedMemAPI_NewSharedMem_Call) RunAndReturn(run func(name string, size int) (int, []byte, error)) *MockSharedMemAPI_NewSharedMem_Call {
163+ _c.Call.Return(run)
164+ return _c
165+}
M internal/ports/out/mocks/mock_StreamOps.go +20 -11
@@ -17,10 +17,19 @@ func NewMockStreamOps(t interface {
1717 mock.TestingT
1818 Cleanup(func())
1919 }) *MockStreamOps {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
2024 mock := &MockStreamOps{}
2125 mock.Mock.Test(t)
2226
23- t.Cleanup(func() { mock.AssertExpectations(t) })
27+ t.Cleanup(func() {
28+ if helper, ok := t.(interface{ Helper() }); ok {
29+ helper.Helper()
30+ }
31+ mock.AssertExpectations(t)
32+ })
2433
2534 return mock
2635 }
@@ -63,7 +72,7 @@ type MockStreamOps_ConnectPlaybackStream_Call struct {
6372 // ConnectPlaybackStream is a helper method to define mock.On call
6473 // - streamPtr unsafe.Pointer
6574 // - format out.PlaybackFormat
66-func (_e *MockStreamOps_Expecter) ConnectPlaybackStream(streamPtr interface{}, format interface{}) *MockStreamOps_ConnectPlaybackStream_Call {
75+func (_e *MockStreamOps_Expecter) ConnectPlaybackStream(streamPtr any, format any) *MockStreamOps_ConnectPlaybackStream_Call {
6776 return &MockStreamOps_ConnectPlaybackStream_Call{Call: _e.mock.On("ConnectPlaybackStream", streamPtr, format)}
6877 }
6978
@@ -183,7 +192,7 @@ type MockStreamOps_CreatePlaybackStream_Call struct {
183192 // - loopPtr unsafe.Pointer
184193 // - name string
185194 // - onProcess func()
186-func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr interface{}, name interface{}, onProcess interface{}) *MockStreamOps_CreatePlaybackStream_Call {
195+func (_e *MockStreamOps_Expecter) CreatePlaybackStream(loopPtr any, name any, onProcess any) *MockStreamOps_CreatePlaybackStream_Call {
187196 return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, onProcess)}
188197 }
189198
@@ -244,7 +253,7 @@ type MockStreamOps_DequeueBuffer_Call struct {
244253
245254 // DequeueBuffer is a helper method to define mock.On call
246255 // - streamPtr unsafe.Pointer
247-func (_e *MockStreamOps_Expecter) DequeueBuffer(streamPtr interface{}) *MockStreamOps_DequeueBuffer_Call {
256+func (_e *MockStreamOps_Expecter) DequeueBuffer(streamPtr any) *MockStreamOps_DequeueBuffer_Call {
248257 return &MockStreamOps_DequeueBuffer_Call{Call: _e.mock.On("DequeueBuffer", streamPtr)}
249258 }
250259
@@ -284,7 +293,7 @@ type MockStreamOps_DestroyMainLoop_Call struct {
284293
285294 // DestroyMainLoop is a helper method to define mock.On call
286295 // - loopPtr unsafe.Pointer
287-func (_e *MockStreamOps_Expecter) DestroyMainLoop(loopPtr interface{}) *MockStreamOps_DestroyMainLoop_Call {
296+func (_e *MockStreamOps_Expecter) DestroyMainLoop(loopPtr any) *MockStreamOps_DestroyMainLoop_Call {
288297 return &MockStreamOps_DestroyMainLoop_Call{Call: _e.mock.On("DestroyMainLoop", loopPtr)}
289298 }
290299
@@ -324,7 +333,7 @@ type MockStreamOps_DestroyStream_Call struct {
324333
325334 // DestroyStream is a helper method to define mock.On call
326335 // - streamPtr unsafe.Pointer
327-func (_e *MockStreamOps_Expecter) DestroyStream(streamPtr interface{}) *MockStreamOps_DestroyStream_Call {
336+func (_e *MockStreamOps_Expecter) DestroyStream(streamPtr any) *MockStreamOps_DestroyStream_Call {
328337 return &MockStreamOps_DestroyStream_Call{Call: _e.mock.On("DestroyStream", streamPtr)}
329338 }
330339
@@ -375,7 +384,7 @@ type MockStreamOps_DisconnectStream_Call struct {
375384
376385 // DisconnectStream is a helper method to define mock.On call
377386 // - streamPtr unsafe.Pointer
378-func (_e *MockStreamOps_Expecter) DisconnectStream(streamPtr interface{}) *MockStreamOps_DisconnectStream_Call {
387+func (_e *MockStreamOps_Expecter) DisconnectStream(streamPtr any) *MockStreamOps_DisconnectStream_Call {
379388 return &MockStreamOps_DisconnectStream_Call{Call: _e.mock.On("DisconnectStream", streamPtr)}
380389 }
381390
@@ -427,7 +436,7 @@ type MockStreamOps_QueueBuffer_Call struct {
427436 // QueueBuffer is a helper method to define mock.On call
428437 // - streamPtr unsafe.Pointer
429438 // - bufPtr unsafe.Pointer
430-func (_e *MockStreamOps_Expecter) QueueBuffer(streamPtr interface{}, bufPtr interface{}) *MockStreamOps_QueueBuffer_Call {
439+func (_e *MockStreamOps_Expecter) QueueBuffer(streamPtr any, bufPtr any) *MockStreamOps_QueueBuffer_Call {
431440 return &MockStreamOps_QueueBuffer_Call{Call: _e.mock.On("QueueBuffer", streamPtr, bufPtr)}
432441 }
433442
@@ -472,7 +481,7 @@ type MockStreamOps_QuitMainLoop_Call struct {
472481
473482 // QuitMainLoop is a helper method to define mock.On call
474483 // - loopPtr unsafe.Pointer
475-func (_e *MockStreamOps_Expecter) QuitMainLoop(loopPtr interface{}) *MockStreamOps_QuitMainLoop_Call {
484+func (_e *MockStreamOps_Expecter) QuitMainLoop(loopPtr any) *MockStreamOps_QuitMainLoop_Call {
476485 return &MockStreamOps_QuitMainLoop_Call{Call: _e.mock.On("QuitMainLoop", loopPtr)}
477486 }
478487
@@ -523,7 +532,7 @@ type MockStreamOps_RunMainLoop_Call struct {
523532
524533 // RunMainLoop is a helper method to define mock.On call
525534 // - loopPtr unsafe.Pointer
526-func (_e *MockStreamOps_Expecter) RunMainLoop(loopPtr interface{}) *MockStreamOps_RunMainLoop_Call {
535+func (_e *MockStreamOps_Expecter) RunMainLoop(loopPtr any) *MockStreamOps_RunMainLoop_Call {
527536 return &MockStreamOps_RunMainLoop_Call{Call: _e.mock.On("RunMainLoop", loopPtr)}
528537 }
529538
@@ -575,7 +584,7 @@ type MockStreamOps_SetStreamActive_Call struct {
575584 // SetStreamActive is a helper method to define mock.On call
576585 // - streamPtr unsafe.Pointer
577586 // - active bool
578-func (_e *MockStreamOps_Expecter) SetStreamActive(streamPtr interface{}, active interface{}) *MockStreamOps_SetStreamActive_Call {
587+func (_e *MockStreamOps_Expecter) SetStreamActive(streamPtr any, active any) *MockStreamOps_SetStreamActive_Call {
579588 return &MockStreamOps_SetStreamActive_Call{Call: _e.mock.On("SetStreamActive", streamPtr, active)}
580589 }
581590
A internal/ports/out/mocks/mock_VideoAPI.go +930 -0
@@ -0,0 +1,930 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "unsafe"
9+
10+ mock "github.com/stretchr/testify/mock"
11+)
12+
13+// NewMockVideoAPI creates a new instance of MockVideoAPI. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// The first argument is typically a *testing.T value.
15+func NewMockVideoAPI(t interface {
16+ mock.TestingT
17+ Cleanup(func())
18+}) *MockVideoAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
23+ mock := &MockVideoAPI{}
24+ mock.Mock.Test(t)
25+
26+ t.Cleanup(func() {
27+ if helper, ok := t.(interface{ Helper() }); ok {
28+ helper.Helper()
29+ }
30+ mock.AssertExpectations(t)
31+ })
32+
33+ return mock
34+}
35+
36+// MockVideoAPI is an autogenerated mock type for the VideoAPI type
37+type MockVideoAPI struct {
38+ mock.Mock
39+}
40+
41+type MockVideoAPI_Expecter struct {
42+ mock *mock.Mock
43+}
44+
45+func (_m *MockVideoAPI) EXPECT() *MockVideoAPI_Expecter {
46+ return &MockVideoAPI_Expecter{mock: &_m.Mock}
47+}
48+
49+// PWContextConnect provides a mock function for the type MockVideoAPI
50+func (_mock *MockVideoAPI) PWContextConnect(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
51+ ret := _mock.Called(context, props, user_data_size)
52+
53+ if len(ret) == 0 {
54+ panic("no return value specified for PWContextConnect")
55+ }
56+
57+ var r0 unsafe.Pointer
58+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
59+ r0 = returnFunc(context, props, user_data_size)
60+ } else {
61+ r0 = ret.Get(0).(unsafe.Pointer)
62+ }
63+ return r0
64+}
65+
66+// MockVideoAPI_PWContextConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextConnect'
67+type MockVideoAPI_PWContextConnect_Call struct {
68+ *mock.Call
69+}
70+
71+// PWContextConnect is a helper method to define mock.On call
72+// - context unsafe.Pointer
73+// - props unsafe.Pointer
74+// - user_data_size uintptr
75+func (_e *MockVideoAPI_Expecter) PWContextConnect(context any, props any, user_data_size any) *MockVideoAPI_PWContextConnect_Call {
76+ return &MockVideoAPI_PWContextConnect_Call{Call: _e.mock.On("PWContextConnect", context, props, user_data_size)}
77+}
78+
79+func (_c *MockVideoAPI_PWContextConnect_Call) Run(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockVideoAPI_PWContextConnect_Call {
80+ _c.Call.Run(func(args mock.Arguments) {
81+ var arg0 unsafe.Pointer
82+ if args[0] != nil {
83+ arg0 = args[0].(unsafe.Pointer)
84+ }
85+ var arg1 unsafe.Pointer
86+ if args[1] != nil {
87+ arg1 = args[1].(unsafe.Pointer)
88+ }
89+ var arg2 uintptr
90+ if args[2] != nil {
91+ arg2 = args[2].(uintptr)
92+ }
93+ run(
94+ arg0,
95+ arg1,
96+ arg2,
97+ )
98+ })
99+ return _c
100+}
101+
102+func (_c *MockVideoAPI_PWContextConnect_Call) Return(v unsafe.Pointer) *MockVideoAPI_PWContextConnect_Call {
103+ _c.Call.Return(v)
104+ return _c
105+}
106+
107+func (_c *MockVideoAPI_PWContextConnect_Call) RunAndReturn(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockVideoAPI_PWContextConnect_Call {
108+ _c.Call.Return(run)
109+ return _c
110+}
111+
112+// PWContextDestroy provides a mock function for the type MockVideoAPI
113+func (_mock *MockVideoAPI) PWContextDestroy(context unsafe.Pointer) {
114+ _mock.Called(context)
115+ return
116+}
117+
118+// MockVideoAPI_PWContextDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextDestroy'
119+type MockVideoAPI_PWContextDestroy_Call struct {
120+ *mock.Call
121+}
122+
123+// PWContextDestroy is a helper method to define mock.On call
124+// - context unsafe.Pointer
125+func (_e *MockVideoAPI_Expecter) PWContextDestroy(context any) *MockVideoAPI_PWContextDestroy_Call {
126+ return &MockVideoAPI_PWContextDestroy_Call{Call: _e.mock.On("PWContextDestroy", context)}
127+}
128+
129+func (_c *MockVideoAPI_PWContextDestroy_Call) Run(run func(context unsafe.Pointer)) *MockVideoAPI_PWContextDestroy_Call {
130+ _c.Call.Run(func(args mock.Arguments) {
131+ var arg0 unsafe.Pointer
132+ if args[0] != nil {
133+ arg0 = args[0].(unsafe.Pointer)
134+ }
135+ run(
136+ arg0,
137+ )
138+ })
139+ return _c
140+}
141+
142+func (_c *MockVideoAPI_PWContextDestroy_Call) Return() *MockVideoAPI_PWContextDestroy_Call {
143+ _c.Call.Return()
144+ return _c
145+}
146+
147+func (_c *MockVideoAPI_PWContextDestroy_Call) RunAndReturn(run func(context unsafe.Pointer)) *MockVideoAPI_PWContextDestroy_Call {
148+ _c.Run(run)
149+ return _c
150+}
151+
152+// PWContextNew provides a mock function for the type MockVideoAPI
153+func (_mock *MockVideoAPI) PWContextNew(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
154+ ret := _mock.Called(loop, props, user_data_size)
155+
156+ if len(ret) == 0 {
157+ panic("no return value specified for PWContextNew")
158+ }
159+
160+ var r0 unsafe.Pointer
161+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
162+ r0 = returnFunc(loop, props, user_data_size)
163+ } else {
164+ r0 = ret.Get(0).(unsafe.Pointer)
165+ }
166+ return r0
167+}
168+
169+// MockVideoAPI_PWContextNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextNew'
170+type MockVideoAPI_PWContextNew_Call struct {
171+ *mock.Call
172+}
173+
174+// PWContextNew is a helper method to define mock.On call
175+// - loop unsafe.Pointer
176+// - props unsafe.Pointer
177+// - user_data_size uintptr
178+func (_e *MockVideoAPI_Expecter) PWContextNew(loop any, props any, user_data_size any) *MockVideoAPI_PWContextNew_Call {
179+ return &MockVideoAPI_PWContextNew_Call{Call: _e.mock.On("PWContextNew", loop, props, user_data_size)}
180+}
181+
182+func (_c *MockVideoAPI_PWContextNew_Call) Run(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockVideoAPI_PWContextNew_Call {
183+ _c.Call.Run(func(args mock.Arguments) {
184+ var arg0 unsafe.Pointer
185+ if args[0] != nil {
186+ arg0 = args[0].(unsafe.Pointer)
187+ }
188+ var arg1 unsafe.Pointer
189+ if args[1] != nil {
190+ arg1 = args[1].(unsafe.Pointer)
191+ }
192+ var arg2 uintptr
193+ if args[2] != nil {
194+ arg2 = args[2].(uintptr)
195+ }
196+ run(
197+ arg0,
198+ arg1,
199+ arg2,
200+ )
201+ })
202+ return _c
203+}
204+
205+func (_c *MockVideoAPI_PWContextNew_Call) Return(v unsafe.Pointer) *MockVideoAPI_PWContextNew_Call {
206+ _c.Call.Return(v)
207+ return _c
208+}
209+
210+func (_c *MockVideoAPI_PWContextNew_Call) RunAndReturn(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockVideoAPI_PWContextNew_Call {
211+ _c.Call.Return(run)
212+ return _c
213+}
214+
215+// PWCoreDisconnect provides a mock function for the type MockVideoAPI
216+func (_mock *MockVideoAPI) PWCoreDisconnect(core unsafe.Pointer) int32 {
217+ ret := _mock.Called(core)
218+
219+ if len(ret) == 0 {
220+ panic("no return value specified for PWCoreDisconnect")
221+ }
222+
223+ var r0 int32
224+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
225+ r0 = returnFunc(core)
226+ } else {
227+ r0 = ret.Get(0).(int32)
228+ }
229+ return r0
230+}
231+
232+// MockVideoAPI_PWCoreDisconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWCoreDisconnect'
233+type MockVideoAPI_PWCoreDisconnect_Call struct {
234+ *mock.Call
235+}
236+
237+// PWCoreDisconnect is a helper method to define mock.On call
238+// - core unsafe.Pointer
239+func (_e *MockVideoAPI_Expecter) PWCoreDisconnect(core any) *MockVideoAPI_PWCoreDisconnect_Call {
240+ return &MockVideoAPI_PWCoreDisconnect_Call{Call: _e.mock.On("PWCoreDisconnect", core)}
241+}
242+
243+func (_c *MockVideoAPI_PWCoreDisconnect_Call) Run(run func(core unsafe.Pointer)) *MockVideoAPI_PWCoreDisconnect_Call {
244+ _c.Call.Run(func(args mock.Arguments) {
245+ var arg0 unsafe.Pointer
246+ if args[0] != nil {
247+ arg0 = args[0].(unsafe.Pointer)
248+ }
249+ run(
250+ arg0,
251+ )
252+ })
253+ return _c
254+}
255+
256+func (_c *MockVideoAPI_PWCoreDisconnect_Call) Return(n int32) *MockVideoAPI_PWCoreDisconnect_Call {
257+ _c.Call.Return(n)
258+ return _c
259+}
260+
261+func (_c *MockVideoAPI_PWCoreDisconnect_Call) RunAndReturn(run func(core unsafe.Pointer) int32) *MockVideoAPI_PWCoreDisconnect_Call {
262+ _c.Call.Return(run)
263+ return _c
264+}
265+
266+// PWLoopDestroy provides a mock function for the type MockVideoAPI
267+func (_mock *MockVideoAPI) PWLoopDestroy(loop unsafe.Pointer) {
268+ _mock.Called(loop)
269+ return
270+}
271+
272+// MockVideoAPI_PWLoopDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopDestroy'
273+type MockVideoAPI_PWLoopDestroy_Call struct {
274+ *mock.Call
275+}
276+
277+// PWLoopDestroy is a helper method to define mock.On call
278+// - loop unsafe.Pointer
279+func (_e *MockVideoAPI_Expecter) PWLoopDestroy(loop any) *MockVideoAPI_PWLoopDestroy_Call {
280+ return &MockVideoAPI_PWLoopDestroy_Call{Call: _e.mock.On("PWLoopDestroy", loop)}
281+}
282+
283+func (_c *MockVideoAPI_PWLoopDestroy_Call) Run(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopDestroy_Call {
284+ _c.Call.Run(func(args mock.Arguments) {
285+ var arg0 unsafe.Pointer
286+ if args[0] != nil {
287+ arg0 = args[0].(unsafe.Pointer)
288+ }
289+ run(
290+ arg0,
291+ )
292+ })
293+ return _c
294+}
295+
296+func (_c *MockVideoAPI_PWLoopDestroy_Call) Return() *MockVideoAPI_PWLoopDestroy_Call {
297+ _c.Call.Return()
298+ return _c
299+}
300+
301+func (_c *MockVideoAPI_PWLoopDestroy_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopDestroy_Call {
302+ _c.Run(run)
303+ return _c
304+}
305+
306+// PWLoopEnter provides a mock function for the type MockVideoAPI
307+func (_mock *MockVideoAPI) PWLoopEnter(loop unsafe.Pointer) {
308+ _mock.Called(loop)
309+ return
310+}
311+
312+// MockVideoAPI_PWLoopEnter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopEnter'
313+type MockVideoAPI_PWLoopEnter_Call struct {
314+ *mock.Call
315+}
316+
317+// PWLoopEnter is a helper method to define mock.On call
318+// - loop unsafe.Pointer
319+func (_e *MockVideoAPI_Expecter) PWLoopEnter(loop any) *MockVideoAPI_PWLoopEnter_Call {
320+ return &MockVideoAPI_PWLoopEnter_Call{Call: _e.mock.On("PWLoopEnter", loop)}
321+}
322+
323+func (_c *MockVideoAPI_PWLoopEnter_Call) Run(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopEnter_Call {
324+ _c.Call.Run(func(args mock.Arguments) {
325+ var arg0 unsafe.Pointer
326+ if args[0] != nil {
327+ arg0 = args[0].(unsafe.Pointer)
328+ }
329+ run(
330+ arg0,
331+ )
332+ })
333+ return _c
334+}
335+
336+func (_c *MockVideoAPI_PWLoopEnter_Call) Return() *MockVideoAPI_PWLoopEnter_Call {
337+ _c.Call.Return()
338+ return _c
339+}
340+
341+func (_c *MockVideoAPI_PWLoopEnter_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopEnter_Call {
342+ _c.Run(run)
343+ return _c
344+}
345+
346+// PWLoopGetFd provides a mock function for the type MockVideoAPI
347+func (_mock *MockVideoAPI) PWLoopGetFd(loop unsafe.Pointer) int32 {
348+ ret := _mock.Called(loop)
349+
350+ if len(ret) == 0 {
351+ panic("no return value specified for PWLoopGetFd")
352+ }
353+
354+ var r0 int32
355+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
356+ r0 = returnFunc(loop)
357+ } else {
358+ r0 = ret.Get(0).(int32)
359+ }
360+ return r0
361+}
362+
363+// MockVideoAPI_PWLoopGetFd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopGetFd'
364+type MockVideoAPI_PWLoopGetFd_Call struct {
365+ *mock.Call
366+}
367+
368+// PWLoopGetFd is a helper method to define mock.On call
369+// - loop unsafe.Pointer
370+func (_e *MockVideoAPI_Expecter) PWLoopGetFd(loop any) *MockVideoAPI_PWLoopGetFd_Call {
371+ return &MockVideoAPI_PWLoopGetFd_Call{Call: _e.mock.On("PWLoopGetFd", loop)}
372+}
373+
374+func (_c *MockVideoAPI_PWLoopGetFd_Call) Run(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopGetFd_Call {
375+ _c.Call.Run(func(args mock.Arguments) {
376+ var arg0 unsafe.Pointer
377+ if args[0] != nil {
378+ arg0 = args[0].(unsafe.Pointer)
379+ }
380+ run(
381+ arg0,
382+ )
383+ })
384+ return _c
385+}
386+
387+func (_c *MockVideoAPI_PWLoopGetFd_Call) Return(n int32) *MockVideoAPI_PWLoopGetFd_Call {
388+ _c.Call.Return(n)
389+ return _c
390+}
391+
392+func (_c *MockVideoAPI_PWLoopGetFd_Call) RunAndReturn(run func(loop unsafe.Pointer) int32) *MockVideoAPI_PWLoopGetFd_Call {
393+ _c.Call.Return(run)
394+ return _c
395+}
396+
397+// PWLoopIterate provides a mock function for the type MockVideoAPI
398+func (_mock *MockVideoAPI) PWLoopIterate(loop unsafe.Pointer, timeout int32) int32 {
399+ ret := _mock.Called(loop, timeout)
400+
401+ if len(ret) == 0 {
402+ panic("no return value specified for PWLoopIterate")
403+ }
404+
405+ var r0 int32
406+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, int32) int32); ok {
407+ r0 = returnFunc(loop, timeout)
408+ } else {
409+ r0 = ret.Get(0).(int32)
410+ }
411+ return r0
412+}
413+
414+// MockVideoAPI_PWLoopIterate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopIterate'
415+type MockVideoAPI_PWLoopIterate_Call struct {
416+ *mock.Call
417+}
418+
419+// PWLoopIterate is a helper method to define mock.On call
420+// - loop unsafe.Pointer
421+// - timeout int32
422+func (_e *MockVideoAPI_Expecter) PWLoopIterate(loop any, timeout any) *MockVideoAPI_PWLoopIterate_Call {
423+ return &MockVideoAPI_PWLoopIterate_Call{Call: _e.mock.On("PWLoopIterate", loop, timeout)}
424+}
425+
426+func (_c *MockVideoAPI_PWLoopIterate_Call) Run(run func(loop unsafe.Pointer, timeout int32)) *MockVideoAPI_PWLoopIterate_Call {
427+ _c.Call.Run(func(args mock.Arguments) {
428+ var arg0 unsafe.Pointer
429+ if args[0] != nil {
430+ arg0 = args[0].(unsafe.Pointer)
431+ }
432+ var arg1 int32
433+ if args[1] != nil {
434+ arg1 = args[1].(int32)
435+ }
436+ run(
437+ arg0,
438+ arg1,
439+ )
440+ })
441+ return _c
442+}
443+
444+func (_c *MockVideoAPI_PWLoopIterate_Call) Return(n int32) *MockVideoAPI_PWLoopIterate_Call {
445+ _c.Call.Return(n)
446+ return _c
447+}
448+
449+func (_c *MockVideoAPI_PWLoopIterate_Call) RunAndReturn(run func(loop unsafe.Pointer, timeout int32) int32) *MockVideoAPI_PWLoopIterate_Call {
450+ _c.Call.Return(run)
451+ return _c
452+}
453+
454+// PWLoopLeave provides a mock function for the type MockVideoAPI
455+func (_mock *MockVideoAPI) PWLoopLeave(loop unsafe.Pointer) {
456+ _mock.Called(loop)
457+ return
458+}
459+
460+// MockVideoAPI_PWLoopLeave_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopLeave'
461+type MockVideoAPI_PWLoopLeave_Call struct {
462+ *mock.Call
463+}
464+
465+// PWLoopLeave is a helper method to define mock.On call
466+// - loop unsafe.Pointer
467+func (_e *MockVideoAPI_Expecter) PWLoopLeave(loop any) *MockVideoAPI_PWLoopLeave_Call {
468+ return &MockVideoAPI_PWLoopLeave_Call{Call: _e.mock.On("PWLoopLeave", loop)}
469+}
470+
471+func (_c *MockVideoAPI_PWLoopLeave_Call) Run(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopLeave_Call {
472+ _c.Call.Run(func(args mock.Arguments) {
473+ var arg0 unsafe.Pointer
474+ if args[0] != nil {
475+ arg0 = args[0].(unsafe.Pointer)
476+ }
477+ run(
478+ arg0,
479+ )
480+ })
481+ return _c
482+}
483+
484+func (_c *MockVideoAPI_PWLoopLeave_Call) Return() *MockVideoAPI_PWLoopLeave_Call {
485+ _c.Call.Return()
486+ return _c
487+}
488+
489+func (_c *MockVideoAPI_PWLoopLeave_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoAPI_PWLoopLeave_Call {
490+ _c.Run(run)
491+ return _c
492+}
493+
494+// PWLoopNew provides a mock function for the type MockVideoAPI
495+func (_mock *MockVideoAPI) PWLoopNew(props unsafe.Pointer) unsafe.Pointer {
496+ ret := _mock.Called(props)
497+
498+ if len(ret) == 0 {
499+ panic("no return value specified for PWLoopNew")
500+ }
501+
502+ var r0 unsafe.Pointer
503+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) unsafe.Pointer); ok {
504+ r0 = returnFunc(props)
505+ } else {
506+ r0 = ret.Get(0).(unsafe.Pointer)
507+ }
508+ return r0
509+}
510+
511+// MockVideoAPI_PWLoopNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopNew'
512+type MockVideoAPI_PWLoopNew_Call struct {
513+ *mock.Call
514+}
515+
516+// PWLoopNew is a helper method to define mock.On call
517+// - props unsafe.Pointer
518+func (_e *MockVideoAPI_Expecter) PWLoopNew(props any) *MockVideoAPI_PWLoopNew_Call {
519+ return &MockVideoAPI_PWLoopNew_Call{Call: _e.mock.On("PWLoopNew", props)}
520+}
521+
522+func (_c *MockVideoAPI_PWLoopNew_Call) Run(run func(props unsafe.Pointer)) *MockVideoAPI_PWLoopNew_Call {
523+ _c.Call.Run(func(args mock.Arguments) {
524+ var arg0 unsafe.Pointer
525+ if args[0] != nil {
526+ arg0 = args[0].(unsafe.Pointer)
527+ }
528+ run(
529+ arg0,
530+ )
531+ })
532+ return _c
533+}
534+
535+func (_c *MockVideoAPI_PWLoopNew_Call) Return(v unsafe.Pointer) *MockVideoAPI_PWLoopNew_Call {
536+ _c.Call.Return(v)
537+ return _c
538+}
539+
540+func (_c *MockVideoAPI_PWLoopNew_Call) RunAndReturn(run func(props unsafe.Pointer) unsafe.Pointer) *MockVideoAPI_PWLoopNew_Call {
541+ _c.Call.Return(run)
542+ return _c
543+}
544+
545+// PWPropertiesNewString provides a mock function for the type MockVideoAPI
546+func (_mock *MockVideoAPI) PWPropertiesNewString(args *byte) unsafe.Pointer {
547+ ret := _mock.Called(args)
548+
549+ if len(ret) == 0 {
550+ panic("no return value specified for PWPropertiesNewString")
551+ }
552+
553+ var r0 unsafe.Pointer
554+ if returnFunc, ok := ret.Get(0).(func(*byte) unsafe.Pointer); ok {
555+ r0 = returnFunc(args)
556+ } else {
557+ r0 = ret.Get(0).(unsafe.Pointer)
558+ }
559+ return r0
560+}
561+
562+// MockVideoAPI_PWPropertiesNewString_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWPropertiesNewString'
563+type MockVideoAPI_PWPropertiesNewString_Call struct {
564+ *mock.Call
565+}
566+
567+// PWPropertiesNewString is a helper method to define mock.On call
568+// - args *byte
569+func (_e *MockVideoAPI_Expecter) PWPropertiesNewString(args any) *MockVideoAPI_PWPropertiesNewString_Call {
570+ return &MockVideoAPI_PWPropertiesNewString_Call{Call: _e.mock.On("PWPropertiesNewString", args)}
571+}
572+
573+func (_c *MockVideoAPI_PWPropertiesNewString_Call) Run(run func(args *byte)) *MockVideoAPI_PWPropertiesNewString_Call {
574+ _c.Call.Run(func(args mock.Arguments) {
575+ var arg0 *byte
576+ if args[0] != nil {
577+ arg0 = args[0].(*byte)
578+ }
579+ run(
580+ arg0,
581+ )
582+ })
583+ return _c
584+}
585+
586+func (_c *MockVideoAPI_PWPropertiesNewString_Call) Return(v unsafe.Pointer) *MockVideoAPI_PWPropertiesNewString_Call {
587+ _c.Call.Return(v)
588+ return _c
589+}
590+
591+func (_c *MockVideoAPI_PWPropertiesNewString_Call) RunAndReturn(run func(args *byte) unsafe.Pointer) *MockVideoAPI_PWPropertiesNewString_Call {
592+ _c.Call.Return(run)
593+ return _c
594+}
595+
596+// PWStreamGetNodeId provides a mock function for the type MockVideoAPI
597+func (_mock *MockVideoAPI) PWStreamGetNodeId(stream unsafe.Pointer) uint32 {
598+ ret := _mock.Called(stream)
599+
600+ if len(ret) == 0 {
601+ panic("no return value specified for PWStreamGetNodeId")
602+ }
603+
604+ var r0 uint32
605+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) uint32); ok {
606+ r0 = returnFunc(stream)
607+ } else {
608+ r0 = ret.Get(0).(uint32)
609+ }
610+ return r0
611+}
612+
613+// MockVideoAPI_PWStreamGetNodeId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamGetNodeId'
614+type MockVideoAPI_PWStreamGetNodeId_Call struct {
615+ *mock.Call
616+}
617+
618+// PWStreamGetNodeId is a helper method to define mock.On call
619+// - stream unsafe.Pointer
620+func (_e *MockVideoAPI_Expecter) PWStreamGetNodeId(stream any) *MockVideoAPI_PWStreamGetNodeId_Call {
621+ return &MockVideoAPI_PWStreamGetNodeId_Call{Call: _e.mock.On("PWStreamGetNodeId", stream)}
622+}
623+
624+func (_c *MockVideoAPI_PWStreamGetNodeId_Call) Run(run func(stream unsafe.Pointer)) *MockVideoAPI_PWStreamGetNodeId_Call {
625+ _c.Call.Run(func(args mock.Arguments) {
626+ var arg0 unsafe.Pointer
627+ if args[0] != nil {
628+ arg0 = args[0].(unsafe.Pointer)
629+ }
630+ run(
631+ arg0,
632+ )
633+ })
634+ return _c
635+}
636+
637+func (_c *MockVideoAPI_PWStreamGetNodeId_Call) Return(v uint32) *MockVideoAPI_PWStreamGetNodeId_Call {
638+ _c.Call.Return(v)
639+ return _c
640+}
641+
642+func (_c *MockVideoAPI_PWStreamGetNodeId_Call) RunAndReturn(run func(stream unsafe.Pointer) uint32) *MockVideoAPI_PWStreamGetNodeId_Call {
643+ _c.Call.Return(run)
644+ return _c
645+}
646+
647+// PWStreamIsDriving provides a mock function for the type MockVideoAPI
648+func (_mock *MockVideoAPI) PWStreamIsDriving(stream unsafe.Pointer) bool {
649+ ret := _mock.Called(stream)
650+
651+ if len(ret) == 0 {
652+ panic("no return value specified for PWStreamIsDriving")
653+ }
654+
655+ var r0 bool
656+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) bool); ok {
657+ r0 = returnFunc(stream)
658+ } else {
659+ r0 = ret.Get(0).(bool)
660+ }
661+ return r0
662+}
663+
664+// MockVideoAPI_PWStreamIsDriving_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamIsDriving'
665+type MockVideoAPI_PWStreamIsDriving_Call struct {
666+ *mock.Call
667+}
668+
669+// PWStreamIsDriving is a helper method to define mock.On call
670+// - stream unsafe.Pointer
671+func (_e *MockVideoAPI_Expecter) PWStreamIsDriving(stream any) *MockVideoAPI_PWStreamIsDriving_Call {
672+ return &MockVideoAPI_PWStreamIsDriving_Call{Call: _e.mock.On("PWStreamIsDriving", stream)}
673+}
674+
675+func (_c *MockVideoAPI_PWStreamIsDriving_Call) Run(run func(stream unsafe.Pointer)) *MockVideoAPI_PWStreamIsDriving_Call {
676+ _c.Call.Run(func(args mock.Arguments) {
677+ var arg0 unsafe.Pointer
678+ if args[0] != nil {
679+ arg0 = args[0].(unsafe.Pointer)
680+ }
681+ run(
682+ arg0,
683+ )
684+ })
685+ return _c
686+}
687+
688+func (_c *MockVideoAPI_PWStreamIsDriving_Call) Return(b bool) *MockVideoAPI_PWStreamIsDriving_Call {
689+ _c.Call.Return(b)
690+ return _c
691+}
692+
693+func (_c *MockVideoAPI_PWStreamIsDriving_Call) RunAndReturn(run func(stream unsafe.Pointer) bool) *MockVideoAPI_PWStreamIsDriving_Call {
694+ _c.Call.Return(run)
695+ return _c
696+}
697+
698+// PWStreamNew provides a mock function for the type MockVideoAPI
699+func (_mock *MockVideoAPI) PWStreamNew(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer {
700+ ret := _mock.Called(core, name, props)
701+
702+ if len(ret) == 0 {
703+ panic("no return value specified for PWStreamNew")
704+ }
705+
706+ var r0 unsafe.Pointer
707+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, *byte, unsafe.Pointer) unsafe.Pointer); ok {
708+ r0 = returnFunc(core, name, props)
709+ } else {
710+ r0 = ret.Get(0).(unsafe.Pointer)
711+ }
712+ return r0
713+}
714+
715+// MockVideoAPI_PWStreamNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamNew'
716+type MockVideoAPI_PWStreamNew_Call struct {
717+ *mock.Call
718+}
719+
720+// PWStreamNew is a helper method to define mock.On call
721+// - core unsafe.Pointer
722+// - name *byte
723+// - props unsafe.Pointer
724+func (_e *MockVideoAPI_Expecter) PWStreamNew(core any, name any, props any) *MockVideoAPI_PWStreamNew_Call {
725+ return &MockVideoAPI_PWStreamNew_Call{Call: _e.mock.On("PWStreamNew", core, name, props)}
726+}
727+
728+func (_c *MockVideoAPI_PWStreamNew_Call) Run(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer)) *MockVideoAPI_PWStreamNew_Call {
729+ _c.Call.Run(func(args mock.Arguments) {
730+ var arg0 unsafe.Pointer
731+ if args[0] != nil {
732+ arg0 = args[0].(unsafe.Pointer)
733+ }
734+ var arg1 *byte
735+ if args[1] != nil {
736+ arg1 = args[1].(*byte)
737+ }
738+ var arg2 unsafe.Pointer
739+ if args[2] != nil {
740+ arg2 = args[2].(unsafe.Pointer)
741+ }
742+ run(
743+ arg0,
744+ arg1,
745+ arg2,
746+ )
747+ })
748+ return _c
749+}
750+
751+func (_c *MockVideoAPI_PWStreamNew_Call) Return(v unsafe.Pointer) *MockVideoAPI_PWStreamNew_Call {
752+ _c.Call.Return(v)
753+ return _c
754+}
755+
756+func (_c *MockVideoAPI_PWStreamNew_Call) RunAndReturn(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer) *MockVideoAPI_PWStreamNew_Call {
757+ _c.Call.Return(run)
758+ return _c
759+}
760+
761+// PWStreamReturnBuffer provides a mock function for the type MockVideoAPI
762+func (_mock *MockVideoAPI) PWStreamReturnBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
763+ ret := _mock.Called(stream, buffer)
764+
765+ if len(ret) == 0 {
766+ panic("no return value specified for PWStreamReturnBuffer")
767+ }
768+
769+ var r0 int32
770+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer) int32); ok {
771+ r0 = returnFunc(stream, buffer)
772+ } else {
773+ r0 = ret.Get(0).(int32)
774+ }
775+ return r0
776+}
777+
778+// MockVideoAPI_PWStreamReturnBuffer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamReturnBuffer'
779+type MockVideoAPI_PWStreamReturnBuffer_Call struct {
780+ *mock.Call
781+}
782+
783+// PWStreamReturnBuffer is a helper method to define mock.On call
784+// - stream unsafe.Pointer
785+// - buffer unsafe.Pointer
786+func (_e *MockVideoAPI_Expecter) PWStreamReturnBuffer(stream any, buffer any) *MockVideoAPI_PWStreamReturnBuffer_Call {
787+ return &MockVideoAPI_PWStreamReturnBuffer_Call{Call: _e.mock.On("PWStreamReturnBuffer", stream, buffer)}
788+}
789+
790+func (_c *MockVideoAPI_PWStreamReturnBuffer_Call) Run(run func(stream unsafe.Pointer, buffer unsafe.Pointer)) *MockVideoAPI_PWStreamReturnBuffer_Call {
791+ _c.Call.Run(func(args mock.Arguments) {
792+ var arg0 unsafe.Pointer
793+ if args[0] != nil {
794+ arg0 = args[0].(unsafe.Pointer)
795+ }
796+ var arg1 unsafe.Pointer
797+ if args[1] != nil {
798+ arg1 = args[1].(unsafe.Pointer)
799+ }
800+ run(
801+ arg0,
802+ arg1,
803+ )
804+ })
805+ return _c
806+}
807+
808+func (_c *MockVideoAPI_PWStreamReturnBuffer_Call) Return(n int32) *MockVideoAPI_PWStreamReturnBuffer_Call {
809+ _c.Call.Return(n)
810+ return _c
811+}
812+
813+func (_c *MockVideoAPI_PWStreamReturnBuffer_Call) RunAndReturn(run func(stream unsafe.Pointer, buffer unsafe.Pointer) int32) *MockVideoAPI_PWStreamReturnBuffer_Call {
814+ _c.Call.Return(run)
815+ return _c
816+}
817+
818+// PWStreamTriggerProcess provides a mock function for the type MockVideoAPI
819+func (_mock *MockVideoAPI) PWStreamTriggerProcess(stream unsafe.Pointer) int32 {
820+ ret := _mock.Called(stream)
821+
822+ if len(ret) == 0 {
823+ panic("no return value specified for PWStreamTriggerProcess")
824+ }
825+
826+ var r0 int32
827+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
828+ r0 = returnFunc(stream)
829+ } else {
830+ r0 = ret.Get(0).(int32)
831+ }
832+ return r0
833+}
834+
835+// MockVideoAPI_PWStreamTriggerProcess_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamTriggerProcess'
836+type MockVideoAPI_PWStreamTriggerProcess_Call struct {
837+ *mock.Call
838+}
839+
840+// PWStreamTriggerProcess is a helper method to define mock.On call
841+// - stream unsafe.Pointer
842+func (_e *MockVideoAPI_Expecter) PWStreamTriggerProcess(stream any) *MockVideoAPI_PWStreamTriggerProcess_Call {
843+ return &MockVideoAPI_PWStreamTriggerProcess_Call{Call: _e.mock.On("PWStreamTriggerProcess", stream)}
844+}
845+
846+func (_c *MockVideoAPI_PWStreamTriggerProcess_Call) Run(run func(stream unsafe.Pointer)) *MockVideoAPI_PWStreamTriggerProcess_Call {
847+ _c.Call.Run(func(args mock.Arguments) {
848+ var arg0 unsafe.Pointer
849+ if args[0] != nil {
850+ arg0 = args[0].(unsafe.Pointer)
851+ }
852+ run(
853+ arg0,
854+ )
855+ })
856+ return _c
857+}
858+
859+func (_c *MockVideoAPI_PWStreamTriggerProcess_Call) Return(n int32) *MockVideoAPI_PWStreamTriggerProcess_Call {
860+ _c.Call.Return(n)
861+ return _c
862+}
863+
864+func (_c *MockVideoAPI_PWStreamTriggerProcess_Call) RunAndReturn(run func(stream unsafe.Pointer) int32) *MockVideoAPI_PWStreamTriggerProcess_Call {
865+ _c.Call.Return(run)
866+ return _c
867+}
868+
869+// PWStreamUpdateParams provides a mock function for the type MockVideoAPI
870+func (_mock *MockVideoAPI) PWStreamUpdateParams(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32 {
871+ ret := _mock.Called(stream, params, n_params)
872+
873+ if len(ret) == 0 {
874+ panic("no return value specified for PWStreamUpdateParams")
875+ }
876+
877+ var r0 int32
878+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uint32) int32); ok {
879+ r0 = returnFunc(stream, params, n_params)
880+ } else {
881+ r0 = ret.Get(0).(int32)
882+ }
883+ return r0
884+}
885+
886+// MockVideoAPI_PWStreamUpdateParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamUpdateParams'
887+type MockVideoAPI_PWStreamUpdateParams_Call struct {
888+ *mock.Call
889+}
890+
891+// PWStreamUpdateParams is a helper method to define mock.On call
892+// - stream unsafe.Pointer
893+// - params unsafe.Pointer
894+// - n_params uint32
895+func (_e *MockVideoAPI_Expecter) PWStreamUpdateParams(stream any, params any, n_params any) *MockVideoAPI_PWStreamUpdateParams_Call {
896+ return &MockVideoAPI_PWStreamUpdateParams_Call{Call: _e.mock.On("PWStreamUpdateParams", stream, params, n_params)}
897+}
898+
899+func (_c *MockVideoAPI_PWStreamUpdateParams_Call) Run(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32)) *MockVideoAPI_PWStreamUpdateParams_Call {
900+ _c.Call.Run(func(args mock.Arguments) {
901+ var arg0 unsafe.Pointer
902+ if args[0] != nil {
903+ arg0 = args[0].(unsafe.Pointer)
904+ }
905+ var arg1 unsafe.Pointer
906+ if args[1] != nil {
907+ arg1 = args[1].(unsafe.Pointer)
908+ }
909+ var arg2 uint32
910+ if args[2] != nil {
911+ arg2 = args[2].(uint32)
912+ }
913+ run(
914+ arg0,
915+ arg1,
916+ arg2,
917+ )
918+ })
919+ return _c
920+}
921+
922+func (_c *MockVideoAPI_PWStreamUpdateParams_Call) Return(n int32) *MockVideoAPI_PWStreamUpdateParams_Call {
923+ _c.Call.Return(n)
924+ return _c
925+}
926+
927+func (_c *MockVideoAPI_PWStreamUpdateParams_Call) RunAndReturn(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32) *MockVideoAPI_PWStreamUpdateParams_Call {
928+ _c.Call.Return(run)
929+ return _c
930+}
A internal/ports/out/mocks/mock_VideoStreamAPI.go +1411 -0
@@ -0,0 +1,1411 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "unsafe"
9+
10+ mock "github.com/stretchr/testify/mock"
11+)
12+
13+// NewMockVideoStreamAPI creates a new instance of MockVideoStreamAPI. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
14+// The first argument is typically a *testing.T value.
15+func NewMockVideoStreamAPI(t interface {
16+ mock.TestingT
17+ Cleanup(func())
18+}) *MockVideoStreamAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
23+ mock := &MockVideoStreamAPI{}
24+ mock.Mock.Test(t)
25+
26+ t.Cleanup(func() {
27+ if helper, ok := t.(interface{ Helper() }); ok {
28+ helper.Helper()
29+ }
30+ mock.AssertExpectations(t)
31+ })
32+
33+ return mock
34+}
35+
36+// MockVideoStreamAPI is an autogenerated mock type for the VideoStreamAPI type
37+type MockVideoStreamAPI struct {
38+ mock.Mock
39+}
40+
41+type MockVideoStreamAPI_Expecter struct {
42+ mock *mock.Mock
43+}
44+
45+func (_m *MockVideoStreamAPI) EXPECT() *MockVideoStreamAPI_Expecter {
46+ return &MockVideoStreamAPI_Expecter{mock: &_m.Mock}
47+}
48+
49+// PWContextConnect provides a mock function for the type MockVideoStreamAPI
50+func (_mock *MockVideoStreamAPI) PWContextConnect(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
51+ ret := _mock.Called(context, props, user_data_size)
52+
53+ if len(ret) == 0 {
54+ panic("no return value specified for PWContextConnect")
55+ }
56+
57+ var r0 unsafe.Pointer
58+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
59+ r0 = returnFunc(context, props, user_data_size)
60+ } else {
61+ r0 = ret.Get(0).(unsafe.Pointer)
62+ }
63+ return r0
64+}
65+
66+// MockVideoStreamAPI_PWContextConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextConnect'
67+type MockVideoStreamAPI_PWContextConnect_Call struct {
68+ *mock.Call
69+}
70+
71+// PWContextConnect is a helper method to define mock.On call
72+// - context unsafe.Pointer
73+// - props unsafe.Pointer
74+// - user_data_size uintptr
75+func (_e *MockVideoStreamAPI_Expecter) PWContextConnect(context any, props any, user_data_size any) *MockVideoStreamAPI_PWContextConnect_Call {
76+ return &MockVideoStreamAPI_PWContextConnect_Call{Call: _e.mock.On("PWContextConnect", context, props, user_data_size)}
77+}
78+
79+func (_c *MockVideoStreamAPI_PWContextConnect_Call) Run(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockVideoStreamAPI_PWContextConnect_Call {
80+ _c.Call.Run(func(args mock.Arguments) {
81+ var arg0 unsafe.Pointer
82+ if args[0] != nil {
83+ arg0 = args[0].(unsafe.Pointer)
84+ }
85+ var arg1 unsafe.Pointer
86+ if args[1] != nil {
87+ arg1 = args[1].(unsafe.Pointer)
88+ }
89+ var arg2 uintptr
90+ if args[2] != nil {
91+ arg2 = args[2].(uintptr)
92+ }
93+ run(
94+ arg0,
95+ arg1,
96+ arg2,
97+ )
98+ })
99+ return _c
100+}
101+
102+func (_c *MockVideoStreamAPI_PWContextConnect_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWContextConnect_Call {
103+ _c.Call.Return(v)
104+ return _c
105+}
106+
107+func (_c *MockVideoStreamAPI_PWContextConnect_Call) RunAndReturn(run func(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockVideoStreamAPI_PWContextConnect_Call {
108+ _c.Call.Return(run)
109+ return _c
110+}
111+
112+// PWContextDestroy provides a mock function for the type MockVideoStreamAPI
113+func (_mock *MockVideoStreamAPI) PWContextDestroy(context unsafe.Pointer) {
114+ _mock.Called(context)
115+ return
116+}
117+
118+// MockVideoStreamAPI_PWContextDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextDestroy'
119+type MockVideoStreamAPI_PWContextDestroy_Call struct {
120+ *mock.Call
121+}
122+
123+// PWContextDestroy is a helper method to define mock.On call
124+// - context unsafe.Pointer
125+func (_e *MockVideoStreamAPI_Expecter) PWContextDestroy(context any) *MockVideoStreamAPI_PWContextDestroy_Call {
126+ return &MockVideoStreamAPI_PWContextDestroy_Call{Call: _e.mock.On("PWContextDestroy", context)}
127+}
128+
129+func (_c *MockVideoStreamAPI_PWContextDestroy_Call) Run(run func(context unsafe.Pointer)) *MockVideoStreamAPI_PWContextDestroy_Call {
130+ _c.Call.Run(func(args mock.Arguments) {
131+ var arg0 unsafe.Pointer
132+ if args[0] != nil {
133+ arg0 = args[0].(unsafe.Pointer)
134+ }
135+ run(
136+ arg0,
137+ )
138+ })
139+ return _c
140+}
141+
142+func (_c *MockVideoStreamAPI_PWContextDestroy_Call) Return() *MockVideoStreamAPI_PWContextDestroy_Call {
143+ _c.Call.Return()
144+ return _c
145+}
146+
147+func (_c *MockVideoStreamAPI_PWContextDestroy_Call) RunAndReturn(run func(context unsafe.Pointer)) *MockVideoStreamAPI_PWContextDestroy_Call {
148+ _c.Run(run)
149+ return _c
150+}
151+
152+// PWContextNew provides a mock function for the type MockVideoStreamAPI
153+func (_mock *MockVideoStreamAPI) PWContextNew(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer {
154+ ret := _mock.Called(loop, props, user_data_size)
155+
156+ if len(ret) == 0 {
157+ panic("no return value specified for PWContextNew")
158+ }
159+
160+ var r0 unsafe.Pointer
161+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uintptr) unsafe.Pointer); ok {
162+ r0 = returnFunc(loop, props, user_data_size)
163+ } else {
164+ r0 = ret.Get(0).(unsafe.Pointer)
165+ }
166+ return r0
167+}
168+
169+// MockVideoStreamAPI_PWContextNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWContextNew'
170+type MockVideoStreamAPI_PWContextNew_Call struct {
171+ *mock.Call
172+}
173+
174+// PWContextNew is a helper method to define mock.On call
175+// - loop unsafe.Pointer
176+// - props unsafe.Pointer
177+// - user_data_size uintptr
178+func (_e *MockVideoStreamAPI_Expecter) PWContextNew(loop any, props any, user_data_size any) *MockVideoStreamAPI_PWContextNew_Call {
179+ return &MockVideoStreamAPI_PWContextNew_Call{Call: _e.mock.On("PWContextNew", loop, props, user_data_size)}
180+}
181+
182+func (_c *MockVideoStreamAPI_PWContextNew_Call) Run(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr)) *MockVideoStreamAPI_PWContextNew_Call {
183+ _c.Call.Run(func(args mock.Arguments) {
184+ var arg0 unsafe.Pointer
185+ if args[0] != nil {
186+ arg0 = args[0].(unsafe.Pointer)
187+ }
188+ var arg1 unsafe.Pointer
189+ if args[1] != nil {
190+ arg1 = args[1].(unsafe.Pointer)
191+ }
192+ var arg2 uintptr
193+ if args[2] != nil {
194+ arg2 = args[2].(uintptr)
195+ }
196+ run(
197+ arg0,
198+ arg1,
199+ arg2,
200+ )
201+ })
202+ return _c
203+}
204+
205+func (_c *MockVideoStreamAPI_PWContextNew_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWContextNew_Call {
206+ _c.Call.Return(v)
207+ return _c
208+}
209+
210+func (_c *MockVideoStreamAPI_PWContextNew_Call) RunAndReturn(run func(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer) *MockVideoStreamAPI_PWContextNew_Call {
211+ _c.Call.Return(run)
212+ return _c
213+}
214+
215+// PWCoreDisconnect provides a mock function for the type MockVideoStreamAPI
216+func (_mock *MockVideoStreamAPI) PWCoreDisconnect(core unsafe.Pointer) int32 {
217+ ret := _mock.Called(core)
218+
219+ if len(ret) == 0 {
220+ panic("no return value specified for PWCoreDisconnect")
221+ }
222+
223+ var r0 int32
224+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
225+ r0 = returnFunc(core)
226+ } else {
227+ r0 = ret.Get(0).(int32)
228+ }
229+ return r0
230+}
231+
232+// MockVideoStreamAPI_PWCoreDisconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWCoreDisconnect'
233+type MockVideoStreamAPI_PWCoreDisconnect_Call struct {
234+ *mock.Call
235+}
236+
237+// PWCoreDisconnect is a helper method to define mock.On call
238+// - core unsafe.Pointer
239+func (_e *MockVideoStreamAPI_Expecter) PWCoreDisconnect(core any) *MockVideoStreamAPI_PWCoreDisconnect_Call {
240+ return &MockVideoStreamAPI_PWCoreDisconnect_Call{Call: _e.mock.On("PWCoreDisconnect", core)}
241+}
242+
243+func (_c *MockVideoStreamAPI_PWCoreDisconnect_Call) Run(run func(core unsafe.Pointer)) *MockVideoStreamAPI_PWCoreDisconnect_Call {
244+ _c.Call.Run(func(args mock.Arguments) {
245+ var arg0 unsafe.Pointer
246+ if args[0] != nil {
247+ arg0 = args[0].(unsafe.Pointer)
248+ }
249+ run(
250+ arg0,
251+ )
252+ })
253+ return _c
254+}
255+
256+func (_c *MockVideoStreamAPI_PWCoreDisconnect_Call) Return(n int32) *MockVideoStreamAPI_PWCoreDisconnect_Call {
257+ _c.Call.Return(n)
258+ return _c
259+}
260+
261+func (_c *MockVideoStreamAPI_PWCoreDisconnect_Call) RunAndReturn(run func(core unsafe.Pointer) int32) *MockVideoStreamAPI_PWCoreDisconnect_Call {
262+ _c.Call.Return(run)
263+ return _c
264+}
265+
266+// PWLoopDestroy provides a mock function for the type MockVideoStreamAPI
267+func (_mock *MockVideoStreamAPI) PWLoopDestroy(loop unsafe.Pointer) {
268+ _mock.Called(loop)
269+ return
270+}
271+
272+// MockVideoStreamAPI_PWLoopDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopDestroy'
273+type MockVideoStreamAPI_PWLoopDestroy_Call struct {
274+ *mock.Call
275+}
276+
277+// PWLoopDestroy is a helper method to define mock.On call
278+// - loop unsafe.Pointer
279+func (_e *MockVideoStreamAPI_Expecter) PWLoopDestroy(loop any) *MockVideoStreamAPI_PWLoopDestroy_Call {
280+ return &MockVideoStreamAPI_PWLoopDestroy_Call{Call: _e.mock.On("PWLoopDestroy", loop)}
281+}
282+
283+func (_c *MockVideoStreamAPI_PWLoopDestroy_Call) Run(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopDestroy_Call {
284+ _c.Call.Run(func(args mock.Arguments) {
285+ var arg0 unsafe.Pointer
286+ if args[0] != nil {
287+ arg0 = args[0].(unsafe.Pointer)
288+ }
289+ run(
290+ arg0,
291+ )
292+ })
293+ return _c
294+}
295+
296+func (_c *MockVideoStreamAPI_PWLoopDestroy_Call) Return() *MockVideoStreamAPI_PWLoopDestroy_Call {
297+ _c.Call.Return()
298+ return _c
299+}
300+
301+func (_c *MockVideoStreamAPI_PWLoopDestroy_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopDestroy_Call {
302+ _c.Run(run)
303+ return _c
304+}
305+
306+// PWLoopEnter provides a mock function for the type MockVideoStreamAPI
307+func (_mock *MockVideoStreamAPI) PWLoopEnter(loop unsafe.Pointer) {
308+ _mock.Called(loop)
309+ return
310+}
311+
312+// MockVideoStreamAPI_PWLoopEnter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopEnter'
313+type MockVideoStreamAPI_PWLoopEnter_Call struct {
314+ *mock.Call
315+}
316+
317+// PWLoopEnter is a helper method to define mock.On call
318+// - loop unsafe.Pointer
319+func (_e *MockVideoStreamAPI_Expecter) PWLoopEnter(loop any) *MockVideoStreamAPI_PWLoopEnter_Call {
320+ return &MockVideoStreamAPI_PWLoopEnter_Call{Call: _e.mock.On("PWLoopEnter", loop)}
321+}
322+
323+func (_c *MockVideoStreamAPI_PWLoopEnter_Call) Run(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopEnter_Call {
324+ _c.Call.Run(func(args mock.Arguments) {
325+ var arg0 unsafe.Pointer
326+ if args[0] != nil {
327+ arg0 = args[0].(unsafe.Pointer)
328+ }
329+ run(
330+ arg0,
331+ )
332+ })
333+ return _c
334+}
335+
336+func (_c *MockVideoStreamAPI_PWLoopEnter_Call) Return() *MockVideoStreamAPI_PWLoopEnter_Call {
337+ _c.Call.Return()
338+ return _c
339+}
340+
341+func (_c *MockVideoStreamAPI_PWLoopEnter_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopEnter_Call {
342+ _c.Run(run)
343+ return _c
344+}
345+
346+// PWLoopGetFd provides a mock function for the type MockVideoStreamAPI
347+func (_mock *MockVideoStreamAPI) PWLoopGetFd(loop unsafe.Pointer) int32 {
348+ ret := _mock.Called(loop)
349+
350+ if len(ret) == 0 {
351+ panic("no return value specified for PWLoopGetFd")
352+ }
353+
354+ var r0 int32
355+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
356+ r0 = returnFunc(loop)
357+ } else {
358+ r0 = ret.Get(0).(int32)
359+ }
360+ return r0
361+}
362+
363+// MockVideoStreamAPI_PWLoopGetFd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopGetFd'
364+type MockVideoStreamAPI_PWLoopGetFd_Call struct {
365+ *mock.Call
366+}
367+
368+// PWLoopGetFd is a helper method to define mock.On call
369+// - loop unsafe.Pointer
370+func (_e *MockVideoStreamAPI_Expecter) PWLoopGetFd(loop any) *MockVideoStreamAPI_PWLoopGetFd_Call {
371+ return &MockVideoStreamAPI_PWLoopGetFd_Call{Call: _e.mock.On("PWLoopGetFd", loop)}
372+}
373+
374+func (_c *MockVideoStreamAPI_PWLoopGetFd_Call) Run(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopGetFd_Call {
375+ _c.Call.Run(func(args mock.Arguments) {
376+ var arg0 unsafe.Pointer
377+ if args[0] != nil {
378+ arg0 = args[0].(unsafe.Pointer)
379+ }
380+ run(
381+ arg0,
382+ )
383+ })
384+ return _c
385+}
386+
387+func (_c *MockVideoStreamAPI_PWLoopGetFd_Call) Return(n int32) *MockVideoStreamAPI_PWLoopGetFd_Call {
388+ _c.Call.Return(n)
389+ return _c
390+}
391+
392+func (_c *MockVideoStreamAPI_PWLoopGetFd_Call) RunAndReturn(run func(loop unsafe.Pointer) int32) *MockVideoStreamAPI_PWLoopGetFd_Call {
393+ _c.Call.Return(run)
394+ return _c
395+}
396+
397+// PWLoopIterate provides a mock function for the type MockVideoStreamAPI
398+func (_mock *MockVideoStreamAPI) PWLoopIterate(loop unsafe.Pointer, timeout int32) int32 {
399+ ret := _mock.Called(loop, timeout)
400+
401+ if len(ret) == 0 {
402+ panic("no return value specified for PWLoopIterate")
403+ }
404+
405+ var r0 int32
406+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, int32) int32); ok {
407+ r0 = returnFunc(loop, timeout)
408+ } else {
409+ r0 = ret.Get(0).(int32)
410+ }
411+ return r0
412+}
413+
414+// MockVideoStreamAPI_PWLoopIterate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopIterate'
415+type MockVideoStreamAPI_PWLoopIterate_Call struct {
416+ *mock.Call
417+}
418+
419+// PWLoopIterate is a helper method to define mock.On call
420+// - loop unsafe.Pointer
421+// - timeout int32
422+func (_e *MockVideoStreamAPI_Expecter) PWLoopIterate(loop any, timeout any) *MockVideoStreamAPI_PWLoopIterate_Call {
423+ return &MockVideoStreamAPI_PWLoopIterate_Call{Call: _e.mock.On("PWLoopIterate", loop, timeout)}
424+}
425+
426+func (_c *MockVideoStreamAPI_PWLoopIterate_Call) Run(run func(loop unsafe.Pointer, timeout int32)) *MockVideoStreamAPI_PWLoopIterate_Call {
427+ _c.Call.Run(func(args mock.Arguments) {
428+ var arg0 unsafe.Pointer
429+ if args[0] != nil {
430+ arg0 = args[0].(unsafe.Pointer)
431+ }
432+ var arg1 int32
433+ if args[1] != nil {
434+ arg1 = args[1].(int32)
435+ }
436+ run(
437+ arg0,
438+ arg1,
439+ )
440+ })
441+ return _c
442+}
443+
444+func (_c *MockVideoStreamAPI_PWLoopIterate_Call) Return(n int32) *MockVideoStreamAPI_PWLoopIterate_Call {
445+ _c.Call.Return(n)
446+ return _c
447+}
448+
449+func (_c *MockVideoStreamAPI_PWLoopIterate_Call) RunAndReturn(run func(loop unsafe.Pointer, timeout int32) int32) *MockVideoStreamAPI_PWLoopIterate_Call {
450+ _c.Call.Return(run)
451+ return _c
452+}
453+
454+// PWLoopLeave provides a mock function for the type MockVideoStreamAPI
455+func (_mock *MockVideoStreamAPI) PWLoopLeave(loop unsafe.Pointer) {
456+ _mock.Called(loop)
457+ return
458+}
459+
460+// MockVideoStreamAPI_PWLoopLeave_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopLeave'
461+type MockVideoStreamAPI_PWLoopLeave_Call struct {
462+ *mock.Call
463+}
464+
465+// PWLoopLeave is a helper method to define mock.On call
466+// - loop unsafe.Pointer
467+func (_e *MockVideoStreamAPI_Expecter) PWLoopLeave(loop any) *MockVideoStreamAPI_PWLoopLeave_Call {
468+ return &MockVideoStreamAPI_PWLoopLeave_Call{Call: _e.mock.On("PWLoopLeave", loop)}
469+}
470+
471+func (_c *MockVideoStreamAPI_PWLoopLeave_Call) Run(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopLeave_Call {
472+ _c.Call.Run(func(args mock.Arguments) {
473+ var arg0 unsafe.Pointer
474+ if args[0] != nil {
475+ arg0 = args[0].(unsafe.Pointer)
476+ }
477+ run(
478+ arg0,
479+ )
480+ })
481+ return _c
482+}
483+
484+func (_c *MockVideoStreamAPI_PWLoopLeave_Call) Return() *MockVideoStreamAPI_PWLoopLeave_Call {
485+ _c.Call.Return()
486+ return _c
487+}
488+
489+func (_c *MockVideoStreamAPI_PWLoopLeave_Call) RunAndReturn(run func(loop unsafe.Pointer)) *MockVideoStreamAPI_PWLoopLeave_Call {
490+ _c.Run(run)
491+ return _c
492+}
493+
494+// PWLoopNew provides a mock function for the type MockVideoStreamAPI
495+func (_mock *MockVideoStreamAPI) PWLoopNew(props unsafe.Pointer) unsafe.Pointer {
496+ ret := _mock.Called(props)
497+
498+ if len(ret) == 0 {
499+ panic("no return value specified for PWLoopNew")
500+ }
501+
502+ var r0 unsafe.Pointer
503+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) unsafe.Pointer); ok {
504+ r0 = returnFunc(props)
505+ } else {
506+ r0 = ret.Get(0).(unsafe.Pointer)
507+ }
508+ return r0
509+}
510+
511+// MockVideoStreamAPI_PWLoopNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWLoopNew'
512+type MockVideoStreamAPI_PWLoopNew_Call struct {
513+ *mock.Call
514+}
515+
516+// PWLoopNew is a helper method to define mock.On call
517+// - props unsafe.Pointer
518+func (_e *MockVideoStreamAPI_Expecter) PWLoopNew(props any) *MockVideoStreamAPI_PWLoopNew_Call {
519+ return &MockVideoStreamAPI_PWLoopNew_Call{Call: _e.mock.On("PWLoopNew", props)}
520+}
521+
522+func (_c *MockVideoStreamAPI_PWLoopNew_Call) Run(run func(props unsafe.Pointer)) *MockVideoStreamAPI_PWLoopNew_Call {
523+ _c.Call.Run(func(args mock.Arguments) {
524+ var arg0 unsafe.Pointer
525+ if args[0] != nil {
526+ arg0 = args[0].(unsafe.Pointer)
527+ }
528+ run(
529+ arg0,
530+ )
531+ })
532+ return _c
533+}
534+
535+func (_c *MockVideoStreamAPI_PWLoopNew_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWLoopNew_Call {
536+ _c.Call.Return(v)
537+ return _c
538+}
539+
540+func (_c *MockVideoStreamAPI_PWLoopNew_Call) RunAndReturn(run func(props unsafe.Pointer) unsafe.Pointer) *MockVideoStreamAPI_PWLoopNew_Call {
541+ _c.Call.Return(run)
542+ return _c
543+}
544+
545+// PWPropertiesNewString provides a mock function for the type MockVideoStreamAPI
546+func (_mock *MockVideoStreamAPI) PWPropertiesNewString(args *byte) unsafe.Pointer {
547+ ret := _mock.Called(args)
548+
549+ if len(ret) == 0 {
550+ panic("no return value specified for PWPropertiesNewString")
551+ }
552+
553+ var r0 unsafe.Pointer
554+ if returnFunc, ok := ret.Get(0).(func(*byte) unsafe.Pointer); ok {
555+ r0 = returnFunc(args)
556+ } else {
557+ r0 = ret.Get(0).(unsafe.Pointer)
558+ }
559+ return r0
560+}
561+
562+// MockVideoStreamAPI_PWPropertiesNewString_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWPropertiesNewString'
563+type MockVideoStreamAPI_PWPropertiesNewString_Call struct {
564+ *mock.Call
565+}
566+
567+// PWPropertiesNewString is a helper method to define mock.On call
568+// - args *byte
569+func (_e *MockVideoStreamAPI_Expecter) PWPropertiesNewString(args any) *MockVideoStreamAPI_PWPropertiesNewString_Call {
570+ return &MockVideoStreamAPI_PWPropertiesNewString_Call{Call: _e.mock.On("PWPropertiesNewString", args)}
571+}
572+
573+func (_c *MockVideoStreamAPI_PWPropertiesNewString_Call) Run(run func(args *byte)) *MockVideoStreamAPI_PWPropertiesNewString_Call {
574+ _c.Call.Run(func(args mock.Arguments) {
575+ var arg0 *byte
576+ if args[0] != nil {
577+ arg0 = args[0].(*byte)
578+ }
579+ run(
580+ arg0,
581+ )
582+ })
583+ return _c
584+}
585+
586+func (_c *MockVideoStreamAPI_PWPropertiesNewString_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWPropertiesNewString_Call {
587+ _c.Call.Return(v)
588+ return _c
589+}
590+
591+func (_c *MockVideoStreamAPI_PWPropertiesNewString_Call) RunAndReturn(run func(args *byte) unsafe.Pointer) *MockVideoStreamAPI_PWPropertiesNewString_Call {
592+ _c.Call.Return(run)
593+ return _c
594+}
595+
596+// PWStreamAddListener provides a mock function for the type MockVideoStreamAPI
597+func (_mock *MockVideoStreamAPI) PWStreamAddListener(stream unsafe.Pointer, listener unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) int32 {
598+ ret := _mock.Called(stream, listener, events, data)
599+
600+ if len(ret) == 0 {
601+ panic("no return value specified for PWStreamAddListener")
602+ }
603+
604+ var r0 int32
605+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer) int32); ok {
606+ r0 = returnFunc(stream, listener, events, data)
607+ } else {
608+ r0 = ret.Get(0).(int32)
609+ }
610+ return r0
611+}
612+
613+// MockVideoStreamAPI_PWStreamAddListener_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamAddListener'
614+type MockVideoStreamAPI_PWStreamAddListener_Call struct {
615+ *mock.Call
616+}
617+
618+// PWStreamAddListener is a helper method to define mock.On call
619+// - stream unsafe.Pointer
620+// - listener unsafe.Pointer
621+// - events unsafe.Pointer
622+// - data unsafe.Pointer
623+func (_e *MockVideoStreamAPI_Expecter) PWStreamAddListener(stream any, listener any, events any, data any) *MockVideoStreamAPI_PWStreamAddListener_Call {
624+ return &MockVideoStreamAPI_PWStreamAddListener_Call{Call: _e.mock.On("PWStreamAddListener", stream, listener, events, data)}
625+}
626+
627+func (_c *MockVideoStreamAPI_PWStreamAddListener_Call) Run(run func(stream unsafe.Pointer, listener unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer)) *MockVideoStreamAPI_PWStreamAddListener_Call {
628+ _c.Call.Run(func(args mock.Arguments) {
629+ var arg0 unsafe.Pointer
630+ if args[0] != nil {
631+ arg0 = args[0].(unsafe.Pointer)
632+ }
633+ var arg1 unsafe.Pointer
634+ if args[1] != nil {
635+ arg1 = args[1].(unsafe.Pointer)
636+ }
637+ var arg2 unsafe.Pointer
638+ if args[2] != nil {
639+ arg2 = args[2].(unsafe.Pointer)
640+ }
641+ var arg3 unsafe.Pointer
642+ if args[3] != nil {
643+ arg3 = args[3].(unsafe.Pointer)
644+ }
645+ run(
646+ arg0,
647+ arg1,
648+ arg2,
649+ arg3,
650+ )
651+ })
652+ return _c
653+}
654+
655+func (_c *MockVideoStreamAPI_PWStreamAddListener_Call) Return(n int32) *MockVideoStreamAPI_PWStreamAddListener_Call {
656+ _c.Call.Return(n)
657+ return _c
658+}
659+
660+func (_c *MockVideoStreamAPI_PWStreamAddListener_Call) RunAndReturn(run func(stream unsafe.Pointer, listener unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) int32) *MockVideoStreamAPI_PWStreamAddListener_Call {
661+ _c.Call.Return(run)
662+ return _c
663+}
664+
665+// PWStreamConnect provides a mock function for the type MockVideoStreamAPI
666+func (_mock *MockVideoStreamAPI) PWStreamConnect(stream unsafe.Pointer, direction int32, id uint32, flags uint32, ports unsafe.Pointer, n_ports uint32) int32 {
667+ ret := _mock.Called(stream, direction, id, flags, ports, n_ports)
668+
669+ if len(ret) == 0 {
670+ panic("no return value specified for PWStreamConnect")
671+ }
672+
673+ var r0 int32
674+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, int32, uint32, uint32, unsafe.Pointer, uint32) int32); ok {
675+ r0 = returnFunc(stream, direction, id, flags, ports, n_ports)
676+ } else {
677+ r0 = ret.Get(0).(int32)
678+ }
679+ return r0
680+}
681+
682+// MockVideoStreamAPI_PWStreamConnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamConnect'
683+type MockVideoStreamAPI_PWStreamConnect_Call struct {
684+ *mock.Call
685+}
686+
687+// PWStreamConnect is a helper method to define mock.On call
688+// - stream unsafe.Pointer
689+// - direction int32
690+// - id uint32
691+// - flags uint32
692+// - ports unsafe.Pointer
693+// - n_ports uint32
694+func (_e *MockVideoStreamAPI_Expecter) PWStreamConnect(stream any, direction any, id any, flags any, ports any, n_ports any) *MockVideoStreamAPI_PWStreamConnect_Call {
695+ return &MockVideoStreamAPI_PWStreamConnect_Call{Call: _e.mock.On("PWStreamConnect", stream, direction, id, flags, ports, n_ports)}
696+}
697+
698+func (_c *MockVideoStreamAPI_PWStreamConnect_Call) Run(run func(stream unsafe.Pointer, direction int32, id uint32, flags uint32, ports unsafe.Pointer, n_ports uint32)) *MockVideoStreamAPI_PWStreamConnect_Call {
699+ _c.Call.Run(func(args mock.Arguments) {
700+ var arg0 unsafe.Pointer
701+ if args[0] != nil {
702+ arg0 = args[0].(unsafe.Pointer)
703+ }
704+ var arg1 int32
705+ if args[1] != nil {
706+ arg1 = args[1].(int32)
707+ }
708+ var arg2 uint32
709+ if args[2] != nil {
710+ arg2 = args[2].(uint32)
711+ }
712+ var arg3 uint32
713+ if args[3] != nil {
714+ arg3 = args[3].(uint32)
715+ }
716+ var arg4 unsafe.Pointer
717+ if args[4] != nil {
718+ arg4 = args[4].(unsafe.Pointer)
719+ }
720+ var arg5 uint32
721+ if args[5] != nil {
722+ arg5 = args[5].(uint32)
723+ }
724+ run(
725+ arg0,
726+ arg1,
727+ arg2,
728+ arg3,
729+ arg4,
730+ arg5,
731+ )
732+ })
733+ return _c
734+}
735+
736+func (_c *MockVideoStreamAPI_PWStreamConnect_Call) Return(n int32) *MockVideoStreamAPI_PWStreamConnect_Call {
737+ _c.Call.Return(n)
738+ return _c
739+}
740+
741+func (_c *MockVideoStreamAPI_PWStreamConnect_Call) RunAndReturn(run func(stream unsafe.Pointer, direction int32, id uint32, flags uint32, ports unsafe.Pointer, n_ports uint32) int32) *MockVideoStreamAPI_PWStreamConnect_Call {
742+ _c.Call.Return(run)
743+ return _c
744+}
745+
746+// PWStreamDequeueBuffer provides a mock function for the type MockVideoStreamAPI
747+func (_mock *MockVideoStreamAPI) PWStreamDequeueBuffer(stream unsafe.Pointer) unsafe.Pointer {
748+ ret := _mock.Called(stream)
749+
750+ if len(ret) == 0 {
751+ panic("no return value specified for PWStreamDequeueBuffer")
752+ }
753+
754+ var r0 unsafe.Pointer
755+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) unsafe.Pointer); ok {
756+ r0 = returnFunc(stream)
757+ } else {
758+ r0 = ret.Get(0).(unsafe.Pointer)
759+ }
760+ return r0
761+}
762+
763+// MockVideoStreamAPI_PWStreamDequeueBuffer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamDequeueBuffer'
764+type MockVideoStreamAPI_PWStreamDequeueBuffer_Call struct {
765+ *mock.Call
766+}
767+
768+// PWStreamDequeueBuffer is a helper method to define mock.On call
769+// - stream unsafe.Pointer
770+func (_e *MockVideoStreamAPI_Expecter) PWStreamDequeueBuffer(stream any) *MockVideoStreamAPI_PWStreamDequeueBuffer_Call {
771+ return &MockVideoStreamAPI_PWStreamDequeueBuffer_Call{Call: _e.mock.On("PWStreamDequeueBuffer", stream)}
772+}
773+
774+func (_c *MockVideoStreamAPI_PWStreamDequeueBuffer_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamDequeueBuffer_Call {
775+ _c.Call.Run(func(args mock.Arguments) {
776+ var arg0 unsafe.Pointer
777+ if args[0] != nil {
778+ arg0 = args[0].(unsafe.Pointer)
779+ }
780+ run(
781+ arg0,
782+ )
783+ })
784+ return _c
785+}
786+
787+func (_c *MockVideoStreamAPI_PWStreamDequeueBuffer_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWStreamDequeueBuffer_Call {
788+ _c.Call.Return(v)
789+ return _c
790+}
791+
792+func (_c *MockVideoStreamAPI_PWStreamDequeueBuffer_Call) RunAndReturn(run func(stream unsafe.Pointer) unsafe.Pointer) *MockVideoStreamAPI_PWStreamDequeueBuffer_Call {
793+ _c.Call.Return(run)
794+ return _c
795+}
796+
797+// PWStreamDestroy provides a mock function for the type MockVideoStreamAPI
798+func (_mock *MockVideoStreamAPI) PWStreamDestroy(stream unsafe.Pointer) {
799+ _mock.Called(stream)
800+ return
801+}
802+
803+// MockVideoStreamAPI_PWStreamDestroy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamDestroy'
804+type MockVideoStreamAPI_PWStreamDestroy_Call struct {
805+ *mock.Call
806+}
807+
808+// PWStreamDestroy is a helper method to define mock.On call
809+// - stream unsafe.Pointer
810+func (_e *MockVideoStreamAPI_Expecter) PWStreamDestroy(stream any) *MockVideoStreamAPI_PWStreamDestroy_Call {
811+ return &MockVideoStreamAPI_PWStreamDestroy_Call{Call: _e.mock.On("PWStreamDestroy", stream)}
812+}
813+
814+func (_c *MockVideoStreamAPI_PWStreamDestroy_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamDestroy_Call {
815+ _c.Call.Run(func(args mock.Arguments) {
816+ var arg0 unsafe.Pointer
817+ if args[0] != nil {
818+ arg0 = args[0].(unsafe.Pointer)
819+ }
820+ run(
821+ arg0,
822+ )
823+ })
824+ return _c
825+}
826+
827+func (_c *MockVideoStreamAPI_PWStreamDestroy_Call) Return() *MockVideoStreamAPI_PWStreamDestroy_Call {
828+ _c.Call.Return()
829+ return _c
830+}
831+
832+func (_c *MockVideoStreamAPI_PWStreamDestroy_Call) RunAndReturn(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamDestroy_Call {
833+ _c.Run(run)
834+ return _c
835+}
836+
837+// PWStreamDisconnect provides a mock function for the type MockVideoStreamAPI
838+func (_mock *MockVideoStreamAPI) PWStreamDisconnect(stream unsafe.Pointer) int32 {
839+ ret := _mock.Called(stream)
840+
841+ if len(ret) == 0 {
842+ panic("no return value specified for PWStreamDisconnect")
843+ }
844+
845+ var r0 int32
846+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
847+ r0 = returnFunc(stream)
848+ } else {
849+ r0 = ret.Get(0).(int32)
850+ }
851+ return r0
852+}
853+
854+// MockVideoStreamAPI_PWStreamDisconnect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamDisconnect'
855+type MockVideoStreamAPI_PWStreamDisconnect_Call struct {
856+ *mock.Call
857+}
858+
859+// PWStreamDisconnect is a helper method to define mock.On call
860+// - stream unsafe.Pointer
861+func (_e *MockVideoStreamAPI_Expecter) PWStreamDisconnect(stream any) *MockVideoStreamAPI_PWStreamDisconnect_Call {
862+ return &MockVideoStreamAPI_PWStreamDisconnect_Call{Call: _e.mock.On("PWStreamDisconnect", stream)}
863+}
864+
865+func (_c *MockVideoStreamAPI_PWStreamDisconnect_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamDisconnect_Call {
866+ _c.Call.Run(func(args mock.Arguments) {
867+ var arg0 unsafe.Pointer
868+ if args[0] != nil {
869+ arg0 = args[0].(unsafe.Pointer)
870+ }
871+ run(
872+ arg0,
873+ )
874+ })
875+ return _c
876+}
877+
878+func (_c *MockVideoStreamAPI_PWStreamDisconnect_Call) Return(n int32) *MockVideoStreamAPI_PWStreamDisconnect_Call {
879+ _c.Call.Return(n)
880+ return _c
881+}
882+
883+func (_c *MockVideoStreamAPI_PWStreamDisconnect_Call) RunAndReturn(run func(stream unsafe.Pointer) int32) *MockVideoStreamAPI_PWStreamDisconnect_Call {
884+ _c.Call.Return(run)
885+ return _c
886+}
887+
888+// PWStreamGetNodeId provides a mock function for the type MockVideoStreamAPI
889+func (_mock *MockVideoStreamAPI) PWStreamGetNodeId(stream unsafe.Pointer) uint32 {
890+ ret := _mock.Called(stream)
891+
892+ if len(ret) == 0 {
893+ panic("no return value specified for PWStreamGetNodeId")
894+ }
895+
896+ var r0 uint32
897+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) uint32); ok {
898+ r0 = returnFunc(stream)
899+ } else {
900+ r0 = ret.Get(0).(uint32)
901+ }
902+ return r0
903+}
904+
905+// MockVideoStreamAPI_PWStreamGetNodeId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamGetNodeId'
906+type MockVideoStreamAPI_PWStreamGetNodeId_Call struct {
907+ *mock.Call
908+}
909+
910+// PWStreamGetNodeId is a helper method to define mock.On call
911+// - stream unsafe.Pointer
912+func (_e *MockVideoStreamAPI_Expecter) PWStreamGetNodeId(stream any) *MockVideoStreamAPI_PWStreamGetNodeId_Call {
913+ return &MockVideoStreamAPI_PWStreamGetNodeId_Call{Call: _e.mock.On("PWStreamGetNodeId", stream)}
914+}
915+
916+func (_c *MockVideoStreamAPI_PWStreamGetNodeId_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamGetNodeId_Call {
917+ _c.Call.Run(func(args mock.Arguments) {
918+ var arg0 unsafe.Pointer
919+ if args[0] != nil {
920+ arg0 = args[0].(unsafe.Pointer)
921+ }
922+ run(
923+ arg0,
924+ )
925+ })
926+ return _c
927+}
928+
929+func (_c *MockVideoStreamAPI_PWStreamGetNodeId_Call) Return(v uint32) *MockVideoStreamAPI_PWStreamGetNodeId_Call {
930+ _c.Call.Return(v)
931+ return _c
932+}
933+
934+func (_c *MockVideoStreamAPI_PWStreamGetNodeId_Call) RunAndReturn(run func(stream unsafe.Pointer) uint32) *MockVideoStreamAPI_PWStreamGetNodeId_Call {
935+ _c.Call.Return(run)
936+ return _c
937+}
938+
939+// PWStreamIsDriving provides a mock function for the type MockVideoStreamAPI
940+func (_mock *MockVideoStreamAPI) PWStreamIsDriving(stream unsafe.Pointer) bool {
941+ ret := _mock.Called(stream)
942+
943+ if len(ret) == 0 {
944+ panic("no return value specified for PWStreamIsDriving")
945+ }
946+
947+ var r0 bool
948+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) bool); ok {
949+ r0 = returnFunc(stream)
950+ } else {
951+ r0 = ret.Get(0).(bool)
952+ }
953+ return r0
954+}
955+
956+// MockVideoStreamAPI_PWStreamIsDriving_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamIsDriving'
957+type MockVideoStreamAPI_PWStreamIsDriving_Call struct {
958+ *mock.Call
959+}
960+
961+// PWStreamIsDriving is a helper method to define mock.On call
962+// - stream unsafe.Pointer
963+func (_e *MockVideoStreamAPI_Expecter) PWStreamIsDriving(stream any) *MockVideoStreamAPI_PWStreamIsDriving_Call {
964+ return &MockVideoStreamAPI_PWStreamIsDriving_Call{Call: _e.mock.On("PWStreamIsDriving", stream)}
965+}
966+
967+func (_c *MockVideoStreamAPI_PWStreamIsDriving_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamIsDriving_Call {
968+ _c.Call.Run(func(args mock.Arguments) {
969+ var arg0 unsafe.Pointer
970+ if args[0] != nil {
971+ arg0 = args[0].(unsafe.Pointer)
972+ }
973+ run(
974+ arg0,
975+ )
976+ })
977+ return _c
978+}
979+
980+func (_c *MockVideoStreamAPI_PWStreamIsDriving_Call) Return(b bool) *MockVideoStreamAPI_PWStreamIsDriving_Call {
981+ _c.Call.Return(b)
982+ return _c
983+}
984+
985+func (_c *MockVideoStreamAPI_PWStreamIsDriving_Call) RunAndReturn(run func(stream unsafe.Pointer) bool) *MockVideoStreamAPI_PWStreamIsDriving_Call {
986+ _c.Call.Return(run)
987+ return _c
988+}
989+
990+// PWStreamNew provides a mock function for the type MockVideoStreamAPI
991+func (_mock *MockVideoStreamAPI) PWStreamNew(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer {
992+ ret := _mock.Called(core, name, props)
993+
994+ if len(ret) == 0 {
995+ panic("no return value specified for PWStreamNew")
996+ }
997+
998+ var r0 unsafe.Pointer
999+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, *byte, unsafe.Pointer) unsafe.Pointer); ok {
1000+ r0 = returnFunc(core, name, props)
1001+ } else {
1002+ r0 = ret.Get(0).(unsafe.Pointer)
1003+ }
1004+ return r0
1005+}
1006+
1007+// MockVideoStreamAPI_PWStreamNew_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamNew'
1008+type MockVideoStreamAPI_PWStreamNew_Call struct {
1009+ *mock.Call
1010+}
1011+
1012+// PWStreamNew is a helper method to define mock.On call
1013+// - core unsafe.Pointer
1014+// - name *byte
1015+// - props unsafe.Pointer
1016+func (_e *MockVideoStreamAPI_Expecter) PWStreamNew(core any, name any, props any) *MockVideoStreamAPI_PWStreamNew_Call {
1017+ return &MockVideoStreamAPI_PWStreamNew_Call{Call: _e.mock.On("PWStreamNew", core, name, props)}
1018+}
1019+
1020+func (_c *MockVideoStreamAPI_PWStreamNew_Call) Run(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer)) *MockVideoStreamAPI_PWStreamNew_Call {
1021+ _c.Call.Run(func(args mock.Arguments) {
1022+ var arg0 unsafe.Pointer
1023+ if args[0] != nil {
1024+ arg0 = args[0].(unsafe.Pointer)
1025+ }
1026+ var arg1 *byte
1027+ if args[1] != nil {
1028+ arg1 = args[1].(*byte)
1029+ }
1030+ var arg2 unsafe.Pointer
1031+ if args[2] != nil {
1032+ arg2 = args[2].(unsafe.Pointer)
1033+ }
1034+ run(
1035+ arg0,
1036+ arg1,
1037+ arg2,
1038+ )
1039+ })
1040+ return _c
1041+}
1042+
1043+func (_c *MockVideoStreamAPI_PWStreamNew_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWStreamNew_Call {
1044+ _c.Call.Return(v)
1045+ return _c
1046+}
1047+
1048+func (_c *MockVideoStreamAPI_PWStreamNew_Call) RunAndReturn(run func(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer) *MockVideoStreamAPI_PWStreamNew_Call {
1049+ _c.Call.Return(run)
1050+ return _c
1051+}
1052+
1053+// PWStreamNewSimple provides a mock function for the type MockVideoStreamAPI
1054+func (_mock *MockVideoStreamAPI) PWStreamNewSimple(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) unsafe.Pointer {
1055+ ret := _mock.Called(context, name, props, events, data)
1056+
1057+ if len(ret) == 0 {
1058+ panic("no return value specified for PWStreamNewSimple")
1059+ }
1060+
1061+ var r0 unsafe.Pointer
1062+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, *byte, unsafe.Pointer, unsafe.Pointer, unsafe.Pointer) unsafe.Pointer); ok {
1063+ r0 = returnFunc(context, name, props, events, data)
1064+ } else {
1065+ r0 = ret.Get(0).(unsafe.Pointer)
1066+ }
1067+ return r0
1068+}
1069+
1070+// MockVideoStreamAPI_PWStreamNewSimple_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamNewSimple'
1071+type MockVideoStreamAPI_PWStreamNewSimple_Call struct {
1072+ *mock.Call
1073+}
1074+
1075+// PWStreamNewSimple is a helper method to define mock.On call
1076+// - context unsafe.Pointer
1077+// - name *byte
1078+// - props unsafe.Pointer
1079+// - events unsafe.Pointer
1080+// - data unsafe.Pointer
1081+func (_e *MockVideoStreamAPI_Expecter) PWStreamNewSimple(context any, name any, props any, events any, data any) *MockVideoStreamAPI_PWStreamNewSimple_Call {
1082+ return &MockVideoStreamAPI_PWStreamNewSimple_Call{Call: _e.mock.On("PWStreamNewSimple", context, name, props, events, data)}
1083+}
1084+
1085+func (_c *MockVideoStreamAPI_PWStreamNewSimple_Call) Run(run func(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer)) *MockVideoStreamAPI_PWStreamNewSimple_Call {
1086+ _c.Call.Run(func(args mock.Arguments) {
1087+ var arg0 unsafe.Pointer
1088+ if args[0] != nil {
1089+ arg0 = args[0].(unsafe.Pointer)
1090+ }
1091+ var arg1 *byte
1092+ if args[1] != nil {
1093+ arg1 = args[1].(*byte)
1094+ }
1095+ var arg2 unsafe.Pointer
1096+ if args[2] != nil {
1097+ arg2 = args[2].(unsafe.Pointer)
1098+ }
1099+ var arg3 unsafe.Pointer
1100+ if args[3] != nil {
1101+ arg3 = args[3].(unsafe.Pointer)
1102+ }
1103+ var arg4 unsafe.Pointer
1104+ if args[4] != nil {
1105+ arg4 = args[4].(unsafe.Pointer)
1106+ }
1107+ run(
1108+ arg0,
1109+ arg1,
1110+ arg2,
1111+ arg3,
1112+ arg4,
1113+ )
1114+ })
1115+ return _c
1116+}
1117+
1118+func (_c *MockVideoStreamAPI_PWStreamNewSimple_Call) Return(v unsafe.Pointer) *MockVideoStreamAPI_PWStreamNewSimple_Call {
1119+ _c.Call.Return(v)
1120+ return _c
1121+}
1122+
1123+func (_c *MockVideoStreamAPI_PWStreamNewSimple_Call) RunAndReturn(run func(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) unsafe.Pointer) *MockVideoStreamAPI_PWStreamNewSimple_Call {
1124+ _c.Call.Return(run)
1125+ return _c
1126+}
1127+
1128+// PWStreamQueueBuffer provides a mock function for the type MockVideoStreamAPI
1129+func (_mock *MockVideoStreamAPI) PWStreamQueueBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
1130+ ret := _mock.Called(stream, buffer)
1131+
1132+ if len(ret) == 0 {
1133+ panic("no return value specified for PWStreamQueueBuffer")
1134+ }
1135+
1136+ var r0 int32
1137+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer) int32); ok {
1138+ r0 = returnFunc(stream, buffer)
1139+ } else {
1140+ r0 = ret.Get(0).(int32)
1141+ }
1142+ return r0
1143+}
1144+
1145+// MockVideoStreamAPI_PWStreamQueueBuffer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamQueueBuffer'
1146+type MockVideoStreamAPI_PWStreamQueueBuffer_Call struct {
1147+ *mock.Call
1148+}
1149+
1150+// PWStreamQueueBuffer is a helper method to define mock.On call
1151+// - stream unsafe.Pointer
1152+// - buffer unsafe.Pointer
1153+func (_e *MockVideoStreamAPI_Expecter) PWStreamQueueBuffer(stream any, buffer any) *MockVideoStreamAPI_PWStreamQueueBuffer_Call {
1154+ return &MockVideoStreamAPI_PWStreamQueueBuffer_Call{Call: _e.mock.On("PWStreamQueueBuffer", stream, buffer)}
1155+}
1156+
1157+func (_c *MockVideoStreamAPI_PWStreamQueueBuffer_Call) Run(run func(stream unsafe.Pointer, buffer unsafe.Pointer)) *MockVideoStreamAPI_PWStreamQueueBuffer_Call {
1158+ _c.Call.Run(func(args mock.Arguments) {
1159+ var arg0 unsafe.Pointer
1160+ if args[0] != nil {
1161+ arg0 = args[0].(unsafe.Pointer)
1162+ }
1163+ var arg1 unsafe.Pointer
1164+ if args[1] != nil {
1165+ arg1 = args[1].(unsafe.Pointer)
1166+ }
1167+ run(
1168+ arg0,
1169+ arg1,
1170+ )
1171+ })
1172+ return _c
1173+}
1174+
1175+func (_c *MockVideoStreamAPI_PWStreamQueueBuffer_Call) Return(n int32) *MockVideoStreamAPI_PWStreamQueueBuffer_Call {
1176+ _c.Call.Return(n)
1177+ return _c
1178+}
1179+
1180+func (_c *MockVideoStreamAPI_PWStreamQueueBuffer_Call) RunAndReturn(run func(stream unsafe.Pointer, buffer unsafe.Pointer) int32) *MockVideoStreamAPI_PWStreamQueueBuffer_Call {
1181+ _c.Call.Return(run)
1182+ return _c
1183+}
1184+
1185+// PWStreamReturnBuffer provides a mock function for the type MockVideoStreamAPI
1186+func (_mock *MockVideoStreamAPI) PWStreamReturnBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32 {
1187+ ret := _mock.Called(stream, buffer)
1188+
1189+ if len(ret) == 0 {
1190+ panic("no return value specified for PWStreamReturnBuffer")
1191+ }
1192+
1193+ var r0 int32
1194+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer) int32); ok {
1195+ r0 = returnFunc(stream, buffer)
1196+ } else {
1197+ r0 = ret.Get(0).(int32)
1198+ }
1199+ return r0
1200+}
1201+
1202+// MockVideoStreamAPI_PWStreamReturnBuffer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamReturnBuffer'
1203+type MockVideoStreamAPI_PWStreamReturnBuffer_Call struct {
1204+ *mock.Call
1205+}
1206+
1207+// PWStreamReturnBuffer is a helper method to define mock.On call
1208+// - stream unsafe.Pointer
1209+// - buffer unsafe.Pointer
1210+func (_e *MockVideoStreamAPI_Expecter) PWStreamReturnBuffer(stream any, buffer any) *MockVideoStreamAPI_PWStreamReturnBuffer_Call {
1211+ return &MockVideoStreamAPI_PWStreamReturnBuffer_Call{Call: _e.mock.On("PWStreamReturnBuffer", stream, buffer)}
1212+}
1213+
1214+func (_c *MockVideoStreamAPI_PWStreamReturnBuffer_Call) Run(run func(stream unsafe.Pointer, buffer unsafe.Pointer)) *MockVideoStreamAPI_PWStreamReturnBuffer_Call {
1215+ _c.Call.Run(func(args mock.Arguments) {
1216+ var arg0 unsafe.Pointer
1217+ if args[0] != nil {
1218+ arg0 = args[0].(unsafe.Pointer)
1219+ }
1220+ var arg1 unsafe.Pointer
1221+ if args[1] != nil {
1222+ arg1 = args[1].(unsafe.Pointer)
1223+ }
1224+ run(
1225+ arg0,
1226+ arg1,
1227+ )
1228+ })
1229+ return _c
1230+}
1231+
1232+func (_c *MockVideoStreamAPI_PWStreamReturnBuffer_Call) Return(n int32) *MockVideoStreamAPI_PWStreamReturnBuffer_Call {
1233+ _c.Call.Return(n)
1234+ return _c
1235+}
1236+
1237+func (_c *MockVideoStreamAPI_PWStreamReturnBuffer_Call) RunAndReturn(run func(stream unsafe.Pointer, buffer unsafe.Pointer) int32) *MockVideoStreamAPI_PWStreamReturnBuffer_Call {
1238+ _c.Call.Return(run)
1239+ return _c
1240+}
1241+
1242+// PWStreamSetActive provides a mock function for the type MockVideoStreamAPI
1243+func (_mock *MockVideoStreamAPI) PWStreamSetActive(stream unsafe.Pointer, active bool) int32 {
1244+ ret := _mock.Called(stream, active)
1245+
1246+ if len(ret) == 0 {
1247+ panic("no return value specified for PWStreamSetActive")
1248+ }
1249+
1250+ var r0 int32
1251+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, bool) int32); ok {
1252+ r0 = returnFunc(stream, active)
1253+ } else {
1254+ r0 = ret.Get(0).(int32)
1255+ }
1256+ return r0
1257+}
1258+
1259+// MockVideoStreamAPI_PWStreamSetActive_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamSetActive'
1260+type MockVideoStreamAPI_PWStreamSetActive_Call struct {
1261+ *mock.Call
1262+}
1263+
1264+// PWStreamSetActive is a helper method to define mock.On call
1265+// - stream unsafe.Pointer
1266+// - active bool
1267+func (_e *MockVideoStreamAPI_Expecter) PWStreamSetActive(stream any, active any) *MockVideoStreamAPI_PWStreamSetActive_Call {
1268+ return &MockVideoStreamAPI_PWStreamSetActive_Call{Call: _e.mock.On("PWStreamSetActive", stream, active)}
1269+}
1270+
1271+func (_c *MockVideoStreamAPI_PWStreamSetActive_Call) Run(run func(stream unsafe.Pointer, active bool)) *MockVideoStreamAPI_PWStreamSetActive_Call {
1272+ _c.Call.Run(func(args mock.Arguments) {
1273+ var arg0 unsafe.Pointer
1274+ if args[0] != nil {
1275+ arg0 = args[0].(unsafe.Pointer)
1276+ }
1277+ var arg1 bool
1278+ if args[1] != nil {
1279+ arg1 = args[1].(bool)
1280+ }
1281+ run(
1282+ arg0,
1283+ arg1,
1284+ )
1285+ })
1286+ return _c
1287+}
1288+
1289+func (_c *MockVideoStreamAPI_PWStreamSetActive_Call) Return(n int32) *MockVideoStreamAPI_PWStreamSetActive_Call {
1290+ _c.Call.Return(n)
1291+ return _c
1292+}
1293+
1294+func (_c *MockVideoStreamAPI_PWStreamSetActive_Call) RunAndReturn(run func(stream unsafe.Pointer, active bool) int32) *MockVideoStreamAPI_PWStreamSetActive_Call {
1295+ _c.Call.Return(run)
1296+ return _c
1297+}
1298+
1299+// PWStreamTriggerProcess provides a mock function for the type MockVideoStreamAPI
1300+func (_mock *MockVideoStreamAPI) PWStreamTriggerProcess(stream unsafe.Pointer) int32 {
1301+ ret := _mock.Called(stream)
1302+
1303+ if len(ret) == 0 {
1304+ panic("no return value specified for PWStreamTriggerProcess")
1305+ }
1306+
1307+ var r0 int32
1308+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer) int32); ok {
1309+ r0 = returnFunc(stream)
1310+ } else {
1311+ r0 = ret.Get(0).(int32)
1312+ }
1313+ return r0
1314+}
1315+
1316+// MockVideoStreamAPI_PWStreamTriggerProcess_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamTriggerProcess'
1317+type MockVideoStreamAPI_PWStreamTriggerProcess_Call struct {
1318+ *mock.Call
1319+}
1320+
1321+// PWStreamTriggerProcess is a helper method to define mock.On call
1322+// - stream unsafe.Pointer
1323+func (_e *MockVideoStreamAPI_Expecter) PWStreamTriggerProcess(stream any) *MockVideoStreamAPI_PWStreamTriggerProcess_Call {
1324+ return &MockVideoStreamAPI_PWStreamTriggerProcess_Call{Call: _e.mock.On("PWStreamTriggerProcess", stream)}
1325+}
1326+
1327+func (_c *MockVideoStreamAPI_PWStreamTriggerProcess_Call) Run(run func(stream unsafe.Pointer)) *MockVideoStreamAPI_PWStreamTriggerProcess_Call {
1328+ _c.Call.Run(func(args mock.Arguments) {
1329+ var arg0 unsafe.Pointer
1330+ if args[0] != nil {
1331+ arg0 = args[0].(unsafe.Pointer)
1332+ }
1333+ run(
1334+ arg0,
1335+ )
1336+ })
1337+ return _c
1338+}
1339+
1340+func (_c *MockVideoStreamAPI_PWStreamTriggerProcess_Call) Return(n int32) *MockVideoStreamAPI_PWStreamTriggerProcess_Call {
1341+ _c.Call.Return(n)
1342+ return _c
1343+}
1344+
1345+func (_c *MockVideoStreamAPI_PWStreamTriggerProcess_Call) RunAndReturn(run func(stream unsafe.Pointer) int32) *MockVideoStreamAPI_PWStreamTriggerProcess_Call {
1346+ _c.Call.Return(run)
1347+ return _c
1348+}
1349+
1350+// PWStreamUpdateParams provides a mock function for the type MockVideoStreamAPI
1351+func (_mock *MockVideoStreamAPI) PWStreamUpdateParams(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32 {
1352+ ret := _mock.Called(stream, params, n_params)
1353+
1354+ if len(ret) == 0 {
1355+ panic("no return value specified for PWStreamUpdateParams")
1356+ }
1357+
1358+ var r0 int32
1359+ if returnFunc, ok := ret.Get(0).(func(unsafe.Pointer, unsafe.Pointer, uint32) int32); ok {
1360+ r0 = returnFunc(stream, params, n_params)
1361+ } else {
1362+ r0 = ret.Get(0).(int32)
1363+ }
1364+ return r0
1365+}
1366+
1367+// MockVideoStreamAPI_PWStreamUpdateParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PWStreamUpdateParams'
1368+type MockVideoStreamAPI_PWStreamUpdateParams_Call struct {
1369+ *mock.Call
1370+}
1371+
1372+// PWStreamUpdateParams is a helper method to define mock.On call
1373+// - stream unsafe.Pointer
1374+// - params unsafe.Pointer
1375+// - n_params uint32
1376+func (_e *MockVideoStreamAPI_Expecter) PWStreamUpdateParams(stream any, params any, n_params any) *MockVideoStreamAPI_PWStreamUpdateParams_Call {
1377+ return &MockVideoStreamAPI_PWStreamUpdateParams_Call{Call: _e.mock.On("PWStreamUpdateParams", stream, params, n_params)}
1378+}
1379+
1380+func (_c *MockVideoStreamAPI_PWStreamUpdateParams_Call) Run(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32)) *MockVideoStreamAPI_PWStreamUpdateParams_Call {
1381+ _c.Call.Run(func(args mock.Arguments) {
1382+ var arg0 unsafe.Pointer
1383+ if args[0] != nil {
1384+ arg0 = args[0].(unsafe.Pointer)
1385+ }
1386+ var arg1 unsafe.Pointer
1387+ if args[1] != nil {
1388+ arg1 = args[1].(unsafe.Pointer)
1389+ }
1390+ var arg2 uint32
1391+ if args[2] != nil {
1392+ arg2 = args[2].(uint32)
1393+ }
1394+ run(
1395+ arg0,
1396+ arg1,
1397+ arg2,
1398+ )
1399+ })
1400+ return _c
1401+}
1402+
1403+func (_c *MockVideoStreamAPI_PWStreamUpdateParams_Call) Return(n int32) *MockVideoStreamAPI_PWStreamUpdateParams_Call {
1404+ _c.Call.Return(n)
1405+ return _c
1406+}
1407+
1408+func (_c *MockVideoStreamAPI_PWStreamUpdateParams_Call) RunAndReturn(run func(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32) *MockVideoStreamAPI_PWStreamUpdateParams_Call {
1409+ _c.Call.Return(run)
1410+ return _c
1411+}
A internal/ports/out/shm.go +11 -0
@@ -0,0 +1,11 @@
1+package out
2+
3+// SharedMemAPI abstracts the anonymous shared memory used for video buffers
4+// so the stream logic can be tested without real memfds.
5+type SharedMemAPI interface {
6+ // NewSharedMem creates a sealed, size-byte memfd and maps it read-write
7+ // and shared. The caller owns fd and data until FreeSharedMem.
8+ NewSharedMem(name string, size int) (fd int, data []byte, err error)
9+ // FreeSharedMem unmaps data and closes fd.
10+ FreeSharedMem(fd int, data []byte)
11+}
A internal/ports/out/video_gen.go +26 -0
@@ -0,0 +1,26 @@
1+// Code generated by pipewiregen. DO NOT EDIT.
2+
3+package out
4+
5+import "unsafe"
6+
7+// VideoAPI defines the outbound interface for video operations.
8+type VideoAPI interface {
9+ PWLoopNew(props unsafe.Pointer) unsafe.Pointer
10+ PWLoopDestroy(loop unsafe.Pointer)
11+ PWLoopGetFd(loop unsafe.Pointer) int32
12+ PWLoopEnter(loop unsafe.Pointer)
13+ PWLoopLeave(loop unsafe.Pointer)
14+ PWLoopIterate(loop unsafe.Pointer, timeout int32) int32
15+ PWContextNew(loop unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer
16+ PWContextConnect(context unsafe.Pointer, props unsafe.Pointer, user_data_size uintptr) unsafe.Pointer
17+ PWContextDestroy(context unsafe.Pointer)
18+ PWCoreDisconnect(core unsafe.Pointer) int32
19+ PWPropertiesNewString(args *byte) unsafe.Pointer
20+ PWStreamNew(core unsafe.Pointer, name *byte, props unsafe.Pointer) unsafe.Pointer
21+ PWStreamUpdateParams(stream unsafe.Pointer, params unsafe.Pointer, n_params uint32) int32
22+ PWStreamGetNodeId(stream unsafe.Pointer) uint32
23+ PWStreamTriggerProcess(stream unsafe.Pointer) int32
24+ PWStreamIsDriving(stream unsafe.Pointer) bool
25+ PWStreamReturnBuffer(stream unsafe.Pointer, buffer unsafe.Pointer) int32
26+}
A internal/ports/out/videostream.go +9 -0
@@ -0,0 +1,9 @@
1+package out
2+
3+// VideoStreamAPI is the set of C entry points needed by the video source: the
4+// generated VideoAPI group plus the stream calls shared with playback. CAPI
5+// satisfies it.
6+type VideoStreamAPI interface {
7+ VideoAPI
8+ StreamPlaybackAPI
9+}
M pipewire/context.go +2 -1
@@ -1,7 +1,8 @@
11 package pipewire
22
33 // Context is the public interface for PipeWire context operations.
4-// Contexts manage resources and connections to the PipeWire server.
4+// Contexts manage resources and connections to the PipeWire server. Create
5+// one with Runtime.NewContext on a Loop; destroy it before the Loop.
56 type Context interface {
67 Destroy()
78 Connect() (Core, error)
M pipewire/core.go +4 -0
@@ -7,7 +7,11 @@ import (
77 // Core is the public interface for PipeWire core connection operations.
88 // A Core represents a connection to the PipeWire server.
99 type Core interface {
10+ // Disconnect disconnects from the server. Destroy streams first.
1011 Disconnect()
1112 Registry() (Registry, error)
1213 NewStream(name string, props unsafe.Pointer) (Stream, error)
14+ // NewVideoSource creates a Video/Source node fed by the caller. Call it
15+ // on the loop thread. See VideoSource.
16+ NewVideoSource(cfg VideoConfig, h VideoHandler) (*VideoSource, error)
1317 }
M pipewire/interfaces.go +5 -0
@@ -5,6 +5,11 @@ type Runtime interface {
55 Init() error
66 Deinit()
77 NewMainLoop() (MainLoop, error)
8+ // NewLoop creates a loop the caller iterates itself. See Loop for the
9+ // threading rules.
10+ NewLoop() (Loop, error)
11+ // NewContext creates a context that runs on loop.
12+ NewContext(loop Loop) (Context, error)
813 }
914
1015 // MainLoop is the public interface for PipeWire main loop operations.
A pipewire/loop.go +19 -0
@@ -0,0 +1,19 @@
1+package pipewire
2+
3+// Loop is a PipeWire loop that the caller iterates itself, typically from a
4+// poll loop that waits on Fd. Create it with Runtime.NewLoop.
5+//
6+// A Loop and everything created on it (Context, Core, VideoSource) must only
7+// be used from one goroutine that stays on one OS thread: call
8+// runtime.LockOSThread before creating the loop and keep it until Destroy.
9+type Loop interface {
10+ // Fd returns a pollable file descriptor that becomes readable when
11+ // Iterate has work to do.
12+ Fd() int
13+ // Iterate dispatches pending events without blocking. Stream callbacks
14+ // (VideoHandler methods) run inside it. A transient failure such as
15+ // EINTR is ignored; the next Iterate retries.
16+ Iterate()
17+ // Destroy destroys the loop. Destroy contexts created on it first.
18+ Destroy()
19+}
M pipewire/mocks/mock_MainLoop.go +10 -1
@@ -14,10 +14,19 @@ func NewMockMainLoop(t interface {
1414 mock.TestingT
1515 Cleanup(func())
1616 }) *MockMainLoop {
17+ if helper, ok := t.(interface{ Helper() }); ok {
18+ helper.Helper()
19+ }
20+
1721 mock := &MockMainLoop{}
1822 mock.Mock.Test(t)
1923
20- t.Cleanup(func() { mock.AssertExpectations(t) })
24+ t.Cleanup(func() {
25+ if helper, ok := t.(interface{ Helper() }); ok {
26+ helper.Helper()
27+ }
28+ mock.AssertExpectations(t)
29+ })
2130
2231 return mock
2332 }
M pipewire/mocks/mock_Player.go +10 -1
@@ -15,10 +15,19 @@ func NewMockPlayer(t interface {
1515 mock.TestingT
1616 Cleanup(func())
1717 }) *MockPlayer {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
1822 mock := &MockPlayer{}
1923 mock.Mock.Test(t)
2024
21- t.Cleanup(func() { mock.AssertExpectations(t) })
25+ t.Cleanup(func() {
26+ if helper, ok := t.(interface{ Helper() }); ok {
27+ helper.Helper()
28+ }
29+ mock.AssertExpectations(t)
30+ })
2231
2332 return mock
2433 }
M pipewire/mocks/mock_Runtime.go +127 -1
@@ -15,10 +15,19 @@ func NewMockRuntime(t interface {
1515 mock.TestingT
1616 Cleanup(func())
1717 }) *MockRuntime {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
1822 mock := &MockRuntime{}
1923 mock.Mock.Test(t)
2024
21- t.Cleanup(func() { mock.AssertExpectations(t) })
25+ t.Cleanup(func() {
26+ if helper, ok := t.(interface{ Helper() }); ok {
27+ helper.Helper()
28+ }
29+ mock.AssertExpectations(t)
30+ })
2231
2332 return mock
2433 }
@@ -113,6 +122,123 @@ func (_c *MockRuntime_Init_Call) RunAndReturn(run func() error) *MockRuntime_Ini
113122 return _c
114123 }
115124
125+// NewContext provides a mock function for the type MockRuntime
126+func (_mock *MockRuntime) NewContext(loop pipewire.Loop) (pipewire.Context, error) {
127+ ret := _mock.Called(loop)
128+
129+ if len(ret) == 0 {
130+ panic("no return value specified for NewContext")
131+ }
132+
133+ var r0 pipewire.Context
134+ var r1 error
135+ if returnFunc, ok := ret.Get(0).(func(pipewire.Loop) (pipewire.Context, error)); ok {
136+ return returnFunc(loop)
137+ }
138+ if returnFunc, ok := ret.Get(0).(func(pipewire.Loop) pipewire.Context); ok {
139+ r0 = returnFunc(loop)
140+ } else {
141+ if ret.Get(0) != nil {
142+ r0 = ret.Get(0).(pipewire.Context)
143+ }
144+ }
145+ if returnFunc, ok := ret.Get(1).(func(pipewire.Loop) error); ok {
146+ r1 = returnFunc(loop)
147+ } else {
148+ r1 = ret.Error(1)
149+ }
150+ return r0, r1
151+}
152+
153+// MockRuntime_NewContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewContext'
154+type MockRuntime_NewContext_Call struct {
155+ *mock.Call
156+}
157+
158+// NewContext is a helper method to define mock.On call
159+// - loop pipewire.Loop
160+func (_e *MockRuntime_Expecter) NewContext(loop any) *MockRuntime_NewContext_Call {
161+ return &MockRuntime_NewContext_Call{Call: _e.mock.On("NewContext", loop)}
162+}
163+
164+func (_c *MockRuntime_NewContext_Call) Run(run func(loop pipewire.Loop)) *MockRuntime_NewContext_Call {
165+ _c.Call.Run(func(args mock.Arguments) {
166+ var arg0 pipewire.Loop
167+ if args[0] != nil {
168+ arg0 = args[0].(pipewire.Loop)
169+ }
170+ run(
171+ arg0,
172+ )
173+ })
174+ return _c
175+}
176+
177+func (_c *MockRuntime_NewContext_Call) Return(context pipewire.Context, err error) *MockRuntime_NewContext_Call {
178+ _c.Call.Return(context, err)
179+ return _c
180+}
181+
182+func (_c *MockRuntime_NewContext_Call) RunAndReturn(run func(loop pipewire.Loop) (pipewire.Context, error)) *MockRuntime_NewContext_Call {
183+ _c.Call.Return(run)
184+ return _c
185+}
186+
187+// NewLoop provides a mock function for the type MockRuntime
188+func (_mock *MockRuntime) NewLoop() (pipewire.Loop, error) {
189+ ret := _mock.Called()
190+
191+ if len(ret) == 0 {
192+ panic("no return value specified for NewLoop")
193+ }
194+
195+ var r0 pipewire.Loop
196+ var r1 error
197+ if returnFunc, ok := ret.Get(0).(func() (pipewire.Loop, error)); ok {
198+ return returnFunc()
199+ }
200+ if returnFunc, ok := ret.Get(0).(func() pipewire.Loop); ok {
201+ r0 = returnFunc()
202+ } else {
203+ if ret.Get(0) != nil {
204+ r0 = ret.Get(0).(pipewire.Loop)
205+ }
206+ }
207+ if returnFunc, ok := ret.Get(1).(func() error); ok {
208+ r1 = returnFunc()
209+ } else {
210+ r1 = ret.Error(1)
211+ }
212+ return r0, r1
213+}
214+
215+// MockRuntime_NewLoop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewLoop'
216+type MockRuntime_NewLoop_Call struct {
217+ *mock.Call
218+}
219+
220+// NewLoop is a helper method to define mock.On call
221+func (_e *MockRuntime_Expecter) NewLoop() *MockRuntime_NewLoop_Call {
222+ return &MockRuntime_NewLoop_Call{Call: _e.mock.On("NewLoop")}
223+}
224+
225+func (_c *MockRuntime_NewLoop_Call) Run(run func()) *MockRuntime_NewLoop_Call {
226+ _c.Call.Run(func(args mock.Arguments) {
227+ run()
228+ })
229+ return _c
230+}
231+
232+func (_c *MockRuntime_NewLoop_Call) Return(loop pipewire.Loop, err error) *MockRuntime_NewLoop_Call {
233+ _c.Call.Return(loop, err)
234+ return _c
235+}
236+
237+func (_c *MockRuntime_NewLoop_Call) RunAndReturn(run func() (pipewire.Loop, error)) *MockRuntime_NewLoop_Call {
238+ _c.Call.Return(run)
239+ return _c
240+}
241+
116242 // NewMainLoop provides a mock function for the type MockRuntime
117243 func (_mock *MockRuntime) NewMainLoop() (pipewire.MainLoop, error) {
118244 ret := _mock.Called()
M pipewire/runtime.go +66 -0
@@ -2,6 +2,7 @@ package pipewire
22
33 import (
44 "errors"
5+ "unsafe"
56
67 "github.com/bnema/purego-pipewire/internal/capi"
78 "github.com/bnema/purego-pipewire/internal/core"
@@ -28,6 +29,71 @@ func (rw runtimeWrapper) NewMainLoop() (MainLoop, error) {
2829 return mainLoopWrapper{inner: ml}, nil
2930 }
3031
32+func (rw runtimeWrapper) NewLoop() (Loop, error) {
33+ l, err := rw.inner.NewLoop()
34+ if err != nil {
35+ return nil, err
36+ }
37+ return l, nil
38+}
39+
40+func (rw runtimeWrapper) NewContext(loop Loop) (Context, error) {
41+ l, ok := loop.(*core.Loop)
42+ if !ok {
43+ return nil, errors.New("pipewire: loop was not created by Runtime.NewLoop")
44+ }
45+ c, err := rw.inner.NewContext(l)
46+ if err != nil {
47+ return nil, err
48+ }
49+ return contextWrapper{inner: c}, nil
50+}
51+
52+// contextWrapper wraps the internal core.Context to implement Context.
53+type contextWrapper struct {
54+ inner *core.Context
55+}
56+
57+func (cw contextWrapper) Destroy() { cw.inner.Destroy() }
58+
59+func (cw contextWrapper) Connect() (Core, error) {
60+ c, err := cw.inner.Connect()
61+ if err != nil {
62+ return nil, err
63+ }
64+ return coreWrapper{inner: c}, nil
65+}
66+
67+// coreWrapper wraps the internal core.Core to implement Core.
68+type coreWrapper struct {
69+ inner *core.Core
70+}
71+
72+func (cw coreWrapper) Disconnect() { cw.inner.Disconnect() }
73+
74+func (cw coreWrapper) Registry() (Registry, error) {
75+ r, err := cw.inner.Registry()
76+ if err != nil {
77+ return nil, err
78+ }
79+ return r, nil
80+}
81+
82+func (cw coreWrapper) NewStream(name string, props unsafe.Pointer) (Stream, error) {
83+ st, err := cw.inner.NewStream(name, props)
84+ if err != nil {
85+ return nil, err
86+ }
87+ return st, nil
88+}
89+
90+func (cw coreWrapper) NewVideoSource(cfg VideoConfig, h VideoHandler) (*VideoSource, error) {
91+ if h == nil {
92+ return nil, core.ErrVideoConfig
93+ }
94+ return cw.inner.NewVideoSource(cfg, h)
95+}
96+
3197 // mainLoopWrapper wraps the internal core.MainLoop to implement the public MainLoop interface.
3298 type mainLoopWrapper struct {
3399 inner *core.MainLoop
A pipewire/video.go +79 -0
@@ -0,0 +1,79 @@
1+package pipewire
2+
3+import (
4+ "github.com/bnema/purego-pipewire/internal/core"
5+ "github.com/bnema/purego-pipewire/spa"
6+)
7+
8+// VideoFormat is a SPA raw video format (BGRx=8, BGRA=12).
9+type VideoFormat = spa.VideoFormat
10+
11+// Supported video formats. They map to wl_shm XRGB8888 (BGRx) and ARGB8888
12+// (BGRA).
13+const (
14+ VideoFormatBGRx = spa.VideoFormatBGRx
15+ VideoFormatBGRA = spa.VideoFormatBGRA
16+)
17+
18+// pw_stream_state values reported to VideoHandler.StateChanged.
19+const (
20+ StreamStateError int32 = -1
21+ StreamStateUnconnected int32 = 0
22+ StreamStateConnecting int32 = 1
23+ StreamStatePaused int32 = 2
24+ StreamStateStreaming int32 = 3
25+)
26+
27+// Errors returned by NewVideoSource and VideoSource, for errors.Is.
28+var (
29+ ErrVideoConfig = core.ErrVideoConfig
30+ ErrVideoTableFull = core.ErrVideoTableFull
31+ ErrVideoStreamCreate = core.ErrVideoStreamCreate
32+ ErrVideoConnect = core.ErrVideoConnect
33+ ErrVideoResize = core.ErrVideoResize
34+ ErrVideoBorrowed = core.ErrVideoBorrowed
35+)
36+
37+// VideoConfig configures a VideoSource: the node name, the initial size, the
38+// maximum frame rate (60 when zero) and the accepted formats (BGRx when
39+// empty, else BGRx and/or BGRA; the first is preferred).
40+type VideoConfig = core.VideoConfig
41+
42+// VideoHandler receives VideoSource events. Every method runs on the loop
43+// thread, inside Loop.Iterate (or, for the first events, inside the call that
44+// triggered them). Implementations must not block and must not allocate.
45+type VideoHandler interface {
46+ // StateChanged reports a stream state change; see the StreamState
47+ // constants. nodeID is the PipeWire node id once known (0 before). err
48+ // carries the message when state is StreamStateError.
49+ StateChanged(state int32, nodeID uint32, err string)
50+ // FormatChanged reports the negotiated format and its row stride.
51+ FormatChanged(f VideoFormat, w, h, stride int)
52+ // BuffersChanged is called after a buffer was added or removed;
53+ // Dequeue may now return a buffer.
54+ BuffersChanged()
55+}
56+
57+// VideoBuffer is a buffer handed out by VideoSource.Dequeue. Data aliases a
58+// mapped memfd owned by the source; it is valid until the buffer is given back
59+// with Queue or Release. The value is owned by the source: do not copy it or
60+// keep it after giving it back.
61+type VideoBuffer = core.VideoBuffer
62+
63+// VideoSource is a PipeWire Video/Source node that drives the graph and
64+// exposes shared-memory (memfd) buffers. All methods run on the loop thread.
65+//
66+// Produce a frame with Dequeue, write Data, Queue, then Trigger — in that
67+// order: the buffer must be queued before Trigger. A steady-state cycle
68+// performs no heap allocation.
69+//
70+// b := src.Dequeue() // nil: no buffer is free
71+// // ... fill b.Data (b.Stride bytes per row) ...
72+// src.Queue(b, seq, pts, false)
73+// src.Trigger()
74+//
75+// Buffers PipeWire removes (renegotiation, Resize, Destroy, consumer
76+// disconnect) stay mapped while borrowed and are unmapped when given back with
77+// Queue or Release. Resize fails with ErrVideoBorrowed while a buffer is
78+// borrowed; Destroy is best called when none is. DMA-BUF is not supported.
79+type VideoSource = core.VideoSource
M spa/constants.go +83 -0
@@ -16,3 +16,86 @@ const (
1616 DirectionInput = 0
1717 DirectionOutput = 1
1818 )
19+
20+// SPA pod types (spa/utils/type.h).
21+const (
22+ TypeId = 3
23+ TypeInt = 4
24+ TypeRectangle = 10
25+ TypeFraction = 11
26+ TypeObject = 15
27+ TypeChoice = 19
28+
29+ TypeObjectFormat = 0x40003 // SPA_TYPE_OBJECT_Format
30+ TypeObjectParamBuffers = 0x40004 // SPA_TYPE_OBJECT_ParamBuffers
31+ TypeObjectParamMeta = 0x40005 // SPA_TYPE_OBJECT_ParamMeta
32+)
33+
34+// SPA choice types (spa/utils/defs.h).
35+const (
36+ ChoiceNone = 0
37+ ChoiceRange = 1
38+ ChoiceEnum = 3
39+ ChoiceFlags = 4
40+)
41+
42+// SPA param ids (spa/param/param.h).
43+const (
44+ ParamEnumFormat = 3
45+ ParamFormat = 4
46+ ParamBuffers = 5
47+ ParamMeta = 6
48+)
49+
50+// SPA format property keys (spa/param/format.h).
51+const (
52+ FormatKeyMediaType = 1
53+ FormatKeyMediaSubtype = 2
54+ FormatKeyVideoFormat = 0x20001
55+ FormatKeyVideoModifier = 0x20002
56+ FormatKeyVideoSize = 0x20003
57+ FormatKeyVideoFramerate = 0x20004
58+ FormatKeyVideoMaxRate = 0x20005
59+)
60+
61+// SPA media subtypes (spa/param/format.h).
62+const MediaSubtypeRaw = 1
63+
64+// SPA buffers param keys (spa/param/buffers.h).
65+const (
66+ BuffersKeyBuffers = 1
67+ BuffersKeyBlocks = 2
68+ BuffersKeySize = 3
69+ BuffersKeyStride = 4
70+ BuffersKeyAlign = 5
71+ BuffersKeyDataType = 6
72+)
73+
74+// SPA meta param keys (spa/param/buffers.h) and meta types (spa/buffer/meta.h).
75+const (
76+ MetaKeyType = 1
77+ MetaKeySize = 2
78+
79+ MetaTypeHeader = 1 // SPA_META_Header
80+ MetaHeaderSize = 32 // sizeof(struct spa_meta_header)
81+)
82+
83+// SPA data types (spa/buffer/buffer.h) and flags.
84+const (
85+ DataMemFd = 2
86+ DataDmaBuf = 3
87+
88+ DataFlagReadWrite = 3 // SPA_DATA_FLAG_READWRITE
89+
90+ ChunkFlagCorrupted = 1 // SPA_CHUNK_FLAG_CORRUPTED
91+ MetaHeaderFlagCorrupted = 2 // SPA_META_HEADER_FLAG_CORRUPTED
92+ IDInvalid = 0xffffffff // SPA_ID_INVALID
93+)
94+
95+// SPA raw video formats (spa/param/video/raw.h).
96+type VideoFormat uint32
97+
98+const (
99+ VideoFormatBGRx VideoFormat = 8
100+ VideoFormatBGRA VideoFormat = 12
101+)
A spa/pod.go +382 -0
@@ -0,0 +1,382 @@
1+package spa
2+
3+import (
4+ "encoding/binary"
5+ "errors"
6+ "math"
7+)
8+
9+// Errors returned by the pod builder.
10+var (
11+ // ErrPodFull is returned when the destination buffer is too small.
12+ ErrPodFull = errors.New("spa: pod buffer full")
13+ // ErrPodState is returned for unbalanced Object/End or Prop outside an object.
14+ ErrPodState = errors.New("spa: invalid pod builder state")
15+)
16+
17+const podMaxDepth = 4
18+
19+// Fraction is a SPA fraction (num/denom).
20+type Fraction struct{ Num, Denom uint32 }
21+
22+// PodBuilder encodes SPA pods little-endian into a caller-owned buffer. It
23+// never allocates and never grows past len(buf): when the buffer is full the
24+// first error is kept and returned by Bytes. The zero value is unusable; use
25+// NewPodBuilder.
26+type PodBuilder struct {
27+ buf []byte
28+ n int
29+ err error
30+ depth int
31+ stack [podMaxDepth]int // offset of each open object header
32+}
33+
34+// NewPodBuilder returns a builder writing into buf.
35+func NewPodBuilder(buf []byte) PodBuilder { return PodBuilder{buf: buf} }
36+
37+// Bytes returns the encoded pod bytes (a prefix of the destination buffer) or
38+// the first error. It fails if an Object is still open.
39+func (b *PodBuilder) Bytes() ([]byte, error) {
40+ if b.err != nil {
41+ return nil, b.err
42+ }
43+ if b.depth != 0 {
44+ return nil, ErrPodState
45+ }
46+ return b.buf[:b.n], nil
47+}
48+
49+func (b *PodBuilder) fail(err error) {
50+ if b.err == nil {
51+ b.err = err
52+ }
53+}
54+
55+// room reports whether n more bytes fit, recording ErrPodFull otherwise.
56+func (b *PodBuilder) room(n int) bool {
57+ if b.err != nil {
58+ return false
59+ }
60+ if n < 0 || b.n+n > len(b.buf) {
61+ b.fail(ErrPodFull)
62+ return false
63+ }
64+ return true
65+}
66+
67+func (b *PodBuilder) u32(v uint32) {
68+ binary.LittleEndian.PutUint32(b.buf[b.n:], v)
69+ b.n += 4
70+}
71+
72+// pad8 zero-pads the write offset to a multiple of 8 bytes.
73+func (b *PodBuilder) pad8() {
74+ for b.n&7 != 0 {
75+ b.buf[b.n] = 0
76+ b.n++
77+ }
78+}
79+
80+// Object opens an object pod. Close it with End.
81+func (b *PodBuilder) Object(objType, id uint32) {
82+ if !b.room(16) {
83+ return
84+ }
85+ if b.depth == podMaxDepth {
86+ b.fail(ErrPodState)
87+ return
88+ }
89+ b.stack[b.depth] = b.n
90+ b.depth++
91+ b.u32(0) // size, patched by End
92+ b.u32(TypeObject)
93+ b.u32(objType)
94+ b.u32(id)
95+}
96+
97+// Prop starts an object property with the given key; the next value written
98+// is the property value.
99+func (b *PodBuilder) Prop(key uint32) {
100+ if b.err == nil && b.depth == 0 {
101+ b.fail(ErrPodState)
102+ return
103+ }
104+ if !b.room(8) {
105+ return
106+ }
107+ b.u32(key)
108+ b.u32(0) // flags
109+}
110+
111+// End closes the innermost open object.
112+func (b *PodBuilder) End() {
113+ if b.err != nil {
114+ return
115+ }
116+ if b.depth == 0 {
117+ b.fail(ErrPodState)
118+ return
119+ }
120+ b.depth--
121+ start := b.stack[b.depth]
122+ binary.LittleEndian.PutUint32(b.buf[start:], uint32(b.n-start-8))
123+ // Padding was already room-checked by the last value; objects always
124+ // end on an 8-byte boundary because every value is padded.
125+ b.pad8()
126+}
127+
128+func (b *PodBuilder) scalar(typ uint32, size uint32, a, c uint32) {
129+ // size is the body size (4 or 8); the pod is padded to 16 bytes.
130+ if !b.room(16) {
131+ return
132+ }
133+ b.u32(size)
134+ b.u32(typ)
135+ b.u32(a)
136+ b.u32(c)
137+}
138+
139+// Id appends an Id pod.
140+func (b *PodBuilder) Id(v uint32) { b.scalar(TypeId, 4, v, 0) }
141+
142+// Int appends an Int pod.
143+func (b *PodBuilder) Int(v int32) { b.scalar(TypeInt, 4, uint32(v), 0) }
144+
145+// Rectangle appends a Rectangle pod.
146+func (b *PodBuilder) Rectangle(w, h uint32) { b.scalar(TypeRectangle, 8, w, h) }
147+
148+// Fraction appends a Fraction pod.
149+func (b *PodBuilder) Fraction(num, denom uint32) { b.scalar(TypeFraction, 8, num, denom) }
150+
151+// choiceBegin writes the choice header; it returns the header offset.
152+func (b *PodBuilder) choiceBegin(choiceType, childSize, childType uint32, nvals int) (int, bool) {
153+ // Room for the header, the values and the padding choiceEnd adds.
154+ if !b.room((b.n+24+nvals*int(childSize)+7)&^7 - b.n) {
155+ return 0, false
156+ }
157+ start := b.n
158+ b.u32(0) // size, patched in choiceEnd
159+ b.u32(TypeChoice)
160+ b.u32(choiceType)
161+ b.u32(0) // flags
162+ b.u32(childSize)
163+ b.u32(childType)
164+ return start, true
165+}
166+
167+func (b *PodBuilder) choiceEnd(start int) {
168+ binary.LittleEndian.PutUint32(b.buf[start:], uint32(b.n-start-8))
169+ b.pad8()
170+}
171+
172+// ChoiceEnumId appends an enum choice of Ids. vals is written verbatim: the
173+// first value is the default and the remaining values are the alternatives.
174+func (b *PodBuilder) ChoiceEnumId(vals ...uint32) {
175+ if len(vals) == 0 {
176+ b.fail(ErrPodState)
177+ return
178+ }
179+ start, ok := b.choiceBegin(ChoiceEnum, 4, TypeId, len(vals))
180+ if !ok {
181+ return
182+ }
183+ for _, v := range vals {
184+ b.u32(v)
185+ }
186+ b.choiceEnd(start)
187+}
188+
189+// ChoiceRangeInt appends a range choice of Ints.
190+func (b *PodBuilder) ChoiceRangeInt(def, min, max int32) {
191+ start, ok := b.choiceBegin(ChoiceRange, 4, TypeInt, 3)
192+ if !ok {
193+ return
194+ }
195+ b.u32(uint32(def))
196+ b.u32(uint32(min))
197+ b.u32(uint32(max))
198+ b.choiceEnd(start)
199+}
200+
201+// ChoiceRangeFraction appends a range choice of Fractions.
202+func (b *PodBuilder) ChoiceRangeFraction(def, min, max Fraction) {
203+ start, ok := b.choiceBegin(ChoiceRange, 8, TypeFraction, 3)
204+ if !ok {
205+ return
206+ }
207+ for _, f := range [3]Fraction{def, min, max} {
208+ b.u32(f.Num)
209+ b.u32(f.Denom)
210+ }
211+ b.choiceEnd(start)
212+}
213+
214+// ChoiceFlagsInt appends a flags choice of Ints with a single flags value.
215+func (b *PodBuilder) ChoiceFlagsInt(flags uint32) {
216+ start, ok := b.choiceBegin(ChoiceFlags, 4, TypeInt, 1)
217+ if !ok {
218+ return
219+ }
220+ b.u32(flags)
221+ b.choiceEnd(start)
222+}
223+
224+// VideoEnumFormat encodes an EnumFormat param for raw video of the given
225+// size, maximum frame rate and formats (the first format is the default).
226+// Modifiers are omitted (shared memory only).
227+func VideoEnumFormat(dst []byte, w, h, maxFPS int, formats ...VideoFormat) ([]byte, error) {
228+ if len(formats) == 0 || w <= 0 || h <= 0 || maxFPS <= 0 {
229+ return nil, ErrPodState
230+ }
231+ b := NewPodBuilder(dst)
232+ b.Object(TypeObjectFormat, ParamEnumFormat)
233+ b.Prop(FormatKeyMediaType)
234+ b.Id(MediaTypeVideo)
235+ b.Prop(FormatKeyMediaSubtype)
236+ b.Id(MediaSubtypeRaw)
237+ b.Prop(FormatKeyVideoFormat)
238+ if len(formats) == 1 {
239+ b.Id(uint32(formats[0]))
240+ } else {
241+ b.podEnumFormats(formats)
242+ }
243+ b.Prop(FormatKeyVideoSize)
244+ b.Rectangle(uint32(w), uint32(h))
245+ b.Prop(FormatKeyVideoFramerate)
246+ b.Fraction(0, 1)
247+ b.Prop(FormatKeyVideoMaxRate)
248+ b.ChoiceRangeFraction(
249+ Fraction{uint32(maxFPS), 1},
250+ Fraction{1, 1},
251+ Fraction{uint32(maxFPS), 1},
252+ )
253+ b.End()
254+ return b.Bytes()
255+}
256+
257+// podEnumFormats writes an enum choice of video formats. As in SPA's own
258+// builders, the first value is the default and is repeated in the list of
259+// alternatives.
260+func (b *PodBuilder) podEnumFormats(formats []VideoFormat) {
261+ start, ok := b.choiceBegin(ChoiceEnum, 4, TypeId, len(formats)+1)
262+ if !ok {
263+ return
264+ }
265+ b.u32(uint32(formats[0]))
266+ for _, f := range formats {
267+ b.u32(uint32(f))
268+ }
269+ b.choiceEnd(start)
270+}
271+
272+// VideoBuffers encodes a Buffers param: one block of size bytes with the
273+// given stride, a buffer count range (minBuf..maxBuf, defBuf by default) and
274+// the allowed SPA data types as a bit mask (for example 1<<DataMemFd).
275+func VideoBuffers(dst []byte, size, stride, minBuf, defBuf, maxBuf int, dataTypes uint32) ([]byte, error) {
276+ if size <= 0 || stride <= 0 || minBuf <= 0 || defBuf < minBuf || maxBuf < defBuf ||
277+ size > math.MaxInt32 || stride > math.MaxInt32 || maxBuf > math.MaxInt32 {
278+ return nil, ErrPodState
279+ }
280+ b := NewPodBuilder(dst)
281+ b.Object(TypeObjectParamBuffers, ParamBuffers)
282+ b.Prop(BuffersKeyBuffers)
283+ b.ChoiceRangeInt(int32(defBuf), int32(minBuf), int32(maxBuf))
284+ b.Prop(BuffersKeyBlocks)
285+ b.Int(1)
286+ b.Prop(BuffersKeySize)
287+ b.Int(int32(size))
288+ b.Prop(BuffersKeyStride)
289+ b.Int(int32(stride))
290+ b.Prop(BuffersKeyAlign)
291+ b.Int(16)
292+ b.Prop(BuffersKeyDataType)
293+ b.ChoiceFlagsInt(dataTypes)
294+ b.End()
295+ return b.Bytes()
296+}
297+
298+// MetaHeader encodes a Meta param requesting a spa_meta_header (32 bytes).
299+func MetaHeader(dst []byte) ([]byte, error) {
300+ b := NewPodBuilder(dst)
301+ b.Object(TypeObjectParamMeta, ParamMeta)
302+ b.Prop(MetaKeyType)
303+ b.Id(MetaTypeHeader)
304+ b.Prop(MetaKeySize)
305+ b.Int(MetaHeaderSize)
306+ b.End()
307+ return b.Bytes()
308+}
309+
310+// ParseVideoFormat extracts the video format and size from a Format param
311+// (an Object pod). It does not allocate. ok is false for malformed,
312+// truncated or non-raw-video pods.
313+//
314+// PipeWire wraps every value of a negotiated Format in a Choice of type None
315+// holding a single value; plain values are accepted as well. Choices that
316+// still offer alternatives (Enum, Range, ...) are not a fixed format and are
317+// rejected.
318+func ParseVideoFormat(pod []byte) (format uint32, w, h int, ok bool) {
319+ if len(pod) < 16 {
320+ return 0, 0, 0, false
321+ }
322+ le := binary.LittleEndian
323+ size := int(le.Uint32(pod[0:]))
324+ if le.Uint32(pod[4:]) != TypeObject || size < 8 || 8+size > len(pod) {
325+ return 0, 0, 0, false
326+ }
327+ if le.Uint32(pod[8:]) != TypeObjectFormat {
328+ return 0, 0, 0, false
329+ }
330+ end := 8 + size
331+ off := 16
332+ var haveFmt, haveSize, isVideo, isRaw bool
333+ for off+16 <= end {
334+ key := le.Uint32(pod[off:])
335+ vsize := int(le.Uint32(pod[off+8:]))
336+ vtype := le.Uint32(pod[off+12:])
337+ body := off + 16
338+ if vsize < 0 || body+vsize > end {
339+ return 0, 0, 0, false
340+ }
341+ next := (body + vsize + 7) &^ 7
342+ if next <= off {
343+ return 0, 0, 0, false
344+ }
345+ if vtype == TypeChoice {
346+ // Choice body: type, flags, then the child pod header (size, type)
347+ // and the values.
348+ if vsize < 16 || le.Uint32(pod[body:]) != ChoiceNone {
349+ return 0, 0, 0, false
350+ }
351+ childSize := int(le.Uint32(pod[body+8:]))
352+ vtype = le.Uint32(pod[body+12:])
353+ body += 16
354+ vsize -= 16
355+ if childSize < 0 || childSize > vsize {
356+ return 0, 0, 0, false
357+ }
358+ vsize = childSize
359+ }
360+ switch {
361+ case key == FormatKeyMediaType && vtype == TypeId && vsize >= 4:
362+ isVideo = le.Uint32(pod[body:]) == MediaTypeVideo
363+ case key == FormatKeyMediaSubtype && vtype == TypeId && vsize >= 4:
364+ isRaw = le.Uint32(pod[body:]) == MediaSubtypeRaw
365+ case key == FormatKeyVideoFormat && vtype == TypeId && vsize >= 4:
366+ format = le.Uint32(pod[body:])
367+ haveFmt = true
368+ case key == FormatKeyVideoSize && vtype == TypeRectangle && vsize >= 8:
369+ uw, uh := le.Uint32(pod[body:]), le.Uint32(pod[body+4:])
370+ if uw == 0 || uh == 0 || uw > 1<<20 || uh > 1<<20 {
371+ return 0, 0, 0, false
372+ }
373+ w, h = int(uw), int(uh)
374+ haveSize = true
375+ }
376+ off = next
377+ }
378+ if !(haveFmt && haveSize && isVideo && isRaw) {
379+ return 0, 0, 0, false
380+ }
381+ return format, w, h, true
382+}
A spa/pod_test.go +274 -0
@@ -0,0 +1,274 @@
1+package spa
2+
3+import (
4+ "encoding/binary"
5+ "encoding/hex"
6+ "errors"
7+ "testing"
8+)
9+
10+// Golden vectors are the bytes produced by the SPA reference builder
11+// (spa/pod/builder.h, libspa 0.2 / PipeWire 1.6.9) for the same parameters
12+// via spa_pod_builder_add_object / SPA_POD_CHOICE_* on amd64.
13+const (
14+ goldenEnumFormatSingle = "b80000000f00000003000400030000000100000000000000040000000300000002000000000000000200000000000000040000000300000001000000000000000100020000000000040000000300000008000000000000000300020000000000080000000a00000040010000f00000000400020000000000080000000b0000000000000001000000050002000000000028000000130000000100000000000000080000000b0000003c0000000100000001000000010000003c00000001000000"
15+ goldenEnumFormatMulti = "d00000000f000000030004000300000001000000000000000400000003000000020000000000000002000000000000000400000003000000010000000000000001000200000000001c000000130000000300000000000000040000000300000008000000080000000c000000000000000300020000000000080000000a00000080070000380400000400020000000000080000000b0000000000000001000000050002000000000028000000130000000100000000000000080000000b000000900000000100000001000000010000009000000001000000"
16+ goldenBuffers = "c00000000f000000040004000500000001000000000000001c0000001300000001000000000000000400000004000000080000000200000010000000000000000200000000000000040000000400000001000000000000000300000000000000040000000400000000b004000000000004000000000000000400000004000000000500000000000005000000000000000400000004000000100000000000000006000000000000001400000013000000040000000000000004000000040000000400000000000000"
17+ goldenMetaHeader = "380000000f0000000500040006000000010000000000000004000000030000000100000000000000020000000000000004000000040000002000000000000000"
18+)
19+
20+func mustHex(t testing.TB, s string) []byte {
21+ t.Helper()
22+ b, err := hex.DecodeString(s)
23+ if err != nil {
24+ t.Fatal(err)
25+ }
26+ return b
27+}
28+
29+func TestVideoEnumFormatGoldenSingle(t *testing.T) {
30+ var dst [512]byte
31+ got, err := VideoEnumFormat(dst[:], 320, 240, 60, VideoFormatBGRx)
32+ if err != nil {
33+ t.Fatal(err)
34+ }
35+ if want := mustHex(t, goldenEnumFormatSingle); hex.EncodeToString(got) != hex.EncodeToString(want) {
36+ t.Fatalf("got %x\nwant %x", got, want)
37+ }
38+}
39+
40+func TestVideoEnumFormatGoldenMulti(t *testing.T) {
41+ var dst [512]byte
42+ got, err := VideoEnumFormat(dst[:], 1920, 1080, 144, VideoFormatBGRx, VideoFormatBGRA)
43+ if err != nil {
44+ t.Fatal(err)
45+ }
46+ if want := mustHex(t, goldenEnumFormatMulti); hex.EncodeToString(got) != hex.EncodeToString(want) {
47+ t.Fatalf("got %x\nwant %x", got, want)
48+ }
49+}
50+
51+func TestVideoBuffersGolden(t *testing.T) {
52+ var dst [512]byte
53+ got, err := VideoBuffers(dst[:], 320*240*4, 320*4, 2, 8, 16, 1<<DataMemFd)
54+ if err != nil {
55+ t.Fatal(err)
56+ }
57+ if want := mustHex(t, goldenBuffers); hex.EncodeToString(got) != hex.EncodeToString(want) {
58+ t.Fatalf("got %x\nwant %x", got, want)
59+ }
60+}
61+
62+// TestBuildersExactFit checks each builder accepts a destination exactly as
63+// long as its pod and rejects one byte less.
64+func TestBuildersExactFit(t *testing.T) {
65+ builds := map[string]func(dst []byte) ([]byte, error){
66+ "enum single": func(dst []byte) ([]byte, error) { return VideoEnumFormat(dst, 320, 240, 60, VideoFormatBGRx) },
67+ "enum multi": func(dst []byte) ([]byte, error) {
68+ return VideoEnumFormat(dst, 1920, 1080, 144, VideoFormatBGRx, VideoFormatBGRA)
69+ },
70+ "buffers": func(dst []byte) ([]byte, error) { return VideoBuffers(dst, 320*240*4, 320*4, 2, 8, 16, 1<<DataMemFd) },
71+ "meta": MetaHeader,
72+ }
73+ for name, build := range builds {
74+ var big [512]byte
75+ pod, err := build(big[:])
76+ if err != nil {
77+ t.Fatalf("%s: %v", name, err)
78+ }
79+ if _, err := build(make([]byte, len(pod))); err != nil {
80+ t.Errorf("%s: exact %d-byte destination: %v", name, len(pod), err)
81+ }
82+ if _, err := build(make([]byte, len(pod)-1)); !errors.Is(err, ErrPodFull) {
83+ t.Errorf("%s: short destination: %v, want ErrPodFull", name, err)
84+ }
85+ }
86+}
87+
88+func TestVideoBuffersRejectsInt32Overflow(t *testing.T) {
89+ var dst [512]byte
90+ if _, err := VideoBuffers(dst[:], 1<<31, 1<<17, 2, 8, 16, 1<<DataMemFd); !errors.Is(err, ErrPodState) {
91+ t.Fatalf("size 1<<31: %v, want ErrPodState", err)
92+ }
93+}
94+
95+func TestMetaHeaderGolden(t *testing.T) {
96+ var dst [128]byte
97+ got, err := MetaHeader(dst[:])
98+ if err != nil {
99+ t.Fatal(err)
100+ }
101+ if want := mustHex(t, goldenMetaHeader); hex.EncodeToString(got) != hex.EncodeToString(want) {
102+ t.Fatalf("got %x\nwant %x", got, want)
103+ }
104+}
105+
106+// formatPod builds a negotiated (non-choice) Format object for parser tests.
107+func formatPod(t testing.TB, dst []byte, format VideoFormat, w, h uint32) []byte {
108+ t.Helper()
109+ b := NewPodBuilder(dst)
110+ b.Object(TypeObjectFormat, ParamFormat)
111+ b.Prop(FormatKeyMediaType)
112+ b.Id(MediaTypeVideo)
113+ b.Prop(FormatKeyMediaSubtype)
114+ b.Id(MediaSubtypeRaw)
115+ b.Prop(FormatKeyVideoFormat)
116+ b.Id(uint32(format))
117+ b.Prop(FormatKeyVideoSize)
118+ b.Rectangle(w, h)
119+ b.Prop(FormatKeyVideoFramerate)
120+ b.Fraction(60, 1)
121+ b.End()
122+ pod, err := b.Bytes()
123+ if err != nil {
124+ t.Fatal(err)
125+ }
126+ return pod
127+}
128+
129+// goldenNegotiatedFormat is a Format param exactly as PipeWire 1.6.9 delivers
130+// it to param_changed for a 320x240 BGRx stream (captured from a live stream):
131+// every value is a Choice of type None around one value.
132+const goldenNegotiatedFormat = "f80000000f0000000300040004000000010000000000000014000000130000000000000000000000040000000300000002000000000000000200000000000000140000001300000000000000000000000400000003000000010000000000000001000200000000001400000013000000000000000000000004000000030000000800000000000000030002000000000018000000130000000000000000000000080000000a00000040010000f00000000400020000000000080000000b0000000000000001000000050002000000000028000000130000000000000000000000080000000b0000003c0000000100000001000000010000003c00000001000000"
133+
134+func TestParseVideoFormatChoiceNone(t *testing.T) {
135+ f, w, h, ok := ParseVideoFormat(mustHex(t, goldenNegotiatedFormat))
136+ if !ok || VideoFormat(f) != VideoFormatBGRx || w != 320 || h != 240 {
137+ t.Fatalf("parse = %d %d %d %v", f, w, h, ok)
138+ }
139+ // Every truncation of the real pod is rejected without panicking.
140+ pod := mustHex(t, goldenNegotiatedFormat)
141+ for n := 0; n < len(pod); n++ {
142+ if _, _, _, ok := ParseVideoFormat(pod[:n]); ok {
143+ t.Fatalf("truncated to %d bytes accepted", n)
144+ }
145+ }
146+}
147+
148+func TestParseVideoFormatRoundTrip(t *testing.T) {
149+ var dst [256]byte
150+ for _, tc := range []struct {
151+ f VideoFormat
152+ w, h int
153+ }{{VideoFormatBGRx, 320, 240}, {VideoFormatBGRA, 3840, 2160}} {
154+ pod := formatPod(t, dst[:], tc.f, uint32(tc.w), uint32(tc.h))
155+ f, w, h, ok := ParseVideoFormat(pod)
156+ if !ok || VideoFormat(f) != tc.f || w != tc.w || h != tc.h {
157+ t.Fatalf("parse = %d %d %d %v, want %d %d %d", f, w, h, ok, tc.f, tc.w, tc.h)
158+ }
159+ }
160+}
161+
162+func TestParseVideoFormatRejectsMalformed(t *testing.T) {
163+ var dst [256]byte
164+ pod := append([]byte(nil), formatPod(t, dst[:], VideoFormatBGRx, 320, 240)...)
165+
166+ // Every truncation must be rejected without panicking.
167+ for n := 0; n < len(pod); n++ {
168+ if _, _, _, ok := ParseVideoFormat(pod[:n]); ok {
169+ t.Fatalf("truncated to %d bytes accepted", n)
170+ }
171+ }
172+ // Non-object, wrong object type, oversized declared size.
173+ bad := append([]byte(nil), pod...)
174+ binary.LittleEndian.PutUint32(bad[4:], TypeInt)
175+ if _, _, _, ok := ParseVideoFormat(bad); ok {
176+ t.Fatal("non-object accepted")
177+ }
178+ bad = append([]byte(nil), pod...)
179+ binary.LittleEndian.PutUint32(bad[8:], TypeObjectParamBuffers)
180+ if _, _, _, ok := ParseVideoFormat(bad); ok {
181+ t.Fatal("wrong object type accepted")
182+ }
183+ bad = append([]byte(nil), pod...)
184+ binary.LittleEndian.PutUint32(bad[0:], 1<<30)
185+ if _, _, _, ok := ParseVideoFormat(bad); ok {
186+ t.Fatal("oversized pod accepted")
187+ }
188+ // Property value size overflowing the object.
189+ bad = append([]byte(nil), pod...)
190+ binary.LittleEndian.PutUint32(bad[16+8:], 1<<31)
191+ if _, _, _, ok := ParseVideoFormat(bad); ok {
192+ t.Fatal("oversized property accepted")
193+ }
194+ // Zero and absurd sizes.
195+ if _, _, _, ok := ParseVideoFormat(formatPod(t, dst[:], VideoFormatBGRx, 0, 240)); ok {
196+ t.Fatal("zero width accepted")
197+ }
198+ if _, _, _, ok := ParseVideoFormat(formatPod(t, dst[:], VideoFormatBGRx, 1<<24, 240)); ok {
199+ t.Fatal("huge width accepted")
200+ }
201+ // An EnumFormat with a choice for the format is not a negotiated format.
202+ var big [512]byte
203+ enum, err := VideoEnumFormat(big[:], 320, 240, 60, VideoFormatBGRx, VideoFormatBGRA)
204+ if err != nil {
205+ t.Fatal(err)
206+ }
207+ if _, _, _, ok := ParseVideoFormat(enum); ok {
208+ t.Fatal("choice format accepted")
209+ }
210+}
211+
212+func TestPodBuilderErrors(t *testing.T) {
213+ // Buffer too small: error, no panic, for every size up to the full pod.
214+ var full [512]byte
215+ pod, err := VideoEnumFormat(full[:], 320, 240, 60, VideoFormatBGRx, VideoFormatBGRA)
216+ if err != nil {
217+ t.Fatal(err)
218+ }
219+ for n := 0; n < len(pod); n++ {
220+ small := make([]byte, n)
221+ if _, err := VideoEnumFormat(small, 320, 240, 60, VideoFormatBGRx, VideoFormatBGRA); err != ErrPodFull {
222+ t.Fatalf("size %d: err = %v, want ErrPodFull", n, err)
223+ }
224+ }
225+ if _, err := VideoBuffers(make([]byte, 16), 1, 1, 2, 8, 16, 4); err != ErrPodFull {
226+ t.Fatalf("buffers small: %v", err)
227+ }
228+ if _, err := MetaHeader(make([]byte, 8)); err != ErrPodFull {
229+ t.Fatalf("meta small: %v", err)
230+ }
231+ // Invalid arguments.
232+ if _, err := VideoEnumFormat(full[:], 0, 240, 60, VideoFormatBGRx); err == nil {
233+ t.Fatal("zero width accepted")
234+ }
235+ if _, err := VideoEnumFormat(full[:], 320, 240, 60); err == nil {
236+ t.Fatal("no formats accepted")
237+ }
238+ if _, err := VideoBuffers(full[:], 0, 4, 2, 8, 16, 4); err == nil {
239+ t.Fatal("zero size accepted")
240+ }
241+ // Unbalanced builder use.
242+ b := NewPodBuilder(full[:])
243+ b.Prop(1)
244+ if _, err := b.Bytes(); err != ErrPodState {
245+ t.Fatalf("prop outside object: %v", err)
246+ }
247+ b = NewPodBuilder(full[:])
248+ b.Object(TypeObjectFormat, ParamFormat)
249+ if _, err := b.Bytes(); err != ErrPodState {
250+ t.Fatalf("open object: %v", err)
251+ }
252+ b = NewPodBuilder(full[:])
253+ b.End()
254+ if _, err := b.Bytes(); err != ErrPodState {
255+ t.Fatalf("end without object: %v", err)
256+ }
257+}
258+
259+func TestPodAllocations(t *testing.T) {
260+ var dst [512]byte
261+ pod := append([]byte(nil), formatPod(t, dst[:], VideoFormatBGRx, 320, 240)...)
262+ real := mustHex(t, goldenNegotiatedFormat)
263+ allocs := testing.AllocsPerRun(200, func() {
264+ e, _ := VideoEnumFormat(dst[:], 320, 240, 60, VideoFormatBGRx, VideoFormatBGRA)
265+ _, _ = VideoBuffers(dst[:], 307200, 1280, 2, 8, 16, 1<<DataMemFd)
266+ _, _ = MetaHeader(dst[:])
267+ _ = e
268+ _, _, _, _ = ParseVideoFormat(pod)
269+ _, _, _, _ = ParseVideoFormat(real)
270+ })
271+ if allocs != 0 {
272+ t.Fatalf("allocs = %v, want 0", allocs)
273+ }
274+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close