Skip to content

Commit 14a9c12

Browse files
committed
Merge tag 'v1.15.10' into mconf-build
# Conflicts: # .github/workflows/build-test-and-deploy.yml # docker-compose.yaml # play/src/common/FrontConfigurationInterface.ts # play/src/front/Components/Video/utils.ts # play/src/front/Enum/EnvironmentVariable.ts # play/src/pusher/enums/EnvironmentVariable.ts
2 parents 9b17720 + c166d26 commit 14a9c12

File tree

11 files changed

+348
-436
lines changed

11 files changed

+348
-436
lines changed

.github/workflows/build-test-and-deploy.yml

Lines changed: 28 additions & 434 deletions
Large diffs are not rendered by default.

docker-compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ services:
103103
REPORT_ISSUES_URL: $REPORT_ISSUES_URL
104104
# LogRocket
105105
LOGROCKET_ID: $LOGROCKET_ID
106+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: "$PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS"
107+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: "$PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS"
106108
labels:
107109
- "traefik.enable=true"
108110
- "traefik.http.routers.front.rule=Host(`front.workadventure.localhost`)"
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import "jasmine";
2+
3+
import { getSdpTransform } from "../../../src/Components/Video/utils";
4+
5+
describe("getSdpTransform()", () => {
6+
it("should not do anything if bandwidth = 0", () => {
7+
const bw = 0;
8+
const originalSdp = `
9+
m=audio
10+
c=IN IP4 0.0.0.0
11+
b=AS:11
12+
m=video
13+
c=IN IP4 0.0.0.0
14+
m=video
15+
c=IN IP4 0.0.0.0
16+
b=AS:22
17+
m=application
18+
c=IN IP4 0.0.0.0
19+
b=AS:33`.replace(/^[\s]*/gm, "");
20+
21+
const modifiedSdp = getSdpTransform(bw)(originalSdp);
22+
expect(modifiedSdp).toEqual(originalSdp);
23+
});
24+
25+
it("should create the 'b' field of the video media if it doesn't exist", () => {
26+
const bw = 55;
27+
const originalSdp = `
28+
m=audio
29+
c=IN IP4 0.0.0.0
30+
m=video
31+
c=IN IP4 0.0.0.0
32+
m=video
33+
c=IN IP4 0.0.0.0
34+
m=application
35+
c=IN IP4 0.0.0.0`.replace(/^[\s]*/gm, "");
36+
37+
const modifiedSdp = getSdpTransform(bw)(originalSdp);
38+
expect(modifiedSdp).toEqual(expectedDefaultSdp(bw));
39+
});
40+
41+
it("should update the 'b' field of the video media if it already exists", () => {
42+
const bw = 66;
43+
const originalSdp = `
44+
m=audio
45+
c=IN IP4 0.0.0.0
46+
m=video
47+
c=IN IP4 0.0.0.0
48+
b=AS:11
49+
m=video
50+
c=IN IP4 0.0.0.0
51+
b=AS:22
52+
m=application
53+
c=IN IP4 0.0.0.0`.replace(/^[\s]*/gm, "");
54+
55+
const modifiedSdp = getSdpTransform(bw)(originalSdp);
56+
expect(modifiedSdp).toEqual(expectedDefaultSdp(bw));
57+
});
58+
59+
it("should create and update the 'b' field of each video media if one doesn't exist and the other does", () => {
60+
const bw = 77;
61+
const originalSdp = `
62+
m=audio
63+
c=IN IP4 0.0.0.0
64+
m=video
65+
c=IN IP4 0.0.0.0
66+
b=AS:11
67+
m=video
68+
c=IN IP4 0.0.0.0
69+
m=application
70+
c=IN IP4 0.0.0.0`.replace(/^[\s]*/gm, "");
71+
72+
const modifiedSdp = getSdpTransform(bw)(originalSdp);
73+
expect(modifiedSdp).toEqual(expectedDefaultSdp(bw));
74+
});
75+
76+
it("should not change the 'b' field of other medias", () => {
77+
const bw = 88;
78+
const originalSdp = `
79+
m=audio
80+
c=IN IP4 0.0.0.0
81+
b=AS:11
82+
m=video
83+
c=IN IP4 0.0.0.0
84+
b=AS:22
85+
m=video
86+
c=IN IP4 0.0.0.0
87+
b=AS:33
88+
m=application
89+
c=IN IP4 0.0.0.0
90+
b=AS:44`.replace(/^[\s]*/gm, "");
91+
92+
const modifiedSdp = getSdpTransform(bw)(originalSdp);
93+
expect(modifiedSdp).toEqual(expectedFullBWSdp(bw));
94+
});
95+
96+
// Expected sdp where the audio and application medias have no 'b' fields
97+
// and the video medias should have 'b=TIAS=:<bw*1000>' and 'b=AS:<bw>'.
98+
const expectedDefaultSdp = (bw: integer) => {
99+
return `
100+
m=audio
101+
c=IN IP4 0.0.0.0
102+
m=video
103+
c=IN IP4 0.0.0.0
104+
b=TIAS:${bw * 1000}
105+
b=AS:${bw}
106+
m=video
107+
c=IN IP4 0.0.0.0
108+
b=TIAS:${bw * 1000}
109+
b=AS:${bw}
110+
m=application
111+
c=IN IP4 0.0.0.0`.replace(/^[\s]*/gm, "");
112+
};
113+
114+
// Expected sdp where the audio and application medias have a 'b' field
115+
// and the video medias should have 'b=TIAS=:<bw*1000>' and 'b=AS:<bw>'.
116+
const expectedFullBWSdp = (bw: integer) => {
117+
return `
118+
m=audio
119+
c=IN IP4 0.0.0.0
120+
b=AS:11
121+
m=video
122+
c=IN IP4 0.0.0.0
123+
b=TIAS:${bw * 1000}
124+
b=AS:${bw}
125+
m=video
126+
c=IN IP4 0.0.0.0
127+
b=TIAS:${bw * 1000}
128+
b=AS:${bw}
129+
m=application
130+
c=IN IP4 0.0.0.0
131+
b=AS:44`.replace(/^[\s]*/gm, "");
132+
};
133+
});

