@camstack/ui-library 1.2.242 → 1.2.244
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/dist/composites/camera-stream-player.d.ts +17 -1
- package/dist/composites/index.d.ts +15 -0
- package/dist/composites/peer-connection-config.d.ts +13 -0
- package/dist/composites/webrtc-diagnostics/EndpointTestCard.d.ts +15 -0
- package/dist/composites/webrtc-diagnostics/HubSessionTestCard.d.ts +18 -0
- package/dist/composites/webrtc-diagnostics/RunReport.d.ts +6 -0
- package/dist/composites/webrtc-diagnostics/endpoint-probe.d.ts +36 -0
- package/dist/composites/webrtc-diagnostics/endpoint-test.d.ts +54 -0
- package/dist/composites/webrtc-diagnostics/format.d.ts +11 -0
- package/dist/composites/webrtc-diagnostics/ice-candidates.d.ts +66 -0
- package/dist/composites/webrtc-diagnostics/nat-mapping.d.ts +44 -0
- package/dist/composites/webrtc-diagnostics/session-run.d.ts +125 -0
- package/dist/composites/webrtc-diagnostics/session-verdict.d.ts +29 -0
- package/dist/generated/system-hooks.d.ts +2 -0
- package/dist/index.cjs +2457 -875
- package/dist/index.js +2441 -876
- package/package.json +1 -1
|
@@ -310,5 +310,21 @@ export interface CameraStreamPlayerProps {
|
|
|
310
310
|
* element itself — the recorded-scrub receiver-credit acks are the use case.
|
|
311
311
|
*/
|
|
312
312
|
onVideoElement?: (el: HTMLVideoElement | null) => void;
|
|
313
|
+
/**
|
|
314
|
+
* DIAGNOSTICS ONLY — the browser's ICE transport policy for every peer
|
|
315
|
+
* connection this player builds. Absent (always, in production) the policy
|
|
316
|
+
* is not set and the browser gathers host/srflx/relay as before. The WebRTC
|
|
317
|
+
* diagnostics set `'relay'` to prove whether a TURN-only path reaches the
|
|
318
|
+
* hub; see `peer-connection-config.ts` for why the player never does.
|
|
319
|
+
*/
|
|
320
|
+
iceTransportPolicy?: RTCIceTransportPolicy;
|
|
321
|
+
/**
|
|
322
|
+
* Observer of every peer connection this player creates (first connect,
|
|
323
|
+
* reconnects, re-offers), handed over right after construction and before
|
|
324
|
+
* any signaling. For listeners only — the diagnostics read ICE / DTLS
|
|
325
|
+
* transitions and local candidates through `addEventListener`; a caller
|
|
326
|
+
* must never assign the `on*` handlers the player owns.
|
|
327
|
+
*/
|
|
328
|
+
onPeerConnection?: (pc: RTCPeerConnection) => void;
|
|
313
329
|
}
|
|
314
|
-
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onDebugStats, onPlaybackStats, onClientNetworkSample, probeAuthToken, onConnectTiming, className, onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, applyDownlinkProbe, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
330
|
+
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onDebugStats, onPlaybackStats, onClientNetworkSample, probeAuthToken, onConnectTiming, className, onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, applyDownlinkProbe, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement, iceTransportPolicy, onPeerConnection, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
@@ -23,6 +23,21 @@ export { StreamDebugOverlay } from './stream-debug/stream-debug-overlay';
|
|
|
23
23
|
export type { StreamDebugOverlayProps } from './stream-debug/stream-debug-overlay';
|
|
24
24
|
export { deriveWebrtcDebugStats, freezeVerdict, } from './stream-debug/webrtc-debug-stats';
|
|
25
25
|
export type { DeriveWebrtcDebugStatsInput, FreezeVerdict, FreezeVerdictKind, ResolutionMark, StreamDebugSample, WebrtcDebugStats, WebrtcDebugWindow, } from './stream-debug/webrtc-debug-stats';
|
|
26
|
+
export { buildPeerConnectionConfig } from './peer-connection-config';
|
|
27
|
+
export { EndpointResultsCard, EndpointTestCard, NatResultCard, } from './webrtc-diagnostics/EndpointTestCard';
|
|
28
|
+
export type { EndpointTestCardProps } from './webrtc-diagnostics/EndpointTestCard';
|
|
29
|
+
export { HUB_SESSION_RUN_MS, HubSessionTestCard, pinnedStreamChoices, } from './webrtc-diagnostics/HubSessionTestCard';
|
|
30
|
+
export type { HubSessionTestCardProps } from './webrtc-diagnostics/HubSessionTestCard';
|
|
31
|
+
export { RunReport } from './webrtc-diagnostics/RunReport';
|
|
32
|
+
export { classifyAddressScope, parseIceCandidate, summarizeHubCandidates, } from './webrtc-diagnostics/ice-candidates';
|
|
33
|
+
export type { HubCandidateSummary, HubReachabilityFlag, ParsedIceCandidate, } from './webrtc-diagnostics/ice-candidates';
|
|
34
|
+
export { compareSrflxMappings } from './webrtc-diagnostics/nat-mapping';
|
|
35
|
+
export type { NatMappingVerdict } from './webrtc-diagnostics/nat-mapping';
|
|
36
|
+
export { emptySessionRun, finalizeSessionRun, reduceSessionRun, } from './webrtc-diagnostics/session-run';
|
|
37
|
+
export type { HubSessionRunReport, SessionRunEvent, SessionRunState, } from './webrtc-diagnostics/session-run';
|
|
38
|
+
export { classifySessionVerdict } from './webrtc-diagnostics/session-verdict';
|
|
39
|
+
export type { DiagnosticIcePolicy, SessionVerdict, SessionVerdictLayer, } from './webrtc-diagnostics/session-verdict';
|
|
40
|
+
export type { EndpointTestRow, IceServerEntry, NatCheckResult, } from './webrtc-diagnostics/endpoint-test';
|
|
26
41
|
export { AutotrackSection } from './cap-settings/AutotrackSection';
|
|
27
42
|
export { ConsumablesPanel } from './cap-settings/ConsumablesPanel';
|
|
28
43
|
export type { CapSettingsComponentProps } from './cap-settings/index';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The player's `RTCConfiguration`, in one place for every connect path.
|
|
3
|
+
*
|
|
4
|
+
* The player never forces `iceTransportPolicy: 'relay'` on its own (werift's
|
|
5
|
+
* relay forward is unreliable across two real TURN servers, and relay would
|
|
6
|
+
* defeat the direct LAN/Tailscale path). The policy exists for the WebRTC
|
|
7
|
+
* diagnostics only, which run the SAME player once with `all` and once with
|
|
8
|
+
* `relay` to tell a reachable relay path from a dead one. Absent, the key is
|
|
9
|
+
* not set at all, so a production connect is byte-for-byte what it was.
|
|
10
|
+
*/
|
|
11
|
+
/** Public STUN kept as a fallback beside whatever the hub's TURN provider returns. */
|
|
12
|
+
export declare const FALLBACK_STUN_SERVER: RTCIceServer;
|
|
13
|
+
export declare function buildPeerConnectionConfig(iceServers: readonly RTCIceServer[] | undefined, iceTransportPolicy: RTCIceTransportPolicy | undefined): RTCConfiguration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { EndpointTestRow, IceServerEntry, NatCheckResult } from './endpoint-test';
|
|
3
|
+
export interface EndpointTestCardProps {
|
|
4
|
+
/** Fetch the live ICE server list (`turnProvider.getTurnServers`). */
|
|
5
|
+
readonly loadServers: () => Promise<readonly IceServerEntry[]>;
|
|
6
|
+
}
|
|
7
|
+
/** The per-endpoint result table — one row per tested ICE URL. */
|
|
8
|
+
export declare function EndpointResultsCard({ rows, }: {
|
|
9
|
+
readonly rows: readonly EndpointTestRow[];
|
|
10
|
+
}): ReactNode;
|
|
11
|
+
/** The NAT mapping verdict for this browser. */
|
|
12
|
+
export declare function NatResultCard({ result }: {
|
|
13
|
+
readonly result: NatCheckResult;
|
|
14
|
+
}): ReactNode;
|
|
15
|
+
export declare function EndpointTestCard({ loadServers }: EndpointTestCardProps): ReactNode;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { UseDeviceWebrtcTrpc } from '../../hooks/use-device-webrtc';
|
|
3
|
+
import { StreamChoice } from '../stream-panel';
|
|
4
|
+
export interface HubSessionTestCardProps {
|
|
5
|
+
readonly trpc: UseDeviceWebrtcTrpc;
|
|
6
|
+
/** The hub base URL the player talks to (the adaptive probe origin). */
|
|
7
|
+
readonly serverUrl: string;
|
|
8
|
+
/** The camera under test; null until the host picked one. */
|
|
9
|
+
readonly deviceId: number | null;
|
|
10
|
+
/** The host's camera picker, drawn at the top of the card. */
|
|
11
|
+
readonly cameraField?: ReactNode;
|
|
12
|
+
/** Length of each run. */
|
|
13
|
+
readonly runMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const HUB_SESSION_RUN_MS = 15000;
|
|
16
|
+
/** Pinned targets only, cheapest first: an adaptive target would climb mid-run. */
|
|
17
|
+
export declare function pinnedStreamChoices(streams: readonly StreamChoice[]): readonly StreamChoice[];
|
|
18
|
+
export declare function HubSessionTestCard({ trpc, serverUrl, deviceId, cameraField, runMs, }: HubSessionTestCardProps): ReactNode;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ParsedIceCandidate } from './ice-candidates';
|
|
2
|
+
/** A single flattened ICE server URL with optional credentials. */
|
|
3
|
+
export interface IceServerConfig {
|
|
4
|
+
readonly url: string;
|
|
5
|
+
readonly username?: string;
|
|
6
|
+
readonly credential?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ProbedCandidate {
|
|
9
|
+
readonly candidate: ParsedIceCandidate;
|
|
10
|
+
/** The server URL the browser says produced this candidate; null when not reported. */
|
|
11
|
+
readonly url: string | null;
|
|
12
|
+
/** udp / tcp / tls between the browser and the TURN server, relay candidates only. */
|
|
13
|
+
readonly relayProtocol: string | null;
|
|
14
|
+
/** Milliseconds from gather start. */
|
|
15
|
+
readonly tMs: number;
|
|
16
|
+
}
|
|
17
|
+
export interface GatherResult {
|
|
18
|
+
readonly candidates: readonly ProbedCandidate[];
|
|
19
|
+
readonly durationMs: number;
|
|
20
|
+
readonly timedOut: boolean;
|
|
21
|
+
readonly error: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface RelayLoopbackResult {
|
|
24
|
+
readonly connected: boolean;
|
|
25
|
+
readonly state: string;
|
|
26
|
+
readonly rttMs: number | null;
|
|
27
|
+
readonly relayProtocol: string | null;
|
|
28
|
+
readonly durationMs: number;
|
|
29
|
+
readonly error: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** `RTCIceCandidate.relayProtocol` — Chromium and WebKit report it; lib.dom does not type it. */
|
|
32
|
+
export declare function candidateRelayProtocol(c: RTCIceCandidate): string | null;
|
|
33
|
+
/** One local candidate from an ICE event, or null for the end-of-candidates event. */
|
|
34
|
+
export declare function probedCandidateFromEvent(e: RTCPeerConnectionIceEvent, tMs: number): ProbedCandidate | null;
|
|
35
|
+
export declare function gatherCandidates(servers: readonly IceServerConfig[], policy: RTCIceTransportPolicy, timeoutMs?: number): Promise<GatherResult>;
|
|
36
|
+
export declare function relayLoopback(servers: readonly IceServerConfig[], timeoutMs?: number): Promise<RelayLoopbackResult>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { GatherResult, IceServerConfig, RelayLoopbackResult } from './endpoint-probe';
|
|
2
|
+
import { NatMappingVerdict, SrflxObservation } from './nat-mapping';
|
|
3
|
+
export type IceEndpointScheme = 'stun' | 'turn' | 'turns' | 'other';
|
|
4
|
+
export interface IceEndpoint extends IceServerConfig {
|
|
5
|
+
/** Stable key: the URL is unique within the flattened list. */
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly scheme: IceEndpointScheme;
|
|
8
|
+
/** `?transport=` of the URL, lower-case; null when the URL names none. */
|
|
9
|
+
readonly transport: string | null;
|
|
10
|
+
readonly hasCredentials: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** The shape `turnProvider.getTurnServers` answers. */
|
|
13
|
+
export interface IceServerEntry {
|
|
14
|
+
readonly urls: string | readonly string[];
|
|
15
|
+
readonly username?: string;
|
|
16
|
+
readonly credential?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface EndpointTestRow {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly url: string;
|
|
21
|
+
readonly scheme: IceEndpointScheme;
|
|
22
|
+
readonly transport: string | null;
|
|
23
|
+
/** The public mapping a STUN answer gave this browser, `address:port`. */
|
|
24
|
+
readonly srflx: string | null;
|
|
25
|
+
/** Whether a relay candidate was allocated. */
|
|
26
|
+
readonly relay: boolean;
|
|
27
|
+
/** udp / tcp / tls to the TURN server — from the loopback's stats, else the candidate. */
|
|
28
|
+
readonly relayProtocol: string | null;
|
|
29
|
+
/** The URL the browser attributed the srflx / relay candidate to (the ICE event `url`). */
|
|
30
|
+
readonly eventUrl: string | null;
|
|
31
|
+
/** Relay loopback outcome; null for a STUN endpoint, which cannot relay. */
|
|
32
|
+
readonly relayConnected: boolean | null;
|
|
33
|
+
readonly rttMs: number | null;
|
|
34
|
+
readonly timedOut: boolean;
|
|
35
|
+
readonly error: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface NatCheckResult {
|
|
38
|
+
readonly destinations: readonly string[];
|
|
39
|
+
readonly verdict: NatMappingVerdict;
|
|
40
|
+
readonly error: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** Queried in addition to the configured STUN servers until two distinct destinations exist. */
|
|
43
|
+
export declare const FALLBACK_STUN_URLS: readonly string[];
|
|
44
|
+
export declare function schemeOf(url: string): IceEndpointScheme;
|
|
45
|
+
export declare function transportOf(url: string): string | null;
|
|
46
|
+
/** One row per URL: an entry's `urls` may be a string or a list. */
|
|
47
|
+
export declare function flattenIceServers(servers: readonly IceServerEntry[]): readonly IceEndpoint[];
|
|
48
|
+
export declare function endpointRowFromProbe(endpoint: IceEndpoint, gather: GatherResult, loopback: RelayLoopbackResult | null): EndpointTestRow;
|
|
49
|
+
/** Gather against ONE endpoint; for a TURN endpoint, also prove the relay forwards. */
|
|
50
|
+
export declare function testEndpoint(endpoint: IceEndpoint): Promise<EndpointTestRow>;
|
|
51
|
+
/** The STUN destinations the NAT check queries: configured first, public fallbacks to reach two. */
|
|
52
|
+
export declare function natCheckDestinations(endpoints: readonly IceEndpoint[]): readonly string[];
|
|
53
|
+
export declare function srflxObservations(gather: GatherResult): readonly SrflxObservation[];
|
|
54
|
+
export declare function runNatCheck(endpoints: readonly IceEndpoint[]): Promise<NatCheckResult>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering helpers for the WebRTC diagnostics: an unmeasured value is the
|
|
3
|
+
* word "unknown", never a dash that reads as zero and never a 0 (D393).
|
|
4
|
+
*/
|
|
5
|
+
export declare const UNKNOWN_TEXT = "unknown";
|
|
6
|
+
export declare function formatMaybe<T>(value: T | null, render: (v: T) => string): string;
|
|
7
|
+
export declare const fmtMs: (v: number | null) => string;
|
|
8
|
+
export declare const fmtCount: (v: number | null) => string;
|
|
9
|
+
export declare const fmtBytes: (v: number | null) => string;
|
|
10
|
+
export declare const fmtPercent: (v: number | null) => string;
|
|
11
|
+
export declare const fmtText: (v: string | null) => string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICE candidate parsing for the WebRTC diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* The hub session test has to answer "what addresses did the HUB offer this
|
|
5
|
+
* browser?" — the one fact that decides whether a remote or relay-only client
|
|
6
|
+
* can ever reach it. The answer lives in two places: the `a=candidate` lines of
|
|
7
|
+
* the hub's answer SDP and the candidates it trickles afterwards
|
|
8
|
+
* (`webrtcSession.getIceCandidates`). Both are the RFC 8839 grammar:
|
|
9
|
+
*
|
|
10
|
+
* candidate:<foundation> <component> <transport> <priority> <address> <port>
|
|
11
|
+
* typ <type> [raddr <address> rport <port>] [tcptype <t>] ...
|
|
12
|
+
*
|
|
13
|
+
* Everything that could not be read stays `null` (D393): a candidate whose
|
|
14
|
+
* port did not parse has an unknown port, not port 0.
|
|
15
|
+
*/
|
|
16
|
+
export type IceCandidateType = 'host' | 'srflx' | 'prflx' | 'relay' | 'unknown';
|
|
17
|
+
/** Where an address is reachable from. `tailscale` is the 100.64.0.0/10
|
|
18
|
+
* CGNAT range and Tailscale's `fd7a:115c:a1e0::/48` — reachable only from
|
|
19
|
+
* inside that tailnet, exactly like a LAN address. */
|
|
20
|
+
export type AddressScope = 'loopback' | 'link-local' | 'lan' | 'tailscale' | 'mdns' | 'public' | 'unknown';
|
|
21
|
+
export type AddressFamily = 'ipv4' | 'ipv6' | 'mdns' | 'unknown';
|
|
22
|
+
export interface ParsedIceCandidate {
|
|
23
|
+
readonly type: IceCandidateType;
|
|
24
|
+
readonly protocol: string | null;
|
|
25
|
+
readonly address: string | null;
|
|
26
|
+
readonly port: number | null;
|
|
27
|
+
readonly relatedAddress: string | null;
|
|
28
|
+
readonly relatedPort: number | null;
|
|
29
|
+
readonly family: AddressFamily;
|
|
30
|
+
readonly scope: AddressScope;
|
|
31
|
+
/** The candidate attribute as received, without `a=`. */
|
|
32
|
+
readonly raw: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* How reachable the hub's offered set is from outside its own networks.
|
|
36
|
+
* - `public-path`: at least one srflx, relay or public host candidate.
|
|
37
|
+
* - `private-only`: candidates exist, all LAN / Tailscale / loopback / mDNS.
|
|
38
|
+
* - `none`: the hub answered and offered nothing (yet).
|
|
39
|
+
* - `unknown`: no answer was received, so nothing was measured.
|
|
40
|
+
*/
|
|
41
|
+
export type HubReachabilityFlag = 'public-path' | 'private-only' | 'none' | 'unknown';
|
|
42
|
+
export interface HubCandidateSummary {
|
|
43
|
+
readonly candidates: readonly ParsedIceCandidate[];
|
|
44
|
+
readonly fromSdp: number;
|
|
45
|
+
readonly fromTrickle: number;
|
|
46
|
+
readonly countByType: Readonly<Record<IceCandidateType, number>>;
|
|
47
|
+
readonly hasSrflx: boolean;
|
|
48
|
+
readonly hasRelay: boolean;
|
|
49
|
+
readonly hasPublicHost: boolean;
|
|
50
|
+
readonly flag: HubReachabilityFlag;
|
|
51
|
+
/** Whether the hub reported its gathering complete; null when never asked. */
|
|
52
|
+
readonly trickleDone: boolean | null;
|
|
53
|
+
}
|
|
54
|
+
export interface SummarizeHubCandidatesInput {
|
|
55
|
+
readonly sdpAnswer: string | null;
|
|
56
|
+
readonly trickled: readonly string[];
|
|
57
|
+
readonly trickleDone: boolean | null;
|
|
58
|
+
}
|
|
59
|
+
export declare function classifyAddressScope(address: string): AddressScope;
|
|
60
|
+
/** Parse one candidate attribute (`candidate:…`, with or without `a=`). */
|
|
61
|
+
export declare function parseIceCandidate(line: string): ParsedIceCandidate | null;
|
|
62
|
+
/** Every `a=candidate` line of an SDP, in order. */
|
|
63
|
+
export declare function candidateLinesFromSdp(sdp: string): readonly string[];
|
|
64
|
+
export declare function countCandidateTypes(candidates: readonly ParsedIceCandidate[]): Readonly<Record<IceCandidateType, number>>;
|
|
65
|
+
/** The hub's offered candidates: answer SDP plus trickle, deduplicated. */
|
|
66
|
+
export declare function summarizeHubCandidates(input: SummarizeHubCandidatesInput): HubCandidateSummary;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cheap client-side NAT mapping check.
|
|
3
|
+
*
|
|
4
|
+
* One peer connection asks two STUN destinations (two servers, or one server
|
|
5
|
+
* on two ports) for its server-reflexive address. Both requests leave from the
|
|
6
|
+
* SAME local socket, so:
|
|
7
|
+
* - the same public `address:port` from both ⇒ the NAT reuses one mapping for
|
|
8
|
+
* every destination (endpoint-independent): a srflx candidate is usable by a
|
|
9
|
+
* peer, direct media can work;
|
|
10
|
+
* - a different public port per destination ⇒ endpoint-dependent mapping
|
|
11
|
+
* ("symmetric NAT"): the mapping the hub would need is never the one STUN
|
|
12
|
+
* saw, so only a TURN relay reaches this browser.
|
|
13
|
+
*
|
|
14
|
+
* Browsers drop a srflx candidate identical to one already gathered, so two
|
|
15
|
+
* servers agreeing usually shows as ONE candidate. That is reported as
|
|
16
|
+
* `likely`, never `confirmed`, because it cannot be told apart from the second
|
|
17
|
+
* server simply not answering.
|
|
18
|
+
*/
|
|
19
|
+
export interface SrflxObservation {
|
|
20
|
+
readonly address: string;
|
|
21
|
+
readonly port: number;
|
|
22
|
+
readonly protocol: string;
|
|
23
|
+
/** The local base (`raddr` / `rport`); `0.0.0.0` / `0` when the browser hides it. */
|
|
24
|
+
readonly relatedAddress: string | null;
|
|
25
|
+
readonly relatedPort: number | null;
|
|
26
|
+
/** The STUN URL that produced it (`RTCPeerConnectionIceEvent.url`), when reported. */
|
|
27
|
+
readonly url: string | null;
|
|
28
|
+
}
|
|
29
|
+
export type NatMappingKind = 'endpoint-independent' | 'endpoint-dependent' | 'unknown';
|
|
30
|
+
export type NatMappingConfidence = 'confirmed' | 'likely' | 'none';
|
|
31
|
+
export interface NatMappingVerdict {
|
|
32
|
+
readonly kind: NatMappingKind;
|
|
33
|
+
readonly confidence: NatMappingConfidence;
|
|
34
|
+
readonly reason: string;
|
|
35
|
+
/** Distinct public mappings seen per local base, e.g. `["203.0.113.7:61000"]`. */
|
|
36
|
+
readonly mappings: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The distinct STUN destinations (`stun:host:port`) among the given URLs —
|
|
40
|
+
* the number the check needs to be at least two. TURN URLs are excluded:
|
|
41
|
+
* this check is about plain STUN binding.
|
|
42
|
+
*/
|
|
43
|
+
export declare function distinctStunDestinations(urls: readonly string[]): readonly string[];
|
|
44
|
+
export declare function compareSrflxMappings(observations: readonly SrflxObservation[], stunDestinationsQueried: number): NatMappingVerdict;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { WebrtcSessionDebug } from '@camstack/types';
|
|
2
|
+
import { StreamDebugSample, WebrtcDebugStats } from '../stream-debug/webrtc-debug-stats';
|
|
3
|
+
import { HubCandidateSummary, IceCandidateType, ParsedIceCandidate } from './ice-candidates';
|
|
4
|
+
import { DiagnosticIcePolicy, SessionVerdict } from './session-verdict';
|
|
5
|
+
export type SessionRunEvent = {
|
|
6
|
+
readonly kind: 'offer-answered';
|
|
7
|
+
readonly at: number;
|
|
8
|
+
readonly sessionId: string;
|
|
9
|
+
readonly sdpAnswer: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'hub-candidates';
|
|
12
|
+
readonly at: number;
|
|
13
|
+
readonly candidates: readonly string[];
|
|
14
|
+
readonly done: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'local-candidate';
|
|
17
|
+
readonly at: number;
|
|
18
|
+
readonly candidate: string;
|
|
19
|
+
readonly url: string | null;
|
|
20
|
+
readonly relayProtocol: string | null;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'ice-state';
|
|
23
|
+
readonly at: number;
|
|
24
|
+
readonly state: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'dtls-state';
|
|
27
|
+
readonly at: number;
|
|
28
|
+
readonly state: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'peer-state';
|
|
31
|
+
readonly at: number;
|
|
32
|
+
readonly state: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'first-frame';
|
|
35
|
+
readonly at: number;
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: 'sample';
|
|
38
|
+
readonly at: number;
|
|
39
|
+
readonly sample: StreamDebugSample;
|
|
40
|
+
} | {
|
|
41
|
+
readonly kind: 'reconnect';
|
|
42
|
+
readonly at: number;
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: 'error';
|
|
45
|
+
readonly at: number;
|
|
46
|
+
readonly message: string;
|
|
47
|
+
};
|
|
48
|
+
export interface StateMark {
|
|
49
|
+
readonly state: string;
|
|
50
|
+
readonly at: number;
|
|
51
|
+
}
|
|
52
|
+
export interface LocalCandidateRecord {
|
|
53
|
+
readonly candidate: ParsedIceCandidate;
|
|
54
|
+
readonly url: string | null;
|
|
55
|
+
readonly relayProtocol: string | null;
|
|
56
|
+
}
|
|
57
|
+
export interface SessionRunState {
|
|
58
|
+
readonly policy: DiagnosticIcePolicy;
|
|
59
|
+
readonly sessionIds: readonly string[];
|
|
60
|
+
readonly sdpAnswer: string | null;
|
|
61
|
+
readonly trickled: readonly string[];
|
|
62
|
+
readonly trickleDone: boolean | null;
|
|
63
|
+
readonly local: readonly LocalCandidateRecord[];
|
|
64
|
+
readonly ice: readonly StateMark[];
|
|
65
|
+
readonly dtls: readonly StateMark[];
|
|
66
|
+
readonly peer: readonly StateMark[];
|
|
67
|
+
readonly firstFrameAt: number | null;
|
|
68
|
+
/** The last sample that carried client stats — a later failed poll keeps it. */
|
|
69
|
+
readonly lastStats: WebrtcDebugStats | null;
|
|
70
|
+
readonly lastServer: WebrtcSessionDebug | null;
|
|
71
|
+
readonly maxServerPacketsSent: number | null;
|
|
72
|
+
readonly lastServerError: string | null;
|
|
73
|
+
readonly samples: number;
|
|
74
|
+
readonly reconnects: number;
|
|
75
|
+
readonly errors: readonly string[];
|
|
76
|
+
}
|
|
77
|
+
export declare function emptySessionRun(policy: DiagnosticIcePolicy): SessionRunState;
|
|
78
|
+
export declare function reduceSessionRun(state: SessionRunState, event: SessionRunEvent): SessionRunState;
|
|
79
|
+
export interface SessionTimeline {
|
|
80
|
+
readonly iceConnectedMs: number | null;
|
|
81
|
+
readonly dtlsConnectedMs: number | null;
|
|
82
|
+
readonly firstFrameMs: number | null;
|
|
83
|
+
readonly iceFinalState: string | null;
|
|
84
|
+
readonly dtlsFinalState: string | null;
|
|
85
|
+
}
|
|
86
|
+
export interface SessionPath {
|
|
87
|
+
/** `local/remote` candidate types of the selected pair, e.g. `relay/host`. */
|
|
88
|
+
readonly pairType: string | null;
|
|
89
|
+
readonly protocol: string | null;
|
|
90
|
+
readonly rttMs: number | null;
|
|
91
|
+
}
|
|
92
|
+
export interface SessionMedia {
|
|
93
|
+
readonly clientBytesReceived: number | null;
|
|
94
|
+
readonly clientPacketsReceived: number | null;
|
|
95
|
+
readonly framesDecoded: number | null;
|
|
96
|
+
readonly serverPacketsSent: number | null;
|
|
97
|
+
readonly serverAlive: boolean | null;
|
|
98
|
+
readonly servedTier: string | null;
|
|
99
|
+
readonly egressLeg: string | null;
|
|
100
|
+
readonly lossPercent: number | null;
|
|
101
|
+
readonly freezeCount: number | null;
|
|
102
|
+
readonly totalFreezeMs: number | null;
|
|
103
|
+
readonly pliCount: number | null;
|
|
104
|
+
readonly nackCount: number | null;
|
|
105
|
+
}
|
|
106
|
+
export interface SessionLocalCandidates {
|
|
107
|
+
readonly countByType: Readonly<Record<IceCandidateType, number>>;
|
|
108
|
+
readonly relayProtocols: readonly string[];
|
|
109
|
+
readonly urls: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
export interface HubSessionRunReport {
|
|
112
|
+
readonly policy: DiagnosticIcePolicy;
|
|
113
|
+
readonly durationMs: number;
|
|
114
|
+
readonly sessionIds: readonly string[];
|
|
115
|
+
readonly hub: HubCandidateSummary;
|
|
116
|
+
readonly local: SessionLocalCandidates;
|
|
117
|
+
readonly timeline: SessionTimeline;
|
|
118
|
+
readonly path: SessionPath;
|
|
119
|
+
readonly media: SessionMedia;
|
|
120
|
+
readonly reconnects: number;
|
|
121
|
+
readonly errors: readonly string[];
|
|
122
|
+
readonly serverError: string | null;
|
|
123
|
+
readonly verdict: SessionVerdict;
|
|
124
|
+
}
|
|
125
|
+
export declare function finalizeSessionRun(state: SessionRunState, durationMs: number): HubSessionRunReport;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HubReachabilityFlag } from './ice-candidates';
|
|
2
|
+
export type SessionVerdictLayer = 'transport' | 'hub-dtls' | 'media-black-hole' | 'hub-not-sending' | 'loss-keyframe' | 'healthy' | 'unknown';
|
|
3
|
+
export interface SessionVerdict {
|
|
4
|
+
readonly layer: SessionVerdictLayer;
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly detail: string;
|
|
7
|
+
}
|
|
8
|
+
export type DiagnosticIcePolicy = 'all' | 'relay';
|
|
9
|
+
export interface VerdictInput {
|
|
10
|
+
readonly policy: DiagnosticIcePolicy;
|
|
11
|
+
readonly durationMs: number;
|
|
12
|
+
readonly iceConnected: boolean;
|
|
13
|
+
readonly iceFinalState: string | null;
|
|
14
|
+
readonly dtlsConnected: boolean;
|
|
15
|
+
readonly dtlsFinalState: string | null;
|
|
16
|
+
readonly hubFlag: HubReachabilityFlag;
|
|
17
|
+
readonly clientPacketsReceived: number | null;
|
|
18
|
+
readonly framesDecoded: number | null;
|
|
19
|
+
readonly serverPacketsSent: number | null;
|
|
20
|
+
readonly pliCount: number | null;
|
|
21
|
+
readonly freezeCount: number | null;
|
|
22
|
+
readonly lossPercent: number | null;
|
|
23
|
+
}
|
|
24
|
+
/** PLIs the browser may send in one run before it counts as a storm: one at
|
|
25
|
+
* start-up plus one recovery is normal; three means keyframes keep breaking. */
|
|
26
|
+
export declare const PLI_STORM_COUNT = 3;
|
|
27
|
+
/** Packet loss over the run above which a freeze is attributed to loss. */
|
|
28
|
+
export declare const LOSS_PERCENT_HIGH = 2;
|
|
29
|
+
export declare function classifySessionVerdict(input: VerdictInput): SessionVerdict;
|
|
@@ -1529,6 +1529,8 @@ export declare const usePipelineOrchestratorSetCameraStepToggle: typeof trpc.pip
|
|
|
1529
1529
|
export declare const usePipelineOrchestratorGetCameraStepOverrides: typeof trpc.pipelineOrchestrator.getCameraStepOverrides.useQuery;
|
|
1530
1530
|
/** Generated alias around `trpc.pipelineOrchestrator.setCameraStepOverride.useMutation`. */
|
|
1531
1531
|
export declare const usePipelineOrchestratorSetCameraStepOverride: typeof trpc.pipelineOrchestrator.setCameraStepOverride.useMutation;
|
|
1532
|
+
/** Generated alias around `trpc.pipelineOrchestrator.setCameraStepSettings.useMutation`. */
|
|
1533
|
+
export declare const usePipelineOrchestratorSetCameraStepSettings: typeof trpc.pipelineOrchestrator.setCameraStepSettings.useMutation;
|
|
1532
1534
|
/** Generated alias around `trpc.pipelineOrchestrator.setCameraPipelineForAgent.useMutation`. */
|
|
1533
1535
|
export declare const usePipelineOrchestratorSetCameraPipelineForAgent: typeof trpc.pipelineOrchestrator.setCameraPipelineForAgent.useMutation;
|
|
1534
1536
|
/** Generated alias around `trpc.pipelineOrchestrator.resolvePipeline.useQuery`. */
|