homebridge-plugin-utils 1.35.0 → 2.1.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.
- package/README.md +130 -2
- package/build/eslint-plugin/README.md +164 -0
- package/build/eslint-plugin/config.mjs +308 -0
- package/build/eslint-plugin/index.mjs +7 -0
- package/build/eslint-plugin/plugin.mjs +46 -0
- package/build/eslint-plugin/rules/blank-line-after-open-brace.mjs +137 -0
- package/build/eslint-plugin/rules/blank-line-after-open-brace.test.mjs +112 -0
- package/build/eslint-plugin/rules/comment-style.mjs +190 -0
- package/build/eslint-plugin/rules/comment-style.test.mjs +190 -0
- package/build/eslint-plugin/rules/enforce-node-protocol.mjs +114 -0
- package/build/eslint-plugin/rules/enforce-node-protocol.test.mjs +116 -0
- package/build/eslint-plugin/rules/paren-comparisons-in-logical.mjs +94 -0
- package/build/eslint-plugin/rules/paren-comparisons-in-logical.test.mjs +107 -0
- package/build/eslint-plugin/rules/split-type-imports.mjs +354 -0
- package/build/eslint-plugin/rules/split-type-imports.test.mjs +268 -0
- package/build/eslint-plugin/test-setup.mjs +21 -0
- package/build/fs-ops.mjs +184 -0
- package/build/tsconfig.json +18 -3
- package/dist/backpressure.d.ts +94 -45
- package/dist/backpressure.js +229 -93
- package/dist/backpressure.js.map +1 -1
- package/dist/cli/index.d.ts +166 -0
- package/dist/cli/index.js +551 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/clock-double.d.ts +93 -0
- package/dist/clock-double.js +141 -0
- package/dist/clock-double.js.map +1 -0
- package/dist/clock.d.ts +39 -0
- package/dist/clock.js +34 -0
- package/dist/clock.js.map +1 -0
- package/dist/disposable-stack.d.ts +59 -0
- package/dist/disposable-stack.js +155 -0
- package/dist/disposable-stack.js.map +1 -0
- package/dist/docChrome.d.ts +260 -0
- package/dist/docChrome.js +361 -0
- package/dist/docChrome.js.map +1 -0
- package/dist/eslint-plugin/config.d.mts +193 -0
- package/dist/eslint-plugin/index.d.mts +2 -0
- package/dist/eslint-plugin/plugin.d.mts +87 -0
- package/dist/eslint-plugin/rules/blank-line-after-open-brace.d.mts +19 -0
- package/dist/eslint-plugin/rules/comment-style.d.mts +15 -0
- package/dist/eslint-plugin/rules/enforce-node-protocol.d.mts +18 -0
- package/dist/eslint-plugin/rules/paren-comparisons-in-logical.d.mts +15 -0
- package/dist/eslint-plugin/rules/split-type-imports.d.mts +16 -0
- package/dist/featureOptions-docs.d.ts +96 -0
- package/dist/featureOptions-docs.js +193 -0
- package/dist/featureOptions-docs.js.map +1 -0
- package/dist/featureOptions.d.ts +674 -0
- package/dist/featureOptions.js +870 -0
- package/dist/featureOptions.js.map +1 -0
- package/dist/ffmpeg/codecs.d.ts +256 -72
- package/dist/ffmpeg/codecs.js +477 -262
- package/dist/ffmpeg/codecs.js.map +1 -1
- package/dist/ffmpeg/dgram-util.d.ts +46 -0
- package/dist/ffmpeg/dgram-util.js +38 -0
- package/dist/ffmpeg/dgram-util.js.map +1 -0
- package/dist/ffmpeg/exec.d.ts +83 -64
- package/dist/ffmpeg/exec.js +77 -86
- package/dist/ffmpeg/exec.js.map +1 -1
- package/dist/ffmpeg/fmp4.d.ts +49 -2
- package/dist/ffmpeg/fmp4.js +47 -11
- package/dist/ffmpeg/fmp4.js.map +1 -1
- package/dist/ffmpeg/hap-enums.d.ts +214 -0
- package/dist/ffmpeg/hap-enums.js +92 -0
- package/dist/ffmpeg/hap-enums.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +16 -9
- package/dist/ffmpeg/index.js +6 -0
- package/dist/ffmpeg/index.js.map +1 -1
- package/dist/ffmpeg/mp4-assembler.d.ts +161 -0
- package/dist/ffmpeg/mp4-assembler.js +424 -0
- package/dist/ffmpeg/mp4-assembler.js.map +1 -0
- package/dist/ffmpeg/mp4-parser.d.ts +94 -0
- package/dist/ffmpeg/mp4-parser.js +130 -0
- package/dist/ffmpeg/mp4-parser.js.map +1 -0
- package/dist/ffmpeg/options.d.ts +62 -149
- package/dist/ffmpeg/options.js +608 -499
- package/dist/ffmpeg/options.js.map +1 -1
- package/dist/ffmpeg/process.d.ts +142 -96
- package/dist/ffmpeg/process.js +406 -278
- package/dist/ffmpeg/process.js.map +1 -1
- package/dist/ffmpeg/record.d.ts +325 -186
- package/dist/ffmpeg/record.js +418 -565
- package/dist/ffmpeg/record.js.map +1 -1
- package/dist/ffmpeg/recording-process-double.d.ts +157 -0
- package/dist/ffmpeg/recording-process-double.js +190 -0
- package/dist/ffmpeg/recording-process-double.js.map +1 -0
- package/dist/ffmpeg/rtp-parser.d.ts +70 -0
- package/dist/ffmpeg/rtp-parser.js +77 -0
- package/dist/ffmpeg/rtp-parser.js.map +1 -0
- package/dist/ffmpeg/rtp.d.ts +198 -141
- package/dist/ffmpeg/rtp.js +474 -251
- package/dist/ffmpeg/rtp.js.map +1 -1
- package/dist/ffmpeg/settings.d.ts +5 -2
- package/dist/ffmpeg/settings.js +20 -5
- package/dist/ffmpeg/settings.js.map +1 -1
- package/dist/ffmpeg/stream.d.ts +57 -107
- package/dist/ffmpeg/stream.js +121 -150
- package/dist/ffmpeg/stream.js.map +1 -1
- package/dist/formatters.d.ts +106 -0
- package/dist/formatters.js +174 -0
- package/dist/formatters.js.map +1 -0
- package/dist/homebridge-enums.d.ts +30 -0
- package/dist/homebridge-enums.js +17 -0
- package/dist/homebridge-enums.js.map +1 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/logclient/auth.d.ts +114 -0
- package/dist/logclient/auth.js +199 -0
- package/dist/logclient/auth.js.map +1 -0
- package/dist/logclient/cli-run.d.ts +76 -0
- package/dist/logclient/cli-run.js +639 -0
- package/dist/logclient/cli-run.js.map +1 -0
- package/dist/logclient/cli.d.ts +3 -0
- package/dist/logclient/cli.js +97 -0
- package/dist/logclient/cli.js.map +1 -0
- package/dist/logclient/client.d.ts +145 -0
- package/dist/logclient/client.js +600 -0
- package/dist/logclient/client.js.map +1 -0
- package/dist/logclient/config.d.ts +173 -0
- package/dist/logclient/config.js +199 -0
- package/dist/logclient/config.js.map +1 -0
- package/dist/logclient/endpoints.d.ts +54 -0
- package/dist/logclient/endpoints.js +73 -0
- package/dist/logclient/endpoints.js.map +1 -0
- package/dist/logclient/filter.d.ts +45 -0
- package/dist/logclient/filter.js +51 -0
- package/dist/logclient/filter.js.map +1 -0
- package/dist/logclient/frame.d.ts +93 -0
- package/dist/logclient/frame.js +203 -0
- package/dist/logclient/frame.js.map +1 -0
- package/dist/logclient/index.d.ts +31 -0
- package/dist/logclient/index.js +12 -0
- package/dist/logclient/index.js.map +1 -0
- package/dist/logclient/parser.d.ts +211 -0
- package/dist/logclient/parser.js +393 -0
- package/dist/logclient/parser.js.map +1 -0
- package/dist/logclient/rest.d.ts +41 -0
- package/dist/logclient/rest.js +111 -0
- package/dist/logclient/rest.js.map +1 -0
- package/dist/logclient/settings.d.ts +15 -0
- package/dist/logclient/settings.js +64 -0
- package/dist/logclient/settings.js.map +1 -0
- package/dist/logclient/socket-double.d.ts +201 -0
- package/dist/logclient/socket-double.js +384 -0
- package/dist/logclient/socket-double.js.map +1 -0
- package/dist/logclient/socket.d.ts +257 -0
- package/dist/logclient/socket.js +620 -0
- package/dist/logclient/socket.js.map +1 -0
- package/dist/logclient/stitch.d.ts +83 -0
- package/dist/logclient/stitch.js +146 -0
- package/dist/logclient/stitch.js.map +1 -0
- package/dist/logclient/time-expression.d.ts +42 -0
- package/dist/logclient/time-expression.js +181 -0
- package/dist/logclient/time-expression.js.map +1 -0
- package/dist/logclient/time-window.d.ts +38 -0
- package/dist/logclient/time-window.js +53 -0
- package/dist/logclient/time-window.js.map +1 -0
- package/dist/logclient/types.d.ts +107 -0
- package/dist/logclient/types.js +6 -0
- package/dist/logclient/types.js.map +1 -0
- package/dist/mqttClient.d.ts +287 -0
- package/dist/mqttClient.js +433 -0
- package/dist/mqttClient.js.map +1 -0
- package/dist/service.d.ts +64 -15
- package/dist/service.js +93 -66
- package/dist/service.js.map +1 -1
- package/dist/ui/featureOptions.js +870 -0
- package/dist/ui/featureOptions.js.map +1 -0
- package/dist/ui/formatters.js +174 -0
- package/dist/ui/formatters.js.map +1 -0
- package/dist/ui/pluginConfigSession.mjs +141 -0
- package/dist/ui/webUi-featureOptions/categoryState.mjs +135 -0
- package/dist/ui/webUi-featureOptions/effects/keyboard.mjs +61 -0
- package/dist/ui/webUi-featureOptions/effects/persist.mjs +226 -0
- package/dist/ui/webUi-featureOptions/effects/theme.mjs +398 -0
- package/dist/ui/webUi-featureOptions/effects/tokens.mjs +152 -0
- package/dist/ui/webUi-featureOptions/rendering.mjs +431 -0
- package/dist/ui/webUi-featureOptions/selectors.mjs +360 -0
- package/dist/ui/webUi-featureOptions/state.mjs +319 -0
- package/dist/ui/webUi-featureOptions/store.mjs +181 -0
- package/dist/ui/webUi-featureOptions/utils.mjs +213 -0
- package/dist/ui/webUi-featureOptions/views/connectionError.mjs +152 -0
- package/dist/ui/webUi-featureOptions/views/deviceInfo.mjs +80 -0
- package/dist/ui/webUi-featureOptions/views/header.mjs +77 -0
- package/dist/ui/webUi-featureOptions/views/nav.mjs +341 -0
- package/dist/ui/webUi-featureOptions/views/options.mjs +521 -0
- package/dist/ui/webUi-featureOptions/views/search.mjs +395 -0
- package/dist/ui/webUi-featureOptions.mjs +702 -0
- package/dist/ui/webUi.mjs +225 -88
- package/dist/util.d.ts +679 -45
- package/dist/util.js +830 -77
- package/dist/util.js.map +1 -1
- package/package.json +33 -15
- package/build/eslint-rules.mjs +0 -511
- package/dist/featureoptions.d.ts +0 -264
- package/dist/featureoptions.js +0 -480
- package/dist/featureoptions.js.map +0 -1
- package/dist/mqttclient.d.ts +0 -178
- package/dist/mqttclient.js +0 -310
- package/dist/mqttclient.js.map +0 -1
- package/dist/ui/featureoptions.js +0 -480
- package/dist/ui/featureoptions.js.map +0 -1
- package/dist/ui/webUi-featureoptions.mjs +0 -3765
package/dist/ffmpeg/rtp.d.ts
CHANGED
|
@@ -1,205 +1,262 @@
|
|
|
1
|
+
import type { HomebridgePluginLogging } from "../util.ts";
|
|
2
|
+
import type { IpFamily } from "./dgram-util.ts";
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* Construction-time options for {@link RtpDemuxer}.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* @property inactivityTimeout - Optional inactivity watchdog window, in milliseconds. The timer arms during construction (immediately after the bind call is issued)
|
|
7
|
+
* and re-arms on every received datagram. When the window lapses without traffic, the demuxer aborts with `HbpuAbortError("timeout")`.
|
|
8
|
+
* Omit to disable the watchdog entirely.
|
|
9
|
+
* @property inputPort - Required. The UDP port to bind to. Typically a value previously reserved via {@link RtpPortAllocator.reserve}. Pass `0` to request
|
|
10
|
+
* kernel-assigned ephemeral allocation: the bind succeeds atomically against whichever port the kernel hands out, eliminating the
|
|
11
|
+
* reserve-then-rebind race that a separate reservation step would carry. The assigned port is then observable via
|
|
12
|
+
* {@link RtpDemuxer.inputPort} once {@link RtpDemuxer.ready} resolves.
|
|
13
|
+
* @property ipFamily - Optional. `"ipv4"` or `"ipv6"`. Defaults to `"ipv4"`.
|
|
14
|
+
* @property log - Optional logger. Used for debug tracing of socket lifecycle and heartbeat events, and for error-path diagnostics. The signal's reason
|
|
15
|
+
* on abort remains the authoritative notification channel; logging is a convenience for operators.
|
|
16
|
+
* @property rtcpPort - Required. The destination UDP port (on the loopback interface) for classified RTCP packets. Typically FFmpeg's RTCP input port.
|
|
17
|
+
* @property rtpPort - Required. The destination UDP port (on the loopback interface) for classified RTP packets. Typically FFmpeg's RTP input port. The
|
|
18
|
+
* heartbeat replay also targets this port - FFmpeg ignores the RTCP shape on its RTP input, but the arriving traffic keeps that input
|
|
19
|
+
* fed during quiet periods.
|
|
20
|
+
* @property signal - Optional parent {@link AbortSignal} to compose with the demuxer's internal controller. When the parent aborts, the demuxer tears down.
|
|
7
21
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - Demultiplexes RTP and RTCP packets received on a single UDP port, forwarding them to the correct FFmpeg destinations for HomeKit livestream compatibility.
|
|
11
|
-
* - Injects periodic heartbeat messages to keep two-way audio streams alive with FFmpeg’s strict timeout requirements.
|
|
12
|
-
* - Dynamically allocates and reserves UDP ports for RTP/RTCP, supporting consecutive port pairing for correct FFmpeg operation.
|
|
13
|
-
* - Event-driven architecture for integration with plugin or automation logic.
|
|
14
|
-
*
|
|
15
|
-
* Designed for plugin developers and advanced users implementing HomeKit livestreaming, audio/video bridging, or similar applications requiring precise RTP/RTCP handling
|
|
16
|
-
* with FFmpeg.
|
|
17
|
-
*
|
|
18
|
-
* @module
|
|
22
|
+
* @category FFmpeg
|
|
19
23
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
export interface RtpDemuxerInit {
|
|
25
|
+
inactivityTimeout?: number;
|
|
26
|
+
inputPort: number;
|
|
27
|
+
ipFamily?: IpFamily;
|
|
28
|
+
log?: HomebridgePluginLogging;
|
|
29
|
+
rtcpPort: number;
|
|
30
|
+
rtpPort: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}
|
|
22
33
|
/**
|
|
23
|
-
*
|
|
34
|
+
* Signal-driven RTP/RTCP demultiplexer with FFmpeg keepalive heartbeat.
|
|
24
35
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
36
|
+
* The class owns one bound UDP socket. Inbound datagrams are classified by an internal {@link RtpPacketParser} and forwarded through the same socket to the configured
|
|
37
|
+
* `rtpPort` (RTP-classified) or `rtcpPort` (RTCP-classified) on the loopback interface. Sharing the bound socket between receive and send gives the relay two
|
|
38
|
+
* essential properties:
|
|
27
39
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
40
|
+
* 1. **Source-endpoint symmetry.** Every forwarded datagram leaves the bound socket with source = `loopback:inputPort`. Because the demuxer holds an exclusive
|
|
41
|
+
* kernel bind on that port, no other non-root process can spoof that source endpoint - downstream receivers (typically FFmpeg) can therefore enforce source
|
|
42
|
+
* filtering against locally-injected traffic without coordinating ephemeral allocations with the demuxer.
|
|
43
|
+
* 2. **Minimized bound-port footprint.** Exactly one kernel-tracked socket exists per demuxer instance; a dual-socket design would bind a second ephemeral port for
|
|
44
|
+
* the duration of the session, expanding the attack surface and the kernel-resource count for no architectural benefit.
|
|
45
|
+
*
|
|
46
|
+
* A self-rearming {@link Watchdog} replays the last observed RTCP packet to `rtpPort` whenever the gap between inbound RTCP arrivals exceeds the configured
|
|
47
|
+
* {@link RTCP_HEARTBEAT_INTERVAL}. The heartbeat is part of the demuxer's contract - FFmpeg-bound two-way audio is the only use case that constructs this
|
|
48
|
+
* class, and that use case relies on the keepalive to keep inbound traffic arriving at FFmpeg's RTP input during legitimate quiet periods on the camera's audio
|
|
49
|
+
* backchannel. No FFmpeg input-timeout flag (`-timeout` / `rw_timeout`) is configured anywhere - the keepalive is a defensive guard against any FFmpeg-side idle
|
|
50
|
+
* handling of a quiet UDP/RTP input, not a timeout this code sets. The cadence is the single exported constant {@link RTCP_HEARTBEAT_INTERVAL}.
|
|
51
|
+
*
|
|
52
|
+
* An optional second {@link Watchdog} aborts the demuxer with `HbpuAbortError("timeout")` when no inbound traffic arrives within a caller-supplied window. Both
|
|
53
|
+
* watchdogs compose against the same lifetime signal, so disposal cleans them up uniformly.
|
|
30
54
|
*
|
|
31
55
|
* @example
|
|
32
56
|
*
|
|
33
57
|
* ```ts
|
|
34
|
-
*
|
|
35
|
-
*
|
|
58
|
+
* await using demuxer = new RtpDemuxer({
|
|
59
|
+
*
|
|
60
|
+
* inactivityTimeout: 5_000,
|
|
61
|
+
* inputPort: audioIncomingPort,
|
|
62
|
+
* ipFamily: "ipv4",
|
|
63
|
+
* log,
|
|
64
|
+
* rtcpPort: audioIncomingRtcpPort,
|
|
65
|
+
* rtpPort: audioIncomingRtpPort,
|
|
66
|
+
* signal: session.signal
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* try {
|
|
36
70
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
71
|
+
* await demuxer.mediaReady;
|
|
72
|
+
* } catch {
|
|
73
|
+
*
|
|
74
|
+
* // Demuxer aborted before any inbound RTP arrived; abandon the return-audio setup.
|
|
75
|
+
* return;
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // Media is now flowing - safe to launch the consuming FFmpeg process.
|
|
39
79
|
* ```
|
|
40
80
|
*
|
|
41
|
-
* @see
|
|
42
|
-
* @see
|
|
81
|
+
* @see RtpPacketParser
|
|
82
|
+
* @see RTCP_HEARTBEAT_INTERVAL
|
|
43
83
|
*
|
|
44
84
|
* @category FFmpeg
|
|
45
85
|
*/
|
|
46
|
-
export declare class RtpDemuxer
|
|
47
|
-
private
|
|
48
|
-
private heartbeatMsg?;
|
|
49
|
-
private _isRunning;
|
|
50
|
-
private log?;
|
|
51
|
-
private inputPort;
|
|
52
|
-
readonly socket: import("node:dgram").Socket;
|
|
86
|
+
export declare class RtpDemuxer implements AsyncDisposable {
|
|
87
|
+
#private;
|
|
53
88
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @param ipFamily - The IP family: "ipv4" or "ipv6".
|
|
57
|
-
* @param inputPort - The UDP port to listen on for incoming packets.
|
|
58
|
-
* @param rtcpPort - The UDP port to forward RTCP packets to.
|
|
59
|
-
* @param rtpPort - The UDP port to forward RTP packets to.
|
|
60
|
-
* @param log - Logger instance for debug and error messages.
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
*
|
|
64
|
-
* ```ts
|
|
65
|
-
* const demuxer = new RtpDemuxer("ipv4", 50000, 50002, 50004, log);
|
|
66
|
-
* ```
|
|
89
|
+
* The composed abort signal representing this demuxer's lifetime. Aborts exactly once when the socket errors, the inactivity watchdog fires, the parent signal
|
|
90
|
+
* propagates, or {@link RtpDemuxer.abort} is called; the reason encoded on `signal.reason` names the cause.
|
|
67
91
|
*/
|
|
68
|
-
|
|
92
|
+
readonly signal: AbortSignal;
|
|
69
93
|
/**
|
|
70
|
-
*
|
|
94
|
+
* Promise that resolves once the underlying UDP socket has finished binding and entered its `"listening"` state. Rejects with `this.signal.reason` when the bind
|
|
95
|
+
* fails, the parent signal propagates, or any other abort path fires before the socket becomes ready.
|
|
71
96
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @param port - The RTP port to send the heartbeat to.
|
|
97
|
+
* The unhandled-rejection tracker is suppressed via {@link markHandled} so consumers that never observe readiness (fire-and-forget demuxers) do not produce warnings;
|
|
98
|
+
* consumers that do observe rejection receive the same structured abort reason they would from `signal.reason`.
|
|
75
99
|
*/
|
|
76
|
-
|
|
100
|
+
readonly ready: Promise<void>;
|
|
77
101
|
/**
|
|
78
|
-
*
|
|
102
|
+
* Promise that resolves once the demuxer has forwarded its first RTP-classified packet - the application-level readiness milestone meaning "media is now flowing
|
|
103
|
+
* through the relay to the downstream consumer." Rejects with `this.signal.reason` if the demuxer aborts before any RTP packet arrives.
|
|
79
104
|
*
|
|
80
|
-
* @
|
|
105
|
+
* Pairs with {@link RtpDemuxer.ready} as a two-tier readiness model: `ready` resolves when the inbound socket has bound (network-level readiness); `mediaReady`
|
|
106
|
+
* resolves when classified RTP traffic has begun flowing (application-level readiness). The canonical gating pattern for two-way audio is "stand up the return-audio
|
|
107
|
+
* path, wait for inbound media before launching the consuming FFmpeg process":
|
|
81
108
|
*
|
|
82
109
|
* ```ts
|
|
83
|
-
*
|
|
110
|
+
* try {
|
|
111
|
+
* await demuxer.mediaReady;
|
|
112
|
+
* } catch {
|
|
113
|
+
* // Demuxer aborted before any RTP arrived; abandon the return-audio setup.
|
|
114
|
+
* return;
|
|
115
|
+
* }
|
|
116
|
+
* // Safe to launch the consuming FFmpeg process now that media is flowing.
|
|
84
117
|
* ```
|
|
118
|
+
*
|
|
119
|
+
* The unhandled-rejection tracker is suppressed via {@link markHandled} so consumers that never observe this promise produce no warnings.
|
|
85
120
|
*/
|
|
86
|
-
|
|
121
|
+
readonly mediaReady: Promise<void>;
|
|
87
122
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* @param message - The UDP packet buffer.
|
|
123
|
+
* The UDP port the demuxer is bound to. For a specific-port construction (`init.inputPort` non-zero), this equals `init.inputPort` from the moment the constructor
|
|
124
|
+
* returns; for an ephemeral construction (`init.inputPort === 0`), the value is `0` until the kernel completes the bind, then the kernel-assigned port. Consumers
|
|
125
|
+
* that need the assigned ephemeral port `await demuxer.ready` before reading this property.
|
|
93
126
|
*
|
|
94
|
-
*
|
|
127
|
+
* Reads from the cached projection of `socket.address().port` captured at the `"listening"` event so the value reflects what the kernel actually bound, not just what
|
|
128
|
+
* was requested. The two coincide for specific-port binds; they diverge for ephemeral binds where the requested value is `0` and the bound value is the kernel's pick.
|
|
129
|
+
*/
|
|
130
|
+
get inputPort(): number;
|
|
131
|
+
/**
|
|
132
|
+
* `true` once `this.signal` has aborted. Derived from the signal; no independent state.
|
|
95
133
|
*/
|
|
96
|
-
|
|
134
|
+
get aborted(): boolean;
|
|
97
135
|
/**
|
|
98
|
-
*
|
|
136
|
+
* `true` when the abort reason indicates a timeout. Matches both the canonical `HbpuAbortError("timeout")` emitted by the inactivity watchdog and the platform
|
|
137
|
+
* `TimeoutError` emitted by `AbortSignal.timeout()` - consumers branch on a single getter regardless of which code path produced the timeout. The branching
|
|
138
|
+
* logic lives in {@link isTimeoutReason} so this getter stays a one-line delegation and every resource class in the library shares one definition of "timeout."
|
|
139
|
+
*/
|
|
140
|
+
get isTimedOut(): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Construct and bind a new RTP demuxer.
|
|
99
143
|
*
|
|
100
|
-
*
|
|
144
|
+
* Socket binding happens synchronously as part of construction: by the time the constructor returns, the socket has been asked to bind on the configured port and
|
|
145
|
+
* the inactivity watchdog (if configured) is already armed. There is no separate `start()` step. The bind itself completes on a later turn; consumers that need to
|
|
146
|
+
* sequence work against bind completion `await demuxer.ready`.
|
|
101
147
|
*
|
|
102
|
-
* @
|
|
148
|
+
* @param init - Required init options. See {@link RtpDemuxerInit}.
|
|
103
149
|
*/
|
|
104
|
-
|
|
150
|
+
constructor(init: RtpDemuxerInit);
|
|
105
151
|
/**
|
|
106
|
-
*
|
|
152
|
+
* Abort the demuxer and tear it down. Defaults to `HbpuAbortError("shutdown")` when no reason is supplied; explicit reasons pass through unchanged.
|
|
107
153
|
*
|
|
108
|
-
*
|
|
154
|
+
* Safe to call more than once: subsequent calls are no-ops because the underlying signal only aborts once. Calling `abort()` after a natural error or watchdog
|
|
155
|
+
* timeout is also safe for the same reason.
|
|
109
156
|
*
|
|
110
|
-
* @
|
|
157
|
+
* @param reason - Optional abort reason. Typically an {@link HbpuAbortError}; platform errors (`TimeoutError`, `AbortError`) also interoperate by convention.
|
|
158
|
+
*/
|
|
159
|
+
abort(reason?: unknown): void;
|
|
160
|
+
/**
|
|
161
|
+
* `AsyncDisposable` implementation. Aborts the demuxer (defaulting to `"shutdown"`) and awaits the socket's `"close"` event so the bound port is releasable by the
|
|
162
|
+
* time the surrounding `await using` scope's next statement runs. Safe to invoke repeatedly: subsequent disposals collapse onto the same `#closed` promise.
|
|
111
163
|
*
|
|
112
|
-
*
|
|
113
|
-
* if(demuxer.isRunning) {
|
|
114
|
-
* // Demuxer is active.
|
|
115
|
-
* }
|
|
116
|
-
* ```
|
|
164
|
+
* @returns A promise that resolves once the kernel has released the bound port.
|
|
117
165
|
*/
|
|
118
|
-
|
|
166
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
119
167
|
}
|
|
120
168
|
/**
|
|
121
|
-
*
|
|
169
|
+
* Construction-time options for {@link RtpPortAllocator.reserve}.
|
|
170
|
+
*
|
|
171
|
+
* @property count - Optional. The number of consecutive UDP ports to reserve. `1` yields a single port, `2` yields the reserved port plus the next port so FFmpeg's
|
|
172
|
+
* "RTP port N implies RTCP port N+1" convention is satisfied. Defaults to `1`.
|
|
173
|
+
* @property ipFamily - Optional. `"ipv4"` or `"ipv6"`. Defaults to `"ipv4"`.
|
|
174
|
+
* @property signal - Optional caller {@link AbortSignal}. Cancels in-flight bind-retry attempts. If the signal aborts while `reserve()` is still looking for
|
|
175
|
+
* consecutive ports, the partially reserved port (if any) is released and the promise rejects with `signal.reason`.
|
|
122
176
|
*
|
|
123
|
-
*
|
|
177
|
+
* @category FFmpeg
|
|
178
|
+
*/
|
|
179
|
+
export interface PortReservationInit {
|
|
180
|
+
count?: 1 | 2;
|
|
181
|
+
ipFamily?: IpFamily;
|
|
182
|
+
signal?: AbortSignal;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* AsyncDisposable handle representing one or two reserved UDP ports.
|
|
186
|
+
*
|
|
187
|
+
* Ports are held exclusively against the {@link RtpPortAllocator}'s internal pool until `[Symbol.asyncDispose]` is invoked. Callers typically
|
|
188
|
+
* manage the lifetime with `await using` for scope-bound reservations, or by storing the handle on a session entry and disposing explicitly when the session ends.
|
|
189
|
+
*
|
|
190
|
+
* Disposal is safe to repeat: the first call releases the ports back to the allocator; subsequent calls are no-ops. This guarantees `await using` combined with an
|
|
191
|
+
* explicit dispose (for example, on an error path that releases early and then falls through the `using` block) does not double-release.
|
|
192
|
+
*
|
|
193
|
+
* @property count - `1` or `2`. A two-port reservation guarantees `port` and `port + 1` are both reserved.
|
|
194
|
+
* @property ipFamily - `"ipv4"` or `"ipv6"`.
|
|
195
|
+
* @property port - The first (and, for single-port reservations, only) reserved UDP port.
|
|
196
|
+
*
|
|
197
|
+
* @category FFmpeg
|
|
198
|
+
*/
|
|
199
|
+
export interface PortReservation extends AsyncDisposable {
|
|
200
|
+
readonly count: 1 | 2;
|
|
201
|
+
readonly ipFamily: IpFamily;
|
|
202
|
+
readonly port: number;
|
|
203
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Registry that reserves consecutive UDP ports for FFmpeg-based RTP/RTCP sessions.
|
|
207
|
+
*
|
|
208
|
+
* The allocator itself is a plugin-singleton registry - it holds no OS resources of its own, only a `Set` of ports marked as in-use. Reservations are the scoped
|
|
209
|
+
* resource: each successful {@link RtpPortAllocator.reserve} call returns a {@link PortReservation} handle whose disposal releases the held ports back to the pool.
|
|
210
|
+
*
|
|
211
|
+
* The reservation bind-retry loop is signal-aware: passing `init.signal` to `reserve()` means an aborting parent signal cancels the pending reservation cleanly, even
|
|
212
|
+
* when the loop is in the middle of probing candidate ports.
|
|
124
213
|
*
|
|
125
214
|
* @example
|
|
126
215
|
*
|
|
127
216
|
* ```ts
|
|
128
|
-
*
|
|
217
|
+
* // Scope-bound: the reservation releases automatically when the block exits.
|
|
218
|
+
* await using reservation = await allocator.reserve({ count: 2, signal: session.controller.signal });
|
|
219
|
+
* const rtpPort = reservation.port;
|
|
220
|
+
* const rtcpPort = reservation.port + 1;
|
|
221
|
+
* // ...use rtpPort and rtcpPort for the duration of the session.
|
|
222
|
+
* ```
|
|
129
223
|
*
|
|
130
|
-
*
|
|
131
|
-
* const rtpPort = await allocator.reserve("ipv4", 2);
|
|
224
|
+
* @example
|
|
132
225
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
226
|
+
* ```ts
|
|
227
|
+
* // Non-scoped: store the handle on a session entry and dispose explicitly later.
|
|
228
|
+
* const reservation = await allocator.reserve({ count: 2, signal: session.controller.signal });
|
|
229
|
+
* session.ports = reservation;
|
|
230
|
+
* // ...later:
|
|
231
|
+
* await session.ports[Symbol.asyncDispose]();
|
|
135
232
|
* ```
|
|
136
233
|
*
|
|
137
234
|
* @category FFmpeg
|
|
138
235
|
*/
|
|
139
236
|
export declare class RtpPortAllocator {
|
|
140
|
-
private
|
|
141
|
-
/**
|
|
142
|
-
* Instantiates a new RTP port allocator and tracker.
|
|
143
|
-
*/
|
|
144
|
-
constructor();
|
|
145
|
-
/**
|
|
146
|
-
* Finds an available UDP port by attempting to bind a new socket.
|
|
147
|
-
*
|
|
148
|
-
* Loops until an available port not already marked as in use is found.
|
|
149
|
-
*
|
|
150
|
-
* @param ipFamily - "ipv4" or "ipv6".
|
|
151
|
-
* @param port - Optional. The port to try to bind to. If 0, selects a random port.
|
|
152
|
-
*
|
|
153
|
-
* @returns A promise resolving to the available port number, or `-1` on error.
|
|
154
|
-
*/
|
|
155
|
-
private getPort;
|
|
237
|
+
#private;
|
|
156
238
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* @param ipFamily - Optional. "ipv4" or "ipv6". Defaults to "ipv4".
|
|
162
|
-
* @param portCount - Optional. The number of consecutive ports to reserve (1 or 2). Defaults to 1.
|
|
163
|
-
* @param attempts - Internal. The number of allocation attempts. Used for recursion.
|
|
239
|
+
* The number of UDP ports currently held by live {@link PortReservation} handles. Exposed for operational diagnostics - a plugin that surfaces "how many
|
|
240
|
+
* reservations are in flight right now" through a status endpoint can read this directly, and it is a useful signal for detecting leaks (a monotonically growing
|
|
241
|
+
* count across normal session cycles indicates a reservation is not being disposed).
|
|
164
242
|
*
|
|
165
|
-
* @returns
|
|
243
|
+
* @returns The number of reserved ports currently tracked by the allocator.
|
|
166
244
|
*/
|
|
167
|
-
|
|
245
|
+
get reservedCount(): number;
|
|
168
246
|
/**
|
|
169
|
-
*
|
|
247
|
+
* Reserve one or two consecutive UDP ports.
|
|
170
248
|
*
|
|
171
|
-
*
|
|
249
|
+
* For a two-port reservation, the allocator first picks a random free port, then probes the next sequential port. If the sequential port is unavailable (already in
|
|
250
|
+
* the in-use set or rejected by the OS), the first port is released and the allocator retries from scratch, up to `RESERVE_MAX_ATTEMPTS` attempts.
|
|
172
251
|
*
|
|
173
|
-
* @param
|
|
174
|
-
* @param portCount - Optional. The number of consecutive ports to reserve (1 or 2). Defaults to 1.
|
|
252
|
+
* @param init - Optional init options. See {@link PortReservationInit}.
|
|
175
253
|
*
|
|
176
|
-
* @returns A
|
|
177
|
-
*
|
|
178
|
-
* @remarks FFmpeg currently lacks the ability to specify both the RTP and RTCP ports. FFmpeg always assumes, by convention, that when you specify an RTP port, the RTCP
|
|
179
|
-
* port is the RTP port + 1. In order to work around that challenge, we need to always ensure that when we reserve multiple ports for RTP (primarily for two-way audio
|
|
180
|
-
* use cases) that we we are reserving consecutive ports only.
|
|
181
|
-
*
|
|
182
|
-
* @example
|
|
183
|
-
*
|
|
184
|
-
* ```ts
|
|
185
|
-
* // Reserve a single port.
|
|
186
|
-
* const port = await allocator.reserve();
|
|
254
|
+
* @returns A {@link PortReservation} handle whose disposal releases the reserved ports.
|
|
187
255
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*/
|
|
192
|
-
reserve(ipFamily?: ("ipv4" | "ipv6"), portCount?: (1 | 2)): Promise<number>;
|
|
193
|
-
/**
|
|
194
|
-
* Cancels and releases a previously reserved port, making it available for future use.
|
|
195
|
-
*
|
|
196
|
-
* @param port - The port number to release.
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
*
|
|
200
|
-
* ```ts
|
|
201
|
-
* allocator.cancel(50000);
|
|
202
|
-
* ```
|
|
256
|
+
* @throws The caller signal's abort reason, if the signal aborts before a reservation is obtained.
|
|
257
|
+
* @throws `RangeError` when `count` is anything other than `1` or `2`.
|
|
258
|
+
* @throws `Error` when no reservation could be obtained within the attempt budget.
|
|
203
259
|
*/
|
|
204
|
-
|
|
260
|
+
reserve(init?: PortReservationInit): Promise<PortReservation>;
|
|
205
261
|
}
|
|
262
|
+
//# sourceMappingURL=rtp.d.ts.map
|