@camstack/ui-library 1.1.36 → 1.1.37
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 +20 -1
- package/dist/index.cjs +53 -1
- package/dist/index.js +53 -1
- package/package.json +1 -1
|
@@ -84,6 +84,15 @@ export interface ClientStreamHints {
|
|
|
84
84
|
downlinkMbps?: number;
|
|
85
85
|
prefersTier?: string;
|
|
86
86
|
}
|
|
87
|
+
/** Client-observed network conditions, sampled from the live PeerConnection.
|
|
88
|
+
* Matches the `networkQuality.reportClientStats` mutation input (minus
|
|
89
|
+
* `deviceId`, which the caller adds). */
|
|
90
|
+
export interface ClientNetworkSample {
|
|
91
|
+
readonly rttMs: number;
|
|
92
|
+
readonly jitterMs: number;
|
|
93
|
+
readonly estimatedBandwidthKbps: number;
|
|
94
|
+
readonly packetLossPercent: number;
|
|
95
|
+
}
|
|
87
96
|
export interface CameraStreamPlayerProps {
|
|
88
97
|
/** Server base URL (e.g. window.location.origin) */
|
|
89
98
|
serverUrl: string;
|
|
@@ -111,6 +120,16 @@ export interface CameraStreamPlayerProps {
|
|
|
111
120
|
* existing host log bridge (`{type:'log'}`), so they respect the app's
|
|
112
121
|
* debug-mode flag. Used to diagnose recorded VOD-over-WebRTC stutter. */
|
|
113
122
|
onPlaybackStats?: (data: Record<string, unknown>) => void;
|
|
123
|
+
/**
|
|
124
|
+
* When provided, samples the inbound-video getStats() ~every 2s and reports
|
|
125
|
+
* the CLIENT's live network conditions (packet-loss %, jitter, downlink
|
|
126
|
+
* bitrate, RTT) so the caller can push them to the server's
|
|
127
|
+
* `networkQuality.reportClientStats`. This is what lets the broker's ADAPTIVE
|
|
128
|
+
* ladder actually downgrade "auto" under a poor link: without a client
|
|
129
|
+
* signal the server sees only coarse RTCP RR and tends to stay on the top
|
|
130
|
+
* tier. Inert (zero behaviour change) unless a callback is passed; wire it
|
|
131
|
+
* only for adaptive sessions. */
|
|
132
|
+
onClientNetworkSample?: (sample: ClientNetworkSample) => void;
|
|
114
133
|
/** Additional CSS classes for the root container */
|
|
115
134
|
className?: string;
|
|
116
135
|
/** Called on connection state change */
|
|
@@ -215,5 +234,5 @@ export interface CameraStreamPlayerProps {
|
|
|
215
234
|
*/
|
|
216
235
|
onControlChannel?: (channel: RTCDataChannel) => void;
|
|
217
236
|
}
|
|
218
|
-
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onPlaybackStats, className, onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
237
|
+
export declare function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay, muted: initialMuted, showControls, showStats, onPlaybackStats, onClientNetworkSample, className, onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel, }: CameraStreamPlayerProps): import("react").JSX.Element;
|
|
219
238
|
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -38937,7 +38937,7 @@ function computeClientHints(container) {
|
|
|
38937
38937
|
var RECONNECT_BASE_DELAY_MS = 1500;
|
|
38938
38938
|
var RECONNECT_MAX_DELAY_MS = 15e3;
|
|
38939
38939
|
var MAX_RECONNECT_ATTEMPTS = 40;
|
|
38940
|
-
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, className = "", onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel }) {
|
|
38940
|
+
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, className = "", onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel }) {
|
|
38941
38941
|
const videoRef = (0, react$1.useRef)(null);
|
|
38942
38942
|
const containerRef = (0, react$1.useRef)(null);
|
|
38943
38943
|
const pcRef = (0, react$1.useRef)(null);
|
|
@@ -39705,6 +39705,58 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
39705
39705
|
}, 1e3);
|
|
39706
39706
|
return () => clearInterval(id);
|
|
39707
39707
|
}, [showStats]);
|
|
39708
|
+
const netPrev = (0, react$1.useRef)(null);
|
|
39709
|
+
(0, react$1.useEffect)(() => {
|
|
39710
|
+
if (!onClientNetworkSample) {
|
|
39711
|
+
netPrev.current = null;
|
|
39712
|
+
return;
|
|
39713
|
+
}
|
|
39714
|
+
const tick = async () => {
|
|
39715
|
+
const pc = pcRef.current;
|
|
39716
|
+
if (!pc) return;
|
|
39717
|
+
try {
|
|
39718
|
+
const report = await pc.getStats();
|
|
39719
|
+
let lost = 0;
|
|
39720
|
+
let recv = 0;
|
|
39721
|
+
let bytes = 0;
|
|
39722
|
+
let ts = 0;
|
|
39723
|
+
let jitterS = 0;
|
|
39724
|
+
let rttS = 0;
|
|
39725
|
+
report.forEach((raw) => {
|
|
39726
|
+
const s = raw;
|
|
39727
|
+
if (s.type === "inbound-rtp" && (s.kind ?? s.mediaType) === "video") {
|
|
39728
|
+
lost = s.packetsLost ?? 0;
|
|
39729
|
+
recv = s.packetsReceived ?? 0;
|
|
39730
|
+
bytes = s.bytesReceived ?? 0;
|
|
39731
|
+
ts = s.timestamp;
|
|
39732
|
+
jitterS = s.jitter ?? 0;
|
|
39733
|
+
} else if (s.type === "candidate-pair" && s.nominated && typeof s.currentRoundTripTime === "number") rttS = s.currentRoundTripTime;
|
|
39734
|
+
});
|
|
39735
|
+
const prev = netPrev.current;
|
|
39736
|
+
netPrev.current = {
|
|
39737
|
+
lost,
|
|
39738
|
+
recv,
|
|
39739
|
+
bytes,
|
|
39740
|
+
ts
|
|
39741
|
+
};
|
|
39742
|
+
if (!prev || ts <= prev.ts) return;
|
|
39743
|
+
const dLost = Math.max(0, lost - prev.lost);
|
|
39744
|
+
const denom = dLost + Math.max(0, recv - prev.recv);
|
|
39745
|
+
const packetLossPercent = denom > 0 ? Math.min(100, dLost / denom * 100) : 0;
|
|
39746
|
+
const estimatedBandwidthKbps = Math.max(0, Math.round((bytes - prev.bytes) * 8 / (ts - prev.ts)));
|
|
39747
|
+
onClientNetworkSample({
|
|
39748
|
+
rttMs: Math.round(rttS * 1e3),
|
|
39749
|
+
jitterMs: Math.round(jitterS * 1e3),
|
|
39750
|
+
estimatedBandwidthKbps,
|
|
39751
|
+
packetLossPercent
|
|
39752
|
+
});
|
|
39753
|
+
} catch {}
|
|
39754
|
+
};
|
|
39755
|
+
const id = setInterval(() => {
|
|
39756
|
+
tick();
|
|
39757
|
+
}, 2e3);
|
|
39758
|
+
return () => clearInterval(id);
|
|
39759
|
+
}, [onClientNetworkSample]);
|
|
39708
39760
|
const pbPrev = (0, react$1.useRef)(null);
|
|
39709
39761
|
(0, react$1.useEffect)(() => {
|
|
39710
39762
|
if (!onPlaybackStats) {
|
package/dist/index.js
CHANGED
|
@@ -38913,7 +38913,7 @@ function computeClientHints(container) {
|
|
|
38913
38913
|
var RECONNECT_BASE_DELAY_MS = 1500;
|
|
38914
38914
|
var RECONNECT_MAX_DELAY_MS = 15e3;
|
|
38915
38915
|
var MAX_RECONNECT_ATTEMPTS = 40;
|
|
38916
|
-
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, className = "", onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel }) {
|
|
38916
|
+
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, className = "", onStateChange, onError, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, reconnectSignal, onControlChannel }) {
|
|
38917
38917
|
const videoRef = useRef(null);
|
|
38918
38918
|
const containerRef = useRef(null);
|
|
38919
38919
|
const pcRef = useRef(null);
|
|
@@ -39681,6 +39681,58 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
39681
39681
|
}, 1e3);
|
|
39682
39682
|
return () => clearInterval(id);
|
|
39683
39683
|
}, [showStats]);
|
|
39684
|
+
const netPrev = useRef(null);
|
|
39685
|
+
useEffect(() => {
|
|
39686
|
+
if (!onClientNetworkSample) {
|
|
39687
|
+
netPrev.current = null;
|
|
39688
|
+
return;
|
|
39689
|
+
}
|
|
39690
|
+
const tick = async () => {
|
|
39691
|
+
const pc = pcRef.current;
|
|
39692
|
+
if (!pc) return;
|
|
39693
|
+
try {
|
|
39694
|
+
const report = await pc.getStats();
|
|
39695
|
+
let lost = 0;
|
|
39696
|
+
let recv = 0;
|
|
39697
|
+
let bytes = 0;
|
|
39698
|
+
let ts = 0;
|
|
39699
|
+
let jitterS = 0;
|
|
39700
|
+
let rttS = 0;
|
|
39701
|
+
report.forEach((raw) => {
|
|
39702
|
+
const s = raw;
|
|
39703
|
+
if (s.type === "inbound-rtp" && (s.kind ?? s.mediaType) === "video") {
|
|
39704
|
+
lost = s.packetsLost ?? 0;
|
|
39705
|
+
recv = s.packetsReceived ?? 0;
|
|
39706
|
+
bytes = s.bytesReceived ?? 0;
|
|
39707
|
+
ts = s.timestamp;
|
|
39708
|
+
jitterS = s.jitter ?? 0;
|
|
39709
|
+
} else if (s.type === "candidate-pair" && s.nominated && typeof s.currentRoundTripTime === "number") rttS = s.currentRoundTripTime;
|
|
39710
|
+
});
|
|
39711
|
+
const prev = netPrev.current;
|
|
39712
|
+
netPrev.current = {
|
|
39713
|
+
lost,
|
|
39714
|
+
recv,
|
|
39715
|
+
bytes,
|
|
39716
|
+
ts
|
|
39717
|
+
};
|
|
39718
|
+
if (!prev || ts <= prev.ts) return;
|
|
39719
|
+
const dLost = Math.max(0, lost - prev.lost);
|
|
39720
|
+
const denom = dLost + Math.max(0, recv - prev.recv);
|
|
39721
|
+
const packetLossPercent = denom > 0 ? Math.min(100, dLost / denom * 100) : 0;
|
|
39722
|
+
const estimatedBandwidthKbps = Math.max(0, Math.round((bytes - prev.bytes) * 8 / (ts - prev.ts)));
|
|
39723
|
+
onClientNetworkSample({
|
|
39724
|
+
rttMs: Math.round(rttS * 1e3),
|
|
39725
|
+
jitterMs: Math.round(jitterS * 1e3),
|
|
39726
|
+
estimatedBandwidthKbps,
|
|
39727
|
+
packetLossPercent
|
|
39728
|
+
});
|
|
39729
|
+
} catch {}
|
|
39730
|
+
};
|
|
39731
|
+
const id = setInterval(() => {
|
|
39732
|
+
tick();
|
|
39733
|
+
}, 2e3);
|
|
39734
|
+
return () => clearInterval(id);
|
|
39735
|
+
}, [onClientNetworkSample]);
|
|
39684
39736
|
const pbPrev = useRef(null);
|
|
39685
39737
|
useEffect(() => {
|
|
39686
39738
|
if (!onPlaybackStats) {
|