Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow simulcast together with E2EE for supported Safari versions #1117

Merged
merged 8 commits into from
Apr 25, 2024
Merged
6 changes: 6 additions & 0 deletions .changeset/giant-days-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"livekit-client": patch
---

Allow simulcast together with E2EE for supported Safari versions
Also fixes the simulcast behaviour for iOS Chrome prior to 17.2
7 changes: 3 additions & 4 deletions src/room/participant/LocalParticipant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ import {
import type { DataPublishOptions } from '../types';
import {
Future,
isE2EESimulcastSupported,
isFireFox,
isSVCCodec,
isSafari,
isSafari17,
isWeb,
supportsAV1,
Expand Down Expand Up @@ -621,10 +621,9 @@ export default class LocalParticipant extends Participant {
...options,
};

// disable simulcast if e2ee is set on safari
if (isSafari() && this.roomOptions.e2ee) {
if (!isE2EESimulcastSupported() && this.roomOptions.e2ee) {
this.log.info(
`End-to-end encryption is set up, simulcast publishing will be disabled on Safari`,
`End-to-end encryption is set up, simulcast publishing will be disabled on Safari versions and iOS browsers running iOS < v17.2`,
{
...this.logContext,
},
Expand Down
30 changes: 29 additions & 1 deletion src/room/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,35 @@ export function isSafari17(): boolean {

export function isMobile(): boolean {
if (!isWeb()) return false;
return /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent);

return (
// @ts-expect-error `userAgentData` is not yet part of typescript
navigator.userAgentData?.mobile ??
/Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent)
);
}

export function isE2EESimulcastSupported() {
const browser = getBrowser();
const supportedSafariVersion = '17.2'; // see https://bugs.webkit.org/show_bug.cgi?id=257803
if (browser) {
if (browser.name !== 'Safari' && browser.os !== 'iOS') {
return true;
} else if (
browser.os === 'iOS' &&
browser.osVersion &&
compareVersions(supportedSafariVersion, browser.osVersion) >= 0
) {
return true;
} else if (
browser.name === 'Safari' &&
compareVersions(supportedSafariVersion, browser.version) >= 0
) {
return true;
} else {
return false;
}
}
}

export function isWeb(): boolean {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/browserParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ describe('browser parser', () => {
expect(details?.name).toBe('Safari');
expect(details?.version).toBe('16.3');
expect(details?.os).toBe('macOS');
expect(details?.osVersion).toBe('10.15.7');
});
it('parses Safari iOS correctly', () => {
const details = getBrowser(iOSSafariUA, true);
expect(details?.name).toBe('Safari');
expect(details?.version).toBe('16.5');
expect(details?.os).toBe('iOS');
expect(details?.osVersion).toBe('16.5.1');
});
it('parses Firefox correctly', () => {
const details = getBrowser(firefoxUA, true);
Expand All @@ -46,6 +48,7 @@ describe('browser parser', () => {
expect(details?.name).toBe('Firefox');
expect(details?.version).toBe('115.0');
expect(details?.os).toBe('iOS');
expect(details?.osVersion).toBe('13.4.1');
});
it('parses Chrome correctly', () => {
const details = getBrowser(chromeUA, true);
Expand All @@ -57,6 +60,7 @@ describe('browser parser', () => {
expect(details?.name).toBe('Chrome');
expect(details?.version).toBe('115.0.5790.130');
expect(details?.os).toBe('iOS');
expect(details?.osVersion).toBe('16.5');
});
it('detects brave as chromium based', () => {
const details = getBrowser(braveUA, true);
Expand Down
12 changes: 11 additions & 1 deletion src/utils/browserParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ export type BrowserDetails = {
name: DetectableBrowser;
version: string;
os?: DetectableOS;
osVersion?: string;
};

let browserDetails: BrowserDetails | undefined;

/**
* @internal
*/
export function getBrowser(userAgent?: string, force = true) {
export function getBrowser(userAgent?: string, force = true): BrowserDetails | undefined {
if (typeof userAgent === 'undefined' && typeof navigator === 'undefined') {
return;
}
Expand All @@ -37,6 +38,7 @@ const browsersList = [
name: 'Firefox',
version: getMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua),
os: ua.toLowerCase().includes('fxios') ? 'iOS' : undefined,
osVersion: getOSVersion(ua),
};
return browser;
},
Expand All @@ -48,6 +50,7 @@ const browsersList = [
name: 'Chrome',
version: getMatch(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua),
os: ua.toLowerCase().includes('crios') ? 'iOS' : undefined,
osVersion: getOSVersion(ua),
};

return browser;
Expand All @@ -61,6 +64,7 @@ const browsersList = [
name: 'Safari',
version: getMatch(commonVersionIdentifier, ua),
os: ua.includes('mobile/') ? 'iOS' : 'macOS',
osVersion: getOSVersion(ua),
};

return browser;
Expand All @@ -72,3 +76,9 @@ function getMatch(exp: RegExp, ua: string, id = 1) {
const match = ua.match(exp);
return (match && match.length >= id && match[id]) || '';
}

function getOSVersion(ua: string) {
return ua.includes('mac os')
? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.')
: undefined;
}
Loading