pipewire 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +29 -9
  2. package/dist/bindings/context.d.ts +20 -0
  3. package/dist/bindings/context.d.ts.map +1 -0
  4. package/dist/bindings/context.js +12 -0
  5. package/dist/bindings/context.js.map +1 -0
  6. package/dist/bindings/core.d.ts +34 -0
  7. package/dist/bindings/core.d.ts.map +1 -0
  8. package/dist/bindings/core.js +36 -0
  9. package/dist/bindings/core.js.map +1 -0
  10. package/dist/bindings/dict.d.ts +3 -0
  11. package/dist/bindings/dict.d.ts.map +1 -0
  12. package/dist/bindings/dict.js +11 -0
  13. package/dist/bindings/dict.js.map +1 -0
  14. package/dist/bindings/hook.d.ts +3 -0
  15. package/dist/bindings/hook.d.ts.map +1 -0
  16. package/dist/bindings/hook.js +14 -0
  17. package/dist/bindings/hook.js.map +1 -0
  18. package/dist/bindings/index.d.ts +32 -0
  19. package/dist/bindings/index.d.ts.map +1 -0
  20. package/dist/bindings/index.js +16 -0
  21. package/dist/bindings/index.js.map +1 -0
  22. package/dist/bindings/list.d.ts +2 -0
  23. package/dist/bindings/list.d.ts.map +1 -0
  24. package/dist/bindings/list.js +7 -0
  25. package/dist/bindings/list.js.map +1 -0
  26. package/dist/bindings/loop.d.ts +33 -0
  27. package/dist/bindings/loop.d.ts.map +1 -0
  28. package/dist/bindings/loop.js +17 -0
  29. package/dist/bindings/loop.js.map +1 -0
  30. package/dist/bindings/main-loop.d.ts +47 -0
  31. package/dist/bindings/main-loop.d.ts.map +1 -0
  32. package/dist/bindings/main-loop.js +15 -0
  33. package/dist/bindings/main-loop.js.map +1 -0
  34. package/dist/bindings/node.d.ts +50 -0
  35. package/dist/bindings/node.d.ts.map +1 -0
  36. package/dist/bindings/node.js +81 -0
  37. package/dist/bindings/node.js.map +1 -0
  38. package/dist/bindings/param.d.ts +25 -0
  39. package/dist/bindings/param.d.ts.map +1 -0
  40. package/dist/bindings/param.js +33 -0
  41. package/dist/bindings/param.js.map +1 -0
  42. package/dist/bindings/pipewire.d.ts +19 -0
  43. package/dist/bindings/pipewire.d.ts.map +1 -0
  44. package/dist/bindings/pipewire.js +5 -0
  45. package/dist/bindings/pipewire.js.map +1 -0
  46. package/dist/bindings/pod.d.ts +2 -0
  47. package/dist/bindings/pod.d.ts.map +1 -0
  48. package/dist/bindings/pod.js +6 -0
  49. package/dist/bindings/pod.js.map +1 -0
  50. package/dist/bindings/properties.d.ts +2 -0
  51. package/dist/bindings/properties.d.ts.map +1 -0
  52. package/dist/bindings/properties.js +3 -0
  53. package/dist/bindings/properties.js.map +1 -0
  54. package/dist/bindings/props.d.ts +59 -0
  55. package/dist/bindings/props.d.ts.map +1 -0
  56. package/dist/bindings/props.js +59 -0
  57. package/dist/bindings/props.js.map +1 -0
  58. package/dist/bindings/proxy.d.ts +11 -0
  59. package/dist/bindings/proxy.d.ts.map +1 -0
  60. package/dist/bindings/proxy.js +5 -0
  61. package/dist/bindings/proxy.js.map +1 -0
  62. package/dist/bindings/registry.d.ts +20 -0
  63. package/dist/bindings/registry.d.ts.map +1 -0
  64. package/dist/bindings/registry.js +19 -0
  65. package/dist/bindings/registry.js.map +1 -0
  66. package/dist/bindings/thread-loop.d.ts +65 -0
  67. package/dist/bindings/thread-loop.d.ts.map +1 -0
  68. package/dist/bindings/thread-loop.js +18 -0
  69. package/dist/bindings/thread-loop.js.map +1 -0
  70. package/dist/index.d.ts +5 -13
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +5 -13
  73. package/dist/index.js.map +1 -1
  74. package/dist/loop.d.ts +27 -0
  75. package/dist/loop.d.ts.map +1 -1
  76. package/dist/loop.js +4 -0
  77. package/dist/loop.js.map +1 -1
  78. package/dist/node.d.ts +53 -39
  79. package/dist/node.d.ts.map +1 -1
  80. package/dist/node.js +104 -69
  81. package/dist/node.js.map +1 -1
  82. package/dist/object.d.ts +18 -0
  83. package/dist/object.d.ts.map +1 -0
  84. package/dist/object.js +22 -0
  85. package/dist/object.js.map +1 -0
  86. package/dist/pipewire.d.ts +50 -15
  87. package/dist/pipewire.d.ts.map +1 -1
  88. package/dist/pipewire.js +94 -3
  89. package/dist/pipewire.js.map +1 -1
  90. package/dist/thread-loop.d.ts +65 -0
  91. package/dist/thread-loop.d.ts.map +1 -0
  92. package/dist/thread-loop.js +18 -0
  93. package/dist/thread-loop.js.map +1 -0
  94. package/examples/basic.ts +20 -0
  95. package/examples/{event-registry.ts → event-registry-async.ts} +13 -6
  96. package/examples/event-registry-sync.ts +60 -0
  97. package/examples/node-events.ts +34 -0
  98. package/package.json +1 -1
  99. package/src/{core.ts → bindings/core.ts} +8 -6
  100. package/src/bindings/index.ts +16 -0
  101. package/src/{loop.ts → bindings/loop.ts} +6 -0
  102. package/src/bindings/node.ts +92 -0
  103. package/src/bindings/param.ts +33 -0
  104. package/src/bindings/pipewire.ts +6 -0
  105. package/src/{registry.ts → bindings/registry.ts} +2 -0
  106. package/src/bindings/thread-loop.ts +23 -0
  107. package/src/index.ts +5 -13
  108. package/src/node.ts +167 -79
  109. package/src/object.ts +36 -0
  110. package/src/pipewire.ts +154 -3
  111. package/src/param.ts +0 -33
  112. /package/src/{context.ts → bindings/context.ts} +0 -0
  113. /package/src/{dict.ts → bindings/dict.ts} +0 -0
  114. /package/src/{hook.ts → bindings/hook.ts} +0 -0
  115. /package/src/{list.ts → bindings/list.ts} +0 -0
  116. /package/src/{main-loop.ts → bindings/main-loop.ts} +0 -0
  117. /package/src/{pod.ts → bindings/pod.ts} +0 -0
  118. /package/src/{properties.ts → bindings/properties.ts} +0 -0
  119. /package/src/{props.ts → bindings/props.ts} +0 -0
  120. /package/src/{proxy.ts → bindings/proxy.ts} +0 -0
