Skip to content

Commit e5d605e

Browse files
authored
Merge branch 'main' into worker-timers
2 parents 7cad627 + 380b0a3 commit e5d605e

File tree

42 files changed

+610
-84
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+610
-84
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"license": "See license in LICENSE",
88
"workspaces": [
99
"packages/*",
10-
"sample-apps/**/*"
10+
"sample-apps/*/*"
1111
],
1212
"scripts": {
1313
"start:react:sdk": "yarn workspace @stream-io/video-react-sdk run start",

packages/client/CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
44

5+
## [1.11.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.3...@stream-io/video-client-1.11.4) (2024-11-21)
6+
7+
8+
### Bug Fixes
9+
10+
* experimental option to force single codec preference in the SDP ([#1581](https://github.com/GetStream/stream-video-js/issues/1581)) ([894a86e](https://github.com/GetStream/stream-video-js/commit/894a86e407dc0dd36b7463bb964c86da0c3055d1))
11+
12+
## [1.11.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.2...@stream-io/video-client-1.11.3) (2024-11-20)
13+
14+
15+
### Bug Fixes
16+
17+
* respect codec overrides when computing the video layers ([#1582](https://github.com/GetStream/stream-video-js/issues/1582)) ([c22b83e](https://github.com/GetStream/stream-video-js/commit/c22b83ef710f2188e680b73790154de046a824e9))
18+
19+
## [1.11.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.1...@stream-io/video-client-1.11.2) (2024-11-14)
20+
21+
22+
### Bug Fixes
23+
24+
* fully reset token manager on user disconnect ([#1578](https://github.com/GetStream/stream-video-js/issues/1578)) ([6751abc](https://github.com/GetStream/stream-video-js/commit/6751abc0507085bd7c9f3f803f4c5929e0598bea)), closes [#1573](https://github.com/GetStream/stream-video-js/issues/1573)
25+
26+
## [1.11.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.0...@stream-io/video-client-1.11.1) (2024-11-14)
27+
28+
29+
### Bug Fixes
30+
31+
* reject was not called on timeout, decline and cancel scenarios ([#1576](https://github.com/GetStream/stream-video-js/issues/1576)) ([8be76a4](https://github.com/GetStream/stream-video-js/commit/8be76a447729aeba7f5c68f8a9bb85b4738cb76d))
32+
533
## [1.11.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.10.5...@stream-io/video-client-1.11.0) (2024-11-13)
634

735

packages/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stream-io/video-client",
3-
"version": "1.11.0",
3+
"version": "1.11.4",
44
"packageManager": "[email protected]",
55
"main": "dist/index.cjs.js",
66
"module": "dist/index.es.js",

packages/client/src/Call.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -516,20 +516,15 @@ export class Call {
516516
await waitUntilCallJoined();
517517
}
518518

519-
if (reject && this.ringing) {
520-
// I'm the one who started the call, so I should cancel it.
521-
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
522-
if (
523-
this.isCreatedByMe &&
524-
!hasOtherParticipants &&
525-
callingState === CallingState.RINGING
526-
) {
527-
// Signals other users that I have cancelled my call to them
528-
// before they accepted it.
529-
await this.reject();
530-
} else if (callingState === CallingState.RINGING) {
531-
// Signals other users that I have rejected the incoming call.
532-
await this.reject();
519+
if (callingState === CallingState.RINGING) {
520+
if (reject) {
521+
await this.reject(reason);
522+
} else {
523+
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
524+
if (this.isCreatedByMe && !hasOtherParticipants) {
525+
// I'm the one who started the call, so I should cancel it when there are no other participants.
526+
await this.reject('cancel');
527+
}
533528
}
534529
}
535530

@@ -1960,13 +1955,16 @@ export class Call {
19601955
// ignore if the call is not ringing
19611956
if (this.state.callingState !== CallingState.RINGING) return;
19621957

1963-
const timeoutInMs = settings.ring.auto_cancel_timeout_ms;
1958+
const timeoutInMs = this.isCreatedByMe
1959+
? settings.ring.auto_cancel_timeout_ms
1960+
: settings.ring.incoming_call_timeout_ms;
1961+
19641962
// 0 means no auto-drop
19651963
if (timeoutInMs <= 0) return;
19661964

19671965
clearTimeout(this.dropTimeout);
19681966
this.dropTimeout = setTimeout(() => {
1969-
this.leave({ reason: 'ring: timeout' }).catch((err) => {
1967+
this.leave({ reject: true, reason: 'timeout' }).catch((err) => {
19701968
this.logger('error', 'Failed to drop call', err);
19711969
});
19721970
}, timeoutInMs);

packages/client/src/__tests__/StreamVideoClient.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ describe('StreamVideoClient', () => {
100100
expect(response.calls).toBeDefined();
101101
});
102102

103+
it('should clear token on disconnect', async () => {
104+
const user = { id: 'jane' };
105+
const tp = vi.fn(tokenProvider(user.id));
106+
await client.connectUser(user, tp);
107+
await client.disconnectUser();
108+
await client.connectUser({ type: 'anonymous' });
109+
expect(tp).toBeCalledTimes(1);
110+
});
111+
103112
afterEach(() => {
104113
client.disconnectUser();
105114
});

packages/client/src/coordinator/connection/token_manager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export class TokenManager {
6363
*/
6464
reset = () => {
6565
this.token = undefined;
66+
this.tokenProvider = undefined;
67+
this.type = 'static';
6668
this.user = undefined;
6769
this.loadTokenPromise = null;
6870
};

packages/client/src/helpers/__tests__/sdp-munging.test.ts

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, expect, it } from 'vitest';
2-
import { enableHighQualityAudio, toggleDtx } from '../sdp-munging';
2+
import {
3+
enableHighQualityAudio,
4+
preserveCodec,
5+
toggleDtx,
6+
} from '../sdp-munging';
37
import { initialSdp as HQAudioSDP } from './hq-audio-sdp';
48

59
describe('sdp-munging', () => {
@@ -21,4 +25,167 @@ a=maxptime:40`;
2125
expect(sdpWithHighQualityAudio).toContain('maxaveragebitrate=510000');
2226
expect(sdpWithHighQualityAudio).toContain('stereo=1');
2327
});
28+
29+
it('preserves the preferred codec', () => {
30+
const sdp = `v=0
31+
o=- 8608371809202407637 2 IN IP4 127.0.0.1
32+
s=-
33+
t=0 0
34+
a=extmap-allow-mixed
35+
a=msid-semantic: WMS 52fafc21-b8bb-4f4f-8072-86a29cb6590e
36+
a=group:BUNDLE 0
37+
m=video 9 UDP/TLS/RTP/SAVPF 98 100 99 101
38+
c=IN IP4 0.0.0.0
39+
a=rtpmap:98 VP9/90000
40+
a=rtpmap:99 rtx/90000
41+
a=rtpmap:100 VP9/90000
42+
a=rtpmap:101 rtx/90000
43+
a=fmtp:98 profile-id=0
44+
a=fmtp:99 apt=98
45+
a=fmtp:100 profile-id=2
46+
a=fmtp:101 apt=100
47+
a=rtcp:9 IN IP4 0.0.0.0
48+
a=rtcp-fb:98 goog-remb
49+
a=rtcp-fb:98 transport-cc
50+
a=rtcp-fb:98 ccm fir
51+
a=rtcp-fb:98 nack
52+
a=rtcp-fb:98 nack pli
53+
a=rtcp-fb:100 goog-remb
54+
a=rtcp-fb:100 transport-cc
55+
a=rtcp-fb:100 ccm fir
56+
a=rtcp-fb:100 nack
57+
a=rtcp-fb:100 nack pli
58+
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
59+
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
60+
a=extmap:3 urn:3gpp:video-orientation
61+
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
62+
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
63+
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
64+
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
65+
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
66+
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
67+
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
68+
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
69+
a=extmap:12 https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension
70+
a=extmap:14 http://www.webrtc.org/experiments/rtp-hdrext/video-layers-allocation00
71+
a=setup:actpass
72+
a=mid:0
73+
a=msid:52fafc21-b8bb-4f4f-8072-86a29cb6590e 1bd1c5c2-d3cc-4490-ac0c-70b187242232
74+
a=sendonly
75+
a=ice-ufrag:LvRk
76+
a=ice-pwd:IpBRr2Rrg9TkOgayjYqALhPY
77+
a=fingerprint:sha-256 18:DE:8F:ED:E6:A2:0C:99:A8:25:AB:C9:F8:3D:91:4C:3E:9F:B4:1F:22:87:A7:3C:85:8F:F3:51:09:A7:E3:FA
78+
a=ice-options:trickle
79+
a=ssrc:3192778601 cname:yYSN5R+RG2j3luO7
80+
a=ssrc:3192778601 msid:52fafc21-b8bb-4f4f-8072-86a29cb6590e 1bd1c5c2-d3cc-4490-ac0c-70b187242232
81+
a=ssrc:283365205 cname:yYSN5R+RG2j3luO7
82+
a=ssrc:283365205 msid:52fafc21-b8bb-4f4f-8072-86a29cb6590e 1bd1c5c2-d3cc-4490-ac0c-70b187242232
83+
a=ssrc-group:FID 3192778601 283365205
84+
a=rtcp-mux
85+
a=rtcp-rsize`;
86+
const target = preserveCodec(sdp, '0', {
87+
mimeType: 'video/VP9',
88+
clockRate: 90000,
89+
sdpFmtpLine: 'profile-id=0',
90+
});
91+
expect(target).toContain('VP9');
92+
expect(target).not.toContain('profile-id=2');
93+
});
94+
95+
it('handles ios munging', () => {
96+
const sdp = `v=0
97+
o=- 525780719364332676 2 IN IP4 127.0.0.1
98+
s=-
99+
t=0 0
100+
a=group:BUNDLE 0
101+
a=extmap-allow-mixed
102+
a=msid-semantic: WMS BF3AFE62-88F8-4189-99D7-7CAE159205E3
103+
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 103 35 36 104 105 106
104+
c=IN IP4 0.0.0.0
105+
a=rtcp:9 IN IP4 0.0.0.0
106+
a=ice-ufrag:SAkq
107+
a=ice-pwd:FYHHro0VWRO8CjI/M1VG5vRw
108+
a=ice-options:trickle renomination
109+
a=fingerprint:sha-256 03:5B:16:0E:E1:7B:FE:4F:9A:5C:AC:CF:08:21:4B:49:CE:53:79:E6:97:AE:4E:73:F8:43:34:C3:11:F7:6D:E7
110+
a=setup:actpass
111+
a=mid:0
112+
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
113+
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
114+
a=extmap:3 urn:3gpp:video-orientation
115+
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
116+
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
117+
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
118+
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
119+
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
120+
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
121+
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
122+
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
123+
a=extmap:12 https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension
124+
a=extmap:14 http://www.webrtc.org/experiments/rtp-hdrext/video-layers-allocation00
125+
a=sendonly
126+
a=msid:BF3AFE62-88F8-4189-99D7-7CAE159205E3 6013DC02-A0A5-43A9-9D41-9D4A89648A42
127+
a=rtcp-mux
128+
a=rtcp-rsize
129+
a=rtpmap:96 H264/90000
130+
a=rtcp-fb:96 goog-remb
131+
a=rtcp-fb:96 transport-cc
132+
a=rtcp-fb:96 ccm fir
133+
a=rtcp-fb:96 nack
134+
a=rtcp-fb:96 nack pli
135+
a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c29
136+
a=rtpmap:97 rtx/90000
137+
a=fmtp:97 apt=96
138+
a=rtpmap:98 H264/90000
139+
a=rtcp-fb:98 goog-remb
140+
a=rtcp-fb:98 transport-cc
141+
a=rtcp-fb:98 ccm fir
142+
a=rtcp-fb:98 nack
143+
a=rtcp-fb:98 nack pli
144+
a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e029
145+
a=rtpmap:99 rtx/90000
146+
a=fmtp:99 apt=98
147+
a=rtpmap:100 VP8/90000
148+
a=rtcp-fb:100 goog-remb
149+
a=rtcp-fb:100 transport-cc
150+
a=rtcp-fb:100 ccm fir
151+
a=rtcp-fb:100 nack
152+
a=rtcp-fb:100 nack pli
153+
a=rtpmap:101 rtx/90000
154+
a=fmtp:101 apt=100
155+
a=rtpmap:127 VP9/90000
156+
a=rtcp-fb:127 goog-remb
157+
a=rtcp-fb:127 transport-cc
158+
a=rtcp-fb:127 ccm fir
159+
a=rtcp-fb:127 nack
160+
a=rtcp-fb:127 nack pli
161+
a=rtpmap:103 rtx/90000
162+
a=fmtp:103 apt=127
163+
a=rtpmap:35 AV1/90000
164+
a=rtcp-fb:35 goog-remb
165+
a=rtcp-fb:35 transport-cc
166+
a=rtcp-fb:35 ccm fir
167+
a=rtcp-fb:35 nack
168+
a=rtcp-fb:35 nack pli
169+
a=rtpmap:36 rtx/90000
170+
a=fmtp:36 apt=35
171+
a=rtpmap:104 red/90000
172+
a=rtpmap:105 rtx/90000
173+
a=fmtp:105 apt=104
174+
a=rtpmap:106 ulpfec/90000
175+
a=rid:q send
176+
a=rid:h send
177+
a=rid:f send
178+
a=simulcast:send q;h;f`;
179+
const target = preserveCodec(sdp, '0', {
180+
mimeType: 'video/H264',
181+
clockRate: 90000,
182+
sdpFmtpLine:
183+
'profile-level-id=42e029;packetization-mode=1;level-asymmetry-allowed=1',
184+
});
185+
expect(target).toContain('H264');
186+
expect(target).toContain('profile-level-id=42e029');
187+
expect(target).not.toContain('profile-level-id=640c29');
188+
expect(target).not.toContain('VP9');
189+
expect(target).not.toContain('AV1');
190+
});
24191
});

packages/client/src/helpers/sdp-munging.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,61 @@ export const toggleDtx = (sdp: string, enable: boolean): string => {
129129
return sdp.replace(opusFmtp.original, newFmtp);
130130
};
131131

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 (
160+
rtp.codec.toLowerCase() === codecName &&
161+
media.fmtp.some(
162+
(f) => f.payload === rtp.payload && equal(toSet(f.config), codecFmtp),
163+
)
164+
) {
165+
payloads.add(rtp.payload);
166+
}
167+
}
168+
169+
// find the corresponding rtx codec by matching apt=<preserved-codec-payload>
170+
for (const fmtp of media.fmtp) {
171+
const match = fmtp.config.match(/(apt)=(\d+)/);
172+
if (!match) continue;
173+
const [, , preservedCodecPayload] = match;
174+
if (payloads.has(Number(preservedCodecPayload))) {
175+
payloads.add(fmtp.payload);
176+
}
177+
}
178+
179+
media.rtp = media.rtp.filter((r) => payloads.has(r.payload));
180+
media.fmtp = media.fmtp.filter((f) => payloads.has(f.payload));
181+
media.rtcpFb = media.rtcpFb?.filter((f) => payloads.has(f.payload));
182+
media.payloads = Array.from(payloads).join(' ');
183+
}
184+
return SDP.write(parsedSdp);
185+
};
186+
132187
/**
133188
* Enables high-quality audio through SDP munging for the given trackMid.
134189
*

0 commit comments

Comments
 (0)