diff --git a/CHANGELOG b/CHANGELOG index 87f096580..cac84e150 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,49 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.0] - 2023-10-15 + +### Added +- Add option to issue full reconnect on data channel error. (#2026) +- Support non-SVC AV1 track publishing (#2030) +- Add batch i/o to improve throughput (#2033) +- Integrate updated TWCC responder (#2038) +- Allow RoomService.SendData to use participant identities (#2051 #2058) +- Support for Participant Egress (#2070) +- Add max playout delay config (#2089) +- Enable SVC codecs by default (#2109) +- Add SyncStreams flag to Room, protocol 10 (#2110) + +### Fixed +- Unlock pendingTracksLock when mid is empty (#1994) +- Do not offer H.264 high profile in subscriber offer, fixes negotiation failures (#1997) +- Prevent erroneous stream pause. (#2008) +- Handle duplicate padding packet in the up stream. (#2012) +- Do not process packets not processed by RTPStats. (#2015) +- Adjust extended sequence number to account for dropped packets (#2017) +- Do not force reconnect on resume if there is a pending track (#2081) +- Fix out-of-range access. (#2082) +- Start key frame requester on start. (#2111) +- Handle RED extended sequence number. (#2123) +- Handle playoutDelay for Firefox (#2135) +- Fix ICE connection fallback (#2144) + +### Changed +- Drop padding only packets on publisher side. (#1990) +- Do not generate a stream key for URL pull ingress (#1993) +- RTPStats optimizations and improvements (#1999 #2000 #2001 #2002 #2003 #2004 #2078) +- Remove sender report warp logs. (#2007) +- Don't create new slice when return broadcast downtracks (#2013) +- Disconnect participant when signal proxy is closed (#2024) +- Use random NodeID instead of MAC based (#2029) +- Split RTPStats into receiver and sender. (#2055) +- Reduce packet meta data cache (#2073 #2078) +- Reduce ghost participant disconnect timeout (#2077) +- Per-session TURN credentials (#2080) +- Use marshal + unmarshal to ensure unmarshallable fields are not copied. (#2092) +- Allow playout delay even when sync stream isn't used. (#2133) +- Increase accuracy of delay since last sender report. (#2136) + ## [1.4.5] - 2023-08-22 ### Added diff --git a/Dockerfile b/Dockerfile index f35b65ada..3258a00fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.20-alpine as builder +FROM golang:1.21-alpine as builder ARG TARGETPLATFORM ARG TARGETARCH diff --git a/go.mod b/go.mod index 39c2d94cc..0f430e9f5 100644 --- a/go.mod +++ b/go.mod @@ -14,11 +14,11 @@ require ( github.com/google/wire v0.5.0 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/golang-lru/v2 v2.0.6 + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 - github.com/livekit/mediatransportutil v0.0.0-20231003064835-a51e0ca1b1fd - github.com/livekit/protocol v1.7.3-0.20230928065809-281e00a4a67d + github.com/livekit/mediatransportutil v0.0.0-20231005043905-c137afffe71c + github.com/livekit/protocol v1.8.0 github.com/livekit/psrpc v0.3.3 github.com/mackerelio/go-osstat v0.2.4 github.com/magefile/mage v1.15.0 @@ -36,9 +36,9 @@ require ( github.com/pion/turn/v2 v2.1.4 github.com/pion/webrtc/v3 v3.2.20 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.16.0 - github.com/redis/go-redis/v9 v9.1.0 - github.com/rs/cors v1.10.0 + github.com/prometheus/client_golang v1.17.0 + github.com/redis/go-redis/v9 v9.2.1 + github.com/rs/cors v1.10.1 github.com/stretchr/testify v1.8.4 github.com/thoas/go-funk v0.9.3 github.com/twitchtv/twirp v8.1.3+incompatible @@ -46,8 +46,8 @@ require ( github.com/urfave/cli/v2 v2.25.7 github.com/urfave/negroni/v3 v3.0.0 go.uber.org/atomic v1.11.0 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/sync v0.3.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/sync v0.4.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -70,15 +70,15 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lithammer/shortuuid/v4 v4.0.0 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mdlayher/netlink v1.7.1 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/nats-io/nats.go v1.28.0 // indirect - github.com/nats-io/nkeys v0.4.4 // indirect + github.com/nats-io/nats.go v1.30.2 // indirect + github.com/nats-io/nkeys v0.4.5 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pion/datachannel v1.5.5 // indirect github.com/pion/logging v0.2.2 // indirect @@ -87,21 +87,21 @@ require ( github.com/pion/srtp/v2 v2.0.17 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.25.0 // indirect - golang.org/x/crypto v0.13.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect - google.golang.org/grpc v1.58.0 // indirect + golang.org/x/tools v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect + google.golang.org/grpc v1.58.3 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index c25e8fc89..c2a682ef9 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= -github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= @@ -47,7 +47,6 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -90,8 +89,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= -github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -109,8 +108,8 @@ github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa79 github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -125,10 +124,10 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= -github.com/livekit/mediatransportutil v0.0.0-20231003064835-a51e0ca1b1fd h1:Dqfr8ehKCukEYE9zr0C035VAOmMq8lntE4iD/DE7504= -github.com/livekit/mediatransportutil v0.0.0-20231003064835-a51e0ca1b1fd/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= -github.com/livekit/protocol v1.7.3-0.20230928065809-281e00a4a67d h1:JLc/seGGKdnv0JUDCkMprJYzud2E8ahQ3QZgP/Imb14= -github.com/livekit/protocol v1.7.3-0.20230928065809-281e00a4a67d/go.mod h1:zbh0QPUcLGOeZeIO/VeigwWWbudz4Lv+Px94FnVfQH0= +github.com/livekit/mediatransportutil v0.0.0-20231005043905-c137afffe71c h1:eTghhsCfx2ltyzArXZ7wiNoFFzbfLXJ4uI/IsLXFZQc= +github.com/livekit/mediatransportutil v0.0.0-20231005043905-c137afffe71c/go.mod h1:+WIOYwiBMive5T81V8B2wdAc2zQNRjNQiJIcPxMTILY= +github.com/livekit/protocol v1.8.0 h1:0z2eRmEXFFXiJ7WPAxRLMNCyUu55w41iikbbeT8dvlQ= +github.com/livekit/protocol v1.8.0/go.mod h1:zbh0QPUcLGOeZeIO/VeigwWWbudz4Lv+Px94FnVfQH0= github.com/livekit/psrpc v0.3.3 h1:+lltbuN39IdaynXhLLxRShgYqYsRMWeeXKzv60oqyWo= github.com/livekit/psrpc v0.3.3/go.mod h1:n6JntEg+zT6Ji8InoyTpV7wusPNwGqqtxmHlkNhDN0U= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -165,10 +164,10 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= github.com/nats-io/nats-server/v2 v2.9.8 h1:jgxZsv+A3Reb3MgwxaINcNq/za8xZInKhDg9Q0cGN1o= -github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c= -github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= -github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= -github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= +github.com/nats-io/nats.go v1.30.2 h1:aloM0TGpPorZKQhbAkdCzYDj+ZmsJDyeo3Gkbr72NuY= +github.com/nats-io/nats.go v1.30.2/go.mod h1:dcfhUgmQNN4GJEfIb2f9R7Fow+gzBF4emzDHrVBd5qM= +github.com/nats-io/nkeys v0.4.5 h1:Zdz2BUlFm4fJlierwvGK+yl20IAKUm7eV6AAZXEhkPk= +github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -230,20 +229,20 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= -github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= -github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= @@ -294,15 +293,15 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -330,8 +329,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -339,8 +338,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -381,8 +380,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -411,16 +410,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a h1:a2MQQVoTo96JC9PMGtGBymLp7+/RzpFc2yX/9WfFg1c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 2d9d21cb7..38fac3f58 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -58,6 +58,9 @@ const ( disconnectCleanupDuration = 5 * time.Second migrationWaitDuration = 3 * time.Second + + PingIntervalSeconds = 5 + PingTimeoutSeconds = 15 ) type pendingTrackInfo struct { @@ -1095,7 +1098,7 @@ func (p *ParticipantImpl) UpdateMediaRTT(rtt uint32) { } func (p *ParticipantImpl) setupTransportManager() error { - tm, err := NewTransportManager(TransportManagerParams{ + params := TransportManagerParams{ Identity: p.params.Identity, SID: p.params.SID, // primary connection does not change, canSubscribe can change if permission was updated @@ -1114,9 +1117,15 @@ func (p *ParticipantImpl) setupTransportManager() error { TCPFallbackRTTThreshold: p.params.TCPFallbackRTTThreshold, AllowUDPUnstableFallback: p.params.AllowUDPUnstableFallback, TURNSEnabled: p.params.TURNSEnabled, - AllowPlayoutDelay: p.params.PlayoutDelay.GetEnabled() && p.SupportsSyncStreamID(), + AllowPlayoutDelay: p.params.PlayoutDelay.GetEnabled(), Logger: p.params.Logger.WithComponent(sutils.ComponentTransport), - }) + } + if p.params.SyncStreams && p.params.PlayoutDelay.GetEnabled() && p.params.ClientInfo.isFirefox() { + // we will disable playout delay for Firefox if the user is expecting + // the streams to be synced. Firefox doesn't support SyncStreams + params.AllowPlayoutDelay = false + } + tm, err := NewTransportManager(params) if err != nil { return err } @@ -2291,7 +2300,7 @@ func (p *ParticipantImpl) SendDataPacket(dp *livekit.DataPacket, data []byte) er err := p.TransportManager.SendDataPacket(dp, data) if err != nil { - if (err == sctp.ErrStreamClosed || err == io.ErrClosedPipe) && p.params.ReconnectOnDataChannelError { + if (errors.Is(err, sctp.ErrStreamClosed) || errors.Is(err, io.ErrClosedPipe)) && p.params.ReconnectOnDataChannelError { p.params.Logger.Infow("issuing full reconnect on data channel error") p.IssueFullReconnect(types.ParticipantCloseReasonDataChannelError) } diff --git a/pkg/rtc/participant_sdp.go b/pkg/rtc/participant_sdp.go index 2d24f15e3..9bacce907 100644 --- a/pkg/rtc/participant_sdp.go +++ b/pkg/rtc/participant_sdp.go @@ -252,7 +252,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript opusPT, err := parsed.GetPayloadTypeForCodec(sdp.Codec{Name: "opus"}) if err != nil { - p.pubLogger.Infow("failed to get opus payload type", "error", err, "trakcID", ti.Sid) + p.pubLogger.Infow("failed to get opus payload type", "error", err, "trackID", ti.Sid) continue } diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index 544713158..e797af6ba 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -852,8 +852,8 @@ func (r *Room) createJoinResponseLocked(participant types.LocalParticipant, iceS SubscriberPrimary: participant.SubscriberAsPrimary(), ClientConfiguration: participant.GetClientConfiguration(), // sane defaults for ping interval & timeout - PingInterval: 10, - PingTimeout: 20, + PingInterval: PingIntervalSeconds, + PingTimeout: PingTimeoutSeconds, ServerInfo: r.serverInfo, ServerVersion: r.serverInfo.Version, ServerRegion: r.serverInfo.Region, diff --git a/pkg/rtc/subscriptionmanager.go b/pkg/rtc/subscriptionmanager.go index 8864c99b4..602e786fd 100644 --- a/pkg/rtc/subscriptionmanager.go +++ b/pkg/rtc/subscriptionmanager.go @@ -36,9 +36,9 @@ var ( reconcileInterval = 3 * time.Second // amount of time to give up if a track or publisher isn't found // ensuring this is longer than iceFailedTimeout so we are certain the participant won't return - notFoundTimeout = iceFailedTimeout + notFoundTimeout = time.Minute // amount of time to try otherwise before flagging subscription as failed - subscriptionTimeout = iceFailedTimeout + subscriptionTimeout = iceFailedTimeoutTotal trackRemoveGracePeriod = time.Second maxUnsubscribeWait = time.Second ) diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index 47338e812..a24826c11 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -59,9 +59,10 @@ const ( negotiationFailedTimeout = 15 * time.Second dtlsRetransmissionInterval = 100 * time.Millisecond - iceDisconnectedTimeout = 10 * time.Second // compatible for ice-lite with firefox client - iceFailedTimeout = 5 * time.Second // time between disconnected and failed - iceKeepaliveInterval = 2 * time.Second // pion's default + iceDisconnectedTimeout = 10 * time.Second // compatible for ice-lite with firefox client + iceFailedTimeout = 5 * time.Second // time between disconnected and failed + iceFailedTimeoutTotal = iceFailedTimeout + iceDisconnectedTimeout // total time between connecting and failure + iceKeepaliveInterval = 2 * time.Second // pion's default minTcpICEConnectTimeout = 5 * time.Second maxTcpICEConnectTimeout = 12 * time.Second // js-sdk has a default 15s timeout for first connection, let server detect failure earlier before that diff --git a/pkg/rtc/transportmanager.go b/pkg/rtc/transportmanager.go index 6e5ef78e3..a36291f19 100644 --- a/pkg/rtc/transportmanager.go +++ b/pkg/rtc/transportmanager.go @@ -607,7 +607,7 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) { lastSignalSince := time.Since(t.lastSignalAt) signalValid := t.signalSourceValid.Load() - if lastSignalSince > iceFailedTimeout || !signalValid { + if !t.hasRecentSignalLocked() || !signalValid { // the failed might cause by network interrupt because signal closed or we have not seen any signal in the time window, // so don't switch to next candidate type t.params.Logger.Infow("ignoring prefer candidate check by ICE failure because signal connection interrupted", @@ -754,7 +754,7 @@ func (t *TransportManager) onMediaLossUpdate(loss uint8) { if loss >= uint8(255*udpLossFracUnstable/100) { t.udpLossUnstableCount |= 1 if bits.OnesCount32(t.udpLossUnstableCount) >= udpLossUnstableCountThreshold { - if t.udpRTT > 0 && t.signalingRTT < uint32(float32(t.udpRTT)*1.3) && int(t.signalingRTT) < t.params.TCPFallbackRTTThreshold && time.Since(t.lastSignalAt) < iceFailedTimeout { + if t.udpRTT > 0 && t.signalingRTT < uint32(float32(t.udpRTT)*1.3) && int(t.signalingRTT) < t.params.TCPFallbackRTTThreshold && t.hasRecentSignalLocked() { t.udpLossUnstableCount = 0 t.lock.Unlock() @@ -826,3 +826,7 @@ func (t *TransportManager) SetSubscriberAllowPause(allowPause bool) { func (t *TransportManager) SetSubscriberChannelCapacity(channelCapacity int64) { t.subscriber.SetChannelCapacityOfStreamAllocator(channelCapacity) } + +func (t *TransportManager) hasRecentSignalLocked() bool { + return time.Since(t.lastSignalAt) < PingTimeoutSeconds*time.Second +} diff --git a/pkg/service/egress.go b/pkg/service/egress.go index 8675d0b80..36f379b3f 100644 --- a/pkg/service/egress.go +++ b/pkg/service/egress.go @@ -295,6 +295,10 @@ func (s *EgressService) UpdateStream(ctx context.Context, req *livekit.UpdateStr return info, nil } +func (s *EgressService) UpdateOutputs(ctx context.Context, req *livekit.UpdateOutputsRequest) (*livekit.EgressInfo, error) { + return nil, twirp.NewError(twirp.Unimplemented, "Update Outputs unimplemented") +} + func (s *EgressService) ListEgress(ctx context.Context, req *livekit.ListEgressRequest) (*livekit.ListEgressResponse, error) { if req.RoomName != "" { AppendLogFields(ctx, "room", req.RoomName) diff --git a/pkg/sfu/buffer/rtpstats_base.go b/pkg/sfu/buffer/rtpstats_base.go index 7438c9c72..6cf0fe6eb 100644 --- a/pkg/sfu/buffer/rtpstats_base.go +++ b/pkg/sfu/buffer/rtpstats_base.go @@ -531,8 +531,13 @@ func (r *rtpStatsBase) deltaInfo(snapshotID uint32, extStartSN uint64, extHighes packetsExpected := now.extStartSN - then.extStartSN if packetsExpected > cNumSequenceNumbers { r.logger.Errorw( - "too many packets expected in delta", - fmt.Errorf("start: %d, end: %d, expected: %d", then.extStartSN, now.extStartSN, packetsExpected), + "too many packets expected in delta", nil, + "startSN", then.extStartSN, + "endSN", now.extStartSN, + "packetsExpected", packetsExpected, + "startTime", startTime, + "endTime", endTime, + "duration", endTime.Sub(startTime), ) return nil } diff --git a/pkg/sfu/buffer/rtpstats_receiver.go b/pkg/sfu/buffer/rtpstats_receiver.go index 0bf9bb49b..10709cf50 100644 --- a/pkg/sfu/buffer/rtpstats_receiver.go +++ b/pkg/sfu/buffer/rtpstats_receiver.go @@ -110,7 +110,7 @@ func (r *RTPStatsReceiver) Update( r.snapshots[i] = r.initSnapshot(r.startTime, r.sequenceNumber.GetExtendedStart()) } - r.logger.Debugw( + r.logger.Infow( "rtp receiver stream start", "startTime", r.startTime.String(), "firstTime", r.firstTime.String(), @@ -150,6 +150,9 @@ func (r *RTPStatsReceiver) Update( return } } + if -gapSN >= cNumSequenceNumbers { + r.logger.Warnw("large sequence number gap negative", nil, "prev", resSN.PreExtendedHighest, "curr", resSN.ExtendedVal, "gap", gapSN) + } if gapSN != 0 { r.packetsOutOfOrder++ @@ -361,9 +364,8 @@ func (r *RTPStatsReceiver) GetRtcpReceptionReport(ssrc uint32, proxyFracLost uin if r.srNewest != nil { lastSR = uint32(r.srNewest.NTPTimestamp >> 16) if !r.srNewest.At.IsZero() { - delayMS := uint32(time.Since(r.srNewest.At).Milliseconds()) - dlsr = (delayMS / 1e3) << 16 - dlsr |= (delayMS % 1e3) * 65536 / 1000 + delayUS := time.Since(r.srNewest.At).Microseconds() + dlsr = uint32(delayUS * 65536 / 1e6) } } diff --git a/pkg/sfu/buffer/rtpstats_sender.go b/pkg/sfu/buffer/rtpstats_sender.go index 22392ec8a..62538bdf7 100644 --- a/pkg/sfu/buffer/rtpstats_sender.go +++ b/pkg/sfu/buffer/rtpstats_sender.go @@ -56,6 +56,7 @@ type intervalStats struct { packetsLost uint64 packetsOutOfOrder uint64 frames uint32 + packetsNotFound uint64 } func (is *intervalStats) aggregate(other *intervalStats) { @@ -72,6 +73,26 @@ func (is *intervalStats) aggregate(other *intervalStats) { is.packetsLost += other.packetsLost is.packetsOutOfOrder += other.packetsOutOfOrder is.frames += other.frames + is.packetsNotFound += other.packetsNotFound +} + +func (is *intervalStats) ToString() string { + if is == nil { + return "-" + } + + return fmt.Sprintf("p: %d, b: %d, hb: %d, pp: %d, bp: %d, hbp: %d, pl: %d, pooo: %d, f: %d, pnf: %d", + is.packets, + is.bytes, + is.headerBytes, + is.packetsPadding, + is.bytesPadding, + is.headerBytesPadding, + is.packetsLost, + is.packetsOutOfOrder, + is.frames, + is.packetsNotFound, + ) } // ------------------------------------------------------------------- @@ -243,7 +264,7 @@ func (r *RTPStatsSender) Update( r.senderSnapshots[i] = r.initSenderSnapshot(r.startTime, r.extStartSN) } - r.logger.Debugw( + r.logger.Infow( "rtp sender stream start", "startTime", r.startTime.String(), "firstTime", r.firstTime.String(), @@ -260,6 +281,9 @@ func (r *RTPStatsSender) Update( // do not start on a padding only packet return } + if -gapSN >= cNumSequenceNumbers { + r.logger.Warnw("large sequence number gap negative", nil, "prev", r.extHighestSN, "curr", extSequenceNumber, "gap", gapSN) + } if extSequenceNumber < r.extStartSN { r.packetsLost += r.extStartSN - extSequenceNumber @@ -440,7 +464,7 @@ func (r *RTPStatsSender) UpdateFromReceiverReport(rr rtcp.ReceptionReport) (rtt } } - extLastRRSN := r.extHighestSNFromRR + (r.extStartSN & 0xFFFF_FFFF_FFFF_0000) + extReceivedRRSN := r.extHighestSNFromRR + (r.extStartSN & 0xFFFF_FFFF_FFFF_0000) for i := uint32(0); i < r.nextSenderSnapshotID-cFirstSnapshotID; i++ { s := &r.senderSnapshots[i] if isRttChanged && rtt > s.maxRtt { @@ -452,10 +476,28 @@ func (r *RTPStatsSender) UpdateFromReceiverReport(rr rtcp.ReceptionReport) (rtt } // on every RR, calculate delta since last RR using packet metadata cache - is := r.getIntervalStats(s.extLastRRSN+1, extLastRRSN+1, r.extHighestSN) + is := r.getIntervalStats(s.extLastRRSN+1, extReceivedRRSN+1, r.extHighestSN) eis := &s.intervalStats eis.aggregate(&is) - s.extLastRRSN = extLastRRSN + if is.packetsNotFound != 0 { + r.logger.Warnw( + "potential sequence number de-sync", nil, + "lastRRTime", r.lastRRTime, + "lastRR", r.lastRR, + "sinceLastRR", time.Since(r.lastRRTime), + "receivedRR", rr, + "extStartSN", r.extStartSN, + "extHighestSN", r.extHighestSN, + "extLastRRSN", s.extLastRRSN, + "extReceivedRRSN", extReceivedRRSN, + "packetsInInterval", extReceivedRRSN-s.extLastRRSN, + "intervalStats", is.ToString(), + "aggregateIntervalStats", eis.ToString(), + "extHighestSNFromRR", r.extHighestSNFromRR, + "packetsLostFromRR", r.packetsLostFromRR, + ) + } + s.extLastRRSN = extReceivedRRSN } r.lastRRTime = time.Now() @@ -600,8 +642,13 @@ func (r *RTPStatsSender) DeltaInfoSender(senderSnapshotID uint32) *RTPDeltaInfo packetsExpected := uint32(now.extStartSN - then.extStartSN) if packetsExpected > cNumSequenceNumbers { r.logger.Warnw( - "too many packets expected in delta (sender)", - fmt.Errorf("start: %d, end: %d, expected: %d", then.extStartSN, now.extStartSN, packetsExpected), + "too many packets expected in delta (sender)", nil, + "startSN", then.extStartSN, + "endSN", now.extStartSN, + "packetsExpected", packetsExpected, + "startTime", startTime, + "endTime", endTime, + "duration", endTime.Sub(startTime), ) return nil } @@ -800,12 +847,15 @@ func (r *RTPStatsSender) isSnInfoLost(esn uint64, ehsn uint64) bool { return r.snInfos[slot].pktSize == 0 } -func (r *RTPStatsSender) getIntervalStats(extStartInclusive uint64, extEndExclusive uint64, ehsn uint64) (intervalStats intervalStats) { - packetsNotFound := uint32(0) +func (r *RTPStatsSender) getIntervalStats( + extStartInclusive uint64, + extEndExclusive uint64, + ehsn uint64, +) (intervalStats intervalStats) { processESN := func(esn uint64, ehsn uint64) { slot := r.getSnInfoOutOfOrderSlot(esn, ehsn) if slot < 0 { - packetsNotFound++ + intervalStats.packetsNotFound++ return } @@ -836,16 +886,6 @@ func (r *RTPStatsSender) getIntervalStats(extStartInclusive uint64, extEndExclus for esn := extStartInclusive; esn != extEndExclusive; esn++ { processESN(esn, ehsn) } - - if packetsNotFound != 0 { - r.logger.Errorw( - "could not find some packets", nil, - "start", extStartInclusive, - "end", extEndExclusive, - "count", packetsNotFound, - "highestSN", ehsn, - ) - } return } diff --git a/pkg/sfu/forwarder.go b/pkg/sfu/forwarder.go index 6324c3ecb..503d13a7f 100644 --- a/pkg/sfu/forwarder.go +++ b/pkg/sfu/forwarder.go @@ -1769,7 +1769,7 @@ func (f *Forwarder) maybeStart() { f.rtpMunger.SetLastSnTs(extPkt) f.extFirstTS = uint64(timestamp) - f.logger.Debugw( + f.logger.Infow( "starting with dummy forwarding", "sequenceNumber", extPkt.Packet.SequenceNumber, "timestamp", extPkt.Packet.Timestamp, diff --git a/pkg/sfu/forwarder_test.go b/pkg/sfu/forwarder_test.go index f621fd587..5e58e3e41 100644 --- a/pkg/sfu/forwarder_test.go +++ b/pkg/sfu/forwarder_test.go @@ -1237,11 +1237,23 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { require.Equal(t, expectedTP, *actualTP) // add a missing sequence number to the cache - f.rtpMunger.snRangeMap.ExcludeRange(23332, 23333) + err = f.rtpMunger.snRangeMap.ExcludeRange(23334, 23335) + require.NoError(t, err) + + params = &testutils.TestExtPacketParams{ + SequenceNumber: 23336, + Timestamp: 0xabcdef, + SSRC: 0x12345678, + PayloadSize: 20, + } + extPkt, _ = testutils.GetTestExtPacket(params) + + _, err = f.GetTranslationParams(extPkt, 0) + require.NoError(t, err) // out-of-order packet should get offset from cache params = &testutils.TestExtPacketParams{ - SequenceNumber: 23331, + SequenceNumber: 23335, Timestamp: 0xabcdef, SSRC: 0x12345678, PayloadSize: 20, @@ -1251,7 +1263,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { expectedTP = TranslationParams{ rtp: &TranslationParamsRTP{ snOrdering: SequenceNumberOrderingOutOfOrder, - extSequenceNumber: 23331, + extSequenceNumber: 23334, extTimestamp: 0xabcdef, }, } @@ -1261,7 +1273,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { // padding only packet in order should be dropped params = &testutils.TestExtPacketParams{ - SequenceNumber: 23334, + SequenceNumber: 23337, Timestamp: 0xabcdef, SSRC: 0x12345678, } @@ -1276,7 +1288,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { // in order packet should be forwarded params = &testutils.TestExtPacketParams{ - SequenceNumber: 23335, + SequenceNumber: 23338, Timestamp: 0xabcdef, SSRC: 0x12345678, PayloadSize: 20, @@ -1286,7 +1298,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { expectedTP = TranslationParams{ rtp: &TranslationParamsRTP{ snOrdering: SequenceNumberOrderingContiguous, - extSequenceNumber: 23333, + extSequenceNumber: 23336, extTimestamp: 0xabcdef, }, } @@ -1296,7 +1308,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { // padding only packet after a gap should not be dropped params = &testutils.TestExtPacketParams{ - SequenceNumber: 23337, + SequenceNumber: 23340, Timestamp: 0xabcdef, SSRC: 0x12345678, } @@ -1305,7 +1317,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { expectedTP = TranslationParams{ rtp: &TranslationParamsRTP{ snOrdering: SequenceNumberOrderingGap, - extSequenceNumber: 23335, + extSequenceNumber: 23338, extTimestamp: 0xabcdef, }, } @@ -1325,7 +1337,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { expectedTP = TranslationParams{ rtp: &TranslationParamsRTP{ snOrdering: SequenceNumberOrderingOutOfOrder, - extSequenceNumber: 23334, + extSequenceNumber: 23335, extTimestamp: 0xabcdef, }, } @@ -1345,7 +1357,7 @@ func TestForwarderGetTranslationParamsAudio(t *testing.T) { expectedTP = TranslationParams{ rtp: &TranslationParamsRTP{ snOrdering: SequenceNumberOrderingContiguous, - extSequenceNumber: 23336, + extSequenceNumber: 23339, extTimestamp: 0xabcdf0, }, } diff --git a/pkg/sfu/rtpmunger.go b/pkg/sfu/rtpmunger.go index c30b8a53b..b162158fe 100644 --- a/pkg/sfu/rtpmunger.go +++ b/pkg/sfu/rtpmunger.go @@ -23,9 +23,7 @@ import ( "github.com/livekit/livekit-server/pkg/sfu/utils" ) -// // RTPMunger -// type SequenceNumberOrdering int const ( @@ -121,6 +119,7 @@ func (r *RTPMunger) SetLastSnTs(extPkt *buffer.ExtPacket) { r.extLastSN = extPkt.ExtSequenceNumber r.extSecondLastSN = r.extLastSN - 1 + r.snRangeMap.ClearAndResetValue(extPkt.ExtSequenceNumber, 0) r.updateSnOffset() r.extLastTS = extPkt.ExtTimestamp @@ -129,7 +128,7 @@ func (r *RTPMunger) SetLastSnTs(extPkt *buffer.ExtPacket) { func (r *RTPMunger) UpdateSnTsOffsets(extPkt *buffer.ExtPacket, snAdjust uint64, tsAdjust uint64) { r.extHighestIncomingSN = extPkt.ExtSequenceNumber - 1 - r.snRangeMap.ClearAndResetValue(extPkt.ExtSequenceNumber - r.extLastSN - snAdjust) + r.snRangeMap.ClearAndResetValue(extPkt.ExtSequenceNumber, extPkt.ExtSequenceNumber-r.extLastSN-snAdjust) r.updateSnOffset() r.tsOffset = extPkt.ExtTimestamp - r.extLastTS - tsAdjust diff --git a/pkg/sfu/rtpmunger_test.go b/pkg/sfu/rtpmunger_test.go index ea8842457..7c202ce1e 100644 --- a/pkg/sfu/rtpmunger_test.go +++ b/pkg/sfu/rtpmunger_test.go @@ -45,8 +45,11 @@ func TestSetLastSnTs(t *testing.T) { require.Equal(t, uint64(23333), r.extLastSN) require.Equal(t, uint64(0xabcdef), r.extLastTS) snOffset, err := r.snRangeMap.GetValue(r.extHighestIncomingSN) + require.Error(t, err) + snOffset, err = r.snRangeMap.GetValue(r.extLastSN) require.NoError(t, err) require.Equal(t, uint64(0), snOffset) + require.Equal(t, uint64(0), r.snOffset) require.Equal(t, uint64(0), r.tsOffset) } @@ -71,9 +74,11 @@ func TestUpdateSnTsOffsets(t *testing.T) { require.Equal(t, uint64(33332), r.extHighestIncomingSN) require.Equal(t, uint64(23333), r.extLastSN) require.Equal(t, uint64(0xabcdef), r.extLastTS) - snOffset, err := r.snRangeMap.GetValue(r.extHighestIncomingSN) - require.NoError(t, err) - require.Equal(t, uint64(9999), snOffset) + _, err := r.snRangeMap.GetValue(r.extHighestIncomingSN) + require.Error(t, err) + _, err = r.snRangeMap.GetValue(r.extLastSN) + require.Error(t, err) + require.Equal(t, uint64(9999), r.snOffset) require.Equal(t, uint64(0xffff_ffff_ffff_ffff), r.tsOffset) } @@ -92,6 +97,8 @@ func TestPacketDropped(t *testing.T) { require.Equal(t, uint64(23333), r.extLastSN) require.Equal(t, uint64(0xabcdef), r.extLastTS) snOffset, err := r.snRangeMap.GetValue(r.extHighestIncomingSN) + require.Error(t, err) + snOffset, err = r.snRangeMap.GetValue(r.extLastSN) require.NoError(t, err) require.Equal(t, uint64(0), snOffset) require.Equal(t, uint64(0), r.tsOffset) @@ -160,10 +167,11 @@ func TestOutOfOrderSequenceNumber(t *testing.T) { r.SetLastSnTs(extPkt) r.UpdateAndGetSnTs(extPkt) - // add a missing sequence number to the cache - r.snRangeMap.ExcludeRange(23332, 23333) + // should not be able to add a missing sequence number to the cache that is before start + err := r.snRangeMap.ExcludeRange(23332, 23333) + require.Error(t, err) - // out-of-order sequence number should be munged using cache + // out-of-order sequence number before start should miss params = &testutils.TestExtPacketParams{ SequenceNumber: 23331, Timestamp: 0xabcdef, @@ -172,13 +180,38 @@ func TestOutOfOrderSequenceNumber(t *testing.T) { } extPkt, _ = testutils.GetTestExtPacket(params) + tp, err := r.UpdateAndGetSnTs(extPkt) + require.Error(t, err) + + // add a missing sequence number to the cache + err = r.snRangeMap.ExcludeRange(23334, 23335) + require.NoError(t, err) + + params = &testutils.TestExtPacketParams{ + SequenceNumber: 23336, + Timestamp: 0xabcdef, + SSRC: 0x12345678, + PayloadSize: 10, + } + extPkt, _ = testutils.GetTestExtPacket(params) + r.UpdateAndGetSnTs(extPkt) + + // out-of-order sequence number should be munged from cache + params = &testutils.TestExtPacketParams{ + SequenceNumber: 23335, + Timestamp: 0xabcdef, + SSRC: 0x12345678, + PayloadSize: 10, + } + extPkt, _ = testutils.GetTestExtPacket(params) + tpExpected := TranslationParamsRTP{ snOrdering: SequenceNumberOrderingOutOfOrder, - extSequenceNumber: 23331, + extSequenceNumber: 23334, extTimestamp: 0xabcdef, } - tp, err := r.UpdateAndGetSnTs(extPkt) + tp, err = r.UpdateAndGetSnTs(extPkt) require.NoError(t, err) require.Equal(t, tpExpected, *tp) diff --git a/pkg/sfu/sequencer.go b/pkg/sfu/sequencer.go index 9bf53de74..7dad24aea 100644 --- a/pkg/sfu/sequencer.go +++ b/pkg/sfu/sequencer.go @@ -202,7 +202,11 @@ func (s *sequencer) pushPadding(extStartSNInclusive uint64, extEndSNInclusive ui // Not recording exclusion range means a few slots (of the size of exclusion range) // are wasted in this cycle. That should be fine as the exclusion ranges should be // a few packets at a time. - s.logger.Warnw("cannot exclude old range", nil, "extHighestSN", s.extHighestSN, "startSN", extStartSNInclusive, "endSN", extEndSNInclusive) + if extEndSNInclusive >= s.extHighestSN { + s.logger.Errorw("cannot exclude overlapping range", nil, "extHighestSN", s.extHighestSN, "startSN", extStartSNInclusive, "endSN", extEndSNInclusive) + } else { + s.logger.Warnw("cannot exclude old range", nil, "extHighestSN", s.extHighestSN, "startSN", extStartSNInclusive, "endSN", extEndSNInclusive) + } // if exclusion range is before what has already been sequenced, invalidate exclusion range slots for sn := extStartSNInclusive; sn != extEndSNInclusive+1; sn++ { diff --git a/pkg/sfu/utils/rangemap.go b/pkg/sfu/utils/rangemap.go index 036c24494..fe5480832 100644 --- a/pkg/sfu/utils/rangemap.go +++ b/pkg/sfu/utils/rangemap.go @@ -59,12 +59,12 @@ func NewRangeMap[RT rangeType, VT valueType](size int) *RangeMap[RT, VT] { halfRange: 1 << ((unsafe.Sizeof(t) * 8) - 1), size: int(math.Max(float64(size), float64(minRanges))), } - r.initRanges(0) + r.initRanges(0, 0) return r } -func (r *RangeMap[RT, VT]) ClearAndResetValue(val VT) { - r.initRanges(val) +func (r *RangeMap[RT, VT]) ClearAndResetValue(start RT, val VT) { + r.initRanges(start, val) } func (r *RangeMap[RT, VT]) DecValue(end RT, dec VT) { @@ -87,10 +87,10 @@ func (r *RangeMap[RT, VT]) DecValue(end RT, dec VT) { r.prune() } -func (r *RangeMap[RT, VT]) initRanges(val VT) { +func (r *RangeMap[RT, VT]) initRanges(start RT, val VT) { r.ranges = []rangeVal[RT, VT]{ { - start: 0, + start: start, end: 0, value: val, }, diff --git a/pkg/sfu/utils/rangemap_test.go b/pkg/sfu/utils/rangemap_test.go index 36a9daa97..657481fc7 100644 --- a/pkg/sfu/utils/rangemap_test.go +++ b/pkg/sfu/utils/rangemap_test.go @@ -219,10 +219,10 @@ func TestRangeMapUint32(t *testing.T) { require.Equal(t, uint32(17), value) // reset - r.ClearAndResetValue(23) + r.ClearAndResetValue(24, 23) expectedRanges = []rangeVal[uint32, uint32]{ { - start: 0, + start: 24, end: 0, value: 23, }, @@ -233,12 +233,13 @@ func TestRangeMapUint32(t *testing.T) { require.NoError(t, err) require.Equal(t, uint32(23), value) - // decrement value and ensure that any key returns that value + // decrement value and ensure that any key after start in ClearAndResetValue above returns that value + // (as given end is higher than open range start, open range should be closed and a new range added) r.DecValue(34, 12) expectedRanges = []rangeVal[uint32, uint32]{ { - start: 0, + start: 24, end: 34, value: 23, }, @@ -260,7 +261,7 @@ func TestRangeMapUint32(t *testing.T) { expectedRanges = []rangeVal[uint32, uint32]{ { - start: 0, + start: 24, end: 34, value: 23, }, @@ -277,8 +278,12 @@ func TestRangeMapUint32(t *testing.T) { } require.Equal(t, expectedRanges, r.ranges) - // first range access + // before first range access value, err = r.GetValue(5) + require.ErrorIs(t, err, errKeyTooOld) + + // first range access + value, err = r.GetValue(25) require.NoError(t, err) require.Equal(t, uint32(23), value) @@ -314,7 +319,7 @@ func TestRangeMapUint32(t *testing.T) { require.Equal(t, expectedRanges, r.ranges) // aged out range access - value, err = r.GetValue(5) + value, err = r.GetValue(25) require.ErrorIs(t, err, errKeyTooOld) // access closed range before decrementing value diff --git a/version/version.go b/version/version.go index a4b4ba000..41403002b 100644 --- a/version/version.go +++ b/version/version.go @@ -14,4 +14,4 @@ package version -const Version = "1.4.5" +const Version = "1.5.0"