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
2 2
3 on: pull_request3 on: pull_request
4 4
5+permissions:
6+ contents: write
7+ pull-requests: write
8+
5 jobs:9 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.
6 auto-merge:12 auto-merge:
7- if: github.event.pull_request.user.login == 'dependabot[bot]'13+ runs-on: ubuntu-latest
8- permissions:14+ if: github.actor == 'dependabot[bot]'
9- contents: write15+ steps:
10- pull-requests: write16+ - name: Dependabot metadata
11- uses: bnema/gh-actions/.github/workflows/dependabot-auto-merge.yml@main17+ 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:
16 InitAPI:16 InitAPI:
17 LoopAPI:17 LoopAPI:
18 StreamOps:18 StreamOps:
19+ VideoAPI:
20+ SharedMemAPI:
21+ VideoStreamAPI:
19 github.com/bnema/purego-pipewire/pipewire:22 github.com/bnema/purego-pipewire/pipewire:
20 config:23 config:
21 all: false24 all: false
@@ -24,3 +27,8 @@ packages:
24 Runtime:27 Runtime:
25 MainLoop:28 MainLoop:
26 Player:29 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
23 - **Inbound port interfaces** for the handwritten core layer23 - **Inbound port interfaces** for the handwritten core layer
24 - **Public API wrappers** in `pipewire/` and SPA constants/helpers in `spa/`24 - **Public API wrappers** in `pipewire/` and SPA constants/helpers in `spa/`
25 25
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.
27 27
28 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/`.28 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/`.
29 29
@@ -86,6 +86,83 @@ if err := player.Start(); err != nil {
86 }86 }
87 ```87 ```
88 88
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+
89 ### Testing with mocks166 ### Testing with mocks
90 167
91 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`.168 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 @@
6 "groups": [6 "groups": [
7 {"name": "init", "interface": "InitAPI", "package": "out", "symbols": ["pw_init", "pw_deinit"]},7 {"name": "init", "interface": "InitAPI", "package": "out", "symbols": ["pw_init", "pw_deinit"]},
8 {"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"]},8 {"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"]}
10 ],11 ],
11 "symbols": [12 "symbols": [
12 {"name": "pw_init", "library": "pipewire", "group": "init", "signature": "func(argc *int32, argv ***byte)"},13 {"name": "pw_init", "library": "pipewire", "group": "init", "signature": "func(argc *int32, argv ***byte)"},
@@ -23,7 +24,24 @@
23 {"name": "pw_stream_dequeue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer) unsafe.Pointer"},24 {"name": "pw_stream_dequeue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer) unsafe.Pointer"},
24 {"name": "pw_stream_queue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer, buffer unsafe.Pointer) int32"},25 {"name": "pw_stream_queue_buffer", "library": "pipewire", "group": "stream_playback", "signature": "func(stream unsafe.Pointer, buffer unsafe.Pointer) int32"},
25 {"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_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"}
27 ],45 ],
28 "callbacks": [46 "callbacks": [
29 {"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"}47 {"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 @@
1 module github.com/bnema/purego-pipewire1 module github.com/bnema/purego-pipewire
2 2
3-go 1.263+go 1.27
4 4
5 require (5 require (
6- github.com/bnema/purego v0.11.0-bnema.36+ github.com/bnema/purego v0.13.0-bnema.1
7 github.com/stretchr/testify v1.11.17 github.com/stretchr/testify v1.11.1
8 )8 )
9 9
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 {
9 registerLoop(h.PipeWire)9 registerLoop(h.PipeWire)
10 registerLoopLock(h.PipeWire)10 registerLoopLock(h.PipeWire)
11 registerStream_playback(h.PipeWire)11 registerStream_playback(h.PipeWire)
12- return nil12+ registerVideo(h.PipeWire)
13+ return registerVideoFast(h.PipeWire)
13 }14 }
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
3 import (3 import (
4 "errors"4 "errors"
5 "unsafe"5 "unsafe"
6+
7+ portout "github.com/bnema/purego-pipewire/internal/ports/out"
6 )8 )
7 9
8 var ErrContextCreate = errors.New("failed to create context")10 var ErrContextCreate = errors.New("failed to create context")
9 11
12+// Context wraps a pw_context. Create it with Runtime.NewContext and destroy
13+// it before the Loop it was created on.
10 type Context struct {14 type Context struct {
11- ptr unsafe.Pointer15+ ptr unsafe.Pointer
16+ capi portout.VideoStreamAPI
17+ shm portout.SharedMemAPI
12 }18 }
13 19
14-func (r *Runtime) NewContext() (*Context, error) {20+// NewContext creates a pw_context that runs on loop. The loop must outlive
15- // For now, return a placeholder - full implementation21+// the context.
16- // will require generated context methods22+func (r *Runtime) NewContext(loop *Loop) (*Context, error) {
17- return nil, ErrContextCreate23+ 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
18 }31 }
19 32
33+// Destroy destroys the context. All cores connected from it must have been
34+// disconnected first. Destroy is idempotent.
20 func (c *Context) Destroy() {35 func (c *Context) Destroy() {
21- // Placeholder for context destruction36+ if c.ptr == nil {
37+ return
38+ }
39+ c.capi.PWContextDestroy(c.ptr)
40+ c.ptr = nil
22 }41 }
23 42
43+// Connect connects to the PipeWire daemon.
24 func (c *Context) Connect() (*Core, error) {44 func (c *Context) Connect() (*Core, error) {
25- // Placeholder - will connect and return a Core45+ if c.ptr == nil {
26- return nil, errors.New("not implemented")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
27 }53 }
28 54
29 func (c *Context) Ptr() unsafe.Pointer {55 func (c *Context) Ptr() unsafe.Pointer {
M internal/core/core.go +11 -1
@@ -3,6 +3,8 @@ package core
3 import (3 import (
4 "errors"4 "errors"
5 "unsafe"5 "unsafe"
6+
7+ portout "github.com/bnema/purego-pipewire/internal/ports/out"
6 )8 )
7 9
8 var ErrCoreConnect = errors.New("failed to connect core")10 var ErrCoreConnect = errors.New("failed to connect core")
@@ -10,11 +12,19 @@ var ErrRegistryUnavailable = errors.New("registry unavailable")
10 12
11 type Core struct {13 type Core struct {
12 ptr unsafe.Pointer14 ptr unsafe.Pointer
15+ capi portout.VideoStreamAPI
16+ shm portout.SharedMemAPI
13 registry *Registry17 registry *Registry
14 }18 }
15 19
20+// Disconnect disconnects from the daemon. Streams created from the core must
21+// have been destroyed first. Disconnect is idempotent.
16 func (c *Core) Disconnect() {22 func (c *Core) Disconnect() {
17- // Placeholder for core disconnection23+ if c.ptr == nil || c.capi == nil {
24+ return
25+ }
26+ c.capi.PWCoreDisconnect(c.ptr)
27+ c.ptr = nil
18 }28 }
19 29
20 func (c *Core) Registry() (*Registry, error) {30 func (c *Core) Registry() (*Registry, error) {
M internal/core/loop.go +50 -0
@@ -33,3 +33,53 @@ func (ml *MainLoop) Destroy() {
33 func (ml *MainLoop) Ptr() unsafe.Pointer {33 func (ml *MainLoop) Ptr() unsafe.Pointer {
34 return ml.ptr34 return ml.ptr
35 }35 }
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 {
16 mock.TestingT16 mock.TestingT
17 Cleanup(func())17 Cleanup(func())
18 }) *MockCAPI {18 }) *MockCAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
19 mock := &MockCAPI{}23 mock := &MockCAPI{}
20 mock.Mock.Test(t)24 mock.Mock.Test(t)
21 25
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+ })
23 32
24 return mock33 return mock
25 }34 }
@@ -37,6 +46,223 @@ func (_m *MockCAPI) EXPECT() *MockCAPI_Expecter {
37 return &MockCAPI_Expecter{mock: &_m.Mock}46 return &MockCAPI_Expecter{mock: &_m.Mock}
38 }47 }
39 48
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+
40 // PWDeinit provides a mock function for the type MockCAPI266 // PWDeinit provides a mock function for the type MockCAPI
41 func (_mock *MockCAPI) PWDeinit() {267 func (_mock *MockCAPI) PWDeinit() {
42 _mock.Called()268 _mock.Called()
@@ -84,7 +310,7 @@ type MockCAPI_PWInit_Call struct {
84 // PWInit is a helper method to define mock.On call310 // PWInit is a helper method to define mock.On call
85 // - argc *int32311 // - argc *int32
86 // - argv ***byte312 // - 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 {
88 return &MockCAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}314 return &MockCAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}
89 }315 }
90 316
@@ -116,6 +342,285 @@ func (_c *MockCAPI_PWInit_Call) RunAndReturn(run func(argc *int32, argv ***byte)
116 return _c342 return _c
117 }343 }
118 344
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+
119 // PWMainLoopDestroy provides a mock function for the type MockCAPI624 // PWMainLoopDestroy provides a mock function for the type MockCAPI
120 func (_mock *MockCAPI) PWMainLoopDestroy(loop unsafe.Pointer) {625 func (_mock *MockCAPI) PWMainLoopDestroy(loop unsafe.Pointer) {
121 _mock.Called(loop)626 _mock.Called(loop)
@@ -129,7 +634,7 @@ type MockCAPI_PWMainLoopDestroy_Call struct {
129 634
130 // PWMainLoopDestroy is a helper method to define mock.On call635 // PWMainLoopDestroy is a helper method to define mock.On call
131 // - loop unsafe.Pointer636 // - 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 {
133 return &MockCAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}638 return &MockCAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}
134 }639 }
135 640
@@ -180,7 +685,7 @@ type MockCAPI_PWMainLoopGetLoop_Call struct {
180 685
181 // PWMainLoopGetLoop is a helper method to define mock.On call686 // PWMainLoopGetLoop is a helper method to define mock.On call
182 // - loop unsafe.Pointer687 // - 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 {
184 return &MockCAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}689 return &MockCAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}
185 }690 }
186 691
@@ -231,7 +736,7 @@ type MockCAPI_PWMainLoopNew_Call struct {
231 736
232 // PWMainLoopNew is a helper method to define mock.On call737 // PWMainLoopNew is a helper method to define mock.On call
233 // - props unsafe.Pointer738 // - 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 {
235 return &MockCAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}740 return &MockCAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}
236 }741 }
237 742
@@ -282,7 +787,7 @@ type MockCAPI_PWMainLoopQuit_Call struct {
282 787
283 // PWMainLoopQuit is a helper method to define mock.On call788 // PWMainLoopQuit is a helper method to define mock.On call
284 // - loop unsafe.Pointer789 // - 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 {
286 return &MockCAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}791 return &MockCAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}
287 }792 }
288 793
@@ -333,7 +838,7 @@ type MockCAPI_PWMainLoopRun_Call struct {
333 838
334 // PWMainLoopRun is a helper method to define mock.On call839 // PWMainLoopRun is a helper method to define mock.On call
335 // - loop unsafe.Pointer840 // - 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 {
337 return &MockCAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}842 return &MockCAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}
338 }843 }
339 844
@@ -355,7 +860,58 @@ func (_c *MockCAPI_PWMainLoopRun_Call) Return(n int32) *MockCAPI_PWMainLoopRun_C
355 return _c860 return _c
356 }861 }
357 862
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 {
359 _c.Call.Return(run)915 _c.Call.Return(run)
360 return _c916 return _c
361 }917 }
@@ -387,7 +943,7 @@ type MockCAPI_PWStreamAddListener_Call struct {
387 // - listener unsafe.Pointer943 // - listener unsafe.Pointer
388 // - events unsafe.Pointer944 // - events unsafe.Pointer
389 // - data unsafe.Pointer945 // - 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 {
391 return &MockCAPI_PWStreamAddListener_Call{Call: _e.mock.On("PWStreamAddListener", stream, listener, events, data)}947 return &MockCAPI_PWStreamAddListener_Call{Call: _e.mock.On("PWStreamAddListener", stream, listener, events, data)}
392 }948 }
393 949
@@ -458,7 +1014,7 @@ type MockCAPI_PWStreamConnect_Call struct {
458 // - flags uint321014 // - flags uint32
459 // - ports unsafe.Pointer1015 // - ports unsafe.Pointer
460 // - n_ports uint321016 // - 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 {
462 return &MockCAPI_PWStreamConnect_Call{Call: _e.mock.On("PWStreamConnect", stream, direction, id, flags, ports, n_ports)}1018 return &MockCAPI_PWStreamConnect_Call{Call: _e.mock.On("PWStreamConnect", stream, direction, id, flags, ports, n_ports)}
463 }1019 }
464 1020
@@ -534,7 +1090,7 @@ type MockCAPI_PWStreamDequeueBuffer_Call struct {
534 1090
535 // PWStreamDequeueBuffer is a helper method to define mock.On call1091 // PWStreamDequeueBuffer is a helper method to define mock.On call
536 // - stream unsafe.Pointer1092 // - 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 {
538 return &MockCAPI_PWStreamDequeueBuffer_Call{Call: _e.mock.On("PWStreamDequeueBuffer", stream)}1094 return &MockCAPI_PWStreamDequeueBuffer_Call{Call: _e.mock.On("PWStreamDequeueBuffer", stream)}
539 }1095 }
540 1096
@@ -574,7 +1130,7 @@ type MockCAPI_PWStreamDestroy_Call struct {
574 1130
575 // PWStreamDestroy is a helper method to define mock.On call1131 // PWStreamDestroy is a helper method to define mock.On call
576 // - stream unsafe.Pointer1132 // - 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 {
578 return &MockCAPI_PWStreamDestroy_Call{Call: _e.mock.On("PWStreamDestroy", stream)}1134 return &MockCAPI_PWStreamDestroy_Call{Call: _e.mock.On("PWStreamDestroy", stream)}
579 }1135 }
580 1136
@@ -625,7 +1181,7 @@ type MockCAPI_PWStreamDisconnect_Call struct {
625 1181
626 // PWStreamDisconnect is a helper method to define mock.On call1182 // PWStreamDisconnect is a helper method to define mock.On call
627 // - stream unsafe.Pointer1183 // - 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 {
629 return &MockCAPI_PWStreamDisconnect_Call{Call: _e.mock.On("PWStreamDisconnect", stream)}1185 return &MockCAPI_PWStreamDisconnect_Call{Call: _e.mock.On("PWStreamDisconnect", stream)}
630 }1186 }
631 1187
@@ -652,6 +1208,171 @@ func (_c *MockCAPI_PWStreamDisconnect_Call) RunAndReturn(run func(stream unsafe.
652 return _c1208 return _c
653 }1209 }
654 1210
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+
655 // PWStreamNewSimple provides a mock function for the type MockCAPI1376 // PWStreamNewSimple provides a mock function for the type MockCAPI
656 func (_mock *MockCAPI) PWStreamNewSimple(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) unsafe.Pointer {1377 func (_mock *MockCAPI) PWStreamNewSimple(context unsafe.Pointer, name *byte, props unsafe.Pointer, events unsafe.Pointer, data unsafe.Pointer) unsafe.Pointer {
657 ret := _mock.Called(context, name, props, events, data)1378 ret := _mock.Called(context, name, props, events, data)
@@ -680,7 +1401,7 @@ type MockCAPI_PWStreamNewSimple_Call struct {
680 // - props unsafe.Pointer1401 // - props unsafe.Pointer
681 // - events unsafe.Pointer1402 // - events unsafe.Pointer
682 // - data unsafe.Pointer1403 // - 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 {
684 return &MockCAPI_PWStreamNewSimple_Call{Call: _e.mock.On("PWStreamNewSimple", context, name, props, events, data)}1405 return &MockCAPI_PWStreamNewSimple_Call{Call: _e.mock.On("PWStreamNewSimple", context, name, props, events, data)}
685 }1406 }
686 1407
@@ -752,7 +1473,7 @@ type MockCAPI_PWStreamQueueBuffer_Call struct {
752 // PWStreamQueueBuffer is a helper method to define mock.On call1473 // PWStreamQueueBuffer is a helper method to define mock.On call
753 // - stream unsafe.Pointer1474 // - stream unsafe.Pointer
754 // - buffer unsafe.Pointer1475 // - 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 {
756 return &MockCAPI_PWStreamQueueBuffer_Call{Call: _e.mock.On("PWStreamQueueBuffer", stream, buffer)}1477 return &MockCAPI_PWStreamQueueBuffer_Call{Call: _e.mock.On("PWStreamQueueBuffer", stream, buffer)}
757 }1478 }
758 1479
@@ -784,6 +1505,63 @@ func (_c *MockCAPI_PWStreamQueueBuffer_Call) RunAndReturn(run func(stream unsafe
784 return _c1505 return _c
785 }1506 }
786 1507
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+
787 // PWStreamSetActive provides a mock function for the type MockCAPI1565 // PWStreamSetActive provides a mock function for the type MockCAPI
788 func (_mock *MockCAPI) PWStreamSetActive(stream unsafe.Pointer, active bool) int32 {1566 func (_mock *MockCAPI) PWStreamSetActive(stream unsafe.Pointer, active bool) int32 {
789 ret := _mock.Called(stream, active)1567 ret := _mock.Called(stream, active)
@@ -809,7 +1587,7 @@ type MockCAPI_PWStreamSetActive_Call struct {
809 // PWStreamSetActive is a helper method to define mock.On call1587 // PWStreamSetActive is a helper method to define mock.On call
810 // - stream unsafe.Pointer1588 // - stream unsafe.Pointer
811 // - active bool1589 // - 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 {
813 return &MockCAPI_PWStreamSetActive_Call{Call: _e.mock.On("PWStreamSetActive", stream, active)}1591 return &MockCAPI_PWStreamSetActive_Call{Call: _e.mock.On("PWStreamSetActive", stream, active)}
814 }1592 }
815 1593
@@ -840,3 +1618,117 @@ func (_c *MockCAPI_PWStreamSetActive_Call) RunAndReturn(run func(stream unsafe.P
840 _c.Call.Return(run)1618 _c.Call.Return(run)
841 return _c1619 return _c
842 }1620 }
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 {
14 mock.TestingT14 mock.TestingT
15 Cleanup(func())15 Cleanup(func())
16 }) *MockInitAPI {16 }) *MockInitAPI {
17+ if helper, ok := t.(interface{ Helper() }); ok {
18+ helper.Helper()
19+ }
20+
17 mock := &MockInitAPI{}21 mock := &MockInitAPI{}
18 mock.Mock.Test(t)22 mock.Mock.Test(t)
19 23
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+ })
21 30
22 return mock31 return mock
23 }32 }
@@ -82,7 +91,7 @@ type MockInitAPI_PWInit_Call struct {
82 // PWInit is a helper method to define mock.On call91 // PWInit is a helper method to define mock.On call
83 // - argc *int3292 // - argc *int32
84 // - argv ***byte93 // - 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 {
86 return &MockInitAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}95 return &MockInitAPI_PWInit_Call{Call: _e.mock.On("PWInit", argc, argv)}
87 }96 }
88 97
M internal/ports/out/mocks/mock_LoopAPI.go +15 -6
@@ -16,10 +16,19 @@ func NewMockLoopAPI(t interface {
16 mock.TestingT16 mock.TestingT
17 Cleanup(func())17 Cleanup(func())
18 }) *MockLoopAPI {18 }) *MockLoopAPI {
19+ if helper, ok := t.(interface{ Helper() }); ok {
20+ helper.Helper()
21+ }
22+
19 mock := &MockLoopAPI{}23 mock := &MockLoopAPI{}
20 mock.Mock.Test(t)24 mock.Mock.Test(t)
21 25
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+ })
23 32
24 return mock33 return mock
25 }34 }
@@ -50,7 +59,7 @@ type MockLoopAPI_PWMainLoopDestroy_Call struct {
50 59
51 // PWMainLoopDestroy is a helper method to define mock.On call60 // PWMainLoopDestroy is a helper method to define mock.On call
52 // - loop unsafe.Pointer61 // - 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 {
54 return &MockLoopAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}63 return &MockLoopAPI_PWMainLoopDestroy_Call{Call: _e.mock.On("PWMainLoopDestroy", loop)}
55 }64 }
56 65
@@ -101,7 +110,7 @@ type MockLoopAPI_PWMainLoopGetLoop_Call struct {
101 110
102 // PWMainLoopGetLoop is a helper method to define mock.On call111 // PWMainLoopGetLoop is a helper method to define mock.On call
103 // - loop unsafe.Pointer112 // - 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 {
105 return &MockLoopAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}114 return &MockLoopAPI_PWMainLoopGetLoop_Call{Call: _e.mock.On("PWMainLoopGetLoop", loop)}
106 }115 }
107 116
@@ -152,7 +161,7 @@ type MockLoopAPI_PWMainLoopNew_Call struct {
152 161
153 // PWMainLoopNew is a helper method to define mock.On call162 // PWMainLoopNew is a helper method to define mock.On call
154 // - props unsafe.Pointer163 // - 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 {
156 return &MockLoopAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}165 return &MockLoopAPI_PWMainLoopNew_Call{Call: _e.mock.On("PWMainLoopNew", props)}
157 }166 }
158 167
@@ -203,7 +212,7 @@ type MockLoopAPI_PWMainLoopQuit_Call struct {
203 212
204 // PWMainLoopQuit is a helper method to define mock.On call213 // PWMainLoopQuit is a helper method to define mock.On call
205 // - loop unsafe.Pointer214 // - 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 {
207 return &MockLoopAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}216 return &MockLoopAPI_PWMainLoopQuit_Call{Call: _e.mock.On("PWMainLoopQuit", loop)}
208 }217 }
209 218
@@ -254,7 +263,7 @@ type MockLoopAPI_PWMainLoopRun_Call struct {
254 263
255 // PWMainLoopRun is a helper method to define mock.On call264 // PWMainLoopRun is a helper method to define mock.On call
256 // - loop unsafe.Pointer265 // - 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 {
258 return &MockLoopAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}267 return &MockLoopAPI_PWMainLoopRun_Call{Call: _e.mock.On("PWMainLoopRun", loop)}
259 }268 }
260 269
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 {
17 mock.TestingT17 mock.TestingT
18 Cleanup(func())18 Cleanup(func())
19 }) *MockStreamOps {19 }) *MockStreamOps {
20+ if helper, ok := t.(interface{ Helper() }); ok {
21+ helper.Helper()
22+ }
23+
20 mock := &MockStreamOps{}24 mock := &MockStreamOps{}
21 mock.Mock.Test(t)25 mock.Mock.Test(t)
22 26
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+ })
24 33
25 return mock34 return mock
26 }35 }
@@ -63,7 +72,7 @@ type MockStreamOps_ConnectPlaybackStream_Call struct {
63 // ConnectPlaybackStream is a helper method to define mock.On call72 // ConnectPlaybackStream is a helper method to define mock.On call
64 // - streamPtr unsafe.Pointer73 // - streamPtr unsafe.Pointer
65 // - format out.PlaybackFormat74 // - 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 {
67 return &MockStreamOps_ConnectPlaybackStream_Call{Call: _e.mock.On("ConnectPlaybackStream", streamPtr, format)}76 return &MockStreamOps_ConnectPlaybackStream_Call{Call: _e.mock.On("ConnectPlaybackStream", streamPtr, format)}
68 }77 }
69 78
@@ -183,7 +192,7 @@ type MockStreamOps_CreatePlaybackStream_Call struct {
183 // - loopPtr unsafe.Pointer192 // - loopPtr unsafe.Pointer
184 // - name string193 // - name string
185 // - onProcess func()194 // - 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 {
187 return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, onProcess)}196 return &MockStreamOps_CreatePlaybackStream_Call{Call: _e.mock.On("CreatePlaybackStream", loopPtr, name, onProcess)}
188 }197 }
189 198
@@ -244,7 +253,7 @@ type MockStreamOps_DequeueBuffer_Call struct {
244 253
245 // DequeueBuffer is a helper method to define mock.On call254 // DequeueBuffer is a helper method to define mock.On call
246 // - streamPtr unsafe.Pointer255 // - 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 {
248 return &MockStreamOps_DequeueBuffer_Call{Call: _e.mock.On("DequeueBuffer", streamPtr)}257 return &MockStreamOps_DequeueBuffer_Call{Call: _e.mock.On("DequeueBuffer", streamPtr)}
249 }258 }
250 259
@@ -284,7 +293,7 @@ type MockStreamOps_DestroyMainLoop_Call struct {
284 293
285 // DestroyMainLoop is a helper method to define mock.On call294 // DestroyMainLoop is a helper method to define mock.On call
286 // - loopPtr unsafe.Pointer295 // - 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 {
288 return &MockStreamOps_DestroyMainLoop_Call{Call: _e.mock.On("DestroyMainLoop", loopPtr)}297 return &MockStreamOps_DestroyMainLoop_Call{Call: _e.mock.On("DestroyMainLoop", loopPtr)}
289 }298 }
290 299
@@ -324,7 +333,7 @@ type MockStreamOps_DestroyStream_Call struct {
324 333
325 // DestroyStream is a helper method to define mock.On call334 // DestroyStream is a helper method to define mock.On call
326 // - streamPtr unsafe.Pointer335 // - 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 {
328 return &MockStreamOps_DestroyStream_Call{Call: _e.mock.On("DestroyStream", streamPtr)}337 return &MockStreamOps_DestroyStream_Call{Call: _e.mock.On("DestroyStream", streamPtr)}
329 }338 }
330 339
@@ -375,7 +384,7 @@ type MockStreamOps_DisconnectStream_Call struct {
375 384
376 // DisconnectStream is a helper method to define mock.On call385 // DisconnectStream is a helper method to define mock.On call
377 // - streamPtr unsafe.Pointer386 // - 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 {
379 return &MockStreamOps_DisconnectStream_Call{Call: _e.mock.On("DisconnectStream", streamPtr)}388 return &MockStreamOps_DisconnectStream_Call{Call: _e.mock.On("DisconnectStream", streamPtr)}
380 }389 }
381 390
@@ -427,7 +436,7 @@ type MockStreamOps_QueueBuffer_Call struct {
427 // QueueBuffer is a helper method to define mock.On call436 // QueueBuffer is a helper method to define mock.On call
428 // - streamPtr unsafe.Pointer437 // - streamPtr unsafe.Pointer
429 // - bufPtr unsafe.Pointer438 // - 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 {
431 return &MockStreamOps_QueueBuffer_Call{Call: _e.mock.On("QueueBuffer", streamPtr, bufPtr)}440 return &MockStreamOps_QueueBuffer_Call{Call: _e.mock.On("QueueBuffer", streamPtr, bufPtr)}
432 }441 }
433 442
@@ -472,7 +481,7 @@ type MockStreamOps_QuitMainLoop_Call struct {
472 481
473 // QuitMainLoop is a helper method to define mock.On call482 // QuitMainLoop is a helper method to define mock.On call
474 // - loopPtr unsafe.Pointer483 // - 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 {
476 return &MockStreamOps_QuitMainLoop_Call{Call: _e.mock.On("QuitMainLoop", loopPtr)}485 return &MockStreamOps_QuitMainLoop_Call{Call: _e.mock.On("QuitMainLoop", loopPtr)}
477 }486 }
478 487
@@ -523,7 +532,7 @@ type MockStreamOps_RunMainLoop_Call struct {
523 532
524 // RunMainLoop is a helper method to define mock.On call533 // RunMainLoop is a helper method to define mock.On call
525 // - loopPtr unsafe.Pointer534 // - 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 {
527 return &MockStreamOps_RunMainLoop_Call{Call: _e.mock.On("RunMainLoop", loopPtr)}536 return &MockStreamOps_RunMainLoop_Call{Call: _e.mock.On("RunMainLoop", loopPtr)}
528 }537 }
529 538
@@ -575,7 +584,7 @@ type MockStreamOps_SetStreamActive_Call struct {
575 // SetStreamActive is a helper method to define mock.On call584 // SetStreamActive is a helper method to define mock.On call
576 // - streamPtr unsafe.Pointer585 // - streamPtr unsafe.Pointer
577 // - active bool586 // - 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 {
579 return &MockStreamOps_SetStreamActive_Call{Call: _e.mock.On("SetStreamActive", streamPtr, active)}588 return &MockStreamOps_SetStreamActive_Call{Call: _e.mock.On("SetStreamActive", streamPtr, active)}
580 }589 }
581 590
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 generated file: collapsed
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 @@
1 package pipewire1 package pipewire
2 2
3 // Context is the public interface for PipeWire context operations.3 // 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.
5 type Context interface {6 type Context interface {
6 Destroy()7 Destroy()
7 Connect() (Core, error)8 Connect() (Core, error)
M pipewire/core.go +4 -0
@@ -7,7 +7,11 @@ import (
7 // Core is the public interface for PipeWire core connection operations.7 // Core is the public interface for PipeWire core connection operations.
8 // A Core represents a connection to the PipeWire server.8 // A Core represents a connection to the PipeWire server.
9 type Core interface {9 type Core interface {
10+ // Disconnect disconnects from the server. Destroy streams first.
10 Disconnect()11 Disconnect()
11 Registry() (Registry, error)12 Registry() (Registry, error)
12 NewStream(name string, props unsafe.Pointer) (Stream, error)13 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)
13 }17 }
M pipewire/interfaces.go +5 -0
@@ -5,6 +5,11 @@ type Runtime interface {
5 Init() error5 Init() error
6 Deinit()6 Deinit()
7 NewMainLoop() (MainLoop, error)7 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)
8 }13 }
9 14
10 // MainLoop is the public interface for PipeWire main loop operations.15 // 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 {
14 mock.TestingT14 mock.TestingT
15 Cleanup(func())15 Cleanup(func())
16 }) *MockMainLoop {16 }) *MockMainLoop {
17+ if helper, ok := t.(interface{ Helper() }); ok {
18+ helper.Helper()
19+ }
20+
17 mock := &MockMainLoop{}21 mock := &MockMainLoop{}
18 mock.Mock.Test(t)22 mock.Mock.Test(t)
19 23
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+ })
21 30
22 return mock31 return mock
23 }32 }
M pipewire/mocks/mock_Player.go +10 -1
@@ -15,10 +15,19 @@ func NewMockPlayer(t interface {
15 mock.TestingT15 mock.TestingT
16 Cleanup(func())16 Cleanup(func())
17 }) *MockPlayer {17 }) *MockPlayer {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
18 mock := &MockPlayer{}22 mock := &MockPlayer{}
19 mock.Mock.Test(t)23 mock.Mock.Test(t)
20 24
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+ })
22 31
23 return mock32 return mock
24 }33 }
M pipewire/mocks/mock_Runtime.go +127 -1
@@ -15,10 +15,19 @@ func NewMockRuntime(t interface {
15 mock.TestingT15 mock.TestingT
16 Cleanup(func())16 Cleanup(func())
17 }) *MockRuntime {17 }) *MockRuntime {
18+ if helper, ok := t.(interface{ Helper() }); ok {
19+ helper.Helper()
20+ }
21+
18 mock := &MockRuntime{}22 mock := &MockRuntime{}
19 mock.Mock.Test(t)23 mock.Mock.Test(t)
20 24
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+ })
22 31
23 return mock32 return mock
24 }33 }
@@ -113,6 +122,123 @@ func (_c *MockRuntime_Init_Call) RunAndReturn(run func() error) *MockRuntime_Ini
113 return _c122 return _c
114 }123 }
115 124
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+
116 // NewMainLoop provides a mock function for the type MockRuntime242 // NewMainLoop provides a mock function for the type MockRuntime
117 func (_mock *MockRuntime) NewMainLoop() (pipewire.MainLoop, error) {243 func (_mock *MockRuntime) NewMainLoop() (pipewire.MainLoop, error) {
118 ret := _mock.Called()244 ret := _mock.Called()
M pipewire/runtime.go +66 -0
@@ -2,6 +2,7 @@ package pipewire
2 2
3 import (3 import (
4 "errors"4 "errors"
5+ "unsafe"
5 6
6 "github.com/bnema/purego-pipewire/internal/capi"7 "github.com/bnema/purego-pipewire/internal/capi"
7 "github.com/bnema/purego-pipewire/internal/core"8 "github.com/bnema/purego-pipewire/internal/core"
@@ -28,6 +29,71 @@ func (rw runtimeWrapper) NewMainLoop() (MainLoop, error) {
28 return mainLoopWrapper{inner: ml}, nil29 return mainLoopWrapper{inner: ml}, nil
29 }30 }
30 31
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+
31 // mainLoopWrapper wraps the internal core.MainLoop to implement the public MainLoop interface.97 // mainLoopWrapper wraps the internal core.MainLoop to implement the public MainLoop interface.
32 type mainLoopWrapper struct {98 type mainLoopWrapper struct {
33 inner *core.MainLoop99 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 (
16 DirectionInput = 016 DirectionInput = 0
17 DirectionOutput = 117 DirectionOutput = 1
18 )18 )
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