play/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
"typesafe-i18n": "^5.14.0",
154154
"typescript": "^4.9.4",
155155
"uuid": "^9.0.0",
156+
"webrtc-adapter": "^8.1.1",
156157
"zod": "^3.19.1"
157158
}
158159
}

play/src/common/FrontConfigurationInterface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ export interface FrontConfigurationInterface {
3232
OPID_WOKA_NAME_POLICY: OpidWokaNamePolicy | undefined;
3333
ENABLE_REPORT_ISSUES_MENU: boolean | undefined;
3434
REPORT_ISSUES_URL: string | undefined;
35+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: number;
36+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: number;
3537
}

play/src/front/Components/Video/utils.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,44 @@ export function checkCoturnServer(user: UserSimplePeerInterface) {
168168
}
169169
}, 5000);
170170
}
171+
172+
export function getSdpTransform(videoBandwidth = 0) {
173+
return (sdp: string) => {
174+
sdp = updateBandwidthRestriction(sdp, videoBandwidth, "video");
175+
176+
return sdp;
177+
};
178+
}
179+
180+
function updateBandwidthRestriction(sdp: string, bandwidth: integer, mediaType: string): string {
181+
if (bandwidth <= 0) {
182+
return sdp;
183+
}
184+
185+
for (
186+
let targetMediaPos = sdp.indexOf(`m=${mediaType}`);
187+
targetMediaPos !== -1;
188+
targetMediaPos = sdp.indexOf(`m=${mediaType}`, targetMediaPos + 1)
189+
) {
190+
// offer TIAS and AS (in this order)
191+
for (const modifier of ["AS", "TIAS"]) {
192+
const nextMediaPos = sdp.indexOf(`m=`, targetMediaPos + 1);
193+
const newBandwidth = modifier === "TIAS" ? (bandwidth >>> 0) * 1000 : bandwidth;
194+
const nextBWPos = sdp.indexOf(`b=${modifier}:`, targetMediaPos + 1);
195+
196+
let mediaSlice = sdp.slice(targetMediaPos);
197+
const bwFieldAlreadyExists = nextBWPos !== -1 && (nextBWPos < nextMediaPos || nextMediaPos === -1);
198+
if (bwFieldAlreadyExists) {
199+
// delete it
200+
mediaSlice = mediaSlice.replace(new RegExp(`b=${modifier}:.*[\r?\n]`), "");
201+
}
202+
// insert b= after c= line.
203+
mediaSlice = mediaSlice.replace(/c=IN (.*)(\r?\n)/, `c=IN $1$2b=${modifier}:${newBandwidth}$2`);
204+
205+
// update the sdp
206+
sdp = sdp.slice(0, targetMediaPos) + mediaSlice;
207+
}
208+
}
209+
210+
return sdp;
211+
}

