mirror of
https://github.com/livekit/livekit.git
synced 2026-11-12 22:58:01 +00:00
* rtc: disable transceiver reuse for all WebKit clients, incl. iOS #4474 turned off transceiver reuse on Safari because of an insertable streams issue that can corrupt E2EE tracks. The check matched only a browser name of exactly "Safari". The server's user agent parser reports iPhone Safari as "Mobile Safari", WKWebView as "Mobile Safari UI/WKWebView", and other iOS browsers as "Chrome Mobile iOS", "Firefox iOS", "Edge Mobile" or an app name. All of these run on WebKit, but they kept transceiver reuse. Match any browser name that contains "safari", and any JS SDK client on iOS. React Native, Swift and Flutter do not match. isSafari() and audio RED are not changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: reuse isSafari() in isWebKit() Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rtc: use isWebKit() for audio RED support too Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: lukasIO <mail@lukasseiler.de>
171 lines
4.7 KiB
Go
171 lines
4.7 KiB
Go
// Copyright 2023 LiveKit, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package rtc
|
|
|
|
import (
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/livekit/protocol/livekit"
|
|
)
|
|
|
|
type ClientInfo struct {
|
|
*livekit.ClientInfo
|
|
}
|
|
|
|
func (c ClientInfo) isFirefox() bool {
|
|
return c.ClientInfo != nil && (strings.EqualFold(c.ClientInfo.Browser, "firefox") || strings.EqualFold(c.ClientInfo.Browser, "firefox mobile"))
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (c ClientInfo) isLinux() bool {
|
|
return c.ClientInfo != nil && strings.EqualFold(c.ClientInfo.Os, "linux")
|
|
}
|
|
|
|
func (c ClientInfo) isAndroid() bool {
|
|
return c.ClientInfo != nil && strings.EqualFold(c.ClientInfo.Os, "android")
|
|
}
|
|
|
|
func (c ClientInfo) isOBS() bool {
|
|
return c.ClientInfo != nil && strings.Contains(c.ClientInfo.Browser, "OBS")
|
|
}
|
|
|
|
func (c ClientInfo) SupportsAudioRED() bool {
|
|
return !c.isFirefox() && !c.isWebKit()
|
|
}
|
|
|
|
func (c ClientInfo) SupportsPrflxOverRelay() bool {
|
|
return !c.isFirefox()
|
|
}
|
|
|
|
// GoSDK(pion) relies on rtp packets to fire ontrack event, browsers and native (libwebrtc) rely on sdp
|
|
func (c ClientInfo) FireTrackByRTPPacket() bool {
|
|
return c.isGo()
|
|
}
|
|
|
|
func (c ClientInfo) SupportsCodecChange() bool {
|
|
return c.ClientInfo != nil && c.ClientInfo.Sdk != livekit.ClientInfo_GO && c.ClientInfo.Sdk != livekit.ClientInfo_UNKNOWN
|
|
}
|
|
|
|
func (c ClientInfo) CanHandleReconnectResponse() bool {
|
|
if c.Sdk == livekit.ClientInfo_JS {
|
|
// JS handles Reconnect explicitly in 1.6.3, prior to 1.6.4 it could not handle unknown responses
|
|
if c.compareVersion("1.6.3") < 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c ClientInfo) SupportsICETCP() bool {
|
|
if c.ClientInfo == nil {
|
|
return false
|
|
}
|
|
if c.ClientInfo.Sdk == livekit.ClientInfo_GO {
|
|
// Go does not support active TCP
|
|
return false
|
|
}
|
|
if c.ClientInfo.Sdk == livekit.ClientInfo_SWIFT {
|
|
// ICE/TCP added in 1.0.5
|
|
return c.compareVersion("1.0.5") >= 0
|
|
}
|
|
// most SDKs support ICE/TCP
|
|
return true
|
|
}
|
|
|
|
func (c ClientInfo) SupportsChangeRTPSenderEncodingActive() bool {
|
|
return !c.isFirefox()
|
|
}
|
|
|
|
func (c ClientInfo) ComplyWithCodecOrderInSDPAnswer() bool {
|
|
return (!c.isLinux() && !c.isAndroid()) || !c.isFirefox()
|
|
}
|
|
|
|
// Rust SDK can't decode unknown signal message (TrackSubscribed and ErrorResponse)
|
|
func (c ClientInfo) SupportsTrackSubscribedEvent() bool {
|
|
return c.ClientInfo.GetSdk() != livekit.ClientInfo_RUST || c.ClientInfo.GetProtocol() >= 10
|
|
}
|
|
|
|
func (c ClientInfo) SupportsRequestResponse() bool {
|
|
return c.SupportsTrackSubscribedEvent()
|
|
}
|
|
|
|
func (c ClientInfo) SupportsSctpZeroChecksum() bool {
|
|
return c.ClientInfo.GetSdk() != livekit.ClientInfo_UNKNOWN &&
|
|
(!c.isGo() || c.compareVersion("2.4.0") >= 0)
|
|
}
|
|
|
|
func (c ClientInfo) SupportsAnswerDTLSRoleServer() bool {
|
|
sdk := c.ClientInfo.GetSdk()
|
|
return sdk != livekit.ClientInfo_UNKNOWN && sdk != livekit.ClientInfo_ESP32
|
|
}
|
|
|
|
func (c ClientInfo) SupportsTransceiverReuse() bool {
|
|
return !c.isWebKit()
|
|
}
|
|
|
|
func (c ClientInfo) HasCapability(cap livekit.ClientInfo_Capability) bool {
|
|
if c.ClientInfo == nil {
|
|
return false
|
|
}
|
|
return slices.Contains(c.ClientInfo.Capabilities, cap)
|
|
}
|
|
|
|
func (c ClientInfo) SupportsPacketTrailer() bool {
|
|
return c.HasCapability(livekit.ClientInfo_CAP_PACKET_TRAILER)
|
|
}
|
|
|
|
// compareVersion compares a semver against the current client SDK version
|
|
// returning 1 if current version is greater than version
|
|
// 0 if they are the same, and -1 if it's an earlier version
|
|
func (c ClientInfo) compareVersion(version string) int {
|
|
if c.ClientInfo == nil {
|
|
return -1
|
|
}
|
|
parts0 := strings.Split(c.ClientInfo.Version, ".")
|
|
parts1 := strings.Split(version, ".")
|
|
ints0 := make([]int, 3)
|
|
ints1 := make([]int, 3)
|
|
for i := range 3 {
|
|
if len(parts0) > i {
|
|
ints0[i], _ = strconv.Atoi(parts0[i])
|
|
}
|
|
if len(parts1) > i {
|
|
ints1[i], _ = strconv.Atoi(parts1[i])
|
|
}
|
|
if ints0[i] > ints1[i] {
|
|
return 1
|
|
} else if ints0[i] < ints1[i] {
|
|
return -1
|
|
}
|
|
}
|
|
return 0
|
|
}
|