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:
Raja Subramanian authored and GitHub committed 2025-12-04 10:44:34 +05:30
1 parent 7158d98366
commit 7954748d7a
52 files changed
+5897 -499

No files matched your search

+6 -6
View File
@@ -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
+12 -12
View File
@@ -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=
+2
View File
@@ -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 {
+104
View File
@@ -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
}
+152
View File
@@ -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())
}
+269
View File
@@ -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
}
+265
View File
@@ -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)
})
}
+74
View File
@@ -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
View File
@@ -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,
+158
View File
@@ -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)
}
}
+2 -2
View File
@@ -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)
+18
View File
@@ -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
View File
@@ -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)
}
}
}()
}
+1 -1
View File
@@ -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()
+70
View File
@@ -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]
}
+3
View File
@@ -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
}
+9
View File
@@ -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
+24
View File
@@ -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
}
File diff suppressed because it is too large. Load diff
+11 -9
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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()
}
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+658
View File
@@ -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)]
+733 -10
View File
@@ -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)]
+74
View File
@@ -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)]
+137
View File
@@ -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
}
+6 -1
View File
@@ -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
}
+2 -2
View File
@@ -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
View File
@@ -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,
})
+4 -3
View File
@@ -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),
}
+4 -3
View File
@@ -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
View File
@@ -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(&params)
@@ -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{
+86
View File
@@ -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()
}
+79
View File
@@ -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
View File
@@ -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() {
}
+3 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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 ""
})
})
}
}