@decartai/sdk 0.0.67 → 0.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 +55 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +43 -28
- package/dist/process/client.js +1 -3
- package/dist/process/request.js +1 -3
- package/dist/queue/client.js +1 -3
- package/dist/queue/polling.js +1 -2
- package/dist/queue/request.js +1 -3
- package/dist/realtime/client.d.ts +23 -13
- package/dist/realtime/client.js +74 -244
- package/dist/realtime/config-realtime.js +49 -0
- package/dist/realtime/event-buffer.js +1 -3
- package/dist/realtime/initial-state-gate.js +21 -0
- package/dist/realtime/media-channel.js +82 -0
- package/dist/realtime/methods.js +12 -42
- package/dist/realtime/mirror-stream.js +1 -2
- package/dist/realtime/observability/diagnostics.d.ts +39 -0
- package/dist/realtime/observability/livekit-stats-provider.js +25 -0
- package/dist/realtime/observability/realtime-observability.js +173 -0
- package/dist/realtime/{telemetry-reporter.js → observability/telemetry-reporter.js} +12 -31
- package/dist/realtime/observability/webrtc-stats.d.ts +148 -0
- package/dist/realtime/observability/webrtc-stats.js +276 -0
- package/dist/realtime/signaling-channel.js +286 -0
- package/dist/realtime/stream-session.js +252 -0
- package/dist/realtime/subscribe-client.d.ts +2 -1
- package/dist/realtime/subscribe-client.js +115 -11
- package/dist/realtime/types.d.ts +25 -1
- package/dist/shared/model.d.ts +11 -1
- package/dist/shared/model.js +51 -14
- package/dist/shared/request.js +1 -3
- package/dist/shared/types.js +1 -3
- package/dist/tokens/client.js +1 -3
- package/dist/utils/env.js +1 -2
- package/dist/utils/errors.js +1 -2
- package/dist/utils/logger.js +1 -2
- package/dist/utils/media.js +43 -0
- package/dist/utils/platform.js +13 -0
- package/dist/utils/user-agent.js +1 -3
- package/dist/version.js +1 -2
- package/package.json +2 -1
- package/dist/realtime/diagnostics.d.ts +0 -78
- package/dist/realtime/webrtc-connection.js +0 -501
- package/dist/realtime/webrtc-manager.js +0 -189
- package/dist/realtime/webrtc-stats.d.ts +0 -59
- package/dist/realtime/webrtc-stats.js +0 -154
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { WebRTCConnection } from "./webrtc-connection.js";
|
|
2
|
-
import pRetry, { AbortError } from "p-retry";
|
|
3
|
-
|
|
4
|
-
//#region src/realtime/webrtc-manager.ts
|
|
5
|
-
const PERMANENT_ERRORS = [
|
|
6
|
-
"permission denied",
|
|
7
|
-
"not allowed",
|
|
8
|
-
"invalid session",
|
|
9
|
-
"401",
|
|
10
|
-
"invalid api key",
|
|
11
|
-
"unauthorized"
|
|
12
|
-
];
|
|
13
|
-
const CONNECTION_TIMEOUT = 6e4 * 5;
|
|
14
|
-
const RETRY_OPTIONS = {
|
|
15
|
-
retries: 5,
|
|
16
|
-
factor: 2,
|
|
17
|
-
minTimeout: 1e3,
|
|
18
|
-
maxTimeout: 1e4
|
|
19
|
-
};
|
|
20
|
-
var WebRTCManager = class {
|
|
21
|
-
connection;
|
|
22
|
-
config;
|
|
23
|
-
logger;
|
|
24
|
-
localStream = null;
|
|
25
|
-
subscribeMode = false;
|
|
26
|
-
managerState = "disconnected";
|
|
27
|
-
hasConnected = false;
|
|
28
|
-
isReconnecting = false;
|
|
29
|
-
intentionalDisconnect = false;
|
|
30
|
-
reconnectGeneration = 0;
|
|
31
|
-
constructor(config) {
|
|
32
|
-
this.config = config;
|
|
33
|
-
this.logger = config.logger ?? {
|
|
34
|
-
debug() {},
|
|
35
|
-
info() {},
|
|
36
|
-
warn() {},
|
|
37
|
-
error() {}
|
|
38
|
-
};
|
|
39
|
-
this.connection = new WebRTCConnection({
|
|
40
|
-
onRemoteStream: config.onRemoteStream,
|
|
41
|
-
onStateChange: (state) => this.handleConnectionStateChange(state),
|
|
42
|
-
onError: config.onError,
|
|
43
|
-
customizeOffer: config.customizeOffer,
|
|
44
|
-
vp8MinBitrate: config.vp8MinBitrate,
|
|
45
|
-
vp8StartBitrate: config.vp8StartBitrate,
|
|
46
|
-
initialImage: config.initialImage,
|
|
47
|
-
initialPrompt: config.initialPrompt,
|
|
48
|
-
logger: this.logger,
|
|
49
|
-
onDiagnostic: config.onDiagnostic
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
emitState(state) {
|
|
53
|
-
if (this.managerState !== state) {
|
|
54
|
-
this.managerState = state;
|
|
55
|
-
if (state === "connected" || state === "generating") this.hasConnected = true;
|
|
56
|
-
this.config.onConnectionStateChange?.(state);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
handleConnectionStateChange(state) {
|
|
60
|
-
if (this.intentionalDisconnect) {
|
|
61
|
-
this.emitState("disconnected");
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (this.isReconnecting) {
|
|
65
|
-
if (state === "connected" || state === "generating") {
|
|
66
|
-
this.isReconnecting = false;
|
|
67
|
-
this.emitState(state);
|
|
68
|
-
}
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (state === "disconnected" && !this.intentionalDisconnect && this.hasConnected) {
|
|
72
|
-
this.reconnect();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
this.emitState(state);
|
|
76
|
-
}
|
|
77
|
-
async reconnect() {
|
|
78
|
-
if (this.isReconnecting || this.intentionalDisconnect) return;
|
|
79
|
-
if (!this.subscribeMode && !this.localStream) return;
|
|
80
|
-
const reconnectGeneration = ++this.reconnectGeneration;
|
|
81
|
-
this.isReconnecting = true;
|
|
82
|
-
this.emitState("reconnecting");
|
|
83
|
-
const reconnectStart = performance.now();
|
|
84
|
-
try {
|
|
85
|
-
let attemptCount = 0;
|
|
86
|
-
await pRetry(async () => {
|
|
87
|
-
attemptCount++;
|
|
88
|
-
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) throw new AbortError("Reconnect cancelled");
|
|
89
|
-
if (!this.subscribeMode && !this.localStream) throw new AbortError("Reconnect cancelled: no local stream");
|
|
90
|
-
this.connection.cleanup();
|
|
91
|
-
await this.connection.connect(this.config.webrtcUrl, this.localStream, CONNECTION_TIMEOUT, this.config.integration);
|
|
92
|
-
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) {
|
|
93
|
-
this.connection.cleanup();
|
|
94
|
-
throw new AbortError("Reconnect cancelled");
|
|
95
|
-
}
|
|
96
|
-
}, {
|
|
97
|
-
...RETRY_OPTIONS,
|
|
98
|
-
onFailedAttempt: (error) => {
|
|
99
|
-
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return;
|
|
100
|
-
this.logger.warn("Reconnect attempt failed", {
|
|
101
|
-
error: error.message,
|
|
102
|
-
attempt: error.attemptNumber
|
|
103
|
-
});
|
|
104
|
-
this.config.onDiagnostic?.("reconnect", {
|
|
105
|
-
attempt: error.attemptNumber,
|
|
106
|
-
maxAttempts: RETRY_OPTIONS.retries + 1,
|
|
107
|
-
durationMs: performance.now() - reconnectStart,
|
|
108
|
-
success: false,
|
|
109
|
-
error: error.message
|
|
110
|
-
});
|
|
111
|
-
this.connection.cleanup();
|
|
112
|
-
},
|
|
113
|
-
shouldRetry: (error) => {
|
|
114
|
-
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return false;
|
|
115
|
-
const msg = error.message.toLowerCase();
|
|
116
|
-
return !PERMANENT_ERRORS.some((err) => msg.includes(err));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
this.config.onDiagnostic?.("reconnect", {
|
|
120
|
-
attempt: attemptCount,
|
|
121
|
-
maxAttempts: RETRY_OPTIONS.retries + 1,
|
|
122
|
-
durationMs: performance.now() - reconnectStart,
|
|
123
|
-
success: true
|
|
124
|
-
});
|
|
125
|
-
} catch (error) {
|
|
126
|
-
this.isReconnecting = false;
|
|
127
|
-
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return;
|
|
128
|
-
this.emitState("disconnected");
|
|
129
|
-
this.config.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
async connect(localStream) {
|
|
133
|
-
this.localStream = localStream;
|
|
134
|
-
this.subscribeMode = localStream === null;
|
|
135
|
-
this.intentionalDisconnect = false;
|
|
136
|
-
this.hasConnected = false;
|
|
137
|
-
this.isReconnecting = false;
|
|
138
|
-
this.reconnectGeneration += 1;
|
|
139
|
-
this.emitState("connecting");
|
|
140
|
-
return pRetry(async () => {
|
|
141
|
-
if (this.intentionalDisconnect) throw new AbortError("Connect cancelled");
|
|
142
|
-
await this.connection.connect(this.config.webrtcUrl, localStream, CONNECTION_TIMEOUT, this.config.integration);
|
|
143
|
-
return true;
|
|
144
|
-
}, {
|
|
145
|
-
...RETRY_OPTIONS,
|
|
146
|
-
onFailedAttempt: (error) => {
|
|
147
|
-
this.logger.warn("Connection attempt failed", {
|
|
148
|
-
error: error.message,
|
|
149
|
-
attempt: error.attemptNumber
|
|
150
|
-
});
|
|
151
|
-
this.connection.cleanup();
|
|
152
|
-
},
|
|
153
|
-
shouldRetry: (error) => {
|
|
154
|
-
if (this.intentionalDisconnect) return false;
|
|
155
|
-
const msg = error.message.toLowerCase();
|
|
156
|
-
return !PERMANENT_ERRORS.some((err) => msg.includes(err));
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
sendMessage(message) {
|
|
161
|
-
return this.connection.send(message);
|
|
162
|
-
}
|
|
163
|
-
cleanup() {
|
|
164
|
-
this.intentionalDisconnect = true;
|
|
165
|
-
this.isReconnecting = false;
|
|
166
|
-
this.reconnectGeneration += 1;
|
|
167
|
-
this.connection.cleanup();
|
|
168
|
-
this.localStream = null;
|
|
169
|
-
this.emitState("disconnected");
|
|
170
|
-
}
|
|
171
|
-
isConnected() {
|
|
172
|
-
return this.managerState === "connected" || this.managerState === "generating";
|
|
173
|
-
}
|
|
174
|
-
getConnectionState() {
|
|
175
|
-
return this.managerState;
|
|
176
|
-
}
|
|
177
|
-
getPeerConnection() {
|
|
178
|
-
return this.connection.getPeerConnection();
|
|
179
|
-
}
|
|
180
|
-
getWebsocketMessageEmitter() {
|
|
181
|
-
return this.connection.websocketMessagesEmitter;
|
|
182
|
-
}
|
|
183
|
-
setImage(imageBase64, options) {
|
|
184
|
-
return this.connection.setImageBase64(imageBase64, options);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
//#endregion
|
|
189
|
-
export { WebRTCManager };
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
//#region src/realtime/webrtc-stats.d.ts
|
|
2
|
-
type WebRTCStats = {
|
|
3
|
-
timestamp: number;
|
|
4
|
-
video: {
|
|
5
|
-
framesDecoded: number;
|
|
6
|
-
framesDropped: number;
|
|
7
|
-
framesPerSecond: number;
|
|
8
|
-
frameWidth: number;
|
|
9
|
-
frameHeight: number;
|
|
10
|
-
bytesReceived: number;
|
|
11
|
-
packetsReceived: number;
|
|
12
|
-
packetsLost: number;
|
|
13
|
-
jitter: number;
|
|
14
|
-
/** Estimated inbound bitrate in bits/sec, computed from bytesReceived delta. */
|
|
15
|
-
bitrate: number;
|
|
16
|
-
freezeCount: number;
|
|
17
|
-
totalFreezesDuration: number;
|
|
18
|
-
/** Delta: packets lost since previous sample. */
|
|
19
|
-
packetsLostDelta: number;
|
|
20
|
-
/** Delta: frames dropped since previous sample. */
|
|
21
|
-
framesDroppedDelta: number;
|
|
22
|
-
/** Delta: freeze count since previous sample. */
|
|
23
|
-
freezeCountDelta: number;
|
|
24
|
-
/** Delta: freeze duration (seconds) since previous sample. */
|
|
25
|
-
freezeDurationDelta: number;
|
|
26
|
-
} | null;
|
|
27
|
-
audio: {
|
|
28
|
-
bytesReceived: number;
|
|
29
|
-
packetsReceived: number;
|
|
30
|
-
packetsLost: number;
|
|
31
|
-
jitter: number;
|
|
32
|
-
/** Estimated inbound bitrate in bits/sec, computed from bytesReceived delta. */
|
|
33
|
-
bitrate: number;
|
|
34
|
-
/** Delta: packets lost since previous sample. */
|
|
35
|
-
packetsLostDelta: number;
|
|
36
|
-
} | null;
|
|
37
|
-
/** Outbound video track stats (from the local camera/screen share being sent). */
|
|
38
|
-
outboundVideo: {
|
|
39
|
-
/** Why the encoder is limiting quality: "none", "bandwidth", "cpu", or "other". */
|
|
40
|
-
qualityLimitationReason: string;
|
|
41
|
-
/** Cumulative time (seconds) spent in each quality limitation state. */
|
|
42
|
-
qualityLimitationDurations: Record<string, number>;
|
|
43
|
-
bytesSent: number;
|
|
44
|
-
packetsSent: number;
|
|
45
|
-
framesPerSecond: number;
|
|
46
|
-
frameWidth: number;
|
|
47
|
-
frameHeight: number;
|
|
48
|
-
/** Estimated outbound bitrate in bits/sec, computed from bytesSent delta. */
|
|
49
|
-
bitrate: number;
|
|
50
|
-
} | null;
|
|
51
|
-
connection: {
|
|
52
|
-
/** Current round-trip time in seconds, or null if unavailable. */
|
|
53
|
-
currentRoundTripTime: number | null;
|
|
54
|
-
/** Available outgoing bitrate estimate in bits/sec, or null if unavailable. */
|
|
55
|
-
availableOutgoingBitrate: number | null;
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
//#endregion
|
|
59
|
-
export { WebRTCStats };
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
//#region src/realtime/webrtc-stats.ts
|
|
2
|
-
const DEFAULT_INTERVAL_MS = 1e3;
|
|
3
|
-
const MIN_INTERVAL_MS = 500;
|
|
4
|
-
var WebRTCStatsCollector = class {
|
|
5
|
-
pc = null;
|
|
6
|
-
intervalId = null;
|
|
7
|
-
prevBytesVideo = 0;
|
|
8
|
-
prevBytesAudio = 0;
|
|
9
|
-
prevBytesSentVideo = 0;
|
|
10
|
-
prevTimestamp = 0;
|
|
11
|
-
prevPacketsLostVideo = 0;
|
|
12
|
-
prevFramesDropped = 0;
|
|
13
|
-
prevFreezeCount = 0;
|
|
14
|
-
prevFreezeDuration = 0;
|
|
15
|
-
prevPacketsLostAudio = 0;
|
|
16
|
-
onStats = null;
|
|
17
|
-
intervalMs;
|
|
18
|
-
constructor(options = {}) {
|
|
19
|
-
this.intervalMs = Math.max(options.intervalMs ?? DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS);
|
|
20
|
-
}
|
|
21
|
-
/** Attach to a peer connection and start polling. */
|
|
22
|
-
start(pc, onStats) {
|
|
23
|
-
this.stop();
|
|
24
|
-
this.pc = pc;
|
|
25
|
-
this.onStats = onStats;
|
|
26
|
-
this.prevBytesVideo = 0;
|
|
27
|
-
this.prevBytesAudio = 0;
|
|
28
|
-
this.prevBytesSentVideo = 0;
|
|
29
|
-
this.prevTimestamp = 0;
|
|
30
|
-
this.prevPacketsLostVideo = 0;
|
|
31
|
-
this.prevFramesDropped = 0;
|
|
32
|
-
this.prevFreezeCount = 0;
|
|
33
|
-
this.prevFreezeDuration = 0;
|
|
34
|
-
this.prevPacketsLostAudio = 0;
|
|
35
|
-
this.intervalId = setInterval(() => this.collect(), this.intervalMs);
|
|
36
|
-
}
|
|
37
|
-
/** Stop polling and release resources. */
|
|
38
|
-
stop() {
|
|
39
|
-
if (this.intervalId !== null) {
|
|
40
|
-
clearInterval(this.intervalId);
|
|
41
|
-
this.intervalId = null;
|
|
42
|
-
}
|
|
43
|
-
this.pc = null;
|
|
44
|
-
this.onStats = null;
|
|
45
|
-
}
|
|
46
|
-
isRunning() {
|
|
47
|
-
return this.intervalId !== null;
|
|
48
|
-
}
|
|
49
|
-
async collect() {
|
|
50
|
-
if (!this.pc || !this.onStats) return;
|
|
51
|
-
try {
|
|
52
|
-
const rawStats = await this.pc.getStats();
|
|
53
|
-
const stats = this.parse(rawStats);
|
|
54
|
-
this.onStats(stats);
|
|
55
|
-
} catch {
|
|
56
|
-
this.stop();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
parse(rawStats) {
|
|
60
|
-
const now = performance.now();
|
|
61
|
-
const elapsed = this.prevTimestamp > 0 ? (now - this.prevTimestamp) / 1e3 : 0;
|
|
62
|
-
let video = null;
|
|
63
|
-
let audio = null;
|
|
64
|
-
let outboundVideo = null;
|
|
65
|
-
const connection = {
|
|
66
|
-
currentRoundTripTime: null,
|
|
67
|
-
availableOutgoingBitrate: null
|
|
68
|
-
};
|
|
69
|
-
rawStats.forEach((report) => {
|
|
70
|
-
if (report.type === "inbound-rtp" && report.kind === "video") {
|
|
71
|
-
const bytesReceived = report.bytesReceived ?? 0;
|
|
72
|
-
const bitrate = elapsed > 0 ? (bytesReceived - this.prevBytesVideo) * 8 / elapsed : 0;
|
|
73
|
-
this.prevBytesVideo = bytesReceived;
|
|
74
|
-
const r = report;
|
|
75
|
-
const packetsLost = r.packetsLost ?? 0;
|
|
76
|
-
const framesDropped = r.framesDropped ?? 0;
|
|
77
|
-
const freezeCount = r.freezeCount ?? 0;
|
|
78
|
-
const freezeDuration = r.totalFreezesDuration ?? 0;
|
|
79
|
-
video = {
|
|
80
|
-
framesDecoded: r.framesDecoded ?? 0,
|
|
81
|
-
framesDropped,
|
|
82
|
-
framesPerSecond: r.framesPerSecond ?? 0,
|
|
83
|
-
frameWidth: r.frameWidth ?? 0,
|
|
84
|
-
frameHeight: r.frameHeight ?? 0,
|
|
85
|
-
bytesReceived,
|
|
86
|
-
packetsReceived: r.packetsReceived ?? 0,
|
|
87
|
-
packetsLost,
|
|
88
|
-
jitter: r.jitter ?? 0,
|
|
89
|
-
bitrate: Math.round(bitrate),
|
|
90
|
-
freezeCount,
|
|
91
|
-
totalFreezesDuration: freezeDuration,
|
|
92
|
-
packetsLostDelta: Math.max(0, packetsLost - this.prevPacketsLostVideo),
|
|
93
|
-
framesDroppedDelta: Math.max(0, framesDropped - this.prevFramesDropped),
|
|
94
|
-
freezeCountDelta: Math.max(0, freezeCount - this.prevFreezeCount),
|
|
95
|
-
freezeDurationDelta: Math.max(0, freezeDuration - this.prevFreezeDuration)
|
|
96
|
-
};
|
|
97
|
-
this.prevPacketsLostVideo = packetsLost;
|
|
98
|
-
this.prevFramesDropped = framesDropped;
|
|
99
|
-
this.prevFreezeCount = freezeCount;
|
|
100
|
-
this.prevFreezeDuration = freezeDuration;
|
|
101
|
-
}
|
|
102
|
-
if (report.type === "outbound-rtp" && report.kind === "video") {
|
|
103
|
-
const r = report;
|
|
104
|
-
const bytesSent = r.bytesSent ?? 0;
|
|
105
|
-
const outBitrate = elapsed > 0 ? (bytesSent - this.prevBytesSentVideo) * 8 / elapsed : 0;
|
|
106
|
-
this.prevBytesSentVideo = bytesSent;
|
|
107
|
-
outboundVideo = {
|
|
108
|
-
qualityLimitationReason: r.qualityLimitationReason ?? "none",
|
|
109
|
-
qualityLimitationDurations: r.qualityLimitationDurations ?? {},
|
|
110
|
-
bytesSent,
|
|
111
|
-
packetsSent: r.packetsSent ?? 0,
|
|
112
|
-
framesPerSecond: r.framesPerSecond ?? 0,
|
|
113
|
-
frameWidth: r.frameWidth ?? 0,
|
|
114
|
-
frameHeight: r.frameHeight ?? 0,
|
|
115
|
-
bitrate: Math.round(outBitrate)
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
if (report.type === "inbound-rtp" && report.kind === "audio") {
|
|
119
|
-
const bytesReceived = report.bytesReceived ?? 0;
|
|
120
|
-
const bitrate = elapsed > 0 ? (bytesReceived - this.prevBytesAudio) * 8 / elapsed : 0;
|
|
121
|
-
this.prevBytesAudio = bytesReceived;
|
|
122
|
-
const r = report;
|
|
123
|
-
const audioPacketsLost = r.packetsLost ?? 0;
|
|
124
|
-
audio = {
|
|
125
|
-
bytesReceived,
|
|
126
|
-
packetsReceived: r.packetsReceived ?? 0,
|
|
127
|
-
packetsLost: audioPacketsLost,
|
|
128
|
-
jitter: r.jitter ?? 0,
|
|
129
|
-
bitrate: Math.round(bitrate),
|
|
130
|
-
packetsLostDelta: Math.max(0, audioPacketsLost - this.prevPacketsLostAudio)
|
|
131
|
-
};
|
|
132
|
-
this.prevPacketsLostAudio = audioPacketsLost;
|
|
133
|
-
}
|
|
134
|
-
if (report.type === "candidate-pair") {
|
|
135
|
-
const r = report;
|
|
136
|
-
if (r.state === "succeeded") {
|
|
137
|
-
connection.currentRoundTripTime = r.currentRoundTripTime ?? null;
|
|
138
|
-
connection.availableOutgoingBitrate = r.availableOutgoingBitrate ?? null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
this.prevTimestamp = now;
|
|
143
|
-
return {
|
|
144
|
-
timestamp: Date.now(),
|
|
145
|
-
video,
|
|
146
|
-
audio,
|
|
147
|
-
outboundVideo,
|
|
148
|
-
connection
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
//#endregion
|
|
154
|
-
export { WebRTCStatsCollector };
|