@stream-io/video-client 1.13.1 → 1.15.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.browser.es.js +1704 -1762
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +1706 -1780
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +1704 -1762
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +61 -30
  9. package/dist/src/StreamSfuClient.d.ts +4 -5
  10. package/dist/src/devices/CameraManager.d.ts +5 -8
  11. package/dist/src/devices/InputMediaDeviceManager.d.ts +5 -5
  12. package/dist/src/devices/MicrophoneManager.d.ts +7 -2
  13. package/dist/src/devices/ScreenShareManager.d.ts +1 -2
  14. package/dist/src/gen/coordinator/index.d.ts +904 -515
  15. package/dist/src/gen/video/sfu/event/events.d.ts +38 -19
  16. package/dist/src/gen/video/sfu/models/models.d.ts +76 -9
  17. package/dist/src/helpers/array.d.ts +7 -0
  18. package/dist/src/permissions/PermissionsContext.d.ts +6 -0
  19. package/dist/src/rtc/BasePeerConnection.d.ts +90 -0
  20. package/dist/src/rtc/Dispatcher.d.ts +0 -1
  21. package/dist/src/rtc/IceTrickleBuffer.d.ts +3 -2
  22. package/dist/src/rtc/Publisher.d.ts +32 -86
  23. package/dist/src/rtc/Subscriber.d.ts +4 -56
  24. package/dist/src/rtc/TransceiverCache.d.ts +55 -0
  25. package/dist/src/rtc/codecs.d.ts +1 -15
  26. package/dist/src/rtc/helpers/sdp.d.ts +8 -0
  27. package/dist/src/rtc/helpers/tracks.d.ts +1 -0
  28. package/dist/src/rtc/index.d.ts +3 -0
  29. package/dist/src/rtc/videoLayers.d.ts +11 -25
  30. package/dist/src/stats/{stateStoreStatsReporter.d.ts → CallStateStatsReporter.d.ts} +5 -1
  31. package/dist/src/stats/SfuStatsReporter.d.ts +4 -2
  32. package/dist/src/stats/index.d.ts +1 -1
  33. package/dist/src/stats/types.d.ts +8 -0
  34. package/dist/src/store/CallState.d.ts +47 -5
  35. package/dist/src/store/rxUtils.d.ts +15 -1
  36. package/dist/src/types.d.ts +26 -22
  37. package/package.json +1 -1
  38. package/src/Call.ts +310 -271
  39. package/src/StreamSfuClient.ts +9 -14
  40. package/src/StreamVideoClient.ts +1 -1
  41. package/src/__tests__/Call.publishing.test.ts +306 -0
  42. package/src/devices/CameraManager.ts +33 -16
  43. package/src/devices/InputMediaDeviceManager.ts +36 -27
  44. package/src/devices/MicrophoneManager.ts +29 -8
  45. package/src/devices/ScreenShareManager.ts +6 -8
  46. package/src/devices/__tests__/CameraManager.test.ts +111 -14
  47. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +4 -4
  48. package/src/devices/__tests__/MicrophoneManager.test.ts +59 -21
  49. package/src/devices/__tests__/ScreenShareManager.test.ts +5 -5
  50. package/src/devices/__tests__/mocks.ts +1 -0
  51. package/src/events/__tests__/internal.test.ts +132 -0
  52. package/src/events/__tests__/mutes.test.ts +0 -3
  53. package/src/events/__tests__/speaker.test.ts +92 -0
  54. package/src/events/participant.ts +3 -4
  55. package/src/gen/coordinator/index.ts +902 -514
  56. package/src/gen/video/sfu/event/events.ts +91 -30
  57. package/src/gen/video/sfu/models/models.ts +105 -13
  58. package/src/helpers/array.ts +14 -0
  59. package/src/permissions/PermissionsContext.ts +22 -0
  60. package/src/permissions/__tests__/PermissionsContext.test.ts +40 -0
  61. package/src/rpc/__tests__/createClient.test.ts +38 -0
  62. package/src/rpc/createClient.ts +11 -5
  63. package/src/rtc/BasePeerConnection.ts +240 -0
  64. package/src/rtc/Dispatcher.ts +0 -9
  65. package/src/rtc/IceTrickleBuffer.ts +24 -4
  66. package/src/rtc/Publisher.ts +210 -528
  67. package/src/rtc/Subscriber.ts +26 -200
  68. package/src/rtc/TransceiverCache.ts +120 -0
  69. package/src/rtc/__tests__/Publisher.test.ts +407 -210
  70. package/src/rtc/__tests__/Subscriber.test.ts +88 -36
  71. package/src/rtc/__tests__/mocks/webrtc.mocks.ts +22 -2
  72. package/src/rtc/__tests__/videoLayers.test.ts +161 -54
  73. package/src/rtc/codecs.ts +1 -131
  74. package/src/rtc/helpers/__tests__/rtcConfiguration.test.ts +34 -0
  75. package/src/rtc/helpers/__tests__/sdp.test.ts +59 -0
  76. package/src/rtc/helpers/sdp.ts +30 -0
  77. package/src/rtc/helpers/tracks.ts +3 -0
  78. package/src/rtc/index.ts +4 -0
  79. package/src/rtc/videoLayers.ts +68 -76
  80. package/src/stats/{stateStoreStatsReporter.ts → CallStateStatsReporter.ts} +58 -27
  81. package/src/stats/SfuStatsReporter.ts +31 -3
  82. package/src/stats/index.ts +1 -1
  83. package/src/stats/types.ts +12 -0
  84. package/src/store/CallState.ts +115 -5
  85. package/src/store/__tests__/CallState.test.ts +101 -0
  86. package/src/store/rxUtils.ts +23 -1
  87. package/src/types.ts +27 -22
  88. package/dist/src/helpers/sdp-munging.d.ts +0 -24
  89. package/dist/src/rtc/bitrateLookup.d.ts +0 -2
  90. package/dist/src/rtc/helpers/iceCandidate.d.ts +0 -2
  91. package/src/helpers/__tests__/hq-audio-sdp.ts +0 -332
  92. package/src/helpers/__tests__/sdp-munging.test.ts +0 -283
  93. package/src/helpers/sdp-munging.ts +0 -265
  94. package/src/rtc/__tests__/bitrateLookup.test.ts +0 -12
  95. package/src/rtc/__tests__/codecs.test.ts +0 -145
  96. package/src/rtc/bitrateLookup.ts +0 -61
  97. package/src/rtc/helpers/iceCandidate.ts +0 -16
  98. /package/dist/src/{compatibility.d.ts → helpers/compatibility.d.ts} +0 -0
  99. /package/src/{compatibility.ts → helpers/compatibility.ts} +0 -0