play/src/front/Enum/EnvironmentVariable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export const FALLBACK_LOCALE = env.FALLBACK_LOCALE;
3939
export const OPID_WOKA_NAME_POLICY = env.OPID_WOKA_NAME_POLICY;
4040
export const ENABLE_REPORT_ISSUES_MENU = env.ENABLE_REPORT_ISSUES_MENU;
4141
export const REPORT_ISSUES_URL = env.REPORT_ISSUES_URL;
42+
export const PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS = env.PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS;
43+
export const PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS = env.PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS;
4244

4345
export const POSITION_DELAY = 200; // Wait 200ms between sending position events
4446
export const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player

play/src/front/WebRtc/ScreenSharingPeer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { MESSAGE_TYPE_CONSTRAINT } from "./VideoPeer";
44
import type { UserSimplePeerInterface } from "./SimplePeer";
55
import type { Readable, Writable } from "svelte/store";
66
import { readable, writable } from "svelte/store";
7-
import { getIceServersConfig } from "../Components/Video/utils";
7+
import { getIceServersConfig, getSdpTransform } from "../Components/Video/utils";
88
import { highlightedEmbedScreen } from "../Stores/EmbedScreensStore";
99
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
1010
import Peer from "simple-peer/simplepeer.min.js";
1111
import { Buffer } from "buffer";
12+
import { PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS } from "../Enum/EnvironmentVariable";
1213

1314
/**
1415
* A peer connection used to transmit video / audio signals between 2 peers.
@@ -37,6 +38,7 @@ export class ScreenSharingPeer extends Peer {
3738
config: {
3839
iceServers: getIceServersConfig(user),
3940
},
41+
sdpTransform: getSdpTransform(PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS),
4042
});
4143

4244
this.userId = user.userId;

play/src/front/WebRtc/VideoPeer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import {
1414
newChatMessageWritingStatusSubject,
1515
writingStatusMessageStore,
1616
} from "../Stores/ChatStore";
17-
import { getIceServersConfig } from "../Components/Video/utils";
17+
import { getIceServersConfig, getSdpTransform } from "../Components/Video/utils";
1818
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
1919
import { SoundMeter } from "../Phaser/Components/SoundMeter";
2020
import Peer from "simple-peer/simplepeer.min.js";
2121
import { Buffer } from "buffer";
2222
import { gameManager } from "../Phaser/Game/GameManager";
23+
import { PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS } from "../Enum/EnvironmentVariable";
2324

2425
export type PeerStatus = "connecting" | "connected" | "error" | "closed";
2526

@@ -63,6 +64,7 @@ export class VideoPeer extends Peer {
6364
config: {
6465
iceServers: getIceServersConfig(user),
6566
},
67+
sdpTransform: getSdpTransform(PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS),
6668
});
6769

6870
this.userId = user.userId;

play/src/pusher/enums/EnvironmentVariable.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ const EnvironmentVariables = z.object({
7676
ENABLE_REPORT_ISSUES_MENU: BoolAsString.optional(),
7777
REPORT_ISSUES_URL: z.string().url().optional().or(z.literal("")),
7878
LOGROCKET_ID: z.string().optional(),
79+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: PositiveIntAsString.optional(),
80+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: PositiveIntAsString.optional(),
7981
});
8082

8183
type EnvironmentVariables = z.infer<typeof EnvironmentVariables>;
@@ -202,4 +204,6 @@ export const FRONT_ENVIRONMENT_VARIABLES: FrontConfigurationInterface = {
202204
OPID_WOKA_NAME_POLICY,
203205
ENABLE_REPORT_ISSUES_MENU: toBool(env.ENABLE_REPORT_ISSUES_MENU, false),
204206
REPORT_ISSUES_URL: env.REPORT_ISSUES_URL,
207+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: toNumber(env.PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS, 0),
208+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: toNumber(env.PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS, 0),
205209
};

0 commit comments

Comments
 (0)