diff --git a/pkg/rtc/clientinfo.go b/pkg/rtc/clientinfo.go index 3dfde07ef..0bad5fdd4 100644 --- a/pkg/rtc/clientinfo.go +++ b/pkg/rtc/clientinfo.go @@ -34,6 +34,13 @@ func (c ClientInfo) isSafari() bool { return c.ClientInfo != nil && strings.EqualFold(c.ClientInfo.Browser, "safari") } +func (c ClientInfo) isWebKit() bool { + if c.isSafari() { + return true + } + return c.ClientInfo != nil && c.ClientInfo.Sdk == livekit.ClientInfo_JS && strings.EqualFold(c.ClientInfo.Os, "ios") +} + func (c ClientInfo) isGo() bool { return c.ClientInfo != nil && c.ClientInfo.Sdk == livekit.ClientInfo_GO } @@ -51,7 +58,7 @@ func (c ClientInfo) isOBS() bool { } func (c ClientInfo) SupportsAudioRED() bool { - return !c.isFirefox() && !c.isSafari() + return !c.isFirefox() && !c.isWebKit() } func (c ClientInfo) SupportsPrflxOverRelay() bool { @@ -121,7 +128,7 @@ func (c ClientInfo) SupportsAnswerDTLSRoleServer() bool { } func (c ClientInfo) SupportsTransceiverReuse() bool { - return !c.isSafari() + return !c.isWebKit() } func (c ClientInfo) HasCapability(cap livekit.ClientInfo_Capability) bool { diff --git a/pkg/rtc/clientinfo_test.go b/pkg/rtc/clientinfo_test.go index 566402430..99055a97b 100644 --- a/pkg/rtc/clientinfo_test.go +++ b/pkg/rtc/clientinfo_test.go @@ -57,3 +57,47 @@ func TestClientInfo_SupportsICETCP(t *testing.T) { require.True(t, c.SupportsICETCP()) }) } + +func TestClientInfo_WebKitFeatures(t *testing.T) { + // Browser / Os values are what the server's user agent parser sets for JS clients + testCases := []struct { + name string + sdk livekit.ClientInfo_SDK + browser string + os string + transceiverReuse bool + audioRED bool + }{ + {"macOS Safari", livekit.ClientInfo_JS, "Safari", "Mac OS X", false, false}, + {"iPadOS Safari (desktop UA)", livekit.ClientInfo_JS, "Safari", "Mac OS X", false, false}, + {"iOS Safari", livekit.ClientInfo_JS, "Mobile Safari", "iOS", false, false}, + {"iOS WKWebView", livekit.ClientInfo_JS, "Mobile Safari UI/WKWebView", "iOS", false, false}, + {"iOS Chrome", livekit.ClientInfo_JS, "Chrome Mobile iOS", "iOS", false, false}, + {"iOS Firefox", livekit.ClientInfo_JS, "Firefox iOS", "iOS", false, false}, + {"iOS Edge", livekit.ClientInfo_JS, "Edge Mobile", "iOS", false, false}, + {"iOS in-app browser", livekit.ClientInfo_JS, "Instagram", "iOS", false, false}, + {"desktop Chrome", livekit.ClientInfo_JS, "Chrome", "Mac OS X", true, true}, + {"desktop Firefox", livekit.ClientInfo_JS, "Firefox", "Windows", true, false}, + {"Android Chrome", livekit.ClientInfo_JS, "Chrome Mobile", "Android", true, true}, + {"React Native iOS", livekit.ClientInfo_REACT_NATIVE, "LiveKitExample", "ios", true, true}, + {"Swift", livekit.ClientInfo_SWIFT, "", "ios", true, true}, + {"Flutter iOS", livekit.ClientInfo_FLUTTER, "Dart", "ios", true, true}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + c := ClientInfo{ + ClientInfo: &livekit.ClientInfo{ + Sdk: tc.sdk, + Browser: tc.browser, + Os: tc.os, + }, + } + require.Equal(t, tc.transceiverReuse, c.SupportsTransceiverReuse()) + require.Equal(t, tc.audioRED, c.SupportsAudioRED()) + }) + } + + require.True(t, ClientInfo{}.SupportsTransceiverReuse()) + require.True(t, ClientInfo{}.SupportsAudioRED()) +}