@camstack/ui-library 1.2.185 → 1.2.186
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.
|
@@ -2,6 +2,7 @@ import { ReactNode } from 'react';
|
|
|
2
2
|
import { CamProfile, WebrtcSessionDebug } from '@camstack/types';
|
|
3
3
|
import { ReconnectAction } from './reconnect-schedule';
|
|
4
4
|
import { AdaptiveHintsReport } from './adaptive-downlink';
|
|
5
|
+
import { StreamDebugSample } from './stream-debug/webrtc-debug-stats.js';
|
|
5
6
|
type WebkitPresentationMode = 'inline' | 'picture-in-picture' | 'fullscreen';
|
|
6
7
|
declare global {
|
|
7
8
|
interface HTMLVideoElement {
|
|
@@ -116,6 +117,20 @@ export interface CameraStreamPlayerProps {
|
|
|
116
117
|
/** Show a live client-side WebRTC stats overlay (bitrate/fps/res/RTT),
|
|
117
118
|
* computed from the PeerConnection's getStats(). Default: false. */
|
|
118
119
|
showStats?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Report the same measurements to a HOST instead of (or as well as) drawing
|
|
122
|
+
* them.
|
|
123
|
+
*
|
|
124
|
+
* The overlay is a DOM layer, and in the native app the player lives inside a
|
|
125
|
+
* WebView whose touches the RN gesture layer owns — so the panel that opens
|
|
126
|
+
* on a tap can never be opened there. The numbers still have to reach the
|
|
127
|
+
* operator, so they travel out to whoever mounted the player and are rendered
|
|
128
|
+
* in its own UI, next to the battery and link details.
|
|
129
|
+
*
|
|
130
|
+
* Wiring this turns the two 1 Hz polls on by itself: a host that asked for
|
|
131
|
+
* samples wants them whether or not the in-picture overlay is drawn.
|
|
132
|
+
*/
|
|
133
|
+
onDebugStats?: (sample: StreamDebugSample) => void;
|
|
119
134
|
/** When provided, polls inbound-video getStats() every 5 s during playback
|
|
120
135
|
* and reports per-window health metrics (frames received/decoded/dropped,
|
|
121
136
|
* freezes, jitter, playback-clock advance). The embed forwards these over the
|
|
@@ -287,5 +302,5 @@ export interface CameraStreamPlayerProps {
|
|
|
287
302
|
*/
|
|
288
303
|
onVideoElement?: (el: HTMLVideoElement | null) => void;
|
|
289
304
|
}
|
|
290
|
-
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onPlaybackStats, onClientNetworkSample, onConnectTiming, className, onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
305
|
+
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onDebugStats, onPlaybackStats, onClientNetworkSample, onConnectTiming, className, onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
291
306
|
export {};
|
|
@@ -20,7 +20,7 @@ export { CameraStreamPlayer } from './camera-stream-player';
|
|
|
20
20
|
export { StreamDebugOverlay } from './stream-debug/stream-debug-overlay';
|
|
21
21
|
export type { StreamDebugOverlayProps } from './stream-debug/stream-debug-overlay';
|
|
22
22
|
export { deriveWebrtcDebugStats, freezeVerdict, } from './stream-debug/webrtc-debug-stats';
|
|
23
|
-
export type { DeriveWebrtcDebugStatsInput, FreezeVerdict, FreezeVerdictKind, ResolutionMark, WebrtcDebugStats, WebrtcDebugWindow, } from './stream-debug/webrtc-debug-stats';
|
|
23
|
+
export type { DeriveWebrtcDebugStatsInput, FreezeVerdict, FreezeVerdictKind, ResolutionMark, StreamDebugSample, WebrtcDebugStats, WebrtcDebugWindow, } from './stream-debug/webrtc-debug-stats';
|
|
24
24
|
export { AutotrackSection } from './cap-settings/AutotrackSection';
|
|
25
25
|
export { ConsumablesPanel } from './cap-settings/ConsumablesPanel';
|
|
26
26
|
export type { CapSettingsComponentProps } from './cap-settings/index';
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Pure derivation of the stream-debug overlay's CLIENT half from one
|
|
3
|
-
* `RTCPeerConnection.getStats()` report.
|
|
4
|
-
*
|
|
5
|
-
* Why it is pure: this is the surface an operator reads while a camera is
|
|
6
|
-
* frozen, and the numbers it shows decide whether the stall is in the
|
|
7
|
-
* transport or in the decoder. A derivation that can only be exercised by
|
|
8
|
-
* mounting a player and waiting for a real freeze is a derivation nobody
|
|
9
|
-
* checks. Everything here is a function of (report, previous sample, clock).
|
|
10
|
-
*
|
|
11
|
-
* D393 — a measurement that FAILED is `null`, never `0`. A stat the browser
|
|
12
|
-
* did not report is absent, and "absent" must never render as "zero" in a
|
|
13
|
-
* debug tool: `freezeCount: 0` means the decoder measured no freeze,
|
|
14
|
-
* `freezeCount: null` means nobody measured. Those are opposite conclusions.
|
|
15
|
-
*/
|
|
1
|
+
import { WebrtcSessionDebug } from '@camstack/types';
|
|
16
2
|
/** One resolution the stream actually presented, and when it started. */
|
|
17
3
|
export interface ResolutionMark {
|
|
18
4
|
readonly width: number;
|
|
@@ -127,3 +113,28 @@ export interface FreezeVerdict {
|
|
|
127
113
|
readonly label: string;
|
|
128
114
|
}
|
|
129
115
|
export declare function freezeVerdict(stats: WebrtcDebugStats | null): FreezeVerdict;
|
|
116
|
+
/**
|
|
117
|
+
* One reported sample: both halves of the stream-debug answer, plus the session
|
|
118
|
+
* they describe.
|
|
119
|
+
*
|
|
120
|
+
* It exists because the overlay cannot always be the surface. In the native app
|
|
121
|
+
* the player lives inside a WebView whose touches belong to the RN gesture
|
|
122
|
+
* layer, so the in-picture panel can never be opened there — the numbers have
|
|
123
|
+
* to leave the player and be drawn by the host, next to the battery and link
|
|
124
|
+
* details where the operator already looks.
|
|
125
|
+
*
|
|
126
|
+
* Everything in it is nullable for the same reason it is nullable everywhere
|
|
127
|
+
* else in this file: a measurement that did not happen is not a zero.
|
|
128
|
+
*/
|
|
129
|
+
export interface StreamDebugSample {
|
|
130
|
+
/** When this sample was taken, by the reporting player's clock. */
|
|
131
|
+
readonly at: number;
|
|
132
|
+
/** The session both halves describe; `null` before the player has one. */
|
|
133
|
+
readonly sessionId: string | null;
|
|
134
|
+
readonly stats: WebrtcDebugStats | null;
|
|
135
|
+
readonly server: WebrtcSessionDebug | null;
|
|
136
|
+
/** Why the last server poll produced nothing, verbatim. */
|
|
137
|
+
readonly serverError: string | null;
|
|
138
|
+
/** Whether the player was given a way to ask the server at all. */
|
|
139
|
+
readonly serverWired: boolean;
|
|
140
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -27667,7 +27667,7 @@ function mergePlayerHints(autoHints, override, downlinkMbps, coldStart) {
|
|
|
27667
27667
|
return coldStart ? stripDownlinkForColdStart(merged) : merged;
|
|
27668
27668
|
}
|
|
27669
27669
|
var FIRST_FRAME_TIMEOUT_MS = 8e3;
|
|
27670
|
-
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27670
|
+
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onDebugStats, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27671
27671
|
const videoRef = (0, react$1.useRef)(null);
|
|
27672
27672
|
const containerRef = (0, react$1.useRef)(null);
|
|
27673
27673
|
const pcRef = (0, react$1.useRef)(null);
|
|
@@ -28568,13 +28568,14 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28568
28568
|
const [serverDebug, setServerDebug] = (0, react$1.useState)(null);
|
|
28569
28569
|
const [serverDebugError, setServerDebugError] = (0, react$1.useState)(null);
|
|
28570
28570
|
const [debugNow, setDebugNow] = (0, react$1.useState)(() => Date.now());
|
|
28571
|
+
const debugWanted = showStats || onDebugStats !== void 0;
|
|
28571
28572
|
(0, react$1.useEffect)(() => {
|
|
28572
28573
|
if (!showStats) return;
|
|
28573
28574
|
const id = setInterval(() => setDebugNow(Date.now()), 500);
|
|
28574
28575
|
return () => clearInterval(id);
|
|
28575
28576
|
}, [showStats]);
|
|
28576
28577
|
(0, react$1.useEffect)(() => {
|
|
28577
|
-
if (!
|
|
28578
|
+
if (!debugWanted) {
|
|
28578
28579
|
setDebugStats(null);
|
|
28579
28580
|
return;
|
|
28580
28581
|
}
|
|
@@ -28605,9 +28606,9 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28605
28606
|
cancelled = true;
|
|
28606
28607
|
clearInterval(id);
|
|
28607
28608
|
};
|
|
28608
|
-
}, [
|
|
28609
|
+
}, [debugWanted]);
|
|
28609
28610
|
(0, react$1.useEffect)(() => {
|
|
28610
|
-
if (!
|
|
28611
|
+
if (!debugWanted || !getSessionDebug) {
|
|
28611
28612
|
setServerDebug(null);
|
|
28612
28613
|
setServerDebugError(null);
|
|
28613
28614
|
return;
|
|
@@ -28637,7 +28638,31 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28637
28638
|
cancelled = true;
|
|
28638
28639
|
clearInterval(id);
|
|
28639
28640
|
};
|
|
28640
|
-
}, [
|
|
28641
|
+
}, [debugWanted, getSessionDebug]);
|
|
28642
|
+
const onDebugStatsRef = (0, react$1.useRef)(onDebugStats);
|
|
28643
|
+
onDebugStatsRef.current = onDebugStats;
|
|
28644
|
+
(0, react$1.useEffect)(() => {
|
|
28645
|
+
if (onDebugStats === void 0) return;
|
|
28646
|
+
const emit = () => {
|
|
28647
|
+
onDebugStatsRef.current?.({
|
|
28648
|
+
at: Date.now(),
|
|
28649
|
+
sessionId: activeSessionIdRef.current,
|
|
28650
|
+
stats: debugStatsRef.current,
|
|
28651
|
+
server: serverDebugRef.current,
|
|
28652
|
+
serverError: serverDebugErrorRef.current,
|
|
28653
|
+
serverWired: getSessionDebug !== void 0
|
|
28654
|
+
});
|
|
28655
|
+
};
|
|
28656
|
+
emit();
|
|
28657
|
+
const id = setInterval(emit, 1e3);
|
|
28658
|
+
return () => clearInterval(id);
|
|
28659
|
+
}, [onDebugStats === void 0, getSessionDebug]);
|
|
28660
|
+
const debugStatsRef = (0, react$1.useRef)(null);
|
|
28661
|
+
debugStatsRef.current = debugStats;
|
|
28662
|
+
const serverDebugRef = (0, react$1.useRef)(null);
|
|
28663
|
+
serverDebugRef.current = serverDebug;
|
|
28664
|
+
const serverDebugErrorRef = (0, react$1.useRef)(null);
|
|
28665
|
+
serverDebugErrorRef.current = serverDebugError;
|
|
28641
28666
|
const netPrev = (0, react$1.useRef)(null);
|
|
28642
28667
|
(0, react$1.useEffect)(() => {
|
|
28643
28668
|
if (!onClientNetworkSample) {
|
package/dist/index.js
CHANGED
|
@@ -27643,7 +27643,7 @@ function mergePlayerHints(autoHints, override, downlinkMbps, coldStart) {
|
|
|
27643
27643
|
return coldStart ? stripDownlinkForColdStart(merged) : merged;
|
|
27644
27644
|
}
|
|
27645
27645
|
var FIRST_FRAME_TIMEOUT_MS = 8e3;
|
|
27646
|
-
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27646
|
+
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onDebugStats, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27647
27647
|
const videoRef = useRef(null);
|
|
27648
27648
|
const containerRef = useRef(null);
|
|
27649
27649
|
const pcRef = useRef(null);
|
|
@@ -28544,13 +28544,14 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28544
28544
|
const [serverDebug, setServerDebug] = useState(null);
|
|
28545
28545
|
const [serverDebugError, setServerDebugError] = useState(null);
|
|
28546
28546
|
const [debugNow, setDebugNow] = useState(() => Date.now());
|
|
28547
|
+
const debugWanted = showStats || onDebugStats !== void 0;
|
|
28547
28548
|
useEffect(() => {
|
|
28548
28549
|
if (!showStats) return;
|
|
28549
28550
|
const id = setInterval(() => setDebugNow(Date.now()), 500);
|
|
28550
28551
|
return () => clearInterval(id);
|
|
28551
28552
|
}, [showStats]);
|
|
28552
28553
|
useEffect(() => {
|
|
28553
|
-
if (!
|
|
28554
|
+
if (!debugWanted) {
|
|
28554
28555
|
setDebugStats(null);
|
|
28555
28556
|
return;
|
|
28556
28557
|
}
|
|
@@ -28581,9 +28582,9 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28581
28582
|
cancelled = true;
|
|
28582
28583
|
clearInterval(id);
|
|
28583
28584
|
};
|
|
28584
|
-
}, [
|
|
28585
|
+
}, [debugWanted]);
|
|
28585
28586
|
useEffect(() => {
|
|
28586
|
-
if (!
|
|
28587
|
+
if (!debugWanted || !getSessionDebug) {
|
|
28587
28588
|
setServerDebug(null);
|
|
28588
28589
|
setServerDebugError(null);
|
|
28589
28590
|
return;
|
|
@@ -28613,7 +28614,31 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28613
28614
|
cancelled = true;
|
|
28614
28615
|
clearInterval(id);
|
|
28615
28616
|
};
|
|
28616
|
-
}, [
|
|
28617
|
+
}, [debugWanted, getSessionDebug]);
|
|
28618
|
+
const onDebugStatsRef = useRef(onDebugStats);
|
|
28619
|
+
onDebugStatsRef.current = onDebugStats;
|
|
28620
|
+
useEffect(() => {
|
|
28621
|
+
if (onDebugStats === void 0) return;
|
|
28622
|
+
const emit = () => {
|
|
28623
|
+
onDebugStatsRef.current?.({
|
|
28624
|
+
at: Date.now(),
|
|
28625
|
+
sessionId: activeSessionIdRef.current,
|
|
28626
|
+
stats: debugStatsRef.current,
|
|
28627
|
+
server: serverDebugRef.current,
|
|
28628
|
+
serverError: serverDebugErrorRef.current,
|
|
28629
|
+
serverWired: getSessionDebug !== void 0
|
|
28630
|
+
});
|
|
28631
|
+
};
|
|
28632
|
+
emit();
|
|
28633
|
+
const id = setInterval(emit, 1e3);
|
|
28634
|
+
return () => clearInterval(id);
|
|
28635
|
+
}, [onDebugStats === void 0, getSessionDebug]);
|
|
28636
|
+
const debugStatsRef = useRef(null);
|
|
28637
|
+
debugStatsRef.current = debugStats;
|
|
28638
|
+
const serverDebugRef = useRef(null);
|
|
28639
|
+
serverDebugRef.current = serverDebug;
|
|
28640
|
+
const serverDebugErrorRef = useRef(null);
|
|
28641
|
+
serverDebugErrorRef.current = serverDebugError;
|
|
28617
28642
|
const netPrev = useRef(null);
|
|
28618
28643
|
useEffect(() => {
|
|
28619
28644
|
if (!onClientNetworkSample) {
|