@@ -0,0 +1,60 @@
1
+ import koffi from "koffi";
2
+ import { bindings as pw } from "../src/index.js";
3
+
4
+ (async () => {
5
+ pw.pipewire.pw_init(0, null);
6
+
7
+ const mainLoop = pw.main_loop.pw_main_loop_new(null);
8
+ const loop = pw.main_loop.pw_main_loop_get_loop(mainLoop);
9
+ const context = pw.context.pw_context_new(loop, null, 0);
10
+ const core = pw.core.pw_context_connect(context, null, 0);
11
+ const registry = pw.core.pw_core_get_registry(core, 3, 0);
12
+
13
+ const registry_event_global = (
14
+ data: unknown,
15
+ id: number,
16
+ permissions: number,
17
+ type: string,
18
+ version: number,
19
+ props: unknown
20
+ ) => {
21
+ console.log(`New object: id: ${id} type: ${type} version: ${version}`);
22
+ };
23
+
24
+ const registry_event_global_remove = (data: unknown, id: number) => {
25
+ console.log(`Removed object: id: ${id}`);
26
+ };
27
+
28
+ const registry_listener = Buffer.alloc(koffi.sizeof(pw.hook.spa_hook));
29
+ const registry_events = Buffer.alloc(koffi.sizeof(pw.core.pw_registry_events));
30
+ const registry_event_global_register = koffi.register(
31
+ registry_event_global,
32
+ koffi.pointer(pw.core.pw_registry_events_global)
33
+ );
34
+ const registry_event_global_remove_register = koffi.register(
35
+ registry_event_global_remove,
36
+ koffi.pointer(pw.core.pw_registry_events_global_remove)
37
+ );
38
+ koffi.encode(registry_events, pw.core.pw_registry_events, {
39
+ version: 0,
40
+ global: registry_event_global_register,
41
+ global_remove: registry_event_global_remove_register
42
+ });
43
+ pw.registry.pw_registry_add_listener(registry, registry_listener, registry_events, null);
44
+
45
+ console.log("Starting PipeWire main loop...");
46
+ pw.loop.pw_loop_enter(loop);
47
+ const interval = setInterval(() => pw.loop.pw_loop_iterate(loop, 1), 10);
48
+ await new Promise((resolve) => process.on("SIGINT", resolve));
49
+ clearInterval(interval);
50
+ pw.loop.pw_loop_leave(loop);
51
+
52
+ pw.proxy.pw_proxy_destroy(registry);
53
+ koffi.unregister(registry_event_global_register);
54
+ koffi.unregister(registry_event_global_remove_register);
55
+ pw.core.pw_core_disconnect(core);
56
+ pw.context.pw_context_destroy(context);
57
+ pw.main_loop.pw_main_loop_destroy(mainLoop);
58
+
59
+ console.log("PipeWire main loop exited, cleanup done.");
60
+ })();
@@ -0,0 +1,34 @@
1
+ import { Pipewire, PipewireNode } from "../src/index.js";
2
+
3
+ const pipewire = new Pipewire();
4
+
5
+ pipewire.on("objectAdded", (obj) => {
6
+ if (!(obj instanceof PipewireNode)) return;
7
+ console.log(`Node added: id: ${obj.id} name: ${obj.nodeName}`);
8
+
9
+ obj.on("nodeInfo", (info) => {
10
+ console.log(`Node info for id ${obj.id}: ${info.changes.join(", ")}`);
11
+ });
12
+ obj.on("rawNodeParam", () => {
13
+ console.log(`Node param for id ${obj.id}`);
14
+ });
15
+
16
+ setImmediate(() => {
17
+ obj.attachListener();
18
+ obj.subscribeParams(Object.values(PipewireNode.spa_param_type).filter((v) => typeof v === "number"));
19
+ });
20
+ });
21
+
22
+ pipewire.on("objectRemoved", (obj) => {
23
+ if (!(obj instanceof PipewireNode)) return;
24
+ console.log(`Node removed: id: ${obj.id} name: ${obj.nodeName}`);
25
+ });
26
+
27
+ console.log("Starting PipeWire main loop...");
28
+ pipewire.startLoop();
29
+
30
+ process.on("SIGINT", () => {
31
+ pipewire.stopLoop();
32
+ pipewire.deinit();
33
+ console.log("PipeWire main loop exited, cleanup done.");
34
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipewire",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -4,19 +4,21 @@ import { pw_properties } from "./properties.js";
4
4
 
5
5
  const pipewire = koffi.load("libpipewire-0.3.so.0");
6
6
 
7
+ export const PW_VERSION_REGISTRY = 3;
8
+
7
9
  export const pw_core = koffi.opaque("pw_core");
8
10
  export const pw_registry = koffi.opaque("pw_registry");
9
11
 
10
12
  export const pw_context_connect = pipewire.func("pw_context_connect", koffi.pointer(pw_core), [
11
- koffi.pointer(pw_context),
12
- koffi.pointer(pw_properties),
13
- "size_t"
13
+ koffi.pointer(pw_context), // context
14
+ koffi.pointer(pw_properties), // properties
15
+ "size_t" // user_data_size
14
16
  ]);
15
17
 
16
18
  export const pw_core_get_registry = pipewire.func("pw_core_get_registry", koffi.pointer(pw_registry), [
17
- koffi.pointer(pw_core),
18
- "uint32_t",
19
- "size_t"
19
+ koffi.pointer(pw_core), // core
20
+ "uint32_t", // version
21
+ "size_t" // user_data_size
20
22
  ]);
21
23
 
22
24
  export const pw_registry_events_global = koffi.proto("void", [
@@ -0,0 +1,16 @@
1
+ import * as context from "./context.js";
2
+ import * as core from "./core.js";
3
+ import * as dict from "./dict.js";
4
+ import * as hook from "./hook.js";
5
+ import * as loop from "./loop.js";
6
+ import * as main_loop from "./main-loop.js";
7
+ import * as node from "./node.js";
8
+ import * as param from "./param.js";
9
+ import * as pipewire from "./pipewire.js";
10
+ import * as pod from "./pod.js";
11
+ import * as props from "./props.js";
12
+ import * as proxy from "./proxy.js";
13
+ import * as registry from "./registry.js";
14
+ import * as thread_loop from "./thread-loop.js";
15
+
16
+ export default { context, core, dict, hook, loop, main_loop, node, param, pod, props, pipewire, proxy, registry, thread_loop };
@@ -1,5 +1,7 @@
1
1
  import koffi from "koffi";
2
2
 
3
+ const pipewire = koffi.load("libpipewire-0.3.so.0");
4
+
3
5
  export const spa_system = koffi.opaque("spa_system");
4
6
  export const spa_loop = koffi.opaque("spa_loop");
5
7
  export const spa_loop_control = koffi.opaque("spa_loop_control");
@@ -13,3 +15,7 @@ export const pw_loop = koffi.struct("pw_loop", {
13
15
  utils: koffi.pointer(spa_loop_utils),
14
16
  name: "const char*"
15
17
  });
18
+
19
+ export const pw_loop_enter = pipewire.func("pw_loop_enter", "void", [koffi.pointer(pw_loop)]);
20
+ export const pw_loop_iterate = pipewire.func("pw_loop_iterate", "int", [koffi.pointer(pw_loop), "int"]);
21
+ export const pw_loop_leave = pipewire.func("pw_loop_leave", "void", [koffi.pointer(pw_loop)]);
@@ -0,0 +1,92 @@
1
+ import koffi from "koffi";
2
+ import { spa_hook } from "./hook.js";
3
+ import { spa_pod } from "./pod.js";
4
+ import { spa_dict } from "./dict.js";
5
+ import { spa_param_info } from "./param.js";
6
+
7
+ const pipewire = koffi.load("libpipewire-0.3.so.0");
8
+
9
+ export const PW_VERSION_NODE = 3;
10
+ export const PW_VERSION_NODE_EVENTS = 0;
11
+
12
+ export const PW_NODE_CHANGE_MASK = {
13
+ INPUT_PORTS: 1 << 0,
14
+ OUTPUT_PORTS: 1 << 1,
15
+ STATE: 1 << 2,
16
+ PROPS: 1 << 3,
17
+ PARAMS: 1 << 4
18
+ };
19
+
20
+ export enum pw_node_state {
21
+ PW_NODE_STATE_ERROR = -1,
22
+ PW_NODE_STATE_CREATING = 0,
23
+ PW_NODE_STATE_SUSPENDED = 1,
24
+ PW_NODE_STATE_IDLE = 2,
25
+ PW_NODE_STATE_RUNNING = 3
26
+ }
27
+
28
+ export const pw_node = koffi.opaque("pw_node");
29
+
30
+ export const pw_node_state_enum = koffi.enumeration("pw_node_state", {
31
+ PW_NODE_STATE_ERROR: -1,
32
+ PW_NODE_STATE_CREATING: 0,
33
+ PW_NODE_STATE_SUSPENDED: 1,
34
+ PW_NODE_STATE_IDLE: 2,
35
+ PW_NODE_STATE_RUNNING: 3
36
+ });
37
+
38
+ export const pw_node_info = koffi.struct("pw_node_info", {
39
+ id: "uint32_t",
40
+ max_input_ports: "uint32_t",
41
+ max_output_ports: "uint32_t",
42
+ change_mask: "uint64_t",
43
+ n_input_ports: "uint32_t",
44
+ n_output_ports: "uint32_t",
45
+ state: pw_node_state_enum,
46
+ error: "const char*",
47
+ props: koffi.pointer(spa_dict),
48
+ params: koffi.pointer(spa_param_info),
49
+ n_params: "uint32_t"
50
+ });
51
+
52
+ export const pw_node_events_info = koffi.proto("void", [
53
+ "void*", // data
54
+ koffi.pointer(pw_node_info) // info
55
+ ]);
56
+
57
+ export const pw_node_events_param = koffi.proto("void", [
58
+ "void*", // data
59
+ "int", // seq
60
+ "uint32_t", // id
61
+ "uint32_t", // index
62
+ "uint32_t", // next
63
+ koffi.pointer(spa_pod) // param
64
+ ]);
65
+
66
+ export const pw_node_events = koffi.struct("pw_node_events", {
67
+ version: "uint32_t",
68
+ info: koffi.pointer(pw_node_events_info),
69
+ param: koffi.pointer(pw_node_events_param)
70
+ });
71
+
72
+ export const pw_node_add_listener = pipewire.func("pw_node_add_listener", "int", [
73
+ koffi.pointer(pw_node), // object
74
+ koffi.pointer(spa_hook), // listener
75
+ koffi.pointer(pw_node_events), // events
76
+ "void*" // data
77
+ ]);
78
+
79
+ export const pw_node_enum_params = pipewire.func("pw_node_enum_params", "int", [
80
+ koffi.pointer(pw_node), // object
81
+ "int", // seq
82
+ "uint32_t", // id
83
+ "uint32_t", // start
84
+ "uint32_t", // num
85
+ koffi.pointer(spa_pod) // filter
86
+ ]);
87
+
88
+ export const pw_node_subscribe_params = pipewire.func("pw_node_subscribe_params", "int", [
89
+ koffi.pointer(pw_node), // object
90
+ "uint32_t*", // ids
91
+ "uint32_t" // n_ids
92
+ ]);
@@ -0,0 +1,33 @@
1
+ import koffi from "koffi";
2
+
3
+ export const spa_param_info = koffi.struct("spa_param_info", {
4
+ id: "uint32_t",
5
+ flags: "uint32_t",
6
+ user: "uint32_t",
7
+ seq: "int32_t",
8
+ padding: koffi.array("uint32_t", 4)
9
+ });
10
+
11
+ export enum spa_param_type {
12
+ SPA_PARAM_Invalid,
13
+ SPA_PARAM_PropInfo,
14
+ SPA_PARAM_Props,
15
+ SPA_PARAM_EnumFormat,
16
+ SPA_PARAM_Format,
17
+ SPA_PARAM_Buffers,
18
+ SPA_PARAM_Meta,
19
+ SPA_PARAM_IO,
20
+ SPA_PARAM_EnumProfile,
21
+ SPA_PARAM_Profile,
22
+ SPA_PARAM_EnumPortConfig,
23
+ SPA_PARAM_PortConfig,
24
+ SPA_PARAM_EnumRoute,
25
+ SPA_PARAM_Route,
26
+ SPA_PARAM_Control,
27
+ SPA_PARAM_Latency,
28
+ SPA_PARAM_ProcessLatency,
29
+ SPA_PARAM_Tag,
30
+ SPA_PARAM_PeerEnumFormat,
31
+ SPA_PARAM_Capability,
32
+ SPA_PARAM_PeerCapability
33
+ }
@@ -0,0 +1,6 @@
1
+ import koffi from "koffi";
2
+
3
+ const pipewire = koffi.load("libpipewire-0.3.so.0");
4
+
5
+ export const pw_init = pipewire.func("pw_init", "void", ["int**", "char***"]);
6
+ export const pw_deinit = pipewire.func("pw_deinit", "void", []);
@@ -4,6 +4,8 @@ import { spa_hook } from "./hook.js";
4
4
 
5
5
  const pipewire = koffi.load("libpipewire-0.3.so.0");
6
6
 
7
+ export const PW_VERSION_REGISTRY_EVENTS = 0;
8
+
7
9
  export const pw_registry_add_listener = pipewire.func("pw_registry_add_listener", "int", [
8
10
  koffi.pointer(pw_registry), // registry
9
11
  koffi.pointer(spa_hook), // listener
@@ -0,0 +1,23 @@
1
+ import koffi from "koffi";
2
+ import { spa_dict } from "./dict.js";
3
+ import { pw_loop } from "./loop.js";
4
+
5
+ const pipewire = koffi.load("libpipewire-0.3.so.0");
6
+
7
+ export const pw_thread_loop = koffi.opaque("pw_thread_loop");
8
+
9
+ export const pw_thread_loop_new = pipewire.func("pw_thread_loop_new", koffi.pointer(pw_thread_loop), [
10
+ "const char*", // name
11
+ koffi.pointer(spa_dict) // props
12
+ ]);
13
+ export const pw_thread_loop_destroy = pipewire.func("pw_thread_loop_destroy", "void", [koffi.pointer(pw_thread_loop)]);
14
+
15
+ export const pw_thread_loop_get_loop = pipewire.func("pw_thread_loop_get_loop", koffi.pointer(pw_loop), [
16
+ koffi.pointer(pw_thread_loop)
17
+ ]);
18
+
19
+ export const pw_thread_loop_lock = pipewire.func("pw_thread_loop_lock", "void", [koffi.pointer(pw_thread_loop)]);
20
+ export const pw_thread_loop_unlock = pipewire.func("pw_thread_loop_unlock", "void", [koffi.pointer(pw_thread_loop)]);
21
+
22
+ export const pw_thread_loop_start = pipewire.func("pw_thread_loop_start", "int", [koffi.pointer(pw_thread_loop)]);
23
+ export const pw_thread_loop_stop = pipewire.func("pw_thread_loop_stop", "void", [koffi.pointer(pw_thread_loop)]);
package/src/index.ts CHANGED
@@ -1,14 +1,6 @@
1
- import * as context from "./context.js";
2
- import * as core from "./core.js";
3
- import * as dict from "./dict.js";
4
- import * as hook from "./hook.js";
5
- import * as main_loop from "./main-loop.js";
6
- import * as node from "./node.js";
7
- import * as param from "./param.js";
8
- import * as pipewire from "./pipewire.js";
9
- import * as pod from "./pod.js";
10
- import * as props from "./props.js";
11
- import * as proxy from "./proxy.js";
12
- import * as registry from "./registry.js";
1
+ import Pipewire from "./pipewire.js";
2
+ import PipewireObject from "./object.js";
3
+ import PipewireNode from "./node.js";
4
+ import bindings from "./bindings/index.js";
13
5
 
14
- export { context, core, dict, hook, main_loop, node, param, pod, props, pipewire, proxy, registry };
6
+ export { Pipewire, PipewireObject, PipewireNode, bindings };
package/src/node.ts CHANGED
@@ -1,81 +1,169 @@
1
1
  import koffi from "koffi";
2
- import { spa_hook } from "./hook.js";
3
- import { spa_pod } from "./pod.js";
4
- import { spa_dict } from "./dict.js";
5
- import { spa_param_info } from "./param.js";
6
-
7
- const pipewire = koffi.load("libpipewire-0.3.so.0");
8
-
9
- export const pw_node = koffi.opaque("pw_node");
10
-
11
- export const pw_node_state = koffi.enumeration("pw_node_state", {
12
- PW_NODE_STATE_ERROR: -1,
13
- PW_NODE_STATE_CREATING: 0,
14
- PW_NODE_STATE_SUSPENDED: 1,
15
- PW_NODE_STATE_IDLE: 2,
16
- PW_NODE_STATE_RUNNING: 3
17
- });
18
-
19
- export const PW_NODE_CHANGE_MASK = {
20
- INPUT_PORTS: 1 << 0,
21
- OUTPUT_PORTS: 1 << 1,
22
- STATE: 1 << 2,
23
- PROPS: 1 << 3,
24
- PARAMS: 1 << 4
25
- };
2
+ import type Pipewire from "./pipewire.js";
3
+ import type { pw_node_state } from "./bindings/node.js";
4
+ import type { spa_param_type } from "./bindings/param.js";
5
+ import PipewireObject from "./object.js";
6
+ import pw from "./bindings/index.js";
7
+
8
+ export default class PipewireNode extends PipewireObject {
9
+ static readonly pw_node_state = pw.node.pw_node_state_enum;
10
+ static readonly spa_param_type = pw.param.spa_param_type;
11
+
12
+ nodeName: string;
13
+ factoryId: number;
14
+
15
+ nodeProxy: unknown | null = null;
16
+ nodeListener: Buffer | null = null;
17
+ nodeEvents: Buffer | null = null;
18
+ nodeEventInfoRegister: bigint | null = null;
19
+ nodeEventParamRegister: bigint | null = null;
20
+
21
+ constructor(
22
+ pipewire: Pipewire,
23
+ id: number,
24
+ permissions: number,
25
+ type: string,
26
+ version: number,
27
+ props: { [key: string]: string }
28
+ ) {
29
+ super(pipewire, id, permissions, type, version, props);
30
+ this.nodeName = props["node.name"]!;
31
+ this.factoryId = Number(props["factory.id"]);
32
+ }
33
+
34
+ attachListener(): void {
35
+ if (this.removed) return;
36
+ if (this.nodeProxy) throw new Error(`Listener already attached to node ${this.id}`);
37
+
38
+ this.nodeProxy = pw.registry.pw_registry_bind(
39
+ this.pipewire.registry,
40
+ this.id,
41
+ this.type,
42
+ pw.node.PW_VERSION_NODE,
43
+ 0
44
+ );
45
+ this.nodeListener = Buffer.alloc(koffi.sizeof(pw.hook.spa_hook));
46
+ this.nodeEvents = Buffer.alloc(koffi.sizeof(pw.node.pw_node_events));
47
+ this.nodeEventInfoRegister = koffi.register(
48
+ this.nodeEventInfo.bind(this),
49
+ koffi.pointer(pw.node.pw_node_events_info)
50
+ );
51
+ this.nodeEventParamRegister = koffi.register(
52
+ this.nodeEventParam.bind(this),
53
+ koffi.pointer(pw.node.pw_node_events_param)
54
+ );
55
+ koffi.encode(this.nodeEvents, pw.node.pw_node_events, {
56
+ version: pw.node.PW_VERSION_NODE_EVENTS,
57
+ info: this.nodeEventInfoRegister,
58
+ param: this.nodeEventParamRegister
59
+ });
60
+ pw.node.pw_node_add_listener(this.nodeProxy, this.nodeListener, this.nodeEvents, null);
61
+ }
62
+
63
+ detachListener(): void {
64
+ if (!this.nodeProxy) throw new Error(`Listener not attached to node ${this.id}`);
65
+
66
+ if (!this.removed) pw.proxy.pw_proxy_destroy(this.nodeProxy);
67
+ koffi.unregister(this.nodeEventParamRegister!);
68
+ koffi.unregister(this.nodeEventInfoRegister!);
69
+ this.nodeEventInfoRegister = null;
70
+ this.nodeEventParamRegister = null;
71
+ this.nodeEvents = null;
72
+ this.nodeListener = null;
73
+ this.nodeProxy = null;
74
+ }
75
+
76
+ subscribeParams(params: spa_param_type[]) {
77
+ if (this.removed) return;
78
+ if (!this.nodeProxy) throw new Error(`Listener not attached to node ${this.id}`);
79
+ pw.node.pw_node_subscribe_params(this.nodeProxy, params, params.length);
80
+ }
81
+
82
+ nodeEventInfo(_data: unknown, info: unknown): void {
83
+ this.emit("rawNodeInfo", info);
84
+
85
+ const nodeInfo = koffi.decode(info, pw.node.pw_node_info);
26
86
 
27
- export const pw_node_info = koffi.struct("pw_node_info", {
28
- id: "uint32_t",
29
- max_input_ports: "uint32_t",
30
- max_output_ports: "uint32_t",
31
- change_mask: "uint64_t",
32
- n_input_ports: "uint32_t",
33
- n_output_ports: "uint32_t",
34
- state: pw_node_state,
35
- error: "const char*",
36
- props: koffi.pointer(spa_dict),
37
- params: koffi.pointer(spa_param_info),
38
- n_params: "uint32_t"
39
- });
40
-
41
- export const pw_node_events_info = koffi.proto("void", [
42
- "void*", // data
43
- koffi.pointer(pw_node_info) // info
44
- ]);
45
-
46
- export const pw_node_events_param = koffi.proto("void", [
47
- "void*", // data
48
- "int", // seq
49
- "uint32_t", // id
50
- "uint32_t", // index
51
- "uint32_t", // next
52
- koffi.pointer(spa_pod) // param
53
- ]);
54
-
55
- export const pw_node_events = koffi.struct("pw_node_events", {
56
- version: "uint32_t",
57
- info: koffi.pointer(pw_node_events_info),
58
- param: koffi.pointer(pw_node_events_param)
59
- });
60
-
61
- export const pw_node_add_listener = pipewire.func("pw_node_add_listener", "int", [
62
- koffi.pointer(pw_node), // object
63
- koffi.pointer(spa_hook), // listener
64
- koffi.pointer(pw_node_events), // events
65
- "void*" // data
66
- ]);
67
-
68
- export const pw_node_enum_params = pipewire.func("pw_node_enum_params", "int", [
69
- koffi.pointer(pw_node), // object
70
- "int", // seq
71
- "uint32_t", // id
72
- "uint32_t", // start
73
- "uint32_t", // num
74
- koffi.pointer(spa_pod) // filter
75
- ]);
76
-
77
- export const pw_node_subscribe_params = pipewire.func("pw_node_subscribe_params", "int", [
78
- koffi.pointer(pw_node), // object
79
- "uint32_t*", // ids
80
- "uint32_t" // n_ids
81
- ]);
87
+ const changesMasks = Object.keys(pw.node.PW_NODE_CHANGE_MASK) as (keyof typeof pw.node.PW_NODE_CHANGE_MASK)[];
88
+ const changes = changesMasks.filter((key) => nodeInfo.change_mask & pw.node.PW_NODE_CHANGE_MASK[key]);
89
+
90
+ const props = koffi.decode(nodeInfo.props, pw.dict.spa_dict);
91
+ if (changes.includes("PROPS") !== !!props.items) throw new Error(`Node ${this.id} props change mismatch`);
92
+
93
+ const propsObj: { [key: string]: string } | null = changes.includes("PROPS") ? {} : null;
94
+ if (propsObj) {
95
+ const propsArray = koffi.decode(props.items, pw.dict.spa_dict_item, props.n_items);
96
+ for (const item of propsArray) propsObj[item.key] = item.value;
97
+ }
98
+
99
+ const paramsObj: spa_param_type[] = [];
100
+ if (nodeInfo.params) {
101
+ const params = koffi.decode(nodeInfo.params, pw.param.spa_param_info, nodeInfo.n_params);
102
+ for (const param of params) paramsObj.push(param.id as spa_param_type);
103
+ }
104
+
105
+ const nodeInfoObj: NodeInfo = {
106
+ id: nodeInfo.id,
107
+ maxInputPorts: nodeInfo.max_input_ports,
108
+ maxOutputPorts: nodeInfo.max_output_ports,
109
+ changes: changes,
110
+ inputPortsCount: nodeInfo.n_input_ports,
111
+ outputPortsCount: nodeInfo.n_output_ports,
112
+ state: nodeInfo.state as pw_node_state,
113
+ error: nodeInfo.error ? koffi.decode(nodeInfo.error, "string") : null,
114
+ props: propsObj,
115
+ params: paramsObj
116
+ };
117
+
118
+ this.emit("nodeInfo", nodeInfoObj);
119
+ }
120
+
121
+ nodeEventParam(_data: unknown, info: unknown): void {
122
+ this.emit("rawNodeParam", info);
123
+ }
124
+
125
+ on(event: "nodeInfo", listener: (info: NodeInfo) => void): this;
126
+ on(event: "rawNodeInfo", listener: (info: unknown) => void): this;
127
+ on(event: "rawNodeParam", listener: (info: unknown) => void): this;
128
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
129
+ on(event: string | symbol, listener: (...args: any[]) => void): this {
130
+ return super.on(event, listener);
131
+ }
132
+
133
+ once(event: "nodeInfo", listener: (info: NodeInfo) => void): this;
134
+ once(event: "rawNodeInfo", listener: (info: unknown) => void): this;
135
+ once(event: "rawNodeParam", listener: (info: unknown) => void): this;
136
+ once(event: string | symbol, listener: (...args: any[]) => void): this;
137
+ once(event: string | symbol, listener: (...args: any[]) => void): this {
138
+ return super.once(event, listener);
139
+ }
140
+
141
+ off(event: "nodeInfo", listener: (info: NodeInfo) => void): this;
142
+ off(event: "rawNodeInfo", listener: (info: unknown) => void): this;
143
+ off(event: "rawNodeParam", listener: (info: unknown) => void): this;
144
+ off(event: string | symbol, listener: (...args: any[]) => void): this;
145
+ off(event: string | symbol, listener: (...args: any[]) => void): this {
146
+ return super.off(event, listener);
147
+ }
148
+
149
+ emit(event: "nodeInfo", info: NodeInfo): boolean;
150
+ emit(event: "rawNodeInfo", info: unknown): boolean;
151
+ emit(event: "rawNodeParam", info: unknown): boolean;
152
+ emit(event: string | symbol, ...args: any[]): boolean;
153
+ emit(event: string | symbol, ...args: any[]): boolean {
154
+ return super.emit(event, ...args);
155
+ }
156
+ }
157
+
158
+ export type NodeInfo = {
159
+ id: number;
160
+ maxInputPorts: number;
161
+ maxOutputPorts: number;
162
+ changes: (keyof typeof pw.node.PW_NODE_CHANGE_MASK)[];
163
+ inputPortsCount: number;
164
+ outputPortsCount: number;
165
+ state: pw_node_state;
166
+ error: string | null;
167
+ props: { [key: string]: string } | null;
168
+ params: spa_param_type[];
169
+ };
package/src/object.ts ADDED
@@ -0,0 +1,36 @@
1
+ import EventEmitter from "node:events";
2
+ import type Pipewire from "./pipewire.js";
3
+
4
+ export default class PipewireObject extends EventEmitter {
5
+ pipewire: Pipewire;
6
+ removed: boolean = false;
7
+
8
+ id: number;
9
+ permissions: number;
10
+ type: string;
11
+ version: number;
12
+ props: { [key: string]: string };
13
+
14
+ objectSerial: number;
15
+
16
+ constructor(
17
+ pipewire: Pipewire,
18
+ id: number,
19
+ permissions: number,
20
+ type: string,
21
+ version: number,
22
+ props: { [key: string]: string }
23
+ ) {
24
+ super();
25
+
26
+ this.pipewire = pipewire;
27
+
28
+ this.id = id;
29
+ this.permissions = permissions;
30
+ this.type = type;
31
+ this.version = version;
32
+ this.props = props;
33
+
34
+ this.objectSerial = Number(props["object.serial"]);
35
+ }
36
+ }