mirror of
https://github.com/livekit/livekit.git
synced 2026-10-22 17:32:23 +00:00
Data tracks (#4089)
* WIP * WIP * Starting to add some signalling integration testing. * Working tests. * fix tests * Forward data packets (#4096) * WIP commit * WIP * WIP * fix forwarding * address PR comments * move some methods from LocalParticipant to Participant interface * handle subscription update * add extensions and tests * more packet tests * add test for replace extension and fix a bug * update protocol and add config
This commit is contained in:
1 parent
7158d98366
commit
7954748d7a
52 files changed
+5897
-499
No files matched your search
@@ -23,7 +23,7 @@ require (
|
||||
github.com/jxskiss/base62 v1.1.0
|
||||
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731
|
||||
github.com/livekit/mediatransportutil v0.0.0-20251201055132-772504244e19
|
||||
github.com/livekit/protocol v1.43.3-0.20251125082437-3bc7a74d3d5e
|
||||
github.com/livekit/protocol v1.43.3-0.20251204035522-21f690495229
|
||||
github.com/livekit/psrpc v0.7.1
|
||||
github.com/mackerelio/go-osstat v0.2.6
|
||||
github.com/magefile/mage v1.15.0
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/ory/dockertest/v3 v3.12.0
|
||||
github.com/pion/datachannel v1.5.10
|
||||
github.com/pion/dtls/v3 v3.0.7
|
||||
github.com/pion/dtls/v3 v3.0.8
|
||||
github.com/pion/ice/v4 v4.0.12
|
||||
github.com/pion/interceptor v0.1.42
|
||||
github.com/pion/rtcp v1.2.16
|
||||
@@ -44,7 +44,7 @@ require (
|
||||
github.com/pion/webrtc/v4 v4.1.6
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/redis/go-redis/v9 v9.17.0
|
||||
github.com/redis/go-redis/v9 v9.17.2
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/thoas/go-funk v0.9.3
|
||||
@@ -55,7 +55,7 @@ require (
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
|
||||
golang.org/x/mod v0.30.0
|
||||
golang.org/x/sync v0.18.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
@@ -106,7 +106,7 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lithammer/shortuuid/v4 v4.2.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
@@ -124,7 +124,7 @@ require (
|
||||
github.com/pion/logging v0.2.4 // indirect
|
||||
github.com/pion/mdns/v2 v2.1.0 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.8 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.9 // indirect
|
||||
github.com/pion/stun/v3 v3.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
|
||||
@@ -150,8 +150,8 @@ 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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -173,8 +173,8 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT
|
||||
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
|
||||
github.com/livekit/mediatransportutil v0.0.0-20251201055132-772504244e19 h1:JLLiGw3xtmLPzncsxe8wxvNQu91de+IV3BHIG5KlGoQ=
|
||||
github.com/livekit/mediatransportutil v0.0.0-20251201055132-772504244e19/go.mod h1:mSNtYzSf6iY9xM3UX42VEI+STHvMgHmrYzEHPcdhB8A=
|
||||
github.com/livekit/protocol v1.43.3-0.20251125082437-3bc7a74d3d5e h1:Fsdj1LoPI5LrzGwOxTGfP7SbGsK+/4SgFCbt83NAiws=
|
||||
github.com/livekit/protocol v1.43.3-0.20251125082437-3bc7a74d3d5e/go.mod h1:yjkL2/HcaCRyHykP9rLgKST2099AGd8laaU8EuHMnfw=
|
||||
github.com/livekit/protocol v1.43.3-0.20251204035522-21f690495229 h1:vY7Y24LPMSt8t8FndopzaNF+vn3+SkzIr6toMu2bBxQ=
|
||||
github.com/livekit/protocol v1.43.3-0.20251204035522-21f690495229/go.mod h1:n00Ul4P6o2YILGhxw+O57B0h/bF3Je9PzRN36fElCmw=
|
||||
github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw=
|
||||
github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk=
|
||||
github.com/mackerelio/go-osstat v0.2.6 h1:gs4U8BZeS1tjrL08tt5VUliVvSWP26Ai2Ob8Lr7f2i0=
|
||||
@@ -240,8 +240,8 @@ github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCy
|
||||
github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=
|
||||
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||
github.com/pion/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q=
|
||||
github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8=
|
||||
github.com/pion/dtls/v3 v3.0.8 h1:ZrPUrvPVDaTJDM8Vu1veatzXebLlsIWeT7Vaate/zwM=
|
||||
github.com/pion/dtls/v3 v3.0.8/go.mod h1:abApPjgadS/ra1wvUzHLc3o2HvoxppAh+NZkyApL4Os=
|
||||
github.com/pion/ice/v4 v4.0.12 h1:vuI3h9OD5M0Z+V304qLC1/o16ahHVnDgqNCWbONv6s0=
|
||||
github.com/pion/ice/v4 v4.0.12/go.mod h1:tAp574oAufhHRHr8EO1xgPmVKVDBROX+708WggYD6NE=
|
||||
github.com/pion/interceptor v0.1.42 h1:0/4tvNtruXflBxLfApMVoMubUMik57VZ+94U0J7cmkQ=
|
||||
@@ -260,8 +260,8 @@ github.com/pion/sctp v1.8.41 h1:20R4OHAno4Vky3/iE4xccInAScAa83X6nWUfyc65MIs=
|
||||
github.com/pion/sctp v1.8.41/go.mod h1:2wO6HBycUH7iCssuGyc2e9+0giXVW0pyCv3ZuL8LiyY=
|
||||
github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo=
|
||||
github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
|
||||
github.com/pion/srtp/v3 v3.0.8 h1:RjRrjcIeQsilPzxvdaElN0CpuQZdMvcl9VZ5UY9suUM=
|
||||
github.com/pion/srtp/v3 v3.0.8/go.mod h1:2Sq6YnDH7/UDCvkSoHSDNDeyBcFgWL0sAVycVbAsXFg=
|
||||
github.com/pion/srtp/v3 v3.0.9 h1:lRGF4G61xxj+m/YluB3ZnBpiALSri2lTzba0kGZMrQY=
|
||||
github.com/pion/srtp/v3 v3.0.9/go.mod h1:E+AuWd7Ug2Fp5u38MKnhduvpVkveXJX6J4Lq4rxUYt8=
|
||||
github.com/pion/stun/v3 v3.0.1 h1:jx1uUq6BdPihF0yF33Jj2mh+C9p0atY94IkdnW174kA=
|
||||
github.com/pion/stun/v3 v3.0.1/go.mod h1:RHnvlKFg+qHgoKIqtQWMOJF52wsImCAf/Jh5GjX+4Tw=
|
||||
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
|
||||
@@ -284,8 +284,8 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM=
|
||||
github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
|
||||
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@@ -373,8 +373,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY=
|
||||
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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=
|
||||
|
||||
@@ -83,6 +83,8 @@ type Config struct {
|
||||
Metric metric.MetricConfig `yaml:"metric,omitempty"`
|
||||
|
||||
NodeStats NodeStatsConfig `yaml:"node_stats,omitempty"`
|
||||
|
||||
EnableDataTracks bool `yaml:"enable_data_tracks,omitempty"`
|
||||
}
|
||||
|
||||
type RTCConfig struct {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type DataDownTrackParams struct {
|
||||
Logger logger.Logger
|
||||
SubscriberID livekit.ParticipantID
|
||||
PublishDataTrack types.DataTrack
|
||||
Transport types.DataTrackTransport
|
||||
}
|
||||
|
||||
type DataDownTrack struct {
|
||||
params DataDownTrackParams
|
||||
dti *livekit.DataTrackInfo
|
||||
handle uint16
|
||||
createdAt int64
|
||||
}
|
||||
|
||||
func NewDataDownTrack(params DataDownTrackParams, dti *livekit.DataTrackInfo) (*DataDownTrack, error) {
|
||||
d := &DataDownTrack{
|
||||
params: params,
|
||||
dti: dti,
|
||||
handle: uint16(rand.Intn(256)),
|
||||
createdAt: time.Now().UnixNano(),
|
||||
}
|
||||
|
||||
if err := d.params.PublishDataTrack.AddDataDownTrack(d); err != nil {
|
||||
d.params.Logger.Warnw("could not add data down track", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.params.Logger.Infow("created data down track", "id", d.ID(), "name", d.Name())
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Close() {
|
||||
d.params.Logger.Infow("closing data down track", "id", d.ID(), "name", d.Name())
|
||||
d.params.PublishDataTrack.DeleteDataDownTrack(d.SubscriberID())
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Handle() uint16 {
|
||||
return d.handle
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) PublishDataTrack() types.DataTrack {
|
||||
return d.params.PublishDataTrack
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) PubHandle() uint16 {
|
||||
return uint16(d.dti.PubHandle)
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) ID() livekit.TrackID {
|
||||
return livekit.TrackID(d.dti.Sid)
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Name() string {
|
||||
return d.dti.Name
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) SubscriberID() livekit.ParticipantID {
|
||||
// add `createdAt` to ensure repeated subscriptions from same subscriber to same publisher does not collide
|
||||
return livekit.ParticipantID(fmt.Sprintf("%s:%d", d.params.SubscriberID, d.createdAt))
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) WritePacket(data []byte, packet *datatrack.Packet) {
|
||||
forwardedPacket := *packet
|
||||
forwardedPacket.Handle = d.handle
|
||||
buf, err := forwardedPacket.Marshal()
|
||||
if err != nil {
|
||||
d.params.Logger.Warnw("could not marshal data track message", err)
|
||||
return
|
||||
}
|
||||
if err := d.params.Transport.SendDataTrackMessage(buf); err != nil {
|
||||
d.params.Logger.Warnw("could not send data track message", err, "handle", d.handle)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions) {
|
||||
// DT-TODO
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/frostbyte73/core"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
sfuutils "github.com/livekit/livekit-server/pkg/sfu/utils"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
errReceiverClosed = errors.New("datatrack is closed")
|
||||
)
|
||||
|
||||
type DataTrackParams struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type DataTrack struct {
|
||||
params DataTrackParams
|
||||
|
||||
lock sync.Mutex
|
||||
dti *livekit.DataTrackInfo
|
||||
subscribedTracks map[livekit.ParticipantID]*DataDownTrack
|
||||
|
||||
downTrackSpreader *sfuutils.DownTrackSpreader[types.DataTrackSender]
|
||||
|
||||
closed core.Fuse
|
||||
}
|
||||
|
||||
func NewDataTrack(params DataTrackParams, dti *livekit.DataTrackInfo) *DataTrack {
|
||||
d := &DataTrack{
|
||||
params: params,
|
||||
dti: dti,
|
||||
subscribedTracks: make(map[livekit.ParticipantID]*DataDownTrack),
|
||||
downTrackSpreader: sfuutils.NewDownTrackSpreader[types.DataTrackSender](sfuutils.DownTrackSpreaderParams{
|
||||
Threshold: 20,
|
||||
Logger: params.Logger,
|
||||
}),
|
||||
}
|
||||
d.params.Logger.Infow("created data track", "id", d.ID(), "name", d.Name())
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DataTrack) Close() {
|
||||
d.params.Logger.Infow("closing data track", "id", d.ID(), "name", d.Name())
|
||||
d.closed.Break()
|
||||
}
|
||||
|
||||
func (d *DataTrack) ToProto() *livekit.DataTrackInfo {
|
||||
return utils.CloneProto(d.dti)
|
||||
}
|
||||
|
||||
func (d *DataTrack) PubHandle() uint16 {
|
||||
return uint16(d.dti.PubHandle)
|
||||
}
|
||||
|
||||
func (d *DataTrack) ID() livekit.TrackID {
|
||||
return livekit.TrackID(d.dti.Sid)
|
||||
}
|
||||
|
||||
func (d *DataTrack) Name() string {
|
||||
return d.dti.Name
|
||||
}
|
||||
|
||||
func (d *DataTrack) AddSubscriber(sub types.LocalParticipant) (types.DataDownTrack, error) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
if _, ok := d.subscribedTracks[sub.ID()]; ok {
|
||||
return nil, errAlreadySubscribed
|
||||
}
|
||||
|
||||
dataDownTrack, err := NewDataDownTrack(
|
||||
DataDownTrackParams{
|
||||
Logger: sub.GetLogger().WithValues("trackID", d.ID()),
|
||||
SubscriberID: sub.ID(),
|
||||
PublishDataTrack: d,
|
||||
Transport: sub.GetDataTrackTransport(),
|
||||
},
|
||||
d.dti,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.subscribedTracks[sub.ID()] = dataDownTrack
|
||||
return dataDownTrack, nil
|
||||
}
|
||||
|
||||
func (d *DataTrack) RemoveSubscriber(subID livekit.ParticipantID) {
|
||||
d.lock.Lock()
|
||||
dataDownTrack, ok := d.subscribedTracks[subID]
|
||||
delete(d.subscribedTracks, subID)
|
||||
d.lock.Unlock()
|
||||
|
||||
if ok {
|
||||
dataDownTrack.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataTrack) IsSubscriber(subID livekit.ParticipantID) bool {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
_, ok := d.subscribedTracks[subID]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (d *DataTrack) AddDataDownTrack(dts types.DataTrackSender) error {
|
||||
if d.closed.IsBroken() {
|
||||
return errReceiverClosed
|
||||
}
|
||||
|
||||
if d.downTrackSpreader.HasDownTrack(dts.SubscriberID()) {
|
||||
d.params.Logger.Infow("subscriberID already exists, replacing data downtrack", "subscriberID", dts.SubscriberID())
|
||||
}
|
||||
|
||||
d.downTrackSpreader.Store(dts)
|
||||
d.params.Logger.Infow("data downtrack added", "subscriberID", dts.SubscriberID())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataTrack) DeleteDataDownTrack(subscriberID livekit.ParticipantID) {
|
||||
d.downTrackSpreader.Free(subscriberID)
|
||||
d.params.Logger.Infow("data downtrack deleted", "subscriberID", subscriberID)
|
||||
}
|
||||
|
||||
func (d *DataTrack) HandlePacket(data []byte, packet *datatrack.Packet) {
|
||||
d.downTrackSpreader.Broadcast(func(dts types.DataTrackSender) {
|
||||
dts.WritePacket(data, packet)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 datatrack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type ExtensionParticipantSid struct {
|
||||
participantID livekit.ParticipantID
|
||||
}
|
||||
|
||||
func NewExtensionParticipantSid(participantID livekit.ParticipantID) (*ExtensionParticipantSid, error) {
|
||||
if len(participantID) >= 65536 {
|
||||
return nil, errors.New("participantID too long")
|
||||
}
|
||||
|
||||
return &ExtensionParticipantSid{participantID}, nil
|
||||
}
|
||||
|
||||
func (e *ExtensionParticipantSid) ParticipantID() livekit.ParticipantID {
|
||||
return e.participantID
|
||||
}
|
||||
|
||||
func (e *ExtensionParticipantSid) Marshal() (Extension, error) {
|
||||
data := make([]byte, len(e.participantID))
|
||||
copy(data, e.participantID)
|
||||
return Extension{
|
||||
id: uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
|
||||
data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExtensionParticipantSid) Unmarshal(ext Extension) error {
|
||||
if ext.id != uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID) {
|
||||
return errors.New("invalid extension ID")
|
||||
}
|
||||
|
||||
if len(ext.data) == 0 {
|
||||
return errors.New("empty extension data")
|
||||
}
|
||||
|
||||
e.participantID = livekit.ParticipantID(ext.data)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 datatrack
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExtensionParticipantSid(t *testing.T) {
|
||||
longTestParticipantID := livekit.ParticipantID(make([]byte, 65536))
|
||||
extParticipantSid, err := NewExtensionParticipantSid(longTestParticipantID)
|
||||
require.Error(t, err)
|
||||
|
||||
testParticipantID := livekit.ParticipantID("test")
|
||||
extParticipantSid, err = NewExtensionParticipantSid(testParticipantID)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExt := Extension{
|
||||
id: uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
|
||||
data: []byte{'t', 'e', 's', 't'},
|
||||
}
|
||||
ext, err := extParticipantSid.Marshal()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedExt, ext)
|
||||
|
||||
var unmarshaled ExtensionParticipantSid
|
||||
err = unmarshaled.Unmarshal(ext)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testParticipantID, unmarshaled.ParticipantID())
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
// 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 datatrack
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
errHeaderSizeInsufficient = errors.New("data track packet header size insufficient")
|
||||
errBufferSizeInsufficient = errors.New("data track packet buffer size insufficient")
|
||||
errExtensionSizeInsufficient = errors.New("data track packet extension size insufficient")
|
||||
errExtensionNotFound = errors.New("data track packet extension not found")
|
||||
)
|
||||
|
||||
const (
|
||||
headerLength = 12
|
||||
|
||||
versionShift = 5
|
||||
versionMask = (1 << 3) - 1
|
||||
|
||||
firstOfFrameShift = 4
|
||||
firstOfFrameMask = (1 << 1) - 1
|
||||
|
||||
lastOfFrameShift = 3
|
||||
lastOfFrameMask = (1 << 1) - 1
|
||||
|
||||
extensionsShift = 2
|
||||
extensionsMask = (1 << 1) - 1
|
||||
|
||||
handleOffset = 2
|
||||
handleLength = 2
|
||||
|
||||
seqNumOffset = 4
|
||||
seqNumLength = 2
|
||||
|
||||
frameNumOffset = 6
|
||||
frameNumLength = 2
|
||||
|
||||
timestampOffset = 8
|
||||
timestampLength = 4
|
||||
|
||||
extensionsSizeOffset = headerLength
|
||||
extensionsSizeLength = 2
|
||||
|
||||
extensionIDLength = 2
|
||||
extensionSizeLength = 2
|
||||
)
|
||||
|
||||
type Extension struct {
|
||||
id uint16
|
||||
data []byte
|
||||
}
|
||||
|
||||
type Header struct {
|
||||
Version uint8
|
||||
IsFirstOfFrame bool
|
||||
IsLastOfFrame bool
|
||||
HasExtensions bool
|
||||
Handle uint16
|
||||
SequenceNumber uint16
|
||||
FrameNumber uint16
|
||||
Timestamp uint32
|
||||
ExtensionsSize uint16
|
||||
Extensions []Extension
|
||||
}
|
||||
|
||||
/*
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* 0 1 2 3
|
||||
┆* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* |V |F|L|X| reserved | handle |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* | sequence number | frame number |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* | timestamp |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|* Extensions Size if X=1 | Extensions... |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Each extension
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* 0 1 2 3
|
||||
┆* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
┆* | Extension ID | Extension size |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|* Extension payload (padded to 4 byte boundary) |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
func (h *Header) Unmarshal(buf []byte) (int, error) {
|
||||
if len(buf) < headerLength {
|
||||
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
|
||||
}
|
||||
|
||||
hdrSize := headerLength
|
||||
h.Version = buf[0] >> versionShift & versionMask
|
||||
h.IsFirstOfFrame = (buf[0] >> firstOfFrameShift & firstOfFrameMask) > 0
|
||||
h.IsLastOfFrame = (buf[0] >> lastOfFrameShift & lastOfFrameMask) > 0
|
||||
h.HasExtensions = (buf[0] >> extensionsShift & extensionsMask) > 0
|
||||
|
||||
h.Handle = binary.BigEndian.Uint16(buf[handleOffset : handleOffset+handleLength])
|
||||
h.SequenceNumber = binary.BigEndian.Uint16(buf[seqNumOffset : seqNumOffset+seqNumLength])
|
||||
h.FrameNumber = binary.BigEndian.Uint16(buf[frameNumOffset : frameNumOffset+frameNumLength])
|
||||
h.Timestamp = binary.BigEndian.Uint32(buf[timestampOffset : timestampOffset+timestampLength])
|
||||
|
||||
if h.HasExtensions {
|
||||
h.ExtensionsSize = (binary.BigEndian.Uint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength]) + 1) * 4
|
||||
hdrSize += extensionsSizeLength
|
||||
|
||||
remainingSize := int(h.ExtensionsSize)
|
||||
idx := extensionsSizeOffset + extensionsSizeLength
|
||||
for remainingSize != 0 {
|
||||
if len(buf[idx:]) < 4 || remainingSize < 4 {
|
||||
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), 4)
|
||||
}
|
||||
|
||||
id := binary.BigEndian.Uint16(buf[idx : idx+2])
|
||||
size := int(binary.BigEndian.Uint16(buf[idx+2 : idx+4]))
|
||||
remainingSize -= 4
|
||||
idx += 4
|
||||
hdrSize += 4
|
||||
|
||||
if len(buf[idx:]) < size || remainingSize < size {
|
||||
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), size)
|
||||
}
|
||||
h.Extensions = append(h.Extensions, Extension{id: id, data: buf[idx : idx+size]})
|
||||
|
||||
size = ((size + 3) / 4) * 4
|
||||
remainingSize -= size
|
||||
idx += size
|
||||
hdrSize += size
|
||||
}
|
||||
}
|
||||
|
||||
return hdrSize, nil
|
||||
}
|
||||
|
||||
func (h *Header) MarshalSize() int {
|
||||
size := headerLength
|
||||
if h.HasExtensions {
|
||||
size += 2 // extensions size field
|
||||
for _, ext := range h.Extensions {
|
||||
size += ((len(ext.data)+3)/4)*4 + 2 /* extension ID field */ + 2 /* extension length field */
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (h *Header) MarshalTo(buf []byte) (int, error) {
|
||||
if len(buf) < headerLength {
|
||||
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
|
||||
}
|
||||
|
||||
hdrSize := headerLength
|
||||
buf[0] = h.Version << versionShift
|
||||
if h.IsFirstOfFrame {
|
||||
buf[0] |= (1 << firstOfFrameShift)
|
||||
}
|
||||
if h.IsLastOfFrame {
|
||||
buf[0] |= (1 << lastOfFrameShift)
|
||||
}
|
||||
if h.HasExtensions {
|
||||
buf[0] |= (1 << extensionsShift)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[handleOffset:handleOffset+handleLength], h.Handle)
|
||||
binary.BigEndian.PutUint16(buf[seqNumOffset:seqNumOffset+seqNumLength], h.SequenceNumber)
|
||||
binary.BigEndian.PutUint16(buf[frameNumOffset:frameNumOffset+frameNumLength], h.FrameNumber)
|
||||
binary.BigEndian.PutUint32(buf[timestampOffset:timestampOffset+timestampLength], h.Timestamp)
|
||||
|
||||
if h.HasExtensions {
|
||||
binary.BigEndian.PutUint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength], (h.ExtensionsSize/4)-1)
|
||||
hdrSize += extensionsSizeLength
|
||||
|
||||
idx := extensionsSizeOffset + extensionsSizeLength
|
||||
for _, ext := range h.Extensions {
|
||||
binary.BigEndian.PutUint16(buf[idx:idx+extensionIDLength], ext.id)
|
||||
binary.BigEndian.PutUint16(buf[idx+extensionIDLength:idx+extensionIDLength+extensionSizeLength], uint16(len(ext.data)))
|
||||
copy(buf[idx+extensionIDLength+extensionSizeLength:], ext.data)
|
||||
|
||||
idx += ((len(ext.data)+3)/4)*4 + 2 /* extension ID field */ + 2 /* extension length field */
|
||||
hdrSize += ((len(ext.data)+3)/4)*4 + 2 /* extension ID field */ + 2 /* extension length field */
|
||||
}
|
||||
}
|
||||
|
||||
return hdrSize, nil
|
||||
}
|
||||
|
||||
func (h *Header) AddExtension(ext Extension) {
|
||||
for i, existingExt := range h.Extensions {
|
||||
if existingExt.id == ext.id {
|
||||
h.ExtensionsSize -= uint16((len(existingExt.data)+3)/4*4 + 2 /* extension ID field */ + 2 /* extension length field */)
|
||||
h.Extensions[i].data = ext.data
|
||||
h.ExtensionsSize += uint16((len(h.Extensions[i].data)+3)/4*4 + 2 /* extension ID field */ + 2 /* extension length field */)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
h.Extensions = append(h.Extensions, ext)
|
||||
h.ExtensionsSize += uint16((len(ext.data)+3)/4*4 + 2 /* extension ID field */ + 2 /* extension length field */)
|
||||
h.HasExtensions = true
|
||||
}
|
||||
|
||||
func (h *Header) GetExtension(id uint16) (Extension, error) {
|
||||
for _, ext := range h.Extensions {
|
||||
if ext.id == id {
|
||||
return ext, nil
|
||||
}
|
||||
}
|
||||
return Extension{}, fmt.Errorf("%w, id: %d", errExtensionNotFound, id)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
type Packet struct {
|
||||
Header
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (p *Packet) Unmarshal(buf []byte) error {
|
||||
hdrSize, err := p.Header.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Payload = buf[hdrSize:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Packet) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, p.Header.MarshalSize()+len(p.Payload))
|
||||
if err := p.MarshalTo(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (p *Packet) MarshalTo(buf []byte) error {
|
||||
size := p.Header.MarshalSize() + len(p.Payload)
|
||||
if len(buf) < size {
|
||||
return fmt.Errorf("%w: %d < %d", errBufferSizeInsufficient, len(buf), size)
|
||||
}
|
||||
|
||||
hdrSize, err := p.Header.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf[hdrSize:], p.Payload)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
// 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 datatrack
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPacket(t *testing.T) {
|
||||
t.Run("without extension", func(t *testing.T) {
|
||||
payload := make([]byte, 6)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsFirstOfFrame: true,
|
||||
IsLastOfFrame: true,
|
||||
Handle: 3333,
|
||||
SequenceNumber: 6666,
|
||||
FrameNumber: 9999,
|
||||
Timestamp: 0xdeadbeef,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
rawPacket, err := packet.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRawPacket := []byte{
|
||||
0x18, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0xff, 0xfe, 0xfd, 0xfc,
|
||||
0xfb, 0xfa,
|
||||
}
|
||||
require.Equal(t, expectedRawPacket, rawPacket)
|
||||
|
||||
var unmarshaled Packet
|
||||
err = unmarshaled.Unmarshal(rawPacket)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, packet, &unmarshaled)
|
||||
})
|
||||
|
||||
t.Run("with extension", func(t *testing.T) {
|
||||
payload := make([]byte, 4)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsFirstOfFrame: true,
|
||||
IsLastOfFrame: false,
|
||||
Handle: 3333,
|
||||
SequenceNumber: 6666,
|
||||
FrameNumber: 9999,
|
||||
Timestamp: 0xdeadbeef,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
|
||||
if ext, err := extParticipantSid.Marshal(); err == nil {
|
||||
packet.AddExtension(ext)
|
||||
}
|
||||
}
|
||||
rawPacket, err := packet.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRawPacket := []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x04, 0x00, 0x01,
|
||||
0x00, 0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70,
|
||||
0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61,
|
||||
0x6e, 0x74, 0xff, 0xfe, 0xfd, 0xfc,
|
||||
}
|
||||
require.Equal(t, expectedRawPacket, rawPacket)
|
||||
|
||||
var unmarshaled Packet
|
||||
err = unmarshaled.Unmarshal(rawPacket)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, packet, &unmarshaled)
|
||||
|
||||
ext, err := unmarshaled.GetExtension(uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
|
||||
require.NoError(t, err)
|
||||
|
||||
var extParticipantSid ExtensionParticipantSid
|
||||
require.NoError(t, extParticipantSid.Unmarshal(ext))
|
||||
require.Equal(t, livekit.ParticipantID("test_participant"), extParticipantSid.ParticipantID())
|
||||
})
|
||||
|
||||
t.Run("with extension padding", func(t *testing.T) {
|
||||
payload := make([]byte, 4)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsFirstOfFrame: true,
|
||||
IsLastOfFrame: false,
|
||||
Handle: 3333,
|
||||
SequenceNumber: 6666,
|
||||
FrameNumber: 9999,
|
||||
Timestamp: 0xdeadbeef,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
if extParticipantSid, err := NewExtensionParticipantSid("participant"); err == nil {
|
||||
if ext, err := extParticipantSid.Marshal(); err == nil {
|
||||
packet.AddExtension(ext)
|
||||
}
|
||||
}
|
||||
rawPacket, err := packet.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRawPacket := []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x00, 0x01,
|
||||
0x00, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
require.Equal(t, expectedRawPacket, rawPacket)
|
||||
|
||||
var unmarshaled Packet
|
||||
err = unmarshaled.Unmarshal(rawPacket)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, packet, &unmarshaled)
|
||||
|
||||
ext, err := unmarshaled.GetExtension(uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
|
||||
require.NoError(t, err)
|
||||
|
||||
var extParticipantSid ExtensionParticipantSid
|
||||
require.NoError(t, extParticipantSid.Unmarshal(ext))
|
||||
require.Equal(t, livekit.ParticipantID("participant"), extParticipantSid.ParticipantID())
|
||||
})
|
||||
|
||||
t.Run("replace extension", func(t *testing.T) {
|
||||
payload := make([]byte, 4)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsFirstOfFrame: true,
|
||||
IsLastOfFrame: false,
|
||||
Handle: 3333,
|
||||
SequenceNumber: 6666,
|
||||
FrameNumber: 9999,
|
||||
Timestamp: 0xdeadbeef,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
if extParticipantSid, err := NewExtensionParticipantSid("participant"); err == nil {
|
||||
if ext, err := extParticipantSid.Marshal(); err == nil {
|
||||
packet.AddExtension(ext)
|
||||
}
|
||||
}
|
||||
rawPacket, err := packet.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRawPacket := []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x00, 0x01,
|
||||
0x00, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
require.Equal(t, expectedRawPacket, rawPacket)
|
||||
|
||||
// replace existing extension ID and ensure that marshalled packet is updated
|
||||
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
|
||||
if ext, err := extParticipantSid.Marshal(); err == nil {
|
||||
packet.AddExtension(ext)
|
||||
}
|
||||
}
|
||||
rawPacket, err = packet.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedRawPacket = []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x04, 0x00, 0x01,
|
||||
0x00, 0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70,
|
||||
0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61,
|
||||
0x6e, 0x74, 0xff, 0xfe, 0xfd, 0xfc,
|
||||
}
|
||||
require.Equal(t, expectedRawPacket, rawPacket)
|
||||
|
||||
var unmarshaled Packet
|
||||
err = unmarshaled.Unmarshal(rawPacket)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, packet, &unmarshaled)
|
||||
|
||||
ext, err := unmarshaled.GetExtension(uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
|
||||
require.NoError(t, err)
|
||||
|
||||
var extParticipantSid ExtensionParticipantSid
|
||||
require.NoError(t, extParticipantSid.Unmarshal(ext))
|
||||
require.Equal(t, livekit.ParticipantID("test_participant"), extParticipantSid.ParticipantID())
|
||||
})
|
||||
|
||||
t.Run("bad pcaket", func(t *testing.T) {
|
||||
var unmarshaled Packet
|
||||
// extensions size too small
|
||||
badPacket := []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x02, 0x00, 0x01,
|
||||
0x00, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
err := unmarshaled.Unmarshal(badPacket)
|
||||
require.Error(t, err)
|
||||
|
||||
// get an invalid extension id
|
||||
badPacket = []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x00, 0x02,
|
||||
0x00, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
err = unmarshaled.Unmarshal(badPacket)
|
||||
require.NoError(t, err)
|
||||
_, err = unmarshaled.GetExtension(uint16(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
|
||||
require.Error(t, err)
|
||||
|
||||
// extension payload size bigger than payload
|
||||
badPacket = []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x00, 0x01,
|
||||
0x00, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
err = unmarshaled.Unmarshal(badPacket)
|
||||
require.Error(t, err)
|
||||
|
||||
// extension payload size smaller than payload
|
||||
badPacket = []byte{
|
||||
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
|
||||
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x00, 0x01,
|
||||
0x00, 0x07, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63,
|
||||
0x69, 0x70, 0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe,
|
||||
0xfd, 0xfc,
|
||||
}
|
||||
err = unmarshaled.Unmarshal(badPacket)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 datatrack
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateRawDataPackets(handle uint16, seqNum uint16, frameNum uint16, numFrames int, frameSize int, frameDuration time.Duration) [][]byte {
|
||||
if seqNum == 0 {
|
||||
seqNum = uint16(rand.Intn(256) + 1)
|
||||
}
|
||||
if frameNum == 0 {
|
||||
frameNum = uint16(rand.Intn(256) + 1)
|
||||
}
|
||||
timestamp := uint32(rand.Intn(1024))
|
||||
|
||||
packetsPerFrame := (frameSize + 255) / 256 // using 256 bytes of payload per packet
|
||||
if packetsPerFrame == 0 {
|
||||
return nil
|
||||
}
|
||||
numPackets := packetsPerFrame * numFrames
|
||||
rawPackets := make([][]byte, 0, numPackets)
|
||||
for range numFrames {
|
||||
remainingSize := frameSize
|
||||
for packetIdx := range packetsPerFrame {
|
||||
payloadSize := min(remainingSize, 256)
|
||||
payload := make([]byte, payloadSize)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsFirstOfFrame: packetIdx == 0,
|
||||
IsLastOfFrame: packetIdx == packetsPerFrame-1,
|
||||
Handle: handle,
|
||||
SequenceNumber: seqNum,
|
||||
FrameNumber: frameNum,
|
||||
Timestamp: timestamp,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
|
||||
if ext, err := extParticipantSid.Marshal(); err == nil {
|
||||
packet.AddExtension(ext)
|
||||
}
|
||||
}
|
||||
rawPacket, err := packet.Marshal()
|
||||
if err == nil {
|
||||
rawPackets = append(rawPackets, rawPacket)
|
||||
}
|
||||
seqNum++
|
||||
remainingSize -= payloadSize
|
||||
}
|
||||
frameNum++
|
||||
timestamp += uint32(90000 * frameDuration.Seconds())
|
||||
}
|
||||
|
||||
return rawPackets
|
||||
}
|
||||
+59
-11
@@ -52,6 +52,7 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/config"
|
||||
"github.com/livekit/livekit-server/pkg/metric"
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/signalling"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/supervisor"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/transport"
|
||||
@@ -217,6 +218,7 @@ type ParticipantParams struct {
|
||||
Country string
|
||||
PreferVideoSizeFromMedia bool
|
||||
UseSinglePeerConnection bool
|
||||
EnableDataTracks bool
|
||||
}
|
||||
|
||||
type ParticipantImpl struct {
|
||||
@@ -264,6 +266,7 @@ type ParticipantImpl struct {
|
||||
|
||||
*TransportManager
|
||||
*UpTrackManager
|
||||
*UpDataTrackManager
|
||||
*SubscriptionManager
|
||||
|
||||
icQueue [2]atomic.Pointer[webrtc.ICECandidate]
|
||||
@@ -293,18 +296,20 @@ type ParticipantImpl struct {
|
||||
version atomic.Uint32
|
||||
|
||||
// callbacks & handlers
|
||||
onTrackPublished func(types.LocalParticipant, types.MediaTrack)
|
||||
onTrackUpdated func(types.LocalParticipant, types.MediaTrack)
|
||||
onTrackUnpublished func(types.LocalParticipant, types.MediaTrack)
|
||||
onTrackPublished func(types.Participant, types.MediaTrack)
|
||||
onTrackUpdated func(types.Participant, types.MediaTrack)
|
||||
onTrackUnpublished func(types.Participant, types.MediaTrack)
|
||||
onStateChange func(p types.LocalParticipant)
|
||||
onSubscriberReady func(p types.LocalParticipant)
|
||||
onMigrateStateChange func(p types.LocalParticipant, migrateState types.MigrateState)
|
||||
onParticipantUpdate func(types.LocalParticipant)
|
||||
onDataPacket func(types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)
|
||||
onDataMessage func(types.LocalParticipant, []byte)
|
||||
onDataTrackMessage func(types.LocalParticipant, []byte, *datatrack.Packet)
|
||||
onMetrics func(types.Participant, *livekit.DataPacket)
|
||||
onUpdateSubscriptions func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)
|
||||
onUpdateSubscriptionPermission func(types.LocalParticipant, *livekit.SubscriptionPermission) error
|
||||
onUpdateDataSubscriptions func(types.LocalParticipant, *livekit.UpdateDataSubscription)
|
||||
onSyncState func(types.LocalParticipant, *livekit.SyncState) error
|
||||
onSimulateScenario func(types.LocalParticipant, *livekit.SimulateScenario) error
|
||||
onLeave func(types.LocalParticipant, types.ParticipantCloseReason)
|
||||
@@ -431,6 +436,7 @@ func NewParticipant(params ParticipantParams) (*ParticipantImpl, error) {
|
||||
}
|
||||
|
||||
p.setupUpTrackManager()
|
||||
p.setupUpDataTrackManager()
|
||||
p.setupSubscriptionManager()
|
||||
p.setupMetrics()
|
||||
|
||||
@@ -788,6 +794,12 @@ func (p *ParticipantImpl) SetPermission(permission *livekit.ParticipantPermissio
|
||||
}
|
||||
}
|
||||
|
||||
if !grants.Video.GetCanPublishData() {
|
||||
for _, dt := range p.UpDataTrackManager.GetPublishedDataTracks() {
|
||||
p.UpDataTrackManager.RemovePublishedDataTrack(dt)
|
||||
}
|
||||
}
|
||||
|
||||
// update isPublisher attribute
|
||||
p.isPublisher.Store(isPublisher)
|
||||
|
||||
@@ -882,6 +894,8 @@ func (p *ParticipantImpl) ToProtoWithVersion() (*livekit.ParticipantInfo, utils.
|
||||
}
|
||||
p.pendingTracksLock.RUnlock()
|
||||
|
||||
pi.DataTracks = p.UpDataTrackManager.ToProto()
|
||||
|
||||
return pi, piv
|
||||
}
|
||||
|
||||
@@ -896,25 +910,25 @@ func (p *ParticipantImpl) TelemetryGuard() *telemetry.ReferenceGuard {
|
||||
|
||||
// callbacks for clients
|
||||
|
||||
func (p *ParticipantImpl) OnTrackPublished(callback func(types.LocalParticipant, types.MediaTrack)) {
|
||||
func (p *ParticipantImpl) OnTrackPublished(callback func(types.Participant, types.MediaTrack)) {
|
||||
p.lock.Lock()
|
||||
p.onTrackPublished = callback
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) getOnTrackPublished() func(types.LocalParticipant, types.MediaTrack) {
|
||||
func (p *ParticipantImpl) getOnTrackPublished() func(types.Participant, types.MediaTrack) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.onTrackPublished
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) OnTrackUnpublished(callback func(types.LocalParticipant, types.MediaTrack)) {
|
||||
func (p *ParticipantImpl) OnTrackUnpublished(callback func(types.Participant, types.MediaTrack)) {
|
||||
p.lock.Lock()
|
||||
p.onTrackUnpublished = callback
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) getOnTrackUnpublished() func(types.LocalParticipant, types.MediaTrack) {
|
||||
func (p *ParticipantImpl) getOnTrackUnpublished() func(types.Participant, types.MediaTrack) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.onTrackUnpublished
|
||||
@@ -956,13 +970,13 @@ func (p *ParticipantImpl) getOnMigrateStateChange() func(p types.LocalParticipan
|
||||
return p.onMigrateStateChange
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) OnTrackUpdated(callback func(types.LocalParticipant, types.MediaTrack)) {
|
||||
func (p *ParticipantImpl) OnTrackUpdated(callback func(types.Participant, types.MediaTrack)) {
|
||||
p.lock.Lock()
|
||||
p.onTrackUpdated = callback
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) getOnTrackUpdated() func(types.LocalParticipant, types.MediaTrack) {
|
||||
func (p *ParticipantImpl) getOnTrackUpdated() func(types.Participant, types.MediaTrack) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.onTrackUpdated
|
||||
@@ -998,6 +1012,18 @@ func (p *ParticipantImpl) getOnDataMessage() func(types.LocalParticipant, []byte
|
||||
return p.onDataMessage
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) OnDataTrackMessage(callback func(types.LocalParticipant, []byte, *datatrack.Packet)) {
|
||||
p.lock.Lock()
|
||||
p.onDataTrackMessage = callback
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) getOnDataTrackMessage() func(types.LocalParticipant, []byte, *datatrack.Packet) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.onDataTrackMessage
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) OnMetrics(callback func(types.Participant, *livekit.DataPacket)) {
|
||||
p.lock.Lock()
|
||||
p.onMetrics = callback
|
||||
@@ -1169,7 +1195,7 @@ func (p *ParticipantImpl) updateRidsFromSDP(parsed *sdp.SessionDescription, unma
|
||||
rids, ok := protosdp.GetSimulcastRids(m)
|
||||
if ok {
|
||||
n := min(len(rids), len(inRids))
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
// disabled layers will have a `~` prefix, remove it while determining actual rid
|
||||
if len(rids[i]) != 0 && rids[i][0] == '~' {
|
||||
outRids[i] = rids[i][1:]
|
||||
@@ -1454,6 +1480,7 @@ func (p *ParticipantImpl) SetMigrateInfo(
|
||||
mediaTracks []*livekit.TrackPublishedResponse,
|
||||
dataChannels []*livekit.DataChannelInfo,
|
||||
dataChannelReceiveState []*livekit.DataChannelReceiveState,
|
||||
dataTracks []*livekit.PublishDataTrackResponse,
|
||||
) {
|
||||
p.pendingTracksLock.Lock()
|
||||
for _, t := range mediaTracks {
|
||||
@@ -1478,7 +1505,13 @@ func (p *ParticipantImpl) SetMigrateInfo(
|
||||
}
|
||||
p.pendingTracksLock.Unlock()
|
||||
|
||||
if len(mediaTracks) != 0 {
|
||||
for _, t := range dataTracks {
|
||||
dti := t.GetInfo()
|
||||
dt := NewDataTrack(DataTrackParams{Logger: p.params.Logger.WithValues("trackID", dti.Sid)}, dti)
|
||||
p.UpDataTrackManager.AddPublishedDataTrack(dt)
|
||||
}
|
||||
|
||||
if len(mediaTracks) != 0 || len(dataTracks) != 0 {
|
||||
p.setIsPublisher(true)
|
||||
}
|
||||
|
||||
@@ -1983,6 +2016,10 @@ func (h PublisherTransportHandler) OnDataMessageUnlabeled(data []byte) {
|
||||
h.p.onReceivedDataMessageUnlabeled(data)
|
||||
}
|
||||
|
||||
func (h PublisherTransportHandler) OnDataTrackMessage(data []byte) {
|
||||
h.p.onReceivedDataTrackMessage(data)
|
||||
}
|
||||
|
||||
func (h PublisherTransportHandler) OnDataSendError(err error) {
|
||||
h.p.onDataSendError(err)
|
||||
}
|
||||
@@ -2089,6 +2126,7 @@ func (p *ParticipantImpl) setupTransportManager() error {
|
||||
DataChannelStats: p.dataChannelStats,
|
||||
UseOneShotSignallingMode: p.params.UseOneShotSignallingMode,
|
||||
FireOnTrackBySdp: p.params.FireOnTrackBySdp,
|
||||
EnableDataTracks: p.params.EnableDataTracks,
|
||||
}
|
||||
if p.params.SyncStreams && p.params.PlayoutDelay.GetEnabled() && p.params.ClientInfo.isFirefox() {
|
||||
// we will disable playout delay for Firefox if the user is expecting
|
||||
@@ -2141,6 +2179,13 @@ func (p *ParticipantImpl) setupUpTrackManager() {
|
||||
p.UpTrackManager.OnUpTrackManagerClose(p.onUpTrackManagerClose)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setupUpDataTrackManager() {
|
||||
p.UpDataTrackManager = NewUpDataTrackManager(UpDataTrackManagerParams{
|
||||
Logger: p.pubLogger,
|
||||
Participant: p,
|
||||
})
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setupSubscriptionManager() {
|
||||
p.SubscriptionManager = NewSubscriptionManager(SubscriptionManagerParams{
|
||||
Participant: p,
|
||||
@@ -2148,6 +2193,9 @@ func (p *ParticipantImpl) setupSubscriptionManager() {
|
||||
TrackResolver: func(lp types.LocalParticipant, ti livekit.TrackID) types.MediaResolverResult {
|
||||
return p.helper().ResolveMediaTrack(lp, ti)
|
||||
},
|
||||
DataTrackResolver: func(lp types.LocalParticipant, ti livekit.TrackID) types.DataResolverResult {
|
||||
return p.helper().ResolveDataTrack(lp, ti)
|
||||
},
|
||||
Telemetry: p.params.Telemetry,
|
||||
OnTrackSubscribed: p.onTrackSubscribed,
|
||||
OnTrackUnsubscribed: p.onTrackUnsubscribed,
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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 (
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/protocol/utils/guid"
|
||||
)
|
||||
|
||||
func (p *ParticipantImpl) OnUpdateDataSubscriptions(callback func(types.LocalParticipant, *livekit.UpdateDataSubscription)) {
|
||||
p.lock.Lock()
|
||||
p.onUpdateDataSubscriptions = callback
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) getOnUpdateDataSubscriptions() func(types.LocalParticipant, *livekit.UpdateDataSubscription) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
return p.onUpdateDataSubscriptions
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) HandlePublishDataTrackRequest(req *livekit.PublishDataTrackRequest) {
|
||||
if !p.CanPublishData() || !p.params.EnableDataTracks {
|
||||
p.pubLogger.Warnw("no permission to publish data track", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_NOT_ALLOWED,
|
||||
Message: "does not have permission to publish data",
|
||||
Request: &livekit.RequestResponse_PublishDataTrack{
|
||||
PublishDataTrack: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.PubHandle == 0 || req.PubHandle > 65535 {
|
||||
p.pubLogger.Warnw("invalid data track handle", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_INVALID_HANDLE,
|
||||
Message: "handle should be > 0 AND < 65536",
|
||||
Request: &livekit.RequestResponse_PublishDataTrack{
|
||||
PublishDataTrack: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Name) == 0 || len(req.Name) > 256 {
|
||||
p.pubLogger.Warnw("invalid data track name", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_INVALID_NAME,
|
||||
Message: "name should not be empty and should not exceed 256 characters",
|
||||
Request: &livekit.RequestResponse_PublishDataTrack{
|
||||
PublishDataTrack: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
publishedDataTracks := p.UpDataTrackManager.GetPublishedDataTracks()
|
||||
for _, dt := range publishedDataTracks {
|
||||
message := ""
|
||||
reason := livekit.RequestResponse_OK
|
||||
switch {
|
||||
case dt.PubHandle() == uint16(req.PubHandle):
|
||||
message = "a data track with same handle already exists"
|
||||
reason = livekit.RequestResponse_DUPLICATE_HANDLE
|
||||
case dt.Name() == req.Name:
|
||||
message = "a data track with same name already exists"
|
||||
reason = livekit.RequestResponse_DUPLICATE_NAME
|
||||
}
|
||||
if message != "" {
|
||||
p.pubLogger.Warnw(
|
||||
"cannot publish duplicate data track", nil,
|
||||
"req", logger.Proto(req),
|
||||
"existing", logger.Proto(dt.ToProto()),
|
||||
)
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
Request: &livekit.RequestResponse_PublishDataTrack{
|
||||
PublishDataTrack: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dti := &livekit.DataTrackInfo{
|
||||
PubHandle: req.PubHandle,
|
||||
Sid: guid.New(utils.DataTrackPrefix),
|
||||
Name: req.Name,
|
||||
Encryption: req.Encryption,
|
||||
}
|
||||
dt := NewDataTrack(DataTrackParams{Logger: p.params.Logger.WithValues("trackID", dti.Sid)}, dti)
|
||||
|
||||
p.UpDataTrackManager.AddPublishedDataTrack(dt)
|
||||
|
||||
p.sendPublishDataTrackResponse(dti)
|
||||
|
||||
p.setIsPublisher(true)
|
||||
p.dirty.Store(true)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) HandleUnpublishDataTrackRequest(req *livekit.UnpublishDataTrackRequest) {
|
||||
dt := p.UpDataTrackManager.GetPublishedDataTrack(uint16(req.PubHandle))
|
||||
if dt == nil {
|
||||
p.pubLogger.Warnw("unpublish data track not found", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_NOT_FOUND,
|
||||
Request: &livekit.RequestResponse_UnpublishDataTrack{
|
||||
UnpublishDataTrack: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
p.UpDataTrackManager.RemovePublishedDataTrack(dt)
|
||||
|
||||
p.sendUnpublishDataTrackResponse(dt.ToProto())
|
||||
|
||||
p.dirty.Store(true)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) HandleUpdateDataSubscription(req *livekit.UpdateDataSubscription) {
|
||||
if onUpdateDataSubscriptions := p.getOnUpdateDataSubscriptions(); onUpdateDataSubscriptions != nil {
|
||||
onUpdateDataSubscriptions(p, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) onReceivedDataTrackMessage(data []byte) {
|
||||
var packet datatrack.Packet
|
||||
if err := packet.Unmarshal(data); err != nil {
|
||||
p.params.Logger.Errorw("could not unmarshal data track message", err)
|
||||
return
|
||||
}
|
||||
|
||||
p.UpDataTrackManager.HandleReceivedDataTrackMessage(data, &packet)
|
||||
|
||||
if onDataTrackMessage := p.getOnDataTrackMessage(); onDataTrackMessage != nil {
|
||||
onDataTrackMessage(p, data, &packet)
|
||||
}
|
||||
}
|
||||
@@ -84,10 +84,10 @@ func TestTrackPublishing(t *testing.T) {
|
||||
track.IDReturns("id")
|
||||
published := false
|
||||
updated := false
|
||||
p.OnTrackUpdated(func(p types.LocalParticipant, track types.MediaTrack) {
|
||||
p.OnTrackUpdated(func(p types.Participant, track types.MediaTrack) {
|
||||
updated = true
|
||||
})
|
||||
p.OnTrackPublished(func(p types.LocalParticipant, track types.MediaTrack) {
|
||||
p.OnTrackPublished(func(p types.Participant, track types.MediaTrack) {
|
||||
published = true
|
||||
})
|
||||
p.UpTrackManager.AddPublishedTrack(track)
|
||||
|
||||
@@ -350,3 +350,21 @@ func (p *ParticipantImpl) sendMediaSectionsRequirement(numAudios uint32, numVide
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendPublishDataTrackResponse(dti *livekit.DataTrackInfo) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalPublishDataTrackResponse(&livekit.PublishDataTrackResponse{
|
||||
Info: dti,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendUnpublishDataTrackResponse(dti *livekit.DataTrackInfo) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalUnpublishDataTrackResponse(&livekit.UnpublishDataTrackResponse{
|
||||
Info: dti,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendDataTrackSubscriberHandles(handles map[uint32]*livekit.DataTrackSubscriberHandles_PublishedDataTrack) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalDataTrackSubscriberHandles(&livekit.DataTrackSubscriberHandles{
|
||||
SubHandles: handles,
|
||||
}))
|
||||
}
|
||||
+134
-13
@@ -134,7 +134,7 @@ type Room struct {
|
||||
|
||||
trailer []byte
|
||||
|
||||
onParticipantChanged func(p types.LocalParticipant)
|
||||
onParticipantChanged func(p types.Participant)
|
||||
onRoomUpdated func()
|
||||
onClose func()
|
||||
|
||||
@@ -518,6 +518,8 @@ func (r *Room) Join(
|
||||
participant.OnTrackPublished(r.onTrackPublished)
|
||||
participant.OnTrackUpdated(r.onTrackUpdated)
|
||||
participant.OnTrackUnpublished(r.onTrackUnpublished)
|
||||
participant.OnDataTrackPublished(r.onDataTrackPublished)
|
||||
participant.OnDataTrackUnpublished(r.onDataTrackUnpublished)
|
||||
participant.OnParticipantUpdate(r.onParticipantUpdate)
|
||||
participant.OnDataPacket(r.onDataPacket)
|
||||
participant.OnDataMessage(r.onDataMessage)
|
||||
@@ -558,6 +560,7 @@ func (r *Room) Join(
|
||||
})
|
||||
participant.OnUpdateSubscriptions(r.onUpdateSubscriptions)
|
||||
participant.OnUpdateSubscriptionPermission(r.onUpdateSubscriptionPermission)
|
||||
participant.OnUpdateDataSubscriptions(r.onUpdateDataSubscriptions)
|
||||
participant.OnSyncState(r.onSyncState)
|
||||
participant.OnSimulateScenario(r.onSimulateScenario)
|
||||
participant.OnLeave(r.onLeave)
|
||||
@@ -745,6 +748,32 @@ func (r *Room) onSyncState(participant types.LocalParticipant, state *livekit.Sy
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pubDataTracks := state.GetPublishDataTracks()
|
||||
existingPubDataTracks := participant.GetPublishedDataTracks()
|
||||
for _, pubDataTrack := range pubDataTracks {
|
||||
// client may not have sent DataTrackInfo for each published data track
|
||||
dti := pubDataTrack.Info
|
||||
if dti == nil {
|
||||
pLogger.Warnw("DataTrackInfo not sent during resume", nil)
|
||||
shouldReconnect = true
|
||||
break
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, existingPubDataTrack := range existingPubDataTracks {
|
||||
if existingPubDataTrack.ID() == livekit.TrackID(dti.Sid) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
pLogger.Warnw("unknown data track during resume", nil, "trackID", dti.Sid)
|
||||
shouldReconnect = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if shouldReconnect {
|
||||
pLogger.Warnw("unable to resume due to missing published tracks, starting full reconnect", nil)
|
||||
participant.IssueFullReconnect(types.ParticipantCloseReasonPublicationError)
|
||||
@@ -800,6 +829,23 @@ func (r *Room) ResolveMediaTrackForSubscriber(sub types.LocalParticipant, trackI
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *Room) ResolveDataTrackForSubscriber(sub types.LocalParticipant, trackID livekit.TrackID) types.DataResolverResult {
|
||||
res := types.DataResolverResult{}
|
||||
|
||||
info := r.trackManager.GetDataTrackInfo(trackID)
|
||||
res.TrackChangedNotifier = r.trackManager.GetOrCreateTrackChangeNotifier(trackID)
|
||||
|
||||
if info == nil {
|
||||
return res
|
||||
}
|
||||
|
||||
res.DataTrack = info.DataTrack
|
||||
res.TrackRemovedNotifier = r.trackManager.GetOrCreateTrackRemoveNotifier(trackID)
|
||||
res.PublisherIdentity = info.PublisherIdentity
|
||||
res.PublisherID = info.PublisherID
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *Room) IsClosed() bool {
|
||||
select {
|
||||
case <-r.closed:
|
||||
@@ -874,7 +920,7 @@ func (r *Room) OnClose(f func()) {
|
||||
r.onClose = f
|
||||
}
|
||||
|
||||
func (r *Room) OnParticipantChanged(f func(participant types.LocalParticipant)) {
|
||||
func (r *Room) OnParticipantChanged(f func(participant types.Participant)) {
|
||||
r.onParticipantChanged = f
|
||||
}
|
||||
|
||||
@@ -1101,7 +1147,9 @@ func (r *Room) createJoinResponseLocked(
|
||||
}
|
||||
|
||||
// a ParticipantImpl in the room added a new track, subscribe other participants to it
|
||||
func (r *Room) onTrackPublished(participant types.LocalParticipant, track types.MediaTrack) {
|
||||
func (r *Room) onTrackPublished(participant types.Participant, track types.MediaTrack) {
|
||||
r.trackManager.AddTrack(track, participant.Identity(), participant.ID())
|
||||
|
||||
// publish participant update, since track state is changed
|
||||
r.broadcastParticipantState(participant, broadcastOptions{skipSource: true})
|
||||
|
||||
@@ -1135,8 +1183,6 @@ func (r *Room) onTrackPublished(participant types.LocalParticipant, track types.
|
||||
onParticipantChanged(participant)
|
||||
}
|
||||
|
||||
r.trackManager.AddTrack(track, participant.Identity(), participant.ID())
|
||||
|
||||
// launch jobs
|
||||
r.lock.Lock()
|
||||
hasPublished := r.hasPublished[participant.Identity()]
|
||||
@@ -1180,7 +1226,7 @@ func (r *Room) onTrackPublished(participant types.LocalParticipant, track types.
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onTrackUpdated(p types.LocalParticipant, _ types.MediaTrack) {
|
||||
func (r *Room) onTrackUpdated(p types.Participant, _ types.MediaTrack) {
|
||||
// send track updates to everyone, especially if track was updated by admin
|
||||
r.broadcastParticipantState(p, broadcastOptions{})
|
||||
if r.onParticipantChanged != nil {
|
||||
@@ -1188,7 +1234,7 @@ func (r *Room) onTrackUpdated(p types.LocalParticipant, _ types.MediaTrack) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onTrackUnpublished(p types.LocalParticipant, track types.MediaTrack) {
|
||||
func (r *Room) onTrackUnpublished(p types.Participant, track types.MediaTrack) {
|
||||
r.trackManager.RemoveTrack(track)
|
||||
if !p.IsClosed() {
|
||||
r.broadcastParticipantState(p, broadcastOptions{skipSource: true})
|
||||
@@ -1198,6 +1244,53 @@ func (r *Room) onTrackUnpublished(p types.LocalParticipant, track types.MediaTra
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onDataTrackPublished(participant types.Participant, dt types.DataTrack) {
|
||||
r.trackManager.AddDataTrack(dt, participant.Identity(), participant.ID())
|
||||
|
||||
// publish participant update, since a new data track was published
|
||||
r.broadcastParticipantState(participant, broadcastOptions{skipSource: true})
|
||||
|
||||
r.lock.RLock()
|
||||
// subscribe all existing participants to this DataTrack
|
||||
for _, existingParticipant := range r.participants {
|
||||
if existingParticipant == participant {
|
||||
// skip publishing participant
|
||||
continue
|
||||
}
|
||||
if existingParticipant.State() != livekit.ParticipantInfo_ACTIVE {
|
||||
// not fully joined. don't subscribe yet
|
||||
continue
|
||||
}
|
||||
if !r.autoSubscribe(existingParticipant) {
|
||||
continue
|
||||
}
|
||||
|
||||
existingParticipant.GetLogger().Debugw(
|
||||
"subscribing to new data track",
|
||||
"publisher", participant.Identity(),
|
||||
"publisherID", participant.ID(),
|
||||
"trackID", dt.ID(),
|
||||
)
|
||||
existingParticipant.SubscribeToDataTrack(dt.ID())
|
||||
}
|
||||
onParticipantChanged := r.onParticipantChanged
|
||||
r.lock.RUnlock()
|
||||
|
||||
if onParticipantChanged != nil {
|
||||
onParticipantChanged(participant)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onDataTrackUnpublished(p types.Participant, dt types.DataTrack) {
|
||||
r.trackManager.RemoveDataTrack(dt)
|
||||
if !p.IsClosed() {
|
||||
r.broadcastParticipantState(p, broadcastOptions{skipSource: true})
|
||||
}
|
||||
if r.onParticipantChanged != nil {
|
||||
r.onParticipantChanged(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onParticipantUpdate(p types.LocalParticipant) {
|
||||
r.protoProxy.MarkDirty(false)
|
||||
// immediately notify when permissions or metadata changed
|
||||
@@ -1247,7 +1340,6 @@ func (r *Room) UpdateSubscriptions(
|
||||
participantTracks []*livekit.ParticipantTracks,
|
||||
subscribe bool,
|
||||
) {
|
||||
// handle subscription changes
|
||||
for _, trackID := range trackIDs {
|
||||
if subscribe {
|
||||
participant.SubscribeToTrack(trackID, false)
|
||||
@@ -1267,6 +1359,19 @@ func (r *Room) UpdateSubscriptions(
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onUpdateDataSubscriptions(participant types.LocalParticipant, req *livekit.UpdateDataSubscription) {
|
||||
for _, update := range req.Updates {
|
||||
trackID := livekit.TrackID(update.TrackSid)
|
||||
if update.Subscribe {
|
||||
participant.SubscribeToDataTrack(trackID)
|
||||
participant.UpdateDataTrackSubscriptionOptions(trackID, update.Options)
|
||||
} else {
|
||||
participant.UnsubscribeFromDataTrack(trackID)
|
||||
participant.UpdateDataTrackSubscriptionOptions(trackID, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onLeave(p types.LocalParticipant, reason types.ParticipantCloseReason) {
|
||||
r.RemoveParticipant(p.Identity(), p.ID(), reason)
|
||||
}
|
||||
@@ -1332,8 +1437,14 @@ func (r *Room) RemoveParticipant(
|
||||
|
||||
// remove all published tracks
|
||||
for _, t := range p.GetPublishedTracks() {
|
||||
p.RemovePublishedTrack(t, false)
|
||||
r.trackManager.RemoveTrack(t)
|
||||
p.RemovePublishedTrack(t, false)
|
||||
}
|
||||
|
||||
// remove all published data tracks
|
||||
for _, t := range p.GetPublishedDataTracks() {
|
||||
r.trackManager.RemoveDataTrack(t)
|
||||
p.RemovePublishedDataTrack(t)
|
||||
}
|
||||
|
||||
if agentJob != nil {
|
||||
@@ -1350,6 +1461,8 @@ func (r *Room) RemoveParticipant(
|
||||
p.OnTrackUpdated(nil)
|
||||
p.OnTrackPublished(nil)
|
||||
p.OnTrackUnpublished(nil)
|
||||
p.OnDataTrackPublished(nil)
|
||||
p.OnDataTrackUnpublished(nil)
|
||||
p.OnStateChange(nil)
|
||||
p.OnSubscriberReady(nil)
|
||||
p.OnParticipantUpdate(nil)
|
||||
@@ -1359,6 +1472,7 @@ func (r *Room) RemoveParticipant(
|
||||
p.OnSubscribeStatusChanged(nil)
|
||||
p.OnUpdateSubscriptions(nil)
|
||||
p.OnUpdateSubscriptionPermission(nil)
|
||||
p.OnUpdateDataSubscriptions(nil)
|
||||
p.OnSyncState(nil)
|
||||
p.OnSimulateScenario(nil)
|
||||
p.OnLeave(nil)
|
||||
@@ -1396,6 +1510,11 @@ func (r *Room) subscribeToExistingTracks(p types.LocalParticipant, isSync bool)
|
||||
trackIDs = append(trackIDs, track.ID())
|
||||
p.SubscribeToTrack(track.ID(), isSync)
|
||||
}
|
||||
|
||||
for _, track := range op.GetPublishedDataTracks() {
|
||||
trackIDs = append(trackIDs, track.ID())
|
||||
p.SubscribeToDataTrack(track.ID())
|
||||
}
|
||||
}
|
||||
if len(trackIDs) > 0 {
|
||||
p.GetLogger().Debugw("subscribed participant to existing tracks", "trackID", trackIDs)
|
||||
@@ -1403,7 +1522,7 @@ func (r *Room) subscribeToExistingTracks(p types.LocalParticipant, isSync bool)
|
||||
}
|
||||
|
||||
// broadcast an update about participant p
|
||||
func (r *Room) broadcastParticipantState(p types.LocalParticipant, opts broadcastOptions) {
|
||||
func (r *Room) broadcastParticipantState(p types.Participant, opts broadcastOptions) {
|
||||
pi := p.ToProto()
|
||||
|
||||
// send it to the same participant immediately
|
||||
@@ -1414,9 +1533,11 @@ func (r *Room) broadcastParticipantState(p types.LocalParticipant, opts broadcas
|
||||
return
|
||||
}
|
||||
|
||||
err := p.SendParticipantUpdate([]*livekit.ParticipantInfo{pi})
|
||||
if err != nil {
|
||||
p.GetLogger().Errorw("could not send update to participant", err)
|
||||
if lp, ok := p.(types.LocalParticipant); ok {
|
||||
err := lp.SendParticipantUpdate([]*livekit.ParticipantInfo{pi})
|
||||
if err != nil {
|
||||
lp.GetLogger().Errorw("could not send update to participant", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestRoomJoin(t *testing.T) {
|
||||
t.Run("participant state change is broadcasted to others", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: numParticipants})
|
||||
var changedParticipant types.Participant
|
||||
rm.OnParticipantChanged(func(participant types.LocalParticipant) {
|
||||
rm.OnParticipantChanged(func(participant types.Participant) {
|
||||
changedParticipant = participant
|
||||
})
|
||||
participants := rm.GetParticipants()
|
||||
|
||||
@@ -31,6 +31,7 @@ type RoomTrackManager struct {
|
||||
changedNotifier *utils.ChangeNotifierManager
|
||||
removedNotifier *utils.ChangeNotifierManager
|
||||
tracks map[livekit.TrackID][]*TrackInfo
|
||||
dataTracks map[livekit.TrackID][]*DataTrackInfo
|
||||
}
|
||||
|
||||
type TrackInfo struct {
|
||||
@@ -39,9 +40,16 @@ type TrackInfo struct {
|
||||
PublisherID livekit.ParticipantID
|
||||
}
|
||||
|
||||
type DataTrackInfo struct {
|
||||
DataTrack types.DataTrack
|
||||
PublisherIdentity livekit.ParticipantIdentity
|
||||
PublisherID livekit.ParticipantID
|
||||
}
|
||||
|
||||
func NewRoomTrackManager() *RoomTrackManager {
|
||||
return &RoomTrackManager{
|
||||
tracks: make(map[livekit.TrackID][]*TrackInfo),
|
||||
dataTracks: make(map[livekit.TrackID][]*DataTrackInfo),
|
||||
changedNotifier: utils.NewChangeNotifierManager(),
|
||||
removedNotifier: utils.NewChangeNotifierManager(),
|
||||
}
|
||||
@@ -145,3 +153,65 @@ func (r *RoomTrackManager) GetOrCreateTrackChangeNotifier(trackID livekit.TrackI
|
||||
func (r *RoomTrackManager) GetOrCreateTrackRemoveNotifier(trackID livekit.TrackID) *utils.ChangeNotifier {
|
||||
return r.removedNotifier.GetOrCreateNotifier(string(trackID))
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) AddDataTrack(dataTrack types.DataTrack, publisherIdentity livekit.ParticipantIdentity, publisherID livekit.ParticipantID) {
|
||||
trackID := dataTrack.ID()
|
||||
r.lock.Lock()
|
||||
r.dataTracks[trackID] = append(r.dataTracks[trackID], &DataTrackInfo{
|
||||
DataTrack: dataTrack,
|
||||
PublisherIdentity: publisherIdentity,
|
||||
PublisherID: publisherID,
|
||||
})
|
||||
r.lock.Unlock()
|
||||
|
||||
r.NotifyTrackChanged(trackID)
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) RemoveDataTrack(dataTrack types.DataTrack) {
|
||||
trackID := dataTrack.ID()
|
||||
r.lock.Lock()
|
||||
// ensure we are removing the same track as added
|
||||
infos, ok := r.dataTracks[trackID]
|
||||
if !ok {
|
||||
r.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
found := false
|
||||
for idx, info := range infos {
|
||||
if info.DataTrack == dataTrack {
|
||||
r.dataTracks[trackID] = slices.Delete(r.dataTracks[trackID], idx, idx+1)
|
||||
if len(r.dataTracks[trackID]) == 0 {
|
||||
delete(r.dataTracks, trackID)
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
r.lock.Unlock()
|
||||
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
n := r.removedNotifier.GetNotifier(string(trackID))
|
||||
if n != nil {
|
||||
n.NotifyChanged()
|
||||
}
|
||||
|
||||
r.changedNotifier.RemoveNotifier(string(trackID), true)
|
||||
r.removedNotifier.RemoveNotifier(string(trackID), true)
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) GetDataTrackInfo(trackID livekit.TrackID) *DataTrackInfo {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
infos := r.dataTracks[trackID]
|
||||
if len(infos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// earliest added data track is used till it is removed
|
||||
return infos[0]
|
||||
}
|
||||
@@ -59,4 +59,7 @@ type ParticipantSignalling interface {
|
||||
SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message
|
||||
SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message
|
||||
SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message
|
||||
SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message
|
||||
SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message
|
||||
SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message
|
||||
}
|
||||
@@ -142,6 +142,15 @@ func (s *signalhandler) HandleMessage(msg proto.Message) error {
|
||||
if err := s.params.Participant.UpdateVideoTrack(msg.UpdateVideoTrack); err != nil {
|
||||
s.params.Logger.Warnw("could not update video track", err, "update", msg.UpdateVideoTrack)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_PublishDataTrackRequest:
|
||||
s.params.Participant.HandlePublishDataTrackRequest(msg.PublishDataTrackRequest)
|
||||
|
||||
case *livekit.SignalRequest_UnpublishDataTrackRequest:
|
||||
s.params.Participant.HandleUnpublishDataTrackRequest(msg.UnpublishDataTrackRequest)
|
||||
|
||||
case *livekit.SignalRequest_UpdateDataSubscription:
|
||||
s.params.Participant.HandleUpdateDataSubscription(msg.UpdateDataSubscription)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -236,3 +236,27 @@ func (s *signalling) SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u *signalling) SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_PublishDataTrackResponse{
|
||||
PublishDataTrackResponse: publishDataTrackResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u *signalling) SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_UnpublishDataTrackResponse{
|
||||
UnpublishDataTrackResponse: unpublishDataTrackResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u *signalling) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_DataTrackSubscriberHandles{
|
||||
DataTrackSubscriberHandles: dataTrackSubscriberHandles,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -115,3 +115,15 @@ func (u *signallingUnimplemented) SignalMediaSectionsRequirement(mediaSectionsRe
|
||||
func (u *signallingUnimplemented) SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
|
||||
return nil
|
||||
}
|
||||
+611
-264
File diff suppressed because it is too large.
Load diff
@@ -197,15 +197,17 @@ func TestUnsubscribe(t *testing.T) {
|
||||
|
||||
resolver := newTestResolver(true, true, "pub", "pubID")
|
||||
|
||||
s := &trackSubscription{
|
||||
trackID: "track",
|
||||
desired: true,
|
||||
subscriberID: sm.params.Participant.ID(),
|
||||
publisherID: "pubID",
|
||||
publisherIdentity: "pub",
|
||||
hasPermission: true,
|
||||
bound: true,
|
||||
logger: logger.GetLogger(),
|
||||
s := &mediaTrackSubscription{
|
||||
trackSubscription: trackSubscription{
|
||||
trackID: "track",
|
||||
desired: true,
|
||||
subscriberID: sm.params.Participant.ID(),
|
||||
publisherID: "pubID",
|
||||
publisherIdentity: "pub",
|
||||
logger: logger.GetLogger(),
|
||||
},
|
||||
hasPermission: true,
|
||||
bound: true,
|
||||
}
|
||||
// a bunch of unfortunate manual wiring
|
||||
res := resolver.Resolve(nil, s.trackID)
|
||||
|
||||
@@ -59,7 +59,7 @@ func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro
|
||||
}
|
||||
})
|
||||
updateTrack := func() {
|
||||
var f func(participant types.LocalParticipant, track types.MediaTrack)
|
||||
var f func(participant types.Participant, track types.MediaTrack)
|
||||
if p.OnTrackUpdatedCallCount() > 0 {
|
||||
f = p.OnTrackUpdatedArgsForCall(p.OnTrackUpdatedCallCount() - 1)
|
||||
}
|
||||
|
||||
+82
-24
@@ -19,6 +19,7 @@ import (
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -63,8 +64,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
LossyDataChannel = "_lossy"
|
||||
ReliableDataChannel = "_reliable"
|
||||
LossyDataChannel = "_lossy"
|
||||
ReliableDataChannel = "_reliable"
|
||||
DataTrackDataChannel = "_data_track"
|
||||
|
||||
fastNegotiationFrequency = 10 * time.Millisecond
|
||||
negotiationFrequency = 150 * time.Millisecond
|
||||
@@ -205,6 +207,7 @@ type PCTransport struct {
|
||||
reliableDCOpened bool
|
||||
lossyDC *datachannel.DataChannelWriter[*webrtc.DataChannel]
|
||||
lossyDCOpened bool
|
||||
dataTrackDC *datachannel.DataChannelWriter[*webrtc.DataChannel]
|
||||
unlabeledDataChannels []*datachannel.DataChannelWriter[*webrtc.DataChannel]
|
||||
|
||||
iceStartedAt time.Time
|
||||
@@ -305,6 +308,8 @@ type TransportParams struct {
|
||||
|
||||
// for development test
|
||||
DatachannelMaxReceiverBufferSize int
|
||||
|
||||
EnableDataTracks bool
|
||||
}
|
||||
|
||||
func newPeerConnection(params TransportParams, onBandwidthEstimator func(estimator cc.BandwidthEstimator)) (*webrtc.PeerConnection, *webrtc.MediaEngine, error) {
|
||||
@@ -399,12 +404,7 @@ func newPeerConnection(params TransportParams, onBandwidthEstimator func(estimat
|
||||
return true
|
||||
}
|
||||
ipstr := ip.String()
|
||||
for _, inc := range includeIps {
|
||||
if inc == ipstr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(includeIps, ipstr)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -657,13 +657,7 @@ func (t *PCTransport) setICEConnectedAt(at time.Time) {
|
||||
|
||||
// set failure timer for dtls handshake
|
||||
iceDuration := at.Sub(t.iceStartedAt)
|
||||
connTimeoutAfterICE := minConnectTimeoutAfterICE
|
||||
if connTimeoutAfterICE < 3*iceDuration {
|
||||
connTimeoutAfterICE = 3 * iceDuration
|
||||
}
|
||||
if connTimeoutAfterICE > maxConnectTimeoutAfterICE {
|
||||
connTimeoutAfterICE = maxConnectTimeoutAfterICE
|
||||
}
|
||||
connTimeoutAfterICE := min(max(minConnectTimeoutAfterICE, 3*iceDuration), maxConnectTimeoutAfterICE)
|
||||
t.params.Logger.Debugw("setting connection timer after ICE connected", "timeout", connTimeoutAfterICE, "iceDuration", iceDuration)
|
||||
t.connectAfterICETimer = time.AfterFunc(connTimeoutAfterICE, func() {
|
||||
state := t.pc.ConnectionState()
|
||||
@@ -830,6 +824,7 @@ func (t *PCTransport) onDataChannel(dc *webrtc.DataChannel) {
|
||||
dc.OnOpen(func() {
|
||||
t.params.Logger.Debugw(dc.Label() + " data channel open")
|
||||
var kind livekit.DataPacket_Kind
|
||||
var isDataTrack bool
|
||||
var isUnlabeled bool
|
||||
switch dc.Label() {
|
||||
case ReliableDataChannel:
|
||||
@@ -838,6 +833,9 @@ func (t *PCTransport) onDataChannel(dc *webrtc.DataChannel) {
|
||||
case LossyDataChannel:
|
||||
kind = livekit.DataPacket_LOSSY
|
||||
|
||||
case DataTrackDataChannel:
|
||||
isDataTrack = true
|
||||
|
||||
default:
|
||||
t.params.Logger.Infow("unlabeled datachannel added", "label", dc.Label())
|
||||
isUnlabeled = true
|
||||
@@ -849,32 +847,45 @@ func (t *PCTransport) onDataChannel(dc *webrtc.DataChannel) {
|
||||
return
|
||||
}
|
||||
|
||||
isHandled := true
|
||||
t.lock.Lock()
|
||||
switch {
|
||||
case isUnlabeled:
|
||||
t.lock.Lock()
|
||||
t.unlabeledDataChannels = append(
|
||||
t.unlabeledDataChannels,
|
||||
datachannel.NewDataChannelWriterReliable(dc, rawDC, t.params.DatachannelSlowThreshold),
|
||||
)
|
||||
t.lock.Unlock()
|
||||
|
||||
case isDataTrack:
|
||||
if !t.params.EnableDataTracks {
|
||||
t.params.Logger.Infow("data tracks not enabled")
|
||||
isHandled = false
|
||||
} else {
|
||||
if t.dataTrackDC != nil {
|
||||
t.dataTrackDC.Close()
|
||||
}
|
||||
t.dataTrackDC = datachannel.NewDataChannelWriterUnreliable(dc, rawDC, 0, 0)
|
||||
}
|
||||
|
||||
case kind == livekit.DataPacket_RELIABLE:
|
||||
t.lock.Lock()
|
||||
if t.reliableDC != nil {
|
||||
t.reliableDC.Close()
|
||||
}
|
||||
t.reliableDC = datachannel.NewDataChannelWriterReliable(dc, rawDC, t.params.DatachannelSlowThreshold)
|
||||
t.reliableDCOpened = true
|
||||
t.lock.Unlock()
|
||||
|
||||
case kind == livekit.DataPacket_LOSSY:
|
||||
t.lock.Lock()
|
||||
if t.lossyDC != nil {
|
||||
t.lossyDC.Close()
|
||||
}
|
||||
t.lossyDC = datachannel.NewDataChannelWriterUnreliable(dc, rawDC, t.params.DatachannelLossyTargetLatency, uint64(lossyDataChannelMinBufferedAmount))
|
||||
t.lossyDCOpened = true
|
||||
t.lock.Unlock()
|
||||
}
|
||||
t.lock.Unlock()
|
||||
|
||||
if !isHandled {
|
||||
rawDC.Close()
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -893,6 +904,9 @@ func (t *PCTransport) onDataChannel(dc *webrtc.DataChannel) {
|
||||
case isUnlabeled:
|
||||
t.params.Handler.OnDataMessageUnlabeled(buffer[:n])
|
||||
|
||||
case isDataTrack:
|
||||
t.params.Handler.OnDataTrackMessage(buffer[:n])
|
||||
|
||||
default:
|
||||
t.params.Handler.OnDataMessage(kind, buffer[:n])
|
||||
}
|
||||
@@ -1159,6 +1173,11 @@ func (t *PCTransport) getNumUnmatchedTransceivers() (uint32, uint32) {
|
||||
}
|
||||
|
||||
func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelInit) error {
|
||||
if label == DataTrackDataChannel && !t.params.EnableDataTracks {
|
||||
t.params.Logger.Infow("data tracks not enabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
dc, err := t.pc.CreateDataChannel(label, dci)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1166,6 +1185,7 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni
|
||||
var (
|
||||
dcPtr **datachannel.DataChannelWriter[*webrtc.DataChannel]
|
||||
dcReady *bool
|
||||
isDataTrack bool
|
||||
isUnlabeled bool
|
||||
kind livekit.DataPacket_Kind
|
||||
)
|
||||
@@ -1173,14 +1193,20 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni
|
||||
default:
|
||||
isUnlabeled = true
|
||||
t.params.Logger.Infow("unlabeled datachannel added", "label", dc.Label())
|
||||
|
||||
case ReliableDataChannel:
|
||||
dcPtr = &t.reliableDC
|
||||
dcReady = &t.reliableDCOpened
|
||||
kind = livekit.DataPacket_RELIABLE
|
||||
|
||||
case LossyDataChannel:
|
||||
dcPtr = &t.lossyDC
|
||||
dcReady = &t.lossyDCOpened
|
||||
kind = livekit.DataPacket_LOSSY
|
||||
|
||||
case DataTrackDataChannel:
|
||||
dcPtr = &t.dataTrackDC
|
||||
isDataTrack = true
|
||||
}
|
||||
|
||||
dc.OnOpen(func() {
|
||||
@@ -1205,8 +1231,17 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni
|
||||
if *dcPtr != nil {
|
||||
(*dcPtr).Close()
|
||||
}
|
||||
*dcPtr = datachannel.NewDataChannelWriterReliable(dc, rawDC, slowThreshold)
|
||||
*dcReady = true
|
||||
switch {
|
||||
case dcPtr == &t.reliableDC:
|
||||
*dcPtr = datachannel.NewDataChannelWriterReliable(dc, rawDC, slowThreshold)
|
||||
case dcPtr == &t.lossyDC:
|
||||
*dcPtr = datachannel.NewDataChannelWriterUnreliable(dc, rawDC, t.params.DatachannelLossyTargetLatency, uint64(lossyDataChannelMinBufferedAmount))
|
||||
case dcPtr == &t.dataTrackDC:
|
||||
*dcPtr = datachannel.NewDataChannelWriterUnreliable(dc, rawDC, 0, 0)
|
||||
}
|
||||
if dcReady != nil {
|
||||
*dcReady = true
|
||||
}
|
||||
}
|
||||
t.lock.Unlock()
|
||||
t.params.Logger.Debugw(dc.Label() + " data channel open")
|
||||
@@ -1227,6 +1262,9 @@ func (t *PCTransport) CreateDataChannel(label string, dci *webrtc.DataChannelIni
|
||||
case isUnlabeled:
|
||||
t.params.Handler.OnDataMessageUnlabeled(buffer[:n])
|
||||
|
||||
case isDataTrack:
|
||||
t.params.Handler.OnDataTrackMessage(buffer[:n])
|
||||
|
||||
default:
|
||||
t.params.Handler.OnDataMessage(kind, buffer[:n])
|
||||
}
|
||||
@@ -1281,6 +1319,12 @@ func (t *PCTransport) CreateReadableDataChannel(label string, dci *webrtc.DataCh
|
||||
}
|
||||
|
||||
func (t *PCTransport) CreateDataChannelIfEmpty(dcLabel string, dci *webrtc.DataChannelInit) (label string, id uint16, existing bool, err error) {
|
||||
if dcLabel == DataTrackDataChannel && !t.params.EnableDataTracks {
|
||||
t.params.Logger.Infow("data tracks not enabled")
|
||||
err = errors.New("data tracks not enabled")
|
||||
return
|
||||
}
|
||||
|
||||
t.lock.RLock()
|
||||
var dcw *datachannel.DataChannelWriter[*webrtc.DataChannel]
|
||||
switch dcLabel {
|
||||
@@ -1288,6 +1332,8 @@ func (t *PCTransport) CreateDataChannelIfEmpty(dcLabel string, dci *webrtc.DataC
|
||||
dcw = t.reliableDC
|
||||
case LossyDataChannel:
|
||||
dcw = t.lossyDC
|
||||
case DataTrackDataChannel:
|
||||
dcw = t.dataTrackDC
|
||||
default:
|
||||
t.params.Logger.Warnw("unknown data channel label", nil, "label", label)
|
||||
err = errors.New("unknown data channel label")
|
||||
@@ -1415,6 +1461,14 @@ func (t *PCTransport) SendDataMessageUnlabeled(data []byte, useRaw bool, sender
|
||||
return t.sendDataMessage(dc, data)
|
||||
}
|
||||
|
||||
func (t *PCTransport) SendDataTrackMessage(data []byte) error {
|
||||
t.lock.RLock()
|
||||
dc := t.dataTrackDC
|
||||
t.lock.RUnlock()
|
||||
|
||||
return t.sendDataMessage(dc, data)
|
||||
}
|
||||
|
||||
func (t *PCTransport) sendDataMessage(dc *datachannel.DataChannelWriter[*webrtc.DataChannel], data []byte) error {
|
||||
if dc == nil {
|
||||
return ErrDataChannelUnavailable
|
||||
@@ -1428,7 +1482,6 @@ func (t *PCTransport) sendDataMessage(dc *datachannel.DataChannelWriter[*webrtc.
|
||||
return ErrDataChannelBufferFull
|
||||
}
|
||||
_, err := dc.Write(data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1466,6 +1519,11 @@ func (t *PCTransport) Close() {
|
||||
t.lossyDC = nil
|
||||
}
|
||||
|
||||
if t.dataTrackDC != nil {
|
||||
t.dataTrackDC.Close()
|
||||
t.dataTrackDC = nil
|
||||
}
|
||||
|
||||
for _, dc := range t.unlabeledDataChannels {
|
||||
dc.Close()
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type Handler interface {
|
||||
OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver)
|
||||
OnDataMessage(kind livekit.DataPacket_Kind, data []byte)
|
||||
OnDataMessageUnlabeled(data []byte)
|
||||
OnDataTrackMessage(data []byte)
|
||||
OnDataSendError(err error)
|
||||
OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error
|
||||
OnSetRemoteDescriptionOffer()
|
||||
@@ -62,6 +63,7 @@ func (h UnimplementedHandler) OnFailed(isShortLived bool)
|
||||
func (h UnimplementedHandler) OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver) {}
|
||||
func (h UnimplementedHandler) OnDataMessage(kind livekit.DataPacket_Kind, data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataMessageUnlabeled(data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataTrackMessage(data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataSendError(err error) {}
|
||||
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error {
|
||||
return ErrNoOfferHandler
|
||||
|
||||
@@ -41,6 +41,11 @@ type FakeHandler struct {
|
||||
onDataSendErrorArgsForCall []struct {
|
||||
arg1 error
|
||||
}
|
||||
OnDataTrackMessageStub func([]byte)
|
||||
onDataTrackMessageMutex sync.RWMutex
|
||||
onDataTrackMessageArgsForCall []struct {
|
||||
arg1 []byte
|
||||
}
|
||||
OnFailedStub func(bool, *types.ICEConnectionInfo)
|
||||
onFailedMutex sync.RWMutex
|
||||
onFailedArgsForCall []struct {
|
||||
@@ -296,6 +301,43 @@ func (fake *FakeHandler) OnDataSendErrorArgsForCall(i int) error {
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessage(arg1 []byte) {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
|
||||
arg1 []byte
|
||||
}{arg1Copy})
|
||||
stub := fake.OnDataTrackMessageStub
|
||||
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1Copy})
|
||||
fake.onDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackMessageStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageCallCount() int {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.onDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageCalls(stub func([]byte)) {
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
defer fake.onDataTrackMessageMutex.Unlock()
|
||||
fake.OnDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageArgsForCall(i int) []byte {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnFailed(arg1 bool, arg2 *types.ICEConnectionInfo) {
|
||||
fake.onFailedMutex.Lock()
|
||||
fake.onFailedArgsForCall = append(fake.onFailedArgsForCall, struct {
|
||||
|
||||
+46
-13
@@ -97,6 +97,7 @@ type TransportManagerParams struct {
|
||||
DataChannelStats *telemetry.BytesTrackStats
|
||||
UseOneShotSignallingMode bool
|
||||
FireOnTrackBySdp bool
|
||||
EnableDataTracks bool
|
||||
}
|
||||
|
||||
type TransportManager struct {
|
||||
@@ -157,6 +158,7 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
|
||||
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
|
||||
DatachannelLossyTargetLatency: params.DatachannelLossyTargetLatency,
|
||||
FireOnTrackBySdp: params.FireOnTrackBySdp,
|
||||
EnableDataTracks: params.EnableDataTracks,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -182,6 +184,7 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
|
||||
Transport: livekit.SignalTarget_SUBSCRIBER,
|
||||
Handler: TransportManagerTransportHandler{params.SubscriberHandler, t, lgr},
|
||||
FireOnTrackBySdp: params.FireOnTrackBySdp,
|
||||
EnableDataTracks: params.EnableDataTracks,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -386,8 +389,8 @@ func (t *TransportManager) handleSendDataResult(err error, kind string, size int
|
||||
|
||||
func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels []*livekit.DataChannelInfo) error {
|
||||
var (
|
||||
reliableID, lossyID uint16
|
||||
reliableIDPtr, lossyIDPtr *uint16
|
||||
reliableID, lossyID, dataTrackID uint16
|
||||
reliableIDPtr, lossyIDPtr, dataTrackIDPtr *uint16
|
||||
)
|
||||
|
||||
//
|
||||
@@ -398,13 +401,17 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
|
||||
// For new version migration clients, we create data channels with new ID and negotiate with client
|
||||
//
|
||||
for _, dc := range pendingDataChannels {
|
||||
if dc.Label == ReliableDataChannel {
|
||||
// pion use step 2 for auto generated ID, so we need to add 4 to avoid conflict
|
||||
reliableID = uint16(dc.Id) + 4
|
||||
switch dc.Label {
|
||||
case ReliableDataChannel:
|
||||
// pion use step 2 for auto generated ID, so we need to add 6 to avoid conflict
|
||||
reliableID = uint16(dc.Id) + 6
|
||||
reliableIDPtr = &reliableID
|
||||
} else if dc.Label == LossyDataChannel {
|
||||
lossyID = uint16(dc.Id) + 4
|
||||
case LossyDataChannel:
|
||||
lossyID = uint16(dc.Id) + 6
|
||||
lossyIDPtr = &lossyID
|
||||
case DataTrackDataChannel:
|
||||
dataTrackID = uint16(dc.Id) + 6
|
||||
dataTrackIDPtr = &dataTrackID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +436,17 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
negotiated = t.params.Migration && dataTrackIDPtr == nil
|
||||
if err := t.subscriber.CreateDataChannel(DataTrackDataChannel, &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &retransmits,
|
||||
ID: dataTrackIDPtr,
|
||||
Negotiated: &negotiated,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -669,6 +687,10 @@ func (t *TransportManager) GetICEConnectionInfo() []*types.ICEConnectionInfo {
|
||||
return infos
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetDataTrackTransport() types.DataTrackTransport {
|
||||
return t.getTransport(true)
|
||||
}
|
||||
|
||||
func (t *TransportManager) getTransport(isPrimary bool) *PCTransport {
|
||||
switch {
|
||||
case t.publisher == nil:
|
||||
@@ -860,7 +882,15 @@ func (t *TransportManager) ProcessPendingPublisherDataChannels() {
|
||||
dcExisting bool
|
||||
err error
|
||||
)
|
||||
if ci.Label == LossyDataChannel {
|
||||
switch ci.Label {
|
||||
case ReliableDataChannel:
|
||||
id := uint16(ci.GetId())
|
||||
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(ReliableDataChannel, &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
Negotiated: &negotiated,
|
||||
ID: &id,
|
||||
})
|
||||
case LossyDataChannel:
|
||||
ordered = false
|
||||
retransmits := uint16(0)
|
||||
id := uint16(ci.GetId())
|
||||
@@ -870,12 +900,15 @@ func (t *TransportManager) ProcessPendingPublisherDataChannels() {
|
||||
Negotiated: &negotiated,
|
||||
ID: &id,
|
||||
})
|
||||
} else if ci.Label == ReliableDataChannel {
|
||||
case DataTrackDataChannel:
|
||||
ordered = false
|
||||
retransmits := uint16(0)
|
||||
id := uint16(ci.GetId())
|
||||
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(ReliableDataChannel, &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
Negotiated: &negotiated,
|
||||
ID: &id,
|
||||
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(DataTrackDataChannel, &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &retransmits,
|
||||
Negotiated: &negotiated,
|
||||
ID: &id,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
+91
-15
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/livekit/protocol/utils"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
@@ -286,15 +287,22 @@ type Participant interface {
|
||||
IsDependent() bool
|
||||
IsAgent() bool
|
||||
|
||||
GetLogger() logger.Logger
|
||||
|
||||
CanSkipBroadcast() bool
|
||||
Version() utils.TimedVersion
|
||||
ToProto() *livekit.ParticipantInfo
|
||||
ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion)
|
||||
|
||||
IsPublisher() bool
|
||||
GetPublishedTrack(trackID livekit.TrackID) MediaTrack
|
||||
GetPublishedTracks() []MediaTrack
|
||||
RemovePublishedTrack(track MediaTrack, isExpectedToResume bool)
|
||||
|
||||
GetPublishedDataTracks() []DataTrack
|
||||
GetPublishedDataTrack(handle uint16) DataTrack
|
||||
RemovePublishedDataTrack(track DataTrack)
|
||||
|
||||
GetAudioLevel() (smoothedLevel float64, active bool)
|
||||
|
||||
// HasPermission checks permission of the subscriber by identity. Returns true if subscriber is allowed to subscribe
|
||||
@@ -304,7 +312,11 @@ type Participant interface {
|
||||
// permissions
|
||||
Hidden() bool
|
||||
|
||||
MigrateState() MigrateState
|
||||
|
||||
Close(sendLeave bool, reason ParticipantCloseReason, isExpectedToResume bool) error
|
||||
IsClosed() bool
|
||||
IsDisconnected() bool
|
||||
|
||||
SubscriptionPermission() (*livekit.SubscriptionPermission, utils.TimedVersion)
|
||||
|
||||
@@ -315,9 +327,19 @@ type Participant interface {
|
||||
resolverBySid func(participantID livekit.ParticipantID) LocalParticipant,
|
||||
) error
|
||||
|
||||
DebugInfo() map[string]interface{}
|
||||
DebugInfo() map[string]any
|
||||
|
||||
// OnTrackPublished - remote added a track
|
||||
OnTrackPublished(func(Participant, MediaTrack))
|
||||
// OnTrackUpdated - one of its publishedTracks changed in status
|
||||
OnTrackUpdated(callback func(Participant, MediaTrack))
|
||||
// OnTrackUnpublished - a track was unpublished
|
||||
OnTrackUnpublished(callback func(Participant, MediaTrack))
|
||||
OnDataTrackPublished(func(Participant, DataTrack))
|
||||
OnDataTrackUnpublished(func(Participant, DataTrack))
|
||||
OnMetrics(callback func(Participant, *livekit.DataPacket))
|
||||
|
||||
HandleReceivedDataTrackMessage([]byte, *datatrack.Packet)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@@ -343,6 +365,7 @@ type DataMessageCache struct {
|
||||
//counterfeiter:generate . LocalParticipantHelper
|
||||
type LocalParticipantHelper interface {
|
||||
ResolveMediaTrack(LocalParticipant, livekit.TrackID) MediaResolverResult
|
||||
ResolveDataTrack(LocalParticipant, livekit.TrackID) DataResolverResult
|
||||
GetParticipantInfo(pID livekit.ParticipantID) *livekit.ParticipantInfo
|
||||
GetRegionSettings(ip string) *livekit.RegionSettings
|
||||
GetSubscriberForwarderState(p LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)
|
||||
@@ -355,12 +378,10 @@ type LocalParticipant interface {
|
||||
Participant
|
||||
|
||||
TelemetryGuard() *telemetry.ReferenceGuard
|
||||
ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion)
|
||||
|
||||
// getters
|
||||
GetCountry() string
|
||||
GetTrailer() []byte
|
||||
GetLogger() logger.Logger
|
||||
GetLoggerResolver() logger.DeferredFieldResolver
|
||||
GetReporter() roomobs.ParticipantSessionReporter
|
||||
GetReporterResolver() roomobs.ParticipantReporterResolver
|
||||
@@ -369,9 +390,7 @@ type LocalParticipant interface {
|
||||
SupportsSyncStreamID() bool
|
||||
SupportsTransceiverReuse() bool
|
||||
IsUsingSinglePeerConnection() bool
|
||||
IsClosed() bool
|
||||
IsReady() bool
|
||||
IsDisconnected() bool
|
||||
Disconnected() <-chan struct{}
|
||||
IsIdle() bool
|
||||
SubscriberAsPrimary() bool
|
||||
@@ -434,6 +453,9 @@ type LocalParticipant interface {
|
||||
UpdateSubscribedTrackSettings(trackID livekit.TrackID, settings *livekit.UpdateTrackSettings)
|
||||
GetSubscribedTracks() []SubscribedTrack
|
||||
IsTrackNameSubscribed(publisherIdentity livekit.ParticipantIdentity, trackName string) bool
|
||||
SubscribeToDataTrack(trackID livekit.TrackID)
|
||||
UnsubscribeFromDataTrack(trackID livekit.TrackID)
|
||||
UpdateDataTrackSubscriptionOptions(trackID livekit.TrackID, subscriptionOptions *livekit.DataTrackSubscriptionOptions)
|
||||
Verify() bool
|
||||
VerifySubscribeParticipantInfo(pID livekit.ParticipantID, version uint32)
|
||||
// WaitUntilSubscribed waits until all subscriptions have been settled, or if the timeout
|
||||
@@ -458,25 +480,19 @@ type LocalParticipant interface {
|
||||
SendConnectionQualityUpdate(update *livekit.ConnectionQualityUpdate) error
|
||||
SendSubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool) error
|
||||
SendRefreshToken(token string) error
|
||||
// RAJA-REMOVE SendRequestResponse(requestResponse *livekit.RequestResponse) error
|
||||
HandleReconnectAndSendResponse(reconnectReason livekit.ReconnectReason, reconnectResponse *livekit.ReconnectResponse) error
|
||||
IssueFullReconnect(reason ParticipantCloseReason)
|
||||
SendRoomMovedResponse(moved *livekit.RoomMovedResponse) error
|
||||
SendDataTrackSubscriberHandles(handles map[uint32]*livekit.DataTrackSubscriberHandles_PublishedDataTrack) error
|
||||
|
||||
// callbacks
|
||||
OnStateChange(func(p LocalParticipant))
|
||||
OnSubscriberReady(callback func(LocalParticipant))
|
||||
OnMigrateStateChange(func(p LocalParticipant, migrateState MigrateState))
|
||||
// OnTrackPublished - remote added a track
|
||||
OnTrackPublished(func(LocalParticipant, MediaTrack))
|
||||
// OnTrackUpdated - one of its publishedTracks changed in status
|
||||
OnTrackUpdated(callback func(LocalParticipant, MediaTrack))
|
||||
// OnTrackUnpublished - a track was unpublished
|
||||
OnTrackUnpublished(callback func(LocalParticipant, MediaTrack))
|
||||
// OnParticipantUpdate - metadata or permission is updated
|
||||
OnParticipantUpdate(callback func(LocalParticipant))
|
||||
OnDataPacket(callback func(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket))
|
||||
OnDataMessage(callback func(LocalParticipant, []byte))
|
||||
OnDataTrackMessage(callback func(LocalParticipant, []byte, *datatrack.Packet))
|
||||
OnSubscribeStatusChanged(fn func(publisherID livekit.ParticipantID, subscribed bool))
|
||||
AddOnClose(key string, callback func(LocalParticipant))
|
||||
OnClaimsChanged(callback func(LocalParticipant))
|
||||
@@ -487,6 +503,7 @@ type LocalParticipant interface {
|
||||
bool,
|
||||
))
|
||||
OnUpdateSubscriptionPermission(func(LocalParticipant, *livekit.SubscriptionPermission) error)
|
||||
OnUpdateDataSubscriptions(func(LocalParticipant, *livekit.UpdateDataSubscription))
|
||||
OnSyncState(func(LocalParticipant, *livekit.SyncState) error)
|
||||
OnSimulateScenario(func(LocalParticipant, *livekit.SimulateScenario) error)
|
||||
OnLeave(func(LocalParticipant, ParticipantCloseReason))
|
||||
@@ -497,13 +514,13 @@ type LocalParticipant interface {
|
||||
MaybeStartMigration(force bool, onStart func()) bool
|
||||
NotifyMigration()
|
||||
SetMigrateState(s MigrateState)
|
||||
MigrateState() MigrateState
|
||||
SetMigrateInfo(
|
||||
previousOffer *webrtc.SessionDescription,
|
||||
previousAnswer *webrtc.SessionDescription,
|
||||
mediaTracks []*livekit.TrackPublishedResponse,
|
||||
dataChannels []*livekit.DataChannelInfo,
|
||||
dataChannelReceiveState []*livekit.DataChannelReceiveState,
|
||||
dataTracks []*livekit.PublishDataTrackResponse,
|
||||
)
|
||||
IsReconnect() bool
|
||||
MoveToRoom(params MoveToRoomParams)
|
||||
@@ -542,9 +559,15 @@ type LocalParticipant interface {
|
||||
HandleSimulateScenario(*livekit.SimulateScenario) error
|
||||
HandleLeaveRequest(reason ParticipantCloseReason)
|
||||
|
||||
HandlePublishDataTrackRequest(*livekit.PublishDataTrackRequest)
|
||||
HandleUnpublishDataTrackRequest(*livekit.UnpublishDataTrackRequest)
|
||||
HandleUpdateDataSubscription(*livekit.UpdateDataSubscription)
|
||||
|
||||
HandleSignalMessage(msg proto.Message) error
|
||||
|
||||
PerformRpc(req *livekit.PerformRpcRequest, resultCh chan string, errorCh chan error)
|
||||
|
||||
GetDataTrackTransport() DataTrackTransport
|
||||
}
|
||||
|
||||
// Room is a container of participants, and can provide room-level actions
|
||||
@@ -561,6 +584,7 @@ type Room interface {
|
||||
subscribe bool,
|
||||
)
|
||||
ResolveMediaTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) MediaResolverResult
|
||||
ResolveDataTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) DataResolverResult
|
||||
GetLocalParticipants() []LocalParticipant
|
||||
IsDataMessageUserPacketDuplicate(ip *livekit.UserPacket) bool
|
||||
}
|
||||
@@ -637,6 +661,47 @@ type LocalMediaTrack interface {
|
||||
NotifySubscriberNodeMediaLoss(nodeID livekit.NodeID, fractionalLoss uint8)
|
||||
}
|
||||
|
||||
// DataTrack represents a data track
|
||||
//
|
||||
//counterfeiter:generate . DataTrack
|
||||
type DataTrack interface {
|
||||
ID() livekit.TrackID
|
||||
PubHandle() uint16
|
||||
Name() string
|
||||
ToProto() *livekit.DataTrackInfo
|
||||
|
||||
AddSubscriber(sub LocalParticipant) (DataDownTrack, error)
|
||||
RemoveSubscriber(participantID livekit.ParticipantID)
|
||||
IsSubscriber(subID livekit.ParticipantID) bool
|
||||
|
||||
AddDataDownTrack(sender DataTrackSender) error
|
||||
DeleteDataDownTrack(subscriberID livekit.ParticipantID)
|
||||
|
||||
HandlePacket(data []byte, packet *datatrack.Packet)
|
||||
|
||||
Close()
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataDownTrack
|
||||
type DataDownTrack interface {
|
||||
Handle() uint16
|
||||
PublishDataTrack() DataTrack
|
||||
|
||||
UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions)
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataTrackSender
|
||||
type DataTrackSender interface {
|
||||
SubscriberID() livekit.ParticipantID
|
||||
|
||||
WritePacket(data []byte, packet *datatrack.Packet)
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataTrackTransport
|
||||
type DataTrackTransport interface {
|
||||
SendDataTrackMessage(data []byte) error
|
||||
}
|
||||
|
||||
//counterfeiter:generate . SubscribedTrack
|
||||
type SubscribedTrack interface {
|
||||
AddOnBind(f func(error))
|
||||
@@ -678,9 +743,20 @@ type MediaResolverResult struct {
|
||||
PublisherIdentity livekit.ParticipantIdentity
|
||||
}
|
||||
|
||||
type DataResolverResult struct {
|
||||
TrackChangedNotifier ChangeNotifier
|
||||
TrackRemovedNotifier ChangeNotifier
|
||||
DataTrack DataTrack
|
||||
PublisherID livekit.ParticipantID
|
||||
PublisherIdentity livekit.ParticipantIdentity
|
||||
}
|
||||
|
||||
// MediaTrackResolver locates a specific media track for a subscriber
|
||||
type MediaTrackResolver func(LocalParticipant, livekit.TrackID) MediaResolverResult
|
||||
|
||||
// DataTrackResolver locates a specific data track for a subscriber
|
||||
type DataTrackResolver func(LocalParticipant, livekit.TrackID) DataResolverResult
|
||||
|
||||
// Supervisor/operation monitor related definitions
|
||||
type OperationMonitorEvent int
|
||||
|
||||
@@ -709,7 +785,7 @@ func (o OperationMonitorEvent) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
type OperationMonitorData interface{}
|
||||
type OperationMonitorData any
|
||||
|
||||
type OperationMonitor interface {
|
||||
PostEvent(ome OperationMonitorEvent, omd OperationMonitorData)
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package typesfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type FakeDataDownTrack struct {
|
||||
HandleStub func() uint16
|
||||
handleMutex sync.RWMutex
|
||||
handleArgsForCall []struct {
|
||||
}
|
||||
handleReturns struct {
|
||||
result1 uint16
|
||||
}
|
||||
handleReturnsOnCall map[int]struct {
|
||||
result1 uint16
|
||||
}
|
||||
PublishDataTrackStub func() types.DataTrack
|
||||
publishDataTrackMutex sync.RWMutex
|
||||
publishDataTrackArgsForCall []struct {
|
||||
}
|
||||
publishDataTrackReturns struct {
|
||||
result1 types.DataTrack
|
||||
}
|
||||
publishDataTrackReturnsOnCall map[int]struct {
|
||||
result1 types.DataTrack
|
||||
}
|
||||
UpdateSubscriptionOptionsStub func(*livekit.DataTrackSubscriptionOptions)
|
||||
updateSubscriptionOptionsMutex sync.RWMutex
|
||||
updateSubscriptionOptionsArgsForCall []struct {
|
||||
arg1 *livekit.DataTrackSubscriptionOptions
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) Handle() uint16 {
|
||||
fake.handleMutex.Lock()
|
||||
ret, specificReturn := fake.handleReturnsOnCall[len(fake.handleArgsForCall)]
|
||||
fake.handleArgsForCall = append(fake.handleArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.HandleStub
|
||||
fakeReturns := fake.handleReturns
|
||||
fake.recordInvocation("Handle", []interface{}{})
|
||||
fake.handleMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleCallCount() int {
|
||||
fake.handleMutex.RLock()
|
||||
defer fake.handleMutex.RUnlock()
|
||||
return len(fake.handleArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleCalls(stub func() uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleReturns(result1 uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = nil
|
||||
fake.handleReturns = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleReturnsOnCall(i int, result1 uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = nil
|
||||
if fake.handleReturnsOnCall == nil {
|
||||
fake.handleReturnsOnCall = make(map[int]struct {
|
||||
result1 uint16
|
||||
})
|
||||
}
|
||||
fake.handleReturnsOnCall[i] = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrack() types.DataTrack {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
ret, specificReturn := fake.publishDataTrackReturnsOnCall[len(fake.publishDataTrackArgsForCall)]
|
||||
fake.publishDataTrackArgsForCall = append(fake.publishDataTrackArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.PublishDataTrackStub
|
||||
fakeReturns := fake.publishDataTrackReturns
|
||||
fake.recordInvocation("PublishDataTrack", []interface{}{})
|
||||
fake.publishDataTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackCallCount() int {
|
||||
fake.publishDataTrackMutex.RLock()
|
||||
defer fake.publishDataTrackMutex.RUnlock()
|
||||
return len(fake.publishDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackCalls(stub func() types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackReturns(result1 types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = nil
|
||||
fake.publishDataTrackReturns = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackReturnsOnCall(i int, result1 types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = nil
|
||||
if fake.publishDataTrackReturnsOnCall == nil {
|
||||
fake.publishDataTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataTrack
|
||||
})
|
||||
}
|
||||
fake.publishDataTrackReturnsOnCall[i] = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptions(arg1 *livekit.DataTrackSubscriptionOptions) {
|
||||
fake.updateSubscriptionOptionsMutex.Lock()
|
||||
fake.updateSubscriptionOptionsArgsForCall = append(fake.updateSubscriptionOptionsArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSubscriptionOptions
|
||||
}{arg1})
|
||||
stub := fake.UpdateSubscriptionOptionsStub
|
||||
fake.recordInvocation("UpdateSubscriptionOptions", []interface{}{arg1})
|
||||
fake.updateSubscriptionOptionsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.UpdateSubscriptionOptionsStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCallCount() int {
|
||||
fake.updateSubscriptionOptionsMutex.RLock()
|
||||
defer fake.updateSubscriptionOptionsMutex.RUnlock()
|
||||
return len(fake.updateSubscriptionOptionsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCalls(stub func(*livekit.DataTrackSubscriptionOptions)) {
|
||||
fake.updateSubscriptionOptionsMutex.Lock()
|
||||
defer fake.updateSubscriptionOptionsMutex.Unlock()
|
||||
fake.UpdateSubscriptionOptionsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsArgsForCall(i int) *livekit.DataTrackSubscriptionOptions {
|
||||
fake.updateSubscriptionOptionsMutex.RLock()
|
||||
defer fake.updateSubscriptionOptionsMutex.RUnlock()
|
||||
argsForCall := fake.updateSubscriptionOptionsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ types.DataDownTrack = new(FakeDataDownTrack)
|
||||
@@ -0,0 +1,658 @@
|
||||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package typesfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type FakeDataTrack struct {
|
||||
AddDataDownTrackStub func(types.DataTrackSender) error
|
||||
addDataDownTrackMutex sync.RWMutex
|
||||
addDataDownTrackArgsForCall []struct {
|
||||
arg1 types.DataTrackSender
|
||||
}
|
||||
addDataDownTrackReturns struct {
|
||||
result1 error
|
||||
}
|
||||
addDataDownTrackReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
AddSubscriberStub func(types.LocalParticipant) (types.DataDownTrack, error)
|
||||
addSubscriberMutex sync.RWMutex
|
||||
addSubscriberArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
}
|
||||
addSubscriberReturns struct {
|
||||
result1 types.DataDownTrack
|
||||
result2 error
|
||||
}
|
||||
addSubscriberReturnsOnCall map[int]struct {
|
||||
result1 types.DataDownTrack
|
||||
result2 error
|
||||
}
|
||||
CloseStub func()
|
||||
closeMutex sync.RWMutex
|
||||
closeArgsForCall []struct {
|
||||
}
|
||||
DeleteDataDownTrackStub func(livekit.ParticipantID)
|
||||
deleteDataDownTrackMutex sync.RWMutex
|
||||
deleteDataDownTrackArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}
|
||||
HandlePacketStub func([]byte, *datatrack.Packet)
|
||||
handlePacketMutex sync.RWMutex
|
||||
handlePacketArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}
|
||||
IDStub func() livekit.TrackID
|
||||
iDMutex sync.RWMutex
|
||||
iDArgsForCall []struct {
|
||||
}
|
||||
iDReturns struct {
|
||||
result1 livekit.TrackID
|
||||
}
|
||||
iDReturnsOnCall map[int]struct {
|
||||
result1 livekit.TrackID
|
||||
}
|
||||
IsSubscriberStub func(livekit.ParticipantID) bool
|
||||
isSubscriberMutex sync.RWMutex
|
||||
isSubscriberArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}
|
||||
isSubscriberReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
isSubscriberReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
NameStub func() string
|
||||
nameMutex sync.RWMutex
|
||||
nameArgsForCall []struct {
|
||||
}
|
||||
nameReturns struct {
|
||||
result1 string
|
||||
}
|
||||
nameReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
PubHandleStub func() uint16
|
||||
pubHandleMutex sync.RWMutex
|
||||
pubHandleArgsForCall []struct {
|
||||
}
|
||||
pubHandleReturns struct {
|
||||
result1 uint16
|
||||
}
|
||||
pubHandleReturnsOnCall map[int]struct {
|
||||
result1 uint16
|
||||
}
|
||||
RemoveSubscriberStub func(livekit.ParticipantID)
|
||||
removeSubscriberMutex sync.RWMutex
|
||||
removeSubscriberArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}
|
||||
ToProtoStub func() *livekit.DataTrackInfo
|
||||
toProtoMutex sync.RWMutex
|
||||
toProtoArgsForCall []struct {
|
||||
}
|
||||
toProtoReturns struct {
|
||||
result1 *livekit.DataTrackInfo
|
||||
}
|
||||
toProtoReturnsOnCall map[int]struct {
|
||||
result1 *livekit.DataTrackInfo
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrack(arg1 types.DataTrackSender) error {
|
||||
fake.addDataDownTrackMutex.Lock()
|
||||
ret, specificReturn := fake.addDataDownTrackReturnsOnCall[len(fake.addDataDownTrackArgsForCall)]
|
||||
fake.addDataDownTrackArgsForCall = append(fake.addDataDownTrackArgsForCall, struct {
|
||||
arg1 types.DataTrackSender
|
||||
}{arg1})
|
||||
stub := fake.AddDataDownTrackStub
|
||||
fakeReturns := fake.addDataDownTrackReturns
|
||||
fake.recordInvocation("AddDataDownTrack", []interface{}{arg1})
|
||||
fake.addDataDownTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrackCallCount() int {
|
||||
fake.addDataDownTrackMutex.RLock()
|
||||
defer fake.addDataDownTrackMutex.RUnlock()
|
||||
return len(fake.addDataDownTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrackCalls(stub func(types.DataTrackSender) error) {
|
||||
fake.addDataDownTrackMutex.Lock()
|
||||
defer fake.addDataDownTrackMutex.Unlock()
|
||||
fake.AddDataDownTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrackArgsForCall(i int) types.DataTrackSender {
|
||||
fake.addDataDownTrackMutex.RLock()
|
||||
defer fake.addDataDownTrackMutex.RUnlock()
|
||||
argsForCall := fake.addDataDownTrackArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrackReturns(result1 error) {
|
||||
fake.addDataDownTrackMutex.Lock()
|
||||
defer fake.addDataDownTrackMutex.Unlock()
|
||||
fake.AddDataDownTrackStub = nil
|
||||
fake.addDataDownTrackReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddDataDownTrackReturnsOnCall(i int, result1 error) {
|
||||
fake.addDataDownTrackMutex.Lock()
|
||||
defer fake.addDataDownTrackMutex.Unlock()
|
||||
fake.AddDataDownTrackStub = nil
|
||||
if fake.addDataDownTrackReturnsOnCall == nil {
|
||||
fake.addDataDownTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.addDataDownTrackReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriber(arg1 types.LocalParticipant) (types.DataDownTrack, error) {
|
||||
fake.addSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.addSubscriberReturnsOnCall[len(fake.addSubscriberArgsForCall)]
|
||||
fake.addSubscriberArgsForCall = append(fake.addSubscriberArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
}{arg1})
|
||||
stub := fake.AddSubscriberStub
|
||||
fakeReturns := fake.addSubscriberReturns
|
||||
fake.recordInvocation("AddSubscriber", []interface{}{arg1})
|
||||
fake.addSubscriberMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriberCallCount() int {
|
||||
fake.addSubscriberMutex.RLock()
|
||||
defer fake.addSubscriberMutex.RUnlock()
|
||||
return len(fake.addSubscriberArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriberCalls(stub func(types.LocalParticipant) (types.DataDownTrack, error)) {
|
||||
fake.addSubscriberMutex.Lock()
|
||||
defer fake.addSubscriberMutex.Unlock()
|
||||
fake.AddSubscriberStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriberArgsForCall(i int) types.LocalParticipant {
|
||||
fake.addSubscriberMutex.RLock()
|
||||
defer fake.addSubscriberMutex.RUnlock()
|
||||
argsForCall := fake.addSubscriberArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriberReturns(result1 types.DataDownTrack, result2 error) {
|
||||
fake.addSubscriberMutex.Lock()
|
||||
defer fake.addSubscriberMutex.Unlock()
|
||||
fake.AddSubscriberStub = nil
|
||||
fake.addSubscriberReturns = struct {
|
||||
result1 types.DataDownTrack
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) AddSubscriberReturnsOnCall(i int, result1 types.DataDownTrack, result2 error) {
|
||||
fake.addSubscriberMutex.Lock()
|
||||
defer fake.addSubscriberMutex.Unlock()
|
||||
fake.AddSubscriberStub = nil
|
||||
if fake.addSubscriberReturnsOnCall == nil {
|
||||
fake.addSubscriberReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataDownTrack
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.addSubscriberReturnsOnCall[i] = struct {
|
||||
result1 types.DataDownTrack
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) Close() {
|
||||
fake.closeMutex.Lock()
|
||||
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.CloseStub
|
||||
fake.recordInvocation("Close", []interface{}{})
|
||||
fake.closeMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.CloseStub()
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) CloseCallCount() int {
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
return len(fake.closeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) CloseCalls(stub func()) {
|
||||
fake.closeMutex.Lock()
|
||||
defer fake.closeMutex.Unlock()
|
||||
fake.CloseStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) DeleteDataDownTrack(arg1 livekit.ParticipantID) {
|
||||
fake.deleteDataDownTrackMutex.Lock()
|
||||
fake.deleteDataDownTrackArgsForCall = append(fake.deleteDataDownTrackArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}{arg1})
|
||||
stub := fake.DeleteDataDownTrackStub
|
||||
fake.recordInvocation("DeleteDataDownTrack", []interface{}{arg1})
|
||||
fake.deleteDataDownTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.DeleteDataDownTrackStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) DeleteDataDownTrackCallCount() int {
|
||||
fake.deleteDataDownTrackMutex.RLock()
|
||||
defer fake.deleteDataDownTrackMutex.RUnlock()
|
||||
return len(fake.deleteDataDownTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) DeleteDataDownTrackCalls(stub func(livekit.ParticipantID)) {
|
||||
fake.deleteDataDownTrackMutex.Lock()
|
||||
defer fake.deleteDataDownTrackMutex.Unlock()
|
||||
fake.DeleteDataDownTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) DeleteDataDownTrackArgsForCall(i int) livekit.ParticipantID {
|
||||
fake.deleteDataDownTrackMutex.RLock()
|
||||
defer fake.deleteDataDownTrackMutex.RUnlock()
|
||||
argsForCall := fake.deleteDataDownTrackArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) HandlePacket(arg1 []byte, arg2 *datatrack.Packet) {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.handlePacketMutex.Lock()
|
||||
fake.handlePacketArgsForCall = append(fake.handlePacketArgsForCall, struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}{arg1Copy, arg2})
|
||||
stub := fake.HandlePacketStub
|
||||
fake.recordInvocation("HandlePacket", []interface{}{arg1Copy, arg2})
|
||||
fake.handlePacketMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandlePacketStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) HandlePacketCallCount() int {
|
||||
fake.handlePacketMutex.RLock()
|
||||
defer fake.handlePacketMutex.RUnlock()
|
||||
return len(fake.handlePacketArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) HandlePacketCalls(stub func([]byte, *datatrack.Packet)) {
|
||||
fake.handlePacketMutex.Lock()
|
||||
defer fake.handlePacketMutex.Unlock()
|
||||
fake.HandlePacketStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) HandlePacketArgsForCall(i int) ([]byte, *datatrack.Packet) {
|
||||
fake.handlePacketMutex.RLock()
|
||||
defer fake.handlePacketMutex.RUnlock()
|
||||
argsForCall := fake.handlePacketArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ID() livekit.TrackID {
|
||||
fake.iDMutex.Lock()
|
||||
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
|
||||
fake.iDArgsForCall = append(fake.iDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.IDStub
|
||||
fakeReturns := fake.iDReturns
|
||||
fake.recordInvocation("ID", []interface{}{})
|
||||
fake.iDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IDCallCount() int {
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
return len(fake.iDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IDCalls(stub func() livekit.TrackID) {
|
||||
fake.iDMutex.Lock()
|
||||
defer fake.iDMutex.Unlock()
|
||||
fake.IDStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IDReturns(result1 livekit.TrackID) {
|
||||
fake.iDMutex.Lock()
|
||||
defer fake.iDMutex.Unlock()
|
||||
fake.IDStub = nil
|
||||
fake.iDReturns = struct {
|
||||
result1 livekit.TrackID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IDReturnsOnCall(i int, result1 livekit.TrackID) {
|
||||
fake.iDMutex.Lock()
|
||||
defer fake.iDMutex.Unlock()
|
||||
fake.IDStub = nil
|
||||
if fake.iDReturnsOnCall == nil {
|
||||
fake.iDReturnsOnCall = make(map[int]struct {
|
||||
result1 livekit.TrackID
|
||||
})
|
||||
}
|
||||
fake.iDReturnsOnCall[i] = struct {
|
||||
result1 livekit.TrackID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
|
||||
fake.isSubscriberArgsForCall = append(fake.isSubscriberArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}{arg1})
|
||||
stub := fake.IsSubscriberStub
|
||||
fakeReturns := fake.isSubscriberReturns
|
||||
fake.recordInvocation("IsSubscriber", []interface{}{arg1})
|
||||
fake.isSubscriberMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriberCallCount() int {
|
||||
fake.isSubscriberMutex.RLock()
|
||||
defer fake.isSubscriberMutex.RUnlock()
|
||||
return len(fake.isSubscriberArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriberCalls(stub func(livekit.ParticipantID) bool) {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
defer fake.isSubscriberMutex.Unlock()
|
||||
fake.IsSubscriberStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriberArgsForCall(i int) livekit.ParticipantID {
|
||||
fake.isSubscriberMutex.RLock()
|
||||
defer fake.isSubscriberMutex.RUnlock()
|
||||
argsForCall := fake.isSubscriberArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriberReturns(result1 bool) {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
defer fake.isSubscriberMutex.Unlock()
|
||||
fake.IsSubscriberStub = nil
|
||||
fake.isSubscriberReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) IsSubscriberReturnsOnCall(i int, result1 bool) {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
defer fake.isSubscriberMutex.Unlock()
|
||||
fake.IsSubscriberStub = nil
|
||||
if fake.isSubscriberReturnsOnCall == nil {
|
||||
fake.isSubscriberReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.isSubscriberReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) Name() string {
|
||||
fake.nameMutex.Lock()
|
||||
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
|
||||
fake.nameArgsForCall = append(fake.nameArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.NameStub
|
||||
fakeReturns := fake.nameReturns
|
||||
fake.recordInvocation("Name", []interface{}{})
|
||||
fake.nameMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) NameCallCount() int {
|
||||
fake.nameMutex.RLock()
|
||||
defer fake.nameMutex.RUnlock()
|
||||
return len(fake.nameArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) NameCalls(stub func() string) {
|
||||
fake.nameMutex.Lock()
|
||||
defer fake.nameMutex.Unlock()
|
||||
fake.NameStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) NameReturns(result1 string) {
|
||||
fake.nameMutex.Lock()
|
||||
defer fake.nameMutex.Unlock()
|
||||
fake.NameStub = nil
|
||||
fake.nameReturns = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) NameReturnsOnCall(i int, result1 string) {
|
||||
fake.nameMutex.Lock()
|
||||
defer fake.nameMutex.Unlock()
|
||||
fake.NameStub = nil
|
||||
if fake.nameReturnsOnCall == nil {
|
||||
fake.nameReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
})
|
||||
}
|
||||
fake.nameReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PubHandle() uint16 {
|
||||
fake.pubHandleMutex.Lock()
|
||||
ret, specificReturn := fake.pubHandleReturnsOnCall[len(fake.pubHandleArgsForCall)]
|
||||
fake.pubHandleArgsForCall = append(fake.pubHandleArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.PubHandleStub
|
||||
fakeReturns := fake.pubHandleReturns
|
||||
fake.recordInvocation("PubHandle", []interface{}{})
|
||||
fake.pubHandleMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PubHandleCallCount() int {
|
||||
fake.pubHandleMutex.RLock()
|
||||
defer fake.pubHandleMutex.RUnlock()
|
||||
return len(fake.pubHandleArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PubHandleCalls(stub func() uint16) {
|
||||
fake.pubHandleMutex.Lock()
|
||||
defer fake.pubHandleMutex.Unlock()
|
||||
fake.PubHandleStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PubHandleReturns(result1 uint16) {
|
||||
fake.pubHandleMutex.Lock()
|
||||
defer fake.pubHandleMutex.Unlock()
|
||||
fake.PubHandleStub = nil
|
||||
fake.pubHandleReturns = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PubHandleReturnsOnCall(i int, result1 uint16) {
|
||||
fake.pubHandleMutex.Lock()
|
||||
defer fake.pubHandleMutex.Unlock()
|
||||
fake.PubHandleStub = nil
|
||||
if fake.pubHandleReturnsOnCall == nil {
|
||||
fake.pubHandleReturnsOnCall = make(map[int]struct {
|
||||
result1 uint16
|
||||
})
|
||||
}
|
||||
fake.pubHandleReturnsOnCall[i] = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) RemoveSubscriber(arg1 livekit.ParticipantID) {
|
||||
fake.removeSubscriberMutex.Lock()
|
||||
fake.removeSubscriberArgsForCall = append(fake.removeSubscriberArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}{arg1})
|
||||
stub := fake.RemoveSubscriberStub
|
||||
fake.recordInvocation("RemoveSubscriber", []interface{}{arg1})
|
||||
fake.removeSubscriberMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.RemoveSubscriberStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) RemoveSubscriberCallCount() int {
|
||||
fake.removeSubscriberMutex.RLock()
|
||||
defer fake.removeSubscriberMutex.RUnlock()
|
||||
return len(fake.removeSubscriberArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) RemoveSubscriberCalls(stub func(livekit.ParticipantID)) {
|
||||
fake.removeSubscriberMutex.Lock()
|
||||
defer fake.removeSubscriberMutex.Unlock()
|
||||
fake.RemoveSubscriberStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) RemoveSubscriberArgsForCall(i int) livekit.ParticipantID {
|
||||
fake.removeSubscriberMutex.RLock()
|
||||
defer fake.removeSubscriberMutex.RUnlock()
|
||||
argsForCall := fake.removeSubscriberArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ToProto() *livekit.DataTrackInfo {
|
||||
fake.toProtoMutex.Lock()
|
||||
ret, specificReturn := fake.toProtoReturnsOnCall[len(fake.toProtoArgsForCall)]
|
||||
fake.toProtoArgsForCall = append(fake.toProtoArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ToProtoStub
|
||||
fakeReturns := fake.toProtoReturns
|
||||
fake.recordInvocation("ToProto", []interface{}{})
|
||||
fake.toProtoMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ToProtoCallCount() int {
|
||||
fake.toProtoMutex.RLock()
|
||||
defer fake.toProtoMutex.RUnlock()
|
||||
return len(fake.toProtoArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ToProtoCalls(stub func() *livekit.DataTrackInfo) {
|
||||
fake.toProtoMutex.Lock()
|
||||
defer fake.toProtoMutex.Unlock()
|
||||
fake.ToProtoStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ToProtoReturns(result1 *livekit.DataTrackInfo) {
|
||||
fake.toProtoMutex.Lock()
|
||||
defer fake.toProtoMutex.Unlock()
|
||||
fake.ToProtoStub = nil
|
||||
fake.toProtoReturns = struct {
|
||||
result1 *livekit.DataTrackInfo
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) ToProtoReturnsOnCall(i int, result1 *livekit.DataTrackInfo) {
|
||||
fake.toProtoMutex.Lock()
|
||||
defer fake.toProtoMutex.Unlock()
|
||||
fake.ToProtoStub = nil
|
||||
if fake.toProtoReturnsOnCall == nil {
|
||||
fake.toProtoReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.DataTrackInfo
|
||||
})
|
||||
}
|
||||
fake.toProtoReturnsOnCall[i] = struct {
|
||||
result1 *livekit.DataTrackInfo
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ types.DataTrack = new(FakeDataTrack)
|
||||
@@ -0,0 +1,146 @@
|
||||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package typesfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type FakeDataTrackSender struct {
|
||||
SubscriberIDStub func() livekit.ParticipantID
|
||||
subscriberIDMutex sync.RWMutex
|
||||
subscriberIDArgsForCall []struct {
|
||||
}
|
||||
subscriberIDReturns struct {
|
||||
result1 livekit.ParticipantID
|
||||
}
|
||||
subscriberIDReturnsOnCall map[int]struct {
|
||||
result1 livekit.ParticipantID
|
||||
}
|
||||
WritePacketStub func([]byte, *datatrack.Packet)
|
||||
writePacketMutex sync.RWMutex
|
||||
writePacketArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) SubscriberID() livekit.ParticipantID {
|
||||
fake.subscriberIDMutex.Lock()
|
||||
ret, specificReturn := fake.subscriberIDReturnsOnCall[len(fake.subscriberIDArgsForCall)]
|
||||
fake.subscriberIDArgsForCall = append(fake.subscriberIDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.SubscriberIDStub
|
||||
fakeReturns := fake.subscriberIDReturns
|
||||
fake.recordInvocation("SubscriberID", []interface{}{})
|
||||
fake.subscriberIDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) SubscriberIDCallCount() int {
|
||||
fake.subscriberIDMutex.RLock()
|
||||
defer fake.subscriberIDMutex.RUnlock()
|
||||
return len(fake.subscriberIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) SubscriberIDCalls(stub func() livekit.ParticipantID) {
|
||||
fake.subscriberIDMutex.Lock()
|
||||
defer fake.subscriberIDMutex.Unlock()
|
||||
fake.SubscriberIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) SubscriberIDReturns(result1 livekit.ParticipantID) {
|
||||
fake.subscriberIDMutex.Lock()
|
||||
defer fake.subscriberIDMutex.Unlock()
|
||||
fake.SubscriberIDStub = nil
|
||||
fake.subscriberIDReturns = struct {
|
||||
result1 livekit.ParticipantID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) SubscriberIDReturnsOnCall(i int, result1 livekit.ParticipantID) {
|
||||
fake.subscriberIDMutex.Lock()
|
||||
defer fake.subscriberIDMutex.Unlock()
|
||||
fake.SubscriberIDStub = nil
|
||||
if fake.subscriberIDReturnsOnCall == nil {
|
||||
fake.subscriberIDReturnsOnCall = make(map[int]struct {
|
||||
result1 livekit.ParticipantID
|
||||
})
|
||||
}
|
||||
fake.subscriberIDReturnsOnCall[i] = struct {
|
||||
result1 livekit.ParticipantID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) WritePacket(arg1 []byte, arg2 *datatrack.Packet) {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.writePacketMutex.Lock()
|
||||
fake.writePacketArgsForCall = append(fake.writePacketArgsForCall, struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}{arg1Copy, arg2})
|
||||
stub := fake.WritePacketStub
|
||||
fake.recordInvocation("WritePacket", []interface{}{arg1Copy, arg2})
|
||||
fake.writePacketMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.WritePacketStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) WritePacketCallCount() int {
|
||||
fake.writePacketMutex.RLock()
|
||||
defer fake.writePacketMutex.RUnlock()
|
||||
return len(fake.writePacketArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) WritePacketCalls(stub func([]byte, *datatrack.Packet)) {
|
||||
fake.writePacketMutex.Lock()
|
||||
defer fake.writePacketMutex.Unlock()
|
||||
fake.WritePacketStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) WritePacketArgsForCall(i int) ([]byte, *datatrack.Packet) {
|
||||
fake.writePacketMutex.RLock()
|
||||
defer fake.writePacketMutex.RUnlock()
|
||||
argsForCall := fake.writePacketArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ types.DataTrackSender = new(FakeDataTrackSender)
|
||||
@@ -0,0 +1,114 @@
|
||||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package typesfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
type FakeDataTrackTransport struct {
|
||||
SendDataTrackMessageStub func([]byte) error
|
||||
sendDataTrackMessageMutex sync.RWMutex
|
||||
sendDataTrackMessageArgsForCall []struct {
|
||||
arg1 []byte
|
||||
}
|
||||
sendDataTrackMessageReturns struct {
|
||||
result1 error
|
||||
}
|
||||
sendDataTrackMessageReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessage(arg1 []byte) error {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.sendDataTrackMessageMutex.Lock()
|
||||
ret, specificReturn := fake.sendDataTrackMessageReturnsOnCall[len(fake.sendDataTrackMessageArgsForCall)]
|
||||
fake.sendDataTrackMessageArgsForCall = append(fake.sendDataTrackMessageArgsForCall, struct {
|
||||
arg1 []byte
|
||||
}{arg1Copy})
|
||||
stub := fake.SendDataTrackMessageStub
|
||||
fakeReturns := fake.sendDataTrackMessageReturns
|
||||
fake.recordInvocation("SendDataTrackMessage", []interface{}{arg1Copy})
|
||||
fake.sendDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessageCallCount() int {
|
||||
fake.sendDataTrackMessageMutex.RLock()
|
||||
defer fake.sendDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.sendDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessageCalls(stub func([]byte) error) {
|
||||
fake.sendDataTrackMessageMutex.Lock()
|
||||
defer fake.sendDataTrackMessageMutex.Unlock()
|
||||
fake.SendDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessageArgsForCall(i int) []byte {
|
||||
fake.sendDataTrackMessageMutex.RLock()
|
||||
defer fake.sendDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.sendDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessageReturns(result1 error) {
|
||||
fake.sendDataTrackMessageMutex.Lock()
|
||||
defer fake.sendDataTrackMessageMutex.Unlock()
|
||||
fake.SendDataTrackMessageStub = nil
|
||||
fake.sendDataTrackMessageReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) SendDataTrackMessageReturnsOnCall(i int, result1 error) {
|
||||
fake.sendDataTrackMessageMutex.Lock()
|
||||
defer fake.sendDataTrackMessageMutex.Unlock()
|
||||
fake.SendDataTrackMessageStub = nil
|
||||
if fake.sendDataTrackMessageReturnsOnCall == nil {
|
||||
fake.sendDataTrackMessageReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.sendDataTrackMessageReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackTransport) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ types.DataTrackTransport = new(FakeDataTrackTransport)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -55,6 +55,18 @@ type FakeLocalParticipantHelper struct {
|
||||
result1 map[livekit.TrackID]*livekit.RTPForwarderState
|
||||
result2 error
|
||||
}
|
||||
ResolveDataTrackStub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult
|
||||
resolveDataTrackMutex sync.RWMutex
|
||||
resolveDataTrackArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}
|
||||
resolveDataTrackReturns struct {
|
||||
result1 types.DataResolverResult
|
||||
}
|
||||
resolveDataTrackReturnsOnCall map[int]struct {
|
||||
result1 types.DataResolverResult
|
||||
}
|
||||
ResolveMediaTrackStub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult
|
||||
resolveMediaTrackMutex sync.RWMutex
|
||||
resolveMediaTrackArgsForCall []struct {
|
||||
@@ -328,6 +340,68 @@ func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateReturnsOnCall
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrack(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.DataResolverResult {
|
||||
fake.resolveDataTrackMutex.Lock()
|
||||
ret, specificReturn := fake.resolveDataTrackReturnsOnCall[len(fake.resolveDataTrackArgsForCall)]
|
||||
fake.resolveDataTrackArgsForCall = append(fake.resolveDataTrackArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}{arg1, arg2})
|
||||
stub := fake.ResolveDataTrackStub
|
||||
fakeReturns := fake.resolveDataTrackReturns
|
||||
fake.recordInvocation("ResolveDataTrack", []interface{}{arg1, arg2})
|
||||
fake.resolveDataTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrackCallCount() int {
|
||||
fake.resolveDataTrackMutex.RLock()
|
||||
defer fake.resolveDataTrackMutex.RUnlock()
|
||||
return len(fake.resolveDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrackCalls(stub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult) {
|
||||
fake.resolveDataTrackMutex.Lock()
|
||||
defer fake.resolveDataTrackMutex.Unlock()
|
||||
fake.ResolveDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrackArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
|
||||
fake.resolveDataTrackMutex.RLock()
|
||||
defer fake.resolveDataTrackMutex.RUnlock()
|
||||
argsForCall := fake.resolveDataTrackArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrackReturns(result1 types.DataResolverResult) {
|
||||
fake.resolveDataTrackMutex.Lock()
|
||||
defer fake.resolveDataTrackMutex.Unlock()
|
||||
fake.ResolveDataTrackStub = nil
|
||||
fake.resolveDataTrackReturns = struct {
|
||||
result1 types.DataResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveDataTrackReturnsOnCall(i int, result1 types.DataResolverResult) {
|
||||
fake.resolveDataTrackMutex.Lock()
|
||||
defer fake.resolveDataTrackMutex.Unlock()
|
||||
fake.ResolveDataTrackStub = nil
|
||||
if fake.resolveDataTrackReturnsOnCall == nil {
|
||||
fake.resolveDataTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataResolverResult
|
||||
})
|
||||
}
|
||||
fake.resolveDataTrackReturnsOnCall[i] = struct {
|
||||
result1 types.DataResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrack(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.MediaResolverResult {
|
||||
fake.resolveMediaTrackMutex.Lock()
|
||||
ret, specificReturn := fake.resolveMediaTrackReturnsOnCall[len(fake.resolveMediaTrackArgsForCall)]
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
@@ -54,15 +56,15 @@ type FakeParticipant struct {
|
||||
connectedAtReturnsOnCall map[int]struct {
|
||||
result1 time.Time
|
||||
}
|
||||
DebugInfoStub func() map[string]interface{}
|
||||
DebugInfoStub func() map[string]any
|
||||
debugInfoMutex sync.RWMutex
|
||||
debugInfoArgsForCall []struct {
|
||||
}
|
||||
debugInfoReturns struct {
|
||||
result1 map[string]interface{}
|
||||
result1 map[string]any
|
||||
}
|
||||
debugInfoReturnsOnCall map[int]struct {
|
||||
result1 map[string]interface{}
|
||||
result1 map[string]any
|
||||
}
|
||||
GetAudioLevelStub func() (float64, bool)
|
||||
getAudioLevelMutex sync.RWMutex
|
||||
@@ -76,6 +78,37 @@ type FakeParticipant struct {
|
||||
result1 float64
|
||||
result2 bool
|
||||
}
|
||||
GetLoggerStub func() logger.Logger
|
||||
getLoggerMutex sync.RWMutex
|
||||
getLoggerArgsForCall []struct {
|
||||
}
|
||||
getLoggerReturns struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
getLoggerReturnsOnCall map[int]struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
GetPublishedDataTrackStub func(uint16) types.DataTrack
|
||||
getPublishedDataTrackMutex sync.RWMutex
|
||||
getPublishedDataTrackArgsForCall []struct {
|
||||
arg1 uint16
|
||||
}
|
||||
getPublishedDataTrackReturns struct {
|
||||
result1 types.DataTrack
|
||||
}
|
||||
getPublishedDataTrackReturnsOnCall map[int]struct {
|
||||
result1 types.DataTrack
|
||||
}
|
||||
GetPublishedDataTracksStub func() []types.DataTrack
|
||||
getPublishedDataTracksMutex sync.RWMutex
|
||||
getPublishedDataTracksArgsForCall []struct {
|
||||
}
|
||||
getPublishedDataTracksReturns struct {
|
||||
result1 []types.DataTrack
|
||||
}
|
||||
getPublishedDataTracksReturnsOnCall map[int]struct {
|
||||
result1 []types.DataTrack
|
||||
}
|
||||
GetPublishedTrackStub func(livekit.TrackID) types.MediaTrack
|
||||
getPublishedTrackMutex sync.RWMutex
|
||||
getPublishedTrackArgsForCall []struct {
|
||||
@@ -97,6 +130,12 @@ type FakeParticipant struct {
|
||||
getPublishedTracksReturnsOnCall map[int]struct {
|
||||
result1 []types.MediaTrack
|
||||
}
|
||||
HandleReceivedDataTrackMessageStub func([]byte, *datatrack.Packet)
|
||||
handleReceivedDataTrackMessageMutex sync.RWMutex
|
||||
handleReceivedDataTrackMessageArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}
|
||||
HasPermissionStub func(livekit.TrackID, livekit.ParticipantIdentity) bool
|
||||
hasPermissionMutex sync.RWMutex
|
||||
hasPermissionArgsForCall []struct {
|
||||
@@ -149,6 +188,16 @@ type FakeParticipant struct {
|
||||
isAgentReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsClosedStub func() bool
|
||||
isClosedMutex sync.RWMutex
|
||||
isClosedArgsForCall []struct {
|
||||
}
|
||||
isClosedReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
isClosedReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsDependentStub func() bool
|
||||
isDependentMutex sync.RWMutex
|
||||
isDependentArgsForCall []struct {
|
||||
@@ -159,6 +208,16 @@ type FakeParticipant struct {
|
||||
isDependentReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsDisconnectedStub func() bool
|
||||
isDisconnectedMutex sync.RWMutex
|
||||
isDisconnectedArgsForCall []struct {
|
||||
}
|
||||
isDisconnectedReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
isDisconnectedReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsPublisherStub func() bool
|
||||
isPublisherMutex sync.RWMutex
|
||||
isPublisherArgsForCall []struct {
|
||||
@@ -189,11 +248,51 @@ type FakeParticipant struct {
|
||||
kindReturnsOnCall map[int]struct {
|
||||
result1 livekit.ParticipantInfo_Kind
|
||||
}
|
||||
MigrateStateStub func() types.MigrateState
|
||||
migrateStateMutex sync.RWMutex
|
||||
migrateStateArgsForCall []struct {
|
||||
}
|
||||
migrateStateReturns struct {
|
||||
result1 types.MigrateState
|
||||
}
|
||||
migrateStateReturnsOnCall map[int]struct {
|
||||
result1 types.MigrateState
|
||||
}
|
||||
OnDataTrackPublishedStub func(func(types.Participant, types.DataTrack))
|
||||
onDataTrackPublishedMutex sync.RWMutex
|
||||
onDataTrackPublishedArgsForCall []struct {
|
||||
arg1 func(types.Participant, types.DataTrack)
|
||||
}
|
||||
OnDataTrackUnpublishedStub func(func(types.Participant, types.DataTrack))
|
||||
onDataTrackUnpublishedMutex sync.RWMutex
|
||||
onDataTrackUnpublishedArgsForCall []struct {
|
||||
arg1 func(types.Participant, types.DataTrack)
|
||||
}
|
||||
OnMetricsStub func(func(types.Participant, *livekit.DataPacket))
|
||||
onMetricsMutex sync.RWMutex
|
||||
onMetricsArgsForCall []struct {
|
||||
arg1 func(types.Participant, *livekit.DataPacket)
|
||||
}
|
||||
OnTrackPublishedStub func(func(types.Participant, types.MediaTrack))
|
||||
onTrackPublishedMutex sync.RWMutex
|
||||
onTrackPublishedArgsForCall []struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}
|
||||
OnTrackUnpublishedStub func(func(types.Participant, types.MediaTrack))
|
||||
onTrackUnpublishedMutex sync.RWMutex
|
||||
onTrackUnpublishedArgsForCall []struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}
|
||||
OnTrackUpdatedStub func(func(types.Participant, types.MediaTrack))
|
||||
onTrackUpdatedMutex sync.RWMutex
|
||||
onTrackUpdatedArgsForCall []struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}
|
||||
RemovePublishedDataTrackStub func(types.DataTrack)
|
||||
removePublishedDataTrackMutex sync.RWMutex
|
||||
removePublishedDataTrackArgsForCall []struct {
|
||||
arg1 types.DataTrack
|
||||
}
|
||||
RemovePublishedTrackStub func(types.MediaTrack, bool)
|
||||
removePublishedTrackMutex sync.RWMutex
|
||||
removePublishedTrackArgsForCall []struct {
|
||||
@@ -232,6 +331,18 @@ type FakeParticipant struct {
|
||||
toProtoReturnsOnCall map[int]struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
}
|
||||
ToProtoWithVersionStub func() (*livekit.ParticipantInfo, utils.TimedVersion)
|
||||
toProtoWithVersionMutex sync.RWMutex
|
||||
toProtoWithVersionArgsForCall []struct {
|
||||
}
|
||||
toProtoWithVersionReturns struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
result2 utils.TimedVersion
|
||||
}
|
||||
toProtoWithVersionReturnsOnCall map[int]struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
result2 utils.TimedVersion
|
||||
}
|
||||
UpdateSubscriptionPermissionStub func(*livekit.SubscriptionPermission, utils.TimedVersion, func(participantID livekit.ParticipantID) types.LocalParticipant) error
|
||||
updateSubscriptionPermissionMutex sync.RWMutex
|
||||
updateSubscriptionPermissionArgsForCall []struct {
|
||||
@@ -481,7 +592,7 @@ func (fake *FakeParticipant) ConnectedAtReturnsOnCall(i int, result1 time.Time)
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) DebugInfo() map[string]interface{} {
|
||||
func (fake *FakeParticipant) DebugInfo() map[string]any {
|
||||
fake.debugInfoMutex.Lock()
|
||||
ret, specificReturn := fake.debugInfoReturnsOnCall[len(fake.debugInfoArgsForCall)]
|
||||
fake.debugInfoArgsForCall = append(fake.debugInfoArgsForCall, struct {
|
||||
@@ -505,32 +616,32 @@ func (fake *FakeParticipant) DebugInfoCallCount() int {
|
||||
return len(fake.debugInfoArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) DebugInfoCalls(stub func() map[string]interface{}) {
|
||||
func (fake *FakeParticipant) DebugInfoCalls(stub func() map[string]any) {
|
||||
fake.debugInfoMutex.Lock()
|
||||
defer fake.debugInfoMutex.Unlock()
|
||||
fake.DebugInfoStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) DebugInfoReturns(result1 map[string]interface{}) {
|
||||
func (fake *FakeParticipant) DebugInfoReturns(result1 map[string]any) {
|
||||
fake.debugInfoMutex.Lock()
|
||||
defer fake.debugInfoMutex.Unlock()
|
||||
fake.DebugInfoStub = nil
|
||||
fake.debugInfoReturns = struct {
|
||||
result1 map[string]interface{}
|
||||
result1 map[string]any
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) DebugInfoReturnsOnCall(i int, result1 map[string]interface{}) {
|
||||
func (fake *FakeParticipant) DebugInfoReturnsOnCall(i int, result1 map[string]any) {
|
||||
fake.debugInfoMutex.Lock()
|
||||
defer fake.debugInfoMutex.Unlock()
|
||||
fake.DebugInfoStub = nil
|
||||
if fake.debugInfoReturnsOnCall == nil {
|
||||
fake.debugInfoReturnsOnCall = make(map[int]struct {
|
||||
result1 map[string]interface{}
|
||||
result1 map[string]any
|
||||
})
|
||||
}
|
||||
fake.debugInfoReturnsOnCall[i] = struct {
|
||||
result1 map[string]interface{}
|
||||
result1 map[string]any
|
||||
}{result1}
|
||||
}
|
||||
|
||||
@@ -590,6 +701,173 @@ func (fake *FakeParticipant) GetAudioLevelReturnsOnCall(i int, result1 float64,
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLogger() logger.Logger {
|
||||
fake.getLoggerMutex.Lock()
|
||||
ret, specificReturn := fake.getLoggerReturnsOnCall[len(fake.getLoggerArgsForCall)]
|
||||
fake.getLoggerArgsForCall = append(fake.getLoggerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.GetLoggerStub
|
||||
fakeReturns := fake.getLoggerReturns
|
||||
fake.recordInvocation("GetLogger", []interface{}{})
|
||||
fake.getLoggerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLoggerCallCount() int {
|
||||
fake.getLoggerMutex.RLock()
|
||||
defer fake.getLoggerMutex.RUnlock()
|
||||
return len(fake.getLoggerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLoggerCalls(stub func() logger.Logger) {
|
||||
fake.getLoggerMutex.Lock()
|
||||
defer fake.getLoggerMutex.Unlock()
|
||||
fake.GetLoggerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLoggerReturns(result1 logger.Logger) {
|
||||
fake.getLoggerMutex.Lock()
|
||||
defer fake.getLoggerMutex.Unlock()
|
||||
fake.GetLoggerStub = nil
|
||||
fake.getLoggerReturns = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLoggerReturnsOnCall(i int, result1 logger.Logger) {
|
||||
fake.getLoggerMutex.Lock()
|
||||
defer fake.getLoggerMutex.Unlock()
|
||||
fake.GetLoggerStub = nil
|
||||
if fake.getLoggerReturnsOnCall == nil {
|
||||
fake.getLoggerReturnsOnCall = make(map[int]struct {
|
||||
result1 logger.Logger
|
||||
})
|
||||
}
|
||||
fake.getLoggerReturnsOnCall[i] = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrack(arg1 uint16) types.DataTrack {
|
||||
fake.getPublishedDataTrackMutex.Lock()
|
||||
ret, specificReturn := fake.getPublishedDataTrackReturnsOnCall[len(fake.getPublishedDataTrackArgsForCall)]
|
||||
fake.getPublishedDataTrackArgsForCall = append(fake.getPublishedDataTrackArgsForCall, struct {
|
||||
arg1 uint16
|
||||
}{arg1})
|
||||
stub := fake.GetPublishedDataTrackStub
|
||||
fakeReturns := fake.getPublishedDataTrackReturns
|
||||
fake.recordInvocation("GetPublishedDataTrack", []interface{}{arg1})
|
||||
fake.getPublishedDataTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrackCallCount() int {
|
||||
fake.getPublishedDataTrackMutex.RLock()
|
||||
defer fake.getPublishedDataTrackMutex.RUnlock()
|
||||
return len(fake.getPublishedDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrackCalls(stub func(uint16) types.DataTrack) {
|
||||
fake.getPublishedDataTrackMutex.Lock()
|
||||
defer fake.getPublishedDataTrackMutex.Unlock()
|
||||
fake.GetPublishedDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrackArgsForCall(i int) uint16 {
|
||||
fake.getPublishedDataTrackMutex.RLock()
|
||||
defer fake.getPublishedDataTrackMutex.RUnlock()
|
||||
argsForCall := fake.getPublishedDataTrackArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrackReturns(result1 types.DataTrack) {
|
||||
fake.getPublishedDataTrackMutex.Lock()
|
||||
defer fake.getPublishedDataTrackMutex.Unlock()
|
||||
fake.GetPublishedDataTrackStub = nil
|
||||
fake.getPublishedDataTrackReturns = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTrackReturnsOnCall(i int, result1 types.DataTrack) {
|
||||
fake.getPublishedDataTrackMutex.Lock()
|
||||
defer fake.getPublishedDataTrackMutex.Unlock()
|
||||
fake.GetPublishedDataTrackStub = nil
|
||||
if fake.getPublishedDataTrackReturnsOnCall == nil {
|
||||
fake.getPublishedDataTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataTrack
|
||||
})
|
||||
}
|
||||
fake.getPublishedDataTrackReturnsOnCall[i] = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTracks() []types.DataTrack {
|
||||
fake.getPublishedDataTracksMutex.Lock()
|
||||
ret, specificReturn := fake.getPublishedDataTracksReturnsOnCall[len(fake.getPublishedDataTracksArgsForCall)]
|
||||
fake.getPublishedDataTracksArgsForCall = append(fake.getPublishedDataTracksArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.GetPublishedDataTracksStub
|
||||
fakeReturns := fake.getPublishedDataTracksReturns
|
||||
fake.recordInvocation("GetPublishedDataTracks", []interface{}{})
|
||||
fake.getPublishedDataTracksMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTracksCallCount() int {
|
||||
fake.getPublishedDataTracksMutex.RLock()
|
||||
defer fake.getPublishedDataTracksMutex.RUnlock()
|
||||
return len(fake.getPublishedDataTracksArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTracksCalls(stub func() []types.DataTrack) {
|
||||
fake.getPublishedDataTracksMutex.Lock()
|
||||
defer fake.getPublishedDataTracksMutex.Unlock()
|
||||
fake.GetPublishedDataTracksStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTracksReturns(result1 []types.DataTrack) {
|
||||
fake.getPublishedDataTracksMutex.Lock()
|
||||
defer fake.getPublishedDataTracksMutex.Unlock()
|
||||
fake.GetPublishedDataTracksStub = nil
|
||||
fake.getPublishedDataTracksReturns = struct {
|
||||
result1 []types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedDataTracksReturnsOnCall(i int, result1 []types.DataTrack) {
|
||||
fake.getPublishedDataTracksMutex.Lock()
|
||||
defer fake.getPublishedDataTracksMutex.Unlock()
|
||||
fake.GetPublishedDataTracksStub = nil
|
||||
if fake.getPublishedDataTracksReturnsOnCall == nil {
|
||||
fake.getPublishedDataTracksReturnsOnCall = make(map[int]struct {
|
||||
result1 []types.DataTrack
|
||||
})
|
||||
}
|
||||
fake.getPublishedDataTracksReturnsOnCall[i] = struct {
|
||||
result1 []types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetPublishedTrack(arg1 livekit.TrackID) types.MediaTrack {
|
||||
fake.getPublishedTrackMutex.Lock()
|
||||
ret, specificReturn := fake.getPublishedTrackReturnsOnCall[len(fake.getPublishedTrackArgsForCall)]
|
||||
@@ -704,6 +982,44 @@ func (fake *FakeParticipant) GetPublishedTracksReturnsOnCall(i int, result1 []ty
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessage(arg1 []byte, arg2 *datatrack.Packet) {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.handleReceivedDataTrackMessageMutex.Lock()
|
||||
fake.handleReceivedDataTrackMessageArgsForCall = append(fake.handleReceivedDataTrackMessageArgsForCall, struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
}{arg1Copy, arg2})
|
||||
stub := fake.HandleReceivedDataTrackMessageStub
|
||||
fake.recordInvocation("HandleReceivedDataTrackMessage", []interface{}{arg1Copy, arg2})
|
||||
fake.handleReceivedDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandleReceivedDataTrackMessageStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessageCallCount() int {
|
||||
fake.handleReceivedDataTrackMessageMutex.RLock()
|
||||
defer fake.handleReceivedDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.handleReceivedDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessageCalls(stub func([]byte, *datatrack.Packet)) {
|
||||
fake.handleReceivedDataTrackMessageMutex.Lock()
|
||||
defer fake.handleReceivedDataTrackMessageMutex.Unlock()
|
||||
fake.HandleReceivedDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessageArgsForCall(i int) ([]byte, *datatrack.Packet) {
|
||||
fake.handleReceivedDataTrackMessageMutex.RLock()
|
||||
defer fake.handleReceivedDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.handleReceivedDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HasPermission(arg1 livekit.TrackID, arg2 livekit.ParticipantIdentity) bool {
|
||||
fake.hasPermissionMutex.Lock()
|
||||
ret, specificReturn := fake.hasPermissionReturnsOnCall[len(fake.hasPermissionArgsForCall)]
|
||||
@@ -978,6 +1294,59 @@ func (fake *FakeParticipant) IsAgentReturnsOnCall(i int, result1 bool) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsClosed() bool {
|
||||
fake.isClosedMutex.Lock()
|
||||
ret, specificReturn := fake.isClosedReturnsOnCall[len(fake.isClosedArgsForCall)]
|
||||
fake.isClosedArgsForCall = append(fake.isClosedArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.IsClosedStub
|
||||
fakeReturns := fake.isClosedReturns
|
||||
fake.recordInvocation("IsClosed", []interface{}{})
|
||||
fake.isClosedMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsClosedCallCount() int {
|
||||
fake.isClosedMutex.RLock()
|
||||
defer fake.isClosedMutex.RUnlock()
|
||||
return len(fake.isClosedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsClosedCalls(stub func() bool) {
|
||||
fake.isClosedMutex.Lock()
|
||||
defer fake.isClosedMutex.Unlock()
|
||||
fake.IsClosedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsClosedReturns(result1 bool) {
|
||||
fake.isClosedMutex.Lock()
|
||||
defer fake.isClosedMutex.Unlock()
|
||||
fake.IsClosedStub = nil
|
||||
fake.isClosedReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsClosedReturnsOnCall(i int, result1 bool) {
|
||||
fake.isClosedMutex.Lock()
|
||||
defer fake.isClosedMutex.Unlock()
|
||||
fake.IsClosedStub = nil
|
||||
if fake.isClosedReturnsOnCall == nil {
|
||||
fake.isClosedReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.isClosedReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDependent() bool {
|
||||
fake.isDependentMutex.Lock()
|
||||
ret, specificReturn := fake.isDependentReturnsOnCall[len(fake.isDependentArgsForCall)]
|
||||
@@ -1031,6 +1400,59 @@ func (fake *FakeParticipant) IsDependentReturnsOnCall(i int, result1 bool) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDisconnected() bool {
|
||||
fake.isDisconnectedMutex.Lock()
|
||||
ret, specificReturn := fake.isDisconnectedReturnsOnCall[len(fake.isDisconnectedArgsForCall)]
|
||||
fake.isDisconnectedArgsForCall = append(fake.isDisconnectedArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.IsDisconnectedStub
|
||||
fakeReturns := fake.isDisconnectedReturns
|
||||
fake.recordInvocation("IsDisconnected", []interface{}{})
|
||||
fake.isDisconnectedMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDisconnectedCallCount() int {
|
||||
fake.isDisconnectedMutex.RLock()
|
||||
defer fake.isDisconnectedMutex.RUnlock()
|
||||
return len(fake.isDisconnectedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDisconnectedCalls(stub func() bool) {
|
||||
fake.isDisconnectedMutex.Lock()
|
||||
defer fake.isDisconnectedMutex.Unlock()
|
||||
fake.IsDisconnectedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDisconnectedReturns(result1 bool) {
|
||||
fake.isDisconnectedMutex.Lock()
|
||||
defer fake.isDisconnectedMutex.Unlock()
|
||||
fake.IsDisconnectedStub = nil
|
||||
fake.isDisconnectedReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsDisconnectedReturnsOnCall(i int, result1 bool) {
|
||||
fake.isDisconnectedMutex.Lock()
|
||||
defer fake.isDisconnectedMutex.Unlock()
|
||||
fake.IsDisconnectedStub = nil
|
||||
if fake.isDisconnectedReturnsOnCall == nil {
|
||||
fake.isDisconnectedReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.isDisconnectedReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) IsPublisher() bool {
|
||||
fake.isPublisherMutex.Lock()
|
||||
ret, specificReturn := fake.isPublisherReturnsOnCall[len(fake.isPublisherArgsForCall)]
|
||||
@@ -1190,6 +1612,123 @@ func (fake *FakeParticipant) KindReturnsOnCall(i int, result1 livekit.Participan
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) MigrateState() types.MigrateState {
|
||||
fake.migrateStateMutex.Lock()
|
||||
ret, specificReturn := fake.migrateStateReturnsOnCall[len(fake.migrateStateArgsForCall)]
|
||||
fake.migrateStateArgsForCall = append(fake.migrateStateArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.MigrateStateStub
|
||||
fakeReturns := fake.migrateStateReturns
|
||||
fake.recordInvocation("MigrateState", []interface{}{})
|
||||
fake.migrateStateMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) MigrateStateCallCount() int {
|
||||
fake.migrateStateMutex.RLock()
|
||||
defer fake.migrateStateMutex.RUnlock()
|
||||
return len(fake.migrateStateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) MigrateStateCalls(stub func() types.MigrateState) {
|
||||
fake.migrateStateMutex.Lock()
|
||||
defer fake.migrateStateMutex.Unlock()
|
||||
fake.MigrateStateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) MigrateStateReturns(result1 types.MigrateState) {
|
||||
fake.migrateStateMutex.Lock()
|
||||
defer fake.migrateStateMutex.Unlock()
|
||||
fake.MigrateStateStub = nil
|
||||
fake.migrateStateReturns = struct {
|
||||
result1 types.MigrateState
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) MigrateStateReturnsOnCall(i int, result1 types.MigrateState) {
|
||||
fake.migrateStateMutex.Lock()
|
||||
defer fake.migrateStateMutex.Unlock()
|
||||
fake.MigrateStateStub = nil
|
||||
if fake.migrateStateReturnsOnCall == nil {
|
||||
fake.migrateStateReturnsOnCall = make(map[int]struct {
|
||||
result1 types.MigrateState
|
||||
})
|
||||
}
|
||||
fake.migrateStateReturnsOnCall[i] = struct {
|
||||
result1 types.MigrateState
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackPublished(arg1 func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
fake.onDataTrackPublishedArgsForCall = append(fake.onDataTrackPublishedArgsForCall, struct {
|
||||
arg1 func(types.Participant, types.DataTrack)
|
||||
}{arg1})
|
||||
stub := fake.OnDataTrackPublishedStub
|
||||
fake.recordInvocation("OnDataTrackPublished", []interface{}{arg1})
|
||||
fake.onDataTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackPublishedStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackPublishedCallCount() int {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackPublishedCalls(stub func(func(types.Participant, types.DataTrack))) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
defer fake.onDataTrackPublishedMutex.Unlock()
|
||||
fake.OnDataTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackPublishedArgsForCall(i int) func(types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackUnpublished(arg1 func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
fake.onDataTrackUnpublishedArgsForCall = append(fake.onDataTrackUnpublishedArgsForCall, struct {
|
||||
arg1 func(types.Participant, types.DataTrack)
|
||||
}{arg1})
|
||||
stub := fake.OnDataTrackUnpublishedStub
|
||||
fake.recordInvocation("OnDataTrackUnpublished", []interface{}{arg1})
|
||||
fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackUnpublishedStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackUnpublishedCallCount() int {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackUnpublishedCalls(stub func(func(types.Participant, types.DataTrack))) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
defer fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
fake.OnDataTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnDataTrackUnpublishedArgsForCall(i int) func(types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnMetrics(arg1 func(types.Participant, *livekit.DataPacket)) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
|
||||
@@ -1222,6 +1761,134 @@ func (fake *FakeParticipant) OnMetricsArgsForCall(i int) func(types.Participant,
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackPublished(arg1 func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}{arg1})
|
||||
stub := fake.OnTrackPublishedStub
|
||||
fake.recordInvocation("OnTrackPublished", []interface{}{arg1})
|
||||
fake.onTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishedStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackPublishedCallCount() int {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackPublishedCalls(stub func(func(types.Participant, types.MediaTrack))) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
defer fake.onTrackPublishedMutex.Unlock()
|
||||
fake.OnTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackPublishedArgsForCall(i int) func(types.Participant, types.MediaTrack) {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUnpublished(arg1 func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}{arg1})
|
||||
stub := fake.OnTrackUnpublishedStub
|
||||
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1})
|
||||
fake.onTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnpublishedStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUnpublishedCallCount() int {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUnpublishedCalls(stub func(func(types.Participant, types.MediaTrack))) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
defer fake.onTrackUnpublishedMutex.Unlock()
|
||||
fake.OnTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUnpublishedArgsForCall(i int) func(types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUpdated(arg1 func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
fake.onTrackUpdatedArgsForCall = append(fake.onTrackUpdatedArgsForCall, struct {
|
||||
arg1 func(types.Participant, types.MediaTrack)
|
||||
}{arg1})
|
||||
stub := fake.OnTrackUpdatedStub
|
||||
fake.recordInvocation("OnTrackUpdated", []interface{}{arg1})
|
||||
fake.onTrackUpdatedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUpdatedStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUpdatedCallCount() int {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
return len(fake.onTrackUpdatedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUpdatedCalls(stub func(func(types.Participant, types.MediaTrack))) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
defer fake.onTrackUpdatedMutex.Unlock()
|
||||
fake.OnTrackUpdatedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnTrackUpdatedArgsForCall(i int) func(types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUpdatedArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrack(arg1 types.DataTrack) {
|
||||
fake.removePublishedDataTrackMutex.Lock()
|
||||
fake.removePublishedDataTrackArgsForCall = append(fake.removePublishedDataTrackArgsForCall, struct {
|
||||
arg1 types.DataTrack
|
||||
}{arg1})
|
||||
stub := fake.RemovePublishedDataTrackStub
|
||||
fake.recordInvocation("RemovePublishedDataTrack", []interface{}{arg1})
|
||||
fake.removePublishedDataTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.RemovePublishedDataTrackStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrackCallCount() int {
|
||||
fake.removePublishedDataTrackMutex.RLock()
|
||||
defer fake.removePublishedDataTrackMutex.RUnlock()
|
||||
return len(fake.removePublishedDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrackCalls(stub func(types.DataTrack)) {
|
||||
fake.removePublishedDataTrackMutex.Lock()
|
||||
defer fake.removePublishedDataTrackMutex.Unlock()
|
||||
fake.RemovePublishedDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrackArgsForCall(i int) types.DataTrack {
|
||||
fake.removePublishedDataTrackMutex.RLock()
|
||||
defer fake.removePublishedDataTrackMutex.RUnlock()
|
||||
argsForCall := fake.removePublishedDataTrackArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedTrack(arg1 types.MediaTrack, arg2 bool) {
|
||||
fake.removePublishedTrackMutex.Lock()
|
||||
fake.removePublishedTrackArgsForCall = append(fake.removePublishedTrackArgsForCall, struct {
|
||||
@@ -1417,6 +2084,62 @@ func (fake *FakeParticipant) ToProtoReturnsOnCall(i int, result1 *livekit.Partic
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion) {
|
||||
fake.toProtoWithVersionMutex.Lock()
|
||||
ret, specificReturn := fake.toProtoWithVersionReturnsOnCall[len(fake.toProtoWithVersionArgsForCall)]
|
||||
fake.toProtoWithVersionArgsForCall = append(fake.toProtoWithVersionArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ToProtoWithVersionStub
|
||||
fakeReturns := fake.toProtoWithVersionReturns
|
||||
fake.recordInvocation("ToProtoWithVersion", []interface{}{})
|
||||
fake.toProtoWithVersionMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) ToProtoWithVersionCallCount() int {
|
||||
fake.toProtoWithVersionMutex.RLock()
|
||||
defer fake.toProtoWithVersionMutex.RUnlock()
|
||||
return len(fake.toProtoWithVersionArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) ToProtoWithVersionCalls(stub func() (*livekit.ParticipantInfo, utils.TimedVersion)) {
|
||||
fake.toProtoWithVersionMutex.Lock()
|
||||
defer fake.toProtoWithVersionMutex.Unlock()
|
||||
fake.ToProtoWithVersionStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) ToProtoWithVersionReturns(result1 *livekit.ParticipantInfo, result2 utils.TimedVersion) {
|
||||
fake.toProtoWithVersionMutex.Lock()
|
||||
defer fake.toProtoWithVersionMutex.Unlock()
|
||||
fake.ToProtoWithVersionStub = nil
|
||||
fake.toProtoWithVersionReturns = struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
result2 utils.TimedVersion
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) ToProtoWithVersionReturnsOnCall(i int, result1 *livekit.ParticipantInfo, result2 utils.TimedVersion) {
|
||||
fake.toProtoWithVersionMutex.Lock()
|
||||
defer fake.toProtoWithVersionMutex.Unlock()
|
||||
fake.ToProtoWithVersionStub = nil
|
||||
if fake.toProtoWithVersionReturnsOnCall == nil {
|
||||
fake.toProtoWithVersionReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
result2 utils.TimedVersion
|
||||
})
|
||||
}
|
||||
fake.toProtoWithVersionReturnsOnCall[i] = struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
result2 utils.TimedVersion
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) UpdateSubscriptionPermission(arg1 *livekit.SubscriptionPermission, arg2 utils.TimedVersion, arg3 func(participantID livekit.ParticipantID) types.LocalParticipant) error {
|
||||
fake.updateSubscriptionPermissionMutex.Lock()
|
||||
ret, specificReturn := fake.updateSubscriptionPermissionReturnsOnCall[len(fake.updateSubscriptionPermissionArgsForCall)]
|
||||
|
||||
@@ -57,6 +57,18 @@ type FakeRoom struct {
|
||||
arg2 livekit.ParticipantID
|
||||
arg3 types.ParticipantCloseReason
|
||||
}
|
||||
ResolveDataTrackForSubscriberStub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult
|
||||
resolveDataTrackForSubscriberMutex sync.RWMutex
|
||||
resolveDataTrackForSubscriberArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}
|
||||
resolveDataTrackForSubscriberReturns struct {
|
||||
result1 types.DataResolverResult
|
||||
}
|
||||
resolveDataTrackForSubscriberReturnsOnCall map[int]struct {
|
||||
result1 types.DataResolverResult
|
||||
}
|
||||
ResolveMediaTrackForSubscriberStub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult
|
||||
resolveMediaTrackForSubscriberMutex sync.RWMutex
|
||||
resolveMediaTrackForSubscriberArgsForCall []struct {
|
||||
@@ -335,6 +347,68 @@ func (fake *FakeRoom) RemoveParticipantArgsForCall(i int) (livekit.ParticipantId
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriber(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.DataResolverResult {
|
||||
fake.resolveDataTrackForSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.resolveDataTrackForSubscriberReturnsOnCall[len(fake.resolveDataTrackForSubscriberArgsForCall)]
|
||||
fake.resolveDataTrackForSubscriberArgsForCall = append(fake.resolveDataTrackForSubscriberArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}{arg1, arg2})
|
||||
stub := fake.ResolveDataTrackForSubscriberStub
|
||||
fakeReturns := fake.resolveDataTrackForSubscriberReturns
|
||||
fake.recordInvocation("ResolveDataTrackForSubscriber", []interface{}{arg1, arg2})
|
||||
fake.resolveDataTrackForSubscriberMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriberCallCount() int {
|
||||
fake.resolveDataTrackForSubscriberMutex.RLock()
|
||||
defer fake.resolveDataTrackForSubscriberMutex.RUnlock()
|
||||
return len(fake.resolveDataTrackForSubscriberArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriberCalls(stub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult) {
|
||||
fake.resolveDataTrackForSubscriberMutex.Lock()
|
||||
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
|
||||
fake.ResolveDataTrackForSubscriberStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriberArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
|
||||
fake.resolveDataTrackForSubscriberMutex.RLock()
|
||||
defer fake.resolveDataTrackForSubscriberMutex.RUnlock()
|
||||
argsForCall := fake.resolveDataTrackForSubscriberArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriberReturns(result1 types.DataResolverResult) {
|
||||
fake.resolveDataTrackForSubscriberMutex.Lock()
|
||||
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
|
||||
fake.ResolveDataTrackForSubscriberStub = nil
|
||||
fake.resolveDataTrackForSubscriberReturns = struct {
|
||||
result1 types.DataResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveDataTrackForSubscriberReturnsOnCall(i int, result1 types.DataResolverResult) {
|
||||
fake.resolveDataTrackForSubscriberMutex.Lock()
|
||||
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
|
||||
fake.ResolveDataTrackForSubscriberStub = nil
|
||||
if fake.resolveDataTrackForSubscriberReturnsOnCall == nil {
|
||||
fake.resolveDataTrackForSubscriberReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataResolverResult
|
||||
})
|
||||
}
|
||||
fake.resolveDataTrackForSubscriberReturnsOnCall[i] = struct {
|
||||
result1 types.DataResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) ResolveMediaTrackForSubscriber(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.MediaResolverResult {
|
||||
fake.resolveMediaTrackForSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.resolveMediaTrackForSubscriberReturnsOnCall[len(fake.resolveMediaTrackForSubscriberArgsForCall)]
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type UpDataTrackManagerParams struct {
|
||||
Logger logger.Logger
|
||||
Participant types.Participant
|
||||
}
|
||||
|
||||
type UpDataTrackManager struct {
|
||||
params UpDataTrackManagerParams
|
||||
|
||||
lock sync.RWMutex
|
||||
dataTracks map[uint16]types.DataTrack
|
||||
|
||||
onDataTrackPublished func(types.Participant, types.DataTrack)
|
||||
onDataTrackUnpublished func(types.Participant, types.DataTrack)
|
||||
}
|
||||
|
||||
func NewUpDataTrackManager(params UpDataTrackManagerParams) *UpDataTrackManager {
|
||||
return &UpDataTrackManager{
|
||||
params: params,
|
||||
dataTracks: make(map[uint16]types.DataTrack),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) OnDataTrackPublished(callback func(types.Participant, types.DataTrack)) {
|
||||
u.lock.Lock()
|
||||
u.onDataTrackPublished = callback
|
||||
u.lock.Unlock()
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) GetOnDataTrackPublished() func(types.Participant, types.DataTrack) {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
return u.onDataTrackPublished
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) OnDataTrackUnpublished(callback func(types.Participant, types.DataTrack)) {
|
||||
u.lock.Lock()
|
||||
u.onDataTrackUnpublished = callback
|
||||
u.lock.Unlock()
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) GetOnDataTrackUnpublished() func(types.Participant, types.DataTrack) {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
return u.onDataTrackUnpublished
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) AddPublishedDataTrack(dt types.DataTrack) {
|
||||
u.lock.Lock()
|
||||
u.dataTracks[dt.PubHandle()] = dt
|
||||
u.lock.Unlock()
|
||||
|
||||
if onDataTrackPublished := u.GetOnDataTrackPublished(); onDataTrackPublished != nil {
|
||||
onDataTrackPublished(u.params.Participant, dt)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) RemovePublishedDataTrack(dt types.DataTrack) {
|
||||
var found bool
|
||||
pubHandle := dt.PubHandle()
|
||||
u.lock.Lock()
|
||||
if u.dataTracks[pubHandle] == dt {
|
||||
delete(u.dataTracks, pubHandle)
|
||||
found = true
|
||||
}
|
||||
u.lock.Unlock()
|
||||
|
||||
if found {
|
||||
dt.Close()
|
||||
|
||||
if onDataTrackUnpublished := u.GetOnDataTrackUnpublished(); onDataTrackUnpublished != nil {
|
||||
onDataTrackUnpublished(u.params.Participant, dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) GetPublishedDataTracks() []types.DataTrack {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
return maps.Values(u.dataTracks)
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) GetPublishedDataTrack(handle uint16) types.DataTrack {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
return u.dataTracks[handle]
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) HandleReceivedDataTrackMessage(data []byte, packet *datatrack.Packet) {
|
||||
u.lock.RLock()
|
||||
dt := u.dataTracks[packet.Handle]
|
||||
u.lock.RUnlock()
|
||||
if dt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dt.HandlePacket(data, packet)
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) ToProto() []*livekit.DataTrackInfo {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
var dataTrackInfos []*livekit.DataTrackInfo
|
||||
for _, dt := range u.dataTracks {
|
||||
dataTrackInfos = append(dataTrackInfos, dt.ToProto())
|
||||
}
|
||||
|
||||
return dataTrackInfos
|
||||
}
|
||||
@@ -502,6 +502,7 @@ func (r *RoomManager) StartSession(
|
||||
DatachannelLossyTargetLatency: r.config.RTC.DatachannelLossyTargetLatency,
|
||||
FireOnTrackBySdp: true,
|
||||
UseSinglePeerConnection: pi.UseSinglePeerConnection,
|
||||
EnableDataTracks: r.config.EnableDataTracks,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -664,7 +665,7 @@ func (r *RoomManager) getOrCreateRoom(ctx context.Context, createRoom *livekit.C
|
||||
}
|
||||
})
|
||||
|
||||
newRoom.OnParticipantChanged(func(p types.LocalParticipant) {
|
||||
newRoom.OnParticipantChanged(func(p types.Participant) {
|
||||
if !p.IsDisconnected() {
|
||||
if err := r.roomStore.StoreParticipant(ctx, roomName, p.ToProto()); err != nil {
|
||||
newRoom.Logger().Errorw("could not handle participant change", err)
|
||||
@@ -1118,6 +1119,10 @@ func (h *roomManagerParticipantHelper) ResolveMediaTrack(lp types.LocalParticipa
|
||||
return h.room.ResolveMediaTrackForSubscriber(lp, trackID)
|
||||
}
|
||||
|
||||
func (h *roomManagerParticipantHelper) ResolveDataTrack(lp types.LocalParticipant, trackID livekit.TrackID) types.DataResolverResult {
|
||||
return h.room.ResolveDataTrackForSubscriber(lp, trackID)
|
||||
}
|
||||
|
||||
func (h *roomManagerParticipantHelper) ShouldRegressCodec() bool {
|
||||
return h.codecRegressionThreshold == 0 || h.room.GetParticipantCount() < h.codecRegressionThreshold
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ func TestFractionLostReport(t *testing.T) {
|
||||
HeaderExtensions: nil,
|
||||
Codecs: []webrtc.RTPCodecParameters{opusCodec},
|
||||
}, opusCodec.RTPCodecCapability, 0)
|
||||
for i := 0; i < 15; i++ {
|
||||
for i := range 15 {
|
||||
pkt := rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
@@ -303,7 +303,7 @@ func TestFractionLostReport(t *testing.T) {
|
||||
HeaderExtensions: nil,
|
||||
Codecs: []webrtc.RTPCodecParameters{opusCodec},
|
||||
}, opusCodec.RTPCodecCapability, 0)
|
||||
for i := 0; i < 15; i++ {
|
||||
for i := range 15 {
|
||||
pkt := rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
|
||||
+5
-4
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/rtpstats"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/streamtracker"
|
||||
sfuutils "github.com/livekit/livekit-server/pkg/sfu/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -197,7 +198,7 @@ type WebRTCReceiver struct {
|
||||
|
||||
streamTrackerManager *StreamTrackerManager
|
||||
|
||||
downTrackSpreader *DownTrackSpreader
|
||||
downTrackSpreader *sfuutils.DownTrackSpreader[TrackSender]
|
||||
|
||||
connectionStats *connectionquality.ConnectionStats
|
||||
|
||||
@@ -274,7 +275,7 @@ func NewWebRTCReceiver(
|
||||
}
|
||||
w.trackInfo.Store(utils.CloneProto(trackInfo))
|
||||
|
||||
w.downTrackSpreader = NewDownTrackSpreader(DownTrackSpreaderParams{
|
||||
w.downTrackSpreader = sfuutils.NewDownTrackSpreader[TrackSender](sfuutils.DownTrackSpreaderParams{
|
||||
Threshold: w.lbThreshold,
|
||||
Logger: logger,
|
||||
})
|
||||
@@ -921,7 +922,7 @@ func (w *WebRTCReceiver) GetPrimaryReceiverForRed() TrackReceiver {
|
||||
|
||||
rt := w.redTransformer.Load()
|
||||
if rt == nil {
|
||||
pr := NewRedPrimaryReceiver(w, DownTrackSpreaderParams{
|
||||
pr := NewRedPrimaryReceiver(w, sfuutils.DownTrackSpreaderParams{
|
||||
Threshold: w.lbThreshold,
|
||||
Logger: w.logger,
|
||||
})
|
||||
@@ -945,7 +946,7 @@ func (w *WebRTCReceiver) GetRedReceiver() TrackReceiver {
|
||||
|
||||
rt := w.redTransformer.Load()
|
||||
if rt == nil {
|
||||
pr := NewRedReceiver(w, DownTrackSpreaderParams{
|
||||
pr := NewRedReceiver(w, sfuutils.DownTrackSpreaderParams{
|
||||
Threshold: w.lbThreshold,
|
||||
Logger: w.logger,
|
||||
})
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/utils"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
@@ -35,7 +36,7 @@ var (
|
||||
|
||||
type RedPrimaryReceiver struct {
|
||||
TrackReceiver
|
||||
downTrackSpreader *DownTrackSpreader
|
||||
downTrackSpreader *utils.DownTrackSpreader[TrackSender]
|
||||
logger logger.Logger
|
||||
closed atomic.Bool
|
||||
redPT uint8
|
||||
@@ -47,10 +48,10 @@ type RedPrimaryReceiver struct {
|
||||
pktHistory byte
|
||||
}
|
||||
|
||||
func NewRedPrimaryReceiver(receiver TrackReceiver, dsp DownTrackSpreaderParams) *RedPrimaryReceiver {
|
||||
func NewRedPrimaryReceiver(receiver TrackReceiver, dsp utils.DownTrackSpreaderParams) *RedPrimaryReceiver {
|
||||
return &RedPrimaryReceiver{
|
||||
TrackReceiver: receiver,
|
||||
downTrackSpreader: NewDownTrackSpreader(dsp),
|
||||
downTrackSpreader: utils.NewDownTrackSpreader[TrackSender](dsp),
|
||||
logger: dsp.Logger,
|
||||
redPT: uint8(receiver.Codec().PayloadType),
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/utils"
|
||||
"github.com/livekit/mediatransportutil/pkg/bucket"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
@@ -43,17 +44,17 @@ const (
|
||||
|
||||
type RedReceiver struct {
|
||||
TrackReceiver
|
||||
downTrackSpreader *DownTrackSpreader
|
||||
downTrackSpreader *utils.DownTrackSpreader[TrackSender]
|
||||
logger logger.Logger
|
||||
closed atomic.Bool
|
||||
pktBuff [maxRedCount]*rtp.Packet
|
||||
redPayloadBuf [mtuSize]byte
|
||||
}
|
||||
|
||||
func NewRedReceiver(receiver TrackReceiver, dsp DownTrackSpreaderParams) *RedReceiver {
|
||||
func NewRedReceiver(receiver TrackReceiver, dsp utils.DownTrackSpreaderParams) *RedReceiver {
|
||||
return &RedReceiver{
|
||||
TrackReceiver: receiver,
|
||||
downTrackSpreader: NewDownTrackSpreader(dsp),
|
||||
downTrackSpreader: utils.NewDownTrackSpreader[TrackSender](dsp),
|
||||
logger: dsp.Logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sfu
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
@@ -22,55 +22,59 @@ import (
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
type sender interface {
|
||||
SubscriberID() livekit.ParticipantID
|
||||
}
|
||||
|
||||
type DownTrackSpreaderParams struct {
|
||||
Threshold int
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type DownTrackSpreader struct {
|
||||
type DownTrackSpreader[T sender] struct {
|
||||
params DownTrackSpreaderParams
|
||||
|
||||
downTrackMu sync.RWMutex
|
||||
downTracks map[livekit.ParticipantID]TrackSender
|
||||
downTracksShadow []TrackSender
|
||||
downTracks map[livekit.ParticipantID]T
|
||||
downTracksShadow []T
|
||||
}
|
||||
|
||||
func NewDownTrackSpreader(params DownTrackSpreaderParams) *DownTrackSpreader {
|
||||
d := &DownTrackSpreader{
|
||||
func NewDownTrackSpreader[T sender](params DownTrackSpreaderParams) *DownTrackSpreader[T] {
|
||||
d := &DownTrackSpreader[T]{
|
||||
params: params,
|
||||
downTracks: make(map[livekit.ParticipantID]TrackSender),
|
||||
downTracks: make(map[livekit.ParticipantID]T),
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) GetDownTracks() []TrackSender {
|
||||
func (d *DownTrackSpreader[T]) GetDownTracks() []T {
|
||||
d.downTrackMu.RLock()
|
||||
defer d.downTrackMu.RUnlock()
|
||||
return d.downTracksShadow
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) ResetAndGetDownTracks() []TrackSender {
|
||||
func (d *DownTrackSpreader[T]) ResetAndGetDownTracks() []T {
|
||||
d.downTrackMu.Lock()
|
||||
defer d.downTrackMu.Unlock()
|
||||
|
||||
downTracks := d.downTracksShadow
|
||||
|
||||
d.downTracks = make(map[livekit.ParticipantID]TrackSender)
|
||||
d.downTracks = make(map[livekit.ParticipantID]T)
|
||||
d.downTracksShadow = nil
|
||||
|
||||
return downTracks
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) Store(ts TrackSender) {
|
||||
func (d *DownTrackSpreader[T]) Store(sender T) {
|
||||
d.downTrackMu.Lock()
|
||||
defer d.downTrackMu.Unlock()
|
||||
|
||||
d.downTracks[ts.SubscriberID()] = ts
|
||||
d.downTracks[sender.SubscriberID()] = sender
|
||||
d.shadowDownTracks()
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) Free(subscriberID livekit.ParticipantID) {
|
||||
func (d *DownTrackSpreader[T]) Free(subscriberID livekit.ParticipantID) {
|
||||
d.downTrackMu.Lock()
|
||||
defer d.downTrackMu.Unlock()
|
||||
|
||||
@@ -78,7 +82,7 @@ func (d *DownTrackSpreader) Free(subscriberID livekit.ParticipantID) {
|
||||
d.shadowDownTracks()
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) HasDownTrack(subscriberID livekit.ParticipantID) bool {
|
||||
func (d *DownTrackSpreader[T]) HasDownTrack(subscriberID livekit.ParticipantID) bool {
|
||||
d.downTrackMu.RLock()
|
||||
defer d.downTrackMu.RUnlock()
|
||||
|
||||
@@ -86,7 +90,7 @@ func (d *DownTrackSpreader) HasDownTrack(subscriberID livekit.ParticipantID) boo
|
||||
return ok
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) Broadcast(writer func(TrackSender)) {
|
||||
func (d *DownTrackSpreader[T]) Broadcast(writer func(T)) {
|
||||
downTracks := d.GetDownTracks()
|
||||
if len(downTracks) == 0 {
|
||||
return
|
||||
@@ -103,14 +107,14 @@ func (d *DownTrackSpreader) Broadcast(writer func(TrackSender)) {
|
||||
utils.ParallelExec(downTracks, threshold, step, writer)
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) DownTrackCount() int {
|
||||
func (d *DownTrackSpreader[T]) DownTrackCount() int {
|
||||
d.downTrackMu.RLock()
|
||||
defer d.downTrackMu.RUnlock()
|
||||
return len(d.downTracksShadow)
|
||||
}
|
||||
|
||||
func (d *DownTrackSpreader) shadowDownTracks() {
|
||||
d.downTracksShadow = make([]TrackSender, 0, len(d.downTracks))
|
||||
func (d *DownTrackSpreader[T]) shadowDownTracks() {
|
||||
d.downTracksShadow = make([]T, 0, len(d.downTracks))
|
||||
for _, dt := range d.downTracks {
|
||||
d.downTracksShadow = append(d.downTracksShadow, dt)
|
||||
}
|
||||
+215
-27
@@ -21,6 +21,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
@@ -43,6 +45,7 @@ import (
|
||||
"github.com/livekit/protocol/signalling"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/transport/transportfakes"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
@@ -88,7 +91,7 @@ type RTCClient struct {
|
||||
// remote tracks waiting to be processed
|
||||
pendingRemoteTracks []*webrtc.TrackRemote
|
||||
|
||||
pendingTrackWriters []*TrackWriter
|
||||
pendingTrackWriters []TrackWriter
|
||||
OnConnected func()
|
||||
OnDataReceived func(data []byte, sid string)
|
||||
OnDataUnlabeledReceived func(data []byte)
|
||||
@@ -99,6 +102,11 @@ type RTCClient struct {
|
||||
bytesReceived map[livekit.ParticipantID]uint64
|
||||
|
||||
subscriptionResponse atomic.Pointer[livekit.SubscriptionResponse]
|
||||
|
||||
nextDataTrackHandle atomic.Uint32
|
||||
pendingPublishedDataTracks map[uint16]*livekit.DataTrackInfo
|
||||
pendingDataTrackWriters []TrackWriter
|
||||
subscribedDataTracks map[livekit.ParticipantID]map[uint16]*DataTrackRemote
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -209,17 +217,20 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
var err error
|
||||
|
||||
c := &RTCClient{
|
||||
useSinglePeerConnection: useSinglePeerConnection,
|
||||
conn: conn,
|
||||
localTracks: make(map[string]webrtc.TrackLocal),
|
||||
trackSenders: make(map[string]*webrtc.RTPSender),
|
||||
pendingPublishedTracks: make(map[string]*livekit.TrackInfo),
|
||||
subscribedTracks: make(map[livekit.ParticipantID][]*webrtc.TrackRemote),
|
||||
remoteParticipants: make(map[livekit.ParticipantID]*livekit.ParticipantInfo),
|
||||
me: &webrtc.MediaEngine{},
|
||||
lastPackets: make(map[livekit.ParticipantID]*rtp.Packet),
|
||||
bytesReceived: make(map[livekit.ParticipantID]uint64),
|
||||
useSinglePeerConnection: useSinglePeerConnection,
|
||||
conn: conn,
|
||||
localTracks: make(map[string]webrtc.TrackLocal),
|
||||
trackSenders: make(map[string]*webrtc.RTPSender),
|
||||
pendingPublishedTracks: make(map[string]*livekit.TrackInfo),
|
||||
subscribedTracks: make(map[livekit.ParticipantID][]*webrtc.TrackRemote),
|
||||
remoteParticipants: make(map[livekit.ParticipantID]*livekit.ParticipantInfo),
|
||||
me: &webrtc.MediaEngine{},
|
||||
lastPackets: make(map[livekit.ParticipantID]*rtp.Packet),
|
||||
bytesReceived: make(map[livekit.ParticipantID]uint64),
|
||||
pendingPublishedDataTracks: make(map[uint16]*livekit.DataTrackInfo),
|
||||
subscribedDataTracks: make(map[livekit.ParticipantID]map[uint16]*DataTrackRemote),
|
||||
}
|
||||
c.nextDataTrackHandle.Store(uint32(rand.IntN(8192)))
|
||||
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||
|
||||
conf := rtc.WebRTCConfig{
|
||||
@@ -274,6 +285,7 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
DatachannelMaxReceiverBufferSize: 1500,
|
||||
DatachannelSlowThreshold: 1024 * 1024 * 1024,
|
||||
FireOnTrackBySdp: true,
|
||||
EnableDataTracks: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -287,6 +299,7 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
})
|
||||
publisherHandler.OnDataMessageCalls(c.handleDataMessage)
|
||||
publisherHandler.OnDataMessageUnlabeledCalls(c.handleDataMessageUnlabeled)
|
||||
publisherHandler.OnDataTrackMessageCalls(c.handleDataTrackMessage)
|
||||
publisherHandler.OnInitialConnectedCalls(func() {
|
||||
logger.Debugw("publisher initial connected", "participant", c.localParticipant.Identity)
|
||||
|
||||
@@ -297,9 +310,15 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
logger.Errorw("track writer error", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.pendingTrackWriters = nil
|
||||
|
||||
for _, dtw := range c.pendingDataTrackWriters {
|
||||
if err := dtw.Start(); err != nil {
|
||||
logger.Errorw("data track writer error", err)
|
||||
}
|
||||
}
|
||||
c.pendingDataTrackWriters = nil
|
||||
|
||||
if c.OnConnected != nil {
|
||||
go c.OnConnected()
|
||||
}
|
||||
@@ -317,6 +336,12 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.publisher.CreateDataChannel("pubraw", &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ordered = false
|
||||
maxRetransmits := uint16(0)
|
||||
if err := c.publisher.CreateDataChannel(rtc.LossyDataChannel, &webrtc.DataChannelInit{
|
||||
@@ -326,8 +351,9 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.publisher.CreateDataChannel("pubraw", &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
if err := c.publisher.CreateDataChannel(rtc.DataTrackDataChannel, &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
MaxRetransmits: &maxRetransmits,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -342,12 +368,13 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
DatachannelMaxReceiverBufferSize: 1500,
|
||||
DatachannelSlowThreshold: 1024 * 1024 * 1024,
|
||||
FireOnTrackBySdp: true,
|
||||
EnableDataTracks: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ordered := false
|
||||
ordered := true
|
||||
if err := c.subscriber.CreateReadableDataChannel("subraw", &webrtc.DataChannelInit{
|
||||
Ordered: &ordered,
|
||||
}); err != nil {
|
||||
@@ -365,6 +392,7 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
})
|
||||
subscriberHandler.OnDataMessageCalls(c.handleDataMessage)
|
||||
subscriberHandler.OnDataMessageUnlabeledCalls(c.handleDataMessageUnlabeled)
|
||||
subscriberHandler.OnDataTrackMessageCalls(c.handleDataTrackMessage)
|
||||
subscriberHandler.OnInitialConnectedCalls(func() {
|
||||
logger.Debugw("subscriber initial connected", "participant", c.localParticipant.Identity)
|
||||
|
||||
@@ -375,9 +403,15 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
logger.Errorw("track writer error", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.pendingTrackWriters = nil
|
||||
|
||||
for _, dtw := range c.pendingDataTrackWriters {
|
||||
if err := dtw.Start(); err != nil {
|
||||
logger.Errorw("data track writer error", err)
|
||||
}
|
||||
}
|
||||
c.pendingDataTrackWriters = nil
|
||||
|
||||
if c.OnConnected != nil {
|
||||
go c.OnConnected()
|
||||
}
|
||||
@@ -399,6 +433,8 @@ func NewRTCClient(conn *websocket.Conn, useSinglePeerConnection bool, opts *Opti
|
||||
},
|
||||
})
|
||||
})
|
||||
} else {
|
||||
go c.ensurePublisherConnected()
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
@@ -460,7 +496,12 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
c.subscriberAsPrimary.Store(true)
|
||||
}
|
||||
|
||||
logger.Infow("join accepted, awaiting offer", "participant", msg.Join.Participant.Identity)
|
||||
if c.subscriber != nil {
|
||||
logger.Infow("join accepted, awaiting offer", "participant", msg.Join.Participant.Identity)
|
||||
} else {
|
||||
logger.Infow("join accepted", "participant", msg.Join.Participant.Identity)
|
||||
}
|
||||
|
||||
case *livekit.SignalResponse_Answer:
|
||||
logger.Infow(
|
||||
"received server answer",
|
||||
@@ -468,6 +509,7 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
"answer", msg.Answer.Sdp,
|
||||
)
|
||||
c.handleAnswer(signalling.FromProtoSessionDescription(msg.Answer))
|
||||
|
||||
case *livekit.SignalResponse_Offer:
|
||||
desc, offerId, midToTrackID := signalling.FromProtoSessionDescription(msg.Offer)
|
||||
logger.Infow(
|
||||
@@ -478,6 +520,7 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
"midToTrackID", midToTrackID,
|
||||
)
|
||||
c.handleOffer(desc, offerId, midToTrackID)
|
||||
|
||||
case *livekit.SignalResponse_Trickle:
|
||||
candidateInit, err := signalling.FromProtoTrickle(msg.Trickle)
|
||||
if err != nil {
|
||||
@@ -488,6 +531,7 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
} else {
|
||||
c.subscriber.AddICECandidate(candidateInit)
|
||||
}
|
||||
|
||||
case *livekit.SignalResponse_Update:
|
||||
c.lock.Lock()
|
||||
for _, p := range msg.Update.Participants {
|
||||
@@ -502,15 +546,22 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
c.lock.Unlock()
|
||||
|
||||
case *livekit.SignalResponse_TrackPublished:
|
||||
logger.Debugw("track published", "trackID", msg.TrackPublished.Track.Name, "participant", c.localParticipant.Sid,
|
||||
"cid", msg.TrackPublished.Cid, "trackSid", msg.TrackPublished.Track.Sid)
|
||||
logger.Debugw(
|
||||
"track published",
|
||||
"participant", c.localParticipant.Identity,
|
||||
"cid", msg.TrackPublished.Cid,
|
||||
"trackID", msg.TrackPublished.Track.Sid,
|
||||
"trackName", msg.TrackPublished.Track.Name,
|
||||
)
|
||||
c.lock.Lock()
|
||||
c.pendingPublishedTracks[msg.TrackPublished.Cid] = msg.TrackPublished.Track
|
||||
c.lock.Unlock()
|
||||
|
||||
case *livekit.SignalResponse_RefreshToken:
|
||||
c.lock.Lock()
|
||||
c.refreshToken = msg.RefreshToken
|
||||
c.lock.Unlock()
|
||||
|
||||
case *livekit.SignalResponse_TrackUnpublished:
|
||||
sid := msg.TrackUnpublished.TrackSid
|
||||
c.lock.Lock()
|
||||
@@ -523,10 +574,13 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
delete(c.trackSenders, sid)
|
||||
delete(c.localTracks, sid)
|
||||
c.lock.Unlock()
|
||||
|
||||
case *livekit.SignalResponse_Pong:
|
||||
c.pongReceivedAt.Store(msg.Pong)
|
||||
|
||||
case *livekit.SignalResponse_SubscriptionResponse:
|
||||
c.subscriptionResponse.Store(msg.SubscriptionResponse)
|
||||
|
||||
case *livekit.SignalResponse_MediaSectionsRequirement:
|
||||
logger.Infow(
|
||||
"received media sections requirement",
|
||||
@@ -535,6 +589,58 @@ func (c *RTCClient) handleSignalResponse(res *livekit.SignalResponse) error {
|
||||
"numVideos", msg.MediaSectionsRequirement.NumVideos,
|
||||
)
|
||||
c.handleMediaSectionsRequirement(msg.MediaSectionsRequirement)
|
||||
|
||||
case *livekit.SignalResponse_PublishDataTrackResponse:
|
||||
logger.Debugw(
|
||||
"data track published",
|
||||
"participant", c.localParticipant.Identity,
|
||||
"trackID", msg.PublishDataTrackResponse.Info.Sid,
|
||||
"trackHandle", msg.PublishDataTrackResponse.Info.PubHandle,
|
||||
"trackName", msg.PublishDataTrackResponse.Info.Name,
|
||||
)
|
||||
c.lock.Lock()
|
||||
c.pendingPublishedDataTracks[uint16(msg.PublishDataTrackResponse.Info.PubHandle)] = msg.PublishDataTrackResponse.Info
|
||||
c.lock.Unlock()
|
||||
|
||||
case *livekit.SignalResponse_DataTrackSubscriberHandles:
|
||||
logger.Infow(
|
||||
"received data track subscriber handles",
|
||||
"participant", c.localParticipant.Identity,
|
||||
"handles", msg.DataTrackSubscriberHandles.SubHandles,
|
||||
)
|
||||
c.lock.Lock()
|
||||
// create new remote data tracks if one does not exist for a handle
|
||||
for handle, publishedDataTrack := range msg.DataTrackSubscriberHandles.SubHandles {
|
||||
publisherID := livekit.ParticipantID(publishedDataTrack.PublisherSid)
|
||||
tracks := c.subscribedDataTracks[publisherID]
|
||||
if tracks == nil {
|
||||
c.subscribedDataTracks[publisherID] = make(map[uint16]*DataTrackRemote)
|
||||
tracks = c.subscribedDataTracks[publisherID]
|
||||
}
|
||||
if tracks[uint16(handle)] == nil {
|
||||
tracks[uint16(handle)] = NewDataTrackRemote(
|
||||
livekit.ParticipantIdentity(publishedDataTrack.PublisherIdentity),
|
||||
livekit.ParticipantID(publishedDataTrack.PublisherSid),
|
||||
uint16(handle),
|
||||
livekit.TrackID(publishedDataTrack.TrackSid),
|
||||
logger.GetLogger().WithValues("participant", c.localParticipant.Identity, "pID", c.localParticipant.Sid),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// delete remote data tracks that have gone away
|
||||
for publisherID, tracks := range c.subscribedDataTracks {
|
||||
for handle, dataTrackRemote := range tracks {
|
||||
if msg.DataTrackSubscriberHandles.SubHandles[uint32(handle)] == nil {
|
||||
dataTrackRemote.Close()
|
||||
delete(tracks, handle)
|
||||
if len(tracks) == 0 {
|
||||
delete(c.subscribedDataTracks, publisherID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
c.lock.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -596,9 +702,16 @@ func (c *RTCClient) SubscribedTracks() map[livekit.ParticipantID][]*webrtc.Track
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
tracks := make(map[livekit.ParticipantID][]*webrtc.TrackRemote, len(c.subscribedTracks))
|
||||
for key, val := range c.subscribedTracks {
|
||||
tracks[key] = val
|
||||
}
|
||||
maps.Copy(tracks, c.subscribedTracks)
|
||||
return tracks
|
||||
}
|
||||
|
||||
func (c *RTCClient) SubscribedDataTracks() map[livekit.ParticipantID]map[uint16]*DataTrackRemote {
|
||||
// create a copy of this
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
tracks := make(map[livekit.ParticipantID]map[uint16]*DataTrackRemote, len(c.subscribedDataTracks))
|
||||
maps.Copy(tracks, c.subscribedDataTracks)
|
||||
return tracks
|
||||
}
|
||||
|
||||
@@ -720,7 +833,7 @@ func AddTrackNoWriter() AddTrackOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RTCClient) AddTrack(track *webrtc.TrackLocalStaticSample, path string, opts ...AddTrackOption) (writer *TrackWriter, err error) {
|
||||
func (c *RTCClient) AddTrack(track *webrtc.TrackLocalStaticSample, path string, opts ...AddTrackOption) (writer TrackWriter, err error) {
|
||||
var params AddTrackParams
|
||||
for _, opt := range opts {
|
||||
opt(¶ms)
|
||||
@@ -789,11 +902,11 @@ func (c *RTCClient) AddTrack(track *webrtc.TrackLocalStaticSample, path string,
|
||||
return
|
||||
}
|
||||
|
||||
func (c *RTCClient) AddStaticTrack(mime string, id string, label string, opts ...AddTrackOption) (writer *TrackWriter, err error) {
|
||||
func (c *RTCClient) AddStaticTrack(mime string, id string, label string, opts ...AddTrackOption) (writer TrackWriter, err error) {
|
||||
return c.AddStaticTrackWithCodec(webrtc.RTPCodecCapability{MimeType: mime}, id, label, opts...)
|
||||
}
|
||||
|
||||
func (c *RTCClient) AddStaticTrackWithCodec(codec webrtc.RTPCodecCapability, id string, label string, opts ...AddTrackOption) (writer *TrackWriter, err error) {
|
||||
func (c *RTCClient) AddStaticTrackWithCodec(codec webrtc.RTPCodecCapability, id string, label string, opts ...AddTrackOption) (writer TrackWriter, err error) {
|
||||
track, err := webrtc.NewTrackLocalStaticSample(codec, id, label)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -802,7 +915,7 @@ func (c *RTCClient) AddStaticTrackWithCodec(codec webrtc.RTPCodecCapability, id
|
||||
return c.AddTrack(track, "", opts...)
|
||||
}
|
||||
|
||||
func (c *RTCClient) AddFileTrack(path string, id string, label string) (writer *TrackWriter, err error) {
|
||||
func (c *RTCClient) AddFileTrack(path string, id string, label string) (writer TrackWriter, err error) {
|
||||
// determine file mime
|
||||
mime, ok := extMimeMapping[filepath.Ext(path)]
|
||||
if !ok {
|
||||
@@ -867,6 +980,60 @@ func (c *RTCClient) PublishDataUnlabeled(data []byte) error {
|
||||
return c.publisher.SendDataMessageUnlabeled(data, true, "test")
|
||||
}
|
||||
|
||||
func (c *RTCClient) PublishDataTrack() (writer TrackWriter, err error) {
|
||||
if err = c.ensurePublisherConnected(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataTrackHandle := uint16(c.nextDataTrackHandle.Inc())
|
||||
if err = c.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_PublishDataTrackRequest{
|
||||
PublishDataTrackRequest: &livekit.PublishDataTrackRequest{
|
||||
PubHandle: uint32(dataTrackHandle),
|
||||
Name: fmt.Sprintf("data_track_%d", dataTrackHandle),
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// wait till data track published message is received
|
||||
timeout := time.After(5 * time.Second)
|
||||
var dti *livekit.DataTrackInfo
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return nil, errors.New("could not publish data track after timeout")
|
||||
default:
|
||||
c.lock.Lock()
|
||||
dti = c.pendingPublishedDataTracks[dataTrackHandle]
|
||||
if dti != nil {
|
||||
delete(c.pendingPublishedDataTracks, dataTrackHandle)
|
||||
c.lock.Unlock()
|
||||
break
|
||||
}
|
||||
c.lock.Unlock()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
if dti != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
writer = NewDataTrackWriter(c.ctx, dataTrackHandle, c.publisher)
|
||||
|
||||
// write data tracks only after connection established
|
||||
if c.hasPrimaryEverConnected() {
|
||||
err = writer.Start()
|
||||
} else {
|
||||
c.pendingDataTrackWriters = append(c.pendingDataTrackWriters, writer)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *RTCClient) GetPublishedTrackIDs() []string {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@@ -925,6 +1092,27 @@ func (c *RTCClient) handleDataMessageUnlabeled(data []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RTCClient) handleDataTrackMessage(data []byte) {
|
||||
var packet datatrack.Packet
|
||||
if err := packet.Unmarshal(data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var dataTrackRemote *DataTrackRemote
|
||||
c.lock.Lock()
|
||||
for _, tracks := range c.subscribedDataTracks {
|
||||
if tracks[packet.Handle] != nil {
|
||||
dataTrackRemote = tracks[packet.Handle]
|
||||
break
|
||||
}
|
||||
}
|
||||
c.lock.Unlock()
|
||||
|
||||
if dataTrackRemote != nil {
|
||||
dataTrackRemote.PacketReceived(&packet)
|
||||
}
|
||||
}
|
||||
|
||||
// handles a server initiated offer, handle on subscriber PC
|
||||
func (c *RTCClient) handleOffer(desc webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) {
|
||||
logger.Infow("handling server offer", "participant", c.localParticipant.Identity)
|
||||
@@ -944,7 +1132,7 @@ func (c *RTCClient) handleAnswer(desc webrtc.SessionDescription, answerId uint32
|
||||
// the client handles media sections requirement on the publisher PC
|
||||
func (c *RTCClient) handleMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) {
|
||||
addTransceivers := func(kind webrtc.RTPCodecType, count uint32) {
|
||||
for i := uint32(0); i < count; i++ {
|
||||
for range count {
|
||||
if _, err := c.publisher.AddTransceiverFromKind(
|
||||
kind,
|
||||
webrtc.RTPTransceiverInit{
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/frostbyte73/core"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
type DataTrackRemote struct {
|
||||
publisherIdentity livekit.ParticipantIdentity
|
||||
publisherID livekit.ParticipantID
|
||||
handle uint16
|
||||
trackID livekit.TrackID
|
||||
logger logger.Logger
|
||||
numReceivedPackets atomic.Uint32
|
||||
|
||||
closed core.Fuse
|
||||
}
|
||||
|
||||
func NewDataTrackRemote(
|
||||
publisherIdentity livekit.ParticipantIdentity,
|
||||
publisherID livekit.ParticipantID,
|
||||
handle uint16,
|
||||
trackID livekit.TrackID,
|
||||
logger logger.Logger,
|
||||
) *DataTrackRemote {
|
||||
logger.Infow(
|
||||
"creating data track remote",
|
||||
"publisherIdentity", publisherIdentity,
|
||||
"publisherID", publisherID,
|
||||
"handle", handle,
|
||||
"trackID", trackID,
|
||||
)
|
||||
return &DataTrackRemote{
|
||||
publisherIdentity: publisherIdentity,
|
||||
publisherID: publisherID,
|
||||
handle: handle,
|
||||
trackID: trackID,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataTrackRemote) Close() {
|
||||
d.logger.Infow(
|
||||
"closing data track remote",
|
||||
"publisherIdentity", d.publisherIdentity,
|
||||
"publisherID", d.publisherID,
|
||||
"handle", d.handle,
|
||||
"trackID", d.trackID,
|
||||
)
|
||||
d.closed.Break()
|
||||
}
|
||||
|
||||
func (d *DataTrackRemote) Handle() uint16 {
|
||||
return d.handle
|
||||
}
|
||||
|
||||
func (d *DataTrackRemote) ID() livekit.TrackID {
|
||||
return d.trackID
|
||||
}
|
||||
|
||||
func (d *DataTrackRemote) PacketReceived(packet *datatrack.Packet) {
|
||||
if d.closed.IsBroken() {
|
||||
return
|
||||
}
|
||||
|
||||
valid := true
|
||||
if len(packet.Payload) == 0 {
|
||||
valid = false
|
||||
}
|
||||
for i := range len(packet.Payload) {
|
||||
if packet.Payload[i] != byte(255-i) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if valid {
|
||||
d.numReceivedPackets.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataTrackRemote) NumReceivedPackets() uint32 {
|
||||
return d.numReceivedPackets.Load()
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type dataTrackWriter struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
handle uint16
|
||||
transport types.DataTrackTransport
|
||||
}
|
||||
|
||||
func NewDataTrackWriter(ctx context.Context, handle uint16, transport types.DataTrackTransport) TrackWriter {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &dataTrackWriter{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
handle: handle,
|
||||
transport: transport,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dataTrackWriter) Start() error {
|
||||
go d.writeFrames()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dataTrackWriter) Stop() {
|
||||
d.cancel()
|
||||
}
|
||||
|
||||
func (d *dataTrackWriter) writeFrames() {
|
||||
seqNum := uint16(0)
|
||||
frameNum := uint16(0)
|
||||
for {
|
||||
select {
|
||||
case <-d.ctx.Done():
|
||||
return
|
||||
|
||||
default:
|
||||
packets := datatrack.GenerateRawDataPackets(d.handle, seqNum, frameNum, 1, rand.Intn(2048)+1, 100*time.Millisecond)
|
||||
for _, packet := range packets {
|
||||
if err := d.transport.SendDataTrackMessage(packet); err != nil {
|
||||
logger.Errorw("could not send data track packet", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(packets) != 0 {
|
||||
var lastPacket datatrack.Packet
|
||||
if err := lastPacket.Unmarshal(packets[len(packets)-1]); err == nil {
|
||||
seqNum = lastPacket.SequenceNumber + 1
|
||||
frameNum = lastPacket.FrameNumber + 1
|
||||
}
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-11
@@ -30,9 +30,14 @@ import (
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type TrackWriter interface {
|
||||
Start() error
|
||||
Stop()
|
||||
}
|
||||
|
||||
// Writes a file to an RTP track.
|
||||
// makes it easier to debug and create RTP streams
|
||||
type TrackWriter struct {
|
||||
type trackWriter struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
track *webrtc.TrackLocalStaticSample
|
||||
@@ -45,9 +50,9 @@ type TrackWriter struct {
|
||||
h264 *h264reader.H264Reader
|
||||
}
|
||||
|
||||
func NewTrackWriter(ctx context.Context, track *webrtc.TrackLocalStaticSample, filePath string) *TrackWriter {
|
||||
func NewTrackWriter(ctx context.Context, track *webrtc.TrackLocalStaticSample, filePath string) TrackWriter {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &TrackWriter{
|
||||
return &trackWriter{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
track: track,
|
||||
@@ -56,7 +61,7 @@ func NewTrackWriter(ctx context.Context, track *webrtc.TrackLocalStaticSample, f
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TrackWriter) Start() error {
|
||||
func (w *trackWriter) Start() error {
|
||||
if w.filePath == "" {
|
||||
go w.writeNull()
|
||||
return nil
|
||||
@@ -95,11 +100,11 @@ func (w *TrackWriter) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *TrackWriter) Stop() {
|
||||
func (w *trackWriter) Stop() {
|
||||
w.cancel()
|
||||
}
|
||||
|
||||
func (w *TrackWriter) writeNull() {
|
||||
func (w *trackWriter) writeNull() {
|
||||
defer w.onWriteComplete()
|
||||
sample := media.Sample{Data: []byte{0x0, 0xff, 0xff, 0xff, 0xff}, Duration: 30 * time.Millisecond}
|
||||
h264Sample := media.Sample{Data: []byte{0x00, 0x00, 0x00, 0x01, 0x7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x8, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x5, 0xff, 0xff, 0xff, 0xff}, Duration: 30 * time.Millisecond}
|
||||
@@ -117,7 +122,7 @@ func (w *TrackWriter) writeNull() {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TrackWriter) writeOgg() {
|
||||
func (w *trackWriter) writeOgg() {
|
||||
// Keep track of last granule, the difference is the amount of samples in the buffer
|
||||
var lastGranule uint64
|
||||
for {
|
||||
@@ -150,7 +155,7 @@ func (w *TrackWriter) writeOgg() {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TrackWriter) writeVP8() {
|
||||
func (w *trackWriter) writeVP8() {
|
||||
// Send our video file frame at a time. Pace our sending such that we send it at the same speed it should be played back as.
|
||||
// This isn't required since the video is timestamped, but we will such much higher loss if we send all at once.
|
||||
sleepTime := time.Millisecond * time.Duration((float32(w.ivfheader.TimebaseNumerator)/float32(w.ivfheader.TimebaseDenominator))*1000)
|
||||
@@ -178,10 +183,9 @@ func (w *TrackWriter) writeVP8() {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TrackWriter) writeH264() {
|
||||
func (w *trackWriter) writeH264() {
|
||||
// TODO: this is harder
|
||||
}
|
||||
|
||||
func (w *TrackWriter) onWriteComplete() {
|
||||
|
||||
func (w *trackWriter) onWriteComplete() {
|
||||
}
|
||||
@@ -154,6 +154,7 @@ func createSingleNodeServer(configUpdater func(*config.Config)) *service.Livekit
|
||||
panic(fmt.Sprintf("could not create config: %v", err))
|
||||
}
|
||||
conf.Keys = map[string]string{testApiKey: testApiSecret}
|
||||
conf.EnableDataTracks = true
|
||||
if configUpdater != nil {
|
||||
configUpdater(conf)
|
||||
}
|
||||
@@ -184,6 +185,7 @@ func createMultiNodeServer(nodeID string, port uint32) *service.LivekitServer {
|
||||
conf.RTC.TCPPort = port + 2
|
||||
conf.Redis.Address = "localhost:6379"
|
||||
conf.Keys = map[string]string{testApiKey: testApiSecret}
|
||||
conf.EnableDataTracks = true
|
||||
|
||||
currentNode, err := routing.NewLocalNode(conf)
|
||||
if err != nil {
|
||||
@@ -302,7 +304,7 @@ func listRoomToken() string {
|
||||
return t
|
||||
}
|
||||
|
||||
func stopWriters(writers ...*testclient.TrackWriter) {
|
||||
func stopWriters(writers ...testclient.TrackWriter) {
|
||||
for _, w := range writers {
|
||||
w.Stop()
|
||||
}
|
||||
|
||||
@@ -98,7 +98,6 @@ func TestConnectWithoutCreation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// testing multiple scenarios rooms
|
||||
func TestMultinodePublishingUponJoining(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
@@ -169,6 +168,7 @@ func TestMultinodeDataPublishing(t *testing.T) {
|
||||
|
||||
scenarioDataPublish(t)
|
||||
scenarioDataUnlabeledPublish(t)
|
||||
scenarioDataTracksPublishingUponJoining(t)
|
||||
}
|
||||
|
||||
func TestMultiNodeJoinAfterClose(t *testing.T) {
|
||||
|
||||
+108
-5
@@ -148,7 +148,7 @@ func scenarioReceiveBeforePublish(t *testing.T) {
|
||||
|
||||
func scenarioDataPublish(t *testing.T) {
|
||||
for _, useSinglePeerConnection := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
t.Run(fmt.Sprintf("scenarioDataPublish/singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
c1 := createRTCClient("dp1", defaultServerPort, useSinglePeerConnection, nil)
|
||||
c2 := createRTCClient("dp2", secondServerPort, useSinglePeerConnection, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
@@ -178,7 +178,7 @@ func scenarioDataPublish(t *testing.T) {
|
||||
|
||||
func scenarioDataUnlabeledPublish(t *testing.T) {
|
||||
for _, useSinglePeerConnection := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
t.Run(fmt.Sprintf("scenarioDataUnlabeledPublish/singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
c1 := createRTCClient("dp1", defaultServerPort, useSinglePeerConnection, nil)
|
||||
c2 := createRTCClient("dp2", secondServerPort, useSinglePeerConnection, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
@@ -206,6 +206,93 @@ func scenarioDataUnlabeledPublish(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func scenarioDataTracksPublishingUponJoining(t *testing.T) {
|
||||
for _, useSinglePeerConnection := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("scenarioDataTracksPublishingUponJoining/singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
c1 := createRTCClient("dtpuj_1", defaultServerPort, useSinglePeerConnection, nil)
|
||||
c2 := createRTCClient("dtpuj_2", secondServerPort, useSinglePeerConnection, &testclient.Options{AutoSubscribe: true})
|
||||
c3 := createRTCClient("dtpuj_3", defaultServerPort, useSinglePeerConnection, &testclient.Options{AutoSubscribe: true})
|
||||
defer stopClients(c1, c2, c3)
|
||||
|
||||
waitUntilConnected(t, c1, c2, c3)
|
||||
|
||||
// c1 and c2 publishing, c3 just receiving
|
||||
writers := publishDataTracksForClients(t, c1, c2)
|
||||
defer stopWriters(writers...)
|
||||
|
||||
logger.Infow("waiting to receive data tracks from c1 and c2")
|
||||
testutils.WithTimeout(t, func() string {
|
||||
tracks := c3.SubscribedDataTracks()
|
||||
if len(tracks[c1.ID()]) != 2 {
|
||||
return "did not receive data tracks from c1"
|
||||
}
|
||||
if len(tracks[c2.ID()]) != 2 {
|
||||
return "did not receive data tracks from c2"
|
||||
}
|
||||
for _, dts := range tracks {
|
||||
for _, dt := range dts {
|
||||
if dt.NumReceivedPackets() == 0 {
|
||||
return fmt.Sprintf("no packets received from %s", dt.ID())
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// after a delay, c2 reconnects, then publishing
|
||||
time.Sleep(syncDelay)
|
||||
c2.Stop()
|
||||
|
||||
logger.Infow("waiting for c2 data tracks to be gone")
|
||||
testutils.WithTimeout(t, func() string {
|
||||
tracks := c3.SubscribedDataTracks()
|
||||
|
||||
if len(tracks[c1.ID()]) != 2 {
|
||||
return fmt.Sprintf("c3 should be subscribed to 2 data tracks from c1, actual: %d", len(tracks[c1.ID()]))
|
||||
}
|
||||
if len(tracks[c2.ID()]) != 0 {
|
||||
return fmt.Sprintf("c3 should be subscribed to 0 data tracks from c2, actual: %d", len(tracks[c2.ID()]))
|
||||
}
|
||||
if len(c1.SubscribedDataTracks()[c2.ID()]) != 0 {
|
||||
return fmt.Sprintf("c3 should be subscribed to 0 data tracks from c2, actual: %d", len(c1.SubscribedTracks()[c2.ID()]))
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
logger.Infow("c2 reconnecting")
|
||||
// connect to a diff port
|
||||
c2 = createRTCClient("dtpuj_2", defaultServerPort, useSinglePeerConnection, nil)
|
||||
defer c2.Stop()
|
||||
waitUntilConnected(t, c2)
|
||||
writers = publishDataTracksForClients(t, c2)
|
||||
defer stopWriters(writers...)
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
tracks := c3.SubscribedDataTracks()
|
||||
// new c2 data tracks should be published again
|
||||
if len(tracks[c2.ID()]) != 2 {
|
||||
return fmt.Sprintf("c3 should be subscribed to 2 data tracks from c2, actual: %d", len(tracks[c2.ID()]))
|
||||
}
|
||||
for _, dt := range tracks[c2.ID()] {
|
||||
if dt.NumReceivedPackets() == 0 {
|
||||
return fmt.Sprintf("c3 did not receive packets from c2 data track after reconnecting %s", dt.ID())
|
||||
}
|
||||
}
|
||||
|
||||
if len(c1.SubscribedDataTracks()[c2.ID()]) != 2 {
|
||||
return fmt.Sprintf("c1 should be subscribed to 2 data tracks from c2, actual: %d", len(c1.SubscribedTracks()[c2.ID()]))
|
||||
}
|
||||
for _, dt := range c1.SubscribedDataTracks()[c2.ID()] {
|
||||
if dt.NumReceivedPackets() == 0 {
|
||||
return fmt.Sprintf("c1 did not receive packets from c2 data track after reconnecting %s", dt.ID())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func scenarioJoinClosedRoom(t *testing.T) {
|
||||
for _, useSinglePeerConnection := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
@@ -240,15 +327,15 @@ func closeNonRTCRoom(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func publishTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []*testclient.TrackWriter {
|
||||
func publishTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []testclient.TrackWriter {
|
||||
logger.Infow("publishing tracks for clients")
|
||||
var writers []*testclient.TrackWriter
|
||||
var writers []testclient.TrackWriter
|
||||
for i := range clients {
|
||||
c := clients[i]
|
||||
tw, err := c.AddStaticTrack("audio/opus", "audio", "webcam")
|
||||
require.NoError(t, err)
|
||||
|
||||
writers = append(writers, tw)
|
||||
|
||||
tw, err = c.AddStaticTrack("video/vp8", "video", "webcam")
|
||||
require.NoError(t, err)
|
||||
writers = append(writers, tw)
|
||||
@@ -256,6 +343,22 @@ func publishTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []*
|
||||
return writers
|
||||
}
|
||||
|
||||
func publishDataTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []testclient.TrackWriter {
|
||||
logger.Infow("publishing data tracks for clients")
|
||||
var writers []testclient.TrackWriter
|
||||
for i := range clients {
|
||||
c := clients[i]
|
||||
dtw, err := c.PublishDataTrack()
|
||||
require.NoError(t, err)
|
||||
writers = append(writers, dtw)
|
||||
|
||||
dtw, err = c.PublishDataTrack()
|
||||
require.NoError(t, err)
|
||||
writers = append(writers, dtw)
|
||||
}
|
||||
return writers
|
||||
}
|
||||
|
||||
// Room service tests
|
||||
|
||||
func roomServiceListRoom(t *testing.T) {
|
||||
|
||||
+78
-6
@@ -166,7 +166,7 @@ func TestSinglePublisher(t *testing.T) {
|
||||
c2 := createRTCClient("c2", defaultServerPort, useSinglePeerConnection, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
|
||||
// publish a track and ensure clients receive it ok
|
||||
// publish an audio and video track and ensure clients receive it ok
|
||||
t1, err := c1.AddStaticTrack("audio/OPUS", "audio", "webcamaudio")
|
||||
require.NoError(t, err)
|
||||
defer t1.Stop()
|
||||
@@ -688,11 +688,7 @@ func TestDeviceCodecOverride(t *testing.T) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if desc == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return desc != nil
|
||||
}, waitTimeout, waitTick, "did not receive answer")
|
||||
|
||||
hasSeenVP8 := false
|
||||
@@ -1018,3 +1014,79 @@ func TestFireTrackBySdp(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSinglePublisherDataTrack(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
s, finish := setupSingleNodeTest("TestSinglePublisherDataTrack")
|
||||
defer finish()
|
||||
|
||||
for _, useSinglePeerConnection := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("singlePeerConnection=%+v", useSinglePeerConnection), func(t *testing.T) {
|
||||
c1 := createRTCClient("c1", defaultServerPort, useSinglePeerConnection, nil)
|
||||
c2 := createRTCClient("c2", defaultServerPort, useSinglePeerConnection, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
|
||||
// publish a couple of data tracks and ensure clients receive it ok
|
||||
dt1, err := c1.PublishDataTrack()
|
||||
require.NoError(t, err)
|
||||
defer dt1.Stop()
|
||||
|
||||
dt2, err := c1.PublishDataTrack()
|
||||
require.NoError(t, err)
|
||||
defer dt2.Stop()
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c2.SubscribedDataTracks()) == 0 {
|
||||
return "c2 was not subscribed to any data tracks"
|
||||
}
|
||||
// should have received two data tracks
|
||||
if len(c2.SubscribedDataTracks()[c1.ID()]) != 2 {
|
||||
return "c2 didn't subscribe to both data tracks from c1"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// a new client joins and should get the initial stream
|
||||
c3 := createRTCClient("c3", defaultServerPort, useSinglePeerConnection, nil)
|
||||
|
||||
// ensure that new client that has joined also received data tracks
|
||||
waitUntilConnected(t, c3)
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c3.SubscribedDataTracks()) == 0 {
|
||||
return "c3 didn't subscribe to any data tracks"
|
||||
}
|
||||
// should have received two data tracks
|
||||
if len(c3.SubscribedDataTracks()[c1.ID()]) != 2 {
|
||||
return "c3 didn't subscribe to tracks from c1"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// ensure that the data track ids are generated by server
|
||||
tracks := c3.SubscribedDataTracks()[c1.ID()]
|
||||
for _, tr := range tracks {
|
||||
require.True(t, strings.HasPrefix(string(tr.ID()), "DTR_"), "data track should begin with DTR")
|
||||
}
|
||||
|
||||
// when c3 disconnects, ensure subscriber is cleaned up correctly
|
||||
c3.Stop()
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
room := s.RoomManager().GetRoom(context.Background(), testRoom)
|
||||
p := room.GetParticipant("c1")
|
||||
require.NotNil(t, p)
|
||||
|
||||
for _, t := range p.GetPublishedDataTracks() {
|
||||
if t.IsSubscriber(c3.ID()) {
|
||||
return "c3 was not a subscriber of c1's data tracks"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user