mirror of
https://github.com/livekit/livekit.git
synced 2026-09-09 14:05:33 +00:00
Async attributes on participant.
How it is different from existing participant attributes? 1. Async attribute can be added one at a time. 2. These are not included in `ParticipantInfo`. 3. Get an attribute bt participant identity and async attribute ID as and when needed.
This commit is contained in:
@@ -164,3 +164,5 @@ require (
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/livekit/protocol => ../protocol
|
||||
|
||||
@@ -181,8 +181,6 @@ 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-20260521171458-ef117e280447 h1:AwzxgDnzGVVLZuSYDdgo7ETdpHzQPagyjtJOTrhQduY=
|
||||
github.com/livekit/mediatransportutil v0.0.0-20260521171458-ef117e280447/go.mod h1:RCd46PT+6sEztld6XpkCrG1xskb0u3SqxIjy4G897Ss=
|
||||
github.com/livekit/protocol v1.45.9-0.20260519061926-8381f2180c45 h1:gJQFJNjHuxeKroI6KTtVeXVcUMOaK8ksdiB6FoiDWmE=
|
||||
github.com/livekit/protocol v1.45.9-0.20260519061926-8381f2180c45/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k=
|
||||
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.7 h1:TCavZi10wF49bT6iQZ9eT2keGZQpC69MTDfdJej5e94=
|
||||
|
||||
@@ -89,6 +89,8 @@ type Config struct {
|
||||
|
||||
EnableDataTracks bool `yaml:"enable_data_tracks,omitempty"`
|
||||
|
||||
EnableParticipantAsyncAttributes bool `yaml:"enable_participant_async_attributes,omitempty"`
|
||||
|
||||
API APIConfig `yaml:"api,omitempty"`
|
||||
}
|
||||
|
||||
@@ -265,6 +267,8 @@ type RegionConfig struct {
|
||||
Lon float64 `yaml:"lon,omitempty"`
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
|
||||
type LimitConfig struct {
|
||||
NumTracks int32 `yaml:"num_tracks,omitempty"`
|
||||
BytesPerSec float32 `yaml:"bytes_per_sec,omitempty"`
|
||||
@@ -276,6 +280,8 @@ type LimitConfig struct {
|
||||
MaxRoomNameLength int `yaml:"max_room_name_length,omitempty"`
|
||||
MaxParticipantIdentityLength int `yaml:"max_participant_identity_length,omitempty"`
|
||||
MaxParticipantNameLength int `yaml:"max_participant_name_length,omitempty"`
|
||||
|
||||
MaxAsyncAttributesSize uint32 `yaml:"max_async_attributes_size,omitempty"`
|
||||
}
|
||||
|
||||
func (l LimitConfig) CheckRoomNameLength(name string) bool {
|
||||
@@ -306,6 +312,32 @@ func (l LimitConfig) CheckAttributesSize(attributes map[string]string) bool {
|
||||
return uint32(total) <= l.MaxAttributesSize
|
||||
}
|
||||
|
||||
func (l LimitConfig) CheckAsyncAttributesSize(asyncAttributes map[string][]byte) bool {
|
||||
if l.MaxAsyncAttributesSize == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
total := 0
|
||||
for k, v := range asyncAttributes {
|
||||
total += len(k) + len(v)
|
||||
}
|
||||
return uint32(total) <= l.MaxAsyncAttributesSize
|
||||
}
|
||||
|
||||
func (l LimitConfig) CanAddAsyncAttribute(asyncAttributes map[string][]byte, toAddKey string, toAddValue []byte) bool {
|
||||
if l.MaxAsyncAttributesSize == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
total := 0
|
||||
for k, v := range asyncAttributes {
|
||||
total += len(k) + len(v)
|
||||
}
|
||||
return uint32(total+len(toAddKey)+len(toAddValue)) <= l.MaxAsyncAttributesSize
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
|
||||
type IngressConfig struct {
|
||||
RTMPBaseURL string `yaml:"rtmp_base_url,omitempty"`
|
||||
WHIPBaseURL string `yaml:"whip_base_url,omitempty"`
|
||||
@@ -425,6 +457,7 @@ var DefaultConfig = Config{
|
||||
MaxRoomNameLength: 256,
|
||||
MaxParticipantIdentityLength: 256,
|
||||
MaxParticipantNameLength: 256,
|
||||
MaxAsyncAttributesSize: 256000,
|
||||
},
|
||||
Logging: LoggingConfig{
|
||||
PionLevel: "error",
|
||||
|
||||
+56
-50
@@ -174,56 +174,57 @@ type ParticipantParams struct {
|
||||
PLIThrottleConfig sfu.PLIThrottleConfig
|
||||
CongestionControlConfig config.CongestionControlConfig
|
||||
// codecs that are enabled for this room
|
||||
PublishEnabledCodecs []*livekit.Codec
|
||||
SubscribeEnabledCodecs []*livekit.Codec
|
||||
Logger logger.Logger
|
||||
LoggerResolver logger.DeferredFieldResolver
|
||||
Reporter roomobs.ParticipantSessionReporter
|
||||
ReporterResolver roomobs.ParticipantReporterResolver
|
||||
SimTracks map[uint32]interceptor.SimulcastTrackInfo
|
||||
Grants *auth.ClaimGrants
|
||||
InitialVersion uint32
|
||||
ClientConf *livekit.ClientConfiguration
|
||||
ClientInfo ClientInfo
|
||||
Region string
|
||||
Migration bool
|
||||
Reconnect bool
|
||||
AdaptiveStream bool
|
||||
AllowTCPFallback bool
|
||||
TCPFallbackRTTThreshold int
|
||||
AllowUDPUnstableFallback bool
|
||||
TURNSEnabled bool
|
||||
ParticipantListener types.LocalParticipantListener
|
||||
ParticipantHelper types.LocalParticipantHelper
|
||||
DisableSupervisor bool
|
||||
ReconnectOnPublicationError bool
|
||||
ReconnectOnSubscriptionError bool
|
||||
ReconnectOnDataChannelError bool
|
||||
VersionGenerator utils.TimedVersionGenerator
|
||||
DisableDynacast bool
|
||||
SubscriberAllowPause bool
|
||||
SubscriptionLimitAudio int32
|
||||
SubscriptionLimitVideo int32
|
||||
PlayoutDelay *livekit.PlayoutDelay
|
||||
SyncStreams bool
|
||||
ForwardStats *sfu.ForwardStats
|
||||
DisableSenderReportPassThrough bool
|
||||
MetricConfig metric.MetricConfig
|
||||
UseOneShotSignallingMode bool
|
||||
EnableMetrics bool
|
||||
DataChannelMaxBufferedAmount uint64
|
||||
DatachannelSlowThreshold int
|
||||
DatachannelLossyTargetLatency time.Duration
|
||||
FireOnTrackBySdp bool
|
||||
DisableCodecRegression bool
|
||||
LastPubReliableSeq uint32
|
||||
Country string
|
||||
PreferVideoSizeFromMedia bool
|
||||
UseSinglePeerConnection bool
|
||||
EnableDataTracks bool
|
||||
EnableRTPStreamRestartDetection bool
|
||||
ForceBackupCodecPolicySimulcast bool
|
||||
DisableTransceiverReuseForE2EE bool
|
||||
PublishEnabledCodecs []*livekit.Codec
|
||||
SubscribeEnabledCodecs []*livekit.Codec
|
||||
Logger logger.Logger
|
||||
LoggerResolver logger.DeferredFieldResolver
|
||||
Reporter roomobs.ParticipantSessionReporter
|
||||
ReporterResolver roomobs.ParticipantReporterResolver
|
||||
SimTracks map[uint32]interceptor.SimulcastTrackInfo
|
||||
Grants *auth.ClaimGrants
|
||||
InitialVersion uint32
|
||||
ClientConf *livekit.ClientConfiguration
|
||||
ClientInfo ClientInfo
|
||||
Region string
|
||||
Migration bool
|
||||
Reconnect bool
|
||||
AdaptiveStream bool
|
||||
AllowTCPFallback bool
|
||||
TCPFallbackRTTThreshold int
|
||||
AllowUDPUnstableFallback bool
|
||||
TURNSEnabled bool
|
||||
ParticipantListener types.LocalParticipantListener
|
||||
ParticipantHelper types.LocalParticipantHelper
|
||||
DisableSupervisor bool
|
||||
ReconnectOnPublicationError bool
|
||||
ReconnectOnSubscriptionError bool
|
||||
ReconnectOnDataChannelError bool
|
||||
VersionGenerator utils.TimedVersionGenerator
|
||||
DisableDynacast bool
|
||||
SubscriberAllowPause bool
|
||||
SubscriptionLimitAudio int32
|
||||
SubscriptionLimitVideo int32
|
||||
PlayoutDelay *livekit.PlayoutDelay
|
||||
SyncStreams bool
|
||||
ForwardStats *sfu.ForwardStats
|
||||
DisableSenderReportPassThrough bool
|
||||
MetricConfig metric.MetricConfig
|
||||
UseOneShotSignallingMode bool
|
||||
EnableMetrics bool
|
||||
DataChannelMaxBufferedAmount uint64
|
||||
DatachannelSlowThreshold int
|
||||
DatachannelLossyTargetLatency time.Duration
|
||||
FireOnTrackBySdp bool
|
||||
DisableCodecRegression bool
|
||||
LastPubReliableSeq uint32
|
||||
Country string
|
||||
PreferVideoSizeFromMedia bool
|
||||
UseSinglePeerConnection bool
|
||||
EnableDataTracks bool
|
||||
EnableRTPStreamRestartDetection bool
|
||||
ForceBackupCodecPolicySimulcast bool
|
||||
DisableTransceiverReuseForE2EE bool
|
||||
EnableParticipantAsyncAttributes bool
|
||||
}
|
||||
|
||||
type ParticipantImpl struct {
|
||||
@@ -332,6 +333,8 @@ type ParticipantImpl struct {
|
||||
rpcLock sync.Mutex
|
||||
rpcPendingAcks map[string]*utils.DataChannelRpcPendingAckHandler
|
||||
rpcPendingResponses map[string]*utils.DataChannelRpcPendingResponseHandler
|
||||
|
||||
asyncAttributes *ParticipantAsyncAttributes
|
||||
}
|
||||
|
||||
func NewParticipant(params ParticipantParams) (*ParticipantImpl, error) {
|
||||
@@ -370,6 +373,9 @@ func NewParticipant(params ParticipantParams) (*ParticipantImpl, error) {
|
||||
telemetryGuard: &telemetry.ReferenceGuard{},
|
||||
nextSubscribedDataTrackHandle: uint16(rand.Intn(256)),
|
||||
requireBroadcast: params.Grants.Metadata != "" || len(params.Grants.Attributes) != 0,
|
||||
asyncAttributes: NewParticipantAsyncAttributes(ParticipantAsyncAttributesParams{
|
||||
Logger: params.Logger,
|
||||
}),
|
||||
}
|
||||
p.setupSignalling()
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2026 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"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type ParticipantAsyncAttributesParams struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type ParticipantAsyncAttributes struct {
|
||||
params ParticipantAsyncAttributesParams
|
||||
lock sync.Mutex
|
||||
attributes map[string][]byte
|
||||
}
|
||||
|
||||
func NewParticipantAsyncAttributes(params ParticipantAsyncAttributesParams) *ParticipantAsyncAttributes {
|
||||
return &ParticipantAsyncAttributes{
|
||||
params: params,
|
||||
attributes: make(map[string][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParticipantAsyncAttributes) Add(id *livekit.DataTrackSchemaId, value []byte) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
if id == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.attributes[ToParticipantAsyncAttributeKey(id)] = value
|
||||
}
|
||||
|
||||
func (p *ParticipantAsyncAttributes) Delete(id *livekit.DataTrackSchemaId) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
if id == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delete(p.attributes, ToParticipantAsyncAttributeKey(id))
|
||||
}
|
||||
|
||||
func (p *ParticipantAsyncAttributes) Get(id *livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
value, ok := p.attributes[ToParticipantAsyncAttributeKey(id)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &livekit.DataTrackSchemaDefinition{
|
||||
Id: id,
|
||||
Definition: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParticipantAsyncAttributes) GetAll() map[string][]byte {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
return p.attributes
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
|
||||
func ToParticipantAsyncAttributeKey(id *livekit.DataTrackSchemaId) string {
|
||||
return fmt.Sprintf("%s/%d", id.Name, id.Encoding)
|
||||
}
|
||||
|
||||
func FromParticipantAsyncAttributeKey(key string) *livekit.DataTrackSchemaId {
|
||||
parts := strings.Split(key, "/")
|
||||
encoding, _ := strconv.Atoi(parts[1])
|
||||
return &livekit.DataTrackSchemaId{
|
||||
Name: parts[0],
|
||||
Encoding: livekit.DataTrackSchemaEncoding(encoding),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright 2026 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/types"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
func (p *ParticipantImpl) HandleDefineDataTrackSchemaRequest(req *livekit.DefineDataTrackSchemaRequest) {
|
||||
if !p.params.EnableParticipantAsyncAttributes {
|
||||
p.pubLogger.Warnw("async attributes not enabled", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_NOT_ALLOWED,
|
||||
Message: "async attributes not enabled",
|
||||
Request: &livekit.RequestResponse_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.SchemaDefinition == nil || req.SchemaDefinition.Id == nil || len(req.SchemaDefinition.Id.Name) == 0 || len(req.SchemaDefinition.Id.Name) > 256 {
|
||||
p.pubLogger.Warnw("aync attribute definition is invalid", nil, "req", logger.Proto(req))
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_INVALID_REQUEST,
|
||||
Message: "async attribute definition is invalid",
|
||||
Request: &livekit.RequestResponse_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.SchemaDefinition.Definition) == 0 {
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_INVALID_REQUEST,
|
||||
Message: "async attribute definition is empty",
|
||||
Request: &livekit.RequestResponse_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !p.params.LimitConfig.CanAddAsyncAttribute(
|
||||
p.asyncAttributes.GetAll(),
|
||||
ToParticipantAsyncAttributeKey(req.SchemaDefinition.Id),
|
||||
req.SchemaDefinition.Definition,
|
||||
) {
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_LIMIT_EXCEEDED,
|
||||
Message: "async attribute definition exceeds limit",
|
||||
Request: &livekit.RequestResponse_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
p.AddDataTrackSchema(req.SchemaDefinition)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) HandleGetDataTrackSchemaRequest(req *livekit.GetDataTrackSchemaRequest) {
|
||||
if req.SchemaId == nil {
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_INVALID_REQUEST,
|
||||
Message: "async attribute id is required",
|
||||
Request: &livekit.RequestResponse_GetDataTrackSchema{
|
||||
GetDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
p.listener().OnGetDataTrackSchema(p, req)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) AddDataTrackSchema(definition *livekit.DataTrackSchemaDefinition) {
|
||||
p.asyncAttributes.Add(definition.Id, definition.Definition)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) GetDataTrackSchema(id *livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition {
|
||||
return p.asyncAttributes.Get(id)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) ProcessGetDataTrackSchemaRequest(req *livekit.GetDataTrackSchemaRequest, publisher types.Participant) {
|
||||
if publisher == nil {
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_NOT_FOUND,
|
||||
Message: "participant not found",
|
||||
Request: &livekit.RequestResponse_GetDataTrackSchema{
|
||||
GetDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
asyncAttribute := publisher.GetDataTrackSchema(req.SchemaId)
|
||||
if asyncAttribute == nil {
|
||||
p.sendRequestResponse(&livekit.RequestResponse{
|
||||
Reason: livekit.RequestResponse_NOT_FOUND,
|
||||
Message: "async attribute not found",
|
||||
Request: &livekit.RequestResponse_GetDataTrackSchema{
|
||||
GetDataTrackSchema: utils.CloneProto(req),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
p.sendGetDataTrackSchemaResponse(asyncAttribute)
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright 2026 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 (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/config"
|
||||
"github.com/livekit/livekit-server/pkg/routing/routingfakes"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
|
||||
)
|
||||
|
||||
func newParticipantWithAsyncAttributes(t *testing.T, enabled bool, maxSize uint32) *ParticipantImpl {
|
||||
t.Helper()
|
||||
p := newParticipantForTest("test")
|
||||
p.params.EnableParticipantAsyncAttributes = enabled
|
||||
p.params.LimitConfig = config.LimitConfig{
|
||||
MaxAsyncAttributesSize: maxSize,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func lastRequestResponse(t *testing.T, sink *routingfakes.FakeMessageSink, idx int) *livekit.RequestResponse {
|
||||
t.Helper()
|
||||
msg := sink.WriteMessageArgsForCall(idx).(*livekit.SignalResponse)
|
||||
rr, ok := msg.Message.(*livekit.SignalResponse_RequestResponse)
|
||||
require.True(t, ok, "expected SignalResponse_RequestResponse, got %T", msg.Message)
|
||||
return rr.RequestResponse
|
||||
}
|
||||
|
||||
func TestHandleDefineDataTrackSchemaRequest(t *testing.T) {
|
||||
t.Run("returns NOT_ALLOWED when feature not enabled", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, false, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
req := &livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: []byte("def"),
|
||||
},
|
||||
}
|
||||
p.HandleDefineDataTrackSchemaRequest(req)
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_NOT_ALLOWED, rr.Reason)
|
||||
require.NotNil(t, rr.GetDefineDataTrackSchema())
|
||||
require.Empty(t, p.asyncAttributes.GetAll())
|
||||
})
|
||||
|
||||
t.Run("returns INVALID_REQUEST when schema definition is nil", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
require.Empty(t, p.asyncAttributes.GetAll())
|
||||
})
|
||||
|
||||
t.Run("returns INVALID_REQUEST when id is nil", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Definition: []byte("def"),
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
})
|
||||
|
||||
t.Run("returns INVALID_REQUEST when name is empty", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: []byte("def"),
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
})
|
||||
|
||||
t.Run("returns INVALID_REQUEST when name exceeds 256 chars", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: strings.Repeat("a", 257),
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: []byte("def"),
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
})
|
||||
|
||||
t.Run("returns INVALID_REQUEST when definition is empty", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: nil,
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
require.Empty(t, p.asyncAttributes.GetAll())
|
||||
})
|
||||
|
||||
t.Run("returns LIMIT_EXCEEDED when adding would breach the limit", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 16)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: []byte(strings.Repeat("x", 32)),
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_LIMIT_EXCEEDED, rr.Reason)
|
||||
require.Empty(t, p.asyncAttributes.GetAll())
|
||||
})
|
||||
|
||||
t.Run("stores a valid definition and sends no response", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
def := []byte("definition-bytes")
|
||||
|
||||
p.HandleDefineDataTrackSchemaRequest(&livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: id,
|
||||
Definition: def,
|
||||
},
|
||||
})
|
||||
|
||||
// on success no response is sent to the client
|
||||
require.Equal(t, 0, sink.WriteMessageCallCount())
|
||||
|
||||
stored := p.asyncAttributes.Get(id)
|
||||
require.NotNil(t, stored)
|
||||
require.Equal(t, def, stored.Definition)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleGetDataTrackSchemaRequest(t *testing.T) {
|
||||
t.Run("returns INVALID_REQUEST when schema id is missing", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.HandleGetDataTrackSchemaRequest(&livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "other",
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_INVALID_REQUEST, rr.Reason)
|
||||
})
|
||||
|
||||
t.Run("forwards request to listener when schema id is provided", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
listener := p.params.ParticipantListener.(*typesfakes.FakeLocalParticipantListener)
|
||||
|
||||
req := &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "other",
|
||||
SchemaId: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
}
|
||||
p.HandleGetDataTrackSchemaRequest(req)
|
||||
|
||||
require.Equal(t, 1, listener.OnGetDataTrackSchemaCallCount())
|
||||
gotParticipant, gotReq := listener.OnGetDataTrackSchemaArgsForCall(0)
|
||||
require.Equal(t, p, gotParticipant)
|
||||
require.Equal(t, req, gotReq)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetDataTrackSchema(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
require.Nil(t, p.GetDataTrackSchema(id))
|
||||
|
||||
p.asyncAttributes.Add(id, []byte("definition"))
|
||||
got := p.GetDataTrackSchema(id)
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, id.Name, got.Id.Name)
|
||||
require.Equal(t, []byte("definition"), got.Definition)
|
||||
}
|
||||
|
||||
func TestProcessGetDataTrackSchemaRequest(t *testing.T) {
|
||||
t.Run("returns NOT_FOUND when publisher is nil", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
p.ProcessGetDataTrackSchemaRequest(&livekit.GetDataTrackSchemaRequest{
|
||||
SchemaId: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_NOT_FOUND, rr.Reason)
|
||||
require.Contains(t, rr.Message, "participant")
|
||||
})
|
||||
|
||||
t.Run("returns NOT_FOUND when publisher has no matching schema", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
publisher := &typesfakes.FakeParticipant{}
|
||||
publisher.GetDataTrackSchemaReturns(nil)
|
||||
|
||||
req := &livekit.GetDataTrackSchemaRequest{
|
||||
SchemaId: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
}
|
||||
p.ProcessGetDataTrackSchemaRequest(req, publisher)
|
||||
|
||||
require.Equal(t, 1, publisher.GetDataTrackSchemaCallCount())
|
||||
require.Equal(t, req.SchemaId, publisher.GetDataTrackSchemaArgsForCall(0))
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
rr := lastRequestResponse(t, sink, 0)
|
||||
require.Equal(t, livekit.RequestResponse_NOT_FOUND, rr.Reason)
|
||||
})
|
||||
|
||||
t.Run("sends schema response when publisher has a matching schema", func(t *testing.T) {
|
||||
p := newParticipantWithAsyncAttributes(t, true, 0)
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
def := &livekit.DataTrackSchemaDefinition{
|
||||
Id: id,
|
||||
Definition: []byte("definition-bytes"),
|
||||
}
|
||||
|
||||
publisher := &typesfakes.FakeParticipant{}
|
||||
publisher.GetDataTrackSchemaReturns(def)
|
||||
|
||||
p.ProcessGetDataTrackSchemaRequest(&livekit.GetDataTrackSchemaRequest{
|
||||
SchemaId: id,
|
||||
}, publisher)
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
msg := sink.WriteMessageArgsForCall(0).(*livekit.SignalResponse)
|
||||
response, ok := msg.Message.(*livekit.SignalResponse_GetDataTrackSchemaResponse)
|
||||
require.True(t, ok, "expected SignalResponse_GetDataTrackSchemaResponse, got %T", msg.Message)
|
||||
require.Equal(t, def, response.GetDataTrackSchemaResponse.SchemaDefinition)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright 2026 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"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
func newTestAsyncAttributes() *ParticipantAsyncAttributes {
|
||||
return NewParticipantAsyncAttributes(ParticipantAsyncAttributesParams{
|
||||
Logger: logger.GetLogger(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_AddAndGet(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
value := []byte("definition-bytes")
|
||||
|
||||
a.Add(id, value)
|
||||
|
||||
got := a.Get(id)
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, id.Name, got.Id.Name)
|
||||
require.Equal(t, id.Encoding, got.Id.Encoding)
|
||||
require.Equal(t, value, got.Definition)
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_AddOverwrites(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
a.Add(id, []byte("v1"))
|
||||
a.Add(id, []byte("v2"))
|
||||
|
||||
got := a.Get(id)
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, []byte("v2"), got.Definition)
|
||||
|
||||
require.Len(t, a.GetAll(), 1)
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_DifferentEncodingsAreDistinct(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
idProto := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
idJSON := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_JSON_SCHEMA,
|
||||
}
|
||||
|
||||
a.Add(idProto, []byte("proto-def"))
|
||||
a.Add(idJSON, []byte("json-def"))
|
||||
|
||||
gotProto := a.Get(idProto)
|
||||
require.NotNil(t, gotProto)
|
||||
require.Equal(t, []byte("proto-def"), gotProto.Definition)
|
||||
|
||||
gotJSON := a.Get(idJSON)
|
||||
require.NotNil(t, gotJSON)
|
||||
require.Equal(t, []byte("json-def"), gotJSON.Definition)
|
||||
|
||||
require.Len(t, a.GetAll(), 2)
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_Delete(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
a.Add(id, []byte("definition"))
|
||||
|
||||
a.Delete(id)
|
||||
require.Nil(t, a.Get(id))
|
||||
require.Empty(t, a.GetAll())
|
||||
|
||||
// deleting a non-existent key is a no-op
|
||||
a.Delete(id)
|
||||
require.Empty(t, a.GetAll())
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_NilId(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
// nil id should be silently ignored, not panic
|
||||
a.Add(nil, []byte("definition"))
|
||||
require.Empty(t, a.GetAll())
|
||||
|
||||
require.Nil(t, a.Get(nil))
|
||||
|
||||
a.Delete(nil)
|
||||
require.Empty(t, a.GetAll())
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_GetMissing(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "missing",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
require.Nil(t, a.Get(id))
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_GetAllContents(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
id1 := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
id2 := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-2",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_FLATBUFFER,
|
||||
}
|
||||
|
||||
a.Add(id1, []byte("def-1"))
|
||||
a.Add(id2, []byte("def-2"))
|
||||
|
||||
all := a.GetAll()
|
||||
require.Len(t, all, 2)
|
||||
require.Equal(t, []byte("def-1"), all[ToParticipantAsyncAttributeKey(id1)])
|
||||
require.Equal(t, []byte("def-2"), all[ToParticipantAsyncAttributeKey(id2)])
|
||||
}
|
||||
|
||||
func TestParticipantAsyncAttributes_ConcurrentAccess(t *testing.T) {
|
||||
a := newTestAsyncAttributes()
|
||||
|
||||
const numGoroutines = 16
|
||||
const opsPerGoroutine = 100
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numGoroutines)
|
||||
for g := 0; g < numGoroutines; g++ {
|
||||
go func(g int) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < opsPerGoroutine; i++ {
|
||||
id := &livekit.DataTrackSchemaId{
|
||||
Name: "schema",
|
||||
Encoding: livekit.DataTrackSchemaEncoding(g % 8),
|
||||
}
|
||||
a.Add(id, []byte("v"))
|
||||
_ = a.Get(id)
|
||||
_ = a.GetAll()
|
||||
if i%3 == 0 {
|
||||
a.Delete(id)
|
||||
}
|
||||
}
|
||||
}(g)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestToKeyFromKey(t *testing.T) {
|
||||
ids := []*livekit.DataTrackSchemaId{
|
||||
{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
{
|
||||
Name: "another-schema",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_JSON_SCHEMA,
|
||||
},
|
||||
{
|
||||
Name: "x",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_UNSPECIFIED,
|
||||
},
|
||||
}
|
||||
for _, id := range ids {
|
||||
t.Run(id.Name, func(t *testing.T) {
|
||||
key := ToParticipantAsyncAttributeKey(id)
|
||||
roundTripped := FromParticipantAsyncAttributeKey(key)
|
||||
require.Equal(t, id.Name, roundTripped.Name)
|
||||
require.Equal(t, id.Encoding, roundTripped.Encoding)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -368,3 +368,9 @@ func (p *ParticipantImpl) SendDataTrackSubscriberHandles(handles map[uint32]*liv
|
||||
SubHandles: handles,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendGetDataTrackSchemaResponse(definition *livekit.DataTrackSchemaDefinition) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalGetDataTrackSchemaResponse(&livekit.GetDataTrackSchemaResponse{
|
||||
SchemaDefinition: definition,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1374,6 +1374,11 @@ func (r *Room) onUpdateDataSubscriptions(participant types.LocalParticipant, req
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Room) onGetDataTrackSchema(participant types.LocalParticipant, req *livekit.GetDataTrackSchemaRequest) {
|
||||
publisher := r.GetParticipant(livekit.ParticipantIdentity(req.ParticipantIdentity))
|
||||
participant.ProcessGetDataTrackSchemaRequest(req, publisher)
|
||||
}
|
||||
|
||||
func (r *Room) onLeave(p types.LocalParticipant, reason types.ParticipantCloseReason) {
|
||||
r.RemoveParticipant(p.Identity(), p.ID(), reason)
|
||||
}
|
||||
@@ -1983,6 +1988,13 @@ func (l *localParticipantListener) OnUpdateDataSubscriptions(p types.LocalPartic
|
||||
l.room.onUpdateDataSubscriptions(p, req)
|
||||
}
|
||||
|
||||
func (l *localParticipantListener) OnDefineDataTrackSchema(_p types.LocalParticipant, _definition *livekit.DataTrackSchemaDefinition) {
|
||||
}
|
||||
|
||||
func (l *localParticipantListener) OnGetDataTrackSchema(p types.LocalParticipant, req *livekit.GetDataTrackSchemaRequest) {
|
||||
l.room.onGetDataTrackSchema(p, req)
|
||||
}
|
||||
|
||||
func (l *localParticipantListener) OnSyncState(p types.LocalParticipant, state *livekit.SyncState) error {
|
||||
return l.room.onSyncState(p, state)
|
||||
}
|
||||
|
||||
@@ -62,4 +62,5 @@ type ParticipantSignalling interface {
|
||||
SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message
|
||||
SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message
|
||||
SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message
|
||||
SignalGetDataTrackSchemaResponse(getDataTrackSchemaResponse *livekit.GetDataTrackSchemaResponse) proto.Message
|
||||
}
|
||||
|
||||
@@ -151,6 +151,12 @@ func (s *signalhandler) HandleMessage(msg proto.Message) error {
|
||||
|
||||
case *livekit.SignalRequest_UpdateDataSubscription:
|
||||
s.params.Participant.HandleUpdateDataSubscription(msg.UpdateDataSubscription)
|
||||
|
||||
case *livekit.SignalRequest_DefineDataTrackSchema:
|
||||
s.params.Participant.HandleDefineDataTrackSchemaRequest(msg.DefineDataTrackSchema)
|
||||
|
||||
case *livekit.SignalRequest_GetDataTrackSchema:
|
||||
s.params.Participant.HandleGetDataTrackSchemaRequest(msg.GetDataTrackSchema)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -258,3 +258,11 @@ func (s *signalling) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u *signalling) SignalGetDataTrackSchemaResponse(getDataTrackSchemaResponse *livekit.GetDataTrackSchemaResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_GetDataTrackSchemaResponse{
|
||||
GetDataTrackSchemaResponse: getDataTrackSchemaResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,3 +127,7 @@ func (u *signallingUnimplemented) SignalUnpublishDataTrackResponse(unpublishData
|
||||
func (u *signallingUnimplemented) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalGetDataTrackSchemaResponse(getDataTrackSchemaResponse *livekit.GetDataTrackSchemaResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -355,6 +355,9 @@ type Participant interface {
|
||||
HandleReceivedDataTrackMessage([]byte, *datatrack.Packet, int64)
|
||||
|
||||
GetParticipantListener() ParticipantListener
|
||||
|
||||
AddDataTrackSchema(definition *livekit.DataTrackSchemaDefinition)
|
||||
GetDataTrackSchema(id *livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@@ -560,6 +563,9 @@ type LocalParticipant interface {
|
||||
HandlePublishDataTrackRequest(*livekit.PublishDataTrackRequest)
|
||||
HandleUnpublishDataTrackRequest(*livekit.UnpublishDataTrackRequest)
|
||||
HandleUpdateDataSubscription(*livekit.UpdateDataSubscription)
|
||||
HandleDefineDataTrackSchemaRequest(*livekit.DefineDataTrackSchemaRequest)
|
||||
HandleGetDataTrackSchemaRequest(*livekit.GetDataTrackSchemaRequest)
|
||||
ProcessGetDataTrackSchemaRequest(*livekit.GetDataTrackSchemaRequest, Participant)
|
||||
|
||||
HandleSignalMessage(msg proto.Message) error
|
||||
|
||||
@@ -619,6 +625,8 @@ type LocalParticipantListener interface {
|
||||
)
|
||||
OnUpdateSubscriptionPermission(LocalParticipant, *livekit.SubscriptionPermission) error
|
||||
OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription)
|
||||
OnDefineDataTrackSchema(LocalParticipant, *livekit.DataTrackSchemaDefinition)
|
||||
OnGetDataTrackSchema(LocalParticipant, *livekit.GetDataTrackSchemaRequest)
|
||||
OnSyncState(LocalParticipant, *livekit.SyncState) error
|
||||
OnSimulateScenario(LocalParticipant, *livekit.SimulateScenario) error
|
||||
OnLeave(LocalParticipant, ParticipantCloseReason)
|
||||
@@ -650,6 +658,10 @@ func (*NullLocalParticipantListener) OnUpdateSubscriptionPermission(LocalPartici
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnDefineDataTrackSchema(LocalParticipant, *livekit.DataTrackSchemaDefinition) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnGetDataTrackSchema(LocalParticipant, *livekit.GetDataTrackSchemaRequest) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnSyncState(LocalParticipant, *livekit.SyncState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ type FakeLocalParticipant struct {
|
||||
activeAtReturnsOnCall map[int]struct {
|
||||
result1 time.Time
|
||||
}
|
||||
AddDataTrackSchemaStub func(*livekit.DataTrackSchemaDefinition)
|
||||
addDataTrackSchemaMutex sync.RWMutex
|
||||
addDataTrackSchemaArgsForCall []struct {
|
||||
arg1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
AddOnCloseStub func(string, func(types.LocalParticipant))
|
||||
addOnCloseMutex sync.RWMutex
|
||||
addOnCloseArgsForCall []struct {
|
||||
@@ -305,6 +310,17 @@ type FakeLocalParticipant struct {
|
||||
getCountryReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
GetDataTrackSchemaStub func(*livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition
|
||||
getDataTrackSchemaMutex sync.RWMutex
|
||||
getDataTrackSchemaArgsForCall []struct {
|
||||
arg1 *livekit.DataTrackSchemaId
|
||||
}
|
||||
getDataTrackSchemaReturns struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
getDataTrackSchemaReturnsOnCall map[int]struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
GetDataTrackTransportStub func() types.DataTrackTransport
|
||||
getDataTrackTransportMutex sync.RWMutex
|
||||
getDataTrackTransportArgsForCall []struct {
|
||||
@@ -566,6 +582,16 @@ type FakeLocalParticipant struct {
|
||||
handleAnswerArgsForCall []struct {
|
||||
arg1 *livekit.SessionDescription
|
||||
}
|
||||
HandleDefineDataTrackSchemaRequestStub func(*livekit.DefineDataTrackSchemaRequest)
|
||||
handleDefineDataTrackSchemaRequestMutex sync.RWMutex
|
||||
handleDefineDataTrackSchemaRequestArgsForCall []struct {
|
||||
arg1 *livekit.DefineDataTrackSchemaRequest
|
||||
}
|
||||
HandleGetDataTrackSchemaRequestStub func(*livekit.GetDataTrackSchemaRequest)
|
||||
handleGetDataTrackSchemaRequestMutex sync.RWMutex
|
||||
handleGetDataTrackSchemaRequestArgsForCall []struct {
|
||||
arg1 *livekit.GetDataTrackSchemaRequest
|
||||
}
|
||||
HandleICERestartSDPFragmentStub func(string) (string, error)
|
||||
handleICERestartSDPFragmentMutex sync.RWMutex
|
||||
handleICERestartSDPFragmentArgsForCall []struct {
|
||||
@@ -976,6 +1002,12 @@ type FakeLocalParticipant struct {
|
||||
arg2 chan string
|
||||
arg3 chan error
|
||||
}
|
||||
ProcessGetDataTrackSchemaRequestStub func(*livekit.GetDataTrackSchemaRequest, types.Participant)
|
||||
processGetDataTrackSchemaRequestMutex sync.RWMutex
|
||||
processGetDataTrackSchemaRequestArgsForCall []struct {
|
||||
arg1 *livekit.GetDataTrackSchemaRequest
|
||||
arg2 types.Participant
|
||||
}
|
||||
ProtocolVersionStub func() types.ProtocolVersion
|
||||
protocolVersionMutex sync.RWMutex
|
||||
protocolVersionArgsForCall []struct {
|
||||
@@ -1574,6 +1606,38 @@ func (fake *FakeLocalParticipant) ActiveAtReturnsOnCall(i int, result1 time.Time
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) AddDataTrackSchema(arg1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.addDataTrackSchemaMutex.Lock()
|
||||
fake.addDataTrackSchemaArgsForCall = append(fake.addDataTrackSchemaArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSchemaDefinition
|
||||
}{arg1})
|
||||
stub := fake.AddDataTrackSchemaStub
|
||||
fake.recordInvocation("AddDataTrackSchema", []interface{}{arg1})
|
||||
fake.addDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.AddDataTrackSchemaStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) AddDataTrackSchemaCallCount() int {
|
||||
fake.addDataTrackSchemaMutex.RLock()
|
||||
defer fake.addDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.addDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) AddDataTrackSchemaCalls(stub func(*livekit.DataTrackSchemaDefinition)) {
|
||||
fake.addDataTrackSchemaMutex.Lock()
|
||||
defer fake.addDataTrackSchemaMutex.Unlock()
|
||||
fake.AddDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) AddDataTrackSchemaArgsForCall(i int) *livekit.DataTrackSchemaDefinition {
|
||||
fake.addDataTrackSchemaMutex.RLock()
|
||||
defer fake.addDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.addDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) AddOnClose(arg1 string, arg2 func(types.LocalParticipant)) {
|
||||
fake.addOnCloseMutex.Lock()
|
||||
fake.addOnCloseArgsForCall = append(fake.addOnCloseArgsForCall, struct {
|
||||
@@ -2963,6 +3027,67 @@ func (fake *FakeLocalParticipant) GetCountryReturnsOnCall(i int, result1 string)
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchema(arg1 *livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
ret, specificReturn := fake.getDataTrackSchemaReturnsOnCall[len(fake.getDataTrackSchemaArgsForCall)]
|
||||
fake.getDataTrackSchemaArgsForCall = append(fake.getDataTrackSchemaArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSchemaId
|
||||
}{arg1})
|
||||
stub := fake.GetDataTrackSchemaStub
|
||||
fakeReturns := fake.getDataTrackSchemaReturns
|
||||
fake.recordInvocation("GetDataTrackSchema", []interface{}{arg1})
|
||||
fake.getDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchemaCallCount() int {
|
||||
fake.getDataTrackSchemaMutex.RLock()
|
||||
defer fake.getDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.getDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchemaCalls(stub func(*livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchemaArgsForCall(i int) *livekit.DataTrackSchemaId {
|
||||
fake.getDataTrackSchemaMutex.RLock()
|
||||
defer fake.getDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.getDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchemaReturns(result1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = nil
|
||||
fake.getDataTrackSchemaReturns = struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackSchemaReturnsOnCall(i int, result1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = nil
|
||||
if fake.getDataTrackSchemaReturnsOnCall == nil {
|
||||
fake.getDataTrackSchemaReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
})
|
||||
}
|
||||
fake.getDataTrackSchemaReturnsOnCall[i] = struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) GetDataTrackTransport() types.DataTrackTransport {
|
||||
fake.getDataTrackTransportMutex.Lock()
|
||||
ret, specificReturn := fake.getDataTrackTransportReturnsOnCall[len(fake.getDataTrackTransportArgsForCall)]
|
||||
@@ -4355,6 +4480,70 @@ func (fake *FakeLocalParticipant) HandleAnswerArgsForCall(i int) *livekit.Sessio
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleDefineDataTrackSchemaRequest(arg1 *livekit.DefineDataTrackSchemaRequest) {
|
||||
fake.handleDefineDataTrackSchemaRequestMutex.Lock()
|
||||
fake.handleDefineDataTrackSchemaRequestArgsForCall = append(fake.handleDefineDataTrackSchemaRequestArgsForCall, struct {
|
||||
arg1 *livekit.DefineDataTrackSchemaRequest
|
||||
}{arg1})
|
||||
stub := fake.HandleDefineDataTrackSchemaRequestStub
|
||||
fake.recordInvocation("HandleDefineDataTrackSchemaRequest", []interface{}{arg1})
|
||||
fake.handleDefineDataTrackSchemaRequestMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandleDefineDataTrackSchemaRequestStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleDefineDataTrackSchemaRequestCallCount() int {
|
||||
fake.handleDefineDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.handleDefineDataTrackSchemaRequestMutex.RUnlock()
|
||||
return len(fake.handleDefineDataTrackSchemaRequestArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleDefineDataTrackSchemaRequestCalls(stub func(*livekit.DefineDataTrackSchemaRequest)) {
|
||||
fake.handleDefineDataTrackSchemaRequestMutex.Lock()
|
||||
defer fake.handleDefineDataTrackSchemaRequestMutex.Unlock()
|
||||
fake.HandleDefineDataTrackSchemaRequestStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleDefineDataTrackSchemaRequestArgsForCall(i int) *livekit.DefineDataTrackSchemaRequest {
|
||||
fake.handleDefineDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.handleDefineDataTrackSchemaRequestMutex.RUnlock()
|
||||
argsForCall := fake.handleDefineDataTrackSchemaRequestArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleGetDataTrackSchemaRequest(arg1 *livekit.GetDataTrackSchemaRequest) {
|
||||
fake.handleGetDataTrackSchemaRequestMutex.Lock()
|
||||
fake.handleGetDataTrackSchemaRequestArgsForCall = append(fake.handleGetDataTrackSchemaRequestArgsForCall, struct {
|
||||
arg1 *livekit.GetDataTrackSchemaRequest
|
||||
}{arg1})
|
||||
stub := fake.HandleGetDataTrackSchemaRequestStub
|
||||
fake.recordInvocation("HandleGetDataTrackSchemaRequest", []interface{}{arg1})
|
||||
fake.handleGetDataTrackSchemaRequestMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandleGetDataTrackSchemaRequestStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleGetDataTrackSchemaRequestCallCount() int {
|
||||
fake.handleGetDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.handleGetDataTrackSchemaRequestMutex.RUnlock()
|
||||
return len(fake.handleGetDataTrackSchemaRequestArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleGetDataTrackSchemaRequestCalls(stub func(*livekit.GetDataTrackSchemaRequest)) {
|
||||
fake.handleGetDataTrackSchemaRequestMutex.Lock()
|
||||
defer fake.handleGetDataTrackSchemaRequestMutex.Unlock()
|
||||
fake.HandleGetDataTrackSchemaRequestStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleGetDataTrackSchemaRequestArgsForCall(i int) *livekit.GetDataTrackSchemaRequest {
|
||||
fake.handleGetDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.handleGetDataTrackSchemaRequestMutex.RUnlock()
|
||||
argsForCall := fake.handleGetDataTrackSchemaRequestArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) HandleICERestartSDPFragment(arg1 string) (string, error) {
|
||||
fake.handleICERestartSDPFragmentMutex.Lock()
|
||||
ret, specificReturn := fake.handleICERestartSDPFragmentReturnsOnCall[len(fake.handleICERestartSDPFragmentArgsForCall)]
|
||||
@@ -6607,6 +6796,39 @@ func (fake *FakeLocalParticipant) PerformRpcArgsForCall(i int) (*livekit.Perform
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) ProcessGetDataTrackSchemaRequest(arg1 *livekit.GetDataTrackSchemaRequest, arg2 types.Participant) {
|
||||
fake.processGetDataTrackSchemaRequestMutex.Lock()
|
||||
fake.processGetDataTrackSchemaRequestArgsForCall = append(fake.processGetDataTrackSchemaRequestArgsForCall, struct {
|
||||
arg1 *livekit.GetDataTrackSchemaRequest
|
||||
arg2 types.Participant
|
||||
}{arg1, arg2})
|
||||
stub := fake.ProcessGetDataTrackSchemaRequestStub
|
||||
fake.recordInvocation("ProcessGetDataTrackSchemaRequest", []interface{}{arg1, arg2})
|
||||
fake.processGetDataTrackSchemaRequestMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.ProcessGetDataTrackSchemaRequestStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) ProcessGetDataTrackSchemaRequestCallCount() int {
|
||||
fake.processGetDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.processGetDataTrackSchemaRequestMutex.RUnlock()
|
||||
return len(fake.processGetDataTrackSchemaRequestArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) ProcessGetDataTrackSchemaRequestCalls(stub func(*livekit.GetDataTrackSchemaRequest, types.Participant)) {
|
||||
fake.processGetDataTrackSchemaRequestMutex.Lock()
|
||||
defer fake.processGetDataTrackSchemaRequestMutex.Unlock()
|
||||
fake.ProcessGetDataTrackSchemaRequestStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) ProcessGetDataTrackSchemaRequestArgsForCall(i int) (*livekit.GetDataTrackSchemaRequest, types.Participant) {
|
||||
fake.processGetDataTrackSchemaRequestMutex.RLock()
|
||||
defer fake.processGetDataTrackSchemaRequestMutex.RUnlock()
|
||||
argsForCall := fake.processGetDataTrackSchemaRequestArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipant) ProtocolVersion() types.ProtocolVersion {
|
||||
fake.protocolVersionMutex.Lock()
|
||||
ret, specificReturn := fake.protocolVersionReturnsOnCall[len(fake.protocolVersionArgsForCall)]
|
||||
|
||||
@@ -42,6 +42,18 @@ type FakeLocalParticipantListener struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}
|
||||
OnDefineDataTrackSchemaStub func(types.LocalParticipant, *livekit.DataTrackSchemaDefinition)
|
||||
onDefineDataTrackSchemaMutex sync.RWMutex
|
||||
onDefineDataTrackSchemaArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
OnGetDataTrackSchemaStub func(types.LocalParticipant, *livekit.GetDataTrackSchemaRequest)
|
||||
onGetDataTrackSchemaMutex sync.RWMutex
|
||||
onGetDataTrackSchemaArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.GetDataTrackSchemaRequest
|
||||
}
|
||||
OnLeaveStub func(types.LocalParticipant, types.ParticipantCloseReason)
|
||||
onLeaveMutex sync.RWMutex
|
||||
onLeaveArgsForCall []struct {
|
||||
@@ -331,6 +343,72 @@ func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedArgsForCall(i in
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDefineDataTrackSchema(arg1 types.LocalParticipant, arg2 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.onDefineDataTrackSchemaMutex.Lock()
|
||||
fake.onDefineDataTrackSchemaArgsForCall = append(fake.onDefineDataTrackSchemaArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.DataTrackSchemaDefinition
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnDefineDataTrackSchemaStub
|
||||
fake.recordInvocation("OnDefineDataTrackSchema", []interface{}{arg1, arg2})
|
||||
fake.onDefineDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDefineDataTrackSchemaStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDefineDataTrackSchemaCallCount() int {
|
||||
fake.onDefineDataTrackSchemaMutex.RLock()
|
||||
defer fake.onDefineDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.onDefineDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDefineDataTrackSchemaCalls(stub func(types.LocalParticipant, *livekit.DataTrackSchemaDefinition)) {
|
||||
fake.onDefineDataTrackSchemaMutex.Lock()
|
||||
defer fake.onDefineDataTrackSchemaMutex.Unlock()
|
||||
fake.OnDefineDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDefineDataTrackSchemaArgsForCall(i int) (types.LocalParticipant, *livekit.DataTrackSchemaDefinition) {
|
||||
fake.onDefineDataTrackSchemaMutex.RLock()
|
||||
defer fake.onDefineDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.onDefineDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnGetDataTrackSchema(arg1 types.LocalParticipant, arg2 *livekit.GetDataTrackSchemaRequest) {
|
||||
fake.onGetDataTrackSchemaMutex.Lock()
|
||||
fake.onGetDataTrackSchemaArgsForCall = append(fake.onGetDataTrackSchemaArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.GetDataTrackSchemaRequest
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnGetDataTrackSchemaStub
|
||||
fake.recordInvocation("OnGetDataTrackSchema", []interface{}{arg1, arg2})
|
||||
fake.onGetDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnGetDataTrackSchemaStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnGetDataTrackSchemaCallCount() int {
|
||||
fake.onGetDataTrackSchemaMutex.RLock()
|
||||
defer fake.onGetDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.onGetDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnGetDataTrackSchemaCalls(stub func(types.LocalParticipant, *livekit.GetDataTrackSchemaRequest)) {
|
||||
fake.onGetDataTrackSchemaMutex.Lock()
|
||||
defer fake.onGetDataTrackSchemaMutex.Unlock()
|
||||
fake.OnGetDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnGetDataTrackSchemaArgsForCall(i int) (types.LocalParticipant, *livekit.GetDataTrackSchemaRequest) {
|
||||
fake.onGetDataTrackSchemaMutex.RLock()
|
||||
defer fake.onGetDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.onGetDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnLeave(arg1 types.LocalParticipant, arg2 types.ParticipantCloseReason) {
|
||||
fake.onLeaveMutex.Lock()
|
||||
fake.onLeaveArgsForCall = append(fake.onLeaveArgsForCall, struct {
|
||||
|
||||
@@ -13,6 +13,11 @@ import (
|
||||
)
|
||||
|
||||
type FakeParticipant struct {
|
||||
AddDataTrackSchemaStub func(*livekit.DataTrackSchemaDefinition)
|
||||
addDataTrackSchemaMutex sync.RWMutex
|
||||
addDataTrackSchemaArgsForCall []struct {
|
||||
arg1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
CanSkipBroadcastStub func() bool
|
||||
canSkipBroadcastMutex sync.RWMutex
|
||||
canSkipBroadcastArgsForCall []struct {
|
||||
@@ -78,6 +83,17 @@ type FakeParticipant struct {
|
||||
result1 float64
|
||||
result2 bool
|
||||
}
|
||||
GetDataTrackSchemaStub func(*livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition
|
||||
getDataTrackSchemaMutex sync.RWMutex
|
||||
getDataTrackSchemaArgsForCall []struct {
|
||||
arg1 *livekit.DataTrackSchemaId
|
||||
}
|
||||
getDataTrackSchemaReturns struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
getDataTrackSchemaReturnsOnCall map[int]struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}
|
||||
GetLoggerStub func() logger.Logger
|
||||
getLoggerMutex sync.RWMutex
|
||||
getLoggerArgsForCall []struct {
|
||||
@@ -361,6 +377,38 @@ type FakeParticipant struct {
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) AddDataTrackSchema(arg1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.addDataTrackSchemaMutex.Lock()
|
||||
fake.addDataTrackSchemaArgsForCall = append(fake.addDataTrackSchemaArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSchemaDefinition
|
||||
}{arg1})
|
||||
stub := fake.AddDataTrackSchemaStub
|
||||
fake.recordInvocation("AddDataTrackSchema", []interface{}{arg1})
|
||||
fake.addDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.AddDataTrackSchemaStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) AddDataTrackSchemaCallCount() int {
|
||||
fake.addDataTrackSchemaMutex.RLock()
|
||||
defer fake.addDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.addDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) AddDataTrackSchemaCalls(stub func(*livekit.DataTrackSchemaDefinition)) {
|
||||
fake.addDataTrackSchemaMutex.Lock()
|
||||
defer fake.addDataTrackSchemaMutex.Unlock()
|
||||
fake.AddDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) AddDataTrackSchemaArgsForCall(i int) *livekit.DataTrackSchemaDefinition {
|
||||
fake.addDataTrackSchemaMutex.RLock()
|
||||
defer fake.addDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.addDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) CanSkipBroadcast() bool {
|
||||
fake.canSkipBroadcastMutex.Lock()
|
||||
ret, specificReturn := fake.canSkipBroadcastReturnsOnCall[len(fake.canSkipBroadcastArgsForCall)]
|
||||
@@ -692,6 +740,67 @@ func (fake *FakeParticipant) GetAudioLevelReturnsOnCall(i int, result1 float64,
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchema(arg1 *livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
ret, specificReturn := fake.getDataTrackSchemaReturnsOnCall[len(fake.getDataTrackSchemaArgsForCall)]
|
||||
fake.getDataTrackSchemaArgsForCall = append(fake.getDataTrackSchemaArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSchemaId
|
||||
}{arg1})
|
||||
stub := fake.GetDataTrackSchemaStub
|
||||
fakeReturns := fake.getDataTrackSchemaReturns
|
||||
fake.recordInvocation("GetDataTrackSchema", []interface{}{arg1})
|
||||
fake.getDataTrackSchemaMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchemaCallCount() int {
|
||||
fake.getDataTrackSchemaMutex.RLock()
|
||||
defer fake.getDataTrackSchemaMutex.RUnlock()
|
||||
return len(fake.getDataTrackSchemaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchemaCalls(stub func(*livekit.DataTrackSchemaId) *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchemaArgsForCall(i int) *livekit.DataTrackSchemaId {
|
||||
fake.getDataTrackSchemaMutex.RLock()
|
||||
defer fake.getDataTrackSchemaMutex.RUnlock()
|
||||
argsForCall := fake.getDataTrackSchemaArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchemaReturns(result1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = nil
|
||||
fake.getDataTrackSchemaReturns = struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetDataTrackSchemaReturnsOnCall(i int, result1 *livekit.DataTrackSchemaDefinition) {
|
||||
fake.getDataTrackSchemaMutex.Lock()
|
||||
defer fake.getDataTrackSchemaMutex.Unlock()
|
||||
fake.GetDataTrackSchemaStub = nil
|
||||
if fake.getDataTrackSchemaReturnsOnCall == nil {
|
||||
fake.getDataTrackSchemaReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
})
|
||||
}
|
||||
fake.getDataTrackSchemaReturnsOnCall[i] = struct {
|
||||
result1 *livekit.DataTrackSchemaDefinition
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetLogger() logger.Logger {
|
||||
fake.getLoggerMutex.Lock()
|
||||
ret, specificReturn := fake.getLoggerReturnsOnCall[len(fake.getLoggerArgsForCall)]
|
||||
|
||||
@@ -520,8 +520,9 @@ func (r *RoomManager) StartSession(
|
||||
DatachannelLossyTargetLatency: r.config.RTC.DatachannelLossyTargetLatency,
|
||||
FireOnTrackBySdp: true,
|
||||
UseSinglePeerConnection: pi.UseSinglePeerConnection,
|
||||
EnableDataTracks: r.config.EnableDataTracks,
|
||||
EnableRTPStreamRestartDetection: r.config.RTC.EnableRTPStreamRestartDetection,
|
||||
EnableDataTracks: r.config.EnableDataTracks,
|
||||
EnableParticipantAsyncAttributes: r.config.EnableParticipantAsyncAttributes,
|
||||
EnableRTPStreamRestartDetection: r.config.RTC.EnableRTPStreamRestartDetection,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -80,9 +80,13 @@ func setupSingleNodeTest(name string) (*service.LivekitServer, func()) {
|
||||
}
|
||||
|
||||
func setupMultiNodeTest(name string) (*service.LivekitServer, *service.LivekitServer, func()) {
|
||||
return setupMultiNodeTestWithConfig(name, nil)
|
||||
}
|
||||
|
||||
func setupMultiNodeTestWithConfig(name string, configUpdater func(*config.Config)) (*service.LivekitServer, *service.LivekitServer, func()) {
|
||||
logger.Infow("----------------STARTING TEST----------------", "test", name)
|
||||
s1 := createMultiNodeServer(guid.New(nodeID1), defaultServerPort)
|
||||
s2 := createMultiNodeServer(guid.New(nodeID2), secondServerPort)
|
||||
s1 := createMultiNodeServer(guid.New(nodeID1), defaultServerPort, configUpdater)
|
||||
s2 := createMultiNodeServer(guid.New(nodeID2), secondServerPort, configUpdater)
|
||||
go s1.Start()
|
||||
go s2.Start()
|
||||
|
||||
@@ -190,7 +194,7 @@ func createSingleNodeServer(configUpdater func(*config.Config)) *service.Livekit
|
||||
return s
|
||||
}
|
||||
|
||||
func createMultiNodeServer(nodeID string, port uint32) *service.LivekitServer {
|
||||
func createMultiNodeServer(nodeID string, port uint32, configUpdater func(*config.Config)) *service.LivekitServer {
|
||||
var err error
|
||||
conf, err := config.NewConfig("", true, nil, nil)
|
||||
if err != nil {
|
||||
@@ -202,6 +206,9 @@ func createMultiNodeServer(nodeID string, port uint32) *service.LivekitServer {
|
||||
conf.Redis.Address = "localhost:6379"
|
||||
conf.Keys = map[string]string{testApiKey: testApiSecret}
|
||||
conf.EnableDataTracks = true
|
||||
if configUpdater != nil {
|
||||
configUpdater(conf)
|
||||
}
|
||||
|
||||
currentNode, err := routing.NewLocalNode(conf)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/livekit/protocol/auth"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/config"
|
||||
"github.com/livekit/livekit-server/pkg/rtc"
|
||||
"github.com/livekit/livekit-server/pkg/testutils"
|
||||
"github.com/livekit/livekit-server/test/client"
|
||||
@@ -425,3 +426,113 @@ func TestCloseDisconnectedParticipantOnSignalClose(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiNodeAsyncAttributes(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTestWithConfig("TestMultiNodeAsyncAttributes", func(c *config.Config) {
|
||||
c.EnableParticipantAsyncAttributes = true
|
||||
c.Limit.MaxAsyncAttributesSize = 1024
|
||||
})
|
||||
defer finish()
|
||||
|
||||
for _, testRTCServicePath := range testRTCServicePaths {
|
||||
t.Run(fmt.Sprintf("testRTCServicePath=%s", testRTCServicePath.String()), func(t *testing.T) {
|
||||
pubCapture := &asyncAttributesCapture{}
|
||||
subCapture := &asyncAttributesCapture{}
|
||||
|
||||
// publisher on node 1, subscriber on node 2
|
||||
pub := createRTCClient("pub", defaultServerPort, testRTCServicePath, &client.Options{
|
||||
AutoSubscribe: true,
|
||||
SignalResponseInterceptor: pubCapture.interceptor(),
|
||||
})
|
||||
sub := createRTCClient("sub", secondServerPort, testRTCServicePath, &client.Options{
|
||||
AutoSubscribe: true,
|
||||
SignalResponseInterceptor: subCapture.interceptor(),
|
||||
})
|
||||
waitUntilConnected(t, pub, sub)
|
||||
defer stopClients(pub, sub)
|
||||
|
||||
// wait for both nodes to see each other so the get request routes correctly
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if sub.GetRemoteParticipant(pub.ID()) == nil {
|
||||
return "sub does not see pub yet"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
schemaID := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-multinode",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
definition := []byte("multinode-definition")
|
||||
|
||||
require.NoError(t, pub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: &livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: schemaID,
|
||||
Definition: definition,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// give the publisher's node time to apply the definition
|
||||
time.Sleep(syncDelay)
|
||||
require.Equal(t, 0, pubCapture.requestResponseCount(), "publisher should not receive an error response on success")
|
||||
|
||||
// subscriber on a different node asks for the schema; the request routes
|
||||
// across nodes to the publisher.
|
||||
require.NoError(t, sub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_GetDataTrackSchema{
|
||||
GetDataTrackSchema: &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "pub",
|
||||
SchemaId: schemaID,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
resp := subCapture.takeSchemaResponse()
|
||||
if resp == nil {
|
||||
return "subscriber did not receive schema response"
|
||||
}
|
||||
if resp.SchemaDefinition == nil {
|
||||
return "schema response missing definition"
|
||||
}
|
||||
if resp.SchemaDefinition.Id.Name != schemaID.Name {
|
||||
return fmt.Sprintf("expected schema name %s, got %s", schemaID.Name, resp.SchemaDefinition.Id.Name)
|
||||
}
|
||||
if string(resp.SchemaDefinition.Definition) != string(definition) {
|
||||
return fmt.Sprintf("expected definition %q, got %q", definition, resp.SchemaDefinition.Definition)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// requesting an unknown publisher identity should return NOT_FOUND
|
||||
require.NoError(t, sub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_GetDataTrackSchema{
|
||||
GetDataTrackSchema: &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "unknown-publisher",
|
||||
SchemaId: schemaID,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rr := subCapture.takeRequestResponse()
|
||||
if rr == nil {
|
||||
return "subscriber did not receive RequestResponse for unknown publisher"
|
||||
}
|
||||
if rr.Reason != livekit.RequestResponse_NOT_FOUND {
|
||||
return fmt.Sprintf("expected NOT_FOUND, got %s", rr.Reason)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1512,3 +1512,269 @@ func TestTurnAuthFailure(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// asyncAttributesCapture buffers RequestResponse and GetDataTrackSchemaResponse messages
|
||||
// sent to a test client so they can be asserted on. Other messages flow through to the
|
||||
// default handler.
|
||||
type asyncAttributesCapture struct {
|
||||
mu sync.Mutex
|
||||
requestResponses []*livekit.RequestResponse
|
||||
schemaResponses []*livekit.GetDataTrackSchemaResponse
|
||||
}
|
||||
|
||||
func (c *asyncAttributesCapture) interceptor() testclient.SignalResponseInterceptor {
|
||||
return func(msg *livekit.SignalResponse, next testclient.SignalResponseHandler) error {
|
||||
switch m := msg.Message.(type) {
|
||||
case *livekit.SignalResponse_RequestResponse:
|
||||
c.mu.Lock()
|
||||
c.requestResponses = append(c.requestResponses, m.RequestResponse)
|
||||
c.mu.Unlock()
|
||||
case *livekit.SignalResponse_GetDataTrackSchemaResponse:
|
||||
c.mu.Lock()
|
||||
c.schemaResponses = append(c.schemaResponses, m.GetDataTrackSchemaResponse)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
return next(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *asyncAttributesCapture) takeRequestResponse() *livekit.RequestResponse {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.requestResponses) == 0 {
|
||||
return nil
|
||||
}
|
||||
rr := c.requestResponses[0]
|
||||
c.requestResponses = c.requestResponses[1:]
|
||||
return rr
|
||||
}
|
||||
|
||||
func (c *asyncAttributesCapture) takeSchemaResponse() *livekit.GetDataTrackSchemaResponse {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(c.schemaResponses) == 0 {
|
||||
return nil
|
||||
}
|
||||
sr := c.schemaResponses[0]
|
||||
c.schemaResponses = c.schemaResponses[1:]
|
||||
return sr
|
||||
}
|
||||
|
||||
func (c *asyncAttributesCapture) requestResponseCount() int {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return len(c.requestResponses)
|
||||
}
|
||||
|
||||
func setupAsyncAttributesServer(t *testing.T, name string, enable bool) (*service.LivekitServer, func()) {
|
||||
logger.Infow("----------------STARTING TEST----------------", "test", name)
|
||||
s := createSingleNodeServer(func(c *config.Config) {
|
||||
c.EnableParticipantAsyncAttributes = enable
|
||||
c.Limit.MaxAsyncAttributesSize = 1024
|
||||
})
|
||||
go func() {
|
||||
if err := s.Start(); err != nil {
|
||||
logger.Errorw("server returned error", err)
|
||||
}
|
||||
}()
|
||||
waitForServerToStart(s)
|
||||
return s, func() {
|
||||
s.Stop(true)
|
||||
logger.Infow("----------------FINISHING TEST----------------", "test", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleNodeAsyncAttributes(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, finish := setupAsyncAttributesServer(t, "TestSingleNodeAsyncAttributes", true)
|
||||
defer finish()
|
||||
|
||||
for _, testRTCServicePath := range testRTCServicePaths {
|
||||
t.Run(fmt.Sprintf("testRTCServicePath=%s", testRTCServicePath.String()), func(t *testing.T) {
|
||||
pubCapture := &asyncAttributesCapture{}
|
||||
subCapture := &asyncAttributesCapture{}
|
||||
|
||||
pub := createRTCClient("pub", defaultServerPort, testRTCServicePath, &testclient.Options{
|
||||
AutoSubscribe: true,
|
||||
SignalResponseInterceptor: pubCapture.interceptor(),
|
||||
})
|
||||
sub := createRTCClient("sub", defaultServerPort, testRTCServicePath, &testclient.Options{
|
||||
AutoSubscribe: true,
|
||||
SignalResponseInterceptor: subCapture.interceptor(),
|
||||
})
|
||||
waitUntilConnected(t, pub, sub)
|
||||
defer stopClients(pub, sub)
|
||||
|
||||
schemaID := &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
}
|
||||
definition := []byte("definition-bytes")
|
||||
|
||||
// publisher defines a schema
|
||||
require.NoError(t, pub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: &livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: schemaID,
|
||||
Definition: definition,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// give the server a moment to process; success path sends no response
|
||||
time.Sleep(syncDelay)
|
||||
require.Equal(t, 0, pubCapture.requestResponseCount(), "publisher should not receive an error response on success")
|
||||
|
||||
// subscriber asks for the schema
|
||||
require.NoError(t, sub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_GetDataTrackSchema{
|
||||
GetDataTrackSchema: &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "pub",
|
||||
SchemaId: schemaID,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
resp := subCapture.takeSchemaResponse()
|
||||
if resp == nil {
|
||||
return "subscriber did not receive schema response"
|
||||
}
|
||||
if resp.SchemaDefinition == nil {
|
||||
return "schema response missing definition"
|
||||
}
|
||||
if resp.SchemaDefinition.Id.Name != schemaID.Name {
|
||||
return fmt.Sprintf("expected schema name %s, got %s", schemaID.Name, resp.SchemaDefinition.Id.Name)
|
||||
}
|
||||
if string(resp.SchemaDefinition.Definition) != string(definition) {
|
||||
return fmt.Sprintf("expected definition %q, got %q", definition, resp.SchemaDefinition.Definition)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// subscriber asks for an unknown schema on a known publisher
|
||||
require.NoError(t, sub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_GetDataTrackSchema{
|
||||
GetDataTrackSchema: &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "pub",
|
||||
SchemaId: &livekit.DataTrackSchemaId{
|
||||
Name: "does-not-exist",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rr := subCapture.takeRequestResponse()
|
||||
if rr == nil {
|
||||
return "subscriber did not receive RequestResponse for missing schema"
|
||||
}
|
||||
if rr.Reason != livekit.RequestResponse_NOT_FOUND {
|
||||
return fmt.Sprintf("expected NOT_FOUND, got %s", rr.Reason)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// subscriber asks for a schema on an unknown publisher identity
|
||||
require.NoError(t, sub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_GetDataTrackSchema{
|
||||
GetDataTrackSchema: &livekit.GetDataTrackSchemaRequest{
|
||||
ParticipantIdentity: "unknown-publisher",
|
||||
SchemaId: schemaID,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rr := subCapture.takeRequestResponse()
|
||||
if rr == nil {
|
||||
return "subscriber did not receive RequestResponse for unknown publisher"
|
||||
}
|
||||
if rr.Reason != livekit.RequestResponse_NOT_FOUND {
|
||||
return fmt.Sprintf("expected NOT_FOUND, got %s", rr.Reason)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// publisher sends an invalid define (empty id name)
|
||||
require.NoError(t, pub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: &livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: definition,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rr := pubCapture.takeRequestResponse()
|
||||
if rr == nil {
|
||||
return "publisher did not receive RequestResponse for invalid define"
|
||||
}
|
||||
if rr.Reason != livekit.RequestResponse_INVALID_REQUEST {
|
||||
return fmt.Sprintf("expected INVALID_REQUEST, got %s", rr.Reason)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleNodeAsyncAttributesDisabled(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, finish := setupAsyncAttributesServer(t, "TestSingleNodeAsyncAttributesDisabled", false)
|
||||
defer finish()
|
||||
|
||||
for _, testRTCServicePath := range testRTCServicePaths {
|
||||
t.Run(fmt.Sprintf("testRTCServicePath=%s", testRTCServicePath.String()), func(t *testing.T) {
|
||||
pubCapture := &asyncAttributesCapture{}
|
||||
pub := createRTCClient("pub", defaultServerPort, testRTCServicePath, &testclient.Options{
|
||||
AutoSubscribe: true,
|
||||
SignalResponseInterceptor: pubCapture.interceptor(),
|
||||
})
|
||||
waitUntilConnected(t, pub)
|
||||
defer stopClients(pub)
|
||||
|
||||
require.NoError(t, pub.SendRequest(&livekit.SignalRequest{
|
||||
Message: &livekit.SignalRequest_DefineDataTrackSchema{
|
||||
DefineDataTrackSchema: &livekit.DefineDataTrackSchemaRequest{
|
||||
SchemaDefinition: &livekit.DataTrackSchemaDefinition{
|
||||
Id: &livekit.DataTrackSchemaId{
|
||||
Name: "schema-1",
|
||||
Encoding: livekit.DataTrackSchemaEncoding_DATA_TRACK_SCHEMA_ENCODING_PROTOBUF,
|
||||
},
|
||||
Definition: []byte("definition-bytes"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rr := pubCapture.takeRequestResponse()
|
||||
if rr == nil {
|
||||
return "publisher did not receive RequestResponse"
|
||||
}
|
||||
if rr.Reason != livekit.RequestResponse_NOT_ALLOWED {
|
||||
return fmt.Sprintf("expected NOT_ALLOWED, got %s", rr.Reason)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user