@stream-io/video-client 1.18.9 → 1.19.1
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/CHANGELOG.md +16 -7
- package/dist/index.browser.es.js +2612 -2301
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +2612 -2300
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +2612 -2301
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +3 -0
- package/dist/src/StreamSfuClient.d.ts +9 -2
- package/dist/src/gen/coordinator/index.d.ts +266 -5
- package/dist/src/gen/google/protobuf/struct.d.ts +1 -3
- package/dist/src/gen/google/protobuf/timestamp.d.ts +1 -3
- package/dist/src/gen/video/sfu/event/events.d.ts +6 -0
- package/dist/src/gen/video/sfu/models/models.d.ts +46 -0
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +13 -1
- package/dist/src/rpc/createClient.d.ts +2 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +10 -3
- package/dist/src/stats/index.d.ts +2 -0
- package/dist/src/stats/rtc/Tracer.d.ts +15 -0
- package/dist/src/stats/rtc/index.d.ts +2 -0
- package/dist/src/stats/rtc/mediaDevices.d.ts +2 -0
- package/dist/src/stats/rtc/pc.d.ts +2 -0
- package/dist/src/stats/rtc/types.d.ts +8 -0
- package/index.ts +4 -0
- package/package.json +1 -1
- package/src/Call.ts +54 -29
- package/src/StreamSfuClient.ts +22 -9
- package/src/devices/MicrophoneManager.ts +5 -2
- package/src/devices/__tests__/MicrophoneManager.test.ts +9 -6
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +9 -6
- package/src/gen/coordinator/index.ts +262 -5
- package/src/gen/google/protobuf/struct.ts +13 -8
- package/src/gen/google/protobuf/timestamp.ts +10 -8
- package/src/gen/video/sfu/event/events.ts +8 -1
- package/src/gen/video/sfu/models/models.ts +63 -1
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +1 -1
- package/src/gen/video/sfu/signal_rpc/signal.ts +27 -1
- package/src/rpc/__tests__/createClient.test.ts +38 -0
- package/src/rpc/createClient.ts +30 -0
- package/src/rtc/BasePeerConnection.ts +22 -4
- package/src/rtc/Publisher.ts +3 -2
- package/src/rtc/Subscriber.ts +0 -2
- package/src/rtc/__tests__/Subscriber.test.ts +1 -0
- package/src/rtc/helpers/__tests__/rtcConfiguration.test.ts +1 -0
- package/src/rtc/helpers/rtcConfiguration.ts +1 -0
- package/src/stats/SfuStatsReporter.ts +36 -12
- package/src/stats/index.ts +2 -0
- package/src/stats/rtc/Tracer.ts +42 -0
- package/src/stats/rtc/index.ts +5 -0
- package/src/stats/rtc/mediaDevices.ts +42 -0
- package/src/stats/rtc/pc.ts +130 -0
- package/src/stats/rtc/types.ts +26 -0
- package/src/store/CallState.ts +10 -0
|
@@ -9,14 +9,16 @@ import { PeerType } from '../gen/video/sfu/models/models';
|
|
|
9
9
|
import { StreamSfuClient } from '../StreamSfuClient';
|
|
10
10
|
import { AllSfuEvents, Dispatcher } from './Dispatcher';
|
|
11
11
|
import { withoutConcurrency } from '../helpers/concurrency';
|
|
12
|
+
import { Tracer, traceRTCPeerConnection, TraceSlice } from '../stats';
|
|
12
13
|
|
|
13
14
|
export type BasePeerConnectionOpts = {
|
|
14
15
|
sfuClient: StreamSfuClient;
|
|
15
16
|
state: CallState;
|
|
16
17
|
connectionConfig?: RTCConfiguration;
|
|
17
18
|
dispatcher: Dispatcher;
|
|
18
|
-
onUnrecoverableError?: () => void;
|
|
19
|
+
onUnrecoverableError?: (reason: string) => void;
|
|
19
20
|
logTag: string;
|
|
21
|
+
enableTracing: boolean;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -31,10 +33,11 @@ export abstract class BasePeerConnection {
|
|
|
31
33
|
protected readonly dispatcher: Dispatcher;
|
|
32
34
|
protected sfuClient: StreamSfuClient;
|
|
33
35
|
|
|
34
|
-
protected onUnrecoverableError?: () => void;
|
|
36
|
+
protected onUnrecoverableError?: (reason: string) => void;
|
|
35
37
|
protected isIceRestarting = false;
|
|
36
38
|
private isDisposed = false;
|
|
37
39
|
|
|
40
|
+
private readonly tracer?: Tracer;
|
|
38
41
|
private readonly subscriptions: (() => void)[] = [];
|
|
39
42
|
private unsubscribeIceTrickle?: () => void;
|
|
40
43
|
|
|
@@ -50,6 +53,7 @@ export abstract class BasePeerConnection {
|
|
|
50
53
|
dispatcher,
|
|
51
54
|
onUnrecoverableError,
|
|
52
55
|
logTag,
|
|
56
|
+
enableTracing,
|
|
53
57
|
}: BasePeerConnectionOpts,
|
|
54
58
|
) {
|
|
55
59
|
this.peerType = peerType;
|
|
@@ -62,6 +66,11 @@ export abstract class BasePeerConnection {
|
|
|
62
66
|
logTag,
|
|
63
67
|
]);
|
|
64
68
|
this.pc = new RTCPeerConnection(connectionConfig);
|
|
69
|
+
if (enableTracing) {
|
|
70
|
+
this.tracer = new Tracer(logTag);
|
|
71
|
+
this.tracer.trace('create', connectionConfig);
|
|
72
|
+
traceRTCPeerConnection(this.pc, this.tracer.trace);
|
|
73
|
+
}
|
|
65
74
|
this.pc.addEventListener('icecandidate', this.onIceCandidate);
|
|
66
75
|
this.pc.addEventListener('icecandidateerror', this.onIceCandidateError);
|
|
67
76
|
this.pc.addEventListener(
|
|
@@ -80,6 +89,7 @@ export abstract class BasePeerConnection {
|
|
|
80
89
|
this.isDisposed = true;
|
|
81
90
|
this.detachEventHandlers();
|
|
82
91
|
this.pc.close();
|
|
92
|
+
this.tracer?.dispose();
|
|
83
93
|
}
|
|
84
94
|
|
|
85
95
|
/**
|
|
@@ -163,6 +173,13 @@ export abstract class BasePeerConnection {
|
|
|
163
173
|
return this.pc.getStats(selector);
|
|
164
174
|
};
|
|
165
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Returns the current tracing buffer.
|
|
178
|
+
*/
|
|
179
|
+
getTrace = (): TraceSlice | undefined => {
|
|
180
|
+
return this.tracer?.take();
|
|
181
|
+
};
|
|
182
|
+
|
|
166
183
|
/**
|
|
167
184
|
* Handles the ICECandidate event and
|
|
168
185
|
* Initiates an ICE Trickle process with the SFU.
|
|
@@ -214,8 +231,9 @@ export abstract class BasePeerConnection {
|
|
|
214
231
|
this.logger('debug', `Attempting to restart ICE`);
|
|
215
232
|
this.restartIce().catch((e) => {
|
|
216
233
|
if (this.isDisposed) return;
|
|
217
|
-
|
|
218
|
-
this.
|
|
234
|
+
const reason = `ICE restart failed`;
|
|
235
|
+
this.logger('error', reason, e);
|
|
236
|
+
this.onUnrecoverableError?.(`${reason}: ${e}`);
|
|
219
237
|
});
|
|
220
238
|
}
|
|
221
239
|
};
|
package/src/rtc/Publisher.ts
CHANGED
|
@@ -45,8 +45,9 @@ export class Publisher extends BasePeerConnection {
|
|
|
45
45
|
this.on('iceRestart', (iceRestart) => {
|
|
46
46
|
if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED) return;
|
|
47
47
|
this.restartIce().catch((err) => {
|
|
48
|
-
|
|
49
|
-
this.
|
|
48
|
+
const reason = `ICE restart failed`;
|
|
49
|
+
this.logger('warn', reason, err);
|
|
50
|
+
this.onUnrecoverableError?.(`${reason}: ${err}`);
|
|
50
51
|
});
|
|
51
52
|
});
|
|
52
53
|
|
package/src/rtc/Subscriber.ts
CHANGED
|
@@ -144,8 +144,6 @@ export class Subscriber extends BasePeerConnection {
|
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
private negotiate = async (subscriberOffer: SubscriberOffer) => {
|
|
147
|
-
this.logger('info', `Received subscriberOffer`, subscriberOffer);
|
|
148
|
-
|
|
149
147
|
await this.pc.setRemoteDescription({
|
|
150
148
|
type: 'offer',
|
|
151
149
|
sdp: subscriberOffer.sdp,
|
|
@@ -190,6 +190,7 @@ describe('Subscriber', () => {
|
|
|
190
190
|
subscriber['pc'].createAnswer = vi
|
|
191
191
|
.fn()
|
|
192
192
|
.mockResolvedValue({ sdp: 'answer-sdp' });
|
|
193
|
+
vi.spyOn(subscriber['pc'], 'setRemoteDescription').mockResolvedValue();
|
|
193
194
|
|
|
194
195
|
const offer = SubscriberOffer.create({ sdp: 'offer-sdp' });
|
|
195
196
|
// @ts-expect-error - private method
|
|
@@ -3,6 +3,7 @@ import { StreamSfuClient } from '../StreamSfuClient';
|
|
|
3
3
|
import { OwnCapability, StatsOptions } from '../gen/coordinator';
|
|
4
4
|
import { getLogger } from '../logger';
|
|
5
5
|
import { Publisher, Subscriber } from '../rtc';
|
|
6
|
+
import { tracer as mediaStatsTracer } from './rtc/mediaDevices';
|
|
6
7
|
import { flatten, getSdkName, getSdkVersion } from './utils';
|
|
7
8
|
import { getDeviceState, getWebRTCInfo } from '../helpers/client-details';
|
|
8
9
|
import {
|
|
@@ -147,23 +148,46 @@ export class SfuStatsReporter {
|
|
|
147
148
|
});
|
|
148
149
|
};
|
|
149
150
|
|
|
150
|
-
private run = async (
|
|
151
|
+
private run = async (telemetry?: Telemetry) => {
|
|
151
152
|
const [subscriberStats, publisherStats] = await Promise.all([
|
|
152
153
|
this.subscriber.getStats().then(flatten).then(JSON.stringify),
|
|
153
154
|
this.publisher?.getStats().then(flatten).then(JSON.stringify) ?? '[]',
|
|
154
155
|
]);
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
const subscriberTrace = this.subscriber.getTrace();
|
|
158
|
+
const publisherTrace = this.publisher?.getTrace();
|
|
159
|
+
const mediaTrace = mediaStatsTracer.take();
|
|
160
|
+
const sfuTrace = this.sfuClient.getTrace();
|
|
161
|
+
const publisherTraces = [
|
|
162
|
+
...mediaTrace.snapshot,
|
|
163
|
+
...(sfuTrace?.snapshot ?? []),
|
|
164
|
+
...(publisherTrace?.snapshot ?? []),
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
await this.sfuClient.sendStats({
|
|
169
|
+
sdk: this.sdkName,
|
|
170
|
+
sdkVersion: this.sdkVersion,
|
|
171
|
+
webrtcVersion: this.webRTCVersion,
|
|
172
|
+
subscriberStats,
|
|
173
|
+
subscriberRtcStats: subscriberTrace
|
|
174
|
+
? JSON.stringify(subscriberTrace.snapshot)
|
|
175
|
+
: '',
|
|
176
|
+
publisherStats,
|
|
177
|
+
publisherRtcStats:
|
|
178
|
+
publisherTraces.length > 0 ? JSON.stringify(publisherTraces) : '',
|
|
179
|
+
audioDevices: this.inputDevices.get('mic'),
|
|
180
|
+
videoDevices: this.inputDevices.get('camera'),
|
|
181
|
+
deviceState: getDeviceState(),
|
|
182
|
+
telemetry,
|
|
183
|
+
});
|
|
184
|
+
} catch (err) {
|
|
185
|
+
publisherTrace?.rollback();
|
|
186
|
+
subscriberTrace?.rollback();
|
|
187
|
+
mediaTrace.rollback();
|
|
188
|
+
sfuTrace?.rollback();
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
167
191
|
};
|
|
168
192
|
|
|
169
193
|
start = () => {
|
package/src/stats/index.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Trace, TraceRecord } from './types';
|
|
2
|
+
|
|
3
|
+
export type TraceSlice = {
|
|
4
|
+
snapshot: TraceRecord[];
|
|
5
|
+
rollback: () => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export class Tracer {
|
|
9
|
+
private buffer: TraceRecord[] = [];
|
|
10
|
+
private enabled = true;
|
|
11
|
+
private readonly id: string | null;
|
|
12
|
+
|
|
13
|
+
constructor(id: string | null) {
|
|
14
|
+
this.id = id;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setEnabled = (enabled: boolean) => {
|
|
18
|
+
if (this.enabled === enabled) return;
|
|
19
|
+
this.enabled = enabled;
|
|
20
|
+
this.buffer = [];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
trace: Trace = (tag, data) => {
|
|
24
|
+
if (!this.enabled) return;
|
|
25
|
+
this.buffer.push([tag, this.id, data, Date.now()]);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
take = (): TraceSlice => {
|
|
29
|
+
const snapshot = this.buffer;
|
|
30
|
+
this.buffer = [];
|
|
31
|
+
return {
|
|
32
|
+
snapshot,
|
|
33
|
+
rollback: () => {
|
|
34
|
+
this.buffer.unshift(...snapshot);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
dispose = () => {
|
|
40
|
+
this.buffer = [];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Tracer } from './Tracer';
|
|
2
|
+
|
|
3
|
+
export const tracer = new Tracer(null);
|
|
4
|
+
|
|
5
|
+
if (
|
|
6
|
+
typeof navigator !== 'undefined' &&
|
|
7
|
+
typeof navigator.mediaDevices !== 'undefined'
|
|
8
|
+
) {
|
|
9
|
+
const dumpStream = (stream: MediaStream) => ({
|
|
10
|
+
id: stream.id,
|
|
11
|
+
tracks: stream.getTracks().map((track) => ({
|
|
12
|
+
id: track.id,
|
|
13
|
+
kind: track.kind,
|
|
14
|
+
label: track.label,
|
|
15
|
+
enabled: track.enabled,
|
|
16
|
+
muted: track.muted,
|
|
17
|
+
readyState: track.readyState,
|
|
18
|
+
})),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const trace = tracer.trace;
|
|
22
|
+
const target = navigator.mediaDevices;
|
|
23
|
+
for (const method of ['getUserMedia', 'getDisplayMedia'] as const) {
|
|
24
|
+
const original = target[method];
|
|
25
|
+
if (!original) continue;
|
|
26
|
+
|
|
27
|
+
target[method] = async function tracedMethod(
|
|
28
|
+
constraints: MediaStreamConstraints,
|
|
29
|
+
) {
|
|
30
|
+
const tag = `navigator.mediaDevices.${method}`;
|
|
31
|
+
trace(tag, constraints);
|
|
32
|
+
try {
|
|
33
|
+
const stream = await original.call(target, constraints);
|
|
34
|
+
trace(`${tag}OnSuccess`, dumpStream(stream));
|
|
35
|
+
return stream;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
trace(`${tag}OnFailure`, (err as Error).name);
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { RTCStatsDataType, Trace } from './types';
|
|
2
|
+
|
|
3
|
+
export const traceRTCPeerConnection = (pc: RTCPeerConnection, trace: Trace) => {
|
|
4
|
+
pc.addEventListener('icecandidate', (e) => {
|
|
5
|
+
trace('onicecandidate', e.candidate);
|
|
6
|
+
});
|
|
7
|
+
pc.addEventListener('track', (e) => {
|
|
8
|
+
const streams = e.streams.map((stream) => `stream:${stream.id}`);
|
|
9
|
+
trace('ontrack', `${e.track.kind}:${e.track.id} ${streams}`);
|
|
10
|
+
});
|
|
11
|
+
pc.addEventListener('signalingstatechange', () => {
|
|
12
|
+
trace('onsignalingstatechange', pc.signalingState);
|
|
13
|
+
});
|
|
14
|
+
pc.addEventListener('iceconnectionstatechange', () => {
|
|
15
|
+
trace('oniceconnectionstatechange', pc.iceConnectionState);
|
|
16
|
+
});
|
|
17
|
+
pc.addEventListener('icegatheringstatechange', () => {
|
|
18
|
+
trace('onicegatheringstatechange', pc.iceGatheringState);
|
|
19
|
+
});
|
|
20
|
+
pc.addEventListener('connectionstatechange', () => {
|
|
21
|
+
trace('onconnectionstatechange', pc.connectionState);
|
|
22
|
+
});
|
|
23
|
+
pc.addEventListener('negotiationneeded', () => {
|
|
24
|
+
trace('onnegotiationneeded', undefined);
|
|
25
|
+
});
|
|
26
|
+
pc.addEventListener('datachannel', ({ channel }) => {
|
|
27
|
+
trace('ondatachannel', [channel.id, channel.label]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
let prev: Record<string, RTCStats> = {};
|
|
31
|
+
const getStats = () => {
|
|
32
|
+
pc.getStats(null)
|
|
33
|
+
.then((stats) => {
|
|
34
|
+
const now = toObject(stats);
|
|
35
|
+
trace('getstats', deltaCompression(prev, now));
|
|
36
|
+
prev = now;
|
|
37
|
+
})
|
|
38
|
+
.catch((err) => {
|
|
39
|
+
trace('getstatsOnFailure', (err as Error).toString());
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
getStats();
|
|
45
|
+
}, 8000);
|
|
46
|
+
|
|
47
|
+
pc.addEventListener('connectionstatechange', () => {
|
|
48
|
+
const state = pc.connectionState;
|
|
49
|
+
if (state === 'connected' || state === 'failed') {
|
|
50
|
+
getStats();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const origClose = pc.close;
|
|
55
|
+
pc.close = function tracedClose() {
|
|
56
|
+
clearInterval(interval);
|
|
57
|
+
trace('close', undefined);
|
|
58
|
+
return origClose.call(this);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
for (const method of [
|
|
62
|
+
'createOffer',
|
|
63
|
+
'createAnswer',
|
|
64
|
+
'setLocalDescription',
|
|
65
|
+
'setRemoteDescription',
|
|
66
|
+
'addIceCandidate',
|
|
67
|
+
] as const) {
|
|
68
|
+
const original = pc[method];
|
|
69
|
+
if (!original) continue;
|
|
70
|
+
|
|
71
|
+
// @ts-expect-error we don't use deprecated APIs
|
|
72
|
+
pc[method] = async function tracedMethod(...args: any[]) {
|
|
73
|
+
try {
|
|
74
|
+
trace(method, args);
|
|
75
|
+
// @ts-expect-error improper types
|
|
76
|
+
const result = await original.apply(this, args);
|
|
77
|
+
trace(`${method}OnSuccess`, result as RTCStatsDataType);
|
|
78
|
+
return result;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
trace(`${method}OnFailure`, (err as Error).toString());
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const toObject = (s: RTCStatsReport): Record<string, RTCStats> => {
|
|
88
|
+
const obj: Record<string, RTCStats> = {};
|
|
89
|
+
s.forEach((v, k) => {
|
|
90
|
+
obj[k] = v;
|
|
91
|
+
});
|
|
92
|
+
return obj;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Apply delta compression to the stats report.
|
|
97
|
+
* Reduces size by ~90%.
|
|
98
|
+
* To reduce further, report keys could be compressed.
|
|
99
|
+
*/
|
|
100
|
+
const deltaCompression = (
|
|
101
|
+
oldStats: Record<any, any>,
|
|
102
|
+
newStats: Record<any, any>,
|
|
103
|
+
): Record<any, any> => {
|
|
104
|
+
newStats = JSON.parse(JSON.stringify(newStats));
|
|
105
|
+
|
|
106
|
+
for (const [id, report] of Object.entries(newStats)) {
|
|
107
|
+
delete report.id;
|
|
108
|
+
if (!oldStats[id]) continue;
|
|
109
|
+
|
|
110
|
+
for (const [name, value] of Object.entries(report)) {
|
|
111
|
+
if (value === oldStats[id][name]) {
|
|
112
|
+
delete report[name];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let timestamp = -Infinity;
|
|
118
|
+
for (const report of Object.values(newStats)) {
|
|
119
|
+
if (report.timestamp > timestamp) {
|
|
120
|
+
timestamp = report.timestamp;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const report of Object.values(newStats)) {
|
|
124
|
+
if (report.timestamp === timestamp) {
|
|
125
|
+
report.timestamp = 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
newStats.timestamp = timestamp;
|
|
129
|
+
return newStats;
|
|
130
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type RTCStatsDataType =
|
|
2
|
+
| RTCConfiguration
|
|
3
|
+
| RTCIceCandidate
|
|
4
|
+
| RTCSignalingState
|
|
5
|
+
| RTCIceConnectionState
|
|
6
|
+
| RTCIceGatheringState
|
|
7
|
+
| RTCPeerConnectionState
|
|
8
|
+
| [number | null | string] // RTCDataChannelEvent
|
|
9
|
+
| string
|
|
10
|
+
| RTCOfferOptions
|
|
11
|
+
| [string | RTCDataChannelInit | undefined] // createDataChannel
|
|
12
|
+
| (RTCOfferOptions | undefined) // createOffer | createAnswer
|
|
13
|
+
| RTCSessionDescriptionInit
|
|
14
|
+
| (RTCIceCandidateInit | RTCIceCandidate) // addIceCandidate
|
|
15
|
+
| object
|
|
16
|
+
| null
|
|
17
|
+
| undefined;
|
|
18
|
+
|
|
19
|
+
export type Trace = (tag: string, data: RTCStatsDataType) => void;
|
|
20
|
+
|
|
21
|
+
export type TraceRecord = [
|
|
22
|
+
tag: string,
|
|
23
|
+
id: string | null,
|
|
24
|
+
data: RTCStatsDataType,
|
|
25
|
+
timestamp: number,
|
|
26
|
+
];
|
package/src/store/CallState.ts
CHANGED
|
@@ -413,6 +413,7 @@ export class CallState {
|
|
|
413
413
|
|
|
414
414
|
this.eventHandlers = {
|
|
415
415
|
// these events are not updating the call state:
|
|
416
|
+
'call.frame_recording_ready': undefined,
|
|
416
417
|
'call.permission_request': undefined,
|
|
417
418
|
'call.recording_ready': undefined,
|
|
418
419
|
'call.rtmp_broadcast_failed': undefined,
|
|
@@ -445,6 +446,15 @@ export class CallState {
|
|
|
445
446
|
this.updateFromCallResponse(e.call);
|
|
446
447
|
this.setCurrentValue(this.endedBySubject, e.user);
|
|
447
448
|
},
|
|
449
|
+
'call.frame_recording_failed': (e) => {
|
|
450
|
+
this.updateFromCallResponse(e.call);
|
|
451
|
+
},
|
|
452
|
+
'call.frame_recording_started': (e) => {
|
|
453
|
+
this.updateFromCallResponse(e.call);
|
|
454
|
+
},
|
|
455
|
+
'call.frame_recording_stopped': (e) => {
|
|
456
|
+
this.updateFromCallResponse(e.call);
|
|
457
|
+
},
|
|
448
458
|
'call.hls_broadcasting_failed': this.updateFromHLSBroadcastingFailed,
|
|
449
459
|
'call.hls_broadcasting_started': (e) => {
|
|
450
460
|
this.updateFromCallResponse(e.call);
|