Skip to content

Commit c166d26

Browse files
committed
Merge tag 'v1.14.15' into mconf-build
# Conflicts: # .github/workflows/build-test-and-deploy.yml # play/yarn.lock
2 parents b6527a2 + c0d8b74 commit c166d26

File tree

11 files changed

+341
-392
lines changed

11 files changed

+341
-392
lines changed

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

Lines changed: 21 additions & 390 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
@@ -95,6 +95,8 @@ services:
9595
ENABLE_CHAT_UPLOAD: "$ENABLE_CHAT_UPLOAD"
9696
ENABLE_CHAT_ONLINE_LIST: "$ENABLE_CHAT_ONLINE_LIST"
9797
ENABLE_CHAT_DISCONNECTED_LIST: "$ENABLE_CHAT_DISCONNECTED_LIST"
98+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: "$PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS"
99+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: "$PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS"
98100
labels:
99101
- "traefik.enable=true"
100102
- "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
@@ -147,6 +147,7 @@
147147
"typesafe-i18n": "^5.14.0",
148148
"typescript": "^4.8.4",
149149
"uuid": "^9.0.0",
150+
"webrtc-adapter": "^8.1.1",
150151
"zod": "^3.19.1"
151152
}
152153
}

play/src/common/FrontConfigurationInterface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ export interface FrontConfigurationInterface {
2929
ENABLE_CHAT_UPLOAD: boolean;
3030
FALLBACK_LOCALE: string | undefined;
3131
OPID_WOKA_NAME_POLICY: OpidWokaNamePolicy | undefined;
32+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: number;
33+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: number;
3234
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,44 @@ export function getIceServersConfig(user: UserSimplePeerInterface): RTCIceServer
6464
}
6565
return config;
6666
}
67+
68+
export function getSdpTransform(videoBandwidth = 0) {
69+
return (sdp: string) => {
70+
sdp = updateBandwidthRestriction(sdp, videoBandwidth, "video");
71+
72+
return sdp;
73+
};
74+
}
75+
76+
function updateBandwidthRestriction(sdp: string, bandwidth: integer, mediaType: string): string {
77+
if (bandwidth <= 0) {
78+
return sdp;
79+
}
80+
81+
for (
82+
let targetMediaPos = sdp.indexOf(`m=${mediaType}`);
83+
targetMediaPos !== -1;
84+
targetMediaPos = sdp.indexOf(`m=${mediaType}`, targetMediaPos + 1)
85+
) {
86+
// offer TIAS and AS (in this order)
87+
for (const modifier of ["AS", "TIAS"]) {
88+
const nextMediaPos = sdp.indexOf(`m=`, targetMediaPos + 1);
89+
const newBandwidth = modifier === "TIAS" ? (bandwidth >>> 0) * 1000 : bandwidth;
90+
const nextBWPos = sdp.indexOf(`b=${modifier}:`, targetMediaPos + 1);
91+
92+
let mediaSlice = sdp.slice(targetMediaPos);
93+
const bwFieldAlreadyExists = nextBWPos !== -1 && (nextBWPos < nextMediaPos || nextMediaPos === -1);
94+
if (bwFieldAlreadyExists) {
95+
// delete it
96+
mediaSlice = mediaSlice.replace(new RegExp(`b=${modifier}:.*[\r?\n]`), "");
97+
}
98+
// insert b= after c= line.
99+
mediaSlice = mediaSlice.replace(/c=IN (.*)(\r?\n)/, `c=IN $1$2b=${modifier}:${newBandwidth}$2`);
100+
101+
// update the sdp
102+
sdp = sdp.slice(0, targetMediaPos) + mediaSlice;
103+
}
104+
}
105+
106+
return sdp;
107+
}

play/src/front/Enum/EnvironmentVariable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const CHAT_URL = env.CHAT_URL;
3636
export const ENABLE_CHAT_UPLOAD = env.ENABLE_CHAT_UPLOAD;
3737
export const FALLBACK_LOCALE = env.FALLBACK_LOCALE;
3838
export const OPID_WOKA_NAME_POLICY = env.OPID_WOKA_NAME_POLICY;
39+
export const PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS = env.PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS;
40+
export const PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS = env.PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS;
3941

4042
export const POSITION_DELAY = 200; // Wait 200ms between sending position events
4143
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
@@ -70,6 +70,8 @@ const EnvironmentVariables = z.object({
7070
FALLBACK_LOCALE: z.string().optional(),
7171
CHAT_URL: z.string().url(),
7272
OPID_WOKA_NAME_POLICY: OpidWokaNamePolicy.optional(),
73+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: PositiveIntAsString.optional(),
74+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: PositiveIntAsString.optional(),
7375
});
7476

7577
type EnvironmentVariables = z.infer<typeof EnvironmentVariables>;
@@ -178,4 +180,6 @@ export const FRONT_ENVIRONMENT_VARIABLES: FrontConfigurationInterface = {
178180
ENABLE_CHAT_UPLOAD: toBool(env.ENABLE_CHAT_UPLOAD, true),
179181
FALLBACK_LOCALE: env.FALLBACK_LOCALE,
180182
OPID_WOKA_NAME_POLICY: env.OPID_WOKA_NAME_POLICY,
183+
PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS: toNumber(env.PEER_VIDEO_MAX_BANDWIDTH_KBITS_PS, 0),
184+
PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS: toNumber(env.PEER_SCREENSHARE_MAX_BANDWIDTH_KBITS_PS, 0),
181185
};

0 commit comments

Comments
 (0)