@@ -1,265 +0,0 @@
1
- import * as SDP from 'sdp-transform';
2
-
3
- type Media = {
4
- original: string;
5
- mediaWithPorts: string;
6
- codecOrder: string;
7
- };
8
-
9
- type RtpMap = {
10
- original: string;
11
- payload: string;
12
- codec: string;
13
- };
14
-
15
- type Fmtp = {
16
- original: string;
17
- payload: string;
18
- config: string;
19
- };
20
-
21
- const getRtpMap = (line: string): RtpMap | undefined => {
22
- // Example: a=rtpmap:110 opus/48000/2
23
- const rtpRegex = /^a=rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/;
24
- // The first captured group is the payload type number, the second captured group is the encoding name, the third captured group is the clock rate, and the fourth captured group is any additional parameters.
25
- const rtpMatch = rtpRegex.exec(line);
26
- if (rtpMatch) {
27
- return {
28
- original: rtpMatch[0],
29
- payload: rtpMatch[1],
30
- codec: rtpMatch[2],
31
- };
32
- }
33
- };
34
-
35
- const getFmtp = (line: string): Fmtp | undefined => {
36
- // Example: a=fmtp:111 minptime=10; useinbandfec=1
37
- const fmtpRegex = /^a=fmtp:(\d*) (.*)/;
38
- const fmtpMatch = fmtpRegex.exec(line);
39
- // The first captured group is the payload type number, the second captured group is any additional parameters.
40
- if (fmtpMatch) {
41
- return {
42
- original: fmtpMatch[0],
43
- payload: fmtpMatch[1],
44
- config: fmtpMatch[2],
45
- };
46
- }
47
- };
48
-
49
- /**
50
- * gets the media section for the specified media type.
51
- * The media section contains the media type, port, codec, and payload type.
52
- * Example: m=video 9 UDP/TLS/RTP/SAVPF 100 101 96 97 35 36 102 125 127
53
- */
54
- const getMedia = (line: string, mediaType: string): Media | undefined => {
55
- const regex = new RegExp(`(m=${mediaType} \\d+ [\\w/]+) ([\\d\\s]+)`);
56
- const match = regex.exec(line);
57
- if (match) {
58
- return {
59
- original: match[0],
60
- mediaWithPorts: match[1],
61
- codecOrder: match[2],
62
- };
63
- }
64
- };
65
-
66
- const getMediaSection = (sdp: string, mediaType: 'video' | 'audio') => {
67
- let media: Media | undefined;
68
- const rtpMap: RtpMap[] = [];
69
- const fmtp: Fmtp[] = [];
70
- let isTheRequiredMediaSection = false;
71
- sdp.split(/(\r\n|\r|\n)/).forEach((line) => {
72
- const isValidLine = /^([a-z])=(.*)/.test(line);
73
- if (!isValidLine) return;
74
- /*
75
- NOTE: according to https://www.rfc-editor.org/rfc/rfc8866.pdf
76
- Each media description starts with an "m=" line and continues to the next media description or the end of the whole session description, whichever comes first
77
- */
78
- const type = line[0];
79
- if (type === 'm') {
80
- const _media = getMedia(line, mediaType);
81
- isTheRequiredMediaSection = !!_media;
82
- if (_media) {
83
- media = _media;
84
- }
85
- } else if (isTheRequiredMediaSection && type === 'a') {
86
- const rtpMapLine = getRtpMap(line);
87
- const fmtpLine = getFmtp(line);
88
- if (rtpMapLine) {
89
- rtpMap.push(rtpMapLine);
90
- } else if (fmtpLine) {
91
- fmtp.push(fmtpLine);
92
- }
93
- }
94
- });
95
- if (media) {
96
- return {
97
- media,
98
- rtpMap,
99
- fmtp,
100
- };
101
- }
102
- };
103
-
104
- /**
105
- * Gets the fmtp line corresponding to opus
106
- */
107
- const getOpusFmtp = (sdp: string): Fmtp | undefined => {
108
- const section = getMediaSection(sdp, 'audio');
109
- const rtpMap = section?.rtpMap.find((r) => r.codec.toLowerCase() === 'opus');
110
- const codecId = rtpMap?.payload;
111
- if (codecId) {
112
- return section?.fmtp.find((f) => f.payload === codecId);
113
- }
114
- };
115
-
116
- /**
117
- * Returns an SDP with DTX enabled or disabled.
118
- */
119
- export const toggleDtx = (sdp: string, enable: boolean): string => {
120
- const opusFmtp = getOpusFmtp(sdp);
121
- if (!opusFmtp) return sdp;
122
-
123
- const matchDtx = /usedtx=(\d)/.exec(opusFmtp.config);
124
- const requiredDtxConfig = `usedtx=${enable ? '1' : '0'}`;
125
- const newFmtp = matchDtx
126
- ? opusFmtp.original.replace(/usedtx=(\d)/, requiredDtxConfig)
127
- : `${opusFmtp.original};${requiredDtxConfig}`;
128
-
129
- return sdp.replace(opusFmtp.original, newFmtp);
130
- };
131
-
132
- /**
133
- * Returns and SDP with all the codecs except the given codec removed.
134
- */
135
- export const preserveCodec = (
136
- sdp: string,
137
- mid: string,
138
- codec: RTCRtpCodec,
139
- ): string => {
140
- const [kind, codecName] = codec.mimeType.toLowerCase().split('/');
141
-
142
- const toSet = (fmtpLine: string) =>
143
- new Set(fmtpLine.split(';').map((f) => f.trim().toLowerCase()));
144
-
145
- const equal = (a: Set<string>, b: Set<string>) => {
146
- if (a.size !== b.size) return false;
147
- for (const item of a) if (!b.has(item)) return false;
148
- return true;
149
- };
150
-
151
- const codecFmtp = toSet(codec.sdpFmtpLine || '');
152
- const parsedSdp = SDP.parse(sdp);
153
- for (const media of parsedSdp.media) {
154
- if (media.type !== kind || String(media.mid) !== mid) continue;
155
-
156
- // find the payload id of the desired codec
157
- const payloads = new Set<number>();
158
- for (const rtp of media.rtp) {
159
- if (rtp.codec.toLowerCase() !== codecName) continue;
160
- const match =
161
- // vp8 doesn't have any fmtp, we preserve it without any additional checks
162
- codecName === 'vp8'
163
- ? true
164
- : media.fmtp.some(
165
- (f) =>
166
- f.payload === rtp.payload && equal(toSet(f.config), codecFmtp),
167
- );
168
- if (match) {
169
- payloads.add(rtp.payload);
170
- }
171
- }
172
-
173
- // find the corresponding rtx codec by matching apt=<preserved-codec-payload>
174
- for (const fmtp of media.fmtp) {
175
- const match = fmtp.config.match(/(apt)=(\d+)/);
176
- if (!match) continue;
177
- const [, , preservedCodecPayload] = match;
178
- if (payloads.has(Number(preservedCodecPayload))) {
179
- payloads.add(fmtp.payload);
180
- }
181
- }
182
-
183
- media.rtp = media.rtp.filter((r) => payloads.has(r.payload));
184
- media.fmtp = media.fmtp.filter((f) => payloads.has(f.payload));
185
- media.rtcpFb = media.rtcpFb?.filter((f) => payloads.has(f.payload));
186
- media.payloads = Array.from(payloads).join(' ');
187
- }
188
- return SDP.write(parsedSdp);
189
- };
190
-
191
- /**
192
- * Enables high-quality audio through SDP munging for the given trackMid.
193
- *
194
- * @param sdp the SDP to munge.
195
- * @param trackMid the trackMid.
196
- * @param maxBitrate the max bitrate to set.
197
- */
198
- export const enableHighQualityAudio = (
199
- sdp: string,
200
- trackMid: string,
201
- maxBitrate: number = 510000,
202
- ): string => {
203
- maxBitrate = Math.max(Math.min(maxBitrate, 510000), 96000);
204
-
205
- const parsedSdp = SDP.parse(sdp);
206
- const audioMedia = parsedSdp.media.find(
207
- (m) => m.type === 'audio' && String(m.mid) === trackMid,
208
- );
209
-
210
- if (!audioMedia) return sdp;
211
-
212
- const opusRtp = audioMedia.rtp.find((r) => r.codec === 'opus');
213
- if (!opusRtp) return sdp;
214
-
215
- const opusFmtp = audioMedia.fmtp.find((f) => f.payload === opusRtp.payload);
216
- if (!opusFmtp) return sdp;
217
-
218
- // enable stereo, if not already enabled
219
- if (opusFmtp.config.match(/stereo=(\d)/)) {
220
- opusFmtp.config = opusFmtp.config.replace(/stereo=(\d)/, 'stereo=1');
221
- } else {
222
- opusFmtp.config = `${opusFmtp.config};stereo=1`;
223
- }
224
-
225
- // set maxaveragebitrate, to the given value
226
- if (opusFmtp.config.match(/maxaveragebitrate=(\d*)/)) {
227
- opusFmtp.config = opusFmtp.config.replace(
228
- /maxaveragebitrate=(\d*)/,
229
- `maxaveragebitrate=${maxBitrate}`,
230
- );
231
- } else {
232
- opusFmtp.config = `${opusFmtp.config};maxaveragebitrate=${maxBitrate}`;
233
- }
234
-
235
- return SDP.write(parsedSdp);
236
- };
237
-
238
- /**
239
- * Extracts the mid from the transceiver or the SDP.
240
- *
241
- * @param transceiver the transceiver.
242
- * @param transceiverInitIndex the index of the transceiver in the transceiver's init array.
243
- * @param sdp the SDP.
244
- */
245
- export const extractMid = (
246
- transceiver: RTCRtpTransceiver,
247
- transceiverInitIndex: number,
248
- sdp: string | undefined,
249
- ): string => {
250
- if (transceiver.mid) return transceiver.mid;
251
- if (!sdp) return '';
252
-
253
- const track = transceiver.sender.track!;
254
- const parsedSdp = SDP.parse(sdp);
255
- const media = parsedSdp.media.find((m) => {
256
- return (
257
- m.type === track.kind &&
258
- // if `msid` is not present, we assume that the track is the first one
259
- (m.msid?.includes(track.id) ?? true)
260
- );
261
- });
262
- if (typeof media?.mid !== 'undefined') return String(media.mid);
263
- if (transceiverInitIndex === -1) return '';
264
- return String(transceiverInitIndex);
265
- };
@@ -1,12 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { getOptimalBitrate } from '../bitrateLookup';
3
-
4
- describe('bitrateLookup', () => {
5
- it('should return optimal bitrate', () => {
6
- expect(getOptimalBitrate('vp9', 720)).toBe(1_250_000);
7
- });
8
-
9
- it('should return nearest bitrate for exotic dimensions', () => {
10
- expect(getOptimalBitrate('vp9', 1000)).toBe(1_500_000);
11
- });
12
- });
@@ -1,145 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { getPreferredCodecs } from '../codecs';
3
- import './mocks/webrtc.mocks';
4
-
5
- describe('codecs', () => {
6
- it('should return preferred audio codec', () => {
7
- RTCRtpReceiver.getCapabilities = vi.fn().mockReturnValue(audioCodecs);
8
- const codecs = getPreferredCodecs('audio', 'red', undefined, 'receiver');
9
- expect(codecs).toBeDefined();
10
- expect(codecs?.map((c) => c.mimeType)).toEqual([
11
- 'audio/red',
12
- 'audio/opus',
13
- 'audio/G722',
14
- 'audio/PCMU',
15
- 'audio/PCMA',
16
- 'audio/CN',
17
- 'audio/telephone-event',
18
- ]);
19
- });
20
-
21
- it('should return preferred video codec', () => {
22
- RTCRtpReceiver.getCapabilities = vi.fn().mockReturnValue(videoCodecs);
23
- const codecs = getPreferredCodecs('video', 'vp8', undefined, 'receiver');
24
- expect(codecs).toBeDefined();
25
- // prettier-ignore
26
- expect(codecs?.map((c) => [c.mimeType, c.sdpFmtpLine])).toEqual([
27
- ['video/VP8', undefined],
28
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f'],
29
- ['video/rtx', undefined],
30
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'],
31
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=640c1f'],
32
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f'],
33
- ['video/VP9', 'profile-id=0'],
34
- ['video/VP9', 'profile-id=2'],
35
- ['video/red', undefined],
36
- ['video/ulpfec', undefined],
37
- ['video/flexfec-03', 'repair-window=10000000'],
38
- ]);
39
- });
40
-
41
- it('should pick the baseline H264 codec', () => {
42
- RTCRtpReceiver.getCapabilities = vi.fn().mockReturnValue(videoCodecs);
43
- const codecs = getPreferredCodecs('video', 'h264', undefined, 'receiver');
44
- expect(codecs).toBeDefined();
45
- // prettier-ignore
46
- expect(codecs?.map((c) => [c.mimeType, c.sdpFmtpLine])).toEqual([
47
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'],
48
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f'],
49
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f'],
50
- ['video/H264', 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=640c1f'],
51
- ['video/rtx', undefined],
52
- ['video/VP8', undefined],
53
- ['video/VP9', 'profile-id=0'],
54
- ['video/VP9', 'profile-id=2'],
55
- ['video/red', undefined],
56
- ['video/ulpfec', undefined],
57
- ['video/flexfec-03', 'repair-window=10000000'],
58
- ]);
59
- });
60
-
61
- it('should pick the baseline H264 codec with optional packetization-mode', () => {
62
- RTCRtpReceiver.getCapabilities = vi
63
- .fn()
64
- .mockReturnValue(videoCodecsFirefox);
65
- const codecs = getPreferredCodecs('video', 'h264', undefined, 'receiver');
66
- expect(codecs).toBeDefined();
67
- // prettier-ignore
68
- expect(codecs?.map((c) => [c.mimeType, c.sdpFmtpLine])).toEqual([
69
- ['video/H264', 'profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1'],
70
- ['video/H264', 'profile-level-id=42e01f;level-asymmetry-allowed=1'],
71
- ['video/VP8', 'max-fs=12288;max-fr=60'],
72
- ['video/rtx', undefined],
73
- ['video/VP9', 'max-fs=12288;max-fr=60'],
74
- ['video/ulpfec', undefined],
75
- ['video/red', undefined],
76
- ]);
77
- });
78
- });
79
-
80
- // prettier-ignore
81
- const videoCodecsFirefox: RTCRtpCapabilities = {
82
- codecs: [
83
- { mimeType: 'video/VP8', sdpFmtpLine: 'max-fs=12288;max-fr=60', clockRate: 90000 },
84
- { mimeType: 'video/rtx', clockRate: 90000 },
85
- { mimeType: 'video/VP9', sdpFmtpLine: 'max-fs=12288;max-fr=60', clockRate: 90000 },
86
- { mimeType: 'video/H264', sdpFmtpLine: 'profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1', clockRate: 90000 },
87
- { mimeType: 'video/H264', sdpFmtpLine: 'profile-level-id=42e01f;level-asymmetry-allowed=1', clockRate: 90000 },
88
- { mimeType: 'video/ulpfec', clockRate: 90000 },
89
- { mimeType: 'video/red', clockRate: 90000 },
90
- ],
91
- headerExtensions: [
92
- { uri: 'urn:ietf:params:rtp-hdrext:sdes:mid' },
93
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' },
94
- { uri: 'urn:ietf:params:rtp-hdrext:toffset' },
95
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay' },
96
- ],
97
- };
98
-
99
- // prettier-ignore
100
- const videoCodecs: RTCRtpCapabilities = {
101
- codecs: [
102
- { mimeType: 'video/H264', sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f', clockRate: 90000 },
103
- { mimeType: 'video/rtx', clockRate: 90000 },
104
- { mimeType: 'video/H264', sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f', clockRate: 90000 },
105
- { mimeType: 'video/H264', sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=640c1f', clockRate: 90000 },
106
- { mimeType: 'video/H264', sdpFmtpLine: 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f', clockRate: 90000 },
107
- { mimeType: 'video/VP8', clockRate: 90000 },
108
- { mimeType: 'video/VP9', sdpFmtpLine: 'profile-id=0', clockRate: 90000 },
109
- { mimeType: 'video/VP9', sdpFmtpLine: 'profile-id=2', clockRate: 90000 },
110
- { mimeType: 'video/red', clockRate: 90000 },
111
- { mimeType: 'video/ulpfec', clockRate: 90000 },
112
- { mimeType: 'video/flexfec-03', sdpFmtpLine: 'repair-window=10000000', clockRate: 90000 },
113
- ],
114
- headerExtensions: [
115
- { uri: 'urn:ietf:params:rtp-hdrext:toffset' },
116
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' },
117
- { uri: 'urn:3gpp:video-orientation' },
118
- { uri: 'http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01' },
119
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/playout-delay' },
120
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/video-content-type' },
121
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/video-timing' },
122
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/color-space' },
123
- { uri: 'urn:ietf:params:rtp-hdrext:sdes:mid' },
124
- { uri: 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id' },
125
- { uri: 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id' },
126
- ],
127
- };
128
-
129
- // prettier-ignore
130
- const audioCodecs: RTCRtpCapabilities = {
131
- codecs: [
132
- { mimeType: 'audio/opus', sdpFmtpLine: 'minptime=10;useinbandfec=1', clockRate: 48000 },
133
- { mimeType: 'audio/red', sdpFmtpLine: '=111/111', clockRate: 48000 },
134
- { mimeType: 'audio/G722', clockRate: 8000, channels: 1 },
135
- { mimeType: 'audio/PCMU', clockRate: 8000, channels: 1 },
136
- { mimeType: 'audio/PCMA', clockRate: 8000, channels: 1 },
137
- { mimeType: 'audio/CN', clockRate: 8000, channels: 1 },
138
- { mimeType: 'audio/telephone-event', clockRate: 8000, channels: 1 },
139
- ],
140
- headerExtensions: [
141
- { uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level' },
142
- { uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time' },
143
- { uri: 'urn:ietf:params:rtp-hdrext:sdes:mid' },
144
- ],
145
- };
@@ -1,61 +0,0 @@
1
- import { PreferredCodec } from '../types';
2
-
3
- const bitrateLookupTable: Record<
4
- PreferredCodec,
5
- Record<number | 'default', number | undefined> | undefined
6
- > = {
7
- h264: {
8
- 2160: 5_000_000,
9
- 1440: 3_000_000,
10
- 1080: 2_000_000,
11
- 720: 1_250_000,
12
- 540: 750_000,
13
- 360: 400_000,
14
- default: 1_250_000,
15
- },
16
- vp8: {
17
- 2160: 5_000_000,
18
- 1440: 2_750_000,
19
- 1080: 2_000_000,
20
- 720: 1_250_000,
21
- 540: 600_000,
22
- 360: 350_000,
23
- default: 1_250_000,
24
- },
25
- vp9: {
26
- 2160: 3_000_000,
27
- 1440: 2_000_000,
28
- 1080: 1_500_000,
29
- 720: 1_250_000,
30
- 540: 500_000,
31
- 360: 275_000,
32
- default: 1_250_000,
33
- },
34
- av1: {
35
- 2160: 2_000_000,
36
- 1440: 1_550_000,
37
- 1080: 1_000_000,
38
- 720: 600_000,
39
- 540: 350_000,
40
- 360: 200_000,
41
- default: 600_000,
42
- },
43
- };
44
-
45
- export const getOptimalBitrate = (
46
- codec: PreferredCodec,
47
- frameHeight: number,
48
- ): number => {
49
- const codecLookup = bitrateLookupTable[codec];
50
- if (!codecLookup) throw new Error(`Unknown codec: ${codec}`);
51
-
52
- let bitrate = codecLookup[frameHeight];
53
- if (!bitrate) {
54
- const keys = Object.keys(codecLookup).map(Number);
55
- const nearest = keys.reduce((a, b) =>
56
- Math.abs(b - frameHeight) < Math.abs(a - frameHeight) ? b : a,
57
- );
58
- bitrate = codecLookup[nearest];
59
- }
60
- return bitrate ?? codecLookup.default!;
61
- };
@@ -1,16 +0,0 @@
1
- import { ICETrickle } from '../../gen/video/sfu/models/models';
2
-
3
- export function getIceCandidate(
4
- candidate: RTCIceCandidate,
5
- ): ICETrickle['iceCandidate'] {
6
- if (!candidate.usernameFragment) {
7
- // react-native-webrtc doesn't include usernameFragment in the candidate
8
- const splittedCandidate = candidate.candidate.split(' ');
9
- const ufragIndex =
10
- splittedCandidate.findIndex((s: string) => s === 'ufrag') + 1;
11
- const usernameFragment = splittedCandidate[ufragIndex];
12
- return JSON.stringify({ ...candidate, usernameFragment });
13
- } else {
14
- return JSON.stringify(candidate.toJSON());
15
- }
16
- }