Merge commit '6bd7fac875d9e9009915053d8a590abb372c5679' into feature/livekit-upgrade-1.13.1
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -45,11 +46,15 @@ func (c ClientInfo) isAndroid() bool {
|
||||
return c.ClientInfo != nil && strings.EqualFold(c.ClientInfo.Os, "android")
|
||||
}
|
||||
|
||||
func (c ClientInfo) isOBS() bool {
|
||||
return c.ClientInfo != nil && strings.Contains(c.ClientInfo.Browser, "OBS")
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportsAudioRED() bool {
|
||||
return !c.isFirefox() && !c.isSafari()
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportPrflxOverRelay() bool {
|
||||
func (c ClientInfo) SupportsPrflxOverRelay() bool {
|
||||
return !c.isFirefox()
|
||||
}
|
||||
|
||||
@@ -93,21 +98,36 @@ func (c ClientInfo) SupportsChangeRTPSenderEncodingActive() bool {
|
||||
}
|
||||
|
||||
func (c ClientInfo) ComplyWithCodecOrderInSDPAnswer() bool {
|
||||
return !((c.isLinux() || c.isAndroid()) && c.isFirefox())
|
||||
return (!c.isLinux() && !c.isAndroid()) || !c.isFirefox()
|
||||
}
|
||||
|
||||
// Rust SDK can't decode unknown signal message (TrackSubscribed and ErrorResponse)
|
||||
func (c ClientInfo) SupportTrackSubscribedEvent() bool {
|
||||
return !(c.ClientInfo.GetSdk() == livekit.ClientInfo_RUST && c.ClientInfo.GetProtocol() < 10)
|
||||
func (c ClientInfo) SupportsTrackSubscribedEvent() bool {
|
||||
return c.ClientInfo.GetSdk() != livekit.ClientInfo_RUST || c.ClientInfo.GetProtocol() >= 10
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportErrorResponse() bool {
|
||||
return c.SupportTrackSubscribedEvent()
|
||||
func (c ClientInfo) SupportsRequestResponse() bool {
|
||||
return c.SupportsTrackSubscribedEvent()
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportSctpZeroChecksum() bool {
|
||||
return !(c.ClientInfo.GetSdk() == livekit.ClientInfo_UNKNOWN ||
|
||||
(c.isGo() && c.compareVersion("2.4.0") < 0))
|
||||
func (c ClientInfo) SupportsSctpZeroChecksum() bool {
|
||||
return c.ClientInfo.GetSdk() != livekit.ClientInfo_UNKNOWN &&
|
||||
(!c.isGo() || c.compareVersion("2.4.0") >= 0)
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportsTransceiverReuse() bool {
|
||||
return !c.isSafari()
|
||||
}
|
||||
|
||||
func (c ClientInfo) HasCapability(cap livekit.ClientInfo_Capability) bool {
|
||||
if c.ClientInfo == nil {
|
||||
return false
|
||||
}
|
||||
return slices.Contains(c.ClientInfo.Capabilities, cap)
|
||||
}
|
||||
|
||||
func (c ClientInfo) SupportsPacketTrailer() bool {
|
||||
return c.HasCapability(livekit.ClientInfo_CAP_PACKET_TRAILER)
|
||||
}
|
||||
|
||||
// compareVersion compares a semver against the current client SDK version
|
||||
@@ -121,7 +141,7 @@ func (c ClientInfo) compareVersion(version string) int {
|
||||
parts1 := strings.Split(version, ".")
|
||||
ints0 := make([]int, 3)
|
||||
ints1 := make([]int, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
if len(parts0) > i {
|
||||
ints0[i], _ = strconv.Atoi(parts0[i])
|
||||
}
|
||||
|
||||
@@ -20,13 +20,14 @@ import (
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/config"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
act "github.com/livekit/livekit-server/pkg/sfu/rtpextension/abscapturetime"
|
||||
dd "github.com/livekit/livekit-server/pkg/sfu/rtpextension/dependencydescriptor"
|
||||
"github.com/livekit/mediatransportutil/pkg/rtcconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
frameMarking = "urn:ietf:params:rtp-hdrext:framemarking"
|
||||
repairedRTPStreamID = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
|
||||
frameMarkingURI = "urn:ietf:params:rtp-hdrext:framemarking"
|
||||
repairedRTPStreamIDURI = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
|
||||
)
|
||||
|
||||
type WebRTCConfig struct {
|
||||
@@ -79,23 +80,82 @@ func NewWebRTCConfig(conf *config.Config) (*WebRTCConfig, error) {
|
||||
rtcConf.PacketBufferSizeAudio = rtcConf.PacketBufferSize
|
||||
}
|
||||
|
||||
// publisher configuration
|
||||
publisherConfig := DirectionConfig{
|
||||
return &WebRTCConfig{
|
||||
WebRTCConfig: *webRTCConfig,
|
||||
Receiver: ReceiverConfig{
|
||||
PacketBufferSizeVideo: rtcConf.PacketBufferSizeVideo,
|
||||
PacketBufferSizeAudio: rtcConf.PacketBufferSizeAudio,
|
||||
},
|
||||
Publisher: getPublisherConfig(false),
|
||||
Subscriber: getSubscriberConfig(rtcConf.CongestionControl.UseSendSideBWEInterceptor || rtcConf.CongestionControl.UseSendSideBWE),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *WebRTCConfig) UpdatePublisherConfig(consolidated bool) {
|
||||
c.Publisher = getPublisherConfig(consolidated)
|
||||
}
|
||||
|
||||
func (c *WebRTCConfig) UpdateSubscriberConfig(ccConf config.CongestionControlConfig) {
|
||||
c.Subscriber = getSubscriberConfig(ccConf.UseSendSideBWEInterceptor || ccConf.UseSendSideBWE)
|
||||
}
|
||||
|
||||
func (c *WebRTCConfig) SetBufferFactory(factory *buffer.Factory) {
|
||||
c.BufferFactory = factory
|
||||
c.SettingEngine.BufferFactory = factory.GetOrNew
|
||||
}
|
||||
|
||||
func getPublisherConfig(consolidated bool) DirectionConfig {
|
||||
if consolidated {
|
||||
return DirectionConfig{
|
||||
RTPHeaderExtension: RTPHeaderExtensionConfig{
|
||||
Audio: []string{
|
||||
sdp.SDESMidURI,
|
||||
sdp.SDESRTPStreamIDURI,
|
||||
sdp.AudioLevelURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
Video: []string{
|
||||
sdp.SDESMidURI,
|
||||
sdp.SDESRTPStreamIDURI,
|
||||
sdp.TransportCCURI,
|
||||
sdp.ABSSendTimeURI,
|
||||
frameMarkingURI,
|
||||
dd.ExtensionURI,
|
||||
repairedRTPStreamIDURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
},
|
||||
RTCPFeedback: RTCPFeedbackConfig{
|
||||
Audio: []webrtc.RTCPFeedback{
|
||||
{Type: webrtc.TypeRTCPFBNACK},
|
||||
},
|
||||
Video: []webrtc.RTCPFeedback{
|
||||
{Type: webrtc.TypeRTCPFBTransportCC},
|
||||
{Type: webrtc.TypeRTCPFBGoogREMB},
|
||||
{Type: webrtc.TypeRTCPFBCCM, Parameter: "fir"},
|
||||
{Type: webrtc.TypeRTCPFBNACK},
|
||||
{Type: webrtc.TypeRTCPFBNACK, Parameter: "pli"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return DirectionConfig{
|
||||
RTPHeaderExtension: RTPHeaderExtensionConfig{
|
||||
Audio: []string{
|
||||
sdp.SDESMidURI,
|
||||
sdp.SDESRTPStreamIDURI,
|
||||
sdp.AudioLevelURI,
|
||||
//act.AbsCaptureTimeURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
Video: []string{
|
||||
sdp.SDESMidURI,
|
||||
sdp.SDESRTPStreamIDURI,
|
||||
sdp.TransportCCURI,
|
||||
frameMarking,
|
||||
frameMarkingURI,
|
||||
dd.ExtensionURI,
|
||||
repairedRTPStreamID,
|
||||
//act.AbsCaptureTimeURI,
|
||||
repairedRTPStreamIDURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
},
|
||||
RTCPFeedback: RTCPFeedbackConfig{
|
||||
@@ -110,25 +170,6 @@ func NewWebRTCConfig(conf *config.Config) (*WebRTCConfig, error) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &WebRTCConfig{
|
||||
WebRTCConfig: *webRTCConfig,
|
||||
Receiver: ReceiverConfig{
|
||||
PacketBufferSizeVideo: rtcConf.PacketBufferSizeVideo,
|
||||
PacketBufferSizeAudio: rtcConf.PacketBufferSizeAudio,
|
||||
},
|
||||
Publisher: publisherConfig,
|
||||
Subscriber: getSubscriberConfig(rtcConf.CongestionControl.UseSendSideBWEInterceptor || rtcConf.CongestionControl.UseSendSideBWE),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *WebRTCConfig) UpdateCongestionControl(ccConf config.CongestionControlConfig) {
|
||||
c.Subscriber = getSubscriberConfig(ccConf.UseSendSideBWEInterceptor || ccConf.UseSendSideBWE)
|
||||
}
|
||||
|
||||
func (c *WebRTCConfig) SetBufferFactory(factory *buffer.Factory) {
|
||||
c.BufferFactory = factory
|
||||
c.SettingEngine.BufferFactory = factory.GetOrNew
|
||||
}
|
||||
|
||||
func getSubscriberConfig(enableTWCC bool) DirectionConfig {
|
||||
@@ -136,10 +177,10 @@ func getSubscriberConfig(enableTWCC bool) DirectionConfig {
|
||||
RTPHeaderExtension: RTPHeaderExtensionConfig{
|
||||
Video: []string{
|
||||
dd.ExtensionURI,
|
||||
//act.AbsCaptureTimeURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
Audio: []string{
|
||||
//act.AbsCaptureTimeURI,
|
||||
act.AbsCaptureTimeURI,
|
||||
},
|
||||
},
|
||||
RTCPFeedback: RTCPFeedbackConfig{
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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"
|
||||
"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"
|
||||
)
|
||||
|
||||
var _ types.DataDownTrack = (*DataDownTrack)(nil)
|
||||
var _ types.DataTrackSender = (*DataDownTrack)(nil)
|
||||
|
||||
type DataDownTrackParams struct {
|
||||
Logger logger.Logger
|
||||
SubscriberID livekit.ParticipantID
|
||||
PublishDataTrack types.DataTrack
|
||||
Handle uint16
|
||||
Transport types.DataTrackTransport
|
||||
BytesTrackStats *BytesTrackStats
|
||||
}
|
||||
|
||||
type DataDownTrack struct {
|
||||
params DataDownTrackParams
|
||||
logger logger.Logger
|
||||
createdAt int64
|
||||
}
|
||||
|
||||
func NewDataDownTrack(params DataDownTrackParams) (*DataDownTrack, error) {
|
||||
d := &DataDownTrack{
|
||||
params: params,
|
||||
createdAt: time.Now().UnixNano(),
|
||||
}
|
||||
d.logger = params.Logger.WithValues("name", d.Name(), "handle", d.Handle())
|
||||
|
||||
if err := d.params.PublishDataTrack.AddDataDownTrack(d); err != nil {
|
||||
d.logger.Warnw("could not add data down track", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Infow("created data down track")
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Close() {
|
||||
d.logger.Infow("closing data down track")
|
||||
if d.params.BytesTrackStats != nil {
|
||||
d.params.BytesTrackStats.Stop()
|
||||
}
|
||||
d.params.PublishDataTrack.DeleteDataDownTrack(d.SubscriberID())
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Handle() uint16 {
|
||||
return d.params.Handle
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) PublishDataTrack() types.DataTrack {
|
||||
return d.params.PublishDataTrack
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) ID() livekit.TrackID {
|
||||
return d.params.PublishDataTrack.ID()
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) Name() string {
|
||||
return d.params.PublishDataTrack.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, _arrivalTime int64) {
|
||||
forwardedPacket := *packet
|
||||
forwardedPacket.Handle = d.params.Handle
|
||||
buf, err := forwardedPacket.Marshal()
|
||||
if err != nil {
|
||||
d.logger.Warnw("could not marshal data track message", err)
|
||||
return
|
||||
}
|
||||
if err := d.params.Transport.SendDataTrackMessage(buf); err != nil {
|
||||
d.logger.Warnw("could not send data track message", err)
|
||||
return
|
||||
}
|
||||
if d.params.BytesTrackStats != nil {
|
||||
d.params.BytesTrackStats.AddBytes(uint64(len(buf)), true)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataDownTrack) UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions) {
|
||||
// DT-TODO
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// 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")
|
||||
)
|
||||
|
||||
var _ types.DataTrack = (*DataTrack)(nil)
|
||||
|
||||
type DataTrackParams struct {
|
||||
Logger logger.Logger
|
||||
ParticipantID func() livekit.ParticipantID
|
||||
ParticipantIdentity livekit.ParticipantIdentity
|
||||
BytesTrackStats *BytesTrackStats
|
||||
}
|
||||
|
||||
type DataTrack struct {
|
||||
params DataTrackParams
|
||||
|
||||
logger logger.Logger
|
||||
|
||||
lock sync.Mutex
|
||||
dti *livekit.DataTrackInfo
|
||||
subscribedTracks map[livekit.ParticipantID]types.DataDownTrack
|
||||
|
||||
downTrackSpreader *sfuutils.DownTrackSpreader[types.DataTrackSender]
|
||||
|
||||
stats *dataTrackStats
|
||||
|
||||
closed core.Fuse
|
||||
}
|
||||
|
||||
func NewDataTrack(params DataTrackParams, dti *livekit.DataTrackInfo) *DataTrack {
|
||||
d := &DataTrack{
|
||||
params: params,
|
||||
dti: dti,
|
||||
subscribedTracks: make(map[livekit.ParticipantID]types.DataDownTrack),
|
||||
}
|
||||
d.logger = params.Logger.WithValues("name", d.Name(), "handle", dti.PubHandle)
|
||||
d.downTrackSpreader = sfuutils.NewDownTrackSpreader[types.DataTrackSender](sfuutils.DownTrackSpreaderParams{
|
||||
Threshold: 20,
|
||||
Logger: d.logger,
|
||||
})
|
||||
d.stats = newDataTrackStats(dataTrackStatsParams{Logger: d.logger})
|
||||
d.logger.Infow("created data track", "dataTrackInfo", logger.Proto(d.dti))
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DataTrack) Close() {
|
||||
d.logger.Infow("closing data track")
|
||||
d.closed.Break()
|
||||
|
||||
d.stats.Close()
|
||||
if d.params.BytesTrackStats != nil {
|
||||
d.params.BytesTrackStats.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataTrack) PublisherID() livekit.ParticipantID {
|
||||
return d.params.ParticipantID()
|
||||
}
|
||||
|
||||
func (d *DataTrack) PublisherIdentity() livekit.ParticipantIdentity {
|
||||
return d.params.ParticipantIdentity
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
bytesStats := NewBytesTrackStats(
|
||||
sub.GetCountry(),
|
||||
d.ID(),
|
||||
sub.ID(),
|
||||
sub.Kind(),
|
||||
sub.KindDetails(),
|
||||
sub.GetTelemetryListener(),
|
||||
sub.GetReporter(),
|
||||
)
|
||||
dataDownTrack, err := NewDataDownTrack(DataDownTrackParams{
|
||||
Logger: sub.GetLogger().WithValues("trackID", d.ID()),
|
||||
SubscriberID: sub.ID(),
|
||||
PublishDataTrack: d,
|
||||
Handle: sub.GetNextSubscribedDataTrackHandle(),
|
||||
Transport: sub.GetDataTrackTransport(),
|
||||
BytesTrackStats: bytesStats,
|
||||
})
|
||||
if err != nil {
|
||||
bytesStats.Stop()
|
||||
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.logger.Infow("subscriberID already exists, replacing data downtrack", "subscriberID", dts.SubscriberID())
|
||||
}
|
||||
|
||||
d.downTrackSpreader.Store(dts)
|
||||
d.logger.Infow("data downtrack added", "subscriberID", dts.SubscriberID())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataTrack) DeleteDataDownTrack(subscriberID livekit.ParticipantID) {
|
||||
d.downTrackSpreader.Free(subscriberID)
|
||||
d.logger.Infow("data downtrack deleted", "subscriberID", subscriberID)
|
||||
}
|
||||
|
||||
func (d *DataTrack) HandlePacket(data []byte, packet *datatrack.Packet, arrivalTime int64) {
|
||||
d.stats.Update(packet, arrivalTime, len(data))
|
||||
if d.params.BytesTrackStats != nil {
|
||||
d.params.BytesTrackStats.AddBytes(uint64(len(data)), false)
|
||||
}
|
||||
|
||||
d.downTrackSpreader.Broadcast(func(dts types.DataTrackSender) {
|
||||
dts.WritePacket(data, packet, arrivalTime)
|
||||
})
|
||||
}
|
||||
@@ -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) >= 256 {
|
||||
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: uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
|
||||
data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *ExtensionParticipantSid) Unmarshal(ext Extension) error {
|
||||
if ext.id != uint8(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, 256))
|
||||
_, err := NewExtensionParticipantSid(longTestParticipantID)
|
||||
require.Error(t, err)
|
||||
|
||||
testParticipantID := livekit.ParticipantID("test")
|
||||
extParticipantSid, err := NewExtensionParticipantSid(testParticipantID)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExt := Extension{
|
||||
id: uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
|
||||
data: []byte{'t', 'e', 's', 't'},
|
||||
}
|
||||
ext, err := extParticipantSid.Marshal()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedExt, ext)
|
||||
|
||||
var unmarshaled ExtensionParticipantSid
|
||||
err = unmarshaled.Unmarshal(ext)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testParticipantID, unmarshaled.ParticipantID())
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// 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")
|
||||
errExtensionSizeTooBig = errors.New("extension size is too big")
|
||||
)
|
||||
|
||||
const (
|
||||
headerLength = 12
|
||||
|
||||
versionShift = 5
|
||||
versionMask = (1 << 3) - 1
|
||||
|
||||
startOfFrameShift = 4
|
||||
startOfFrameMask = (1 << 1) - 1
|
||||
|
||||
finalOfFrameShift = 3
|
||||
finalOfFrameMask = (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 = 1
|
||||
extensionSizeLength = 1
|
||||
)
|
||||
|
||||
type Extension struct {
|
||||
id uint8
|
||||
data []byte
|
||||
}
|
||||
|
||||
type Header struct {
|
||||
Version uint8
|
||||
IsStartOfFrame bool
|
||||
IsFinalOfFrame 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 |S|F|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 |
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
End of all extensions
|
||||
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|* padded to 4 byte boundary if aggregate of `Extensions Size` |
|
||||
|* field and all extensions do not end on a 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.IsStartOfFrame = (buf[0] >> startOfFrameShift & startOfFrameMask) > 0
|
||||
h.IsFinalOfFrame = (buf[0] >> finalOfFrameShift & finalOfFrameMask) > 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 {
|
||||
extensionsSize := (binary.BigEndian.Uint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength])+1)*4 - extensionsSizeLength
|
||||
hdrSize += extensionsSizeLength
|
||||
|
||||
extensionHeaderSize := extensionIDLength + extensionSizeLength
|
||||
remainingSize := int(extensionsSize)
|
||||
idx := extensionsSizeOffset + extensionsSizeLength
|
||||
for remainingSize != 0 {
|
||||
// read extension header
|
||||
if len(buf[idx:]) < extensionIDLength || remainingSize < extensionIDLength {
|
||||
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), extensionIDLength)
|
||||
}
|
||||
id := buf[idx]
|
||||
if id == 0 {
|
||||
// end of extensions, padding has started
|
||||
hdrSize += remainingSize
|
||||
break
|
||||
}
|
||||
|
||||
if len(buf[idx+1:]) < extensionSizeLength || remainingSize < extensionSizeLength {
|
||||
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), extensionSizeLength)
|
||||
}
|
||||
size := int(buf[idx+1])
|
||||
|
||||
remainingSize -= extensionHeaderSize
|
||||
idx += extensionHeaderSize
|
||||
hdrSize += extensionHeaderSize
|
||||
|
||||
// read extension data
|
||||
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]})
|
||||
|
||||
remainingSize -= size
|
||||
idx += size
|
||||
hdrSize += size
|
||||
}
|
||||
h.ExtensionsSize = extensionsSize - uint16(remainingSize)
|
||||
}
|
||||
|
||||
return hdrSize, nil
|
||||
}
|
||||
|
||||
func (h *Header) MarshalSize() int {
|
||||
extensionsSize := 0
|
||||
if h.HasExtensions {
|
||||
extensionsSize += extensionsSizeLength
|
||||
for _, ext := range h.Extensions {
|
||||
extensionsSize += len(ext.data) + extensionIDLength + extensionSizeLength
|
||||
}
|
||||
}
|
||||
|
||||
return headerLength + (extensionsSize+3)/4*4
|
||||
}
|
||||
|
||||
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.IsStartOfFrame {
|
||||
buf[0] |= (1 << startOfFrameShift)
|
||||
}
|
||||
if h.IsFinalOfFrame {
|
||||
buf[0] |= (1 << finalOfFrameShift)
|
||||
}
|
||||
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 {
|
||||
extensionsSize := (extensionsSizeLength + h.ExtensionsSize + 3) / 4 * 4
|
||||
binary.BigEndian.PutUint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength], (extensionsSize/4)-1)
|
||||
hdrSize += extensionsSizeLength
|
||||
|
||||
addedSize := 0
|
||||
idx := extensionsSizeOffset + extensionsSizeLength
|
||||
for _, ext := range h.Extensions {
|
||||
buf[idx] = ext.id
|
||||
if len(ext.data) > 255 {
|
||||
return 0, fmt.Errorf("%w: %d > 255", errExtensionSizeTooBig, len(ext.data))
|
||||
}
|
||||
buf[idx+extensionIDLength] = byte(len(ext.data))
|
||||
copy(buf[idx+extensionIDLength+extensionSizeLength:], ext.data)
|
||||
|
||||
extSize := len(ext.data) + extensionIDLength + extensionSizeLength
|
||||
idx += extSize
|
||||
hdrSize += extSize
|
||||
addedSize += extSize
|
||||
}
|
||||
|
||||
paddingSize := extensionsSize - extensionsSizeLength - uint16(addedSize)
|
||||
for i := range paddingSize {
|
||||
buf[idx+int(i)] = 0
|
||||
}
|
||||
idx += int(paddingSize)
|
||||
hdrSize += int(paddingSize)
|
||||
}
|
||||
|
||||
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) + extensionIDLength + extensionSizeLength)
|
||||
h.Extensions[i].data = ext.data
|
||||
h.ExtensionsSize += uint16(len(h.Extensions[i].data) + extensionIDLength + extensionSizeLength)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
h.Extensions = append(h.Extensions, ext)
|
||||
h.ExtensionsSize += uint16(len(ext.data) + extensionIDLength + extensionSizeLength)
|
||||
h.HasExtensions = true
|
||||
}
|
||||
|
||||
func (h *Header) GetExtension(id uint8) (Extension, error) {
|
||||
for _, ext := range h.Extensions {
|
||||
if ext.id == id {
|
||||
return ext, nil
|
||||
}
|
||||
}
|
||||
return Extension{}, fmt.Errorf("%w, id: %d", errExtensionNotFound, id)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
type Packet struct {
|
||||
Header
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (p *Packet) Unmarshal(buf []byte) error {
|
||||
hdrSize, err := p.Header.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Payload = buf[hdrSize:]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Packet) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, p.Header.MarshalSize()+len(p.Payload))
|
||||
if err := p.MarshalTo(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (p *Packet) MarshalTo(buf []byte) error {
|
||||
size := p.Header.MarshalSize() + len(p.Payload)
|
||||
if len(buf) < size {
|
||||
return fmt.Errorf("%w: %d < %d", errBufferSizeInsufficient, len(buf), size)
|
||||
}
|
||||
|
||||
hdrSize, err := p.Header.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf[hdrSize:], p.Payload)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
// 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,
|
||||
IsStartOfFrame: true,
|
||||
IsFinalOfFrame: 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,
|
||||
IsStartOfFrame: true,
|
||||
IsFinalOfFrame: 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, 0x01, 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(uint8(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,
|
||||
IsStartOfFrame: true,
|
||||
IsFinalOfFrame: 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, 0x01, 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(uint8(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,
|
||||
IsStartOfFrame: true,
|
||||
IsFinalOfFrame: 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, 0x01, 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, 0x01, 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(uint8(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 packet", 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, 0x01, 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, 0x02, 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(uint8(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, 0x01, 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, 0x01, 0x07,
|
||||
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
|
||||
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
|
||||
}
|
||||
err = unmarshaled.Unmarshal(badPacket)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2023 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package datatrack
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateRawDataPackets(handle uint16, seqNum uint16, frameNum uint16, numFrames int, frameSize int, frameDuration time.Duration) [][]byte {
|
||||
if seqNum == 0 {
|
||||
seqNum = uint16(rand.Intn(256) + 1)
|
||||
}
|
||||
if frameNum == 0 {
|
||||
frameNum = uint16(rand.Intn(256) + 1)
|
||||
}
|
||||
timestamp := uint32(rand.Intn(1024))
|
||||
|
||||
packetsPerFrame := (frameSize + 255) / 256 // using 256 bytes of payload per packet
|
||||
if packetsPerFrame == 0 {
|
||||
return nil
|
||||
}
|
||||
numPackets := packetsPerFrame * numFrames
|
||||
rawPackets := make([][]byte, 0, numPackets)
|
||||
for range numFrames {
|
||||
remainingSize := frameSize
|
||||
for packetIdx := range packetsPerFrame {
|
||||
payloadSize := min(remainingSize, 256)
|
||||
payload := make([]byte, payloadSize)
|
||||
for i := range len(payload) {
|
||||
payload[i] = byte(255 - i)
|
||||
}
|
||||
packet := &Packet{
|
||||
Header: Header{
|
||||
Version: 0,
|
||||
IsStartOfFrame: packetIdx == 0,
|
||||
IsFinalOfFrame: 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
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils/mono"
|
||||
)
|
||||
|
||||
type dataTrackStatsParams struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type dataTrackStats struct {
|
||||
params dataTrackStatsParams
|
||||
|
||||
lock sync.Mutex
|
||||
startTime int64
|
||||
endTime int64
|
||||
highestSequenceNumber uint16
|
||||
numPackets int
|
||||
numPacketsLost int
|
||||
numPacketsOutOfOrder int
|
||||
numFrames int // count of `F` tagged packets, i. e. packets with final packet of frame marker
|
||||
numBytes int
|
||||
}
|
||||
|
||||
func newDataTrackStats(params dataTrackStatsParams) *dataTrackStats {
|
||||
return &dataTrackStats{
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dataTrackStats) Update(packet *datatrack.Packet, arrivalTime int64, payloadLength int) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.numBytes += payloadLength
|
||||
|
||||
if d.endTime != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if d.startTime == 0 {
|
||||
d.startTime = arrivalTime
|
||||
d.highestSequenceNumber = packet.SequenceNumber
|
||||
d.numPackets = 1
|
||||
} else {
|
||||
diff := packet.SequenceNumber - d.highestSequenceNumber
|
||||
switch {
|
||||
case diff == 0: // duplicate
|
||||
return
|
||||
|
||||
case diff > (1 << 15): // out of order
|
||||
d.numPackets++
|
||||
d.numPacketsOutOfOrder++
|
||||
if d.numPacketsLost > 0 {
|
||||
d.numPacketsLost--
|
||||
}
|
||||
|
||||
default: // in order
|
||||
d.numPackets++
|
||||
d.numPacketsLost += int(diff) - 1
|
||||
d.highestSequenceNumber = packet.SequenceNumber
|
||||
}
|
||||
}
|
||||
|
||||
if packet.IsFinalOfFrame {
|
||||
d.numFrames++
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dataTrackStats) Close() {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.endTime = mono.UnixNano()
|
||||
|
||||
if d.startTime != 0 {
|
||||
duration := time.Duration(d.endTime - d.startTime).Seconds()
|
||||
fps := float64(d.numFrames) / duration
|
||||
|
||||
d.params.Logger.Infow(
|
||||
"data track stats",
|
||||
"duration", duration,
|
||||
"numPackets", d.numPackets,
|
||||
"numPacketsLost", d.numPacketsLost,
|
||||
"numPacketsOutOfOrder", d.numPacketsOutOfOrder,
|
||||
"numFrames", d.numFrames,
|
||||
"fps", fps,
|
||||
"numBytes", d.numBytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
package dynacast
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -23,20 +24,41 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
func TestSubscribedMaxQuality(t *testing.T) {
|
||||
type testDynacastManagerListener struct {
|
||||
onSubscribedMaxQualityChange func(subscribedQualties []*livekit.SubscribedCodec)
|
||||
onSubscribedAudioCodecChange func(subscribedCodecs []*livekit.SubscribedAudioCodec)
|
||||
}
|
||||
|
||||
func (t *testDynacastManagerListener) OnDynacastSubscribedMaxQualityChange(
|
||||
subscribedQualities []*livekit.SubscribedCodec,
|
||||
_maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
) {
|
||||
t.onSubscribedMaxQualityChange(subscribedQualities)
|
||||
}
|
||||
|
||||
func (t *testDynacastManagerListener) OnDynacastSubscribedAudioCodecChange(
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) {
|
||||
t.onSubscribedAudioCodecChange(codecs)
|
||||
}
|
||||
|
||||
func TestSubscribedMaxQuality(t *testing.T) {
|
||||
t.Run("subscribers muted", func(t *testing.T) {
|
||||
dm := NewDynacastManager(DynacastManagerParams{})
|
||||
var lock sync.Mutex
|
||||
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
|
||||
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
|
||||
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
|
||||
Listener: &testDynacastManagerListener{
|
||||
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeVP8, livekit.VideoQuality_HIGH)
|
||||
@@ -72,21 +94,20 @@ func TestSubscribedMaxQuality(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("subscribers max quality", func(t *testing.T) {
|
||||
dm := NewDynacastManager(DynacastManagerParams{
|
||||
DynacastPauseDelay: 100 * time.Millisecond,
|
||||
})
|
||||
|
||||
lock := sync.RWMutex{}
|
||||
lock.Lock()
|
||||
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
|
||||
lock.Unlock()
|
||||
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
|
||||
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
|
||||
Listener: &testDynacastManagerListener{
|
||||
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
dm.maxSubscribedQuality = map[mime.MimeType]livekit.VideoQuality{
|
||||
dm.(*dynacastManagerVideo).maxSubscribedQuality = map[mime.MimeType]livekit.VideoQuality{
|
||||
mime.MimeTypeVP8: livekit.VideoQuality_LOW,
|
||||
mime.MimeTypeAV1: livekit.VideoQuality_LOW,
|
||||
}
|
||||
@@ -279,98 +300,224 @@ func TestSubscribedMaxQuality(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCodecRegression(t *testing.T) {
|
||||
dm := NewDynacastManager(DynacastManagerParams{})
|
||||
var lock sync.Mutex
|
||||
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
|
||||
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
t.Run("codec regression video", func(t *testing.T) {
|
||||
var lock sync.Mutex
|
||||
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
|
||||
|
||||
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
|
||||
Listener: &testDynacastManagerListener{
|
||||
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
|
||||
lock.Lock()
|
||||
actualSubscribedQualities = subscribedQualities
|
||||
lock.Unlock()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_HIGH)
|
||||
|
||||
expectedSubscribedQualities := []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
dm.HandleCodecRegression(mime.MimeTypeAV1, mime.MimeTypeVP8)
|
||||
|
||||
expectedSubscribedQualities = []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeVP8.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
// av1 quality change should be forwarded to vp8
|
||||
// av1 quality change of node should be ignored
|
||||
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_MEDIUM)
|
||||
dm.NotifySubscriberNodeMaxQuality("n1", []types.SubscribedCodecQuality{
|
||||
{CodecMime: mime.MimeTypeAV1, Quality: livekit.VideoQuality_HIGH},
|
||||
})
|
||||
expectedSubscribedQualities = []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeVP8.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
})
|
||||
|
||||
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_HIGH)
|
||||
t.Run("codec regression audio", func(t *testing.T) {
|
||||
var lock sync.Mutex
|
||||
actualSubscribedCodecs := make([]*livekit.SubscribedAudioCodec, 0)
|
||||
|
||||
expectedSubscribedQualities := []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
|
||||
dm := NewDynacastManagerAudio(DynacastManagerAudioParams{
|
||||
Listener: &testDynacastManagerListener{
|
||||
onSubscribedAudioCodecChange: func(subscribedCodecs []*livekit.SubscribedAudioCodec) {
|
||||
lock.Lock()
|
||||
actualSubscribedCodecs = subscribedCodecs
|
||||
lock.Unlock()
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
})
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
dm.NotifySubscription("s1", mime.MimeTypeRED, true)
|
||||
|
||||
dm.HandleCodecRegression(mime.MimeTypeAV1, mime.MimeTypeVP8)
|
||||
|
||||
expectedSubscribedQualities = []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
expectedSubscribedCodecs := []*livekit.SubscribedAudioCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeRED.String(),
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeVP8.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
dm.HandleCodecRegression(mime.MimeTypeRED, mime.MimeTypeOpus)
|
||||
|
||||
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeRED.String(),
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
{
|
||||
Codec: mime.MimeTypeOpus.String(),
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
// RED disable as subscriber or subscriber node should be ignored as it has been regressed
|
||||
dm.NotifySubscription("s1", mime.MimeTypeRED, false)
|
||||
dm.NotifySubscriptionNode("n1", []*livekit.SubscribedAudioCodec{
|
||||
{Codec: mime.MimeTypeRED.String(), Enabled: false},
|
||||
})
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
// `s1` unsubscription should turn off `opus`
|
||||
dm.NotifySubscription("s1", mime.MimeTypeOpus, false)
|
||||
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeRED.String(),
|
||||
Enabled: false,
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeOpus.String(),
|
||||
Enabled: false,
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
// a node subscription should turn `opus` back on
|
||||
dm.NotifySubscriptionNode("n1", []*livekit.SubscribedAudioCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeOpus.String(),
|
||||
Enabled: true,
|
||||
},
|
||||
})
|
||||
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeRED.String(),
|
||||
Enabled: false,
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeOpus.String(),
|
||||
Enabled: true,
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
|
||||
// av1 quality change should be forwarded to vp8
|
||||
// av1 quality change of node should be ignored
|
||||
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_MEDIUM)
|
||||
dm.NotifySubscriberNodeMaxQuality("n1", []types.SubscribedCodecQuality{
|
||||
{CodecMime: mime.MimeTypeAV1, Quality: livekit.VideoQuality_HIGH},
|
||||
})
|
||||
expectedSubscribedQualities = []*livekit.SubscribedCodec{
|
||||
{
|
||||
Codec: mime.MimeTypeAV1.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Codec: mime.MimeTypeVP8.String(),
|
||||
Qualities: []*livekit.SubscribedQuality{
|
||||
{Quality: livekit.VideoQuality_LOW, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
|
||||
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Eventually(t, func() bool {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
|
||||
}, 10*time.Second, 100*time.Millisecond)
|
||||
}
|
||||
|
||||
func subscribedCodecsAsString(c1 []*livekit.SubscribedCodec) string {
|
||||
sort.Slice(c1, func(i, j int) bool { return c1[i].Codec < c1[j].Codec })
|
||||
var s1 string
|
||||
slices.SortFunc(c1, func(a, b *livekit.SubscribedCodec) int {
|
||||
return strings.Compare(a.Codec, b.Codec)
|
||||
})
|
||||
|
||||
var s1 strings.Builder
|
||||
for _, c := range c1 {
|
||||
s1 += c.String()
|
||||
s1.WriteString(c.String())
|
||||
}
|
||||
return s1
|
||||
return s1.String()
|
||||
}
|
||||
|
||||
func subscribedAudioCodecsAsString(c1 []*livekit.SubscribedAudioCodec) string {
|
||||
slices.SortFunc(c1, func(a, b *livekit.SubscribedAudioCodec) int {
|
||||
return strings.Compare(a.Codec, b.Codec)
|
||||
})
|
||||
|
||||
var s1 strings.Builder
|
||||
for _, c := range c1 {
|
||||
s1.WriteString(c.String())
|
||||
}
|
||||
return s1.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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 dynacast
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
)
|
||||
|
||||
var _ DynacastManager = (*dynacastManagerAudio)(nil)
|
||||
var _ dynacastQualityListener = (*dynacastManagerAudio)(nil)
|
||||
|
||||
type DynacastManagerAudioParams struct {
|
||||
Listener DynacastManagerListener
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type dynacastManagerAudio struct {
|
||||
params DynacastManagerAudioParams
|
||||
|
||||
subscribedCodecs map[mime.MimeType]bool
|
||||
committedSubscribedCodecs map[mime.MimeType]bool
|
||||
|
||||
isClosed bool
|
||||
|
||||
*dynacastManagerBase
|
||||
}
|
||||
|
||||
func NewDynacastManagerAudio(params DynacastManagerAudioParams) DynacastManager {
|
||||
if params.Logger == nil {
|
||||
params.Logger = logger.GetLogger()
|
||||
}
|
||||
d := &dynacastManagerAudio{
|
||||
params: params,
|
||||
subscribedCodecs: make(map[mime.MimeType]bool),
|
||||
committedSubscribedCodecs: make(map[mime.MimeType]bool),
|
||||
}
|
||||
d.dynacastManagerBase = newDynacastManagerBase(dynacastManagerBaseParams{
|
||||
Logger: params.Logger,
|
||||
OpsQueueDepth: 4,
|
||||
OnRestart: func() {
|
||||
d.committedSubscribedCodecs = make(map[mime.MimeType]bool)
|
||||
},
|
||||
OnDynacastQualityCreate: func(mimeType mime.MimeType) dynacastQuality {
|
||||
dq := newDynacastQualityAudio(dynacastQualityAudioParams{
|
||||
MimeType: mimeType,
|
||||
Listener: d,
|
||||
Logger: d.params.Logger,
|
||||
})
|
||||
return dq
|
||||
},
|
||||
OnRegressCodec: func(fromMime, toMime mime.MimeType) {
|
||||
d.subscribedCodecs[fromMime] = false
|
||||
|
||||
// if the new codec is not added, notify the publisher to start publishing
|
||||
if _, ok := d.subscribedCodecs[toMime]; !ok {
|
||||
d.subscribedCodecs[toMime] = true
|
||||
}
|
||||
},
|
||||
OnUpdateNeeded: d.update,
|
||||
})
|
||||
return d
|
||||
}
|
||||
|
||||
// It is possible for tracks to be in pending close state. When track
|
||||
// is waiting to be closed, a node is not streaming a track. This can
|
||||
// be used to force an update announcing that subscribed codec is disabled,
|
||||
// i.e. indicating not pulling track any more.
|
||||
func (d *dynacastManagerAudio) ForceEnable(enabled bool) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
for mime := range d.committedSubscribedCodecs {
|
||||
d.committedSubscribedCodecs[mime] = enabled
|
||||
}
|
||||
|
||||
d.enqueueSubscribedChange()
|
||||
}
|
||||
|
||||
func (d *dynacastManagerAudio) NotifySubscription(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
enabled bool,
|
||||
) {
|
||||
dq := d.getOrCreateDynacastQuality(mime)
|
||||
if dq != nil {
|
||||
dq.NotifySubscription(subscriberID, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dynacastManagerAudio) NotifySubscriptionNode(
|
||||
nodeID livekit.NodeID,
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) {
|
||||
for _, codec := range codecs {
|
||||
dq := d.getOrCreateDynacastQuality(mime.NormalizeMimeType(codec.Codec))
|
||||
if dq != nil {
|
||||
dq.NotifySubscriptionNode(nodeID, codec.Enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dynacastManagerAudio) OnUpdateAudioCodecForMime(mime mime.MimeType, enabled bool) {
|
||||
d.lock.Lock()
|
||||
if _, ok := d.regressedCodec[mime]; !ok {
|
||||
d.subscribedCodecs[mime] = enabled
|
||||
}
|
||||
d.lock.Unlock()
|
||||
|
||||
d.update(false)
|
||||
}
|
||||
|
||||
func (d *dynacastManagerAudio) update(force bool) {
|
||||
d.lock.Lock()
|
||||
|
||||
d.params.Logger.Debugw(
|
||||
"processing subscribed codec change",
|
||||
"force", force,
|
||||
"committedSubscribedCodecs", d.committedSubscribedCodecs,
|
||||
"subscribedCodecs", d.subscribedCodecs,
|
||||
)
|
||||
|
||||
if len(d.subscribedCodecs) == 0 {
|
||||
// no mime has been added, nothing to update
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// add or remove of a mime triggers an update
|
||||
changed := len(d.subscribedCodecs) != len(d.committedSubscribedCodecs)
|
||||
if !changed {
|
||||
for mime, enabled := range d.subscribedCodecs {
|
||||
if ce, ok := d.committedSubscribedCodecs[mime]; ok {
|
||||
if ce != enabled {
|
||||
changed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !force && !changed {
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
d.params.Logger.Debugw(
|
||||
"committing subscribed codec change",
|
||||
"force", force,
|
||||
"committedSubscribedCoecs", d.committedSubscribedCodecs,
|
||||
"subscribedcodecs", d.subscribedCodecs,
|
||||
)
|
||||
|
||||
// commit change
|
||||
d.committedSubscribedCodecs = make(map[mime.MimeType]bool, len(d.subscribedCodecs))
|
||||
for mime, enabled := range d.subscribedCodecs {
|
||||
d.committedSubscribedCodecs[mime] = enabled
|
||||
}
|
||||
|
||||
d.enqueueSubscribedChange()
|
||||
d.lock.Unlock()
|
||||
}
|
||||
|
||||
func (d *dynacastManagerAudio) enqueueSubscribedChange() {
|
||||
if d.isClosed || d.params.Listener == nil {
|
||||
return
|
||||
}
|
||||
|
||||
subscribedCodecs := make([]*livekit.SubscribedAudioCodec, 0, len(d.committedSubscribedCodecs))
|
||||
for mime, enabled := range d.committedSubscribedCodecs {
|
||||
subscribedCodecs = append(subscribedCodecs, &livekit.SubscribedAudioCodec{
|
||||
Codec: mime.String(),
|
||||
Enabled: enabled,
|
||||
})
|
||||
}
|
||||
|
||||
d.params.Logger.Debugw(
|
||||
"subscribedAudioCodecChange",
|
||||
"subscribedCodecs", logger.ProtoSlice(subscribedCodecs),
|
||||
)
|
||||
d.notifyOpsQueue.Enqueue(func() {
|
||||
d.params.Listener.OnDynacastSubscribedAudioCodecChange(subscribedCodecs)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// 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 dynacast
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/utils"
|
||||
)
|
||||
|
||||
type dynacastManagerBaseParams struct {
|
||||
Logger logger.Logger
|
||||
OpsQueueDepth uint
|
||||
OnRestart func()
|
||||
OnDynacastQualityCreate func(mimeType mime.MimeType) dynacastQuality
|
||||
OnRegressCodec func(fromMime, toMime mime.MimeType)
|
||||
OnUpdateNeeded func(force bool)
|
||||
}
|
||||
|
||||
type dynacastManagerBase struct {
|
||||
params dynacastManagerBaseParams
|
||||
|
||||
lock sync.RWMutex
|
||||
regressedCodec map[mime.MimeType]struct{}
|
||||
dynacastQuality map[mime.MimeType]dynacastQuality
|
||||
|
||||
notifyOpsQueue *utils.OpsQueue
|
||||
|
||||
isClosed bool
|
||||
|
||||
dynacastManagerNull
|
||||
dynacastQualityListenerNull
|
||||
}
|
||||
|
||||
func newDynacastManagerBase(params dynacastManagerBaseParams) *dynacastManagerBase {
|
||||
if params.OpsQueueDepth == 0 {
|
||||
params.OpsQueueDepth = 4
|
||||
}
|
||||
d := &dynacastManagerBase{
|
||||
params: params,
|
||||
regressedCodec: make(map[mime.MimeType]struct{}),
|
||||
dynacastQuality: make(map[mime.MimeType]dynacastQuality),
|
||||
notifyOpsQueue: utils.NewOpsQueue(utils.OpsQueueParams{
|
||||
Name: "dynacast-notify",
|
||||
MinSize: params.OpsQueueDepth,
|
||||
FlushOnStop: true,
|
||||
Logger: params.Logger,
|
||||
}),
|
||||
}
|
||||
d.notifyOpsQueue.Start()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) AddCodec(mime mime.MimeType) {
|
||||
d.getOrCreateDynacastQuality(mime)
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) HandleCodecRegression(fromMime, toMime mime.MimeType) {
|
||||
fromDq := d.getOrCreateDynacastQuality(fromMime)
|
||||
|
||||
d.lock.Lock()
|
||||
if d.isClosed {
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if fromDq == nil {
|
||||
// should not happen as we have added the codec on setup receiver
|
||||
d.params.Logger.Warnw("regression from codec not found", nil, "mime", fromMime, "toMime", toMime)
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
d.regressedCodec[fromMime] = struct{}{}
|
||||
d.params.OnRegressCodec(fromMime, toMime)
|
||||
d.lock.Unlock()
|
||||
|
||||
d.params.OnUpdateNeeded(false)
|
||||
|
||||
fromDq.Stop()
|
||||
fromDq.RegressTo(d.getOrCreateDynacastQuality(toMime))
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) Restart() {
|
||||
d.lock.Lock()
|
||||
d.params.OnRestart()
|
||||
|
||||
dqs := d.getDynacastQualitiesLocked()
|
||||
d.lock.Unlock()
|
||||
|
||||
for _, dq := range dqs {
|
||||
dq.Restart()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) Close() {
|
||||
d.notifyOpsQueue.Stop()
|
||||
|
||||
d.lock.Lock()
|
||||
dqs := d.getDynacastQualitiesLocked()
|
||||
d.dynacastQuality = make(map[mime.MimeType]dynacastQuality)
|
||||
|
||||
d.isClosed = true
|
||||
d.lock.Unlock()
|
||||
|
||||
for _, dq := range dqs {
|
||||
dq.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// There are situations like track unmute or streaming from a different node
|
||||
// where subscription changes needs to sent to the provider immediately.
|
||||
// This bypasses any debouncing and forces a subscription change update
|
||||
// with immediate effect.
|
||||
func (d *dynacastManagerBase) ForceUpdate() {
|
||||
d.params.OnUpdateNeeded(true)
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) ClearSubscriberNodes() {
|
||||
d.lock.Lock()
|
||||
dqs := d.getDynacastQualitiesLocked()
|
||||
d.lock.Unlock()
|
||||
for _, dq := range dqs {
|
||||
dq.ClearSubscriberNodes()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) getOrCreateDynacastQuality(mimeType mime.MimeType) dynacastQuality {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
if d.isClosed || mimeType == mime.MimeTypeUnknown {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dq := d.dynacastQuality[mimeType]; dq != nil {
|
||||
return dq
|
||||
}
|
||||
|
||||
dq := d.params.OnDynacastQualityCreate(mimeType)
|
||||
dq.Start()
|
||||
|
||||
d.dynacastQuality[mimeType] = dq
|
||||
return dq
|
||||
}
|
||||
|
||||
func (d *dynacastManagerBase) getDynacastQualitiesLocked() []dynacastQuality {
|
||||
return slices.Collect(maps.Values(d.dynacastQuality))
|
||||
}
|
||||
+64
-142
@@ -15,148 +15,85 @@
|
||||
package dynacast
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/bep/debounce"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/utils"
|
||||
)
|
||||
|
||||
type DynacastManagerParams struct {
|
||||
var _ DynacastManager = (*dynacastManagerVideo)(nil)
|
||||
var _ dynacastQualityListener = (*dynacastManagerVideo)(nil)
|
||||
|
||||
type DynacastManagerVideoParams struct {
|
||||
DynacastPauseDelay time.Duration
|
||||
Listener DynacastManagerListener
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type DynacastManager struct {
|
||||
params DynacastManagerParams
|
||||
type dynacastManagerVideo struct {
|
||||
params DynacastManagerVideoParams
|
||||
|
||||
lock sync.RWMutex
|
||||
regressedCodec map[mime.MimeType]struct{}
|
||||
dynacastQuality map[mime.MimeType]*DynacastQuality
|
||||
maxSubscribedQuality map[mime.MimeType]livekit.VideoQuality
|
||||
committedMaxSubscribedQuality map[mime.MimeType]livekit.VideoQuality
|
||||
|
||||
maxSubscribedQualityDebounce func(func())
|
||||
maxSubscribedQualityDebouncePending bool
|
||||
|
||||
qualityNotifyOpQueue *utils.OpsQueue
|
||||
|
||||
isClosed bool
|
||||
|
||||
onSubscribedMaxQualityChange func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality)
|
||||
*dynacastManagerBase
|
||||
}
|
||||
|
||||
func NewDynacastManager(params DynacastManagerParams) *DynacastManager {
|
||||
func NewDynacastManagerVideo(params DynacastManagerVideoParams) DynacastManager {
|
||||
if params.Logger == nil {
|
||||
params.Logger = logger.GetLogger()
|
||||
}
|
||||
d := &DynacastManager{
|
||||
d := &dynacastManagerVideo{
|
||||
params: params,
|
||||
regressedCodec: make(map[mime.MimeType]struct{}),
|
||||
dynacastQuality: make(map[mime.MimeType]*DynacastQuality),
|
||||
maxSubscribedQuality: make(map[mime.MimeType]livekit.VideoQuality),
|
||||
committedMaxSubscribedQuality: make(map[mime.MimeType]livekit.VideoQuality),
|
||||
qualityNotifyOpQueue: utils.NewOpsQueue(utils.OpsQueueParams{
|
||||
Name: "quality-notify",
|
||||
MinSize: 64,
|
||||
FlushOnStop: true,
|
||||
Logger: params.Logger,
|
||||
}),
|
||||
}
|
||||
if params.DynacastPauseDelay > 0 {
|
||||
d.maxSubscribedQualityDebounce = debounce.New(params.DynacastPauseDelay)
|
||||
}
|
||||
d.qualityNotifyOpQueue.Start()
|
||||
d.dynacastManagerBase = newDynacastManagerBase(dynacastManagerBaseParams{
|
||||
Logger: params.Logger,
|
||||
OpsQueueDepth: 64,
|
||||
OnRestart: func() {
|
||||
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality)
|
||||
},
|
||||
OnDynacastQualityCreate: func(mimeType mime.MimeType) dynacastQuality {
|
||||
dq := newDynacastQualityVideo(dynacastQualityVideoParams{
|
||||
MimeType: mimeType,
|
||||
Listener: d,
|
||||
Logger: d.params.Logger,
|
||||
})
|
||||
return dq
|
||||
},
|
||||
OnRegressCodec: func(fromMime, toMime mime.MimeType) {
|
||||
d.maxSubscribedQuality[fromMime] = livekit.VideoQuality_OFF
|
||||
|
||||
// if the new codec is not added, notify the publisher to start publishing
|
||||
if _, ok := d.maxSubscribedQuality[toMime]; !ok {
|
||||
d.maxSubscribedQuality[toMime] = livekit.VideoQuality_HIGH
|
||||
}
|
||||
},
|
||||
OnUpdateNeeded: d.update,
|
||||
})
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DynacastManager) OnSubscribedMaxQualityChange(f func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality)) {
|
||||
d.lock.Lock()
|
||||
d.onSubscribedMaxQualityChange = f
|
||||
d.lock.Unlock()
|
||||
}
|
||||
|
||||
func (d *DynacastManager) AddCodec(mime mime.MimeType) {
|
||||
d.getOrCreateDynacastQuality(mime)
|
||||
}
|
||||
|
||||
func (d *DynacastManager) HandleCodecRegression(fromMime, toMime mime.MimeType) {
|
||||
fromDq := d.getOrCreateDynacastQuality(fromMime)
|
||||
|
||||
d.lock.Lock()
|
||||
if d.isClosed {
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if fromDq == nil {
|
||||
// should not happen as we have added the codec on setup receiver
|
||||
d.params.Logger.Warnw("regression from codec not found", nil, "mime", fromMime)
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
d.regressedCodec[fromMime] = struct{}{}
|
||||
d.maxSubscribedQuality[fromMime] = livekit.VideoQuality_OFF
|
||||
|
||||
// if the new codec is not added, notify the publisher to start publishing
|
||||
if _, ok := d.maxSubscribedQuality[toMime]; !ok {
|
||||
d.maxSubscribedQuality[toMime] = livekit.VideoQuality_HIGH
|
||||
}
|
||||
|
||||
d.lock.Unlock()
|
||||
d.update(false)
|
||||
|
||||
fromDq.Stop()
|
||||
fromDq.RegressTo(d.getOrCreateDynacastQuality(toMime))
|
||||
}
|
||||
|
||||
func (d *DynacastManager) Restart() {
|
||||
d.lock.Lock()
|
||||
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality)
|
||||
|
||||
dqs := d.getDynacastQualitiesLocked()
|
||||
d.lock.Unlock()
|
||||
|
||||
for _, dq := range dqs {
|
||||
dq.Restart()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DynacastManager) Close() {
|
||||
d.qualityNotifyOpQueue.Stop()
|
||||
|
||||
d.lock.Lock()
|
||||
dqs := d.getDynacastQualitiesLocked()
|
||||
d.dynacastQuality = make(map[mime.MimeType]*DynacastQuality)
|
||||
|
||||
d.isClosed = true
|
||||
d.lock.Unlock()
|
||||
|
||||
for _, dq := range dqs {
|
||||
dq.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// THere are situations like track unmute or streaming from a different node
|
||||
// where subscribed quality needs to sent to the provider immediately.
|
||||
// This bypasses any debouncing and forces a subscribed quality update
|
||||
// with immediate effect.
|
||||
func (d *DynacastManager) ForceUpdate() {
|
||||
d.update(true)
|
||||
}
|
||||
|
||||
// It is possible for tracks to be in pending close state. When track
|
||||
// is waiting to be closed, a node is not streaming a track. This can
|
||||
// be used to force an update announcing that subscribed quality is OFF,
|
||||
// i.e. indicating not pulling track any more.
|
||||
func (d *DynacastManager) ForceQuality(quality livekit.VideoQuality) {
|
||||
func (d *dynacastManagerVideo) ForceQuality(quality livekit.VideoQuality) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
@@ -167,14 +104,21 @@ func (d *DynacastManager) ForceQuality(quality livekit.VideoQuality) {
|
||||
d.enqueueSubscribedQualityChange()
|
||||
}
|
||||
|
||||
func (d *DynacastManager) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, mime mime.MimeType, quality livekit.VideoQuality) {
|
||||
func (d *dynacastManagerVideo) NotifySubscriberMaxQuality(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
quality livekit.VideoQuality,
|
||||
) {
|
||||
dq := d.getOrCreateDynacastQuality(mime)
|
||||
if dq != nil {
|
||||
dq.NotifySubscriberMaxQuality(subscriberID, quality)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DynacastManager) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []types.SubscribedCodecQuality) {
|
||||
func (d *dynacastManagerVideo) NotifySubscriberNodeMaxQuality(
|
||||
nodeID livekit.NodeID,
|
||||
qualities []types.SubscribedCodecQuality,
|
||||
) {
|
||||
for _, quality := range qualities {
|
||||
dq := d.getOrCreateDynacastQuality(quality.CodecMime)
|
||||
if dq != nil {
|
||||
@@ -183,34 +127,10 @@ func (d *DynacastManager) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DynacastManager) getOrCreateDynacastQuality(mimeType mime.MimeType) *DynacastQuality {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
if d.isClosed || mimeType == mime.MimeTypeUnknown {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dq := d.dynacastQuality[mimeType]; dq != nil {
|
||||
return dq
|
||||
}
|
||||
|
||||
dq := NewDynacastQuality(DynacastQualityParams{
|
||||
MimeType: mimeType,
|
||||
Logger: d.params.Logger,
|
||||
})
|
||||
dq.OnSubscribedMaxQualityChange(d.updateMaxQualityForMime)
|
||||
dq.Start()
|
||||
|
||||
d.dynacastQuality[mimeType] = dq
|
||||
return dq
|
||||
}
|
||||
|
||||
func (d *DynacastManager) getDynacastQualitiesLocked() []*DynacastQuality {
|
||||
return maps.Values(d.dynacastQuality)
|
||||
}
|
||||
|
||||
func (d *DynacastManager) updateMaxQualityForMime(mime mime.MimeType, maxQuality livekit.VideoQuality) {
|
||||
func (d *dynacastManagerVideo) OnUpdateMaxQualityForMime(
|
||||
mime mime.MimeType,
|
||||
maxQuality livekit.VideoQuality,
|
||||
) {
|
||||
d.lock.Lock()
|
||||
if _, ok := d.regressedCodec[mime]; !ok {
|
||||
d.maxSubscribedQuality[mime] = maxQuality
|
||||
@@ -220,10 +140,11 @@ func (d *DynacastManager) updateMaxQualityForMime(mime mime.MimeType, maxQuality
|
||||
d.update(false)
|
||||
}
|
||||
|
||||
func (d *DynacastManager) update(force bool) {
|
||||
func (d *dynacastManagerVideo) update(force bool) {
|
||||
d.lock.Lock()
|
||||
|
||||
d.params.Logger.Debugw("processing quality change",
|
||||
d.params.Logger.Debugw(
|
||||
"processing quality change",
|
||||
"force", force,
|
||||
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
|
||||
"maxSubscribedQuality", d.maxSubscribedQuality,
|
||||
@@ -260,7 +181,8 @@ func (d *DynacastManager) update(force bool) {
|
||||
|
||||
if downgradesOnly && d.maxSubscribedQualityDebounce != nil {
|
||||
if !d.maxSubscribedQualityDebouncePending {
|
||||
d.params.Logger.Debugw("debouncing quality downgrade",
|
||||
d.params.Logger.Debugw(
|
||||
"debouncing quality downgrade",
|
||||
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
|
||||
"maxSubscribedQuality", d.maxSubscribedQuality,
|
||||
)
|
||||
@@ -269,7 +191,8 @@ func (d *DynacastManager) update(force bool) {
|
||||
})
|
||||
d.maxSubscribedQualityDebouncePending = true
|
||||
} else {
|
||||
d.params.Logger.Debugw("quality downgrade waiting for debounce",
|
||||
d.params.Logger.Debugw(
|
||||
"quality downgrade waiting for debounce",
|
||||
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
|
||||
"maxSubscribedQuality", d.maxSubscribedQuality,
|
||||
)
|
||||
@@ -285,7 +208,8 @@ func (d *DynacastManager) update(force bool) {
|
||||
d.maxSubscribedQualityDebouncePending = false
|
||||
}
|
||||
|
||||
d.params.Logger.Debugw("committing quality change",
|
||||
d.params.Logger.Debugw(
|
||||
"committing quality change",
|
||||
"force", force,
|
||||
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
|
||||
"maxSubscribedQuality", d.maxSubscribedQuality,
|
||||
@@ -293,16 +217,14 @@ func (d *DynacastManager) update(force bool) {
|
||||
|
||||
// commit change
|
||||
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality, len(d.maxSubscribedQuality))
|
||||
for mime, quality := range d.maxSubscribedQuality {
|
||||
d.committedMaxSubscribedQuality[mime] = quality
|
||||
}
|
||||
maps.Copy(d.committedMaxSubscribedQuality, d.maxSubscribedQuality)
|
||||
|
||||
d.enqueueSubscribedQualityChange()
|
||||
d.lock.Unlock()
|
||||
}
|
||||
|
||||
func (d *DynacastManager) enqueueSubscribedQualityChange() {
|
||||
if d.isClosed || d.onSubscribedMaxQualityChange == nil {
|
||||
func (d *dynacastManagerVideo) enqueueSubscribedQualityChange() {
|
||||
if d.isClosed || d.params.Listener == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -343,7 +265,7 @@ func (d *DynacastManager) enqueueSubscribedQualityChange() {
|
||||
"subscribedCodecs", subscribedCodecs,
|
||||
"maxSubscribedQualities", maxSubscribedQualities,
|
||||
)
|
||||
d.qualityNotifyOpQueue.Enqueue(func() {
|
||||
d.onSubscribedMaxQualityChange(subscribedCodecs, maxSubscribedQualities)
|
||||
d.notifyOpsQueue.Enqueue(func() {
|
||||
d.params.Listener.OnDynacastSubscribedMaxQualityChange(subscribedCodecs, maxSubscribedQualities)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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 dynacast
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
var _ dynacastQuality = (*dynacastQualityAudio)(nil)
|
||||
|
||||
type dynacastQualityAudioParams struct {
|
||||
MimeType mime.MimeType
|
||||
Listener dynacastQualityListener
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
// dynacastQualityAudio manages enable a single receiver of a media track
|
||||
type dynacastQualityAudio struct {
|
||||
params dynacastQualityAudioParams
|
||||
|
||||
// quality level enable/disable
|
||||
lock sync.RWMutex
|
||||
initialized bool
|
||||
subscriberEnables map[livekit.ParticipantID]bool
|
||||
subscriberNodeEnables map[livekit.NodeID]bool
|
||||
enabled bool
|
||||
regressTo dynacastQuality
|
||||
|
||||
dynacastQualityNull
|
||||
}
|
||||
|
||||
func newDynacastQualityAudio(params dynacastQualityAudioParams) dynacastQuality {
|
||||
return &dynacastQualityAudio{
|
||||
params: params,
|
||||
subscriberEnables: make(map[livekit.ParticipantID]bool),
|
||||
subscriberNodeEnables: make(map[livekit.NodeID]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) Start() {
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) Restart() {
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) Stop() {
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) NotifySubscription(subscriberID livekit.ParticipantID, enabled bool) {
|
||||
d.params.Logger.Debugw(
|
||||
"setting subscriber codec enable",
|
||||
"mime", d.params.MimeType,
|
||||
"subscriberID", subscriberID,
|
||||
"enabled", enabled,
|
||||
)
|
||||
|
||||
d.lock.Lock()
|
||||
if r := d.regressTo; r != nil {
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
delete(d.subscriberEnables, subscriberID)
|
||||
} else {
|
||||
d.subscriberEnables[subscriberID] = true
|
||||
}
|
||||
d.lock.Unlock()
|
||||
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool) {
|
||||
d.params.Logger.Debugw(
|
||||
"setting subscriber node codec enabled",
|
||||
"mime", d.params.MimeType,
|
||||
"subscriberNodeID", nodeID,
|
||||
"enabled", enabled,
|
||||
)
|
||||
|
||||
d.lock.Lock()
|
||||
if r := d.regressTo; r != nil {
|
||||
// the downstream node will synthesize correct enable (its dynacast manager has codec regression), just ignore it
|
||||
d.params.Logger.Debugw(
|
||||
"ignoring node codec change, regressed to another dynacast quality",
|
||||
"mime", d.params.MimeType,
|
||||
"regressedMime", d.regressTo.Mime(),
|
||||
)
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
delete(d.subscriberNodeEnables, nodeID)
|
||||
} else {
|
||||
d.subscriberNodeEnables[nodeID] = true
|
||||
}
|
||||
d.lock.Unlock()
|
||||
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) ClearSubscriberNodes() {
|
||||
d.lock.Lock()
|
||||
d.subscriberNodeEnables = make(map[livekit.NodeID]bool)
|
||||
d.lock.Unlock()
|
||||
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) Mime() mime.MimeType {
|
||||
return d.params.MimeType
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) RegressTo(other dynacastQuality) {
|
||||
d.lock.Lock()
|
||||
d.regressTo = other
|
||||
d.lock.Unlock()
|
||||
|
||||
other.Restart()
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) reset() {
|
||||
d.lock.Lock()
|
||||
d.initialized = false
|
||||
d.lock.Unlock()
|
||||
}
|
||||
|
||||
func (d *dynacastQualityAudio) updateQualityChange(force bool) {
|
||||
d.lock.Lock()
|
||||
enabled := len(d.subscriberEnables) != 0 || len(d.subscriberNodeEnables) != 0
|
||||
if enabled == d.enabled && d.initialized && !force {
|
||||
d.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
d.initialized = true
|
||||
d.enabled = enabled
|
||||
d.params.Logger.Debugw(
|
||||
"notifying enabled change",
|
||||
"mime", d.params.MimeType,
|
||||
"enabled", d.enabled,
|
||||
"subscriberNodeEnables", d.subscriberNodeEnables,
|
||||
"subscribedEnables", d.subscriberEnables,
|
||||
"force", force,
|
||||
)
|
||||
d.lock.Unlock()
|
||||
|
||||
d.params.Listener.OnUpdateAudioCodecForMime(d.params.MimeType, enabled)
|
||||
}
|
||||
+61
-42
@@ -18,23 +18,26 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
var _ dynacastQuality = (*dynacastQualityVideo)(nil)
|
||||
|
||||
const (
|
||||
initialQualityUpdateWait = 10 * time.Second
|
||||
)
|
||||
|
||||
type DynacastQualityParams struct {
|
||||
type dynacastQualityVideoParams struct {
|
||||
MimeType mime.MimeType
|
||||
Listener dynacastQualityListener
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
// DynacastQuality manages max subscribed quality of a single receiver of a media track
|
||||
type DynacastQuality struct {
|
||||
params DynacastQualityParams
|
||||
// dynacastQualityVideo manages max subscribed quality of a single receiver of a media track
|
||||
type dynacastQualityVideo struct {
|
||||
params dynacastQualityVideoParams
|
||||
|
||||
// quality level enable/disable
|
||||
lock sync.RWMutex
|
||||
@@ -43,38 +46,32 @@ type DynacastQuality struct {
|
||||
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality
|
||||
maxSubscribedQuality livekit.VideoQuality
|
||||
maxQualityTimer *time.Timer
|
||||
regressTo *DynacastQuality
|
||||
regressTo dynacastQuality
|
||||
|
||||
onSubscribedMaxQualityChange func(mimeType mime.MimeType, maxSubscribedQuality livekit.VideoQuality)
|
||||
dynacastQualityNull
|
||||
}
|
||||
|
||||
func NewDynacastQuality(params DynacastQualityParams) *DynacastQuality {
|
||||
return &DynacastQuality{
|
||||
func newDynacastQualityVideo(params dynacastQualityVideoParams) dynacastQuality {
|
||||
return &dynacastQualityVideo{
|
||||
params: params,
|
||||
maxSubscriberQuality: make(map[livekit.ParticipantID]livekit.VideoQuality),
|
||||
maxSubscriberNodeQuality: make(map[livekit.NodeID]livekit.VideoQuality),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) Start() {
|
||||
func (d *dynacastQualityVideo) Start() {
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) Restart() {
|
||||
func (d *dynacastQualityVideo) Restart() {
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) Stop() {
|
||||
func (d *dynacastQualityVideo) Stop() {
|
||||
d.stopMaxQualityTimer()
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) OnSubscribedMaxQualityChange(f func(mimeType mime.MimeType, maxSubscribedQuality livekit.VideoQuality)) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
d.onSubscribedMaxQualityChange = f
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
|
||||
func (d *dynacastQualityVideo) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
|
||||
d.params.Logger.Debugw(
|
||||
"setting subscriber max quality",
|
||||
"mime", d.params.MimeType,
|
||||
@@ -99,7 +96,7 @@ func (d *DynacastQuality) NotifySubscriberMaxQuality(subscriberID livekit.Partic
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
|
||||
func (d *dynacastQualityVideo) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
|
||||
d.params.Logger.Debugw(
|
||||
"setting subscriber node max quality",
|
||||
"mime", d.params.MimeType,
|
||||
@@ -110,8 +107,12 @@ func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
|
||||
d.lock.Lock()
|
||||
if r := d.regressTo; r != nil {
|
||||
// the downstream node will synthesize correct quality notify (its dynacast manager has codec regression), just ignore it
|
||||
d.params.Logger.Debugw(
|
||||
"ignoring node quality change, regressed to another dynacast quality",
|
||||
"mime", d.params.MimeType,
|
||||
"regressedMime", d.regressTo.Mime(),
|
||||
)
|
||||
d.lock.Unlock()
|
||||
r.params.Logger.Debugw("ignoring node quality change, regressed to another dynacast quality", "mime", d.params.MimeType)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,7 +126,19 @@ func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) RegressTo(other *DynacastQuality) {
|
||||
func (d *dynacastQualityVideo) ClearSubscriberNodes() {
|
||||
d.lock.Lock()
|
||||
d.maxSubscriberNodeQuality = make(map[livekit.NodeID]livekit.VideoQuality)
|
||||
d.lock.Unlock()
|
||||
|
||||
d.updateQualityChange(false)
|
||||
}
|
||||
|
||||
func (d *dynacastQualityVideo) Mime() mime.MimeType {
|
||||
return d.params.MimeType
|
||||
}
|
||||
|
||||
func (d *dynacastQualityVideo) RegressTo(other dynacastQuality) {
|
||||
d.lock.Lock()
|
||||
d.regressTo = other
|
||||
maxSubscriberQuality := d.maxSubscriberQuality
|
||||
@@ -134,33 +147,41 @@ func (d *DynacastQuality) RegressTo(other *DynacastQuality) {
|
||||
d.maxSubscriberNodeQuality = make(map[livekit.NodeID]livekit.VideoQuality)
|
||||
d.lock.Unlock()
|
||||
|
||||
other.lock.Lock()
|
||||
other.Replace(maxSubscriberQuality, maxSubscriberNodeQuality)
|
||||
}
|
||||
|
||||
func (d *dynacastQualityVideo) Replace(
|
||||
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
|
||||
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
|
||||
) {
|
||||
d.lock.Lock()
|
||||
for subID, quality := range maxSubscriberQuality {
|
||||
if otherQuality, ok := other.maxSubscriberQuality[subID]; ok {
|
||||
if oldQuality, ok := d.maxSubscriberQuality[subID]; ok {
|
||||
// no QUALITY_OFF in the map
|
||||
if quality > otherQuality {
|
||||
other.maxSubscriberQuality[subID] = quality
|
||||
if quality > oldQuality {
|
||||
d.maxSubscriberQuality[subID] = quality
|
||||
}
|
||||
} else {
|
||||
other.maxSubscriberQuality[subID] = quality
|
||||
d.maxSubscriberQuality[subID] = quality
|
||||
}
|
||||
}
|
||||
|
||||
for nodeID, quality := range maxSubscriberNodeQuality {
|
||||
if otherQuality, ok := other.maxSubscriberNodeQuality[nodeID]; ok {
|
||||
if oldQuality, ok := d.maxSubscriberNodeQuality[nodeID]; ok {
|
||||
// no QUALITY_OFF in the map
|
||||
if quality > otherQuality {
|
||||
other.maxSubscriberNodeQuality[nodeID] = quality
|
||||
if quality > oldQuality {
|
||||
d.maxSubscriberNodeQuality[nodeID] = quality
|
||||
}
|
||||
} else {
|
||||
other.maxSubscriberNodeQuality[nodeID] = quality
|
||||
d.maxSubscriberNodeQuality[nodeID] = quality
|
||||
}
|
||||
}
|
||||
other.lock.Unlock()
|
||||
other.Restart()
|
||||
d.lock.Unlock()
|
||||
|
||||
d.Restart()
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) reset() {
|
||||
func (d *dynacastQualityVideo) reset() {
|
||||
d.lock.Lock()
|
||||
d.initialized = false
|
||||
d.lock.Unlock()
|
||||
@@ -168,7 +189,7 @@ func (d *DynacastQuality) reset() {
|
||||
d.startMaxQualityTimer()
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) updateQualityChange(force bool) {
|
||||
func (d *dynacastQualityVideo) updateQualityChange(force bool) {
|
||||
d.lock.Lock()
|
||||
maxSubscribedQuality := livekit.VideoQuality_OFF
|
||||
for _, subQuality := range d.maxSubscriberQuality {
|
||||
@@ -189,22 +210,20 @@ func (d *DynacastQuality) updateQualityChange(force bool) {
|
||||
|
||||
d.initialized = true
|
||||
d.maxSubscribedQuality = maxSubscribedQuality
|
||||
d.params.Logger.Debugw("notifying quality change",
|
||||
d.params.Logger.Debugw(
|
||||
"notifying quality change",
|
||||
"mime", d.params.MimeType,
|
||||
"maxSubscriberQuality", d.maxSubscriberQuality,
|
||||
"maxSubscriberNodeQuality", d.maxSubscriberNodeQuality,
|
||||
"maxSubscribedQuality", d.maxSubscribedQuality,
|
||||
"force", force,
|
||||
)
|
||||
onSubscribedMaxQualityChange := d.onSubscribedMaxQualityChange
|
||||
d.lock.Unlock()
|
||||
|
||||
if onSubscribedMaxQualityChange != nil {
|
||||
onSubscribedMaxQualityChange(d.params.MimeType, maxSubscribedQuality)
|
||||
}
|
||||
d.params.Listener.OnUpdateMaxQualityForMime(d.params.MimeType, maxSubscribedQuality)
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) startMaxQualityTimer() {
|
||||
func (d *dynacastQualityVideo) startMaxQualityTimer() {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
@@ -219,7 +238,7 @@ func (d *DynacastQuality) startMaxQualityTimer() {
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DynacastQuality) stopMaxQualityTimer() {
|
||||
func (d *dynacastQualityVideo) stopMaxQualityTimer() {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
// 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 dynacast
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
type DynacastManagerListener interface {
|
||||
OnDynacastSubscribedMaxQualityChange(
|
||||
subscribedQualities []*livekit.SubscribedCodec,
|
||||
maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
)
|
||||
|
||||
OnDynacastSubscribedAudioCodecChange(codecs []*livekit.SubscribedAudioCodec)
|
||||
}
|
||||
|
||||
var _ DynacastManagerListener = (*DynacastManagerListenerNull)(nil)
|
||||
|
||||
type DynacastManagerListenerNull struct {
|
||||
}
|
||||
|
||||
func (d *DynacastManagerListenerNull) OnDynacastSubscribedMaxQualityChange(
|
||||
subscribedQualities []*livekit.SubscribedCodec,
|
||||
maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
) {
|
||||
}
|
||||
func (d *DynacastManagerListenerNull) OnDynacastSubscribedAudioCodecChange(
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) {
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
type DynacastManager interface {
|
||||
AddCodec(mime mime.MimeType)
|
||||
HandleCodecRegression(fromMime, toMime mime.MimeType)
|
||||
Restart()
|
||||
Close()
|
||||
ForceUpdate()
|
||||
ForceQuality(quality livekit.VideoQuality)
|
||||
ForceEnable(enabled bool)
|
||||
|
||||
NotifySubscriberMaxQuality(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
quality livekit.VideoQuality,
|
||||
)
|
||||
NotifySubscription(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
enabled bool,
|
||||
)
|
||||
|
||||
NotifySubscriberNodeMaxQuality(
|
||||
nodeID livekit.NodeID,
|
||||
qualities []types.SubscribedCodecQuality,
|
||||
)
|
||||
NotifySubscriptionNode(
|
||||
nodeID livekit.NodeID,
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
)
|
||||
ClearSubscriberNodes()
|
||||
}
|
||||
|
||||
var _ DynacastManager = (*dynacastManagerNull)(nil)
|
||||
|
||||
type dynacastManagerNull struct {
|
||||
}
|
||||
|
||||
func (d *dynacastManagerNull) AddCodec(mime mime.MimeType) {}
|
||||
func (d *dynacastManagerNull) HandleCodecRegression(fromMime, toMime mime.MimeType) {}
|
||||
func (d *dynacastManagerNull) Restart() {}
|
||||
func (d *dynacastManagerNull) Close() {}
|
||||
func (d *dynacastManagerNull) ForceUpdate() {}
|
||||
func (d *dynacastManagerNull) ForceQuality(quality livekit.VideoQuality) {}
|
||||
func (d *dynacastManagerNull) ForceEnable(enabled bool) {}
|
||||
func (d *dynacastManagerNull) NotifySubscriberMaxQuality(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
quality livekit.VideoQuality,
|
||||
) {
|
||||
}
|
||||
func (d *dynacastManagerNull) NotifySubscription(
|
||||
subscriberID livekit.ParticipantID,
|
||||
mime mime.MimeType,
|
||||
enabled bool,
|
||||
) {
|
||||
}
|
||||
func (d *dynacastManagerNull) NotifySubscriberNodeMaxQuality(
|
||||
nodeID livekit.NodeID,
|
||||
qualities []types.SubscribedCodecQuality,
|
||||
) {
|
||||
}
|
||||
func (d *dynacastManagerNull) NotifySubscriptionNode(
|
||||
nodeID livekit.NodeID,
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) {
|
||||
}
|
||||
func (d *dynacastManagerNull) ClearSubscriberNodes() {}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type dynacastQualityListener interface {
|
||||
OnUpdateMaxQualityForMime(mimeType mime.MimeType, maxQuality livekit.VideoQuality)
|
||||
OnUpdateAudioCodecForMime(mimeType mime.MimeType, enabled bool)
|
||||
}
|
||||
|
||||
var _ dynacastQualityListener = (*dynacastQualityListenerNull)(nil)
|
||||
|
||||
type dynacastQualityListenerNull struct {
|
||||
}
|
||||
|
||||
func (d *dynacastQualityListenerNull) OnUpdateMaxQualityForMime(
|
||||
mimeType mime.MimeType,
|
||||
maxQuality livekit.VideoQuality,
|
||||
) {
|
||||
}
|
||||
|
||||
func (d *dynacastQualityListenerNull) OnUpdateAudioCodecForMime(
|
||||
mimeType mime.MimeType,
|
||||
enabled bool,
|
||||
) {
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
type dynacastQuality interface {
|
||||
Start()
|
||||
Restart()
|
||||
Stop()
|
||||
|
||||
NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality)
|
||||
NotifySubscription(subscriberID livekit.ParticipantID, enabled bool)
|
||||
|
||||
NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality)
|
||||
NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool)
|
||||
ClearSubscriberNodes()
|
||||
|
||||
Replace(
|
||||
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
|
||||
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
|
||||
)
|
||||
|
||||
Mime() mime.MimeType
|
||||
RegressTo(other dynacastQuality)
|
||||
}
|
||||
|
||||
var _ dynacastQuality = (*dynacastQualityNull)(nil)
|
||||
|
||||
type dynacastQualityNull struct {
|
||||
}
|
||||
|
||||
func (d *dynacastQualityNull) Start() {}
|
||||
func (d *dynacastQualityNull) Restart() {}
|
||||
func (d *dynacastQualityNull) Stop() {}
|
||||
func (d *dynacastQualityNull) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
|
||||
}
|
||||
func (d *dynacastQualityNull) NotifySubscription(subscriberID livekit.ParticipantID, enabled bool) {}
|
||||
func (d *dynacastQualityNull) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
|
||||
}
|
||||
func (d *dynacastQualityNull) NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool) {}
|
||||
func (d *dynacastQualityNull) ClearSubscriberNodes() {}
|
||||
func (d *dynacastQualityNull) Replace(
|
||||
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
|
||||
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
|
||||
) {
|
||||
}
|
||||
func (d *dynacastQualityNull) Mime() mime.MimeType { return mime.MimeTypeUnknown }
|
||||
func (d *dynacastQualityNull) RegressTo(other dynacastQuality) {}
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
|
||||
type EgressLauncher interface {
|
||||
StartEgress(context.Context, *rpc.StartEgressRequest) (*livekit.EgressInfo, error)
|
||||
StopEgress(context.Context, *livekit.StopEgressRequest) (*livekit.EgressInfo, error)
|
||||
}
|
||||
|
||||
func StartParticipantEgress(
|
||||
@@ -42,16 +43,17 @@ func StartParticipantEgress(
|
||||
) error {
|
||||
if req, err := startParticipantEgress(ctx, launcher, opts, identity, roomName, roomID); err != nil {
|
||||
// send egress failed webhook
|
||||
ts.NotifyEvent(ctx, &livekit.WebhookEvent{
|
||||
Event: webhook.EventEgressEnded,
|
||||
EgressInfo: &livekit.EgressInfo{
|
||||
RoomId: string(roomID),
|
||||
RoomName: string(roomName),
|
||||
Status: livekit.EgressStatus_EGRESS_FAILED,
|
||||
Error: err.Error(),
|
||||
Request: &livekit.EgressInfo_Participant{Participant: req},
|
||||
},
|
||||
})
|
||||
|
||||
info := &livekit.EgressInfo{
|
||||
RoomId: string(roomID),
|
||||
RoomName: string(roomName),
|
||||
Status: livekit.EgressStatus_EGRESS_FAILED,
|
||||
Error: err.Error(),
|
||||
Request: &livekit.EgressInfo_Participant{Participant: req},
|
||||
}
|
||||
|
||||
ts.NotifyEgressEvent(ctx, webhook.EventEgressEnded, info)
|
||||
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -103,16 +105,16 @@ func StartTrackEgress(
|
||||
) error {
|
||||
if req, err := startTrackEgress(ctx, launcher, opts, track, roomName, roomID); err != nil {
|
||||
// send egress failed webhook
|
||||
ts.NotifyEvent(ctx, &livekit.WebhookEvent{
|
||||
Event: webhook.EventEgressEnded,
|
||||
EgressInfo: &livekit.EgressInfo{
|
||||
RoomId: string(roomID),
|
||||
RoomName: string(roomName),
|
||||
Status: livekit.EgressStatus_EGRESS_FAILED,
|
||||
Error: err.Error(),
|
||||
Request: &livekit.EgressInfo_Track{Track: req},
|
||||
},
|
||||
})
|
||||
|
||||
info := &livekit.EgressInfo{
|
||||
RoomId: string(roomID),
|
||||
RoomName: string(roomName),
|
||||
Status: livekit.EgressStatus_EGRESS_FAILED,
|
||||
Error: err.Error(),
|
||||
Request: &livekit.EgressInfo_Track{Track: req},
|
||||
}
|
||||
ts.NotifyEgressEvent(ctx, webhook.EventEgressEnded, info)
|
||||
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -32,9 +32,6 @@ var (
|
||||
ErrEmptyParticipantID = errors.New("participant ID cannot be empty")
|
||||
ErrMissingGrants = errors.New("VideoGrant is missing")
|
||||
ErrInternalError = errors.New("internal error")
|
||||
ErrNameExceedsLimits = errors.New("name length exceeds limits")
|
||||
ErrMetadataExceedsLimits = errors.New("metadata size exceeds limits")
|
||||
ErrAttributesExceedsLimits = errors.New("attributes size exceeds limits")
|
||||
|
||||
// Track subscription related
|
||||
ErrNoTrackPermission = errors.New("participant is not allowed to subscribe to this track")
|
||||
|
||||
@@ -20,146 +20,67 @@ import (
|
||||
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
protoCodecs "github.com/livekit/protocol/codecs"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
var OpusCodecCapability = webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeOpus.String(),
|
||||
ClockRate: 48000,
|
||||
Channels: 2,
|
||||
SDPFmtpLine: "minptime=10;useinbandfec=1",
|
||||
}
|
||||
var RedCodecCapability = webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeRED.String(),
|
||||
ClockRate: 48000,
|
||||
Channels: 2,
|
||||
SDPFmtpLine: "111/111",
|
||||
}
|
||||
var videoRTX = webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeRTX.String(),
|
||||
ClockRate: 90000,
|
||||
}
|
||||
|
||||
func registerCodecs(me *webrtc.MediaEngine, codecs []*livekit.Codec, rtcpFeedback RTCPFeedbackConfig, filterOutH264HighProfile bool) error {
|
||||
opusCodec := OpusCodecCapability
|
||||
opusCodec.RTCPFeedback = rtcpFeedback.Audio
|
||||
var opusPayload webrtc.PayloadType
|
||||
if IsCodecEnabled(codecs, opusCodec) {
|
||||
opusPayload = 111
|
||||
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: opusCodec,
|
||||
PayloadType: opusPayload,
|
||||
}, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
// audio codecs
|
||||
if IsCodecEnabled(codecs, protoCodecs.OpusCodecParameters.RTPCodecCapability) {
|
||||
cp := protoCodecs.OpusCodecParameters
|
||||
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Audio
|
||||
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if IsCodecEnabled(codecs, RedCodecCapability) {
|
||||
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: RedCodecCapability,
|
||||
PayloadType: 63,
|
||||
}, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
if IsCodecEnabled(codecs, protoCodecs.RedCodecParameters.RTPCodecCapability) {
|
||||
if err := me.RegisterCodec(protoCodecs.RedCodecParameters, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtxEnabled := IsCodecEnabled(codecs, videoRTX)
|
||||
for _, codec := range []webrtc.RTPCodecParameters{protoCodecs.PCMUCodecParameters, protoCodecs.PCMACodecParameters} {
|
||||
if !IsCodecEnabled(codecs, codec.RTPCodecCapability) {
|
||||
continue
|
||||
}
|
||||
|
||||
h264HighProfileFmtp := "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032"
|
||||
for _, codec := range []webrtc.RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeVP8.String(),
|
||||
ClockRate: 90000,
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeVP9.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=0",
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 98,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeVP9.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "profile-id=1",
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 100,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 125,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f",
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 108,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: h264HighProfileFmtp,
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 123,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeAV1.String(),
|
||||
ClockRate: 90000,
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 35,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: webrtc.MimeTypeH265,
|
||||
ClockRate: 90000,
|
||||
RTCPFeedback: rtcpFeedback.Video,
|
||||
},
|
||||
PayloadType: 116,
|
||||
},
|
||||
} {
|
||||
if filterOutH264HighProfile && codec.RTPCodecCapability.SDPFmtpLine == h264HighProfileFmtp {
|
||||
cp := codec
|
||||
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Audio
|
||||
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// video codecs
|
||||
rtxEnabled := IsCodecEnabled(codecs, protoCodecs.VideoRTXCodecParameters.RTPCodecCapability)
|
||||
for _, codec := range protoCodecs.VideoCodecsParameters {
|
||||
if filterOutH264HighProfile && codec.RTPCodecCapability.SDPFmtpLine == protoCodecs.H264HighProfileFmtp {
|
||||
continue
|
||||
}
|
||||
if mime.IsMimeTypeStringRTX(codec.MimeType) {
|
||||
continue
|
||||
}
|
||||
if IsCodecEnabled(codecs, codec.RTPCodecCapability) {
|
||||
if err := me.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
if rtxEnabled {
|
||||
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||
MimeType: mime.MimeTypeRTX.String(),
|
||||
ClockRate: 90000,
|
||||
SDPFmtpLine: fmt.Sprintf("apt=%d", codec.PayloadType),
|
||||
},
|
||||
PayloadType: codec.PayloadType + 1,
|
||||
}, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !IsCodecEnabled(codecs, codec.RTPCodecCapability) {
|
||||
continue
|
||||
}
|
||||
|
||||
cp := codec
|
||||
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Video
|
||||
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !rtxEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
cp = protoCodecs.VideoRTXCodecParameters
|
||||
cp.RTPCodecCapability.SDPFmtpLine = fmt.Sprintf("apt=%d", codec.PayloadType)
|
||||
cp.PayloadType = codec.PayloadType + 1
|
||||
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -215,3 +136,77 @@ func selectAlternativeVideoCodec(enabledCodecs []*livekit.Codec) string {
|
||||
// no viable codec in the list of enabled codecs, fall back to the most widely supported codec
|
||||
return mime.MimeTypeVP8.String()
|
||||
}
|
||||
|
||||
func selectAlternativeAudioCodec(enabledCodecs []*livekit.Codec) string {
|
||||
for _, c := range enabledCodecs {
|
||||
if mime.IsMimeTypeStringAudio(c.Mime) {
|
||||
return c.Mime
|
||||
}
|
||||
}
|
||||
// no viable codec in the list of enabled codecs, fall back to the most widely supported codec
|
||||
return mime.MimeTypeOpus.String()
|
||||
}
|
||||
|
||||
// mergeCodecsByMime returns a union b, deduplicated by mime type.
|
||||
func mergeCodecsByMime(a, b []*livekit.Codec) []*livekit.Codec {
|
||||
merged := make([]*livekit.Codec, 0, len(a)+len(b))
|
||||
merged = append(merged, a...)
|
||||
for _, c := range b {
|
||||
seen := false
|
||||
for _, existing := range a {
|
||||
if mime.IsMimeTypeStringEqual(c.Mime, existing.Mime) {
|
||||
seen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
merged = append(merged, c)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
func filterCodecs(
|
||||
codecs []webrtc.RTPCodecParameters,
|
||||
enabledCodecs []*livekit.Codec,
|
||||
rtcpFeedbackConfig RTCPFeedbackConfig,
|
||||
filterOutH264HighProfile bool,
|
||||
) []webrtc.RTPCodecParameters {
|
||||
filteredCodecs := make([]webrtc.RTPCodecParameters, 0, len(codecs))
|
||||
for _, c := range codecs {
|
||||
if filterOutH264HighProfile && isH264HighProfile(c.RTPCodecCapability.SDPFmtpLine) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, enabledCodec := range enabledCodecs {
|
||||
if mime.NormalizeMimeType(enabledCodec.Mime) == mime.NormalizeMimeType(c.RTPCodecCapability.MimeType) {
|
||||
if !mime.IsMimeTypeStringEqual(c.RTPCodecCapability.MimeType, mime.MimeTypeRTX.String()) {
|
||||
if mime.IsMimeTypeStringVideo(c.RTPCodecCapability.MimeType) {
|
||||
c.RTPCodecCapability.RTCPFeedback = rtcpFeedbackConfig.Video
|
||||
} else {
|
||||
c.RTPCodecCapability.RTCPFeedback = rtcpFeedbackConfig.Audio
|
||||
}
|
||||
}
|
||||
filteredCodecs = append(filteredCodecs, c)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredCodecs
|
||||
}
|
||||
|
||||
func isH264HighProfile(fmtp string) bool {
|
||||
params := strings.Split(fmtp, ";")
|
||||
for _, param := range params {
|
||||
parts := strings.Split(param, "=")
|
||||
if len(parts) == 2 {
|
||||
if parts[0] == "profile-level-id" {
|
||||
// https://datatracker.ietf.org/doc/html/rfc6184#section-8.1
|
||||
// hex value 0x64 for profile_idc is high profile
|
||||
return strings.HasPrefix(parts[1], "64")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,8 +23,11 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
"github.com/livekit/protocol/utils/mono"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/config"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/dynacast"
|
||||
@@ -33,77 +35,100 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/connectionquality"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/interceptor"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
util "github.com/livekit/mediatransportutil"
|
||||
)
|
||||
|
||||
var _ types.LocalMediaTrack = (*MediaTrack)(nil)
|
||||
|
||||
// MediaTrack represents a WebRTC track that needs to be forwarded
|
||||
// Implements MediaTrack and PublishedTrack interface
|
||||
type MediaTrack struct {
|
||||
params MediaTrackParams
|
||||
numUpTracks atomic.Uint32
|
||||
buffer *buffer.Buffer
|
||||
everSubscribed atomic.Bool
|
||||
|
||||
*MediaTrackReceiver
|
||||
*MediaLossProxy
|
||||
|
||||
dynacastManager *dynacast.DynacastManager
|
||||
dynacastManager dynacast.DynacastManager
|
||||
|
||||
lock sync.RWMutex
|
||||
|
||||
rttFromXR atomic.Bool
|
||||
|
||||
enableRegression bool
|
||||
backupCodecPolicy livekit.BackupCodecPolicy
|
||||
regressionTargetCodec mime.MimeType
|
||||
regressionTargetCodecReceived bool
|
||||
|
||||
onSubscribedMaxQualityChange func(
|
||||
trackID livekit.TrackID,
|
||||
trackInfo *livekit.TrackInfo,
|
||||
subscribedQualities []*livekit.SubscribedCodec,
|
||||
maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
) error
|
||||
onSubscribedAudioCodecChange func(
|
||||
trackID livekit.TrackID,
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) error
|
||||
}
|
||||
|
||||
type MediaTrackParams struct {
|
||||
SignalCid string
|
||||
SdpCid string
|
||||
ParticipantID livekit.ParticipantID
|
||||
ParticipantIdentity livekit.ParticipantIdentity
|
||||
ParticipantVersion uint32
|
||||
BufferFactory *buffer.Factory
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
PLIThrottleConfig sfu.PLIThrottleConfig
|
||||
AudioConfig sfu.AudioConfig
|
||||
VideoConfig config.VideoConfig
|
||||
Telemetry telemetry.TelemetryService
|
||||
Logger logger.Logger
|
||||
SimTracks map[uint32]SimulcastTrackInfo
|
||||
OnRTCP func([]rtcp.Packet)
|
||||
ForwardStats *sfu.ForwardStats
|
||||
OnTrackEverSubscribed func(livekit.TrackID)
|
||||
ParticipantID func() livekit.ParticipantID
|
||||
ParticipantIdentity livekit.ParticipantIdentity
|
||||
ParticipantVersion uint32
|
||||
ParticipantCountry string
|
||||
ParticipantKind livekit.ParticipantInfo_Kind
|
||||
ParticipantKindDetails []livekit.ParticipantInfo_KindDetail
|
||||
BufferFactory *buffer.Factory
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
PLIThrottleConfig sfu.PLIThrottleConfig
|
||||
AudioConfig sfu.AudioConfig
|
||||
VideoConfig config.VideoConfig
|
||||
TelemetryListener types.ParticipantTelemetryListener
|
||||
Logger logger.Logger
|
||||
Reporter roomobs.TrackReporter
|
||||
SimTracks map[uint32]interceptor.SimulcastTrackInfo
|
||||
OnRTCP func([]rtcp.Packet)
|
||||
ForwardStats *sfu.ForwardStats
|
||||
OnTrackEverSubscribed func(livekit.TrackID)
|
||||
ShouldRegressCodec func() bool
|
||||
PreferVideoSizeFromMedia bool
|
||||
EnableRTPStreamRestartDetection bool
|
||||
UpdateTrackInfoByVideoSizeChange bool
|
||||
ForceBackupCodecPolicySimulcast bool
|
||||
}
|
||||
|
||||
func NewMediaTrack(params MediaTrackParams, ti *livekit.TrackInfo) *MediaTrack {
|
||||
t := &MediaTrack{
|
||||
params: params,
|
||||
params: params,
|
||||
backupCodecPolicy: ti.BackupCodecPolicy,
|
||||
}
|
||||
|
||||
// TODO: disable codec regression until simulcast-codec clients knows that
|
||||
if ti.BackupCodecPolicy == livekit.BackupCodecPolicy_REGRESSION && len(ti.Codecs) > 1 {
|
||||
t.enableRegression = true
|
||||
if t.params.ForceBackupCodecPolicySimulcast {
|
||||
t.backupCodecPolicy = livekit.BackupCodecPolicy_SIMULCAST
|
||||
}
|
||||
|
||||
if t.backupCodecPolicy != livekit.BackupCodecPolicy_SIMULCAST && len(ti.Codecs) > 1 {
|
||||
t.regressionTargetCodec = mime.NormalizeMimeType(ti.Codecs[1].MimeType)
|
||||
t.params.Logger.Debugw("track enabled codec regression", "regressionCodec", t.regressionTargetCodec)
|
||||
}
|
||||
|
||||
t.MediaTrackReceiver = NewMediaTrackReceiver(MediaTrackReceiverParams{
|
||||
MediaTrack: t,
|
||||
IsRelayed: false,
|
||||
ParticipantID: params.ParticipantID,
|
||||
ParticipantIdentity: params.ParticipantIdentity,
|
||||
ParticipantVersion: params.ParticipantVersion,
|
||||
ReceiverConfig: params.ReceiverConfig,
|
||||
SubscriberConfig: params.SubscriberConfig,
|
||||
AudioConfig: params.AudioConfig,
|
||||
Telemetry: params.Telemetry,
|
||||
Logger: params.Logger,
|
||||
RegressionTargetCodec: t.regressionTargetCodec,
|
||||
MediaTrack: t,
|
||||
IsRelayed: false,
|
||||
ParticipantID: params.ParticipantID,
|
||||
ParticipantIdentity: params.ParticipantIdentity,
|
||||
ParticipantVersion: params.ParticipantVersion,
|
||||
ReceiverConfig: params.ReceiverConfig,
|
||||
SubscriberConfig: params.SubscriberConfig,
|
||||
AudioConfig: params.AudioConfig,
|
||||
TelemetryListener: params.TelemetryListener,
|
||||
Logger: params.Logger,
|
||||
RegressionTargetCodec: t.regressionTargetCodec,
|
||||
PreferVideoSizeFromMedia: params.PreferVideoSizeFromMedia,
|
||||
}, ti)
|
||||
|
||||
if ti.Type == livekit.TrackType_AUDIO {
|
||||
@@ -118,27 +143,57 @@ func NewMediaTrack(params MediaTrackParams, ti *livekit.TrackInfo) *MediaTrack {
|
||||
t.MediaTrackReceiver.OnMediaLossFeedback(t.MediaLossProxy.HandleMaxLossFeedback)
|
||||
}
|
||||
|
||||
if ti.Type == livekit.TrackType_VIDEO {
|
||||
t.dynacastManager = dynacast.NewDynacastManager(dynacast.DynacastManagerParams{
|
||||
switch ti.Type {
|
||||
case livekit.TrackType_VIDEO:
|
||||
t.dynacastManager = dynacast.NewDynacastManagerVideo(dynacast.DynacastManagerVideoParams{
|
||||
DynacastPauseDelay: params.VideoConfig.DynacastPauseDelay,
|
||||
Listener: t,
|
||||
Logger: params.Logger,
|
||||
})
|
||||
t.MediaTrackReceiver.OnSetupReceiver(func(mime mime.MimeType) {
|
||||
|
||||
case livekit.TrackType_AUDIO:
|
||||
if len(ti.Codecs) > 1 {
|
||||
t.dynacastManager = dynacast.NewDynacastManagerAudio(dynacast.DynacastManagerAudioParams{
|
||||
Listener: t,
|
||||
Logger: params.Logger,
|
||||
})
|
||||
}
|
||||
}
|
||||
t.MediaTrackReceiver.OnSetupReceiver(func(mime mime.MimeType) {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.AddCodec(mime)
|
||||
})
|
||||
t.MediaTrackReceiver.OnSubscriberMaxQualityChange(
|
||||
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, layer int32) {
|
||||
}
|
||||
})
|
||||
t.MediaTrackReceiver.OnSubscriberMaxQualityChange(
|
||||
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, layer int32) {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.NotifySubscriberMaxQuality(
|
||||
subscriberID,
|
||||
mimeType,
|
||||
buffer.SpatialLayerToVideoQuality(layer, t.MediaTrackReceiver.TrackInfo()),
|
||||
buffer.GetVideoQualityForSpatialLayer(
|
||||
mimeType,
|
||||
layer,
|
||||
t.MediaTrackReceiver.TrackInfo(),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
t.MediaTrackReceiver.OnCodecRegression(func(old, new webrtc.RTPCodecParameters) {
|
||||
t.dynacastManager.HandleCodecRegression(mime.NormalizeMimeType(old.MimeType), mime.NormalizeMimeType(new.MimeType))
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
t.MediaTrackReceiver.OnSubscriberAudioCodecChange(
|
||||
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, enabled bool) {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.NotifySubscription(subscriberID, mimeType, enabled)
|
||||
}
|
||||
},
|
||||
)
|
||||
t.MediaTrackReceiver.OnCodecRegression(func(old, new webrtc.RTPCodecParameters) {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.HandleCodecRegression(
|
||||
mime.NormalizeMimeType(old.MimeType),
|
||||
mime.NormalizeMimeType(new.MimeType),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return t
|
||||
}
|
||||
@@ -151,24 +206,20 @@ func (t *MediaTrack) OnSubscribedMaxQualityChange(
|
||||
maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
) error,
|
||||
) {
|
||||
if t.dynacastManager == nil {
|
||||
return
|
||||
}
|
||||
t.lock.Lock()
|
||||
t.onSubscribedMaxQualityChange = f
|
||||
t.lock.Unlock()
|
||||
}
|
||||
|
||||
handler := func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality) {
|
||||
if f != nil && !t.IsMuted() {
|
||||
_ = f(t.ID(), t.ToProto(), subscribedQualities, maxSubscribedQualities)
|
||||
}
|
||||
|
||||
for _, q := range maxSubscribedQualities {
|
||||
receiver := t.Receiver(q.CodecMime)
|
||||
if receiver != nil {
|
||||
receiver.SetMaxExpectedSpatialLayer(buffer.VideoQualityToSpatialLayer(q.Quality, t.MediaTrackReceiver.TrackInfo()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.dynacastManager.OnSubscribedMaxQualityChange(handler)
|
||||
func (t *MediaTrack) OnSubscribedAudioCodecChange(
|
||||
f func(
|
||||
trackID livekit.TrackID,
|
||||
codecs []*livekit.SubscribedAudioCodec,
|
||||
) error,
|
||||
) {
|
||||
t.lock.Lock()
|
||||
t.onSubscribedAudioCodecChange = f
|
||||
t.lock.Unlock()
|
||||
}
|
||||
|
||||
func (t *MediaTrack) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []types.SubscribedCodecQuality) {
|
||||
@@ -177,47 +228,84 @@ func (t *MediaTrack) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quali
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrack) SignalCid() string {
|
||||
return t.params.SignalCid
|
||||
func (t *MediaTrack) NotifySubscriptionNode(nodeID livekit.NodeID, codecs []*livekit.SubscribedAudioCodec) {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.NotifySubscriptionNode(nodeID, codecs)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrack) HasSdpCid(cid string) bool {
|
||||
if t.params.SdpCid == cid {
|
||||
return true
|
||||
func (t *MediaTrack) ClearSubscriberNodes() {
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.ClearSubscriberNodes()
|
||||
}
|
||||
}
|
||||
|
||||
ti := t.MediaTrackReceiver.TrackInfoClone()
|
||||
for _, c := range ti.Codecs {
|
||||
if c.Cid == cid {
|
||||
return true
|
||||
func (t *MediaTrack) HasSignalCid(cid string) bool {
|
||||
if cid != "" {
|
||||
ti := t.MediaTrackReceiver.TrackInfoClone()
|
||||
for _, c := range ti.Codecs {
|
||||
if c.Cid == cid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *MediaTrack) HasSdpCid(cid string) bool {
|
||||
if cid != "" {
|
||||
ti := t.MediaTrackReceiver.TrackInfoClone()
|
||||
for _, c := range ti.Codecs {
|
||||
if c.Cid == cid || c.SdpCid == cid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *MediaTrack) GetMimeTypeForSdpCid(cid string) mime.MimeType {
|
||||
if cid != "" {
|
||||
ti := t.MediaTrackReceiver.TrackInfoClone()
|
||||
for _, c := range ti.Codecs {
|
||||
if c.Cid == cid || c.SdpCid == cid {
|
||||
return mime.NormalizeMimeType(c.MimeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mime.MimeTypeUnknown
|
||||
}
|
||||
|
||||
func (t *MediaTrack) GetCidsForMimeType(mimeType mime.MimeType) (string, string) {
|
||||
ti := t.MediaTrackReceiver.TrackInfoClone()
|
||||
for _, c := range ti.Codecs {
|
||||
if mime.NormalizeMimeType(c.MimeType) == mimeType {
|
||||
return c.Cid, c.SdpCid
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (t *MediaTrack) ToProto() *livekit.TrackInfo {
|
||||
return t.MediaTrackReceiver.TrackInfoClone()
|
||||
}
|
||||
|
||||
func (t *MediaTrack) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
|
||||
t.MediaTrackReceiver.UpdateCodecCid(codecs)
|
||||
}
|
||||
|
||||
// AddReceiver adds a new RTP receiver to the track, returns true when receiver represents a new codec
|
||||
func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRemote, mid string) bool {
|
||||
// and if a receiver was added successfully
|
||||
func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRemote, mid string) (bool, bool) {
|
||||
var newCodec bool
|
||||
ssrc := uint32(track.SSRC())
|
||||
buff, rtcpReader := t.params.BufferFactory.GetBufferPair(ssrc)
|
||||
if buff == nil || rtcpReader == nil {
|
||||
t.params.Logger.Errorw("could not retrieve buffer pair", nil)
|
||||
return newCodec
|
||||
return newCodec, false
|
||||
}
|
||||
|
||||
var lastRR uint32
|
||||
rtcpReader.OnPacket(func(bytes []byte) {
|
||||
pkts, err := rtcp.Unmarshal(bytes)
|
||||
if err != nil {
|
||||
t.params.Logger.Errorw("could not unmarshal RTCP", err)
|
||||
t.params.Logger.Errorw("could not unmarshal RTCP", err, "size", len(bytes))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -226,7 +314,13 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
case *rtcp.SourceDescription:
|
||||
case *rtcp.SenderReport:
|
||||
if pkt.SSRC == uint32(track.SSRC()) {
|
||||
buff.SetSenderReportData(pkt.RTPTime, pkt.NTPTime, pkt.PacketCount, pkt.OctetCount)
|
||||
buff.SetSenderReportData(&livekit.RTCPSenderReportState{
|
||||
RtpTimestamp: pkt.RTPTime,
|
||||
NtpTimestamp: pkt.NTPTime,
|
||||
Packets: pkt.PacketCount,
|
||||
Octets: uint64(pkt.OctetCount),
|
||||
At: mono.UnixNano(),
|
||||
})
|
||||
}
|
||||
case *rtcp.ExtendedReport:
|
||||
rttFromXR:
|
||||
@@ -255,13 +349,27 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
t.lock.Lock()
|
||||
var regressCodec bool
|
||||
mimeType := mime.NormalizeMimeType(track.Codec().MimeType)
|
||||
layer := buffer.RidToSpatialLayer(track.RID(), ti)
|
||||
layer := buffer.GetSpatialLayerForRid(mimeType, track.RID(), ti)
|
||||
if layer < 0 {
|
||||
t.params.Logger.Warnw(
|
||||
"AddReceiver failed due to negative layer", nil,
|
||||
"rid", track.RID(),
|
||||
"layer", layer,
|
||||
"ssrc", track.SSRC(),
|
||||
"codec", track.Codec(),
|
||||
"trackInfo", logger.Proto(ti),
|
||||
)
|
||||
t.lock.Unlock()
|
||||
return newCodec, false
|
||||
}
|
||||
|
||||
t.params.Logger.Debugw(
|
||||
"AddReceiver",
|
||||
"rid", track.RID(),
|
||||
"layer", layer,
|
||||
"ssrc", track.SSRC(),
|
||||
"codec", track.Codec(),
|
||||
"trackInfo", logger.Proto(ti),
|
||||
)
|
||||
wr := t.MediaTrackReceiver.Receiver(mimeType)
|
||||
if wr == nil {
|
||||
@@ -276,6 +384,11 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
switch len(ti.Codecs) {
|
||||
case 0:
|
||||
// audio track
|
||||
t.params.Logger.Warnw(
|
||||
"unexpected 0 codecs in track info", nil,
|
||||
"mime", mimeType,
|
||||
"track", logger.Proto(ti),
|
||||
)
|
||||
priority = 0
|
||||
case 1:
|
||||
// older clients or non simulcast-codec, mime type only set later
|
||||
@@ -285,9 +398,13 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
}
|
||||
}
|
||||
if priority < 0 {
|
||||
t.params.Logger.Warnw("could not find codec for webrtc receiver", nil, "webrtcCodec", mimeType, "track", logger.Proto(ti))
|
||||
t.params.Logger.Warnw(
|
||||
"could not find codec for webrtc receiver", nil,
|
||||
"mime", mimeType,
|
||||
"track", logger.Proto(ti),
|
||||
)
|
||||
t.lock.Unlock()
|
||||
return false
|
||||
return newCodec, false
|
||||
}
|
||||
|
||||
newWR := sfu.NewWebRTCReceiver(
|
||||
@@ -300,11 +417,11 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
sfu.WithPliThrottleConfig(t.params.PLIThrottleConfig),
|
||||
sfu.WithAudioConfig(t.params.AudioConfig),
|
||||
sfu.WithLoadBalanceThreshold(20),
|
||||
sfu.WithStreamTrackers(),
|
||||
sfu.WithForwardStats(t.params.ForwardStats),
|
||||
sfu.WithEnableRTPStreamRestartDetection(t.params.EnableRTPStreamRestartDetection),
|
||||
)
|
||||
newWR.OnCloseHandler(func() {
|
||||
t.MediaTrackReceiver.SetClosing()
|
||||
t.MediaTrackReceiver.SetClosing(false)
|
||||
t.MediaTrackReceiver.ClearReceiver(mimeType, false)
|
||||
if t.MediaTrackReceiver.TryClose() {
|
||||
if t.dynacastManager != nil {
|
||||
@@ -312,15 +429,61 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
}
|
||||
}
|
||||
})
|
||||
// SIMULCAST-CODEC-TODO: these need to be receiver/mime aware, setting it up only for primary now
|
||||
if priority == 0 {
|
||||
newWR.OnStatsUpdate(func(_ *sfu.WebRTCReceiver, stat *livekit.AnalyticsStat) {
|
||||
key := telemetry.StatsKeyForTrack(livekit.StreamType_UPSTREAM, t.PublisherID(), t.ID(), ti.Source, ti.Type)
|
||||
t.params.Telemetry.TrackStats(key, stat)
|
||||
})
|
||||
|
||||
newWR.OnMaxLayerChange(t.onMaxLayerChange)
|
||||
// SIMULCAST-CODEC-TODO: these need to be receiver/mime aware, setting it up only for primary now
|
||||
statsKey := telemetry.StatsKeyForTrack(
|
||||
t.params.ParticipantCountry,
|
||||
livekit.StreamType_UPSTREAM,
|
||||
t.PublisherID(),
|
||||
t.ID(),
|
||||
ti.Source,
|
||||
ti.Type,
|
||||
)
|
||||
for _, c := range ti.Codecs {
|
||||
for _, l := range c.Layers {
|
||||
t.params.Reporter.ReportLayer(roomobs.PackTrackLayer(l.Height, l.Width))
|
||||
}
|
||||
}
|
||||
newWR.OnStatsUpdate(func(_ *sfu.WebRTCReceiver, stat *livekit.AnalyticsStat) {
|
||||
// send for only one codec, either primary (priority == 0) OR regressed codec
|
||||
t.lock.RLock()
|
||||
regressionTargetCodecReceived := t.regressionTargetCodecReceived
|
||||
t.lock.RUnlock()
|
||||
if priority == 0 || regressionTargetCodecReceived {
|
||||
t.params.TelemetryListener.OnTrackStats(statsKey, stat)
|
||||
|
||||
if cs, ok := telemetry.CondenseStat(stat); ok {
|
||||
t.params.Reporter.Tx(func(tx roomobs.TrackTx) {
|
||||
tx.ParticipantSession().ReportKind(t.params.ParticipantKind.String())
|
||||
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(t.params.ParticipantKind))
|
||||
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(t.params.ParticipantKindDetails))
|
||||
tx.ReportName(ti.Name)
|
||||
tx.ReportKind(roomobs.TrackKindPub)
|
||||
tx.ReportType(roomobs.TrackTypeFromProto(ti.Type))
|
||||
tx.ReportSource(roomobs.TrackSourceFromProto(ti.Source))
|
||||
tx.ReportMime(mime.NormalizeMimeType(ti.MimeType).ReporterType())
|
||||
tx.ReportDuration(uint16(cs.EndTime.Sub(cs.StartTime).Milliseconds()))
|
||||
tx.ReportFrames(uint16(cs.Frames))
|
||||
tx.ReportRecvBytes(uint32(cs.Bytes))
|
||||
tx.ReportRecvPackets(cs.Packets)
|
||||
tx.ReportPacketsLost(cs.PacketsLost)
|
||||
tx.ReportScore(stat.Score)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
newWR.OnMaxLayerChange(func(mimeType mime.MimeType, maxLayer int32) {
|
||||
// send for only one codec, either primary (priority == 0) OR regressed codec
|
||||
t.lock.RLock()
|
||||
regressionTargetCodecReceived := t.regressionTargetCodecReceived
|
||||
t.lock.RUnlock()
|
||||
if priority == 0 || regressionTargetCodecReceived {
|
||||
t.MediaTrackReceiver.NotifyMaxLayerChange(mimeType, maxLayer)
|
||||
}
|
||||
})
|
||||
// SIMULCAST-CODEC-TODO END: these need to be receiver/mime aware, setting it up only for primary now
|
||||
|
||||
if t.PrimaryReceiver() == nil {
|
||||
// primary codec published, set potential codecs
|
||||
potentialCodecs := make([]webrtc.RTPCodecParameters, 0, len(ti.Codecs))
|
||||
@@ -345,8 +508,8 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
t.MediaTrackReceiver.SetupReceiver(newWR, priority, mid)
|
||||
|
||||
for ssrc, info := range t.params.SimTracks {
|
||||
if info.Mid == mid {
|
||||
t.MediaTrackReceiver.SetLayerSsrc(mimeType, info.Rid, ssrc)
|
||||
if info.Mid == mid && !info.IsRepairStream {
|
||||
t.MediaTrackReceiver.SetLayerSsrcsForRid(mimeType, info.StreamID, ssrc, info.RepairSSRC)
|
||||
}
|
||||
}
|
||||
wr = newWR
|
||||
@@ -355,9 +518,18 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
newWR.AddOnCodecStateChange(func(codec webrtc.RTPCodecParameters, state sfu.ReceiverCodecState) {
|
||||
t.MediaTrackReceiver.HandleReceiverCodecChange(newWR, codec, state)
|
||||
})
|
||||
|
||||
// update subscriber video layers when video size changes
|
||||
newWR.OnVideoSizeChanged(func() {
|
||||
if t.params.UpdateTrackInfoByVideoSizeChange {
|
||||
t.MediaTrackReceiver.UpdateVideoSize(mimeType, newWR.VideoSizes())
|
||||
}
|
||||
|
||||
t.MediaTrackSubscriptions.UpdateVideoLayers()
|
||||
})
|
||||
}
|
||||
|
||||
if newCodec && t.enableRegression {
|
||||
if newCodec && t.enableRegression() {
|
||||
if mimeType == t.regressionTargetCodec {
|
||||
t.params.Logger.Infow("regression target codec received", "codec", mimeType)
|
||||
t.regressionTargetCodecReceived = true
|
||||
@@ -377,21 +549,28 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
"newCodec", newCodec,
|
||||
)
|
||||
buff.Close()
|
||||
return false
|
||||
return newCodec, false
|
||||
}
|
||||
|
||||
// LK-TODO: can remove this completely when VideoLayers protocol becomes the default as it has info from client or if we decide to use TrackInfo.Simulcast
|
||||
if t.numUpTracks.Inc() > 1 || track.RID() != "" {
|
||||
// cannot only rely on numUpTracks since we fire metadata events immediately after the first layer
|
||||
t.SetSimulcast(true)
|
||||
var expectedBitrate int
|
||||
layers := buffer.GetVideoLayersForMimeType(mimeType, ti)
|
||||
if layer >= 0 && len(layers) > int(layer) {
|
||||
expectedBitrate = int(layers[layer].GetBitrate())
|
||||
}
|
||||
if err := buff.Bind(receiver.GetParameters(), track.Codec().RTPCodecCapability, expectedBitrate); err != nil {
|
||||
t.params.Logger.Warnw(
|
||||
"binding buffer failed", err,
|
||||
"rid", track.RID(),
|
||||
"layer", layer,
|
||||
"ssrc", track.SSRC(),
|
||||
"newCodec", newCodec,
|
||||
)
|
||||
buff.Close()
|
||||
return newCodec, false
|
||||
}
|
||||
|
||||
var bitrates int
|
||||
if len(ti.Layers) > int(layer) {
|
||||
bitrates = int(ti.Layers[layer].GetBitrate())
|
||||
}
|
||||
|
||||
t.MediaTrackReceiver.SetLayerSsrc(mimeType, track.RID(), uint32(track.SSRC()))
|
||||
t.MediaTrackReceiver.MaybeSetSimulcast()
|
||||
t.MediaTrackReceiver.SetLayerSsrcsForRid(mimeType, track.RID(), uint32(track.SSRC()), 0)
|
||||
|
||||
if regressCodec {
|
||||
for _, c := range ti.Codecs {
|
||||
@@ -408,7 +587,7 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
}
|
||||
}
|
||||
|
||||
buff.Bind(receiver.GetParameters(), track.Codec().RTPCodecCapability, bitrates)
|
||||
buff.OnNotifyRTX(t.MediaTrackReceiver.setLayerRtxInfo)
|
||||
|
||||
// if subscriber request fps before fps calculated, update them after fps updated.
|
||||
buff.OnFpsChanged(func() {
|
||||
@@ -416,20 +595,19 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
|
||||
})
|
||||
|
||||
buff.OnFinalRtpStats(func(stats *livekit.RTPStats) {
|
||||
t.params.Telemetry.TrackPublishRTPStats(
|
||||
context.Background(),
|
||||
t.params.ParticipantID,
|
||||
t.params.TelemetryListener.OnTrackPublishRTPStats(
|
||||
t.params.ParticipantID(),
|
||||
t.ID(),
|
||||
mimeType,
|
||||
int(layer),
|
||||
stats,
|
||||
)
|
||||
})
|
||||
return newCodec
|
||||
return newCodec, true
|
||||
}
|
||||
|
||||
func (t *MediaTrack) GetConnectionScoreAndQuality() (float32, livekit.ConnectionQuality) {
|
||||
receiver := t.PrimaryReceiver()
|
||||
receiver := t.ActiveReceiver()
|
||||
if rtcReceiver, ok := receiver.(*sfu.WebRTCReceiver); ok {
|
||||
return rtcReceiver.GetConnectionScoreAndQuality()
|
||||
}
|
||||
@@ -447,10 +625,6 @@ func (t *MediaTrack) HasPendingCodec() bool {
|
||||
return t.MediaTrackReceiver.PrimaryReceiver() == nil
|
||||
}
|
||||
|
||||
func (t *MediaTrack) onMaxLayerChange(maxLayer int32) {
|
||||
t.MediaTrackReceiver.NotifyMaxLayerChange(maxLayer)
|
||||
}
|
||||
|
||||
func (t *MediaTrack) Restart() {
|
||||
t.MediaTrackReceiver.Restart()
|
||||
|
||||
@@ -460,11 +634,10 @@ func (t *MediaTrack) Restart() {
|
||||
}
|
||||
|
||||
func (t *MediaTrack) Close(isExpectedToResume bool) {
|
||||
t.MediaTrackReceiver.SetClosing()
|
||||
t.MediaTrackReceiver.SetClosing(isExpectedToResume)
|
||||
if t.dynacastManager != nil {
|
||||
t.dynacastManager.Close()
|
||||
}
|
||||
t.MediaTrackReceiver.ClearAllReceivers(isExpectedToResume)
|
||||
t.MediaTrackReceiver.Close(isExpectedToResume)
|
||||
}
|
||||
|
||||
@@ -486,3 +659,56 @@ func (t *MediaTrack) OnTrackSubscribed() {
|
||||
go t.params.OnTrackEverSubscribed(t.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrack) enableRegression() bool {
|
||||
return t.backupCodecPolicy == livekit.BackupCodecPolicy_REGRESSION ||
|
||||
(t.backupCodecPolicy == livekit.BackupCodecPolicy_PREFER_REGRESSION && t.params.ShouldRegressCodec())
|
||||
}
|
||||
|
||||
func (t *MediaTrack) Logger() logger.Logger {
|
||||
return t.params.Logger
|
||||
}
|
||||
|
||||
// dynacast.DynacastManagerListtener implementation
|
||||
var _ dynacast.DynacastManagerListener = (*MediaTrack)(nil)
|
||||
|
||||
func (t *MediaTrack) OnDynacastSubscribedMaxQualityChange(
|
||||
subscribedQualities []*livekit.SubscribedCodec,
|
||||
maxSubscribedQualities []types.SubscribedCodecQuality,
|
||||
) {
|
||||
t.lock.RLock()
|
||||
onSubscribedMaxQualityChange := t.onSubscribedMaxQualityChange
|
||||
t.lock.RUnlock()
|
||||
|
||||
if onSubscribedMaxQualityChange != nil && !t.IsMuted() {
|
||||
_ = onSubscribedMaxQualityChange(
|
||||
t.ID(),
|
||||
t.ToProto(),
|
||||
subscribedQualities,
|
||||
maxSubscribedQualities,
|
||||
)
|
||||
}
|
||||
|
||||
for _, q := range maxSubscribedQualities {
|
||||
receiver := t.Receiver(q.CodecMime)
|
||||
if receiver != nil {
|
||||
receiver.SetMaxExpectedSpatialLayer(
|
||||
buffer.GetSpatialLayerForVideoQuality(
|
||||
q.CodecMime,
|
||||
q.Quality,
|
||||
t.MediaTrackReceiver.TrackInfo(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrack) OnDynacastSubscribedAudioCodecChange(codecs []*livekit.SubscribedAudioCodec) {
|
||||
t.lock.RLock()
|
||||
onSubscribedAudioCodecChange := t.onSubscribedAudioCodecChange
|
||||
t.lock.RUnlock()
|
||||
|
||||
if onSubscribedAudioCodecChange != nil {
|
||||
_ = onSubscribedAudioCodecChange(t.ID(), codecs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
func TestTrackInfo(t *testing.T) {
|
||||
@@ -47,128 +49,149 @@ func TestTrackInfo(t *testing.T) {
|
||||
require.Equal(t, ti.Width, outInfo.Width)
|
||||
require.Equal(t, ti.Height, outInfo.Height)
|
||||
require.Equal(t, ti.Simulcast, outInfo.Simulcast)
|
||||
|
||||
// make it simulcasted
|
||||
mt.SetSimulcast(true)
|
||||
require.True(t, mt.ToProto().Simulcast)
|
||||
}
|
||||
|
||||
func TestGetQualityForDimension(t *testing.T) {
|
||||
t.Run("landscape source", func(t *testing.T) {
|
||||
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
|
||||
mt := NewMediaTrack(MediaTrackParams{
|
||||
Logger: logger.GetLogger(),
|
||||
}, &livekit.TrackInfo{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
})
|
||||
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 200))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(200, 250))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(700, 480))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(500, 1000))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 300, 200))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 200, 250))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 700, 480))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 500, 1000))
|
||||
})
|
||||
|
||||
t.Run("portrait source", func(t *testing.T) {
|
||||
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
|
||||
mt := NewMediaTrack(MediaTrackParams{
|
||||
Logger: logger.GetLogger(),
|
||||
}, &livekit.TrackInfo{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Width: 540,
|
||||
Height: 960,
|
||||
})
|
||||
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(200, 400))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(400, 400))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(400, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(600, 900))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 200, 400))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 400, 400))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 400, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 600, 900))
|
||||
})
|
||||
|
||||
t.Run("layers provided", func(t *testing.T) {
|
||||
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
|
||||
mt := NewMediaTrack(MediaTrackParams{
|
||||
Logger: logger.GetLogger(),
|
||||
}, &livekit.TrackInfo{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
Layers: []*livekit.VideoLayer{
|
||||
Codecs: []*livekit.SimulcastCodecInfo{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 960,
|
||||
Height: 540,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
Layers: []*livekit.VideoLayer{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 960,
|
||||
Height: 540,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
|
||||
})
|
||||
|
||||
t.Run("highest layer with smallest dimensions", func(t *testing.T) {
|
||||
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
|
||||
mt := NewMediaTrack(MediaTrackParams{
|
||||
Logger: logger.GetLogger(),
|
||||
}, &livekit.TrackInfo{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
Layers: []*livekit.VideoLayer{
|
||||
Codecs: []*livekit.SimulcastCodecInfo{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
Layers: []*livekit.VideoLayer{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1200, 800))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1200, 800))
|
||||
|
||||
mt = NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
|
||||
mt = NewMediaTrack(MediaTrackParams{
|
||||
Logger: logger.GetLogger(),
|
||||
}, &livekit.TrackInfo{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
Layers: []*livekit.VideoLayer{
|
||||
Codecs: []*livekit.SimulcastCodecInfo{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
MimeType: mime.MimeTypeH264.String(),
|
||||
Layers: []*livekit.VideoLayer{
|
||||
{
|
||||
Quality: livekit.VideoQuality_LOW,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_MEDIUM,
|
||||
Width: 480,
|
||||
Height: 270,
|
||||
},
|
||||
{
|
||||
Quality: livekit.VideoQuality_HIGH,
|
||||
Width: 1080,
|
||||
Height: 720,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1200, 800))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
|
||||
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1200, 800))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -15,19 +15,19 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"go.uber.org/atomic"
|
||||
"golang.org/x/exp/slices"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/mediatransportutil/pkg/codec"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
@@ -35,9 +35,8 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/rtpstats"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
sutils "github.com/livekit/livekit-server/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -118,17 +117,18 @@ func (r *simulcastReceiver) IsRegressed() bool {
|
||||
// -----------------------------------------------------
|
||||
|
||||
type MediaTrackReceiverParams struct {
|
||||
MediaTrack types.MediaTrack
|
||||
IsRelayed bool
|
||||
ParticipantID livekit.ParticipantID
|
||||
ParticipantIdentity livekit.ParticipantIdentity
|
||||
ParticipantVersion uint32
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
AudioConfig sfu.AudioConfig
|
||||
Telemetry telemetry.TelemetryService
|
||||
Logger logger.Logger
|
||||
RegressionTargetCodec mime.MimeType
|
||||
MediaTrack types.MediaTrack
|
||||
IsRelayed bool
|
||||
ParticipantID func() livekit.ParticipantID
|
||||
ParticipantIdentity livekit.ParticipantIdentity
|
||||
ParticipantVersion uint32
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
AudioConfig sfu.AudioConfig
|
||||
TelemetryListener types.ParticipantTelemetryListener
|
||||
Logger logger.Logger
|
||||
RegressionTargetCodec mime.MimeType
|
||||
PreferVideoSizeFromMedia bool
|
||||
}
|
||||
|
||||
type MediaTrackReceiver struct {
|
||||
@@ -161,21 +161,15 @@ func NewMediaTrackReceiver(params MediaTrackReceiverParams, ti *livekit.TrackInf
|
||||
IsRelayed: params.IsRelayed,
|
||||
ReceiverConfig: params.ReceiverConfig,
|
||||
SubscriberConfig: params.SubscriberConfig,
|
||||
Telemetry: params.Telemetry,
|
||||
Logger: params.Logger,
|
||||
})
|
||||
t.MediaTrackSubscriptions.OnDownTrackCreated(t.onDownTrackCreated)
|
||||
|
||||
if ti.Muted {
|
||||
t.SetMuted(true)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) Restart() {
|
||||
hq := buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_HIGH, t.TrackInfo())
|
||||
|
||||
for _, receiver := range t.loadReceivers() {
|
||||
hq := buffer.GetSpatialLayerForVideoQuality(receiver.Mime(), livekit.VideoQuality_HIGH, t.TrackInfo())
|
||||
receiver.SetMaxExpectedSpatialLayer(hq)
|
||||
}
|
||||
}
|
||||
@@ -213,8 +207,8 @@ func (t *MediaTrackReceiver) SetupReceiver(receiver sfu.TrackReceiver, priority
|
||||
receivers = append(receivers, &simulcastReceiver{TrackReceiver: receiver, priority: priority})
|
||||
}
|
||||
|
||||
sort.Slice(receivers, func(i, j int) bool {
|
||||
return receivers[i].Priority() < receivers[j].Priority()
|
||||
slices.SortFunc(receivers, func(a, b *simulcastReceiver) int {
|
||||
return sutils.Signum(a.Priority() - b.Priority())
|
||||
})
|
||||
|
||||
if mid != "" {
|
||||
@@ -308,12 +302,16 @@ func (t *MediaTrackReceiver) HandleReceiverCodecChange(r sfu.TrackReceiver, code
|
||||
return
|
||||
}
|
||||
|
||||
t.params.Logger.Infow("regressing codec", "from", codec.MimeType, "to", backupCodecReceiver.Codec().MimeType)
|
||||
t.params.Logger.Infow(
|
||||
"regressing codec",
|
||||
"from", codec.MimeType,
|
||||
"to", backupCodecReceiver.Codec().MimeType,
|
||||
)
|
||||
|
||||
// remove old codec from potential codecs
|
||||
for i, c := range t.potentialCodecs {
|
||||
if strings.EqualFold(c.MimeType, codec.MimeType) {
|
||||
slices.Delete(t.potentialCodecs, i, i+1)
|
||||
t.potentialCodecs = slices.Delete(t.potentialCodecs, i, i+1)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -351,8 +349,8 @@ func (t *MediaTrackReceiver) SetPotentialCodecs(codecs []webrtc.RTPCodecParamete
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.Slice(receivers, func(i, j int) bool {
|
||||
return receivers[i].Priority() < receivers[j].Priority()
|
||||
slices.SortFunc(receivers, func(a, b *simulcastReceiver) int {
|
||||
return sutils.Signum(a.Priority() - b.Priority())
|
||||
})
|
||||
t.receivers = receivers
|
||||
t.lock.Unlock()
|
||||
@@ -376,7 +374,7 @@ func (t *MediaTrackReceiver) ClearReceiver(mime mime.MimeType, isExpectedToResum
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) ClearAllReceivers(isExpectedToResume bool) {
|
||||
t.params.Logger.Debugw("clearing all receivers")
|
||||
t.params.Logger.Debugw("clearing all receivers", "isExpectedToResume", isExpectedToResume)
|
||||
t.lock.Lock()
|
||||
receivers := t.receivers
|
||||
t.receivers = nil
|
||||
@@ -408,12 +406,15 @@ func (t *MediaTrackReceiver) IsOpen() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) SetClosing() {
|
||||
func (t *MediaTrackReceiver) SetClosing(isExpectedToResume bool) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.state == mediaTrackReceiverStateOpen {
|
||||
t.state = mediaTrackReceiverStateClosing
|
||||
}
|
||||
|
||||
t.isExpectedToResume = isExpectedToResume
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) TryClose() bool {
|
||||
@@ -444,6 +445,8 @@ func (t *MediaTrackReceiver) TryClose() bool {
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) Close(isExpectedToResume bool) {
|
||||
t.ClearAllReceivers(isExpectedToResume)
|
||||
|
||||
t.lock.Lock()
|
||||
if t.state == mediaTrackReceiverStateClosed {
|
||||
t.lock.Unlock()
|
||||
@@ -476,7 +479,7 @@ func (t *MediaTrackReceiver) Stream() string {
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) PublisherID() livekit.ParticipantID {
|
||||
return t.params.ParticipantID
|
||||
return t.params.ParticipantID()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) PublisherIdentity() livekit.ParticipantIdentity {
|
||||
@@ -487,19 +490,6 @@ func (t *MediaTrackReceiver) PublisherVersion() uint32 {
|
||||
return t.params.ParticipantVersion
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) IsSimulcast() bool {
|
||||
return t.TrackInfo().Simulcast
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) SetSimulcast(simulcast bool) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
trackInfo := t.TrackInfoClone()
|
||||
trackInfo.Simulcast = simulcast
|
||||
t.trackInfo.Store(trackInfo)
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) Name() string {
|
||||
return t.TrackInfo().Name
|
||||
}
|
||||
@@ -513,21 +503,19 @@ func (t *MediaTrackReceiver) SetMuted(muted bool) {
|
||||
trackInfo := t.TrackInfoClone()
|
||||
trackInfo.Muted = muted
|
||||
t.trackInfo.Store(trackInfo)
|
||||
|
||||
receivers := t.receivers
|
||||
t.lock.Unlock()
|
||||
|
||||
for _, receiver := range receivers {
|
||||
receiver.SetUpTrackPaused(muted)
|
||||
}
|
||||
|
||||
t.MediaTrackSubscriptions.SetMuted(muted)
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) IsEncrypted() bool {
|
||||
return t.TrackInfo().Encryption != livekit.Encryption_NONE
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) HasPacketTrailer() bool {
|
||||
return len(t.TrackInfo().GetPacketTrailerFeatures()) > 0
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) AddOnClose(f func(isExpectedToResume bool)) {
|
||||
if f == nil {
|
||||
return
|
||||
@@ -588,8 +576,10 @@ func (t *MediaTrackReceiver) AddSubscriber(sub types.LocalParticipant) (types.Su
|
||||
StreamId: streamId,
|
||||
UpstreamCodecs: potentialCodecs,
|
||||
Logger: tLogger,
|
||||
DisableRed: t.TrackInfo().GetDisableRed() || !t.params.AudioConfig.ActiveREDEncoding,
|
||||
DisableRed: !IsRedEnabled(t.TrackInfo()) || !t.params.AudioConfig.ActiveREDEncoding,
|
||||
IsEncrypted: t.IsEncrypted(),
|
||||
})
|
||||
subID := sub.ID()
|
||||
subTrack, err := t.MediaTrackSubscriptions.AddSubscriber(sub, wr)
|
||||
|
||||
// media track could have been closed while adding subscription
|
||||
@@ -603,7 +593,12 @@ func (t *MediaTrackReceiver) AddSubscriber(sub types.LocalParticipant) (types.Su
|
||||
t.lock.RUnlock()
|
||||
|
||||
if remove {
|
||||
_ = t.MediaTrackSubscriptions.RemoveSubscriber(sub.ID(), isExpectedToResume)
|
||||
t.params.Logger.Debugw(
|
||||
"removing subscriber on a not-open track",
|
||||
"subscriberID", subID,
|
||||
"isExpectedToResume", isExpectedToResume,
|
||||
)
|
||||
_ = t.MediaTrackSubscriptions.RemoveSubscriber(subID, isExpectedToResume)
|
||||
return nil, ErrNotOpen
|
||||
}
|
||||
|
||||
@@ -617,7 +612,7 @@ func (t *MediaTrackReceiver) RemoveSubscriber(subscriberID livekit.ParticipantID
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) removeAllSubscribersForMime(mime mime.MimeType, isExpectedToResume bool) {
|
||||
t.params.Logger.Debugw("removing all subscribers for mime", "mime", mime)
|
||||
t.params.Logger.Debugw("removing all subscribers for mime", "mime", mime, "isExpectedToResume", isExpectedToResume)
|
||||
for _, subscriberID := range t.MediaTrackSubscriptions.GetAllSubscribersForMime(mime) {
|
||||
t.RemoveSubscriber(subscriberID, isExpectedToResume)
|
||||
}
|
||||
@@ -632,14 +627,7 @@ func (t *MediaTrackReceiver) RevokeDisallowedSubscribers(allowedSubscriberIdenti
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, allowedIdentity := range allowedSubscriberIdentities {
|
||||
if subTrack.SubscriberIdentity() == allowedIdentity {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
found := slices.Contains(allowedSubscriberIdentities, subTrack.SubscriberIdentity())
|
||||
if !found {
|
||||
t.params.Logger.Infow("revoking subscription",
|
||||
"subscriber", subTrack.SubscriberIdentity(),
|
||||
@@ -658,17 +646,42 @@ func (t *MediaTrackReceiver) updateTrackInfoOfReceivers() {
|
||||
for _, r := range t.loadReceivers() {
|
||||
r.UpdateTrackInfo(ti)
|
||||
}
|
||||
|
||||
t.MediaTrackSubscriptions.SetMuted(ti.GetMuted())
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ssrc uint32) {
|
||||
func (t *MediaTrackReceiver) MaybeSetSimulcast() {
|
||||
// only primary receiver (i.e. receiver at index 0) for legacy use case
|
||||
primaryReceiver := t.PrimaryReceiver()
|
||||
if primaryReceiver == nil {
|
||||
return
|
||||
}
|
||||
if wr, ok := primaryReceiver.(*sfu.WebRTCReceiver); !ok || wr.NumUpTracks() < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
layer := buffer.RidToSpatialLayer(rid, trackInfo)
|
||||
if trackInfo.Simulcast {
|
||||
t.lock.Unlock()
|
||||
return
|
||||
}
|
||||
trackInfo.Simulcast = true
|
||||
t.trackInfo.Store(trackInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) SetLayerSsrcsForRid(mimeType mime.MimeType, rid string, ssrc uint32, repairSSRC uint32) {
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
layer := buffer.GetSpatialLayerForRid(mimeType, rid, trackInfo)
|
||||
if layer == buffer.InvalidLayerSpatial {
|
||||
// non-simulcast case will not have `rid`
|
||||
layer = 0
|
||||
}
|
||||
quality := buffer.SpatialLayerToVideoQuality(layer, trackInfo)
|
||||
quality := buffer.GetVideoQualityForSpatialLayer(mimeType, layer, trackInfo)
|
||||
// set video layer ssrc info
|
||||
for i, ci := range trackInfo.Codecs {
|
||||
if mime.NormalizeMimeType(ci.MimeType) != mimeType {
|
||||
@@ -689,6 +702,20 @@ func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ss
|
||||
}
|
||||
if !ssrcFound && matchingLayer != nil {
|
||||
matchingLayer.Ssrc = ssrc
|
||||
if repairSSRC != 0 {
|
||||
matchingLayer.RepairSsrc = repairSSRC
|
||||
}
|
||||
}
|
||||
if ssrcFound && (matchingLayer.Ssrc != ssrc || matchingLayer.RepairSsrc != repairSSRC) {
|
||||
t.params.Logger.Warnw(
|
||||
"not overriding ssrc", nil,
|
||||
"rid", rid,
|
||||
"ssrc", ssrc,
|
||||
"existingSSRC", matchingLayer.Ssrc,
|
||||
"repairSSRC", repairSSRC,
|
||||
"existingRepairSSRC", matchingLayer.RepairSsrc,
|
||||
"trackInfo", logger.Proto(trackInfo),
|
||||
)
|
||||
}
|
||||
|
||||
// for client don't use simulcast codecs (old client version or single codec)
|
||||
@@ -703,13 +730,99 @@ func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ss
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
|
||||
func (t *MediaTrackReceiver) setLayerRtxInfo(ssrc uint32, repairSSRC uint32, rsid string) {
|
||||
t.params.Logger.Debugw("rtx notification", "ssrc", ssrc, "repairSSRC", repairSSRC, "rsid", rsid)
|
||||
if ssrc == 0 || repairSSRC == 0 || rsid == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
|
||||
done:
|
||||
for _, ci := range trackInfo.Codecs {
|
||||
for _, l := range ci.Layers {
|
||||
if l.Ssrc == ssrc {
|
||||
if (l.RepairSsrc != 0 && l.RepairSsrc != repairSSRC) || (l.Rid != "" && l.Rid != rsid) {
|
||||
t.params.Logger.Warnw(
|
||||
"not overriding rtx info", nil,
|
||||
"ssrc", ssrc,
|
||||
"repairSSRC", repairSSRC,
|
||||
"existingRepairSSRC", l.RepairSsrc,
|
||||
"rsid", rsid,
|
||||
"existingRid", l.Rid,
|
||||
"trackInfo", logger.Proto(trackInfo),
|
||||
)
|
||||
} else {
|
||||
l.RepairSsrc = repairSSRC
|
||||
t.params.Logger.Debugw(
|
||||
"set rtx info",
|
||||
"ssrc", ssrc,
|
||||
"repairSSRC", repairSSRC,
|
||||
"rsid", rsid,
|
||||
"trackInfo", logger.Proto(trackInfo),
|
||||
)
|
||||
}
|
||||
break done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backwards compatibility
|
||||
for _, l := range trackInfo.Layers {
|
||||
if l.Ssrc == ssrc {
|
||||
if (l.RepairSsrc != 0 && l.RepairSsrc != repairSSRC) || (l.Rid != "" && l.Rid != rsid) {
|
||||
t.params.Logger.Warnw(
|
||||
"not overriding rtx info", nil,
|
||||
"ssrc", ssrc,
|
||||
"repairSSRC", repairSSRC,
|
||||
"existingRepairSSRC", l.RepairSsrc,
|
||||
"rsid", rsid,
|
||||
"existingRid", l.Rid,
|
||||
"trackInfo", logger.Proto(trackInfo),
|
||||
)
|
||||
} else {
|
||||
l.RepairSsrc = repairSSRC
|
||||
t.params.Logger.Debugw(
|
||||
"set rtx info",
|
||||
"ssrc", ssrc,
|
||||
"repairSSRC", repairSSRC,
|
||||
"rsid", rsid,
|
||||
"trackInfo", logger.Proto(trackInfo),
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
t.trackInfo.Store(trackInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
// change not propagated as it is internal
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateCodecInfo(codecs []*livekit.SimulcastCodec) {
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
for _, c := range codecs {
|
||||
for _, origin := range trackInfo.Codecs {
|
||||
if mime.GetMimeTypeCodec(origin.MimeType) == mime.NormalizeMimeTypeCodec(c.Codec) {
|
||||
origin.Cid = c.Cid
|
||||
|
||||
if len(c.Layers) != 0 {
|
||||
clonedLayers := make([]*livekit.VideoLayer, 0, len(c.Layers))
|
||||
for _, l := range c.Layers {
|
||||
clonedLayers = append(clonedLayers, utils.CloneProto(l))
|
||||
}
|
||||
origin.Layers = clonedLayers
|
||||
|
||||
mimeType := mime.NormalizeMimeType(origin.MimeType)
|
||||
for _, layer := range origin.Layers {
|
||||
layer.SpatialLayer = buffer.VideoQualityToSpatialLayer(mimeType, layer.Quality, trackInfo)
|
||||
layer.Rid = buffer.VideoQualityToRid(mimeType, layer.Quality, trackInfo, buffer.DefaultVideoLayersRid)
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -720,13 +833,49 @@ func (t *MediaTrackReceiver) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateCodecSdpCid(mimeType mime.MimeType, sdpCid string) {
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
for _, origin := range trackInfo.Codecs {
|
||||
if mime.NormalizeMimeType(origin.MimeType) == mimeType {
|
||||
if sdpCid != origin.Cid {
|
||||
origin.SdpCid = sdpCid
|
||||
}
|
||||
}
|
||||
}
|
||||
t.trackInfo.Store(trackInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateCodecRids(mimeType mime.MimeType, rids buffer.VideoLayersRid) {
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfoClone()
|
||||
for _, origin := range trackInfo.Codecs {
|
||||
originMimeType := mime.NormalizeMimeType(origin.MimeType)
|
||||
if originMimeType != mimeType {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, layer := range origin.Layers {
|
||||
layer.SpatialLayer = buffer.VideoQualityToSpatialLayer(mimeType, layer.Quality, trackInfo)
|
||||
layer.Rid = buffer.VideoQualityToRid(mimeType, layer.Quality, trackInfo, rids)
|
||||
}
|
||||
break
|
||||
}
|
||||
t.trackInfo.Store(trackInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
|
||||
updateMute := false
|
||||
clonedInfo := utils.CloneProto(ti)
|
||||
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfo()
|
||||
// patch Mid and SSRC of codecs/layers by keeping original if available
|
||||
// patch Mid/Rid and Ssrc/RtxSsrc of codecs/layers by keeping original if available
|
||||
for i, ci := range clonedInfo.Codecs {
|
||||
for _, originCi := range trackInfo.Codecs {
|
||||
if !mime.IsMimeTypeStringEqual(ci.MimeType, originCi.MimeType) {
|
||||
@@ -743,6 +892,13 @@ func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
|
||||
if originLayer.Ssrc != 0 {
|
||||
layer.Ssrc = originLayer.Ssrc
|
||||
}
|
||||
if originLayer.Rid != "" {
|
||||
layer.Rid = originLayer.Rid
|
||||
}
|
||||
|
||||
if originLayer.RepairSsrc != 0 {
|
||||
layer.RepairSsrc = originLayer.RepairSsrc
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -755,16 +911,9 @@ func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
|
||||
clonedInfo.Layers = ci.Layers
|
||||
}
|
||||
}
|
||||
if trackInfo.Muted != clonedInfo.Muted {
|
||||
updateMute = true
|
||||
}
|
||||
t.trackInfo.Store(clonedInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
if updateMute {
|
||||
t.SetMuted(clonedInfo.Muted)
|
||||
}
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
}
|
||||
|
||||
@@ -776,7 +925,9 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfo()
|
||||
clonedInfo := utils.CloneProto(trackInfo)
|
||||
clonedInfo.AudioFeatures = update.Features
|
||||
|
||||
clonedInfo.AudioFeatures = sutils.DedupeSlice(update.Features)
|
||||
|
||||
clonedInfo.Stereo = false
|
||||
clonedInfo.DisableDtx = false
|
||||
for _, feature := range update.Features {
|
||||
@@ -787,6 +938,7 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
|
||||
clonedInfo.DisableDtx = true
|
||||
}
|
||||
}
|
||||
|
||||
if proto.Equal(trackInfo, clonedInfo) {
|
||||
t.lock.Unlock()
|
||||
return
|
||||
@@ -797,7 +949,7 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
|
||||
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), clonedInfo)
|
||||
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
|
||||
t.params.Logger.Debugw("updated audio track", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
|
||||
}
|
||||
|
||||
@@ -821,10 +973,56 @@ func (t *MediaTrackReceiver) UpdateVideoTrack(update *livekit.UpdateLocalVideoTr
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
|
||||
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), clonedInfo)
|
||||
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
|
||||
t.params.Logger.Debugw("updated video track", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) UpdateVideoSize(mimeType mime.MimeType, sizes []codec.VideoSize) {
|
||||
var changed bool
|
||||
t.lock.Lock()
|
||||
trackInfo := t.TrackInfo()
|
||||
clonedInfo := utils.CloneProto(trackInfo)
|
||||
var maxWidth, maxHeight uint32
|
||||
for _, size := range sizes {
|
||||
if size.Width > maxWidth {
|
||||
maxWidth = size.Width
|
||||
maxHeight = size.Height
|
||||
}
|
||||
}
|
||||
|
||||
if clonedInfo.Width != maxWidth || clonedInfo.Height != maxHeight {
|
||||
clonedInfo.Width = maxWidth
|
||||
clonedInfo.Height = maxHeight
|
||||
changed = true
|
||||
}
|
||||
|
||||
for _, c := range clonedInfo.Codecs {
|
||||
if mime.NormalizeMimeType(c.MimeType) == mimeType {
|
||||
for i, l := range c.Layers {
|
||||
if i < len(sizes) && (sizes[i].Width != 0 || sizes[i].Height != 0) &&
|
||||
(l.Width != sizes[i].Width || l.Height != sizes[i].Height) {
|
||||
l.Width = sizes[i].Width
|
||||
l.Height = sizes[i].Height
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !changed {
|
||||
t.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
t.trackInfo.Store(clonedInfo)
|
||||
t.lock.Unlock()
|
||||
|
||||
t.updateTrackInfoOfReceivers()
|
||||
|
||||
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
|
||||
t.params.Logger.Debugw("updated video sizes", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) TrackInfo() *livekit.TrackInfo {
|
||||
return t.trackInfo.Load()
|
||||
}
|
||||
@@ -833,16 +1031,17 @@ func (t *MediaTrackReceiver) TrackInfoClone() *livekit.TrackInfo {
|
||||
return utils.CloneProto(t.TrackInfo())
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) NotifyMaxLayerChange(maxLayer int32) {
|
||||
func (t *MediaTrackReceiver) NotifyMaxLayerChange(mimeType mime.MimeType, maxLayer int32) {
|
||||
trackInfo := t.TrackInfo()
|
||||
quality := buffer.SpatialLayerToVideoQuality(maxLayer, trackInfo)
|
||||
quality := buffer.GetVideoQualityForSpatialLayer(mimeType, maxLayer, trackInfo)
|
||||
ti := &livekit.TrackInfo{
|
||||
Sid: trackInfo.Sid,
|
||||
Type: trackInfo.Type,
|
||||
Layers: []*livekit.VideoLayer{{Quality: quality}},
|
||||
}
|
||||
if quality != livekit.VideoQuality_OFF {
|
||||
for _, layer := range trackInfo.Layers {
|
||||
layers := buffer.GetVideoLayersForMimeType(mimeType, trackInfo)
|
||||
for _, layer := range layers {
|
||||
if layer.Quality == quality {
|
||||
ti.Layers[0].Width = layer.Width
|
||||
ti.Layers[0].Height = layer.Height
|
||||
@@ -851,12 +1050,12 @@ func (t *MediaTrackReceiver) NotifyMaxLayerChange(maxLayer int32) {
|
||||
}
|
||||
}
|
||||
|
||||
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), ti)
|
||||
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), ti)
|
||||
}
|
||||
|
||||
// GetQualityForDimension finds the closest quality to use for desired dimensions
|
||||
// affords a 20% tolerance on dimension
|
||||
func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) livekit.VideoQuality {
|
||||
func (t *MediaTrackReceiver) GetQualityForDimension(mimeType mime.MimeType, width, height uint32) livekit.VideoQuality {
|
||||
quality := livekit.VideoQuality_HIGH
|
||||
if t.Kind() == livekit.TrackType_AUDIO {
|
||||
return quality
|
||||
@@ -864,7 +1063,12 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
|
||||
|
||||
trackInfo := t.TrackInfo()
|
||||
|
||||
if trackInfo.Height == 0 {
|
||||
var mediaSizes []codec.VideoSize
|
||||
if receiver := t.Receiver(mimeType); receiver != nil {
|
||||
mediaSizes = receiver.VideoSizes()
|
||||
}
|
||||
|
||||
if trackInfo.Height == 0 && len(mediaSizes) == 0 {
|
||||
return quality
|
||||
}
|
||||
origSize := trackInfo.Height
|
||||
@@ -875,19 +1079,38 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
|
||||
requestedSize = width
|
||||
}
|
||||
|
||||
if origSize == 0 {
|
||||
for i := len(mediaSizes) - 1; i >= 0; i-- {
|
||||
if mediaSizes[i].Height > 0 {
|
||||
origSize = min(mediaSizes[i].Width, mediaSizes[i].Height)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default sizes representing qualities low - high
|
||||
layerSizes := []uint32{180, 360, origSize}
|
||||
var providedSizes []uint32
|
||||
for _, layer := range trackInfo.Layers {
|
||||
for _, layer := range buffer.GetVideoLayersForMimeType(mimeType, trackInfo) {
|
||||
providedSizes = append(providedSizes, layer.Height)
|
||||
}
|
||||
|
||||
if len(providedSizes) == 0 || providedSizes[0] == 0 || t.params.PreferVideoSizeFromMedia {
|
||||
if len(mediaSizes) > 0 {
|
||||
providedSizes = providedSizes[:0]
|
||||
for _, size := range mediaSizes {
|
||||
providedSizes = append(providedSizes, size.Height)
|
||||
}
|
||||
} else {
|
||||
t.params.Logger.Debugw("no video sizes provided by receiver, using track info sizes")
|
||||
}
|
||||
}
|
||||
|
||||
if len(providedSizes) > 0 {
|
||||
layerSizes = providedSizes
|
||||
// comparing height always
|
||||
requestedSize = height
|
||||
sort.Slice(layerSizes, func(i, j int) bool {
|
||||
return layerSizes[i] < layerSizes[j]
|
||||
})
|
||||
slices.Sort(layerSizes)
|
||||
}
|
||||
|
||||
// finds the highest layer with smallest dimensions that still satisfy client demands
|
||||
@@ -905,7 +1128,7 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) GetAudioLevel() (float64, bool) {
|
||||
receiver := t.PrimaryReceiver()
|
||||
receiver := t.ActiveReceiver()
|
||||
if receiver == nil {
|
||||
return 0, false
|
||||
}
|
||||
@@ -923,8 +1146,8 @@ func (t *MediaTrackReceiver) onDownTrackCreated(downTrack *sfu.DownTrack) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) DebugInfo() map[string]interface{} {
|
||||
info := map[string]interface{}{
|
||||
func (t *MediaTrackReceiver) DebugInfo() map[string]any {
|
||||
info := map[string]any{
|
||||
"ID": t.ID(),
|
||||
"Kind": t.Kind().String(),
|
||||
"PubMuted": t.IsMuted(),
|
||||
@@ -950,6 +1173,16 @@ func (t *MediaTrackReceiver) PrimaryReceiver() sfu.TrackReceiver {
|
||||
return receivers[0].TrackReceiver
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) ActiveReceiver() sfu.TrackReceiver {
|
||||
for _, r := range t.loadReceivers() {
|
||||
if r.IsRegressed() {
|
||||
return r.TrackReceiver
|
||||
}
|
||||
}
|
||||
|
||||
return t.PrimaryReceiver()
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) Receiver(mime mime.MimeType) sfu.TrackReceiver {
|
||||
for _, r := range t.loadReceivers() {
|
||||
if r.Mime() == mime {
|
||||
@@ -985,8 +1218,8 @@ func (t *MediaTrackReceiver) SetRTT(rtt uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MediaTrackReceiver) GetTemporalLayerForSpatialFps(spatial int32, fps uint32, mime mime.MimeType) int32 {
|
||||
receiver := t.Receiver(mime)
|
||||
func (t *MediaTrackReceiver) GetTemporalLayerForSpatialFps(mimeType mime.MimeType, spatial int32, fps uint32) int32 {
|
||||
receiver := t.Receiver(mimeType)
|
||||
if receiver == nil {
|
||||
return buffer.DefaultMaxLayerTemporal
|
||||
}
|
||||
|
||||
@@ -16,21 +16,17 @@ package rtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
sutils "github.com/livekit/livekit-server/pkg/utils"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -47,6 +43,7 @@ type MediaTrackSubscriptions struct {
|
||||
|
||||
onDownTrackCreated func(downTrack *sfu.DownTrack)
|
||||
onSubscriberMaxQualityChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, layer int32)
|
||||
onSubscriberAudioCodecChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)
|
||||
}
|
||||
|
||||
type MediaTrackSubscriptionsParams struct {
|
||||
@@ -56,8 +53,6 @@ type MediaTrackSubscriptionsParams struct {
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
|
||||
Telemetry telemetry.TelemetryService
|
||||
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
@@ -76,6 +71,10 @@ func (t *MediaTrackSubscriptions) OnSubscriberMaxQualityChange(f func(subscriber
|
||||
t.onSubscriberMaxQualityChange = f
|
||||
}
|
||||
|
||||
func (t *MediaTrackSubscriptions) OnSubscriberAudioCodecChange(f func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)) {
|
||||
t.onSubscriberAudioCodecChange = f
|
||||
}
|
||||
|
||||
func (t *MediaTrackSubscriptions) SetMuted(muted bool) {
|
||||
// update mute of all subscribed tracks
|
||||
for _, st := range t.getAllSubscribedTracks() {
|
||||
@@ -104,86 +103,32 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
|
||||
}
|
||||
t.subscribedTracksMu.Unlock()
|
||||
|
||||
var rtcpFeedback []webrtc.RTCPFeedback
|
||||
var maxTrack int
|
||||
switch t.params.MediaTrack.Kind() {
|
||||
case livekit.TrackType_AUDIO:
|
||||
rtcpFeedback = t.params.SubscriberConfig.RTCPFeedback.Audio
|
||||
maxTrack = t.params.ReceiverConfig.PacketBufferSizeAudio
|
||||
case livekit.TrackType_VIDEO:
|
||||
rtcpFeedback = t.params.SubscriberConfig.RTCPFeedback.Video
|
||||
maxTrack = t.params.ReceiverConfig.PacketBufferSizeVideo
|
||||
}
|
||||
codecs := wr.Codecs()
|
||||
for _, c := range codecs {
|
||||
c.RTCPFeedback = rtcpFeedback
|
||||
}
|
||||
|
||||
streamID := wr.StreamID()
|
||||
if sub.SupportsSyncStreamID() && t.params.MediaTrack.Stream() != "" {
|
||||
streamID = PackSyncStreamID(t.params.MediaTrack.PublisherID(), t.params.MediaTrack.Stream())
|
||||
}
|
||||
|
||||
var trailer []byte
|
||||
if t.params.MediaTrack.IsEncrypted() {
|
||||
trailer = sub.GetTrailer()
|
||||
}
|
||||
|
||||
downTrack, err := sfu.NewDownTrack(sfu.DowntrackParams{
|
||||
Codecs: codecs,
|
||||
Source: t.params.MediaTrack.Source(),
|
||||
Receiver: wr,
|
||||
BufferFactory: sub.GetBufferFactory(),
|
||||
SubID: subscriberID,
|
||||
StreamID: streamID,
|
||||
MaxTrack: maxTrack,
|
||||
PlayoutDelayLimit: sub.GetPlayoutDelayConfig(),
|
||||
Pacer: sub.GetPacer(),
|
||||
Trailer: trailer,
|
||||
Logger: LoggerWithTrack(sub.GetLogger().WithComponent(sutils.ComponentSub), trackID, t.params.IsRelayed),
|
||||
RTCPWriter: sub.WriteSubscriberRTCP,
|
||||
DisableSenderReportPassThrough: sub.GetDisableSenderReportPassThrough(),
|
||||
SupportsCodecChange: sub.SupportsCodecChange(),
|
||||
subTrack, err := NewSubscribedTrack(SubscribedTrackParams{
|
||||
ReceiverConfig: t.params.ReceiverConfig,
|
||||
SubscriberConfig: t.params.SubscriberConfig,
|
||||
Subscriber: sub,
|
||||
MediaTrack: t.params.MediaTrack,
|
||||
AdaptiveStream: sub.GetAdaptiveStream(),
|
||||
TelemetryListener: sub.GetTelemetryListener(),
|
||||
WrappedReceiver: wr,
|
||||
IsRelayed: t.params.IsRelayed,
|
||||
OnDownTrackCreated: t.onDownTrackCreated,
|
||||
OnDownTrackClosed: func(subscriberID livekit.ParticipantID) {
|
||||
t.subscribedTracksMu.Lock()
|
||||
delete(t.subscribedTracks, subscriberID)
|
||||
t.subscribedTracksMu.Unlock()
|
||||
},
|
||||
OnSubscriberMaxQualityChange: t.onSubscriberMaxQualityChange,
|
||||
OnSubscriberAudioCodecChange: t.onSubscriberAudioCodecChange,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.onDownTrackCreated != nil {
|
||||
t.onDownTrackCreated(downTrack)
|
||||
}
|
||||
|
||||
subTrack := NewSubscribedTrack(SubscribedTrackParams{
|
||||
PublisherID: t.params.MediaTrack.PublisherID(),
|
||||
PublisherIdentity: t.params.MediaTrack.PublisherIdentity(),
|
||||
PublisherVersion: t.params.MediaTrack.PublisherVersion(),
|
||||
Subscriber: sub,
|
||||
MediaTrack: t.params.MediaTrack,
|
||||
DownTrack: downTrack,
|
||||
AdaptiveStream: sub.GetAdaptiveStream(),
|
||||
})
|
||||
|
||||
if !sub.Hidden() {
|
||||
subTrack.AddOnBind(func(err error) {
|
||||
if err == nil {
|
||||
t.params.MediaTrack.OnTrackSubscribed()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Bind callback can happen from replaceTrack, so set it up early
|
||||
var reusingTransceiver atomic.Bool
|
||||
var dtState sfu.DownTrackState
|
||||
downTrack.OnCodecNegotiated(func(codec webrtc.RTPCodecCapability) {
|
||||
if !wr.DetermineReceiver(codec) {
|
||||
if t.onSubscriberMaxQualityChange != nil {
|
||||
go func() {
|
||||
spatial := buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_HIGH, t.params.MediaTrack.ToProto())
|
||||
t.onSubscriberMaxQualityChange(downTrack.SubscriberID(), mime.NormalizeMimeType(codec.MimeType), spatial)
|
||||
}()
|
||||
}
|
||||
}
|
||||
})
|
||||
downTrack := subTrack.DownTrack()
|
||||
downTrack.OnBinding(func(err error) {
|
||||
if err != nil {
|
||||
go subTrack.Bound(err)
|
||||
@@ -207,25 +152,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
|
||||
subTrack.SetPublisherMuted(t.params.MediaTrack.IsMuted())
|
||||
})
|
||||
|
||||
downTrack.OnStatsUpdate(func(_ *sfu.DownTrack, stat *livekit.AnalyticsStat) {
|
||||
key := telemetry.StatsKeyForTrack(livekit.StreamType_DOWNSTREAM, subscriberID, trackID, t.params.MediaTrack.Source(), t.params.MediaTrack.Kind())
|
||||
t.params.Telemetry.TrackStats(key, stat)
|
||||
})
|
||||
|
||||
downTrack.OnMaxLayerChanged(func(dt *sfu.DownTrack, layer int32) {
|
||||
if t.onSubscriberMaxQualityChange != nil {
|
||||
t.onSubscriberMaxQualityChange(dt.SubscriberID(), dt.Mime(), layer)
|
||||
}
|
||||
})
|
||||
|
||||
downTrack.OnRttUpdate(func(_ *sfu.DownTrack, rtt uint32) {
|
||||
go sub.UpdateMediaRTT(rtt)
|
||||
})
|
||||
|
||||
downTrack.AddReceiverReportListener(func(dt *sfu.DownTrack, report *rtcp.ReceiverReport) {
|
||||
sub.HandleReceiverReport(dt, report)
|
||||
})
|
||||
|
||||
var transceiver *webrtc.RTPTransceiver
|
||||
var sender *webrtc.RTPSender
|
||||
|
||||
@@ -277,15 +203,16 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
|
||||
if transceiver == nil {
|
||||
info := t.params.MediaTrack.ToProto()
|
||||
addTrackParams := types.AddTrackParams{
|
||||
Stereo: info.Stereo,
|
||||
Red: !info.DisableRed,
|
||||
Stereo: slices.Contains(info.AudioFeatures, livekit.AudioTrackFeature_TF_STEREO),
|
||||
Red: IsRedEnabled(info),
|
||||
}
|
||||
codecs := wr.Codecs()
|
||||
if addTrackParams.Red && (len(codecs) == 1 && mime.IsMimeTypeStringOpus(codecs[0].MimeType)) {
|
||||
addTrackParams.Red = false
|
||||
}
|
||||
|
||||
sub.VerifySubscribeParticipantInfo(subTrack.PublisherID(), subTrack.PublisherVersion())
|
||||
if sub.SupportsTransceiverReuse() {
|
||||
if sub.SupportsTransceiverReuse(t.params.MediaTrack) {
|
||||
//
|
||||
// AddTrack will create a new transceiver or re-use an unused one
|
||||
// if the attributes match. This prevents SDP from bloating
|
||||
@@ -320,6 +247,7 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
|
||||
// But, the subscription could be removed early if the published track is closed
|
||||
// while adding subscription. In those cases, subscription manager would not have set
|
||||
// the `OnClose` callback. So, set it here to handle cases of early close.
|
||||
// Subscription manager will reset this if this subscription proceeds till that point.
|
||||
subTrack.OnClose(func(isExpectedToResume bool) {
|
||||
if !isExpectedToResume {
|
||||
if err := sub.RemoveTrackLocal(sender); err != nil {
|
||||
@@ -330,10 +258,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
|
||||
|
||||
downTrack.SetTransceiver(transceiver)
|
||||
|
||||
downTrack.OnCloseHandler(func(isExpectedToResume bool) {
|
||||
t.downTrackClosed(sub, subTrack, isExpectedToResume)
|
||||
})
|
||||
|
||||
t.subscribedTracksMu.Lock()
|
||||
t.subscribedTracks[subscriberID] = subTrack
|
||||
t.subscribedTracksMu.Unlock()
|
||||
@@ -361,10 +285,10 @@ func (t *MediaTrackSubscriptions) closeSubscribedTrack(subTrack types.Subscribed
|
||||
}
|
||||
|
||||
if isExpectedToResume {
|
||||
dt.CloseWithFlush(false)
|
||||
dt.CloseWithFlush(false, false)
|
||||
} else {
|
||||
// flushing blocks, avoid blocking when publisher removes all its subscribers
|
||||
go dt.CloseWithFlush(true)
|
||||
go dt.CloseWithFlush(true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,8 +353,8 @@ func (t *MediaTrackSubscriptions) getAllSubscribedTracksLocked() []types.Subscri
|
||||
return subTracks
|
||||
}
|
||||
|
||||
func (t *MediaTrackSubscriptions) DebugInfo() []map[string]interface{} {
|
||||
subscribedTrackInfo := make([]map[string]interface{}, 0)
|
||||
func (t *MediaTrackSubscriptions) DebugInfo() []map[string]any {
|
||||
subscribedTrackInfo := make([]map[string]any, 0)
|
||||
for _, val := range t.getAllSubscribedTracks() {
|
||||
if st, ok := val.(*SubscribedTrack); ok {
|
||||
subscribedTrackInfo = append(subscribedTrackInfo, st.DownTrack().DebugInfo())
|
||||
@@ -439,27 +363,3 @@ func (t *MediaTrackSubscriptions) DebugInfo() []map[string]interface{} {
|
||||
|
||||
return subscribedTrackInfo
|
||||
}
|
||||
|
||||
func (t *MediaTrackSubscriptions) downTrackClosed(
|
||||
sub types.LocalParticipant,
|
||||
subTrack types.SubscribedTrack,
|
||||
isExpectedToResume bool,
|
||||
) {
|
||||
// Cache transceiver for potential re-use on resume.
|
||||
// To ensure subscription manager does not re-subscribe before caching,
|
||||
// delete the subscribed track only after caching.
|
||||
if isExpectedToResume {
|
||||
dt := subTrack.DownTrack()
|
||||
tr := dt.GetTransceiver()
|
||||
if tr != nil {
|
||||
sub.CacheDownTrack(subTrack.ID(), tr, dt.GetState())
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
t.subscribedTracksMu.Lock()
|
||||
delete(t.subscribedTracks, sub.ID())
|
||||
t.subscribedTracksMu.Unlock()
|
||||
subTrack.Close(isExpectedToResume)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type MigrationDataCacheState int
|
||||
|
||||
const (
|
||||
MigrationDataCacheStateWaiting MigrationDataCacheState = iota
|
||||
MigrationDataCacheStateTimeout
|
||||
MigrationDataCacheStateDone
|
||||
)
|
||||
|
||||
type MigrationDataCache struct {
|
||||
lastSeq uint32
|
||||
pkts []*livekit.DataPacket
|
||||
state MigrationDataCacheState
|
||||
expiredAt time.Time
|
||||
}
|
||||
|
||||
func NewMigrationDataCache(lastSeq uint32, expiredAt time.Time) *MigrationDataCache {
|
||||
return &MigrationDataCache{
|
||||
lastSeq: lastSeq,
|
||||
expiredAt: expiredAt,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a message to the cache if there is a gap between the last sequence number and cached messages then return the cache State:
|
||||
// - MigrationDataCacheStateWaiting: waiting for the next packet (lastSeq + 1) of last sequence from old node
|
||||
// - MigrationDataCacheStateTimeout: the next packet is not received before the expiredAt, participant will
|
||||
// continue to process the reliable messages, subscribers will see the gap after the publisher migration
|
||||
// - MigrationDataCacheStateDone: the next packet is received, participant can continue to process the reliable messages
|
||||
func (c *MigrationDataCache) Add(pkt *livekit.DataPacket) MigrationDataCacheState {
|
||||
if c.state == MigrationDataCacheStateDone || c.state == MigrationDataCacheStateTimeout {
|
||||
return c.state
|
||||
}
|
||||
|
||||
if pkt.Sequence <= c.lastSeq {
|
||||
return c.state
|
||||
}
|
||||
|
||||
if pkt.Sequence == c.lastSeq+1 {
|
||||
c.state = MigrationDataCacheStateDone
|
||||
return c.state
|
||||
}
|
||||
|
||||
c.pkts = append(c.pkts, pkt)
|
||||
if time.Now().After(c.expiredAt) {
|
||||
c.state = MigrationDataCacheStateTimeout
|
||||
}
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *MigrationDataCache) Get() []*livekit.DataPacket {
|
||||
return c.pkts
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
func TestMigrationDataCache_Add(t *testing.T) {
|
||||
expiredAt := time.Now().Add(100 * time.Millisecond)
|
||||
cache := NewMigrationDataCache(10, expiredAt)
|
||||
|
||||
pkt1 := &livekit.DataPacket{Sequence: 9}
|
||||
state := cache.Add(pkt1)
|
||||
require.Equal(t, MigrationDataCacheStateWaiting, state)
|
||||
require.Empty(t, cache.Get())
|
||||
|
||||
pkt2 := &livekit.DataPacket{Sequence: 11}
|
||||
state = cache.Add(pkt2)
|
||||
require.Equal(t, MigrationDataCacheStateDone, state)
|
||||
require.Empty(t, cache.Get())
|
||||
|
||||
pkt3 := &livekit.DataPacket{Sequence: 12}
|
||||
state = cache.Add(pkt3)
|
||||
require.Equal(t, MigrationDataCacheStateDone, state)
|
||||
require.Empty(t, cache.Get())
|
||||
|
||||
cache2 := NewMigrationDataCache(20, time.Now().Add(10*time.Millisecond))
|
||||
pkt4 := &livekit.DataPacket{Sequence: 22}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
state = cache2.Add(pkt4)
|
||||
require.Equal(t, MigrationDataCacheStateTimeout, state)
|
||||
require.Len(t, cache2.Get(), 1)
|
||||
require.Equal(t, uint32(22), cache2.Get()[0].Sequence)
|
||||
}
|
||||
+1704
-585
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
// 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/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/protocol/utils/guid"
|
||||
)
|
||||
|
||||
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),
|
||||
ParticipantID: p.ID,
|
||||
ParticipantIdentity: p.params.Identity,
|
||||
BytesTrackStats: NewBytesTrackStats(
|
||||
p.params.Country,
|
||||
livekit.TrackID(dti.Sid),
|
||||
p.ID(),
|
||||
p.Kind(),
|
||||
p.KindDetails(),
|
||||
p.params.TelemetryListener,
|
||||
p.params.Reporter,
|
||||
),
|
||||
},
|
||||
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) {
|
||||
p.listener().OnUpdateDataSubscriptions(p, req)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) onReceivedDataTrackMessage(data []byte, arrivalTime int64) {
|
||||
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, arrivalTime)
|
||||
|
||||
p.listener().OnDataTrackMessage(p, data, &packet)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) GetNextSubscribedDataTrackHandle() uint16 {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.nextSubscribedDataTrackHandle++
|
||||
if p.nextSubscribedDataTrackHandle == 0 {
|
||||
p.nextSubscribedDataTrackHandle++
|
||||
}
|
||||
|
||||
return p.nextSubscribedDataTrackHandle
|
||||
}
|
||||
@@ -26,11 +26,14 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry/telemetryfakes"
|
||||
"github.com/livekit/protocol/auth"
|
||||
protoCodecs "github.com/livekit/protocol/codecs"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
lksdp "github.com/livekit/protocol/sdp"
|
||||
"github.com/livekit/protocol/signalling"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/protocol/utils/guid"
|
||||
|
||||
@@ -81,14 +84,14 @@ func TestTrackPublishing(t *testing.T) {
|
||||
track.IDReturns("id")
|
||||
published := false
|
||||
updated := false
|
||||
p.OnTrackUpdated(func(p types.LocalParticipant, track types.MediaTrack) {
|
||||
p.listener().(*typesfakes.FakeLocalParticipantListener).OnTrackUpdatedCalls(func(p types.Participant, track types.MediaTrack) {
|
||||
updated = true
|
||||
})
|
||||
p.OnTrackPublished(func(p types.LocalParticipant, track types.MediaTrack) {
|
||||
p.listener().(*typesfakes.FakeLocalParticipantListener).OnTrackPublishedCalls(func(p types.Participant, track types.MediaTrack) {
|
||||
published = true
|
||||
})
|
||||
p.UpTrackManager.AddPublishedTrack(track)
|
||||
p.handleTrackPublished(track)
|
||||
p.handleTrackPublished(track, false)
|
||||
require.True(t, published)
|
||||
require.False(t, updated)
|
||||
require.Len(t, p.UpTrackManager.publishedTracks, 1)
|
||||
@@ -129,7 +132,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Type: livekit.TrackType_AUDIO,
|
||||
})
|
||||
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
// error response on duplicate adds a message
|
||||
require.Equal(t, 2, sink.WriteMessageCallCount())
|
||||
})
|
||||
|
||||
t.Run("should queue adding of duplicate tracks if already published by client id in signalling", func(t *testing.T) {
|
||||
@@ -137,7 +141,7 @@ func TestTrackPublishing(t *testing.T) {
|
||||
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
|
||||
|
||||
track := &typesfakes.FakeLocalMediaTrack{}
|
||||
track.SignalCidReturns("cid")
|
||||
track.HasSignalCidCalls(func(s string) bool { return s == "cid" })
|
||||
track.ToProtoReturns(&livekit.TrackInfo{})
|
||||
// directly add to publishedTracks without lock - for testing purpose only
|
||||
p.UpTrackManager.publishedTracks["cid"] = track
|
||||
@@ -147,7 +151,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Name: "webcam",
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
})
|
||||
require.Equal(t, 0, sink.WriteMessageCallCount())
|
||||
// `queued` `RequestResponse` should add a message
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
require.Equal(t, 1, len(p.pendingTracks["cid"].trackInfos))
|
||||
|
||||
// add again - it should be added to the queue
|
||||
@@ -156,7 +161,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Name: "webcam",
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
})
|
||||
require.Equal(t, 0, sink.WriteMessageCallCount())
|
||||
// `queued` `RequestResponse`s should have been sent for duplicate additions
|
||||
require.Equal(t, 2, sink.WriteMessageCallCount())
|
||||
require.Equal(t, 2, len(p.pendingTracks["cid"].trackInfos))
|
||||
|
||||
// check SID is the same
|
||||
@@ -178,7 +184,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Name: "webcam",
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
})
|
||||
require.Equal(t, 0, sink.WriteMessageCallCount())
|
||||
// `queued` `RequestResponse` should add a message
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
require.Equal(t, 1, len(p.pendingTracks["cid"].trackInfos))
|
||||
|
||||
// add again - it should be added to the queue
|
||||
@@ -187,7 +194,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Name: "webcam",
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
})
|
||||
require.Equal(t, 0, sink.WriteMessageCallCount())
|
||||
// `queued` `RequestResponse`s should have been sent for duplicate additions
|
||||
require.Equal(t, 2, sink.WriteMessageCallCount())
|
||||
require.Equal(t, 2, len(p.pendingTracks["cid"].trackInfos))
|
||||
|
||||
// check SID is the same
|
||||
@@ -217,7 +225,8 @@ func TestTrackPublishing(t *testing.T) {
|
||||
Type: livekit.TrackType_AUDIO,
|
||||
Source: livekit.TrackSource_MICROPHONE,
|
||||
})
|
||||
require.Equal(t, 1, sink.WriteMessageCallCount())
|
||||
// an error response for disallowed source should send a `RequestResponse`.
|
||||
require.Equal(t, 2, sink.WriteMessageCallCount())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -225,7 +234,7 @@ func TestOutOfOrderUpdates(t *testing.T) {
|
||||
p := newParticipantForTest("test")
|
||||
p.updateState(livekit.ParticipantInfo_JOINED)
|
||||
p.SetMetadata("initial metadata")
|
||||
sink := p.getResponseSink().(*routingfakes.FakeMessageSink)
|
||||
sink := p.GetResponseSink().(*routingfakes.FakeMessageSink)
|
||||
pi1 := p.ToProto()
|
||||
p.SetMetadata("second update")
|
||||
pi2 := p.ToProto()
|
||||
@@ -255,7 +264,7 @@ func TestDisconnectTiming(t *testing.T) {
|
||||
}()
|
||||
track := &typesfakes.FakeMediaTrack{}
|
||||
p.UpTrackManager.AddPublishedTrack(track)
|
||||
p.handleTrackPublished(track)
|
||||
p.handleTrackPublished(track, false)
|
||||
|
||||
// close channel and then try to Negotiate
|
||||
msg.Close()
|
||||
@@ -275,7 +284,10 @@ func TestMuteSetting(t *testing.T) {
|
||||
ti := &livekit.TrackInfo{Sid: "testTrack"}
|
||||
p.pendingTracks["cid"] = &pendingTrackInfo{trackInfos: []*livekit.TrackInfo{ti}}
|
||||
|
||||
p.SetTrackMuted(livekit.TrackID(ti.Sid), true, false)
|
||||
p.SetTrackMuted(&livekit.MuteTrackRequest{
|
||||
Sid: ti.Sid,
|
||||
Muted: true,
|
||||
}, false)
|
||||
require.True(t, p.pendingTracks["cid"].trackInfos[0].Muted)
|
||||
})
|
||||
|
||||
@@ -287,7 +299,7 @@ func TestMuteSetting(t *testing.T) {
|
||||
Muted: true,
|
||||
})
|
||||
|
||||
_, ti, _, _ := p.getPendingTrack("cid", livekit.TrackType_AUDIO, false)
|
||||
_, ti, _, _, _ := p.getPendingTrack("cid", livekit.TrackType_AUDIO, false)
|
||||
require.NotNil(t, ti)
|
||||
require.True(t, ti.Muted)
|
||||
})
|
||||
@@ -362,28 +374,36 @@ func TestDisableCodecs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
require.True(t, found264)
|
||||
offerId := uint32(42)
|
||||
|
||||
// negotiated codec should not contain h264
|
||||
sink := &routingfakes.FakeMessageSink{}
|
||||
participant.SetResponseSink(sink)
|
||||
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
|
||||
var answer webrtc.SessionDescription
|
||||
var answerId uint32
|
||||
var answerReceived atomic.Bool
|
||||
var answerIdReceived atomic.Uint32
|
||||
sink.WriteMessageCalls(func(msg proto.Message) error {
|
||||
if res, ok := msg.(*livekit.SignalResponse); ok {
|
||||
if res.GetAnswer() != nil {
|
||||
answer = FromProtoSessionDescription(res.GetAnswer())
|
||||
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
|
||||
answerReceived.Store(true)
|
||||
answerIdReceived.Store(answerId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
participant.HandleOffer(sdp)
|
||||
participant.HandleOffer(&livekit.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer.String(),
|
||||
Sdp: sdp.SDP,
|
||||
Id: offerId,
|
||||
})
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if answerReceived.Load() {
|
||||
if answerReceived.Load() && answerIdReceived.Load() == offerId {
|
||||
return ""
|
||||
} else {
|
||||
return "answer not received"
|
||||
return "answer not received OR answer id mismatch"
|
||||
}
|
||||
})
|
||||
require.NoError(t, pc.SetRemoteDescription(answer), answer.SDP, sdp.SDP)
|
||||
@@ -415,7 +435,7 @@ func TestDisablePublishCodec(t *testing.T) {
|
||||
}
|
||||
|
||||
sink := &routingfakes.FakeMessageSink{}
|
||||
participant.SetResponseSink(sink)
|
||||
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
|
||||
var publishReceived atomic.Bool
|
||||
sink.WriteMessageCalls(func(msg proto.Message) error {
|
||||
if res, ok := msg.(*livekit.SignalResponse); ok {
|
||||
@@ -463,89 +483,138 @@ func TestDisablePublishCodec(t *testing.T) {
|
||||
require.Eventually(t, func() bool { return publishReceived.Load() }, 5*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestPreferVideoCodecForPublisher(t *testing.T) {
|
||||
participant := newParticipantForTestWithOpts("123", &participantOpts{
|
||||
publisher: true,
|
||||
})
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer pc.Close()
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
// publish h264 track without client preferred codec
|
||||
trackCid := fmt.Sprintf("preferh264video%d", i)
|
||||
participant.AddTrack(&livekit.AddTrackRequest{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Name: "video",
|
||||
Width: 1280,
|
||||
Height: 720,
|
||||
Source: livekit.TrackSource_CAMERA,
|
||||
SimulcastCodecs: []*livekit.SimulcastCodec{
|
||||
{
|
||||
Codec: "h264",
|
||||
Cid: trackCid,
|
||||
},
|
||||
func TestPreferMediaCodecForPublisher(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
mediaKind string
|
||||
trackBaseCid string
|
||||
preferredCodec string
|
||||
addTrack *livekit.AddTrackRequest
|
||||
mimeTypeStringChecker func(string) bool
|
||||
mimeTypeCodecStringChecker func(string) bool
|
||||
transceiverMimeType mime.MimeType
|
||||
}{
|
||||
{
|
||||
name: "video",
|
||||
mediaKind: "video",
|
||||
trackBaseCid: "preferH264Video",
|
||||
preferredCodec: "h264",
|
||||
addTrack: &livekit.AddTrackRequest{
|
||||
Type: livekit.TrackType_VIDEO,
|
||||
Name: "video",
|
||||
Width: 1280,
|
||||
Height: 720,
|
||||
Source: livekit.TrackSource_CAMERA,
|
||||
},
|
||||
})
|
||||
mimeTypeStringChecker: mime.IsMimeTypeStringH264,
|
||||
mimeTypeCodecStringChecker: mime.IsMimeTypeCodecStringH264,
|
||||
transceiverMimeType: mime.MimeTypeVP8,
|
||||
},
|
||||
{
|
||||
name: "audio",
|
||||
mediaKind: "audio",
|
||||
trackBaseCid: "preferPCMAAudio",
|
||||
preferredCodec: "pcma",
|
||||
addTrack: &livekit.AddTrackRequest{
|
||||
Type: livekit.TrackType_AUDIO,
|
||||
Name: "audio",
|
||||
Source: livekit.TrackSource_MICROPHONE,
|
||||
},
|
||||
mimeTypeStringChecker: mime.IsMimeTypeStringPCMA,
|
||||
mimeTypeCodecStringChecker: mime.IsMimeTypeCodecStringPCMA,
|
||||
transceiverMimeType: mime.MimeTypeOpus,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
participant := newParticipantForTestWithOpts("123", &participantOpts{
|
||||
publisher: true,
|
||||
})
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "video/vp8"}, trackCid, trackCid)
|
||||
require.NoError(t, err)
|
||||
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
|
||||
require.NoError(t, err)
|
||||
codecs := transceiver.Receiver().GetParameters().Codecs
|
||||
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer pc.Close()
|
||||
|
||||
if i > 0 {
|
||||
// the negotiated codecs order could be updated by first negotiation, reorder to make h264 not preferred
|
||||
for mime.IsMimeTypeStringH264(codecs[0].MimeType) {
|
||||
codecs = append(codecs[1:], codecs[0])
|
||||
}
|
||||
}
|
||||
// h264 should not be preferred
|
||||
require.False(t, mime.IsMimeTypeStringH264(codecs[0].MimeType), "codecs", codecs)
|
||||
|
||||
sdp, err := pc.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, pc.SetLocalDescription(sdp))
|
||||
|
||||
sink := &routingfakes.FakeMessageSink{}
|
||||
participant.SetResponseSink(sink)
|
||||
var answer webrtc.SessionDescription
|
||||
var answerReceived atomic.Bool
|
||||
sink.WriteMessageCalls(func(msg proto.Message) error {
|
||||
if res, ok := msg.(*livekit.SignalResponse); ok {
|
||||
if res.GetAnswer() != nil {
|
||||
answer = FromProtoSessionDescription(res.GetAnswer())
|
||||
pc.SetRemoteDescription(answer)
|
||||
answerReceived.Store(true)
|
||||
for i := 0; i < 2; i++ {
|
||||
// publish preferred track without client using setCodecPreferences()
|
||||
trackCid := fmt.Sprintf("%s-%d", tc.trackBaseCid, i)
|
||||
req := utils.CloneProto(tc.addTrack)
|
||||
req.SimulcastCodecs = []*livekit.SimulcastCodec{
|
||||
{
|
||||
Codec: tc.preferredCodec,
|
||||
Cid: trackCid,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
participant.HandleOffer(sdp)
|
||||
participant.AddTrack(req)
|
||||
|
||||
require.Eventually(t, func() bool { return answerReceived.Load() }, 5*time.Second, 10*time.Millisecond)
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: tc.transceiverMimeType.String()}, trackCid, trackCid)
|
||||
require.NoError(t, err)
|
||||
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
|
||||
require.NoError(t, err)
|
||||
codecs := transceiver.Receiver().GetParameters().Codecs
|
||||
|
||||
var h264Preferred bool
|
||||
parsed, err := answer.Unmarshal()
|
||||
require.NoError(t, err)
|
||||
var videoSectionIndex int
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
if m.MediaName.Media == "video" {
|
||||
if videoSectionIndex == i {
|
||||
codecs, err := codecsFromMediaDescription(m)
|
||||
require.NoError(t, err)
|
||||
if mime.IsMimeTypeCodecStringH264(codecs[0].Name) {
|
||||
h264Preferred = true
|
||||
break
|
||||
if i > 0 {
|
||||
// the negotiated codecs order could be updated by first negotiation,
|
||||
// reorder to make tested preferred codec not preferred
|
||||
for tc.mimeTypeStringChecker(codecs[0].MimeType) {
|
||||
codecs = append(codecs[1:], codecs[0])
|
||||
}
|
||||
}
|
||||
videoSectionIndex++
|
||||
}
|
||||
}
|
||||
// preferred codec should not be preferred in `offer`
|
||||
require.False(t, tc.mimeTypeStringChecker(codecs[0].MimeType), "codecs", codecs)
|
||||
|
||||
require.Truef(t, h264Preferred, "h264 should be preferred for video section %d, answer sdp: \n%s", i, answer.SDP)
|
||||
sdp, err := pc.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, pc.SetLocalDescription(sdp))
|
||||
offerId := uint32(23)
|
||||
|
||||
sink := &routingfakes.FakeMessageSink{}
|
||||
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
|
||||
var answer webrtc.SessionDescription
|
||||
var answerId uint32
|
||||
var answerReceived atomic.Bool
|
||||
var answerIdReceived atomic.Uint32
|
||||
sink.WriteMessageCalls(func(msg proto.Message) error {
|
||||
if res, ok := msg.(*livekit.SignalResponse); ok {
|
||||
if res.GetAnswer() != nil {
|
||||
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
|
||||
pc.SetRemoteDescription(answer)
|
||||
answerReceived.Store(true)
|
||||
answerIdReceived.Store(answerId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
participant.HandleOffer(&livekit.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer.String(),
|
||||
Sdp: sdp.SDP,
|
||||
Id: offerId,
|
||||
})
|
||||
|
||||
require.Eventually(t, func() bool { return answerReceived.Load() && answerIdReceived.Load() == offerId }, 5*time.Second, 10*time.Millisecond)
|
||||
|
||||
var havePreferred bool
|
||||
parsed, err := answer.Unmarshal()
|
||||
require.NoError(t, err)
|
||||
var mediaSectionIndex int
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
if m.MediaName.Media == tc.mediaKind {
|
||||
if mediaSectionIndex == i {
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(m)
|
||||
require.NoError(t, err)
|
||||
if tc.mimeTypeCodecStringChecker(codecs[0].Name) {
|
||||
havePreferred = true
|
||||
break
|
||||
}
|
||||
}
|
||||
mediaSectionIndex++
|
||||
}
|
||||
}
|
||||
|
||||
require.Truef(t, havePreferred, "%s should be preferred for %s section %d, answer sdp: \n%s", tc.preferredCodec, tc.mediaKind, i, answer.SDP)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,33 +625,59 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
me := webrtc.MediaEngine{}
|
||||
me.RegisterDefaultCodecs()
|
||||
require.NoError(t, me.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: RedCodecCapability,
|
||||
PayloadType: 63,
|
||||
}, webrtc.RTPCodecTypeAudio))
|
||||
opusCodecParameters := protoCodecs.OpusCodecParameters
|
||||
opusCodecParameters.RTPCodecCapability.RTCPFeedback = []webrtc.RTCPFeedback{{Type: webrtc.TypeRTCPFBNACK}}
|
||||
require.NoError(t, me.RegisterCodec(opusCodecParameters, webrtc.RTPCodecTypeAudio))
|
||||
redCodecParameters := protoCodecs.RedCodecParameters
|
||||
redCodecParameters.RTPCodecCapability.RTCPFeedback = []webrtc.RTCPFeedback{{Type: webrtc.TypeRTCPFBNACK}}
|
||||
require.NoError(t, me.RegisterCodec(redCodecParameters, webrtc.RTPCodecTypeAudio))
|
||||
|
||||
api := webrtc.NewAPI(webrtc.WithMediaEngine(&me))
|
||||
pc, err := api.NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer pc.Close()
|
||||
|
||||
for i, disableRed := range []bool{false, true} {
|
||||
for idx, disableRed := range []bool{false, true, false, true} {
|
||||
t.Run(fmt.Sprintf("disableRed=%v", disableRed), func(t *testing.T) {
|
||||
trackCid := fmt.Sprintf("audiotrack%d", i)
|
||||
participant.AddTrack(&livekit.AddTrackRequest{
|
||||
Type: livekit.TrackType_AUDIO,
|
||||
DisableRed: disableRed,
|
||||
Cid: trackCid,
|
||||
})
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "audio/opus"}, trackCid, trackCid)
|
||||
trackCid := fmt.Sprintf("audiotrack%d", idx)
|
||||
req := &livekit.AddTrackRequest{
|
||||
Type: livekit.TrackType_AUDIO,
|
||||
Cid: trackCid,
|
||||
}
|
||||
if idx < 2 {
|
||||
req.DisableRed = disableRed
|
||||
} else {
|
||||
codec := "red"
|
||||
if disableRed {
|
||||
codec = "opus"
|
||||
}
|
||||
req.SimulcastCodecs = []*livekit.SimulcastCodec{
|
||||
{
|
||||
Codec: codec,
|
||||
Cid: trackCid,
|
||||
},
|
||||
}
|
||||
}
|
||||
participant.AddTrack(req)
|
||||
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(
|
||||
webrtc.RTPCodecCapability{MimeType: "audio/opus"},
|
||||
trackCid,
|
||||
trackCid,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
|
||||
|
||||
transceiver, err := pc.AddTransceiverFromTrack(
|
||||
track,
|
||||
webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
codecs := transceiver.Sender().GetParameters().Codecs
|
||||
for i, c := range codecs {
|
||||
if c.MimeType == "audio/opus" && i != 0 {
|
||||
codecs[0], codecs[i] = codecs[i], codecs[0]
|
||||
if c.MimeType == "audio/opus" {
|
||||
if i != 0 {
|
||||
codecs[0], codecs[i] = codecs[i], codecs[0]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -592,24 +687,38 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
pc.SetLocalDescription(sdp)
|
||||
// opus should be preferred
|
||||
require.Equal(t, codecs[0].MimeType, "audio/opus", sdp)
|
||||
offerId := uint32(0xffffff)
|
||||
|
||||
sink := &routingfakes.FakeMessageSink{}
|
||||
participant.SetResponseSink(sink)
|
||||
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
|
||||
var answer webrtc.SessionDescription
|
||||
var answerId uint32
|
||||
var answerReceived atomic.Bool
|
||||
var answerIdReceived atomic.Uint32
|
||||
sink.WriteMessageCalls(func(msg proto.Message) error {
|
||||
if res, ok := msg.(*livekit.SignalResponse); ok {
|
||||
if res.GetAnswer() != nil {
|
||||
answer = FromProtoSessionDescription(res.GetAnswer())
|
||||
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
|
||||
pc.SetRemoteDescription(answer)
|
||||
answerReceived.Store(true)
|
||||
answerIdReceived.Store(answerId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
participant.HandleOffer(sdp)
|
||||
|
||||
require.Eventually(t, func() bool { return answerReceived.Load() }, 5*time.Second, 10*time.Millisecond)
|
||||
participant.HandleOffer(&livekit.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer.String(),
|
||||
Sdp: sdp.SDP,
|
||||
Id: offerId,
|
||||
})
|
||||
require.Eventually(
|
||||
t,
|
||||
func() bool {
|
||||
return answerReceived.Load() && answerIdReceived.Load() == offerId
|
||||
},
|
||||
5*time.Second,
|
||||
10*time.Millisecond,
|
||||
)
|
||||
|
||||
var redPreferred bool
|
||||
parsed, err := answer.Unmarshal()
|
||||
@@ -617,8 +726,8 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
var audioSectionIndex int
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
if m.MediaName.Media == "audio" {
|
||||
if audioSectionIndex == i {
|
||||
codecs, err := codecsFromMediaDescription(m)
|
||||
if audioSectionIndex == idx {
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(m)
|
||||
require.NoError(t, err)
|
||||
// nack is always enabled. if red is preferred, server will not generate nack request
|
||||
var nackEnabled bool
|
||||
@@ -702,8 +811,11 @@ func newParticipantForTestWithOpts(identity livekit.ParticipantIdentity, opts *p
|
||||
ClientConf: opts.clientConf,
|
||||
ClientInfo: ClientInfo{ClientInfo: opts.clientInfo},
|
||||
Logger: LoggerWithParticipant(logger.GetLogger(), identity, sid, false),
|
||||
Telemetry: &telemetryfakes.FakeTelemetryService{},
|
||||
Reporter: roomobs.NewNoopParticipantSessionReporter(),
|
||||
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
|
||||
VersionGenerator: utils.NewDefaultTimedVersionGenerator(),
|
||||
ParticipantListener: &typesfakes.FakeLocalParticipantListener{},
|
||||
ParticipantHelper: &typesfakes.FakeLocalParticipantHelper{},
|
||||
})
|
||||
p.isPublisher.Store(opts.publisher)
|
||||
p.updateState(livekit.ParticipantInfo_ACTIVE)
|
||||
|
||||
@@ -16,44 +16,279 @@ package rtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
lksdp "github.com/livekit/protocol/sdp"
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
|
||||
offer = p.setCodecPreferencesOpusRedForPublisher(offer)
|
||||
offer = p.setCodecPreferencesVideoForPublisher(offer)
|
||||
return offer
|
||||
}
|
||||
func (p *ParticipantImpl) populateSdpCid(parsedOffer *sdp.SessionDescription) ([]*sdp.MediaDescription, []*sdp.MediaDescription) {
|
||||
processUnmatch := func(unmatches []*sdp.MediaDescription, trackType livekit.TrackType) {
|
||||
for _, unmatch := range unmatches {
|
||||
streamID, ok := lksdp.ExtractStreamID(unmatch)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
|
||||
parsed, unmatchAudios, err := p.TransportManager.GetUnmatchMediaForOffer(offer, "audio")
|
||||
if err != nil || len(unmatchAudios) == 0 {
|
||||
return offer
|
||||
sdpCodecs, err := lksdp.CodecsFromMediaDescription(unmatch)
|
||||
if err != nil || len(sdpCodecs) == 0 {
|
||||
p.pubLogger.Errorw(
|
||||
"extract codecs from media section failed", err,
|
||||
"media", unmatch,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
p.pendingTracksLock.Lock()
|
||||
signalCid, info, _, migrated, _ := p.getPendingTrack(streamID, trackType, false)
|
||||
if migrated {
|
||||
p.pendingTracksLock.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
p.pendingTracksLock.Unlock()
|
||||
|
||||
// could be already published track and the unmatch could be a back up codec publish
|
||||
numUnmatchedTracks := 0
|
||||
var unmatchedTrack types.MediaTrack
|
||||
var unmatchedSdpMimeType mime.MimeType
|
||||
|
||||
found := false
|
||||
for _, sdpCodec := range sdpCodecs {
|
||||
sdpMimeType := mime.NormalizeMimeTypeCodec(sdpCodec.Name).ToMimeType()
|
||||
for _, publishedTrack := range p.GetPublishedTracks() {
|
||||
if sigCid, sdpCid := publishedTrack.(*MediaTrack).GetCidsForMimeType(sdpMimeType); sigCid != "" && sdpCid == "" {
|
||||
// a back up codec has a SDP cid match
|
||||
if sigCid == streamID {
|
||||
found = true
|
||||
break
|
||||
} else {
|
||||
numUnmatchedTracks++
|
||||
unmatchedTrack = publishedTrack
|
||||
unmatchedSdpMimeType = sdpMimeType
|
||||
}
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found && unmatchedTrack != nil {
|
||||
if numUnmatchedTracks != 1 {
|
||||
p.pubLogger.Warnw(
|
||||
"too many unmatched tracks", nil,
|
||||
"media", unmatch,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
}
|
||||
unmatchedTrack.(*MediaTrack).UpdateCodecSdpCid(unmatchedSdpMimeType, streamID)
|
||||
p.pubLogger.Debugw(
|
||||
"published track SDP cid updated",
|
||||
"trackID", unmatchedTrack.ID(),
|
||||
"track", logger.Proto(unmatchedTrack.ToProto()),
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(info.Codecs) == 0 {
|
||||
p.pendingTracksLock.Unlock()
|
||||
p.pubLogger.Warnw(
|
||||
"track without codecs", nil,
|
||||
"trackID", info.Sid,
|
||||
"pendingTrack", p.pendingTracks[signalCid],
|
||||
"media", unmatch,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
updated := false
|
||||
for _, sdpCodec := range sdpCodecs {
|
||||
if mime.NormalizeMimeTypeCodec(sdpCodec.Name) == mime.GetMimeTypeCodec(info.Codecs[0].MimeType) {
|
||||
// set SdpCid only if different from SignalCid
|
||||
if streamID != info.Codecs[0].Cid {
|
||||
info.Codecs[0].SdpCid = streamID
|
||||
updated = true
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// not using SimulcastCodec, i. e. mime type not available till track publish
|
||||
if len(info.Codecs) == 1 {
|
||||
// set SdpCid only if different from SignalCid
|
||||
if streamID != info.Codecs[0].Cid {
|
||||
info.Codecs[0].SdpCid = streamID
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updated {
|
||||
p.pendingTracks[signalCid].trackInfos[0] = utils.CloneProto(info)
|
||||
p.pubLogger.Debugw(
|
||||
"pending track SDP cid updated",
|
||||
"signalCid", signalCid,
|
||||
"trackID", info.Sid,
|
||||
"pendingTrack", p.pendingTracks[signalCid],
|
||||
)
|
||||
}
|
||||
p.pendingTracksLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
unmatchAudios, err := p.TransportManager.GetUnmatchMediaForOffer(parsedOffer, "audio")
|
||||
if err != nil {
|
||||
p.pubLogger.Warnw("could not get unmatch audios", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
unmatchVideos, err := p.TransportManager.GetUnmatchMediaForOffer(parsedOffer, "video")
|
||||
if err != nil {
|
||||
p.pubLogger.Warnw("could not get unmatch videos", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
processUnmatch(unmatchAudios, livekit.TrackType_AUDIO)
|
||||
processUnmatch(unmatchVideos, livekit.TrackType_VIDEO)
|
||||
return unmatchAudios, unmatchVideos
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesForPublisher(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatchAudios []*sdp.MediaDescription,
|
||||
unmatchVideos []*sdp.MediaDescription,
|
||||
) {
|
||||
unprocessedUnmatchAudios := p.setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer,
|
||||
unmatchAudios,
|
||||
livekit.TrackType_AUDIO,
|
||||
)
|
||||
p.setCodecPreferencesOpusRedForPublisher(parsedOffer, unprocessedUnmatchAudios)
|
||||
_ = p.setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer,
|
||||
unmatchVideos,
|
||||
livekit.TrackType_VIDEO,
|
||||
)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatches []*sdp.MediaDescription,
|
||||
trackType livekit.TrackType,
|
||||
) []*sdp.MediaDescription {
|
||||
unprocessed := make([]*sdp.MediaDescription, 0, len(unmatches))
|
||||
for _, unmatch := range unmatches {
|
||||
var ti *livekit.TrackInfo
|
||||
var mimeType string
|
||||
|
||||
mid := lksdp.GetMidValue(unmatch)
|
||||
if mid == "" {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
transceiver := p.TransportManager.GetPublisherRTPTransceiver(mid)
|
||||
if transceiver == nil {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
|
||||
streamID, ok := lksdp.ExtractStreamID(unmatch)
|
||||
if !ok {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
|
||||
p.pendingTracksLock.RLock()
|
||||
mt := p.getPublishedTrackBySdpCid(streamID)
|
||||
if mt != nil {
|
||||
ti = mt.ToProto()
|
||||
} else {
|
||||
_, ti, _, _, _ = p.getPendingTrack(streamID, trackType, false)
|
||||
}
|
||||
p.pendingTracksLock.RUnlock()
|
||||
|
||||
if ti == nil {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, c := range ti.Codecs {
|
||||
if c.Cid == streamID || c.SdpCid == streamID {
|
||||
mimeType = c.MimeType
|
||||
break
|
||||
}
|
||||
}
|
||||
if mimeType == "" && len(ti.Codecs) > 0 {
|
||||
mimeType = ti.Codecs[0].MimeType
|
||||
}
|
||||
|
||||
if mimeType == "" {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
|
||||
configureReceiverCodecs(
|
||||
transceiver,
|
||||
mimeType,
|
||||
p.params.ClientInfo.ComplyWithCodecOrderInSDPAnswer(),
|
||||
)
|
||||
}
|
||||
|
||||
return unprocessed
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatchAudios []*sdp.MediaDescription,
|
||||
) {
|
||||
for _, unmatchAudio := range unmatchAudios {
|
||||
mid := lksdp.GetMidValue(unmatchAudio)
|
||||
if mid == "" {
|
||||
continue
|
||||
}
|
||||
transceiver := p.TransportManager.GetPublisherRTPTransceiver(mid)
|
||||
if transceiver == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
streamID, ok := lksdp.ExtractStreamID(unmatchAudio)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p.pendingTracksLock.RLock()
|
||||
_, info, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
// if RED is disabled for this track, don't prefer RED codec in offer
|
||||
disableRed := info != nil && info.DisableRed
|
||||
_, ti, _, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
p.pendingTracksLock.RUnlock()
|
||||
if ti == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codecs, err := codecsFromMediaDescription(unmatchAudio)
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(unmatchAudio)
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw("extract codecs from media section failed", err, "media", unmatchAudio)
|
||||
p.pubLogger.Errorw(
|
||||
"extract codecs from media section failed", err,
|
||||
"media", unmatchAudio,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -68,122 +303,17 @@ func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(offer webrtc.Se
|
||||
continue
|
||||
}
|
||||
|
||||
var preferredCodecs, leftCodecs []string
|
||||
preferRED := IsRedEnabled(ti)
|
||||
// if RED is enabled for this track, prefer RED codec in offer
|
||||
for _, codec := range codecs {
|
||||
// codec contain opus/red
|
||||
if !disableRed && mime.IsMimeTypeCodecStringRED(codec.Name) && strings.Contains(codec.Fmtp, strconv.FormatInt(int64(opusPayload), 10)) {
|
||||
preferredCodecs = append(preferredCodecs, strconv.FormatInt(int64(codec.PayloadType), 10))
|
||||
} else {
|
||||
leftCodecs = append(leftCodecs, strconv.FormatInt(int64(codec.PayloadType), 10))
|
||||
}
|
||||
}
|
||||
|
||||
// ensure nack enabled for audio in publisher offer
|
||||
var nackFound bool
|
||||
for _, attr := range unmatchAudio.Attributes {
|
||||
if attr.Key == "rtcp-fb" && strings.Contains(attr.Value, fmt.Sprintf("%d nack", opusPayload)) {
|
||||
nackFound = true
|
||||
if preferRED &&
|
||||
mime.IsMimeTypeCodecStringRED(codec.Name) &&
|
||||
strings.Contains(codec.Fmtp, strconv.FormatInt(int64(opusPayload), 10)) {
|
||||
configureReceiverCodecs(transceiver, "audio/red", true)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !nackFound {
|
||||
unmatchAudio.Attributes = append(unmatchAudio.Attributes, sdp.Attribute{
|
||||
Key: "rtcp-fb",
|
||||
Value: fmt.Sprintf("%d nack", opusPayload),
|
||||
})
|
||||
}
|
||||
|
||||
// no opus/red found
|
||||
if len(preferredCodecs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
unmatchAudio.MediaName.Formats = append(unmatchAudio.MediaName.Formats[:0], preferredCodecs...)
|
||||
unmatchAudio.MediaName.Formats = append(unmatchAudio.MediaName.Formats, leftCodecs...)
|
||||
}
|
||||
|
||||
bytes, err := parsed.Marshal()
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw("failed to marshal offer", err)
|
||||
return offer
|
||||
}
|
||||
|
||||
return webrtc.SessionDescription{
|
||||
Type: offer.Type,
|
||||
SDP: string(bytes),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesVideoForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
|
||||
parsed, unmatchVideos, err := p.TransportManager.GetUnmatchMediaForOffer(offer, "video")
|
||||
if err != nil || len(unmatchVideos) == 0 {
|
||||
return offer
|
||||
}
|
||||
// unmatched video is pending for publish, set codec preference
|
||||
for _, unmatchVideo := range unmatchVideos {
|
||||
streamID, ok := lksdp.ExtractStreamID(unmatchVideo)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var info *livekit.TrackInfo
|
||||
p.pendingTracksLock.RLock()
|
||||
mt := p.getPublishedTrackBySdpCid(streamID)
|
||||
if mt != nil {
|
||||
info = mt.ToProto()
|
||||
} else {
|
||||
_, info, _, _ = p.getPendingTrack(streamID, livekit.TrackType_VIDEO, false)
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
p.pendingTracksLock.RUnlock()
|
||||
continue
|
||||
}
|
||||
var mimeType string
|
||||
for _, c := range info.Codecs {
|
||||
if c.Cid == streamID {
|
||||
mimeType = c.MimeType
|
||||
break
|
||||
}
|
||||
}
|
||||
if mimeType == "" && len(info.Codecs) > 0 {
|
||||
mimeType = info.Codecs[0].MimeType
|
||||
}
|
||||
p.pendingTracksLock.RUnlock()
|
||||
|
||||
if mimeType != "" {
|
||||
codecs, err := codecsFromMediaDescription(unmatchVideo)
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw("extract codecs from media section failed", err, "media", unmatchVideo)
|
||||
continue
|
||||
}
|
||||
|
||||
var preferredCodecs, leftCodecs []string
|
||||
for _, c := range codecs {
|
||||
if mime.GetMimeTypeCodec(mimeType) == mime.NormalizeMimeTypeCodec(c.Name) {
|
||||
preferredCodecs = append(preferredCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
|
||||
} else {
|
||||
leftCodecs = append(leftCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
|
||||
}
|
||||
}
|
||||
|
||||
unmatchVideo.MediaName.Formats = append(unmatchVideo.MediaName.Formats[:0], preferredCodecs...)
|
||||
// if the client don't comply with codec order in SDP answer, only keep preferred codecs to force client to use it
|
||||
if p.params.ClientInfo.ComplyWithCodecOrderInSDPAnswer() {
|
||||
unmatchVideo.MediaName.Formats = append(unmatchVideo.MediaName.Formats, leftCodecs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytes, err := parsed.Marshal()
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw("failed to marshal offer", err)
|
||||
return offer
|
||||
}
|
||||
|
||||
return webrtc.SessionDescription{
|
||||
Type: offer.Type,
|
||||
SDP: string(bytes),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,12 +325,12 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
return answer
|
||||
}
|
||||
|
||||
parsed, err := answer.Unmarshal()
|
||||
parsedAnswer, err := answer.Unmarshal()
|
||||
if err != nil {
|
||||
return answer
|
||||
}
|
||||
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
for _, m := range parsedAnswer.MediaDescriptions {
|
||||
switch m.MediaName.Media {
|
||||
case "audio":
|
||||
_, ok := m.Attribute(sdp.AttrKeyInactive)
|
||||
@@ -227,7 +357,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
track, _ := p.getPublishedTrackBySdpCid(streamID).(*MediaTrack)
|
||||
if track == nil {
|
||||
p.pendingTracksLock.RLock()
|
||||
_, ti, _, _ = p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
_, ti, _, _, _ = p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
p.pendingTracksLock.RUnlock()
|
||||
} else {
|
||||
ti = track.ToProto()
|
||||
@@ -236,12 +366,12 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
}
|
||||
}
|
||||
|
||||
if ti == nil || (ti.DisableDtx && !ti.Stereo) {
|
||||
if ti == nil {
|
||||
// no need to configure
|
||||
continue
|
||||
}
|
||||
|
||||
opusPT, err := parsed.GetPayloadTypeForCodec(sdp.Codec{Name: mime.MimeTypeCodecOpus.String()})
|
||||
opusPT, err := parsedAnswer.GetPayloadTypeForCodec(sdp.Codec{Name: mime.MimeTypeCodecOpus.String()})
|
||||
if err != nil {
|
||||
p.pubLogger.Infow("failed to get opus payload type", "error", err, "trackID", ti.Sid)
|
||||
continue
|
||||
@@ -249,11 +379,15 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
|
||||
for i, attr := range m.Attributes {
|
||||
if strings.HasPrefix(attr.String(), fmt.Sprintf("fmtp:%d", opusPT)) {
|
||||
if !ti.DisableDtx {
|
||||
if !slices.Contains(ti.AudioFeatures, livekit.AudioTrackFeature_TF_NO_DTX) {
|
||||
attr.Value += ";usedtx=1"
|
||||
} else {
|
||||
attr.Value = strings.ReplaceAll(attr.Value, ";usedtx=1", "")
|
||||
}
|
||||
if ti.Stereo {
|
||||
if slices.Contains(ti.AudioFeatures, livekit.AudioTrackFeature_TF_STEREO) {
|
||||
attr.Value += ";stereo=1;maxaveragebitrate=510000"
|
||||
} else {
|
||||
attr.Value = strings.ReplaceAll(attr.Value, ";stereo=1", "")
|
||||
}
|
||||
m.Attributes[i] = attr
|
||||
}
|
||||
@@ -264,7 +398,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
}
|
||||
}
|
||||
|
||||
bytes, err := parsed.Marshal()
|
||||
bytes, err := parsedAnswer.Marshal()
|
||||
if err != nil {
|
||||
p.pubLogger.Infow("failed to marshal answer", "error", err)
|
||||
return answer
|
||||
|
||||
@@ -15,30 +15,28 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/psrpc"
|
||||
protosignalling "github.com/livekit/protocol/signalling"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
func (p *ParticipantImpl) getResponseSink() routing.MessageSink {
|
||||
p.resSinkMu.Lock()
|
||||
defer p.resSinkMu.Unlock()
|
||||
return p.resSink
|
||||
func (p *ParticipantImpl) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
|
||||
p.signaller.SwapResponseSink(sink, reason)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SetResponseSink(sink routing.MessageSink) {
|
||||
p.resSinkMu.Lock()
|
||||
defer p.resSinkMu.Unlock()
|
||||
p.resSink = sink
|
||||
func (p *ParticipantImpl) GetResponseSink() routing.MessageSink {
|
||||
return p.signaller.GetResponseSink()
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) CloseSignalConnection(reason types.SignallingCloseReason) {
|
||||
p.signaller.CloseSignalConnection(reason)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendJoinResponse(joinResponse *livekit.JoinResponse) error {
|
||||
@@ -55,11 +53,7 @@ func (p *ParticipantImpl) SendJoinResponse(joinResponse *livekit.JoinResponse) e
|
||||
p.updateLock.Unlock()
|
||||
|
||||
// send Join response
|
||||
err := p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Join{
|
||||
Join: joinResponse,
|
||||
},
|
||||
})
|
||||
err := p.signaller.WriteMessage(p.signalling.SignalJoinResponse(joinResponse))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,7 +105,7 @@ func (p *ParticipantImpl) SendParticipantUpdate(participantsToUpdate []*livekit.
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
if pi.Permission != nil && pi.Permission.Hidden && pi.Sid != string(p.params.SID) {
|
||||
if pi.Permission != nil && pi.Permission.Hidden && pi.Sid != string(p.ID()) {
|
||||
p.params.Logger.Debugw("skipping hidden participant update", "otherParticipant", pi.Identity)
|
||||
isValid = false
|
||||
}
|
||||
@@ -127,17 +121,7 @@ func (p *ParticipantImpl) SendParticipantUpdate(participantsToUpdate []*livekit.
|
||||
}
|
||||
p.updateLock.Unlock()
|
||||
|
||||
if len(validUpdates) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Update{
|
||||
Update: &livekit.ParticipantUpdate{
|
||||
Participants: validUpdates,
|
||||
},
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalParticipantUpdate(validUpdates))
|
||||
}
|
||||
|
||||
// SendSpeakerUpdate notifies participant changes to speakers. only send members that have changed since last update
|
||||
@@ -158,47 +142,23 @@ func (p *ParticipantImpl) SendSpeakerUpdate(speakers []*livekit.SpeakerInfo, for
|
||||
}
|
||||
}
|
||||
|
||||
if len(scopedSpeakers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SpeakersChanged{
|
||||
SpeakersChanged: &livekit.SpeakersChanged{
|
||||
Speakers: scopedSpeakers,
|
||||
},
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSpeakerUpdate(scopedSpeakers))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendRoomUpdate(room *livekit.Room) error {
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RoomUpdate{
|
||||
RoomUpdate: &livekit.RoomUpdate{
|
||||
Room: room,
|
||||
},
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalRoomUpdate(room))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendConnectionQualityUpdate(update *livekit.ConnectionQualityUpdate) error {
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_ConnectionQuality{
|
||||
ConnectionQuality: update,
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalConnectionQualityUpdate(update))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendRefreshToken(token string) error {
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RefreshToken{
|
||||
RefreshToken: token,
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalRefreshToken(token))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendRequestResponse(requestResponse *livekit.RequestResponse) error {
|
||||
if requestResponse.RequestId == 0 || !p.params.ClientInfo.SupportErrorResponse() {
|
||||
func (p *ParticipantImpl) sendRequestResponse(requestResponse *livekit.RequestResponse) error {
|
||||
if !p.params.ClientInfo.SupportsRequestResponse() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -206,11 +166,11 @@ func (p *ParticipantImpl) SendRequestResponse(requestResponse *livekit.RequestRe
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RequestResponse{
|
||||
RequestResponse: requestResponse,
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalRequestResponse(requestResponse))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendRoomMovedResponse(roomMovedResponse *livekit.RoomMovedResponse) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalRoomMovedResponse(roomMovedResponse))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) HandleReconnectAndSendResponse(reconnectReason livekit.ReconnectReason, reconnectResponse *livekit.ReconnectResponse) error {
|
||||
@@ -219,15 +179,11 @@ func (p *ParticipantImpl) HandleReconnectAndSendResponse(reconnectReason livekit
|
||||
if !p.params.ClientInfo.CanHandleReconnectResponse() {
|
||||
return nil
|
||||
}
|
||||
if err := p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Reconnect{
|
||||
Reconnect: reconnectResponse,
|
||||
},
|
||||
}); err != nil {
|
||||
if err := p.signaller.WriteMessage(p.signalling.SignalReconnectResponse(reconnectResponse)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.params.ProtocolVersion.SupportHandlesDisconnectedUpdate() {
|
||||
if p.params.ProtocolVersion.SupportsDisconnectedUpdate() {
|
||||
return p.sendDisconnectUpdatesForReconnect()
|
||||
}
|
||||
|
||||
@@ -255,17 +211,7 @@ func (p *ParticipantImpl) sendDisconnectUpdatesForReconnect() error {
|
||||
}
|
||||
p.updateLock.Unlock()
|
||||
|
||||
if len(disconnectedParticipants) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Update{
|
||||
Update: &livekit.ParticipantUpdate{
|
||||
Participants: disconnectedParticipants,
|
||||
},
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalParticipantUpdate(disconnectedParticipants))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendICECandidate(ic *webrtc.ICECandidate, target livekit.SignalTarget) error {
|
||||
@@ -274,90 +220,43 @@ func (p *ParticipantImpl) sendICECandidate(ic *webrtc.ICECandidate, target livek
|
||||
return nil
|
||||
}
|
||||
|
||||
trickle := ToProtoTrickle(prevIC.ToJSON(), target, ic == nil)
|
||||
trickle := protosignalling.ToProtoTrickle(prevIC.ToJSON(), target, ic == nil)
|
||||
p.params.Logger.Debugw("sending ICE candidate", "transport", target, "trickle", logger.Proto(trickle))
|
||||
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Trickle{
|
||||
Trickle: trickle,
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalICECandidate(trickle))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendTrackMuted(trackID livekit.TrackID, muted bool) {
|
||||
_ = p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Mute{
|
||||
Mute: &livekit.MuteTrackRequest{
|
||||
Sid: string(trackID),
|
||||
Muted: muted,
|
||||
},
|
||||
},
|
||||
})
|
||||
_ = p.signaller.WriteMessage(p.signalling.SignalTrackMuted(&livekit.MuteTrackRequest{
|
||||
Sid: string(trackID),
|
||||
Muted: muted,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendTrackPublished(cid string, ti *livekit.TrackInfo) error {
|
||||
p.pubLogger.Debugw("sending track published", "cid", cid, "trackInfo", logger.Proto(ti))
|
||||
return p.signaller.WriteMessage(p.signalling.SignalTrackPublished(&livekit.TrackPublishedResponse{
|
||||
Cid: cid,
|
||||
Track: ti,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendTrackUnpublished(trackID livekit.TrackID) {
|
||||
_ = p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_TrackUnpublished{
|
||||
TrackUnpublished: &livekit.TrackUnpublishedResponse{
|
||||
TrackSid: string(trackID),
|
||||
},
|
||||
},
|
||||
})
|
||||
_ = p.signaller.WriteMessage(p.signalling.SignalTrackUnpublished(&livekit.TrackUnpublishedResponse{
|
||||
TrackSid: string(trackID),
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendTrackHasBeenSubscribed(trackID livekit.TrackID) {
|
||||
if !p.params.ClientInfo.SupportTrackSubscribedEvent() {
|
||||
if !p.params.ClientInfo.SupportsTrackSubscribedEvent() {
|
||||
return
|
||||
}
|
||||
_ = p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_TrackSubscribed{
|
||||
TrackSubscribed: &livekit.TrackSubscribed{
|
||||
TrackSid: string(trackID),
|
||||
},
|
||||
},
|
||||
})
|
||||
_ = p.signaller.WriteMessage(p.signalling.SignalTrackSubscribed(&livekit.TrackSubscribed{
|
||||
TrackSid: string(trackID),
|
||||
}))
|
||||
p.params.Logger.Debugw("track has been subscribed", "trackID", trackID)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) writeMessage(msg *livekit.SignalResponse) error {
|
||||
if p.IsDisconnected() || (!p.IsReady() && msg.GetJoin() == nil) {
|
||||
return nil
|
||||
}
|
||||
|
||||
sink := p.getResponseSink()
|
||||
if sink == nil {
|
||||
p.params.Logger.Debugw("could not send message to participant", "messageType", fmt.Sprintf("%T", msg.Message))
|
||||
return nil
|
||||
}
|
||||
|
||||
err := sink.WriteMessage(msg)
|
||||
if utils.ErrorIsOneOf(err, psrpc.Canceled, routing.ErrChannelClosed) {
|
||||
p.params.Logger.Debugw(
|
||||
"could not send message to participant",
|
||||
"error", err,
|
||||
"messageType", fmt.Sprintf("%T", msg.Message),
|
||||
)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
p.params.Logger.Warnw(
|
||||
"could not send message to participant", err,
|
||||
"messageType", fmt.Sprintf("%T", msg.Message),
|
||||
)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// closes signal connection to notify client to resume/reconnect
|
||||
func (p *ParticipantImpl) CloseSignalConnection(reason types.SignallingCloseReason) {
|
||||
sink := p.getResponseSink()
|
||||
if sink != nil {
|
||||
p.params.Logger.Debugw("closing signal connection", "reason", reason, "connID", sink.ConnectionID())
|
||||
sink.Close()
|
||||
p.SetResponseSink(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendLeaveRequest(
|
||||
reason types.ParticipantCloseReason,
|
||||
isExpectedToResume bool,
|
||||
@@ -377,9 +276,9 @@ func (p *ParticipantImpl) sendLeaveRequest(
|
||||
default:
|
||||
leave.Action = livekit.LeaveRequest_DISCONNECT
|
||||
}
|
||||
if leave.Action != livekit.LeaveRequest_DISCONNECT && p.params.GetRegionSettings != nil {
|
||||
if leave.Action != livekit.LeaveRequest_DISCONNECT {
|
||||
// sending region settings even for RESUME just in case client wants to a full reconnect despite server saying RESUME
|
||||
leave.Regions = p.params.GetRegionSettings(p.params.ClientInfo.Address)
|
||||
leave.Regions = p.helper().GetRegionSettings(p.params.ClientInfo.Address)
|
||||
}
|
||||
} else {
|
||||
if !sendOnlyIfSupportingLeaveRequestWithAction {
|
||||
@@ -390,12 +289,82 @@ func (p *ParticipantImpl) sendLeaveRequest(
|
||||
}
|
||||
}
|
||||
if leave != nil {
|
||||
return p.writeMessage(&livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Leave{
|
||||
Leave: leave,
|
||||
},
|
||||
})
|
||||
return p.signaller.WriteMessage(p.signalling.SignalLeaveRequest(leave))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendSdpAnswer(answer webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSdpAnswer(protosignalling.ToProtoSessionDescription(answer, answerId, midToTrackID)))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendSdpOffer(offer webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSdpOffer(protosignalling.ToProtoSessionDescription(offer, offerId, midToTrackID)))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalStreamStateUpdate(streamStateUpdate))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSubscribedQualityUpdate(subscribedQualityUpdate))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendSubscriptionResponse(trackID livekit.TrackID, subErr livekit.SubscriptionError) error {
|
||||
return p.signaller.WriteMessage(p.signalling.SignalSubscriptionResponse(&livekit.SubscriptionResponse{
|
||||
TrackSid: string(trackID),
|
||||
Err: subErr,
|
||||
}))
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) SendSubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool) error {
|
||||
p.subLogger.Debugw("sending subscription permission update", "publisherID", publisherID, "trackID", trackID, "allowed", allowed)
|
||||
err := p.signaller.WriteMessage(p.signalling.SignalSubscriptionPermissionUpdate(&livekit.SubscriptionPermissionUpdate{
|
||||
ParticipantSid: string(publisherID),
|
||||
TrackSid: string(trackID),
|
||||
Allowed: allowed,
|
||||
}))
|
||||
if err != nil {
|
||||
p.subLogger.Errorw("could not send subscription permission update", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) sendMediaSectionsRequirement(numAudios uint32, numVideos uint32) error {
|
||||
p.pubLogger.Debugw(
|
||||
"sending media sections requirement",
|
||||
"numAudios", numAudios,
|
||||
"numVideos", numVideos,
|
||||
)
|
||||
err := p.signaller.WriteMessage(p.signalling.SignalMediaSectionsRequirement(&livekit.MediaSectionsRequirement{
|
||||
NumAudios: numAudios,
|
||||
NumVideos: numVideos,
|
||||
}))
|
||||
if err != nil {
|
||||
p.subLogger.Errorw("could not send media sections requirement", err)
|
||||
}
|
||||
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,
|
||||
}))
|
||||
}
|
||||
|
||||
+892
-429
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/protocol/auth/authfakes"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/protocol/webhook"
|
||||
@@ -89,7 +90,7 @@ func TestJoinedState(t *testing.T) {
|
||||
func TestRoomJoin(t *testing.T) {
|
||||
t.Run("joining returns existing participant data", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: numParticipants})
|
||||
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false)
|
||||
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
|
||||
|
||||
_ = rm.Join(pNew, nil, nil, iceServersForRoom)
|
||||
|
||||
@@ -104,14 +105,14 @@ func TestRoomJoin(t *testing.T) {
|
||||
t.Run("subscribe to existing channels upon join", func(t *testing.T) {
|
||||
numExisting := 3
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: numExisting})
|
||||
p := NewMockParticipant("new", types.CurrentProtocol, false, false)
|
||||
lpl := rm.LocalParticipantListener()
|
||||
p := NewMockParticipant("new", types.CurrentProtocol, false, false, lpl)
|
||||
|
||||
err := rm.Join(p, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
|
||||
require.NoError(t, err)
|
||||
|
||||
stateChangeCB := p.OnStateChangeArgsForCall(0)
|
||||
require.NotNil(t, stateChangeCB)
|
||||
stateChangeCB(p, livekit.ParticipantInfo_ACTIVE)
|
||||
p.StateReturns(livekit.ParticipantInfo_ACTIVE)
|
||||
lpl.OnStateChange(p)
|
||||
|
||||
// it should become a subscriber when connectivity changes
|
||||
numTracks := 0
|
||||
@@ -128,7 +129,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()
|
||||
@@ -159,7 +160,7 @@ func TestRoomJoin(t *testing.T) {
|
||||
rm.lock.Lock()
|
||||
rm.protoRoom.MaxParticipants = 1
|
||||
rm.lock.Unlock()
|
||||
p := NewMockParticipant("second", types.ProtocolVersion(0), false, false)
|
||||
p := NewMockParticipant("second", types.ProtocolVersion(0), false, false, rm.LocalParticipantListener())
|
||||
|
||||
err := rm.Join(p, nil, nil, iceServersForRoom)
|
||||
require.Equal(t, ErrMaxParticipantsExceeded, err)
|
||||
@@ -177,7 +178,7 @@ func TestParticipantUpdate(t *testing.T) {
|
||||
"track mutes are sent to everyone",
|
||||
true,
|
||||
func(p types.LocalParticipant) {
|
||||
p.SetTrackMuted("", true, false)
|
||||
p.SetTrackMuted(&livekit.MuteTrackRequest{Muted: true}, false)
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -270,14 +271,14 @@ func TestPushAndDequeueUpdates(t *testing.T) {
|
||||
pi *livekit.ParticipantInfo
|
||||
closeReason types.ParticipantCloseReason
|
||||
immediate bool
|
||||
existing *participantUpdate
|
||||
expected []*participantUpdate
|
||||
validate func(t *testing.T, rm *Room, updates []*participantUpdate)
|
||||
existing *ParticipantUpdate
|
||||
expected []*ParticipantUpdate
|
||||
validate func(t *testing.T, rm *Room, updates []*ParticipantUpdate)
|
||||
}{
|
||||
{
|
||||
name: "publisher updates are immediate",
|
||||
pi: publisher1v1,
|
||||
expected: []*participantUpdate{{pi: publisher1v1}},
|
||||
expected: []*ParticipantUpdate{{ParticipantInfo: publisher1v1}},
|
||||
},
|
||||
{
|
||||
name: "subscriber updates are queued",
|
||||
@@ -286,20 +287,20 @@ func TestPushAndDequeueUpdates(t *testing.T) {
|
||||
{
|
||||
name: "last version is enqueued",
|
||||
pi: subscriber1v2,
|
||||
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)}, // clone the existing value since it can be modified when setting to disconnected
|
||||
validate: func(t *testing.T, rm *Room, _ []*participantUpdate) {
|
||||
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)}, // clone the existing value since it can be modified when setting to disconnected
|
||||
validate: func(t *testing.T, rm *Room, _ []*ParticipantUpdate) {
|
||||
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
|
||||
require.NotNil(t, queued)
|
||||
requirePIEquals(t, subscriber1v2, queued.pi)
|
||||
requirePIEquals(t, subscriber1v2, queued.ParticipantInfo)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "latest version when immediate",
|
||||
pi: subscriber1v2,
|
||||
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)},
|
||||
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)},
|
||||
immediate: true,
|
||||
expected: []*participantUpdate{{pi: subscriber1v2}},
|
||||
validate: func(t *testing.T, rm *Room, _ []*participantUpdate) {
|
||||
expected: []*ParticipantUpdate{{ParticipantInfo: subscriber1v2}},
|
||||
validate: func(t *testing.T, rm *Room, _ []*ParticipantUpdate) {
|
||||
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
|
||||
require.Nil(t, queued)
|
||||
},
|
||||
@@ -307,37 +308,37 @@ func TestPushAndDequeueUpdates(t *testing.T) {
|
||||
{
|
||||
name: "out of order updates are rejected",
|
||||
pi: subscriber1v1,
|
||||
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v2)},
|
||||
validate: func(t *testing.T, rm *Room, updates []*participantUpdate) {
|
||||
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v2)},
|
||||
validate: func(t *testing.T, rm *Room, updates []*ParticipantUpdate) {
|
||||
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
|
||||
requirePIEquals(t, subscriber1v2, queued.pi)
|
||||
requirePIEquals(t, subscriber1v2, queued.ParticipantInfo)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sid change is broadcasted immediately with synthsized disconnect",
|
||||
pi: publisher2,
|
||||
closeReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant, // just to test if update contain the close reason
|
||||
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v2), closeReason: types.ParticipantCloseReasonStale},
|
||||
expected: []*participantUpdate{
|
||||
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v2), CloseReason: types.ParticipantCloseReasonStale},
|
||||
expected: []*ParticipantUpdate{
|
||||
{
|
||||
pi: &livekit.ParticipantInfo{
|
||||
ParticipantInfo: &livekit.ParticipantInfo{
|
||||
Identity: identity,
|
||||
Sid: "1",
|
||||
Version: 2,
|
||||
State: livekit.ParticipantInfo_DISCONNECTED,
|
||||
},
|
||||
isSynthesizedDisconnect: true,
|
||||
closeReason: types.ParticipantCloseReasonStale,
|
||||
IsSynthesizedDisconnect: true,
|
||||
CloseReason: types.ParticipantCloseReasonStale,
|
||||
},
|
||||
{pi: publisher2, closeReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant},
|
||||
{ParticipantInfo: publisher2, CloseReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when switching to publisher, queue is cleared",
|
||||
pi: publisher1v2,
|
||||
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)},
|
||||
expected: []*participantUpdate{{pi: publisher1v2}},
|
||||
validate: func(t *testing.T, rm *Room, updates []*participantUpdate) {
|
||||
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)},
|
||||
expected: []*ParticipantUpdate{{ParticipantInfo: publisher1v2}},
|
||||
validate: func(t *testing.T, rm *Room, updates []*ParticipantUpdate) {
|
||||
require.Empty(t, rm.batchedUpdates)
|
||||
},
|
||||
},
|
||||
@@ -347,14 +348,22 @@ func TestPushAndDequeueUpdates(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 1})
|
||||
if tc.existing != nil {
|
||||
rm.batchedUpdates[livekit.ParticipantIdentity(tc.existing.pi.Identity)] = tc.existing
|
||||
rm.batchedUpdates[livekit.ParticipantIdentity(tc.existing.ParticipantInfo.Identity)] = tc.existing
|
||||
}
|
||||
updates := rm.pushAndDequeueUpdates(tc.pi, tc.closeReason, tc.immediate)
|
||||
rm.batchedUpdatesMu.Lock()
|
||||
updates := PushAndDequeueUpdates(
|
||||
tc.pi,
|
||||
tc.closeReason,
|
||||
tc.immediate,
|
||||
rm.GetParticipant(livekit.ParticipantIdentity(tc.pi.Identity)),
|
||||
rm.batchedUpdates,
|
||||
)
|
||||
rm.batchedUpdatesMu.Unlock()
|
||||
require.Equal(t, len(tc.expected), len(updates))
|
||||
for i, item := range tc.expected {
|
||||
requirePIEquals(t, item.pi, updates[i].pi)
|
||||
require.Equal(t, item.isSynthesizedDisconnect, updates[i].isSynthesizedDisconnect)
|
||||
require.Equal(t, item.closeReason, updates[i].closeReason)
|
||||
requirePIEquals(t, item.ParticipantInfo, updates[i].ParticipantInfo)
|
||||
require.Equal(t, item.IsSynthesizedDisconnect, updates[i].IsSynthesizedDisconnect)
|
||||
require.Equal(t, item.CloseReason, updates[i].CloseReason)
|
||||
}
|
||||
|
||||
if tc.validate != nil {
|
||||
@@ -417,6 +426,8 @@ func TestRoomClosure(t *testing.T) {
|
||||
func TestNewTrack(t *testing.T) {
|
||||
t.Run("new track should be added to ready participants", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 3})
|
||||
lpl := rm.LocalParticipantListener()
|
||||
|
||||
participants := rm.GetParticipants()
|
||||
p0 := participants[0].(*typesfakes.FakeLocalParticipant)
|
||||
p0.StateReturns(livekit.ParticipantInfo_JOINED)
|
||||
@@ -427,9 +438,8 @@ func TestNewTrack(t *testing.T) {
|
||||
|
||||
// pub adds track
|
||||
track := NewMockTrack(livekit.TrackType_VIDEO, "webcam")
|
||||
trackCB := pub.OnTrackPublishedArgsForCall(0)
|
||||
require.NotNil(t, trackCB)
|
||||
trackCB(pub, track)
|
||||
lpl.OnTrackPublished(pub, track)
|
||||
|
||||
// only p1 should've been subscribed to
|
||||
require.Equal(t, 0, p0.SubscribeToTrackCallCount())
|
||||
require.Equal(t, 1, p1.SubscribeToTrackCallCount())
|
||||
@@ -441,7 +451,7 @@ func TestActiveSpeakers(t *testing.T) {
|
||||
getActiveSpeakerUpdates := func(p *typesfakes.FakeLocalParticipant) [][]*livekit.SpeakerInfo {
|
||||
var updates [][]*livekit.SpeakerInfo
|
||||
numCalls := p.SendSpeakerUpdateCallCount()
|
||||
for i := 0; i < numCalls; i++ {
|
||||
for i := range numCalls {
|
||||
infos, _ := p.SendSpeakerUpdateArgsForCall(i)
|
||||
updates = append(updates, infos)
|
||||
}
|
||||
@@ -518,6 +528,7 @@ func TestActiveSpeakers(t *testing.T) {
|
||||
t.Run("audio level is smoothed", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 2, protocol: 3, audioSmoothIntervals: 3})
|
||||
defer rm.Close(types.ParticipantCloseReasonNone)
|
||||
|
||||
participants := rm.GetParticipants()
|
||||
p := participants[0].(*typesfakes.FakeLocalParticipant)
|
||||
op := participants[1].(*typesfakes.FakeLocalParticipant)
|
||||
@@ -611,6 +622,9 @@ func TestDataChannel(t *testing.T) {
|
||||
t.Run(modeNames[mode], func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 3})
|
||||
defer rm.Close(types.ParticipantCloseReasonNone)
|
||||
|
||||
lpl := rm.LocalParticipantListener()
|
||||
|
||||
participants := rm.GetParticipants()
|
||||
p := participants[0].(*typesfakes.FakeLocalParticipant)
|
||||
|
||||
@@ -631,17 +645,17 @@ func TestDataChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
encoded, _ := proto.Marshal(packetExp)
|
||||
p.OnDataPacketArgsForCall(0)(p, packet.Kind, packet)
|
||||
lpl.OnDataMessage(p, packet.Kind, packet)
|
||||
|
||||
// ensure everyone has received the packet
|
||||
for _, op := range participants {
|
||||
fp := op.(*typesfakes.FakeLocalParticipant)
|
||||
if fp == p {
|
||||
require.Zero(t, fp.SendDataPacketCallCount())
|
||||
require.Zero(t, fp.SendDataMessageCallCount())
|
||||
continue
|
||||
}
|
||||
require.Equal(t, 1, fp.SendDataPacketCallCount())
|
||||
_, got := fp.SendDataPacketArgsForCall(0)
|
||||
require.Equal(t, 1, fp.SendDataMessageCallCount())
|
||||
_, got, _, _ := fp.SendDataMessageArgsForCall(0)
|
||||
require.Equal(t, encoded, got)
|
||||
}
|
||||
})
|
||||
@@ -654,6 +668,9 @@ func TestDataChannel(t *testing.T) {
|
||||
t.Run(modeNames[mode], func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 4})
|
||||
defer rm.Close(types.ParticipantCloseReasonNone)
|
||||
|
||||
lpl := rm.LocalParticipantListener()
|
||||
|
||||
participants := rm.GetParticipants()
|
||||
p := participants[0].(*typesfakes.FakeLocalParticipant)
|
||||
p1 := participants[1].(*typesfakes.FakeLocalParticipant)
|
||||
@@ -678,17 +695,17 @@ func TestDataChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
encoded, _ := proto.Marshal(packetExp)
|
||||
p.OnDataPacketArgsForCall(0)(p, packet.Kind, packet)
|
||||
lpl.OnDataMessage(p, packet.Kind, packet)
|
||||
|
||||
// only p1 should receive the data
|
||||
for _, op := range participants {
|
||||
fp := op.(*typesfakes.FakeLocalParticipant)
|
||||
if fp != p1 {
|
||||
require.Zero(t, fp.SendDataPacketCallCount())
|
||||
require.Zero(t, fp.SendDataMessageCallCount())
|
||||
}
|
||||
}
|
||||
require.Equal(t, 1, p1.SendDataPacketCallCount())
|
||||
_, got := p1.SendDataPacketArgsForCall(0)
|
||||
require.Equal(t, 1, p1.SendDataMessageCallCount())
|
||||
_, got, _, _ := p1.SendDataMessageArgsForCall(0)
|
||||
require.Equal(t, encoded, got)
|
||||
})
|
||||
}
|
||||
@@ -697,6 +714,7 @@ func TestDataChannel(t *testing.T) {
|
||||
t.Run("publishing disallowed", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 2})
|
||||
defer rm.Close(types.ParticipantCloseReasonNone)
|
||||
|
||||
participants := rm.GetParticipants()
|
||||
p := participants[0].(*typesfakes.FakeLocalParticipant)
|
||||
p.CanPublishDataReturns(false)
|
||||
@@ -710,13 +728,14 @@ func TestDataChannel(t *testing.T) {
|
||||
},
|
||||
}
|
||||
if p.CanPublishData() {
|
||||
p.OnDataPacketArgsForCall(0)(p, packet.Kind, &packet)
|
||||
lpl := rm.LocalParticipantListener()
|
||||
lpl.OnDataMessage(p, packet.Kind, &packet)
|
||||
}
|
||||
|
||||
// no one should've been sent packet
|
||||
for _, op := range participants {
|
||||
fp := op.(*typesfakes.FakeLocalParticipant)
|
||||
require.Zero(t, fp.SendDataPacketCallCount())
|
||||
require.Zero(t, fp.SendDataMessageCallCount())
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -726,7 +745,7 @@ func TestHiddenParticipants(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 2, numHidden: 1})
|
||||
defer rm.Close(types.ParticipantCloseReasonNone)
|
||||
|
||||
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false)
|
||||
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
|
||||
rm.Join(pNew, nil, nil, iceServersForRoom)
|
||||
|
||||
// expect new participant to get a JoinReply
|
||||
@@ -740,14 +759,14 @@ func TestHiddenParticipants(t *testing.T) {
|
||||
|
||||
t.Run("hidden participant subscribes to tracks", func(t *testing.T) {
|
||||
rm := newRoomWithParticipants(t, testRoomOpts{num: 2})
|
||||
hidden := NewMockParticipant("hidden", types.CurrentProtocol, true, false)
|
||||
lpl := rm.LocalParticipantListener()
|
||||
hidden := NewMockParticipant("hidden", types.CurrentProtocol, true, false, lpl)
|
||||
|
||||
err := rm.Join(hidden, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
|
||||
require.NoError(t, err)
|
||||
|
||||
stateChangeCB := hidden.OnStateChangeArgsForCall(0)
|
||||
require.NotNil(t, stateChangeCB)
|
||||
stateChangeCB(hidden, livekit.ParticipantInfo_ACTIVE)
|
||||
hidden.StateReturns(livekit.ParticipantInfo_ACTIVE)
|
||||
lpl.OnStateChange(hidden)
|
||||
|
||||
require.Eventually(t, func() bool { return hidden.SubscribeToTrackCallCount() == 2 }, 5*time.Second, 10*time.Millisecond)
|
||||
})
|
||||
@@ -761,7 +780,7 @@ func TestRoomUpdate(t *testing.T) {
|
||||
p1 := rm.GetParticipants()[0].(*typesfakes.FakeLocalParticipant)
|
||||
require.Equal(t, 0, p1.SendRoomUpdateCallCount())
|
||||
|
||||
p2 := NewMockParticipant("p2", types.CurrentProtocol, false, false)
|
||||
p2 := NewMockParticipant("p2", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
|
||||
require.NoError(t, rm.Join(p2, nil, nil, iceServersForRoom))
|
||||
|
||||
// p1 should have received an update
|
||||
@@ -795,6 +814,12 @@ type testRoomOpts struct {
|
||||
}
|
||||
|
||||
func newRoomWithParticipants(t *testing.T, opts testRoomOpts) *Room {
|
||||
kp := &authfakes.FakeKeyProvider{}
|
||||
kp.GetSecretReturns("testkey")
|
||||
|
||||
n, err := webhook.NewDefaultNotifier(webhook.DefaultWebHookConfig, kp)
|
||||
require.NoError(t, err)
|
||||
|
||||
rm := NewRoom(
|
||||
&livekit.Room{Name: "room"},
|
||||
nil,
|
||||
@@ -816,12 +841,12 @@ func newRoomWithParticipants(t *testing.T, opts testRoomOpts) *Room {
|
||||
NodeId: "testnode",
|
||||
Region: "testregion",
|
||||
},
|
||||
telemetry.NewTelemetryService(webhook.NewDefaultNotifier("", "", nil), &telemetryfakes.FakeAnalyticsService{}),
|
||||
telemetry.NewTelemetryService(n, &telemetryfakes.FakeAnalyticsService{}),
|
||||
nil, nil, nil,
|
||||
)
|
||||
for i := 0; i < opts.num+opts.numHidden; i++ {
|
||||
identity := livekit.ParticipantIdentity(fmt.Sprintf("p%d", i))
|
||||
participant := NewMockParticipant(identity, opts.protocol, i >= opts.num, true)
|
||||
participant := NewMockParticipant(identity, opts.protocol, i >= opts.num, true, rm.LocalParticipantListener())
|
||||
err := rm.Join(participant, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
|
||||
require.NoError(t, err)
|
||||
participant.StateReturns(livekit.ParticipantInfo_ACTIVE)
|
||||
|
||||
@@ -22,14 +22,18 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/utils"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
// RoomTrackManager holds tracks that are published to the room
|
||||
type RoomTrackManager struct {
|
||||
logger logger.Logger
|
||||
|
||||
lock sync.RWMutex
|
||||
changedNotifier *utils.ChangeNotifierManager
|
||||
removedNotifier *utils.ChangeNotifierManager
|
||||
tracks map[livekit.TrackID]*TrackInfo
|
||||
tracks map[livekit.TrackID][]*TrackInfo
|
||||
dataTracks map[livekit.TrackID][]*DataTrackInfo
|
||||
}
|
||||
|
||||
type TrackInfo struct {
|
||||
@@ -38,54 +42,101 @@ type TrackInfo struct {
|
||||
PublisherID livekit.ParticipantID
|
||||
}
|
||||
|
||||
func NewRoomTrackManager() *RoomTrackManager {
|
||||
type DataTrackInfo struct {
|
||||
DataTrack types.DataTrack
|
||||
PublisherIdentity livekit.ParticipantIdentity
|
||||
PublisherID livekit.ParticipantID
|
||||
}
|
||||
|
||||
func NewRoomTrackManager(logger logger.Logger) *RoomTrackManager {
|
||||
return &RoomTrackManager{
|
||||
tracks: make(map[livekit.TrackID]*TrackInfo),
|
||||
logger: logger,
|
||||
tracks: make(map[livekit.TrackID][]*TrackInfo),
|
||||
dataTracks: make(map[livekit.TrackID][]*DataTrackInfo),
|
||||
changedNotifier: utils.NewChangeNotifierManager(),
|
||||
removedNotifier: utils.NewChangeNotifierManager(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) AddTrack(track types.MediaTrack, publisherIdentity livekit.ParticipantIdentity, publisherID livekit.ParticipantID) {
|
||||
trackID := track.ID()
|
||||
r.lock.Lock()
|
||||
r.tracks[track.ID()] = &TrackInfo{
|
||||
infos, ok := r.tracks[trackID]
|
||||
if ok {
|
||||
for _, info := range infos {
|
||||
if info.Track == track {
|
||||
r.lock.Unlock()
|
||||
r.logger.Infow("not adding duplicate track", "trackID", trackID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
r.tracks[trackID] = append(r.tracks[trackID], &TrackInfo{
|
||||
Track: track,
|
||||
PublisherIdentity: publisherIdentity,
|
||||
PublisherID: publisherID,
|
||||
}
|
||||
})
|
||||
r.lock.Unlock()
|
||||
|
||||
r.NotifyTrackChanged(track.ID())
|
||||
r.NotifyTrackChanged(trackID)
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) RemoveTrack(track types.MediaTrack) {
|
||||
trackID := track.ID()
|
||||
r.lock.Lock()
|
||||
// ensure we are removing the same track as added
|
||||
info, ok := r.tracks[track.ID()]
|
||||
if !ok || info.Track != track {
|
||||
infos, ok := r.tracks[trackID]
|
||||
if !ok {
|
||||
r.lock.Unlock()
|
||||
return
|
||||
}
|
||||
delete(r.tracks, track.ID())
|
||||
r.lock.Unlock()
|
||||
|
||||
n := r.removedNotifier.GetNotifier(string(track.ID()))
|
||||
numRemoved := 0
|
||||
idx := 0
|
||||
for _, info := range infos {
|
||||
if info.Track == track {
|
||||
numRemoved++
|
||||
} else {
|
||||
r.tracks[trackID][idx] = info
|
||||
idx++
|
||||
}
|
||||
}
|
||||
for j := idx; j < len(infos); j++ {
|
||||
r.tracks[trackID][j] = nil
|
||||
}
|
||||
r.tracks[trackID] = r.tracks[trackID][:idx]
|
||||
if len(r.tracks[trackID]) == 0 {
|
||||
delete(r.tracks, trackID)
|
||||
}
|
||||
r.lock.Unlock()
|
||||
if numRemoved == 0 {
|
||||
return
|
||||
}
|
||||
if numRemoved > 1 {
|
||||
r.logger.Warnw("removed multiple tracks", nil, "trackID", trackID, "numRemoved", numRemoved)
|
||||
}
|
||||
|
||||
n := r.removedNotifier.GetNotifier(string(trackID))
|
||||
if n != nil {
|
||||
n.NotifyChanged()
|
||||
}
|
||||
|
||||
r.changedNotifier.RemoveNotifier(string(track.ID()), true)
|
||||
r.removedNotifier.RemoveNotifier(string(track.ID()), true)
|
||||
r.changedNotifier.RemoveNotifier(string(trackID), true)
|
||||
r.removedNotifier.RemoveNotifier(string(trackID), true)
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) GetTrackInfo(trackID livekit.TrackID) *TrackInfo {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
info := r.tracks[trackID]
|
||||
if info == nil {
|
||||
infos := r.tracks[trackID]
|
||||
if len(infos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// earliest added track is used till it is removed
|
||||
info := infos[0]
|
||||
|
||||
// when track is about to close, do not resolve
|
||||
if info.Track != nil && !info.Track.IsOpen() {
|
||||
return nil
|
||||
@@ -123,3 +174,90 @@ 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()
|
||||
infos, ok := r.dataTracks[trackID]
|
||||
if ok {
|
||||
for _, info := range infos {
|
||||
if info.DataTrack == dataTrack {
|
||||
r.lock.Unlock()
|
||||
r.logger.Infow("not adding duplicate data track", "trackID", trackID)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
numRemoved := 0
|
||||
idx := 0
|
||||
for _, info := range infos {
|
||||
if info.DataTrack == dataTrack {
|
||||
numRemoved++
|
||||
} else {
|
||||
r.dataTracks[trackID][idx] = info
|
||||
idx++
|
||||
}
|
||||
}
|
||||
for j := idx; j < len(infos); j++ {
|
||||
r.dataTracks[trackID][j] = nil
|
||||
}
|
||||
r.dataTracks[trackID] = r.dataTracks[trackID][:idx]
|
||||
if len(r.dataTracks[trackID]) == 0 {
|
||||
delete(r.dataTracks, trackID)
|
||||
}
|
||||
r.lock.Unlock()
|
||||
if numRemoved == 0 {
|
||||
return
|
||||
}
|
||||
if numRemoved > 1 {
|
||||
r.logger.Warnw("removed multiple data tracks", nil, "trackID", trackID, "numRemoved", numRemoved)
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
func (r *RoomTrackManager) Report() (int, int) {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
return len(r.tracks), len(r.dataTracks)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/frostbyte73/core"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
"github.com/livekit/protocol/utils"
|
||||
)
|
||||
|
||||
type BytesTrackType string
|
||||
|
||||
const (
|
||||
BytesTrackTypeData BytesTrackType = "DT"
|
||||
BytesTrackTypeSignal BytesTrackType = "SG"
|
||||
)
|
||||
|
||||
// -------------------------------
|
||||
|
||||
type TrafficTotals struct {
|
||||
At time.Time
|
||||
SendBytes uint64
|
||||
SendMessages uint32
|
||||
RecvBytes uint64
|
||||
RecvMessages uint32
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
|
||||
// stats for signal and data channel
|
||||
type BytesTrackStats struct {
|
||||
country string
|
||||
pID livekit.ParticipantID
|
||||
kind livekit.ParticipantInfo_Kind
|
||||
kindDetails []livekit.ParticipantInfo_KindDetail
|
||||
trackID livekit.TrackID
|
||||
send, recv atomic.Uint64
|
||||
sendMessages, recvMessages atomic.Uint32
|
||||
totalSendBytes, totalRecvBytes atomic.Uint64
|
||||
totalSendMessages, totalRecvMessages atomic.Uint32
|
||||
telemetryListener types.ParticipantTelemetryListener
|
||||
reporter roomobs.TrackReporter
|
||||
done core.Fuse
|
||||
}
|
||||
|
||||
func NewBytesTrackStats(
|
||||
country string,
|
||||
trackID livekit.TrackID,
|
||||
pID livekit.ParticipantID,
|
||||
kind livekit.ParticipantInfo_Kind,
|
||||
kindDetails []livekit.ParticipantInfo_KindDetail,
|
||||
telemetryListener types.ParticipantTelemetryListener,
|
||||
participantReporter roomobs.ParticipantSessionReporter,
|
||||
) *BytesTrackStats {
|
||||
s := &BytesTrackStats{
|
||||
country: country,
|
||||
pID: pID,
|
||||
kind: kind,
|
||||
kindDetails: kindDetails,
|
||||
trackID: trackID,
|
||||
telemetryListener: telemetryListener,
|
||||
reporter: participantReporter.WithTrack(trackID.String()),
|
||||
}
|
||||
go s.worker()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *BytesTrackStats) AddBytes(bytes uint64, isSend bool) {
|
||||
if isSend {
|
||||
s.send.Add(bytes)
|
||||
s.sendMessages.Inc()
|
||||
s.totalSendBytes.Add(bytes)
|
||||
s.totalSendMessages.Inc()
|
||||
|
||||
s.reporter.Tx(func(tx roomobs.TrackTx) {
|
||||
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(s.kind))
|
||||
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(s.kindDetails))
|
||||
tx.ReportType(roomobs.TrackTypeData)
|
||||
tx.ReportSendBytes(uint32(bytes))
|
||||
tx.ReportSendPackets(1)
|
||||
})
|
||||
} else {
|
||||
s.recv.Add(bytes)
|
||||
s.recvMessages.Inc()
|
||||
s.totalRecvBytes.Add(bytes)
|
||||
s.totalRecvMessages.Inc()
|
||||
|
||||
s.reporter.Tx(func(tx roomobs.TrackTx) {
|
||||
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(s.kind))
|
||||
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(s.kindDetails))
|
||||
tx.ReportType(roomobs.TrackTypeData)
|
||||
tx.ReportRecvBytes(uint32(bytes))
|
||||
tx.ReportRecvPackets(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BytesTrackStats) GetTrafficTotals() *TrafficTotals {
|
||||
return &TrafficTotals{
|
||||
At: time.Now(),
|
||||
SendBytes: s.totalSendBytes.Load(),
|
||||
SendMessages: s.totalSendMessages.Load(),
|
||||
RecvBytes: s.totalRecvBytes.Load(),
|
||||
RecvMessages: s.totalRecvMessages.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BytesTrackStats) Stop() {
|
||||
s.done.Break()
|
||||
}
|
||||
|
||||
func (s *BytesTrackStats) report() {
|
||||
if recv := s.recv.Swap(0); recv > 0 {
|
||||
packets := s.recvMessages.Swap(0)
|
||||
s.telemetryListener.OnTrackStats(
|
||||
telemetry.StatsKeyForData(s.country, livekit.StreamType_UPSTREAM, s.pID, s.trackID),
|
||||
&livekit.AnalyticsStat{
|
||||
Streams: []*livekit.AnalyticsStream{
|
||||
{
|
||||
PrimaryBytes: recv,
|
||||
PrimaryPackets: packets,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if send := s.send.Swap(0); send > 0 {
|
||||
packets := s.sendMessages.Swap(0)
|
||||
s.telemetryListener.OnTrackStats(
|
||||
telemetry.StatsKeyForData(s.country, livekit.StreamType_DOWNSTREAM, s.pID, s.trackID),
|
||||
&livekit.AnalyticsStat{
|
||||
Streams: []*livekit.AnalyticsStream{
|
||||
{
|
||||
PrimaryBytes: send,
|
||||
PrimaryPackets: packets,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BytesTrackStats) worker() {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
s.report()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.done.Watch():
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.report()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
var _ types.ParticipantTelemetryListener = (*BytesSignalStats)(nil)
|
||||
|
||||
type BytesSignalStats struct {
|
||||
BytesTrackStats
|
||||
ctx context.Context
|
||||
|
||||
telemetry telemetry.TelemetryService
|
||||
guard *telemetry.ReferenceGuard
|
||||
|
||||
participantResolver roomobs.ParticipantReporterResolver
|
||||
trackResolver roomobs.KeyResolver
|
||||
|
||||
mu sync.Mutex
|
||||
ri *livekit.Room
|
||||
pi *livekit.ParticipantInfo
|
||||
stopped chan struct{}
|
||||
|
||||
types.NullParticipantTelemetryListener
|
||||
}
|
||||
|
||||
func NewBytesSignalStats(
|
||||
ctx context.Context,
|
||||
telemetryService telemetry.TelemetryService,
|
||||
) *BytesSignalStats {
|
||||
projectReporter := telemetryService.RoomProjectReporter(ctx)
|
||||
participantReporter, participantReporterResolver := roomobs.DeferredParticipantReporter(projectReporter)
|
||||
trackReporter, trackReporterResolver := participantReporter.WithDeferredTrack()
|
||||
b := &BytesSignalStats{
|
||||
ctx: ctx,
|
||||
telemetry: telemetryService,
|
||||
guard: &telemetry.ReferenceGuard{},
|
||||
participantResolver: participantReporterResolver,
|
||||
trackResolver: trackReporterResolver,
|
||||
}
|
||||
b.BytesTrackStats = BytesTrackStats{
|
||||
telemetryListener: b,
|
||||
reporter: trackReporter,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) ResolveRoom(ri *livekit.Room) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.ri == nil && ri.GetSid() != "" {
|
||||
s.ri = &livekit.Room{
|
||||
Sid: ri.Sid,
|
||||
Name: ri.Name,
|
||||
}
|
||||
s.maybeStart()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) ResolveParticipant(pi *livekit.ParticipantInfo) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.pi == nil && pi != nil {
|
||||
s.pi = &livekit.ParticipantInfo{
|
||||
Sid: pi.Sid,
|
||||
Identity: pi.Identity,
|
||||
Kind: pi.Kind,
|
||||
KindDetails: pi.KindDetails,
|
||||
}
|
||||
s.kind = pi.Kind
|
||||
s.kindDetails = pi.KindDetails
|
||||
s.maybeStart()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) Reset() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.stopped != nil {
|
||||
s.done.Break()
|
||||
<-s.stopped
|
||||
s.stopped = nil
|
||||
s.done = core.Fuse{}
|
||||
s.guard = &telemetry.ReferenceGuard{}
|
||||
}
|
||||
s.ri = nil
|
||||
s.pi = nil
|
||||
|
||||
s.participantResolver.Reset()
|
||||
s.trackResolver.Reset()
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) maybeStart() {
|
||||
if s.ri == nil || s.pi == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.pID = livekit.ParticipantID(s.pi.Sid)
|
||||
s.trackID = BytesTrackIDForParticipantID(BytesTrackTypeSignal, s.pID)
|
||||
|
||||
s.participantResolver.Resolve(
|
||||
livekit.RoomName(s.ri.Name),
|
||||
livekit.RoomID(s.ri.Sid),
|
||||
livekit.ParticipantIdentity(s.pi.Identity),
|
||||
livekit.ParticipantID(s.pi.Sid),
|
||||
)
|
||||
s.trackResolver.Resolve(string(s.trackID))
|
||||
|
||||
s.telemetry.ParticipantJoined(s.ctx, s.ri, s.pi, nil, nil, false, s.guard)
|
||||
s.stopped = make(chan struct{})
|
||||
go s.worker()
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) worker() {
|
||||
s.BytesTrackStats.worker()
|
||||
s.telemetry.ParticipantLeft(s.ctx, s.ri, s.pi, false, s.guard)
|
||||
close(s.stopped)
|
||||
}
|
||||
|
||||
func (s *BytesSignalStats) OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat) {
|
||||
stat.RoomId, stat.RoomName = s.ri.Sid, s.ri.Name
|
||||
s.telemetry.TrackStats(livekit.RoomID(s.ri.Sid), livekit.RoomName(s.ri.Name), key, stat)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
func BytesTrackIDForParticipantID(typ BytesTrackType, participantID livekit.ParticipantID) livekit.TrackID {
|
||||
return livekit.TrackID(fmt.Sprintf("%s%s%s", utils.TrackPrefix, typ, participantID))
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// 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/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
func HandleParticipantSignal(room types.Room, participant types.LocalParticipant, req *livekit.SignalRequest, pLogger logger.Logger) error {
|
||||
participant.UpdateLastSeenSignal()
|
||||
|
||||
switch msg := req.GetMessage().(type) {
|
||||
case *livekit.SignalRequest_Offer:
|
||||
participant.HandleOffer(FromProtoSessionDescription(msg.Offer))
|
||||
|
||||
case *livekit.SignalRequest_Answer:
|
||||
participant.HandleAnswer(FromProtoSessionDescription(msg.Answer))
|
||||
|
||||
case *livekit.SignalRequest_Trickle:
|
||||
candidateInit, err := FromProtoTrickle(msg.Trickle)
|
||||
if err != nil {
|
||||
pLogger.Warnw("could not decode trickle", err)
|
||||
return nil
|
||||
}
|
||||
participant.AddICECandidate(candidateInit, msg.Trickle.Target)
|
||||
|
||||
case *livekit.SignalRequest_AddTrack:
|
||||
pLogger.Debugw("add track request", "trackID", msg.AddTrack.Cid)
|
||||
participant.AddTrack(msg.AddTrack)
|
||||
|
||||
case *livekit.SignalRequest_Mute:
|
||||
participant.SetTrackMuted(livekit.TrackID(msg.Mute.Sid), msg.Mute.Muted, false)
|
||||
|
||||
case *livekit.SignalRequest_Subscription:
|
||||
// allow participant to indicate their interest in the subscription
|
||||
// permission check happens later in SubscriptionManager
|
||||
room.UpdateSubscriptions(
|
||||
participant,
|
||||
livekit.StringsAsIDs[livekit.TrackID](msg.Subscription.TrackSids),
|
||||
msg.Subscription.ParticipantTracks,
|
||||
msg.Subscription.Subscribe,
|
||||
)
|
||||
|
||||
case *livekit.SignalRequest_TrackSetting:
|
||||
for _, sid := range livekit.StringsAsIDs[livekit.TrackID](msg.TrackSetting.TrackSids) {
|
||||
participant.UpdateSubscribedTrackSettings(sid, msg.TrackSetting)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_Leave:
|
||||
reason := types.ParticipantCloseReasonClientRequestLeave
|
||||
switch msg.Leave.Reason {
|
||||
case livekit.DisconnectReason_CLIENT_INITIATED:
|
||||
reason = types.ParticipantCloseReasonClientRequestLeave
|
||||
case livekit.DisconnectReason_USER_UNAVAILABLE:
|
||||
reason = types.ParticipantCloseReasonUserUnavailable
|
||||
case livekit.DisconnectReason_USER_REJECTED:
|
||||
reason = types.ParticipantCloseReasonUserRejected
|
||||
}
|
||||
pLogger.Debugw("client leaving room", "reason", reason)
|
||||
room.RemoveParticipant(participant.Identity(), participant.ID(), reason)
|
||||
|
||||
case *livekit.SignalRequest_SubscriptionPermission:
|
||||
err := room.UpdateSubscriptionPermission(participant, msg.SubscriptionPermission)
|
||||
if err != nil {
|
||||
pLogger.Warnw("could not update subscription permission", err,
|
||||
"permissions", msg.SubscriptionPermission)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_SyncState:
|
||||
err := room.SyncState(participant, msg.SyncState)
|
||||
if err != nil {
|
||||
pLogger.Warnw("could not sync state", err,
|
||||
"state", msg.SyncState)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_Simulate:
|
||||
err := room.SimulateScenario(participant, msg.Simulate)
|
||||
if err != nil {
|
||||
pLogger.Warnw("could not simulate scenario", err,
|
||||
"simulate", msg.Simulate)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_PingReq:
|
||||
if msg.PingReq.Rtt > 0 {
|
||||
participant.UpdateSignalingRTT(uint32(msg.PingReq.Rtt))
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_UpdateMetadata:
|
||||
requestResponse := &livekit.RequestResponse{
|
||||
RequestId: msg.UpdateMetadata.RequestId,
|
||||
Reason: livekit.RequestResponse_OK,
|
||||
}
|
||||
if participant.ClaimGrants().Video.GetCanUpdateOwnMetadata() {
|
||||
if err := participant.CheckMetadataLimits(
|
||||
msg.UpdateMetadata.Name,
|
||||
msg.UpdateMetadata.Metadata,
|
||||
msg.UpdateMetadata.Attributes,
|
||||
); err == nil {
|
||||
if msg.UpdateMetadata.Name != "" {
|
||||
participant.SetName(msg.UpdateMetadata.Name)
|
||||
}
|
||||
if msg.UpdateMetadata.Metadata != "" {
|
||||
participant.SetMetadata(msg.UpdateMetadata.Metadata)
|
||||
}
|
||||
if msg.UpdateMetadata.Attributes != nil {
|
||||
participant.SetAttributes(msg.UpdateMetadata.Attributes)
|
||||
}
|
||||
} else {
|
||||
pLogger.Warnw("could not update metadata", err)
|
||||
|
||||
switch err {
|
||||
case ErrNameExceedsLimits:
|
||||
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
|
||||
requestResponse.Message = "exceeds name length limit"
|
||||
|
||||
case ErrMetadataExceedsLimits:
|
||||
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
|
||||
requestResponse.Message = "exceeds metadata size limit"
|
||||
|
||||
case ErrAttributesExceedsLimits:
|
||||
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
|
||||
requestResponse.Message = "exceeds attributes size limit"
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
requestResponse.Reason = livekit.RequestResponse_NOT_ALLOWED
|
||||
requestResponse.Message = "does not have permission to update own metadata"
|
||||
}
|
||||
participant.SendRequestResponse(requestResponse)
|
||||
|
||||
case *livekit.SignalRequest_UpdateAudioTrack:
|
||||
if err := participant.UpdateAudioTrack(msg.UpdateAudioTrack); err != nil {
|
||||
pLogger.Warnw("could not update audio track", err, "update", msg.UpdateAudioTrack)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_UpdateVideoTrack:
|
||||
if err := participant.UpdateVideoTrack(msg.UpdateVideoTrack); err != nil {
|
||||
pLogger.Warnw("could not update video track", err, "update", msg.UpdateVideoTrack)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMessageType = errors.New("invalid message type")
|
||||
ErrNameExceedsLimits = errors.New("name length exceeds limits")
|
||||
ErrMetadataExceedsLimits = errors.New("metadata size exceeds limits")
|
||||
ErrAttributesExceedsLimits = errors.New("attributes size exceeds limits")
|
||||
ErrUpdateOwnMetadataNotAllowed = errors.New("update own metadata not allowed")
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ParticipantSignalHandler interface {
|
||||
HandleMessage(msg proto.Message) error
|
||||
}
|
||||
|
||||
type ParticipantSignaller interface {
|
||||
SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason)
|
||||
GetResponseSink() routing.MessageSink
|
||||
CloseSignalConnection(reason types.SignallingCloseReason)
|
||||
|
||||
WriteMessage(msg proto.Message) error
|
||||
}
|
||||
|
||||
type ParticipantSignalling interface {
|
||||
SignalJoinResponse(join *livekit.JoinResponse) proto.Message
|
||||
SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message
|
||||
SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message
|
||||
SignalRoomUpdate(room *livekit.Room) proto.Message
|
||||
SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message
|
||||
SignalRefreshToken(token string) proto.Message
|
||||
SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message
|
||||
SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message
|
||||
SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message
|
||||
SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message
|
||||
SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message
|
||||
SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message
|
||||
SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message
|
||||
SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message
|
||||
SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message
|
||||
SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message
|
||||
SignalSdpOffer(offer *livekit.SessionDescription) proto.Message
|
||||
SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message
|
||||
SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message
|
||||
SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
var _ ParticipantSignalHandler = (*signalhandler)(nil)
|
||||
|
||||
type SignalHandlerParams struct {
|
||||
Logger logger.Logger
|
||||
Participant types.LocalParticipant
|
||||
}
|
||||
|
||||
type signalhandler struct {
|
||||
params SignalHandlerParams
|
||||
}
|
||||
|
||||
func NewSignalHandler(params SignalHandlerParams) ParticipantSignalHandler {
|
||||
return &signalhandler{
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalhandler) HandleMessage(msg proto.Message) error {
|
||||
req, ok := msg.(*livekit.SignalRequest)
|
||||
if !ok {
|
||||
s.params.Logger.Warnw(
|
||||
"unknown message type", nil,
|
||||
"messageType", fmt.Sprintf("%T", msg),
|
||||
)
|
||||
return ErrInvalidMessageType
|
||||
}
|
||||
s.params.Participant.UpdateLastSeenSignal()
|
||||
|
||||
s.params.Logger.Debugw("handling signal request", "request", logger.Proto(req))
|
||||
switch msg := req.GetMessage().(type) {
|
||||
case *livekit.SignalRequest_Offer:
|
||||
s.params.Participant.HandleOffer(msg.Offer)
|
||||
|
||||
case *livekit.SignalRequest_Answer:
|
||||
s.params.Participant.HandleAnswer(msg.Answer)
|
||||
|
||||
case *livekit.SignalRequest_Trickle:
|
||||
s.params.Participant.HandleICETrickle(msg.Trickle)
|
||||
|
||||
case *livekit.SignalRequest_AddTrack:
|
||||
s.params.Participant.AddTrack(msg.AddTrack)
|
||||
|
||||
case *livekit.SignalRequest_Mute:
|
||||
s.params.Participant.SetTrackMuted(msg.Mute, false)
|
||||
|
||||
case *livekit.SignalRequest_Subscription:
|
||||
// allow participant to indicate their interest in the subscription
|
||||
// permission check happens later in SubscriptionManager
|
||||
s.params.Participant.HandleUpdateSubscriptions(
|
||||
livekit.StringsAsIDs[livekit.TrackID](msg.Subscription.TrackSids),
|
||||
msg.Subscription.ParticipantTracks,
|
||||
msg.Subscription.Subscribe,
|
||||
)
|
||||
|
||||
case *livekit.SignalRequest_TrackSetting:
|
||||
for _, sid := range livekit.StringsAsIDs[livekit.TrackID](msg.TrackSetting.TrackSids) {
|
||||
s.params.Participant.UpdateSubscribedTrackSettings(sid, msg.TrackSetting)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_Leave:
|
||||
reason := types.ParticipantCloseReasonClientRequestLeave
|
||||
switch msg.Leave.Reason {
|
||||
case livekit.DisconnectReason_CLIENT_INITIATED:
|
||||
reason = types.ParticipantCloseReasonClientRequestLeave
|
||||
case livekit.DisconnectReason_USER_UNAVAILABLE:
|
||||
reason = types.ParticipantCloseReasonUserUnavailable
|
||||
case livekit.DisconnectReason_USER_REJECTED:
|
||||
reason = types.ParticipantCloseReasonUserRejected
|
||||
case livekit.DisconnectReason_AGENT_ERROR:
|
||||
reason = types.ParticipantCloseReasonAgentError
|
||||
}
|
||||
s.params.Logger.Debugw("client leaving room", "reason", reason)
|
||||
s.params.Participant.HandleLeaveRequest(reason)
|
||||
|
||||
case *livekit.SignalRequest_SubscriptionPermission:
|
||||
err := s.params.Participant.HandleUpdateSubscriptionPermission(msg.SubscriptionPermission)
|
||||
if err != nil {
|
||||
s.params.Logger.Warnw(
|
||||
"could not update subscription permission", err,
|
||||
"permissions", msg.SubscriptionPermission,
|
||||
)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_SyncState:
|
||||
err := s.params.Participant.HandleSyncState(msg.SyncState)
|
||||
if err != nil {
|
||||
s.params.Logger.Warnw(
|
||||
"could not sync state", err,
|
||||
"state", msg.SyncState,
|
||||
)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_Simulate:
|
||||
err := s.params.Participant.HandleSimulateScenario(msg.Simulate)
|
||||
if err != nil {
|
||||
s.params.Logger.Warnw(
|
||||
"could not simulate scenario", err,
|
||||
"simulate", msg.Simulate,
|
||||
)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_PingReq:
|
||||
if msg.PingReq.Rtt > 0 {
|
||||
s.params.Participant.UpdateSignalingRTT(uint32(msg.PingReq.Rtt))
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_UpdateMetadata:
|
||||
s.params.Participant.UpdateMetadata(msg.UpdateMetadata, false)
|
||||
|
||||
case *livekit.SignalRequest_UpdateAudioTrack:
|
||||
if err := s.params.Participant.UpdateAudioTrack(msg.UpdateAudioTrack); err != nil {
|
||||
s.params.Logger.Warnw("could not update audio track", err, "update", msg.UpdateAudioTrack)
|
||||
}
|
||||
|
||||
case *livekit.SignalRequest_UpdateVideoTrack:
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ ParticipantSignalHandler = (*signalhandlerUnimplemented)(nil)
|
||||
|
||||
type signalhandlerUnimplemented struct{}
|
||||
|
||||
func (u *signalhandlerUnimplemented) HandleMessage(msg proto.Message) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/psrpc"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ ParticipantSignaller = (*signallerAsync)(nil)
|
||||
|
||||
type SignallerAsyncParams struct {
|
||||
Logger logger.Logger
|
||||
Participant types.LocalParticipant
|
||||
}
|
||||
|
||||
type signallerAsync struct {
|
||||
params SignallerAsyncParams
|
||||
|
||||
*signallerAsyncBase
|
||||
}
|
||||
|
||||
func NewSignallerAsync(params SignallerAsyncParams) ParticipantSignaller {
|
||||
return &signallerAsync{
|
||||
params: params,
|
||||
signallerAsyncBase: newSignallerAsyncBase(signallerAsyncBaseParams{Logger: params.Logger}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signallerAsync) WriteMessage(msg proto.Message) error {
|
||||
if msg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
getMessageType := func(msg proto.Message) string {
|
||||
messageType := "unknown"
|
||||
if typed, ok := msg.(*livekit.SignalResponse); ok {
|
||||
messageType = fmt.Sprintf("%T", typed.Message)
|
||||
}
|
||||
return messageType
|
||||
}
|
||||
|
||||
if s.params.Participant.IsDisconnected() {
|
||||
s.params.Logger.Debugw(
|
||||
"could not send message to participant, participant disconnected",
|
||||
"messageType", getMessageType(msg),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !s.params.Participant.IsReady() {
|
||||
if typed, ok := msg.(*livekit.SignalResponse); !ok {
|
||||
s.params.Logger.Warnw(
|
||||
"unknown message type", nil,
|
||||
"messageType", fmt.Sprintf("%T", msg),
|
||||
)
|
||||
} else {
|
||||
if typed.GetJoin() == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sink := s.GetResponseSink()
|
||||
if sink == nil {
|
||||
s.params.Logger.Debugw(
|
||||
"could not send message to participant, no sink",
|
||||
"messageType", getMessageType(msg),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := sink.WriteMessage(msg)
|
||||
if err != nil {
|
||||
if utils.ErrorIsOneOf(err, psrpc.Canceled, routing.ErrChannelClosed) {
|
||||
s.params.Logger.Debugw(
|
||||
"could not send message to participant",
|
||||
"error", err,
|
||||
"messageType", getMessageType(msg),
|
||||
)
|
||||
return nil
|
||||
} else {
|
||||
s.params.Logger.Warnw(
|
||||
"could not send message to participant", err,
|
||||
"messageType", getMessageType(msg),
|
||||
)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s.params.Logger.Debugw("sent signal response", "response", logger.Proto(msg))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -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 signalling
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
)
|
||||
|
||||
type signallerAsyncBaseParams struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type signallerAsyncBase struct {
|
||||
signallerUnimplemented
|
||||
|
||||
params signallerAsyncBaseParams
|
||||
|
||||
resSinkMu sync.Mutex
|
||||
resSink routing.MessageSink
|
||||
}
|
||||
|
||||
func newSignallerAsyncBase(params signallerAsyncBaseParams) *signallerAsyncBase {
|
||||
return &signallerAsyncBase{
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signallerAsyncBase) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
|
||||
s.resSinkMu.Lock()
|
||||
oldSink := s.resSink
|
||||
s.resSink = sink
|
||||
s.resSinkMu.Unlock()
|
||||
|
||||
if oldSink != nil {
|
||||
if sink != nil {
|
||||
s.params.Logger.Debugw(
|
||||
"swapping signal connection",
|
||||
"reason", reason,
|
||||
"connID", oldSink.ConnectionID(),
|
||||
"newConnID", sink.ConnectionID(),
|
||||
)
|
||||
} else {
|
||||
s.params.Logger.Debugw(
|
||||
"closing signal connection",
|
||||
"reason", reason,
|
||||
"connID", oldSink.ConnectionID(),
|
||||
)
|
||||
}
|
||||
oldSink.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signallerAsyncBase) GetResponseSink() routing.MessageSink {
|
||||
s.resSinkMu.Lock()
|
||||
defer s.resSinkMu.Unlock()
|
||||
return s.resSink
|
||||
}
|
||||
|
||||
// closes signal connection to notify client to resume/reconnect
|
||||
func (s *signallerAsyncBase) CloseSignalConnection(reason types.SignallingCloseReason) {
|
||||
s.SwapResponseSink(nil, reason)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"github.com/livekit/livekit-server/pkg/routing"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ ParticipantSignaller = (*signallerUnimplemented)(nil)
|
||||
|
||||
type signallerUnimplemented struct{}
|
||||
|
||||
func (u *signallerUnimplemented) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
|
||||
}
|
||||
|
||||
func (u *signallerUnimplemented) GetResponseSink() routing.MessageSink {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallerUnimplemented) CloseSignalConnection(reason types.SignallingCloseReason) {}
|
||||
|
||||
func (u *signallerUnimplemented) WriteMessage(msg proto.Message) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ ParticipantSignalling = (*signalling)(nil)
|
||||
|
||||
type SignallingParams struct {
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type signalling struct {
|
||||
params SignallingParams
|
||||
}
|
||||
|
||||
func NewSignalling(params SignallingParams) ParticipantSignalling {
|
||||
return &signalling{
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalJoinResponse(join *livekit.JoinResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Join{
|
||||
Join: join,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message {
|
||||
if len(participants) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Update{
|
||||
Update: &livekit.ParticipantUpdate{
|
||||
Participants: participants,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message {
|
||||
if len(speakers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SpeakersChanged{
|
||||
SpeakersChanged: &livekit.SpeakersChanged{
|
||||
Speakers: speakers,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalRoomUpdate(room *livekit.Room) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RoomUpdate{
|
||||
RoomUpdate: &livekit.RoomUpdate{
|
||||
Room: room,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_ConnectionQuality{
|
||||
ConnectionQuality: connectionQuality,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalRefreshToken(token string) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RefreshToken{
|
||||
RefreshToken: token,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RequestResponse{
|
||||
RequestResponse: requestResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_RoomMoved{
|
||||
RoomMoved: roomMoved,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Reconnect{
|
||||
Reconnect: reconnect,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Trickle{
|
||||
Trickle: trickle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Mute{
|
||||
Mute: mute,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_TrackPublished{
|
||||
TrackPublished: trackPublished,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_TrackUnpublished{
|
||||
TrackUnpublished: trackUnpublished,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_TrackSubscribed{
|
||||
TrackSubscribed: trackSubscribed,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Leave{
|
||||
Leave: leave,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Answer{
|
||||
Answer: answer,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSdpOffer(offer *livekit.SessionDescription) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_Offer{
|
||||
Offer: offer,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_StreamStateUpdate{
|
||||
StreamStateUpdate: streamStateUpdate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SubscribedQualityUpdate{
|
||||
SubscribedQualityUpdate: subscribedQualityUpdate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SubscriptionResponse{
|
||||
SubscriptionResponse: subscriptionResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SubscriptionPermissionUpdate{
|
||||
SubscriptionPermissionUpdate: subscriptionPermissionUpdate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_MediaSectionsRequirement{
|
||||
MediaSectionsRequirement: mediaSectionsRequirement,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_SubscribedAudioCodecUpdate{
|
||||
SubscribedAudioCodecUpdate: subscribedAudioCodecUpdate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_PublishDataTrackResponse{
|
||||
PublishDataTrackResponse: publishDataTrackResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_UnpublishDataTrackResponse{
|
||||
UnpublishDataTrackResponse: unpublishDataTrackResponse,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *signalling) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
|
||||
return &livekit.SignalResponse{
|
||||
Message: &livekit.SignalResponse_DataTrackSubscriberHandles{
|
||||
DataTrackSubscriberHandles: dataTrackSubscriberHandles,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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 signalling
|
||||
|
||||
import (
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ ParticipantSignalling = (*signallingUnimplemented)(nil)
|
||||
|
||||
type signallingUnimplemented struct{}
|
||||
|
||||
func (u *signallingUnimplemented) SignalJoinResponse(join *livekit.JoinResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalRoomUpdate(room *livekit.Room) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalRefreshToken(token string) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSdpOffer(offer *livekit.SessionDescription) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *signallingUnimplemented) SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -23,9 +23,12 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
sutils "github.com/livekit/livekit-server/pkg/utils"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
"github.com/livekit/protocol/utils"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
@@ -37,19 +40,27 @@ const (
|
||||
subscriptionDebounceInterval = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
var _ types.SubscribedTrack = (*SubscribedTrack)(nil)
|
||||
|
||||
type SubscribedTrackParams struct {
|
||||
PublisherID livekit.ParticipantID
|
||||
PublisherIdentity livekit.ParticipantIdentity
|
||||
PublisherVersion uint32
|
||||
Subscriber types.LocalParticipant
|
||||
MediaTrack types.MediaTrack
|
||||
DownTrack *sfu.DownTrack
|
||||
AdaptiveStream bool
|
||||
ReceiverConfig ReceiverConfig
|
||||
SubscriberConfig DirectionConfig
|
||||
Subscriber types.LocalParticipant
|
||||
MediaTrack types.MediaTrack
|
||||
AdaptiveStream bool
|
||||
TelemetryListener types.ParticipantTelemetryListener
|
||||
WrappedReceiver *WrappedReceiver
|
||||
IsRelayed bool
|
||||
OnDownTrackCreated func(downTrack *sfu.DownTrack)
|
||||
OnDownTrackClosed func(subscriberID livekit.ParticipantID)
|
||||
OnSubscriberMaxQualityChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, layer int32)
|
||||
OnSubscriberAudioCodecChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)
|
||||
}
|
||||
|
||||
type SubscribedTrack struct {
|
||||
params SubscribedTrackParams
|
||||
logger logger.Logger
|
||||
downTrack *sfu.DownTrack
|
||||
sender atomic.Pointer[webrtc.RTPSender]
|
||||
needsNegotiation atomic.Bool
|
||||
|
||||
@@ -65,21 +76,98 @@ type SubscribedTrack struct {
|
||||
onClose atomic.Value // func(bool)
|
||||
|
||||
debouncer func(func())
|
||||
|
||||
statsKey telemetry.StatsKey
|
||||
reporter roomobs.TrackReporter
|
||||
}
|
||||
|
||||
func NewSubscribedTrack(params SubscribedTrackParams) *SubscribedTrack {
|
||||
func NewSubscribedTrack(params SubscribedTrackParams) (*SubscribedTrack, error) {
|
||||
s := &SubscribedTrack{
|
||||
params: params,
|
||||
logger: params.Subscriber.GetLogger().WithComponent(sutils.ComponentSub).WithValues(
|
||||
"trackID", params.DownTrack.ID(),
|
||||
"publisherID", params.PublisherID,
|
||||
"publisher", params.PublisherIdentity,
|
||||
"trackID", params.MediaTrack.ID(),
|
||||
"publisherID", params.MediaTrack.PublisherID(),
|
||||
"publisher", params.MediaTrack.PublisherIdentity(),
|
||||
),
|
||||
versionGenerator: utils.NewDefaultTimedVersionGenerator(),
|
||||
debouncer: debounce.New(subscriptionDebounceInterval),
|
||||
statsKey: telemetry.StatsKeyForTrack(
|
||||
params.Subscriber.GetCountry(),
|
||||
livekit.StreamType_DOWNSTREAM,
|
||||
params.Subscriber.ID(),
|
||||
params.MediaTrack.ID(),
|
||||
params.MediaTrack.Source(),
|
||||
params.MediaTrack.Kind(),
|
||||
),
|
||||
reporter: params.Subscriber.GetReporter().WithTrack(params.MediaTrack.ID().String()),
|
||||
}
|
||||
|
||||
return s
|
||||
var rtcpFeedback []webrtc.RTCPFeedback
|
||||
var maxTrack int
|
||||
switch params.MediaTrack.Kind() {
|
||||
case livekit.TrackType_AUDIO:
|
||||
rtcpFeedback = params.SubscriberConfig.RTCPFeedback.Audio
|
||||
maxTrack = params.ReceiverConfig.PacketBufferSizeAudio
|
||||
case livekit.TrackType_VIDEO:
|
||||
rtcpFeedback = params.SubscriberConfig.RTCPFeedback.Video
|
||||
maxTrack = params.ReceiverConfig.PacketBufferSizeVideo
|
||||
default:
|
||||
s.logger.Warnw("unexpected track type", nil, "kind", params.MediaTrack.Kind())
|
||||
}
|
||||
codecs := params.WrappedReceiver.Codecs()
|
||||
for _, c := range codecs {
|
||||
c.RTCPFeedback = rtcpFeedback
|
||||
}
|
||||
|
||||
streamID := params.WrappedReceiver.StreamID()
|
||||
if params.Subscriber.SupportsSyncStreamID() && params.MediaTrack.Stream() != "" {
|
||||
streamID = PackSyncStreamID(params.MediaTrack.PublisherID(), params.MediaTrack.Stream())
|
||||
}
|
||||
|
||||
isEncrypted := params.MediaTrack.IsEncrypted()
|
||||
var trailer []byte
|
||||
if isEncrypted {
|
||||
trailer = params.Subscriber.GetTrailer()
|
||||
}
|
||||
subClientInfo := ClientInfo{ClientInfo: params.Subscriber.GetClientInfo()}
|
||||
subSupportsPacketTrailer := subClientInfo.SupportsPacketTrailer()
|
||||
// Strip packet trailer if track has packet trailer but subscriber does not have cap
|
||||
stripPacketTrailer := params.MediaTrack.HasPacketTrailer() && !subSupportsPacketTrailer
|
||||
downTrack, err := sfu.NewDownTrack(sfu.DownTrackParams{
|
||||
Codecs: codecs,
|
||||
IsEncrypted: isEncrypted,
|
||||
Source: params.MediaTrack.Source(),
|
||||
Receiver: params.WrappedReceiver,
|
||||
BufferFactory: params.Subscriber.GetBufferFactory(),
|
||||
SubID: params.Subscriber.ID(),
|
||||
StreamID: streamID,
|
||||
MaxTrack: maxTrack,
|
||||
PlayoutDelayLimit: params.Subscriber.GetPlayoutDelayConfig(),
|
||||
Pacer: params.Subscriber.GetPacer(),
|
||||
Trailer: trailer,
|
||||
StripPacketTrailer: stripPacketTrailer,
|
||||
Logger: LoggerWithTrack(
|
||||
params.Subscriber.GetLogger().WithComponent(sutils.ComponentSub),
|
||||
params.MediaTrack.ID(),
|
||||
params.IsRelayed,
|
||||
),
|
||||
RTCPWriter: params.Subscriber.WriteSubscriberRTCP,
|
||||
DisableSenderReportPassThrough: params.Subscriber.GetDisableSenderReportPassThrough(),
|
||||
SupportsCodecChange: params.Subscriber.SupportsCodecChange(),
|
||||
Listener: s,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.OnDownTrackCreated != nil {
|
||||
params.OnDownTrackCreated(downTrack)
|
||||
}
|
||||
|
||||
downTrack.AddReceiverReportListener(params.Subscriber.HandleReceiverReport)
|
||||
|
||||
s.downTrack = downTrack
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) AddOnBind(f func(error)) {
|
||||
@@ -121,6 +209,7 @@ func (t *SubscribedTrack) Bound(err error) {
|
||||
if t.params.AdaptiveStream {
|
||||
// remove `disabled` flag to force a visibility update
|
||||
t.settings.Disabled = false
|
||||
t.logger.Debugw("enabling subscriber track settings on bind", "settings", logger.Proto(t.settings))
|
||||
}
|
||||
} else {
|
||||
if t.params.AdaptiveStream {
|
||||
@@ -128,6 +217,7 @@ func (t *SubscribedTrack) Bound(err error) {
|
||||
} else {
|
||||
t.settings = &livekit.UpdateTrackSettings{Quality: livekit.VideoQuality_HIGH}
|
||||
}
|
||||
t.logger.Debugw("initializing subscriber track settings on bind", "settings", logger.Proto(t.settings))
|
||||
}
|
||||
t.settingsLock.Unlock()
|
||||
t.applySettings()
|
||||
@@ -141,7 +231,7 @@ func (t *SubscribedTrack) Bound(err error) {
|
||||
// for DownTrack callback to notify us that it's closed
|
||||
func (t *SubscribedTrack) Close(isExpectedToResume bool) {
|
||||
if onClose := t.onClose.Load(); onClose != nil {
|
||||
go onClose.(func(bool))(isExpectedToResume)
|
||||
onClose.(func(bool))(isExpectedToResume)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,19 +247,19 @@ func (t *SubscribedTrack) IsBound() bool {
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) ID() livekit.TrackID {
|
||||
return livekit.TrackID(t.params.DownTrack.ID())
|
||||
return livekit.TrackID(t.downTrack.ID())
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) PublisherID() livekit.ParticipantID {
|
||||
return t.params.PublisherID
|
||||
return t.params.MediaTrack.PublisherID()
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) PublisherIdentity() livekit.ParticipantIdentity {
|
||||
return t.params.PublisherIdentity
|
||||
return t.params.MediaTrack.PublisherIdentity()
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) PublisherVersion() uint32 {
|
||||
return t.params.PublisherVersion
|
||||
return t.params.MediaTrack.PublisherVersion()
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) SubscriberID() livekit.ParticipantID {
|
||||
@@ -185,7 +275,7 @@ func (t *SubscribedTrack) Subscriber() types.LocalParticipant {
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) DownTrack() *sfu.DownTrack {
|
||||
return t.params.DownTrack
|
||||
return t.downTrack
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) MediaTrack() types.MediaTrack {
|
||||
@@ -209,18 +299,20 @@ func (t *SubscribedTrack) isMutedLocked() bool {
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) SetPublisherMuted(muted bool) {
|
||||
t.DownTrack().PubMute(muted)
|
||||
t.downTrack.PubMute(muted)
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings, isImmediate bool) {
|
||||
t.settingsLock.Lock()
|
||||
if proto.Equal(t.settings, settings) {
|
||||
t.logger.Debugw("skipping subscriber track settings", "settings", logger.Proto(t.settings))
|
||||
t.settingsLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
isImmediate = isImmediate || (!settings.Disabled && settings.Disabled != t.isMutedLocked())
|
||||
t.settings = utils.CloneProto(settings)
|
||||
t.logger.Debugw("saving subscriber track settings", "settings", logger.Proto(t.settings))
|
||||
t.settingsLock.Unlock()
|
||||
|
||||
if isImmediate {
|
||||
@@ -242,7 +334,6 @@ func (t *SubscribedTrack) applySettings() {
|
||||
return
|
||||
}
|
||||
|
||||
t.logger.Debugw("updating subscriber track settings", "settings", logger.Proto(t.settings))
|
||||
t.settingsVersion = t.versionGenerator.Next()
|
||||
settingsVersion := t.settingsVersion
|
||||
t.settingsLock.Unlock()
|
||||
@@ -253,23 +344,25 @@ func (t *SubscribedTrack) applySettings() {
|
||||
if dt.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
mt := t.MediaTrack()
|
||||
quality := t.settings.Quality
|
||||
mimeType := dt.Mime()
|
||||
if t.settings.Width > 0 {
|
||||
quality = mt.GetQualityForDimension(t.settings.Width, t.settings.Height)
|
||||
quality = mt.GetQualityForDimension(mimeType, t.settings.Width, t.settings.Height)
|
||||
}
|
||||
|
||||
spatial = buffer.VideoQualityToSpatialLayer(quality, mt.ToProto())
|
||||
spatial = buffer.GetSpatialLayerForVideoQuality(mimeType, quality, mt.ToProto())
|
||||
if t.settings.Fps > 0 {
|
||||
temporal = mt.GetTemporalLayerForSpatialFps(spatial, t.settings.Fps, dt.Mime())
|
||||
temporal = mt.GetTemporalLayerForSpatialFps(mimeType, spatial, t.settings.Fps)
|
||||
}
|
||||
}
|
||||
|
||||
t.settingsLock.Lock()
|
||||
if settingsVersion != t.settingsVersion {
|
||||
// a newer settings has superceded this one
|
||||
// a newer settings has superseded this one
|
||||
t.settingsLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
t.logger.Debugw("applying subscriber track settings", "settings", logger.Proto(t.settings))
|
||||
if t.settings.Disabled {
|
||||
dt.Mute(true)
|
||||
t.settingsLock.Unlock()
|
||||
@@ -302,3 +395,96 @@ func (t *SubscribedTrack) RTPSender() *webrtc.RTPSender {
|
||||
func (t *SubscribedTrack) SetRTPSender(sender *webrtc.RTPSender) {
|
||||
t.sender.Store(sender)
|
||||
}
|
||||
|
||||
// DownTrackListener implementation
|
||||
var _ sfu.DownTrackListener = (*SubscribedTrack)(nil)
|
||||
|
||||
func (t *SubscribedTrack) OnBindAndConnected() {
|
||||
if t.params.Subscriber.Hidden() {
|
||||
return
|
||||
}
|
||||
|
||||
t.params.MediaTrack.OnTrackSubscribed()
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnStatsUpdate(stat *livekit.AnalyticsStat) {
|
||||
t.params.TelemetryListener.OnTrackStats(t.statsKey, stat)
|
||||
|
||||
if cs, ok := telemetry.CondenseStat(stat); ok {
|
||||
ti := t.params.WrappedReceiver.TrackInfo()
|
||||
t.reporter.Tx(func(tx roomobs.TrackTx) {
|
||||
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(t.params.Subscriber.Kind()))
|
||||
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(t.params.Subscriber.KindDetails()))
|
||||
tx.ReportName(ti.Name)
|
||||
tx.ReportKind(roomobs.TrackKindSub)
|
||||
tx.ReportType(roomobs.TrackTypeFromProto(ti.Type))
|
||||
tx.ReportSource(roomobs.TrackSourceFromProto(ti.Source))
|
||||
tx.ReportMime(mime.NormalizeMimeType(ti.MimeType).ReporterType())
|
||||
tx.ReportLayer(roomobs.PackTrackLayer(ti.Height, ti.Width))
|
||||
tx.ReportDuration(uint16(cs.EndTime.Sub(cs.StartTime).Milliseconds()))
|
||||
tx.ReportFrames(uint16(cs.Frames))
|
||||
tx.ReportSendBytes(uint32(cs.Bytes))
|
||||
tx.ReportSendPackets(cs.Packets)
|
||||
tx.ReportPacketsLost(cs.PacketsLost)
|
||||
tx.ReportScore(stat.Score)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnMaxSubscribedLayerChanged(layer int32) {
|
||||
if t.params.OnSubscriberMaxQualityChange != nil {
|
||||
t.params.OnSubscriberMaxQualityChange(t.downTrack.SubscriberID(), t.downTrack.Mime(), layer)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnRttUpdate(rtt uint32) {
|
||||
go t.params.Subscriber.UpdateMediaRTT(rtt)
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnCodecNegotiated(codec webrtc.RTPCodecCapability) {
|
||||
if isAvailable, needsPublish := t.params.WrappedReceiver.DetermineReceiver(codec); !isAvailable || !needsPublish {
|
||||
return
|
||||
}
|
||||
|
||||
if t.params.OnSubscriberMaxQualityChange != nil || t.params.OnSubscriberAudioCodecChange != nil {
|
||||
go func() {
|
||||
mimeType := mime.NormalizeMimeType(codec.MimeType)
|
||||
switch t.params.MediaTrack.Kind() {
|
||||
case livekit.TrackType_VIDEO:
|
||||
spatial := buffer.GetSpatialLayerForVideoQuality(
|
||||
mimeType,
|
||||
livekit.VideoQuality_HIGH,
|
||||
t.params.MediaTrack.ToProto(),
|
||||
)
|
||||
if t.params.OnSubscriberMaxQualityChange != nil {
|
||||
t.params.OnSubscriberMaxQualityChange(t.downTrack.SubscriberID(), mimeType, spatial)
|
||||
}
|
||||
|
||||
case livekit.TrackType_AUDIO:
|
||||
if t.params.OnSubscriberAudioCodecChange != nil {
|
||||
t.params.OnSubscriberAudioCodecChange(t.downTrack.SubscriberID(), mimeType, true)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnDownTrackClose(isExpectedToResume bool) {
|
||||
// Cache transceiver for potential re-use on resume.
|
||||
// To ensure subscription manager does not re-subscribe before caching,
|
||||
// delete the subscribed track only after caching.
|
||||
if isExpectedToResume {
|
||||
if tr := t.downTrack.GetTransceiver(); tr != nil {
|
||||
t.params.Subscriber.CacheDownTrack(t.ID(), tr, t.downTrack.GetState())
|
||||
}
|
||||
}
|
||||
|
||||
if t.params.OnDownTrackClosed != nil {
|
||||
t.params.OnDownTrackClosed(t.params.Subscriber.ID())
|
||||
}
|
||||
t.Close(isExpectedToResume)
|
||||
}
|
||||
|
||||
func (t *SubscribedTrack) OnStreamStarted() {
|
||||
t.params.TelemetryListener.OnTrackSubscribeStreamStarted(t.params.Subscriber.ID(), t.params.MediaTrack.ToProto())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,6 @@ import (
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry/telemetryfakes"
|
||||
"github.com/livekit/livekit-server/pkg/utils"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
@@ -67,7 +66,7 @@ func TestSubscribe(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
s := sm.subscriptions["track"]
|
||||
require.True(t, s.isDesired())
|
||||
require.Eventually(t, func() bool {
|
||||
@@ -83,8 +82,8 @@ func TestSubscribe(t *testing.T) {
|
||||
require.Equal(t, "pubID", string(sm.GetSubscribedParticipants()[0]))
|
||||
|
||||
// ensure telemetry events are sent
|
||||
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
|
||||
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
|
||||
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
|
||||
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
|
||||
|
||||
// ensure bound
|
||||
setTestSubscribedTrackBound(t, s.getSubscribedTrack())
|
||||
@@ -93,7 +92,7 @@ func TestSubscribe(t *testing.T) {
|
||||
}, subSettleTimeout, subCheckInterval, "track was not bound")
|
||||
|
||||
// telemetry event should have been sent
|
||||
require.Equal(t, 1, tm.TrackSubscribedCallCount())
|
||||
require.Equal(t, 1, tl.OnTrackSubscribedCallCount())
|
||||
|
||||
time.Sleep(notFoundTimeout)
|
||||
require.False(t, failed.Load())
|
||||
@@ -127,7 +126,7 @@ func TestSubscribe(t *testing.T) {
|
||||
failed.Store(true)
|
||||
}
|
||||
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
s := sm.subscriptions["track"]
|
||||
require.Eventually(t, func() bool {
|
||||
return !s.getHasPermission()
|
||||
@@ -142,9 +141,9 @@ func TestSubscribe(t *testing.T) {
|
||||
require.Len(t, sm.GetSubscribedTracks(), 0)
|
||||
|
||||
// trackSubscribed telemetry not sent
|
||||
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
|
||||
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 0, tm.TrackSubscribedCallCount())
|
||||
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
|
||||
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 0, tl.OnTrackSubscribedCallCount())
|
||||
|
||||
// give permissions now
|
||||
resolver.lock.Lock()
|
||||
@@ -168,7 +167,7 @@ func TestSubscribe(t *testing.T) {
|
||||
failed.Store(true)
|
||||
}
|
||||
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
s := sm.subscriptions["track"]
|
||||
require.Eventually(t, func() bool {
|
||||
return !s.needsSubscribe()
|
||||
@@ -197,15 +196,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)
|
||||
@@ -242,18 +243,15 @@ func TestUnsubscribe(t *testing.T) {
|
||||
sm.lock.RLock()
|
||||
subLen := len(sm.subscriptions)
|
||||
sm.lock.RUnlock()
|
||||
if subLen != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return subLen == 0
|
||||
}, subSettleTimeout, subCheckInterval, "Track was not unsubscribed")
|
||||
|
||||
// no traces should be left
|
||||
require.Len(t, sm.GetSubscribedTracks(), 0)
|
||||
require.False(t, res.TrackChangedNotifier.HasObservers())
|
||||
|
||||
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
|
||||
require.Equal(t, 1, tm.TrackUnsubscribedCallCount())
|
||||
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
|
||||
require.Equal(t, 1, tl.OnTrackUnsubscribedCallCount())
|
||||
}
|
||||
|
||||
func TestSubscribeStatusChanged(t *testing.T) {
|
||||
@@ -271,8 +269,8 @@ func TestSubscribeStatusChanged(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
sm.SubscribeToTrack("track1")
|
||||
sm.SubscribeToTrack("track2")
|
||||
sm.SubscribeToTrack("track1", false)
|
||||
sm.SubscribeToTrack("track2", false)
|
||||
s1 := sm.subscriptions["track1"]
|
||||
s2 := sm.subscriptions["track2"]
|
||||
require.Eventually(t, func() bool {
|
||||
@@ -332,7 +330,7 @@ func TestUpdateSettingsBeforeSubscription(t *testing.T) {
|
||||
}
|
||||
sm.UpdateSubscribedTrackSettings("track", settings)
|
||||
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
|
||||
s := sm.subscriptions["track"]
|
||||
require.Eventually(t, func() bool {
|
||||
@@ -376,7 +374,7 @@ func TestSubscriptionLimits(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
s := sm.subscriptions["track"]
|
||||
require.True(t, s.isDesired())
|
||||
require.Eventually(t, func() bool {
|
||||
@@ -392,8 +390,8 @@ func TestSubscriptionLimits(t *testing.T) {
|
||||
require.Equal(t, "pubID", string(sm.GetSubscribedParticipants()[0]))
|
||||
|
||||
// ensure telemetry events are sent
|
||||
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
|
||||
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
|
||||
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
|
||||
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
|
||||
|
||||
// ensure bound
|
||||
setTestSubscribedTrackBound(t, s.getSubscribedTrack())
|
||||
@@ -402,15 +400,15 @@ func TestSubscriptionLimits(t *testing.T) {
|
||||
}, subSettleTimeout, subCheckInterval, "track was not bound")
|
||||
|
||||
// telemetry event should have been sent
|
||||
require.Equal(t, 1, tm.TrackSubscribedCallCount())
|
||||
require.Equal(t, 1, tl.OnTrackSubscribedCallCount())
|
||||
|
||||
// reach subscription limit, subscribe pending
|
||||
sm.SubscribeToTrack("track2")
|
||||
sm.SubscribeToTrack("track2", false)
|
||||
s2 := sm.subscriptions["track2"]
|
||||
time.Sleep(subscriptionTimeout * 2)
|
||||
require.True(t, s2.needsSubscribe())
|
||||
require.Equal(t, 2, tm.TrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 1, tm.TrackSubscribeFailedCallCount())
|
||||
require.Equal(t, 2, tl.OnTrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 1, tl.OnTrackSubscribeFailedCallCount())
|
||||
require.Len(t, sm.GetSubscribedTracks(), 1)
|
||||
|
||||
// unsubscribe track1, then track2 should be subscribed
|
||||
@@ -427,7 +425,7 @@ func TestSubscriptionLimits(t *testing.T) {
|
||||
require.False(t, s2.needsSubscribe())
|
||||
require.EqualValues(t, 2, subCount.Load())
|
||||
require.NotNil(t, s2.getSubscribedTrack())
|
||||
require.Equal(t, 2, tm.TrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 2, tl.OnTrackSubscribeRequestedCallCount())
|
||||
require.Len(t, sm.GetSubscribedTracks(), 1)
|
||||
|
||||
// ensure bound
|
||||
@@ -437,13 +435,13 @@ func TestSubscriptionLimits(t *testing.T) {
|
||||
}, subSettleTimeout, subCheckInterval, "track was not bound")
|
||||
|
||||
// subscribe to track1 again, which should pending
|
||||
sm.SubscribeToTrack("track")
|
||||
sm.SubscribeToTrack("track", false)
|
||||
s = sm.subscriptions["track"]
|
||||
require.True(t, s.isDesired())
|
||||
time.Sleep(subscriptionTimeout * 2)
|
||||
require.True(t, s.needsSubscribe())
|
||||
require.Equal(t, 3, tm.TrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 2, tm.TrackSubscribeFailedCallCount())
|
||||
require.Equal(t, 3, tl.OnTrackSubscribeRequestedCallCount())
|
||||
require.Equal(t, 2, tl.OnTrackSubscribeFailedCallCount())
|
||||
require.Len(t, sm.GetSubscribedTracks(), 1)
|
||||
}
|
||||
|
||||
@@ -471,7 +469,7 @@ func newTestSubscriptionManagerWithParams(params testSubscriptionParams) *Subscr
|
||||
TrackResolver: func(sub types.LocalParticipant, trackID livekit.TrackID) types.MediaResolverResult {
|
||||
return types.MediaResolverResult{}
|
||||
},
|
||||
Telemetry: &telemetryfakes.FakeTelemetryService{},
|
||||
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
|
||||
SubscriptionLimitAudio: params.SubscriptionLimitAudio,
|
||||
SubscriptionLimitVideo: params.SubscriptionLimitVideo,
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ package rtc
|
||||
import (
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
"github.com/livekit/protocol/utils"
|
||||
"github.com/livekit/protocol/utils/guid"
|
||||
|
||||
@@ -24,7 +25,13 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
|
||||
)
|
||||
|
||||
func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.ProtocolVersion, hidden bool, publisher bool) *typesfakes.FakeLocalParticipant {
|
||||
func NewMockParticipant(
|
||||
identity livekit.ParticipantIdentity,
|
||||
protocol types.ProtocolVersion,
|
||||
hidden bool,
|
||||
publisher bool,
|
||||
participantListener types.LocalParticipantListener,
|
||||
) *typesfakes.FakeLocalParticipant {
|
||||
p := &typesfakes.FakeLocalParticipant{}
|
||||
sid := guid.New(utils.ParticipantPrefix)
|
||||
p.IDReturns(livekit.ParticipantID(sid))
|
||||
@@ -49,25 +56,13 @@ func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro
|
||||
}, utils.TimedVersion(0))
|
||||
|
||||
p.SetMetadataCalls(func(m string) {
|
||||
var f func(participant types.LocalParticipant)
|
||||
if p.OnParticipantUpdateCallCount() > 0 {
|
||||
f = p.OnParticipantUpdateArgsForCall(p.OnParticipantUpdateCallCount() - 1)
|
||||
}
|
||||
if f != nil {
|
||||
f(p)
|
||||
}
|
||||
participantListener.OnParticipantUpdate(p)
|
||||
})
|
||||
updateTrack := func() {
|
||||
var f func(participant types.LocalParticipant, track types.MediaTrack)
|
||||
if p.OnTrackUpdatedCallCount() > 0 {
|
||||
f = p.OnTrackUpdatedArgsForCall(p.OnTrackUpdatedCallCount() - 1)
|
||||
}
|
||||
if f != nil {
|
||||
f(p, NewMockTrack(livekit.TrackType_VIDEO, "testcam"))
|
||||
}
|
||||
participantListener.OnTrackUpdated(p, NewMockTrack(livekit.TrackType_VIDEO, "testcam"))
|
||||
}
|
||||
|
||||
p.SetTrackMutedCalls(func(sid livekit.TrackID, muted bool, fromServer bool) *livekit.TrackInfo {
|
||||
p.SetTrackMutedCalls(func(mute *livekit.MuteTrackRequest, fromServer bool) *livekit.TrackInfo {
|
||||
updateTrack()
|
||||
return nil
|
||||
})
|
||||
@@ -75,6 +70,7 @@ func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro
|
||||
updateTrack()
|
||||
})
|
||||
p.GetLoggerReturns(logger.GetLogger())
|
||||
p.GetReporterReturns(roomobs.NewNoopParticipantSessionReporter())
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
+1241
-219
File diff suppressed because it is too large
Load Diff
@@ -39,13 +39,17 @@ type Handler interface {
|
||||
OnFullyEstablished()
|
||||
OnFailed(isShortLived bool, iceConnectionInfo *types.ICEConnectionInfo)
|
||||
OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver)
|
||||
OnDataPacket(kind livekit.DataPacket_Kind, data []byte)
|
||||
OnDataMessage(kind livekit.DataPacket_Kind, data []byte)
|
||||
OnDataMessageUnlabeled(data []byte)
|
||||
OnDataTrackMessage(data []byte, arrivalTime int64)
|
||||
OnDataSendError(err error)
|
||||
OnOffer(sd webrtc.SessionDescription) error
|
||||
OnAnswer(sd webrtc.SessionDescription) error
|
||||
OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error
|
||||
OnSetRemoteDescriptionOffer()
|
||||
OnAnswer(sd webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error
|
||||
OnNegotiationStateChanged(state NegotiationState)
|
||||
OnNegotiationFailed()
|
||||
OnStreamStateChange(update *streamallocator.StreamStateUpdate) error
|
||||
OnUnmatchedMedia(numAudios uint32, numVideos uint32) error
|
||||
}
|
||||
|
||||
type UnimplementedHandler struct{}
|
||||
@@ -57,12 +61,15 @@ func (h UnimplementedHandler) OnInitialConnected()
|
||||
func (h UnimplementedHandler) OnFullyEstablished() {}
|
||||
func (h UnimplementedHandler) OnFailed(isShortLived bool) {}
|
||||
func (h UnimplementedHandler) OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver) {}
|
||||
func (h UnimplementedHandler) OnDataPacket(kind livekit.DataPacket_Kind, data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataMessage(kind livekit.DataPacket_Kind, data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataMessageUnlabeled(data []byte) {}
|
||||
func (h UnimplementedHandler) OnDataTrackMessage(data []byte, arrivalTime int64) {}
|
||||
func (h UnimplementedHandler) OnDataSendError(err error) {}
|
||||
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription) error {
|
||||
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error {
|
||||
return ErrNoOfferHandler
|
||||
}
|
||||
func (h UnimplementedHandler) OnAnswer(sd webrtc.SessionDescription) error {
|
||||
func (h UnimplementedHandler) OnSetRemoteDescriptionOffer() {}
|
||||
func (h UnimplementedHandler) OnAnswer(sd webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error {
|
||||
return ErrNoAnswerHandler
|
||||
}
|
||||
func (h UnimplementedHandler) OnNegotiationStateChanged(state NegotiationState) {}
|
||||
@@ -70,3 +77,6 @@ func (h UnimplementedHandler) OnNegotiationFailed()
|
||||
func (h UnimplementedHandler) OnStreamStateChange(update *streamallocator.StreamStateUpdate) error {
|
||||
return nil
|
||||
}
|
||||
func (h UnimplementedHandler) OnUnmatchedMedia(numAudios uint32, numVideos uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
)
|
||||
|
||||
type FakeHandler struct {
|
||||
OnAnswerStub func(webrtc.SessionDescription) error
|
||||
OnAnswerStub func(webrtc.SessionDescription, uint32, map[string]string) error
|
||||
onAnswerMutex sync.RWMutex
|
||||
onAnswerArgsForCall []struct {
|
||||
arg1 webrtc.SessionDescription
|
||||
arg2 uint32
|
||||
arg3 map[string]string
|
||||
}
|
||||
onAnswerReturns struct {
|
||||
result1 error
|
||||
@@ -23,17 +25,28 @@ type FakeHandler struct {
|
||||
onAnswerReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
OnDataPacketStub func(livekit.DataPacket_Kind, []byte)
|
||||
onDataPacketMutex sync.RWMutex
|
||||
onDataPacketArgsForCall []struct {
|
||||
OnDataMessageStub func(livekit.DataPacket_Kind, []byte)
|
||||
onDataMessageMutex sync.RWMutex
|
||||
onDataMessageArgsForCall []struct {
|
||||
arg1 livekit.DataPacket_Kind
|
||||
arg2 []byte
|
||||
}
|
||||
OnDataMessageUnlabeledStub func([]byte)
|
||||
onDataMessageUnlabeledMutex sync.RWMutex
|
||||
onDataMessageUnlabeledArgsForCall []struct {
|
||||
arg1 []byte
|
||||
}
|
||||
OnDataSendErrorStub func(error)
|
||||
onDataSendErrorMutex sync.RWMutex
|
||||
onDataSendErrorArgsForCall []struct {
|
||||
arg1 error
|
||||
}
|
||||
OnDataTrackMessageStub func([]byte, int64)
|
||||
onDataTrackMessageMutex sync.RWMutex
|
||||
onDataTrackMessageArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 int64
|
||||
}
|
||||
OnFailedStub func(bool, *types.ICEConnectionInfo)
|
||||
onFailedMutex sync.RWMutex
|
||||
onFailedArgsForCall []struct {
|
||||
@@ -69,10 +82,12 @@ type FakeHandler struct {
|
||||
onNegotiationStateChangedArgsForCall []struct {
|
||||
arg1 transport.NegotiationState
|
||||
}
|
||||
OnOfferStub func(webrtc.SessionDescription) error
|
||||
OnOfferStub func(webrtc.SessionDescription, uint32, map[string]string) error
|
||||
onOfferMutex sync.RWMutex
|
||||
onOfferArgsForCall []struct {
|
||||
arg1 webrtc.SessionDescription
|
||||
arg2 uint32
|
||||
arg3 map[string]string
|
||||
}
|
||||
onOfferReturns struct {
|
||||
result1 error
|
||||
@@ -80,6 +95,10 @@ type FakeHandler struct {
|
||||
onOfferReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
OnSetRemoteDescriptionOfferStub func()
|
||||
onSetRemoteDescriptionOfferMutex sync.RWMutex
|
||||
onSetRemoteDescriptionOfferArgsForCall []struct {
|
||||
}
|
||||
OnStreamStateChangeStub func(*streamallocator.StreamStateUpdate) error
|
||||
onStreamStateChangeMutex sync.RWMutex
|
||||
onStreamStateChangeArgsForCall []struct {
|
||||
@@ -97,22 +116,36 @@ type FakeHandler struct {
|
||||
arg1 *webrtc.TrackRemote
|
||||
arg2 *webrtc.RTPReceiver
|
||||
}
|
||||
OnUnmatchedMediaStub func(uint32, uint32) error
|
||||
onUnmatchedMediaMutex sync.RWMutex
|
||||
onUnmatchedMediaArgsForCall []struct {
|
||||
arg1 uint32
|
||||
arg2 uint32
|
||||
}
|
||||
onUnmatchedMediaReturns struct {
|
||||
result1 error
|
||||
}
|
||||
onUnmatchedMediaReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnAnswer(arg1 webrtc.SessionDescription) error {
|
||||
func (fake *FakeHandler) OnAnswer(arg1 webrtc.SessionDescription, arg2 uint32, arg3 map[string]string) error {
|
||||
fake.onAnswerMutex.Lock()
|
||||
ret, specificReturn := fake.onAnswerReturnsOnCall[len(fake.onAnswerArgsForCall)]
|
||||
fake.onAnswerArgsForCall = append(fake.onAnswerArgsForCall, struct {
|
||||
arg1 webrtc.SessionDescription
|
||||
}{arg1})
|
||||
arg2 uint32
|
||||
arg3 map[string]string
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnAnswerStub
|
||||
fakeReturns := fake.onAnswerReturns
|
||||
fake.recordInvocation("OnAnswer", []interface{}{arg1})
|
||||
fake.recordInvocation("OnAnswer", []interface{}{arg1, arg2, arg3})
|
||||
fake.onAnswerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
return stub(arg1, arg2, arg3)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
@@ -126,17 +159,17 @@ func (fake *FakeHandler) OnAnswerCallCount() int {
|
||||
return len(fake.onAnswerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnAnswerCalls(stub func(webrtc.SessionDescription) error) {
|
||||
func (fake *FakeHandler) OnAnswerCalls(stub func(webrtc.SessionDescription, uint32, map[string]string) error) {
|
||||
fake.onAnswerMutex.Lock()
|
||||
defer fake.onAnswerMutex.Unlock()
|
||||
fake.OnAnswerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnAnswerArgsForCall(i int) webrtc.SessionDescription {
|
||||
func (fake *FakeHandler) OnAnswerArgsForCall(i int) (webrtc.SessionDescription, uint32, map[string]string) {
|
||||
fake.onAnswerMutex.RLock()
|
||||
defer fake.onAnswerMutex.RUnlock()
|
||||
argsForCall := fake.onAnswerArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnAnswerReturns(result1 error) {
|
||||
@@ -162,44 +195,81 @@ func (fake *FakeHandler) OnAnswerReturnsOnCall(i int, result1 error) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataPacket(arg1 livekit.DataPacket_Kind, arg2 []byte) {
|
||||
func (fake *FakeHandler) OnDataMessage(arg1 livekit.DataPacket_Kind, arg2 []byte) {
|
||||
var arg2Copy []byte
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]byte, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.onDataPacketMutex.Lock()
|
||||
fake.onDataPacketArgsForCall = append(fake.onDataPacketArgsForCall, struct {
|
||||
fake.onDataMessageMutex.Lock()
|
||||
fake.onDataMessageArgsForCall = append(fake.onDataMessageArgsForCall, struct {
|
||||
arg1 livekit.DataPacket_Kind
|
||||
arg2 []byte
|
||||
}{arg1, arg2Copy})
|
||||
stub := fake.OnDataPacketStub
|
||||
fake.recordInvocation("OnDataPacket", []interface{}{arg1, arg2Copy})
|
||||
fake.onDataPacketMutex.Unlock()
|
||||
stub := fake.OnDataMessageStub
|
||||
fake.recordInvocation("OnDataMessage", []interface{}{arg1, arg2Copy})
|
||||
fake.onDataMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataPacketStub(arg1, arg2)
|
||||
fake.OnDataMessageStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataPacketCallCount() int {
|
||||
fake.onDataPacketMutex.RLock()
|
||||
defer fake.onDataPacketMutex.RUnlock()
|
||||
return len(fake.onDataPacketArgsForCall)
|
||||
func (fake *FakeHandler) OnDataMessageCallCount() int {
|
||||
fake.onDataMessageMutex.RLock()
|
||||
defer fake.onDataMessageMutex.RUnlock()
|
||||
return len(fake.onDataMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataPacketCalls(stub func(livekit.DataPacket_Kind, []byte)) {
|
||||
fake.onDataPacketMutex.Lock()
|
||||
defer fake.onDataPacketMutex.Unlock()
|
||||
fake.OnDataPacketStub = stub
|
||||
func (fake *FakeHandler) OnDataMessageCalls(stub func(livekit.DataPacket_Kind, []byte)) {
|
||||
fake.onDataMessageMutex.Lock()
|
||||
defer fake.onDataMessageMutex.Unlock()
|
||||
fake.OnDataMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataPacketArgsForCall(i int) (livekit.DataPacket_Kind, []byte) {
|
||||
fake.onDataPacketMutex.RLock()
|
||||
defer fake.onDataPacketMutex.RUnlock()
|
||||
argsForCall := fake.onDataPacketArgsForCall[i]
|
||||
func (fake *FakeHandler) OnDataMessageArgsForCall(i int) (livekit.DataPacket_Kind, []byte) {
|
||||
fake.onDataMessageMutex.RLock()
|
||||
defer fake.onDataMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataMessageUnlabeled(arg1 []byte) {
|
||||
var arg1Copy []byte
|
||||
if arg1 != nil {
|
||||
arg1Copy = make([]byte, len(arg1))
|
||||
copy(arg1Copy, arg1)
|
||||
}
|
||||
fake.onDataMessageUnlabeledMutex.Lock()
|
||||
fake.onDataMessageUnlabeledArgsForCall = append(fake.onDataMessageUnlabeledArgsForCall, struct {
|
||||
arg1 []byte
|
||||
}{arg1Copy})
|
||||
stub := fake.OnDataMessageUnlabeledStub
|
||||
fake.recordInvocation("OnDataMessageUnlabeled", []interface{}{arg1Copy})
|
||||
fake.onDataMessageUnlabeledMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataMessageUnlabeledStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataMessageUnlabeledCallCount() int {
|
||||
fake.onDataMessageUnlabeledMutex.RLock()
|
||||
defer fake.onDataMessageUnlabeledMutex.RUnlock()
|
||||
return len(fake.onDataMessageUnlabeledArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataMessageUnlabeledCalls(stub func([]byte)) {
|
||||
fake.onDataMessageUnlabeledMutex.Lock()
|
||||
defer fake.onDataMessageUnlabeledMutex.Unlock()
|
||||
fake.OnDataMessageUnlabeledStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataMessageUnlabeledArgsForCall(i int) []byte {
|
||||
fake.onDataMessageUnlabeledMutex.RLock()
|
||||
defer fake.onDataMessageUnlabeledMutex.RUnlock()
|
||||
argsForCall := fake.onDataMessageUnlabeledArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataSendError(arg1 error) {
|
||||
fake.onDataSendErrorMutex.Lock()
|
||||
fake.onDataSendErrorArgsForCall = append(fake.onDataSendErrorArgsForCall, struct {
|
||||
@@ -232,6 +302,44 @@ func (fake *FakeHandler) OnDataSendErrorArgsForCall(i int) error {
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessage(arg1 []byte, arg2 int64) {
|
||||
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
|
||||
arg2 int64
|
||||
}{arg1Copy, arg2})
|
||||
stub := fake.OnDataTrackMessageStub
|
||||
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1Copy, arg2})
|
||||
fake.onDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackMessageStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageCallCount() int {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.onDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageCalls(stub func([]byte, int64)) {
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
defer fake.onDataTrackMessageMutex.Unlock()
|
||||
fake.OnDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnDataTrackMessageArgsForCall(i int) ([]byte, int64) {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnFailed(arg1 bool, arg2 *types.ICEConnectionInfo) {
|
||||
fake.onFailedMutex.Lock()
|
||||
fake.onFailedArgsForCall = append(fake.onFailedArgsForCall, struct {
|
||||
@@ -431,18 +539,20 @@ func (fake *FakeHandler) OnNegotiationStateChangedArgsForCall(i int) transport.N
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnOffer(arg1 webrtc.SessionDescription) error {
|
||||
func (fake *FakeHandler) OnOffer(arg1 webrtc.SessionDescription, arg2 uint32, arg3 map[string]string) error {
|
||||
fake.onOfferMutex.Lock()
|
||||
ret, specificReturn := fake.onOfferReturnsOnCall[len(fake.onOfferArgsForCall)]
|
||||
fake.onOfferArgsForCall = append(fake.onOfferArgsForCall, struct {
|
||||
arg1 webrtc.SessionDescription
|
||||
}{arg1})
|
||||
arg2 uint32
|
||||
arg3 map[string]string
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnOfferStub
|
||||
fakeReturns := fake.onOfferReturns
|
||||
fake.recordInvocation("OnOffer", []interface{}{arg1})
|
||||
fake.recordInvocation("OnOffer", []interface{}{arg1, arg2, arg3})
|
||||
fake.onOfferMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
return stub(arg1, arg2, arg3)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
@@ -456,17 +566,17 @@ func (fake *FakeHandler) OnOfferCallCount() int {
|
||||
return len(fake.onOfferArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnOfferCalls(stub func(webrtc.SessionDescription) error) {
|
||||
func (fake *FakeHandler) OnOfferCalls(stub func(webrtc.SessionDescription, uint32, map[string]string) error) {
|
||||
fake.onOfferMutex.Lock()
|
||||
defer fake.onOfferMutex.Unlock()
|
||||
fake.OnOfferStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnOfferArgsForCall(i int) webrtc.SessionDescription {
|
||||
func (fake *FakeHandler) OnOfferArgsForCall(i int) (webrtc.SessionDescription, uint32, map[string]string) {
|
||||
fake.onOfferMutex.RLock()
|
||||
defer fake.onOfferMutex.RUnlock()
|
||||
argsForCall := fake.onOfferArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnOfferReturns(result1 error) {
|
||||
@@ -492,6 +602,30 @@ func (fake *FakeHandler) OnOfferReturnsOnCall(i int, result1 error) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnSetRemoteDescriptionOffer() {
|
||||
fake.onSetRemoteDescriptionOfferMutex.Lock()
|
||||
fake.onSetRemoteDescriptionOfferArgsForCall = append(fake.onSetRemoteDescriptionOfferArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.OnSetRemoteDescriptionOfferStub
|
||||
fake.recordInvocation("OnSetRemoteDescriptionOffer", []interface{}{})
|
||||
fake.onSetRemoteDescriptionOfferMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnSetRemoteDescriptionOfferStub()
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnSetRemoteDescriptionOfferCallCount() int {
|
||||
fake.onSetRemoteDescriptionOfferMutex.RLock()
|
||||
defer fake.onSetRemoteDescriptionOfferMutex.RUnlock()
|
||||
return len(fake.onSetRemoteDescriptionOfferArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnSetRemoteDescriptionOfferCalls(stub func()) {
|
||||
fake.onSetRemoteDescriptionOfferMutex.Lock()
|
||||
defer fake.onSetRemoteDescriptionOfferMutex.Unlock()
|
||||
fake.OnSetRemoteDescriptionOfferStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnStreamStateChange(arg1 *streamallocator.StreamStateUpdate) error {
|
||||
fake.onStreamStateChangeMutex.Lock()
|
||||
ret, specificReturn := fake.onStreamStateChangeReturnsOnCall[len(fake.onStreamStateChangeArgsForCall)]
|
||||
@@ -586,33 +720,71 @@ func (fake *FakeHandler) OnTrackArgsForCall(i int) (*webrtc.TrackRemote, *webrtc
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMedia(arg1 uint32, arg2 uint32) error {
|
||||
fake.onUnmatchedMediaMutex.Lock()
|
||||
ret, specificReturn := fake.onUnmatchedMediaReturnsOnCall[len(fake.onUnmatchedMediaArgsForCall)]
|
||||
fake.onUnmatchedMediaArgsForCall = append(fake.onUnmatchedMediaArgsForCall, struct {
|
||||
arg1 uint32
|
||||
arg2 uint32
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnUnmatchedMediaStub
|
||||
fakeReturns := fake.onUnmatchedMediaReturns
|
||||
fake.recordInvocation("OnUnmatchedMedia", []interface{}{arg1, arg2})
|
||||
fake.onUnmatchedMediaMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMediaCallCount() int {
|
||||
fake.onUnmatchedMediaMutex.RLock()
|
||||
defer fake.onUnmatchedMediaMutex.RUnlock()
|
||||
return len(fake.onUnmatchedMediaArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMediaCalls(stub func(uint32, uint32) error) {
|
||||
fake.onUnmatchedMediaMutex.Lock()
|
||||
defer fake.onUnmatchedMediaMutex.Unlock()
|
||||
fake.OnUnmatchedMediaStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMediaArgsForCall(i int) (uint32, uint32) {
|
||||
fake.onUnmatchedMediaMutex.RLock()
|
||||
defer fake.onUnmatchedMediaMutex.RUnlock()
|
||||
argsForCall := fake.onUnmatchedMediaArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMediaReturns(result1 error) {
|
||||
fake.onUnmatchedMediaMutex.Lock()
|
||||
defer fake.onUnmatchedMediaMutex.Unlock()
|
||||
fake.OnUnmatchedMediaStub = nil
|
||||
fake.onUnmatchedMediaReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) OnUnmatchedMediaReturnsOnCall(i int, result1 error) {
|
||||
fake.onUnmatchedMediaMutex.Lock()
|
||||
defer fake.onUnmatchedMediaMutex.Unlock()
|
||||
fake.OnUnmatchedMediaStub = nil
|
||||
if fake.onUnmatchedMediaReturnsOnCall == nil {
|
||||
fake.onUnmatchedMediaReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.onUnmatchedMediaReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeHandler) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.onAnswerMutex.RLock()
|
||||
defer fake.onAnswerMutex.RUnlock()
|
||||
fake.onDataPacketMutex.RLock()
|
||||
defer fake.onDataPacketMutex.RUnlock()
|
||||
fake.onDataSendErrorMutex.RLock()
|
||||
defer fake.onDataSendErrorMutex.RUnlock()
|
||||
fake.onFailedMutex.RLock()
|
||||
defer fake.onFailedMutex.RUnlock()
|
||||
fake.onFullyEstablishedMutex.RLock()
|
||||
defer fake.onFullyEstablishedMutex.RUnlock()
|
||||
fake.onICECandidateMutex.RLock()
|
||||
defer fake.onICECandidateMutex.RUnlock()
|
||||
fake.onInitialConnectedMutex.RLock()
|
||||
defer fake.onInitialConnectedMutex.RUnlock()
|
||||
fake.onNegotiationFailedMutex.RLock()
|
||||
defer fake.onNegotiationFailedMutex.RUnlock()
|
||||
fake.onNegotiationStateChangedMutex.RLock()
|
||||
defer fake.onNegotiationStateChangedMutex.RUnlock()
|
||||
fake.onOfferMutex.RLock()
|
||||
defer fake.onOfferMutex.RUnlock()
|
||||
fake.onStreamStateChangeMutex.RLock()
|
||||
defer fake.onStreamStateChangeMutex.RUnlock()
|
||||
fake.onTrackMutex.RLock()
|
||||
defer fake.onTrackMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -28,17 +28,15 @@ import (
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/transport"
|
||||
"github.com/livekit/livekit-server/pkg/rtc/transport/transportfakes"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/testutils"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
func TestMissingAnswerDuringICERestart(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
}
|
||||
|
||||
paramsA := params
|
||||
@@ -70,7 +68,7 @@ func TestMissingAnswerDuringICERestart(t *testing.T) {
|
||||
|
||||
// offer again, but missed
|
||||
var offerReceived atomic.Bool
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, _offerId uint32, _midToTrackID map[string]string) error {
|
||||
require.Equal(t, webrtc.SignalingStateHaveLocalOffer, transportA.pc.SignalingState())
|
||||
require.Equal(t, transport.NegotiationStateRemote, negotiationState.Load().(transport.NegotiationState))
|
||||
offerReceived.Store(true)
|
||||
@@ -91,10 +89,8 @@ func TestMissingAnswerDuringICERestart(t *testing.T) {
|
||||
|
||||
func TestNegotiationTiming(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
}
|
||||
|
||||
paramsA := params
|
||||
@@ -116,9 +112,16 @@ func TestNegotiationTiming(t *testing.T) {
|
||||
require.False(t, transportB.IsEstablished())
|
||||
|
||||
handleICEExchange(t, transportA, transportB, handlerA, handlerB)
|
||||
offer := atomic.Value{}
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
offer.Store(&sd)
|
||||
firstOffer := atomic.Value{}
|
||||
firstOfferId := atomic.Uint32{}
|
||||
secondOffer := atomic.Value{}
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
if _, ok := firstOffer.Load().(*webrtc.SessionDescription); !ok {
|
||||
firstOffer.Store(&sd)
|
||||
firstOfferId.Store(offerId)
|
||||
} else {
|
||||
secondOffer.Store(&sd)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -161,15 +164,22 @@ func TestNegotiationTiming(t *testing.T) {
|
||||
return state == transport.NegotiationStateRetry
|
||||
}, 10*time.Second, 10*time.Millisecond, "negotiation state does not match NegotiateStateRetry")
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
actualOffer, ok := offer.Load().(*webrtc.SessionDescription)
|
||||
require.True(t, ok)
|
||||
require.Eventually(t, func() bool {
|
||||
_, ok := firstOffer.Load().(*webrtc.SessionDescription)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if firstOfferId.Load() == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, 10*time.Second, 10*time.Millisecond, "first offer not received yet")
|
||||
|
||||
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
|
||||
transportA.HandleRemoteDescription(answer)
|
||||
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
|
||||
transportA.HandleRemoteDescription(answer, answerId)
|
||||
return nil
|
||||
})
|
||||
transportB.HandleRemoteDescription(*actualOffer)
|
||||
transportB.HandleRemoteDescription(*firstOffer.Load().(*webrtc.SessionDescription), firstOfferId.Load())
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return transportA.IsEstablished()
|
||||
@@ -178,11 +188,18 @@ func TestNegotiationTiming(t *testing.T) {
|
||||
return transportB.IsEstablished()
|
||||
}, 10*time.Second, time.Millisecond*10, "transportB is not established")
|
||||
|
||||
// it should still be negotiating again
|
||||
require.Equal(t, transport.NegotiationStateRemote, negotiationState.Load().(transport.NegotiationState))
|
||||
offer2, ok := offer.Load().(*webrtc.SessionDescription)
|
||||
// offerer should send another offer after processing the answer
|
||||
// as there were forced negotiations a couple of time above
|
||||
require.Eventually(t, func() bool {
|
||||
state, ok := negotiationState.Load().(transport.NegotiationState)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return state == transport.NegotiationStateRemote
|
||||
}, 10*time.Second, 10*time.Millisecond, "negotiation state does not match NegotiateStateRemote")
|
||||
_, ok := secondOffer.Load().(*webrtc.SessionDescription)
|
||||
require.True(t, ok)
|
||||
require.False(t, offer2 == actualOffer)
|
||||
|
||||
transportA.Close()
|
||||
transportB.Close()
|
||||
@@ -190,10 +207,8 @@ func TestNegotiationTiming(t *testing.T) {
|
||||
|
||||
func TestFirstOfferMissedDuringICERestart(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
}
|
||||
|
||||
paramsA := params
|
||||
@@ -216,7 +231,7 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
|
||||
|
||||
// first offer missed
|
||||
var firstOfferReceived atomic.Bool
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, _offerId uint32, _midToTrackID map[string]string) error {
|
||||
firstOfferReceived.Store(true)
|
||||
return nil
|
||||
})
|
||||
@@ -228,13 +243,13 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
|
||||
// set offer/answer with restart ICE, will negotiate twice,
|
||||
// first one is recover from missed offer
|
||||
// second one is restartICE
|
||||
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
|
||||
transportA.HandleRemoteDescription(answer)
|
||||
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
|
||||
transportA.HandleRemoteDescription(answer, answerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
var offerCount atomic.Int32
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
offerCount.Inc()
|
||||
|
||||
// the second offer is a ice restart offer, so we wait transportB complete the ice gathering
|
||||
@@ -244,7 +259,7 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
|
||||
}, 10*time.Second, time.Millisecond*10)
|
||||
}
|
||||
|
||||
transportB.HandleRemoteDescription(sd)
|
||||
transportB.HandleRemoteDescription(sd, offerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -264,10 +279,8 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
|
||||
|
||||
func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
}
|
||||
|
||||
paramsA := params
|
||||
@@ -290,17 +303,17 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
|
||||
|
||||
// first answer missed
|
||||
var firstAnswerReceived atomic.Bool
|
||||
handlerB.OnAnswerCalls(func(sd webrtc.SessionDescription) error {
|
||||
handlerB.OnAnswerCalls(func(sd webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
|
||||
if firstAnswerReceived.Load() {
|
||||
transportA.HandleRemoteDescription(sd)
|
||||
transportA.HandleRemoteDescription(sd, answerId)
|
||||
} else {
|
||||
// do not send first answer so that remote misses the first answer
|
||||
firstAnswerReceived.Store(true)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
transportB.HandleRemoteDescription(sd)
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
transportB.HandleRemoteDescription(sd, offerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -313,7 +326,7 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
|
||||
// first one is recover from missed offer
|
||||
// second one is restartICE
|
||||
var offerCount atomic.Int32
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
offerCount.Inc()
|
||||
|
||||
// the second offer is a ice restart offer, so we wait for transportB to complete ICE gathering
|
||||
@@ -323,7 +336,7 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
|
||||
}, 10*time.Second, time.Millisecond*10)
|
||||
}
|
||||
|
||||
transportB.HandleRemoteDescription(sd)
|
||||
transportB.HandleRemoteDescription(sd, offerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -343,10 +356,8 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
|
||||
|
||||
func TestNegotiationFailed(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
Config: &WebRTCConfig{},
|
||||
IsOfferer: true,
|
||||
}
|
||||
|
||||
paramsA := params
|
||||
@@ -371,7 +382,9 @@ func TestNegotiationFailed(t *testing.T) {
|
||||
connectTransports(t, transportA, transportB, handlerA, handlerB, false, 1, 1)
|
||||
|
||||
// reset OnOffer to force a negotiation failure
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error { return nil })
|
||||
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
return nil
|
||||
})
|
||||
var failed atomic.Int32
|
||||
handlerA.OnNegotiationFailedCalls(func() {
|
||||
failed.Inc()
|
||||
@@ -386,10 +399,8 @@ func TestNegotiationFailed(t *testing.T) {
|
||||
|
||||
func TestFilteringCandidates(t *testing.T) {
|
||||
params := TransportParams{
|
||||
ParticipantID: "id",
|
||||
ParticipantIdentity: "identity",
|
||||
Config: &WebRTCConfig{},
|
||||
EnabledCodecs: []*livekit.Codec{
|
||||
Config: &WebRTCConfig{},
|
||||
EnabledPublishCodecs: []*livekit.Codec{
|
||||
{Mime: mime.MimeTypeOpus.String()},
|
||||
{Mime: mime.MimeTypeVP8.String()},
|
||||
{Mime: mime.MimeTypeH264.String()},
|
||||
@@ -527,15 +538,15 @@ func handleICEExchange(t *testing.T, a, b *PCTransport, ah, bh *transportfakes.F
|
||||
func connectTransports(t *testing.T, offerer, answerer *PCTransport, offererHandler, answererHandler *transportfakes.FakeHandler, isICERestart bool, expectedOfferCount int32, expectedAnswerCount int32) {
|
||||
var offerCount atomic.Int32
|
||||
var answerCount atomic.Int32
|
||||
answererHandler.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
|
||||
answererHandler.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
|
||||
answerCount.Inc()
|
||||
offerer.HandleRemoteDescription(answer)
|
||||
offerer.HandleRemoteDescription(answer, answerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
offererHandler.OnOfferCalls(func(offer webrtc.SessionDescription) error {
|
||||
offererHandler.OnOfferCalls(func(offer webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
|
||||
offerCount.Inc()
|
||||
answerer.HandleRemoteDescription(offer)
|
||||
answerer.HandleRemoteDescription(offer, offerId)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -606,7 +617,7 @@ func TestConfigureAudioTransceiver(t *testing.T) {
|
||||
tr, err := pc.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
|
||||
require.NoError(t, err)
|
||||
|
||||
configureAudioTransceiver(tr, testcase.stereo, testcase.nack)
|
||||
configureSenderAudio(tr, testcase.stereo, testcase.nack)
|
||||
codecs := tr.Sender().GetParameters().Codecs
|
||||
for _, codec := range codecs {
|
||||
if mime.IsMimeTypeStringOpus(codec.MimeType) {
|
||||
@@ -624,3 +635,145 @@ func TestConfigureAudioTransceiver(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// In single-PC mode the publisher PC carries both publish and subscribe
|
||||
// directions. If the MediaEngine were built only from the publish codec list,
|
||||
// the SDP offer would not advertise some codecs in the m-section even though
|
||||
// the subscribe direction is supposed to support it. This regression-tests
|
||||
// the union behavior in newPeerConnection: build the MediaEngine from publish +
|
||||
// subscribe codec lists.
|
||||
func TestSinglePCMediaEngineUnionsCodecs(t *testing.T) {
|
||||
videoMSectionCodecs := func(transport *PCTransport) []string {
|
||||
_, err := transport.pc.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo)
|
||||
require.NoError(t, err)
|
||||
offer, err := transport.pc.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
parsed, err := offer.Unmarshal()
|
||||
require.NoError(t, err)
|
||||
var rtpmaps []string
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
if m.MediaName.Media != "video" {
|
||||
continue
|
||||
}
|
||||
for _, a := range m.Attributes {
|
||||
if a.Key == "rtpmap" {
|
||||
rtpmaps = append(rtpmaps, a.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rtpmaps
|
||||
}
|
||||
|
||||
sdpHasH264 := func(rtpmaps []string) bool {
|
||||
for _, r := range rtpmaps {
|
||||
if strings.Contains(r, "H264/") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
publishOnly := []*livekit.Codec{
|
||||
{Mime: mime.MimeTypeOpus.String()},
|
||||
{Mime: mime.MimeTypeVP8.String()},
|
||||
}
|
||||
subscribeOnly := []*livekit.Codec{
|
||||
{Mime: mime.MimeTypeOpus.String()},
|
||||
{Mime: mime.MimeTypeVP8.String()},
|
||||
{Mime: mime.MimeTypeH264.String()},
|
||||
}
|
||||
|
||||
// Control: only publish codecs set (dual-PC publisher PC). H.264 absent.
|
||||
dualPC, err := NewPCTransport(TransportParams{
|
||||
Config: &WebRTCConfig{},
|
||||
EnabledPublishCodecs: publishOnly,
|
||||
Handler: &transportfakes.FakeHandler{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.False(t, sdpHasH264(videoMSectionCodecs(dualPC)),
|
||||
"dual-PC publisher must not advertise H.264 when it's stripped from the publish list")
|
||||
|
||||
// Single-PC publisher PC: both lists set. H.264 must appear.
|
||||
singlePC, err := NewPCTransport(TransportParams{
|
||||
Config: &WebRTCConfig{},
|
||||
EnabledPublishCodecs: publishOnly,
|
||||
EnabledSubscribeCodecs: subscribeOnly,
|
||||
IsSendSide: true,
|
||||
Handler: &transportfakes.FakeHandler{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, sdpHasH264(videoMSectionCodecs(singlePC)),
|
||||
"single-PC publisher must advertise H.264 from the subscribe list even when it's stripped from the publish list")
|
||||
}
|
||||
|
||||
// Regression test for restrictReceiverCodecsToPublishList: subscribe-only
|
||||
// codecs (e.g., H.264) registered for subscriptions must not leak into the
|
||||
// recv-side m-section of an answer, or the peer could publish them.
|
||||
func TestSinglePCAnswerStripsSubscribeOnlyCodecsFromRecvSide(t *testing.T) {
|
||||
publishCodecs := []*livekit.Codec{
|
||||
{Mime: mime.MimeTypeOpus.String()},
|
||||
{Mime: mime.MimeTypeVP8.String()},
|
||||
}
|
||||
subscribeCodecs := []*livekit.Codec{
|
||||
{Mime: mime.MimeTypeOpus.String()},
|
||||
{Mime: mime.MimeTypeVP8.String()},
|
||||
{Mime: mime.MimeTypeH264.String()},
|
||||
}
|
||||
|
||||
handler := &transportfakes.FakeHandler{}
|
||||
server, err := NewPCTransport(TransportParams{
|
||||
Config: &WebRTCConfig{},
|
||||
EnabledPublishCodecs: publishCodecs,
|
||||
EnabledSubscribeCodecs: subscribeCodecs,
|
||||
IsSendSide: true,
|
||||
Handler: handler,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer server.Close()
|
||||
|
||||
var clientME webrtc.MediaEngine
|
||||
require.NoError(t, registerCodecs(&clientME, subscribeCodecs, RTCPFeedbackConfig{}, false))
|
||||
client, err := webrtc.NewAPI(webrtc.WithMediaEngine(&clientME)).NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer client.Close()
|
||||
|
||||
_, err = client.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RTPTransceiverInit{
|
||||
Direction: webrtc.RTPTransceiverDirectionSendonly,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
offer, err := client.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, offer.SDP, "H264/", "offer must advertise H.264")
|
||||
require.NoError(t, client.SetLocalDescription(offer))
|
||||
|
||||
var answer atomic.Pointer[webrtc.SessionDescription]
|
||||
handler.OnAnswerCalls(func(sd webrtc.SessionDescription, _ uint32, _ map[string]string) error {
|
||||
answer.Store(&sd)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, server.HandleRemoteDescription(*client.LocalDescription(), 1))
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return answer.Load() != nil
|
||||
}, 5*time.Second, 10*time.Millisecond, "server did not produce answer")
|
||||
|
||||
parsed, err := answer.Load().Unmarshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
var videoSection *sdp.MediaDescription
|
||||
for _, m := range parsed.MediaDescriptions {
|
||||
if m.MediaName.Media == "video" {
|
||||
videoSection = m
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, videoSection, "answer missing video m-section")
|
||||
|
||||
for _, a := range videoSection.Attributes {
|
||||
if a.Key != "rtpmap" {
|
||||
continue
|
||||
}
|
||||
require.NotContains(t, a.Value, "H264/",
|
||||
"answer must not advertise H.264 in recv-side m-section: %s", a.Value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ import (
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/datachannel"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/interceptor"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/pacer"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -71,43 +71,33 @@ func (h TransportManagerTransportHandler) OnFailed(isShortLived bool, iceConnect
|
||||
|
||||
// -------------------------------
|
||||
|
||||
type TransportManagerPublisherTransportHandler struct {
|
||||
TransportManagerTransportHandler
|
||||
}
|
||||
|
||||
func (h TransportManagerPublisherTransportHandler) OnAnswer(sd webrtc.SessionDescription) error {
|
||||
h.t.lastPublisherAnswer.Store(sd)
|
||||
return h.Handler.OnAnswer(sd)
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
|
||||
type TransportManagerParams struct {
|
||||
Identity livekit.ParticipantIdentity
|
||||
SID livekit.ParticipantID
|
||||
SubscriberAsPrimary bool
|
||||
Config *WebRTCConfig
|
||||
Twcc *twcc.Responder
|
||||
ProtocolVersion types.ProtocolVersion
|
||||
CongestionControlConfig config.CongestionControlConfig
|
||||
EnabledSubscribeCodecs []*livekit.Codec
|
||||
EnabledPublishCodecs []*livekit.Codec
|
||||
SimTracks map[uint32]SimulcastTrackInfo
|
||||
ClientInfo ClientInfo
|
||||
Migration bool
|
||||
AllowTCPFallback bool
|
||||
TCPFallbackRTTThreshold int
|
||||
AllowUDPUnstableFallback bool
|
||||
TURNSEnabled bool
|
||||
AllowPlayoutDelay bool
|
||||
DataChannelMaxBufferedAmount uint64
|
||||
DatachannelSlowThreshold int
|
||||
Logger logger.Logger
|
||||
PublisherHandler transport.Handler
|
||||
SubscriberHandler transport.Handler
|
||||
DataChannelStats *telemetry.BytesTrackStats
|
||||
UseOneShotSignallingMode bool
|
||||
FireOnTrackBySdp bool
|
||||
SubscriberAsPrimary bool
|
||||
UseSinglePeerConnection bool
|
||||
Config *WebRTCConfig
|
||||
Twcc *twcc.Responder
|
||||
ProtocolVersion types.ProtocolVersion
|
||||
CongestionControlConfig config.CongestionControlConfig
|
||||
EnabledSubscribeCodecs []*livekit.Codec
|
||||
EnabledPublishCodecs []*livekit.Codec
|
||||
SimTracks map[uint32]interceptor.SimulcastTrackInfo
|
||||
ClientInfo ClientInfo
|
||||
Migration bool
|
||||
AllowTCPFallback bool
|
||||
TCPFallbackRTTThreshold int
|
||||
AllowUDPUnstableFallback bool
|
||||
TURNSEnabled bool
|
||||
AllowPlayoutDelay bool
|
||||
DataChannelMaxBufferedAmount uint64
|
||||
DatachannelSlowThreshold int
|
||||
DatachannelLossyTargetLatency time.Duration
|
||||
Logger logger.Logger
|
||||
PublisherHandler transport.Handler
|
||||
SubscriberHandler transport.Handler
|
||||
DataChannelStats *BytesTrackStats
|
||||
UseOneShotSignallingMode bool
|
||||
FireOnTrackBySdp bool
|
||||
EnableDataTracks bool
|
||||
}
|
||||
|
||||
type TransportManager struct {
|
||||
@@ -124,9 +114,8 @@ type TransportManager struct {
|
||||
signalSourceValid atomic.Bool
|
||||
|
||||
pendingOfferPublisher *webrtc.SessionDescription
|
||||
pendingOfferIdPublisher uint32
|
||||
pendingDataChannelsPublisher []*livekit.DataChannelInfo
|
||||
lastPublisherAnswer atomic.Value
|
||||
lastPublisherOffer atomic.Value
|
||||
iceConfig *livekit.ICEConfig
|
||||
|
||||
mediaLossProxy *MediaLossProxy
|
||||
@@ -135,7 +124,8 @@ type TransportManager struct {
|
||||
|
||||
onICEConfigChanged func(iceConfig *livekit.ICEConfig)
|
||||
|
||||
droppedBySlowReaderCount atomic.Uint32
|
||||
dataChannelSendErrorDroppedBySlowReaderCount atomic.Uint32
|
||||
dataChannelSendErrorCount atomic.Uint32
|
||||
}
|
||||
|
||||
func NewTransportManager(params TransportManagerParams) (*TransportManager, error) {
|
||||
@@ -150,53 +140,67 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
|
||||
t.mediaLossProxy.OnMediaLossUpdate(t.onMediaLossUpdate)
|
||||
|
||||
lgr := LoggerWithPCTarget(params.Logger, livekit.SignalTarget_PUBLISHER)
|
||||
// In single-PC mode the one PC carries both directions, so it needs both
|
||||
// codec lists registered on its MediaEngine. In dual-PC mode the publisher
|
||||
// PC is recvonly, so subscribe codecs are left nil.
|
||||
var publisherSubscribeCodecs []*livekit.Codec
|
||||
if params.UseSinglePeerConnection || params.UseOneShotSignallingMode {
|
||||
publisherSubscribeCodecs = params.EnabledSubscribeCodecs
|
||||
}
|
||||
publisher, err := NewPCTransport(TransportParams{
|
||||
ParticipantID: params.SID,
|
||||
ParticipantIdentity: params.Identity,
|
||||
ProtocolVersion: params.ProtocolVersion,
|
||||
Config: params.Config,
|
||||
Twcc: params.Twcc,
|
||||
DirectionConfig: params.Config.Publisher,
|
||||
CongestionControlConfig: params.CongestionControlConfig,
|
||||
EnabledCodecs: params.EnabledPublishCodecs,
|
||||
Logger: lgr,
|
||||
SimTracks: params.SimTracks,
|
||||
ClientInfo: params.ClientInfo,
|
||||
Transport: livekit.SignalTarget_PUBLISHER,
|
||||
Handler: TransportManagerPublisherTransportHandler{TransportManagerTransportHandler{params.PublisherHandler, t, lgr}},
|
||||
UseOneShotSignallingMode: params.UseOneShotSignallingMode,
|
||||
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
|
||||
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
|
||||
FireOnTrackBySdp: params.FireOnTrackBySdp,
|
||||
ProtocolVersion: params.ProtocolVersion,
|
||||
Config: params.Config,
|
||||
Twcc: params.Twcc,
|
||||
DirectionConfig: params.Config.Publisher,
|
||||
CongestionControlConfig: params.CongestionControlConfig,
|
||||
EnabledPublishCodecs: params.EnabledPublishCodecs,
|
||||
EnabledSubscribeCodecs: publisherSubscribeCodecs,
|
||||
Logger: lgr,
|
||||
SimTracks: params.SimTracks,
|
||||
ClientInfo: params.ClientInfo,
|
||||
IsSendSide: params.UseOneShotSignallingMode || params.UseSinglePeerConnection,
|
||||
AllowPlayoutDelay: params.AllowPlayoutDelay,
|
||||
Transport: livekit.SignalTarget_PUBLISHER,
|
||||
Handler: TransportManagerTransportHandler{params.PublisherHandler, t, lgr},
|
||||
UseOneShotSignallingMode: params.UseOneShotSignallingMode,
|
||||
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
|
||||
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
|
||||
DatachannelLossyTargetLatency: params.DatachannelLossyTargetLatency,
|
||||
FireOnTrackBySdp: params.FireOnTrackBySdp,
|
||||
EnableDataTracks: params.EnableDataTracks,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.publisher = publisher
|
||||
|
||||
lgr = LoggerWithPCTarget(params.Logger, livekit.SignalTarget_SUBSCRIBER)
|
||||
subscriber, err := NewPCTransport(TransportParams{
|
||||
ParticipantID: params.SID,
|
||||
ParticipantIdentity: params.Identity,
|
||||
ProtocolVersion: params.ProtocolVersion,
|
||||
Config: params.Config,
|
||||
DirectionConfig: params.Config.Subscriber,
|
||||
CongestionControlConfig: params.CongestionControlConfig,
|
||||
EnabledCodecs: params.EnabledSubscribeCodecs,
|
||||
Logger: lgr,
|
||||
ClientInfo: params.ClientInfo,
|
||||
IsOfferer: true,
|
||||
IsSendSide: true,
|
||||
AllowPlayoutDelay: params.AllowPlayoutDelay,
|
||||
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
|
||||
Transport: livekit.SignalTarget_SUBSCRIBER,
|
||||
Handler: TransportManagerTransportHandler{params.SubscriberHandler, t, lgr},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !t.params.UseOneShotSignallingMode && !t.params.UseSinglePeerConnection {
|
||||
lgr := LoggerWithPCTarget(params.Logger, livekit.SignalTarget_SUBSCRIBER)
|
||||
subscriber, err := NewPCTransport(TransportParams{
|
||||
ProtocolVersion: params.ProtocolVersion,
|
||||
Config: params.Config,
|
||||
DirectionConfig: params.Config.Subscriber,
|
||||
CongestionControlConfig: params.CongestionControlConfig,
|
||||
EnabledSubscribeCodecs: params.EnabledSubscribeCodecs,
|
||||
Logger: lgr,
|
||||
ClientInfo: params.ClientInfo,
|
||||
IsOfferer: true,
|
||||
IsSendSide: true,
|
||||
AllowPlayoutDelay: params.AllowPlayoutDelay,
|
||||
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
|
||||
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
|
||||
DatachannelLossyTargetLatency: params.DatachannelLossyTargetLatency,
|
||||
Transport: livekit.SignalTarget_SUBSCRIBER,
|
||||
Handler: TransportManagerTransportHandler{params.SubscriberHandler, t, lgr},
|
||||
FireOnTrackBySdp: params.FireOnTrackBySdp,
|
||||
EnableDataTracks: params.EnableDataTracks,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.subscriber = subscriber
|
||||
}
|
||||
t.subscriber = subscriber
|
||||
if !t.params.Migration {
|
||||
if !t.params.Migration && t.params.SubscriberAsPrimary {
|
||||
if err := t.createDataChannelsForSubscriber(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -207,8 +211,12 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
|
||||
}
|
||||
|
||||
func (t *TransportManager) Close() {
|
||||
t.publisher.Close()
|
||||
t.subscriber.Close()
|
||||
if t.publisher != nil {
|
||||
t.publisher.Close()
|
||||
}
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) SubscriberClose() {
|
||||
@@ -231,6 +239,10 @@ func (t *TransportManager) GetPublisherMid(rtpReceiver *webrtc.RTPReceiver) stri
|
||||
return t.publisher.GetMid(rtpReceiver)
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetPublisherRTPTransceiver(mid string) *webrtc.RTPTransceiver {
|
||||
return t.publisher.GetRTPTransceiver(mid)
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetPublisherRTPReceiver(mid string) *webrtc.RTPReceiver {
|
||||
return t.publisher.GetRTPReceiver(mid)
|
||||
}
|
||||
@@ -240,37 +252,49 @@ func (t *TransportManager) WritePublisherRTCP(pkts []rtcp.Packet) error {
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetSubscriberRTT() (float64, bool) {
|
||||
return t.subscriber.GetRTT()
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.GetRTT()
|
||||
} else {
|
||||
return t.subscriber.GetRTT()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) HasSubscriberEverConnected() bool {
|
||||
return t.subscriber.HasEverConnected()
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.HasEverConnected()
|
||||
} else {
|
||||
return t.subscriber.HasEverConnected()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) AddTrackLocal(
|
||||
trackLocal webrtc.TrackLocal,
|
||||
params types.AddTrackParams,
|
||||
enabledCodecs []*livekit.Codec,
|
||||
rtcpFeedbackConfig RTCPFeedbackConfig,
|
||||
) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error) {
|
||||
if t.params.UseOneShotSignallingMode {
|
||||
return t.publisher.AddTrack(trackLocal, params)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.AddTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
|
||||
} else {
|
||||
return t.subscriber.AddTrack(trackLocal, params)
|
||||
return t.subscriber.AddTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) AddTransceiverFromTrackLocal(
|
||||
trackLocal webrtc.TrackLocal,
|
||||
params types.AddTrackParams,
|
||||
enabledCodecs []*livekit.Codec,
|
||||
rtcpFeedbackConfig RTCPFeedbackConfig,
|
||||
) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error) {
|
||||
if t.params.UseOneShotSignallingMode {
|
||||
return t.publisher.AddTransceiverFromTrack(trackLocal, params)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.AddTransceiverFromTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
|
||||
} else {
|
||||
return t.subscriber.AddTransceiverFromTrack(trackLocal, params)
|
||||
return t.subscriber.AddTransceiverFromTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) RemoveTrackLocal(sender *webrtc.RTPSender) error {
|
||||
if t.params.UseOneShotSignallingMode {
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.RemoveTrack(sender)
|
||||
} else {
|
||||
return t.subscriber.RemoveTrack(sender)
|
||||
@@ -278,7 +302,7 @@ func (t *TransportManager) RemoveTrackLocal(sender *webrtc.RTPSender) error {
|
||||
}
|
||||
|
||||
func (t *TransportManager) WriteSubscriberRTCP(pkts []rtcp.Packet) error {
|
||||
if t.params.UseOneShotSignallingMode {
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.WriteRTCP(pkts)
|
||||
} else {
|
||||
return t.subscriber.WriteRTCP(pkts)
|
||||
@@ -286,36 +310,85 @@ func (t *TransportManager) WriteSubscriberRTCP(pkts []rtcp.Packet) error {
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetSubscriberPacer() pacer.Pacer {
|
||||
return t.subscriber.GetPacer()
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
return t.publisher.GetPacer()
|
||||
} else {
|
||||
return t.subscriber.GetPacer()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) AddSubscribedTrack(subTrack types.SubscribedTrack) {
|
||||
t.subscriber.AddTrackToStreamAllocator(subTrack)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
t.publisher.AddTrackToStreamAllocator(subTrack)
|
||||
} else {
|
||||
t.subscriber.AddTrackToStreamAllocator(subTrack)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) RemoveSubscribedTrack(subTrack types.SubscribedTrack) {
|
||||
t.subscriber.RemoveTrackFromStreamAllocator(subTrack)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
t.publisher.RemoveTrackFromStreamAllocator(subTrack)
|
||||
} else {
|
||||
t.subscriber.RemoveTrackFromStreamAllocator(subTrack)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) SendDataPacket(kind livekit.DataPacket_Kind, encoded []byte) error {
|
||||
func (t *TransportManager) SendDataMessage(kind livekit.DataPacket_Kind, data []byte) error {
|
||||
// downstream data is sent via primary peer connection
|
||||
err := t.getTransport(true).SendDataPacket(kind, encoded)
|
||||
return t.handleSendDataResult(t.getTransport(true).SendDataMessage(kind, data), kind.String(), len(data))
|
||||
}
|
||||
|
||||
func (t *TransportManager) SendDataMessageUnlabeled(data []byte, useRaw bool, sender livekit.ParticipantIdentity) error {
|
||||
// downstream data is sent via primary peer connection
|
||||
return t.handleSendDataResult(
|
||||
t.getTransport(true).SendDataMessageUnlabeled(data, useRaw, sender),
|
||||
"unlabeled",
|
||||
len(data),
|
||||
)
|
||||
}
|
||||
|
||||
func (t *TransportManager) handleSendDataResult(err error, kind string, size int) error {
|
||||
if err != nil {
|
||||
if !utils.ErrorIsOneOf(err, io.ErrClosedPipe, sctp.ErrStreamClosed, ErrTransportFailure, ErrDataChannelBufferFull, context.DeadlineExceeded) {
|
||||
if !utils.ErrorIsOneOf(
|
||||
err,
|
||||
io.ErrClosedPipe,
|
||||
sctp.ErrStreamClosed,
|
||||
ErrTransportFailure,
|
||||
ErrDataChannelUnavailable,
|
||||
context.DeadlineExceeded,
|
||||
datachannel.ErrDataDroppedByHighBufferedAmount,
|
||||
) {
|
||||
if errors.Is(err, datachannel.ErrDataDroppedBySlowReader) {
|
||||
droppedBySlowReaderCount := t.droppedBySlowReaderCount.Inc()
|
||||
droppedBySlowReaderCount := t.dataChannelSendErrorDroppedBySlowReaderCount.Inc()
|
||||
if (droppedBySlowReaderCount-1)%100 == 0 {
|
||||
t.params.Logger.Infow("drop data packet by slow reader", "error", err, "kind", kind, "count", droppedBySlowReaderCount)
|
||||
t.params.Logger.Infow(
|
||||
"drop data message by slow reader",
|
||||
"error", err,
|
||||
"kind", kind,
|
||||
"count", droppedBySlowReaderCount,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
t.params.Logger.Warnw("send data packet error", err)
|
||||
count := t.dataChannelSendErrorCount.Inc()
|
||||
if (count-1)%100 == 0 {
|
||||
t.params.Logger.Infow(
|
||||
"send data message error",
|
||||
"error", err,
|
||||
"kind", kind,
|
||||
"count", count,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if utils.ErrorIsOneOf(err, sctp.ErrStreamClosed, io.ErrClosedPipe) {
|
||||
t.params.SubscriberHandler.OnDataSendError(err)
|
||||
if t.params.SubscriberAsPrimary {
|
||||
t.params.SubscriberHandler.OnDataSendError(err)
|
||||
} else {
|
||||
t.params.PublisherHandler.OnDataSendError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.params.DataChannelStats.AddBytes(uint64(len(encoded)), true)
|
||||
t.params.DataChannelStats.AddBytes(uint64(size), true)
|
||||
}
|
||||
|
||||
return err
|
||||
@@ -323,8 +396,8 @@ func (t *TransportManager) SendDataPacket(kind livekit.DataPacket_Kind, encoded
|
||||
|
||||
func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels []*livekit.DataChannelInfo) error {
|
||||
var (
|
||||
reliableID, lossyID uint16
|
||||
reliableIDPtr, lossyIDPtr *uint16
|
||||
reliableID, lossyID, dataTrackID uint16
|
||||
reliableIDPtr, lossyIDPtr, dataTrackIDPtr *uint16
|
||||
)
|
||||
|
||||
//
|
||||
@@ -335,13 +408,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +432,7 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
|
||||
return err
|
||||
}
|
||||
|
||||
ordered = false
|
||||
retransmits := uint16(0)
|
||||
negotiated = t.params.Migration && lossyIDPtr == nil
|
||||
if err := t.subscriber.CreateDataChannel(LossyDataChannel, &webrtc.DataChannelInit{
|
||||
@@ -365,26 +443,28 @@ 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
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescription, mediaType string) (parsed *sdp.SessionDescription, unmatched []*sdp.MediaDescription, err error) {
|
||||
// prefer codec from offer for clients that don't support setCodecPreferences
|
||||
parsed, err = offer.Unmarshal()
|
||||
if err != nil {
|
||||
t.params.Logger.Errorw("failed to parse offer for codec preference", err)
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetUnmatchMediaForOffer(parsedOffer *sdp.SessionDescription, mediaType string) (unmatched []*sdp.MediaDescription, err error) {
|
||||
var lastMatchedMid string
|
||||
lastAnswer := t.lastPublisherAnswer.Load()
|
||||
if lastAnswer != nil {
|
||||
answer := lastAnswer.(webrtc.SessionDescription)
|
||||
parsedAnswer, err1 := answer.Unmarshal()
|
||||
if lastAnswer := t.publisher.CurrentLocalDescription(); lastAnswer != nil {
|
||||
parsedAnswer, err1 := lastAnswer.Unmarshal()
|
||||
if err1 != nil {
|
||||
// should not happen
|
||||
t.params.Logger.Errorw("failed to parse last answer", err)
|
||||
return
|
||||
t.params.Logger.Errorw("failed to parse last answer", err1)
|
||||
return unmatched, err1
|
||||
}
|
||||
|
||||
for i := len(parsedAnswer.MediaDescriptions) - 1; i >= 0; i-- {
|
||||
@@ -396,8 +476,8 @@ func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescripti
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(parsed.MediaDescriptions) - 1; i >= 0; i-- {
|
||||
media := parsed.MediaDescriptions[i]
|
||||
for i := len(parsedOffer.MediaDescriptions) - 1; i >= 0; i-- {
|
||||
media := parsedOffer.MediaDescriptions[i]
|
||||
if media.MediaName.Media == mediaType {
|
||||
mid, _ := media.Attribute(sdp.AttrKeyMID)
|
||||
if mid == lastMatchedMid {
|
||||
@@ -410,56 +490,76 @@ func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescripti
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TransportManager) LastPublisherOffer() webrtc.SessionDescription {
|
||||
if sd := t.lastPublisherOffer.Load(); sd != nil {
|
||||
return sd.(webrtc.SessionDescription)
|
||||
}
|
||||
return webrtc.SessionDescription{}
|
||||
func (t *TransportManager) LastPublisherOffer() *webrtc.SessionDescription {
|
||||
return t.publisher.CurrentRemoteDescription()
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleOffer(offer webrtc.SessionDescription, shouldPend bool) error {
|
||||
func (t *TransportManager) LastPublisherOfferPending() *webrtc.SessionDescription {
|
||||
return t.publisher.PendingRemoteDescription()
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleOffer(offer webrtc.SessionDescription, offerId uint32, shouldPend bool) error {
|
||||
t.lock.Lock()
|
||||
if shouldPend {
|
||||
t.pendingOfferPublisher = &offer
|
||||
t.pendingOfferIdPublisher = offerId
|
||||
t.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
t.lock.Unlock()
|
||||
t.lastPublisherOffer.Store(offer)
|
||||
|
||||
return t.publisher.HandleRemoteDescription(offer)
|
||||
return t.publisher.HandleRemoteDescription(offer, offerId)
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetAnswer() (webrtc.SessionDescription, error) {
|
||||
answer, err := t.publisher.GetAnswer()
|
||||
if err == nil {
|
||||
t.lastPublisherAnswer.Store(answer)
|
||||
}
|
||||
return answer, err
|
||||
func (t *TransportManager) GetAnswer() (webrtc.SessionDescription, uint32, error) {
|
||||
return t.publisher.GetAnswer()
|
||||
}
|
||||
|
||||
func (t *TransportManager) GetPublisherICESessionUfrag() (string, error) {
|
||||
return t.publisher.GetICESessionUfrag()
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleICETrickleSDPFragment(sdpFragment string) error {
|
||||
return t.publisher.HandleICETrickleSDPFragment(sdpFragment)
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleICERestartSDPFragment(sdpFragment string) (string, error) {
|
||||
return t.publisher.HandleICERestartSDPFragment(sdpFragment)
|
||||
}
|
||||
|
||||
func (t *TransportManager) ProcessPendingPublisherOffer() {
|
||||
t.lock.Lock()
|
||||
pendingOffer := t.pendingOfferPublisher
|
||||
t.pendingOfferPublisher = nil
|
||||
|
||||
pendingOfferId := t.pendingOfferIdPublisher
|
||||
t.pendingOfferIdPublisher = 0
|
||||
t.lock.Unlock()
|
||||
|
||||
if pendingOffer != nil {
|
||||
t.HandleOffer(*pendingOffer, false)
|
||||
t.HandleOffer(*pendingOffer, pendingOfferId, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleAnswer(answer webrtc.SessionDescription) {
|
||||
t.subscriber.HandleRemoteDescription(answer)
|
||||
func (t *TransportManager) HandleAnswer(answer webrtc.SessionDescription, answerId uint32) {
|
||||
t.subscriber.HandleRemoteDescription(answer, answerId)
|
||||
}
|
||||
|
||||
// AddICECandidate adds candidates for remote peer
|
||||
func (t *TransportManager) AddICECandidate(candidate webrtc.ICECandidateInit, target livekit.SignalTarget) {
|
||||
switch target {
|
||||
case livekit.SignalTarget_PUBLISHER:
|
||||
t.publisher.AddICECandidate(candidate)
|
||||
if t.publisher != nil {
|
||||
t.publisher.AddICECandidate(candidate)
|
||||
} else {
|
||||
t.params.Logger.Warnw("ice candidate for publisher, but no peer connection", nil, "candidate", candidate)
|
||||
}
|
||||
case livekit.SignalTarget_SUBSCRIBER:
|
||||
t.subscriber.AddICECandidate(candidate)
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.AddICECandidate(candidate)
|
||||
} else {
|
||||
t.params.Logger.Warnw("ice candidate for subscriber, but no peer connection", nil, "candidate", candidate)
|
||||
}
|
||||
default:
|
||||
err := errors.New("unknown signal target")
|
||||
t.params.Logger.Errorw("ice candidate for unknown signal target", err, "target", target)
|
||||
@@ -467,7 +567,11 @@ func (t *TransportManager) AddICECandidate(candidate webrtc.ICECandidateInit, ta
|
||||
}
|
||||
|
||||
func (t *TransportManager) NegotiateSubscriber(force bool) {
|
||||
t.subscriber.Negotiate(force)
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.Negotiate(force)
|
||||
} else {
|
||||
t.publisher.Negotiate(force)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason) {
|
||||
@@ -478,12 +582,16 @@ func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason)
|
||||
)
|
||||
switch reason {
|
||||
case livekit.ReconnectReason_RR_PUBLISHER_FAILED:
|
||||
resetShortConnection = true
|
||||
isShort, duration = t.publisher.IsShortConnection(time.Now())
|
||||
if t.publisher != nil {
|
||||
resetShortConnection = true
|
||||
isShort, duration = t.publisher.IsShortConnection(time.Now())
|
||||
}
|
||||
|
||||
case livekit.ReconnectReason_RR_SUBSCRIBER_FAILED:
|
||||
resetShortConnection = true
|
||||
isShort, duration = t.subscriber.IsShortConnection(time.Now())
|
||||
if t.subscriber != nil {
|
||||
resetShortConnection = true
|
||||
isShort, duration = t.subscriber.IsShortConnection(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
if isShort {
|
||||
@@ -495,15 +603,23 @@ func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason)
|
||||
}
|
||||
|
||||
if resetShortConnection {
|
||||
t.publisher.ResetShortConnOnICERestart()
|
||||
t.subscriber.ResetShortConnOnICERestart()
|
||||
if t.publisher != nil {
|
||||
t.publisher.ResetShortConnOnICERestart()
|
||||
}
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.ResetShortConnOnICERestart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) ICERestart(iceConfig *livekit.ICEConfig) error {
|
||||
t.SetICEConfig(iceConfig)
|
||||
|
||||
return t.subscriber.ICERestart()
|
||||
if t.subscriber != nil {
|
||||
return t.subscriber.ICERestart()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TransportManager) OnICEConfigChanged(f func(iceConfig *livekit.ICEConfig)) {
|
||||
@@ -555,8 +671,12 @@ func (t *TransportManager) configureICE(iceConfig *livekit.ICEConfig, reset bool
|
||||
t.mediaLossProxy.OnMediaLossUpdate(nil)
|
||||
}
|
||||
|
||||
t.publisher.SetPreferTCP(iceConfig.PreferencePublisher == livekit.ICECandidateType_ICT_TCP)
|
||||
t.subscriber.SetPreferTCP(iceConfig.PreferenceSubscriber == livekit.ICECandidateType_ICT_TCP)
|
||||
if t.publisher != nil {
|
||||
t.publisher.SetPreferTCP(iceConfig.PreferencePublisher == livekit.ICECandidateType_ICT_TCP)
|
||||
}
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.SetPreferTCP(iceConfig.PreferenceSubscriber == livekit.ICECandidateType_ICT_TCP)
|
||||
}
|
||||
|
||||
if onICEConfigChanged != nil {
|
||||
onICEConfigChanged(iceConfig)
|
||||
@@ -570,6 +690,10 @@ func (t *TransportManager) SubscriberAsPrimary() bool {
|
||||
func (t *TransportManager) GetICEConnectionInfo() []*types.ICEConnectionInfo {
|
||||
infos := make([]*types.ICEConnectionInfo, 0, 2)
|
||||
for _, pc := range []*PCTransport{t.publisher, t.subscriber} {
|
||||
if pc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
info := pc.GetICEConnectionInfo()
|
||||
if info.HasCandidates() {
|
||||
infos = append(infos, info)
|
||||
@@ -578,13 +702,43 @@ func (t *TransportManager) GetICEConnectionInfo() []*types.ICEConnectionInfo {
|
||||
return infos
|
||||
}
|
||||
|
||||
func (t *TransportManager) getTransport(isPrimary bool) *PCTransport {
|
||||
pcTransport := t.publisher
|
||||
if (isPrimary && t.params.SubscriberAsPrimary) || (!isPrimary && !t.params.SubscriberAsPrimary) {
|
||||
pcTransport = t.subscriber
|
||||
}
|
||||
func (t *TransportManager) GetDataTrackTransport() types.DataTrackTransport {
|
||||
return t.getTransport(true)
|
||||
}
|
||||
|
||||
return pcTransport
|
||||
func (t *TransportManager) getTransport(isPrimary bool) *PCTransport {
|
||||
switch {
|
||||
case t.publisher == nil:
|
||||
return t.subscriber
|
||||
|
||||
case t.subscriber == nil:
|
||||
return t.publisher
|
||||
|
||||
default:
|
||||
pcTransport := t.publisher
|
||||
if (isPrimary && t.params.SubscriberAsPrimary) || (!isPrimary && !t.params.SubscriberAsPrimary) {
|
||||
pcTransport = t.subscriber
|
||||
}
|
||||
|
||||
return pcTransport
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) getLowestPriorityConnectionType() types.ICEConnectionType {
|
||||
switch {
|
||||
case t.publisher == nil:
|
||||
return t.subscriber.GetICEConnectionType()
|
||||
|
||||
case t.subscriber == nil:
|
||||
return t.publisher.GetICEConnectionType()
|
||||
|
||||
default:
|
||||
ctype := t.publisher.GetICEConnectionType()
|
||||
if stype := t.subscriber.GetICEConnectionType(); stype > ctype {
|
||||
ctype = stype
|
||||
}
|
||||
return ctype
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
|
||||
@@ -614,6 +768,8 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
|
||||
return
|
||||
}
|
||||
|
||||
lowestPriorityConnectionType := t.getLowestPriorityConnectionType()
|
||||
|
||||
//
|
||||
// Checking only `PreferenceSubscriber` field although any connection failure (PUBLISHER OR SUBSCRIBER) will
|
||||
// flow through here.
|
||||
@@ -621,13 +777,36 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
|
||||
// As both transports are switched to the same type on any failure, checking just subscriber should be fine.
|
||||
//
|
||||
getNext := func(ic *livekit.ICEConfig) livekit.ICECandidateType {
|
||||
if ic.PreferenceSubscriber == livekit.ICECandidateType_ICT_NONE && t.params.ClientInfo.SupportsICETCP() && t.canUseICETCP() {
|
||||
return livekit.ICECandidateType_ICT_TCP
|
||||
} else if ic.PreferenceSubscriber != livekit.ICECandidateType_ICT_TLS && t.params.TURNSEnabled {
|
||||
return livekit.ICECandidateType_ICT_TLS
|
||||
} else {
|
||||
return livekit.ICECandidateType_ICT_NONE
|
||||
switch lowestPriorityConnectionType {
|
||||
case types.ICEConnectionTypeUDP:
|
||||
// try ICE/TCP if ICE/UDP failed
|
||||
if ic.PreferenceSubscriber == livekit.ICECandidateType_ICT_NONE {
|
||||
if t.params.ClientInfo.SupportsICETCP() && t.canUseICETCP() {
|
||||
return livekit.ICECandidateType_ICT_TCP
|
||||
} else if t.params.TURNSEnabled {
|
||||
// fallback to TURN/TLS if TCP is not supported
|
||||
return livekit.ICECandidateType_ICT_TLS
|
||||
}
|
||||
}
|
||||
|
||||
case types.ICEConnectionTypeTCP:
|
||||
// try TURN/TLS if ICE/TCP failed,
|
||||
// the configuration could have been ICT_NONE or ICT_TCP,
|
||||
// in either case, fallback to TURN/TLS
|
||||
if t.params.TURNSEnabled {
|
||||
return livekit.ICECandidateType_ICT_TLS
|
||||
} else {
|
||||
// keep the current config
|
||||
return ic.PreferenceSubscriber
|
||||
}
|
||||
|
||||
case types.ICEConnectionTypeTURN:
|
||||
// TURN/TLS is the most permissive option, if that fails there is nowhere to go to
|
||||
// the configuration could have been ICT_NONE or ICT_TLS,
|
||||
// keep the current config
|
||||
return ic.PreferenceSubscriber
|
||||
}
|
||||
return livekit.ICECandidateType_ICT_NONE
|
||||
}
|
||||
|
||||
var preferNext livekit.ICECandidateType
|
||||
@@ -672,7 +851,11 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
|
||||
}, false)
|
||||
}
|
||||
|
||||
func (t *TransportManager) SetMigrateInfo(previousOffer, previousAnswer *webrtc.SessionDescription, dataChannels []*livekit.DataChannelInfo) {
|
||||
func (t *TransportManager) SetMigrateInfo(
|
||||
previousOffer *webrtc.SessionDescription,
|
||||
previousAnswer *webrtc.SessionDescription,
|
||||
dataChannels []*livekit.DataChannelInfo,
|
||||
) {
|
||||
t.lock.Lock()
|
||||
t.pendingDataChannelsPublisher = make([]*livekit.DataChannelInfo, 0, len(dataChannels))
|
||||
pendingDataChannelsSubscriber := make([]*livekit.DataChannelInfo, 0, len(dataChannels))
|
||||
@@ -691,7 +874,11 @@ func (t *TransportManager) SetMigrateInfo(previousOffer, previousAnswer *webrtc.
|
||||
}
|
||||
}
|
||||
|
||||
t.subscriber.SetPreviousSdp(previousOffer, previousAnswer)
|
||||
if t.params.UseSinglePeerConnection {
|
||||
t.publisher.SetPreviousSdp(previousAnswer, previousOffer)
|
||||
} else {
|
||||
t.subscriber.SetPreviousSdp(previousOffer, previousAnswer)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) ProcessPendingPublisherDataChannels() {
|
||||
@@ -710,7 +897,16 @@ 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())
|
||||
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(LossyDataChannel, &webrtc.DataChannelInit{
|
||||
@@ -719,12 +915,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 {
|
||||
@@ -755,7 +954,11 @@ func (t *TransportManager) onMediaLossUpdate(loss uint8) {
|
||||
t.lock.Unlock()
|
||||
|
||||
t.params.Logger.Infow("udp connection unstable, switch to tcp", "signalingRTT", t.signalingRTT)
|
||||
t.params.SubscriberHandler.OnFailed(true, t.subscriber.GetICEConnectionInfo())
|
||||
if t.params.UseSinglePeerConnection {
|
||||
t.params.PublisherHandler.OnFailed(true, t.publisher.GetICEConnectionInfo())
|
||||
} else {
|
||||
t.params.SubscriberHandler.OnFailed(true, t.subscriber.GetICEConnectionInfo())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -767,8 +970,12 @@ func (t *TransportManager) UpdateSignalingRTT(rtt uint32) {
|
||||
t.lock.Lock()
|
||||
t.signalingRTT = rtt
|
||||
t.lock.Unlock()
|
||||
t.publisher.SetSignalingRTT(rtt)
|
||||
t.subscriber.SetSignalingRTT(rtt)
|
||||
if t.publisher != nil {
|
||||
t.publisher.SetSignalingRTT(rtt)
|
||||
}
|
||||
if t.subscriber != nil {
|
||||
t.subscriber.SetSignalingRTT(rtt)
|
||||
}
|
||||
|
||||
// TODO: considering using tcp rtt to calculate ice connection cost, if ice connection can't be established
|
||||
// within 5 * tcp rtt(at least 5s), means udp traffic might be block/dropped, switch to tcp.
|
||||
@@ -813,13 +1020,25 @@ func (t *TransportManager) SetSignalSourceValid(valid bool) {
|
||||
}
|
||||
|
||||
func (t *TransportManager) SetSubscriberAllowPause(allowPause bool) {
|
||||
t.subscriber.SetAllowPauseOfStreamAllocator(allowPause)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
t.publisher.SetAllowPauseOfStreamAllocator(allowPause)
|
||||
} else {
|
||||
t.subscriber.SetAllowPauseOfStreamAllocator(allowPause)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) SetSubscriberChannelCapacity(channelCapacity int64) {
|
||||
t.subscriber.SetChannelCapacityOfStreamAllocator(channelCapacity)
|
||||
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
|
||||
t.publisher.SetChannelCapacityOfStreamAllocator(channelCapacity)
|
||||
} else {
|
||||
t.subscriber.SetChannelCapacityOfStreamAllocator(channelCapacity)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TransportManager) hasRecentSignalLocked() bool {
|
||||
return time.Since(t.lastSignalAt) < PingTimeoutSeconds*time.Second
|
||||
}
|
||||
|
||||
func (t *TransportManager) RTPStreamPublished(ssrc uint32, mid, rid string) {
|
||||
t.publisher.RTPStreamPublished(ssrc, mid, rid)
|
||||
}
|
||||
|
||||
+149
-103
@@ -18,30 +18,63 @@ package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/ice/v4"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
)
|
||||
|
||||
type ICEConnectionType string
|
||||
type ICEConnectionType int
|
||||
|
||||
const (
|
||||
ICEConnectionTypeUDP ICEConnectionType = "udp"
|
||||
ICEConnectionTypeTCP ICEConnectionType = "tcp"
|
||||
ICEConnectionTypeTURN ICEConnectionType = "turn"
|
||||
ICEConnectionTypeUnknown ICEConnectionType = "unknown"
|
||||
// this is in ICE priority highest -> lowest ordering
|
||||
// WARNING: Keep this ordering as it is used to find lowest priority connection type.
|
||||
ICEConnectionTypeUnknown ICEConnectionType = iota
|
||||
ICEConnectionTypeUDP
|
||||
ICEConnectionTypeTCP
|
||||
ICEConnectionTypeTURN
|
||||
)
|
||||
|
||||
func (i ICEConnectionType) String() string {
|
||||
switch i {
|
||||
case ICEConnectionTypeUnknown:
|
||||
return "unknown"
|
||||
case ICEConnectionTypeUDP:
|
||||
return "udp"
|
||||
case ICEConnectionTypeTCP:
|
||||
return "tcp"
|
||||
case ICEConnectionTypeTURN:
|
||||
return "turn"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func (i ICEConnectionType) ReporterType() roomobs.ConnectionType {
|
||||
switch i {
|
||||
case ICEConnectionTypeUnknown:
|
||||
return roomobs.ConnectionTypeUndefined
|
||||
case ICEConnectionTypeUDP:
|
||||
return roomobs.ConnectionTypeUDP
|
||||
case ICEConnectionTypeTCP:
|
||||
return roomobs.ConnectionTypeTCP
|
||||
case ICEConnectionTypeTURN:
|
||||
return roomobs.ConnectionTypeTurn
|
||||
default:
|
||||
return roomobs.ConnectionTypeUndefined
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
type ICECandidateExtended struct {
|
||||
// only one of local or remote is set. This is due to type foo in Pion
|
||||
Local *webrtc.ICECandidate
|
||||
Remote ice.Candidate
|
||||
Candidate *webrtc.ICECandidate
|
||||
SelectedOrder int
|
||||
Filtered bool
|
||||
Trickle bool
|
||||
@@ -60,6 +93,15 @@ func (i *ICEConnectionInfo) HasCandidates() bool {
|
||||
return len(i.Local) > 0 || len(i.Remote) > 0
|
||||
}
|
||||
|
||||
func ICEConnectionInfosType(infos []*ICEConnectionInfo) ICEConnectionType {
|
||||
for _, info := range infos {
|
||||
if info.Type != ICEConnectionTypeUnknown {
|
||||
return info.Type
|
||||
}
|
||||
}
|
||||
return ICEConnectionTypeUnknown
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
type ICEConnectionDetails struct {
|
||||
@@ -91,7 +133,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
|
||||
}
|
||||
for _, c := range d.Local {
|
||||
info.Local = append(info.Local, &ICECandidateExtended{
|
||||
Local: c.Local,
|
||||
Candidate: c.Candidate,
|
||||
Filtered: c.Filtered,
|
||||
SelectedOrder: c.SelectedOrder,
|
||||
Trickle: c.Trickle,
|
||||
@@ -99,7 +141,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
|
||||
}
|
||||
for _, c := range d.Remote {
|
||||
info.Remote = append(info.Remote, &ICECandidateExtended{
|
||||
Remote: c.Remote,
|
||||
Candidate: c.Candidate,
|
||||
Filtered: c.Filtered,
|
||||
SelectedOrder: c.SelectedOrder,
|
||||
Trickle: c.Trickle,
|
||||
@@ -108,19 +150,26 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) GetConnectionType() ICEConnectionType {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
return d.Type
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) AddLocalCandidate(c *webrtc.ICECandidate, filtered, trickle bool) {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
compFn := func(e *ICECandidateExtended) bool {
|
||||
return isCandidateEqualTo(e.Local, c)
|
||||
return isCandidateEqualTo(e.Candidate, c)
|
||||
}
|
||||
if slices.ContainsFunc(d.Local, compFn) {
|
||||
return
|
||||
}
|
||||
d.Local = append(d.Local, &ICECandidateExtended{
|
||||
Local: c,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
Candidate: c,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -135,24 +184,30 @@ func (d *ICEConnectionDetails) AddLocalICECandidate(c ice.Candidate, filtered, t
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, filtered, trickle, canUpdate bool) {
|
||||
candidate, err := unmarshalICECandidate(c)
|
||||
iceCandidate, err := unmarshalICECandidate(c)
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal candidate", err, "candidate", c)
|
||||
return
|
||||
}
|
||||
d.AddRemoteICECandidate(candidate, filtered, trickle, canUpdate)
|
||||
d.AddRemoteICECandidate(iceCandidate, filtered, trickle, canUpdate)
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, filtered, trickle, canUpdate bool) {
|
||||
if candidate == nil {
|
||||
func (d *ICEConnectionDetails) AddRemoteICECandidate(iceCandidate ice.Candidate, filtered, trickle, canUpdate bool) {
|
||||
if iceCandidate == nil {
|
||||
// end-of-candidates candidate
|
||||
return
|
||||
}
|
||||
|
||||
candidate, err := unmarshalCandidate(iceCandidate)
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal ice candidate", err, "candidate", iceCandidate)
|
||||
return
|
||||
}
|
||||
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
indexFn := func(e *ICECandidateExtended) bool {
|
||||
return isICECandidateEqualTo(e.Remote, candidate)
|
||||
return isCandidateEqualTo(e.Candidate, candidate)
|
||||
}
|
||||
if idx := slices.IndexFunc(d.Remote, indexFn); idx != -1 {
|
||||
if canUpdate {
|
||||
@@ -162,10 +217,11 @@ func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, fi
|
||||
return
|
||||
}
|
||||
d.Remote = append(d.Remote, &ICECandidateExtended{
|
||||
Remote: candidate,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
Candidate: candidate,
|
||||
Filtered: filtered,
|
||||
Trickle: trickle,
|
||||
})
|
||||
d.updateConnectionTypeLocked()
|
||||
}
|
||||
|
||||
func (d *ICEConnectionDetails) Clear() {
|
||||
@@ -183,56 +239,78 @@ func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) {
|
||||
d.selectedCount++
|
||||
|
||||
remoteIdx := slices.IndexFunc(d.Remote, func(e *ICECandidateExtended) bool {
|
||||
return isICECandidateEqualToCandidate(e.Remote, pair.Remote)
|
||||
return isCandidateEqualTo(e.Candidate, pair.Remote)
|
||||
})
|
||||
if remoteIdx < 0 {
|
||||
// it's possible for prflx candidates to be generated by Pion, we'll add them
|
||||
candidate, err := unmarshalICECandidate(pair.Remote.ToJSON())
|
||||
if err != nil {
|
||||
d.logger.Errorw("could not unmarshal remote candidate", err, "candidate", pair.Remote)
|
||||
return
|
||||
}
|
||||
if candidate == nil {
|
||||
return
|
||||
}
|
||||
d.Remote = append(d.Remote, &ICECandidateExtended{
|
||||
Remote: candidate,
|
||||
Filtered: false,
|
||||
Trickle: true,
|
||||
Candidate: pair.Remote,
|
||||
Filtered: false,
|
||||
Trickle: false,
|
||||
})
|
||||
remoteIdx = len(d.Remote) - 1
|
||||
}
|
||||
remote := d.Remote[remoteIdx]
|
||||
remote.SelectedOrder = d.selectedCount
|
||||
d.Remote[remoteIdx].SelectedOrder = d.selectedCount
|
||||
d.updateConnectionTypeLocked()
|
||||
|
||||
localIdx := slices.IndexFunc(d.Local, func(e *ICECandidateExtended) bool {
|
||||
return isCandidateEqualTo(e.Local, pair.Local)
|
||||
return isCandidateEqualTo(e.Candidate, pair.Local)
|
||||
})
|
||||
if localIdx < 0 {
|
||||
d.logger.Errorw("could not match local candidate", nil, "local", pair.Local)
|
||||
// should not happen
|
||||
return
|
||||
}
|
||||
local := d.Local[localIdx]
|
||||
local.SelectedOrder = d.selectedCount
|
||||
d.Local[localIdx].SelectedOrder = d.selectedCount
|
||||
}
|
||||
|
||||
d.Type = ICEConnectionTypeUDP
|
||||
if pair.Remote.Protocol == webrtc.ICEProtocolTCP {
|
||||
func (d *ICEConnectionDetails) updateConnectionTypeLocked() {
|
||||
highestSelectedOrder := -1
|
||||
var selectedRemoteCandidate *ICECandidateExtended
|
||||
for _, remote := range d.Remote {
|
||||
if remote.SelectedOrder == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if remote.SelectedOrder > highestSelectedOrder {
|
||||
highestSelectedOrder = remote.SelectedOrder
|
||||
selectedRemoteCandidate = remote
|
||||
}
|
||||
}
|
||||
|
||||
if selectedRemoteCandidate == nil {
|
||||
return
|
||||
}
|
||||
|
||||
remoteCandidate := selectedRemoteCandidate.Candidate
|
||||
switch remoteCandidate.Protocol {
|
||||
case webrtc.ICEProtocolUDP:
|
||||
d.Type = ICEConnectionTypeUDP
|
||||
|
||||
case webrtc.ICEProtocolTCP:
|
||||
d.Type = ICEConnectionTypeTCP
|
||||
}
|
||||
if pair.Remote.Typ == webrtc.ICECandidateTypeRelay {
|
||||
|
||||
switch remoteCandidate.Typ {
|
||||
case webrtc.ICECandidateTypeRelay:
|
||||
d.Type = ICEConnectionTypeTURN
|
||||
} else if pair.Remote.Typ == webrtc.ICECandidateTypePrflx {
|
||||
|
||||
case webrtc.ICECandidateTypePrflx:
|
||||
// if the remote relay candidate pings us *before* we get a relay candidate,
|
||||
// Pion would have created a prflx candidate with the same address as the relay candidate.
|
||||
// to report an accurate connection type, we'll compare to see if existing relay candidates match
|
||||
for _, other := range d.Remote {
|
||||
or := other.Remote
|
||||
if or.Type() == ice.CandidateTypeRelay &&
|
||||
pair.Remote.Address == or.Address() &&
|
||||
pair.Remote.Port == uint16(or.Port()) &&
|
||||
pair.Remote.Protocol.String() == or.NetworkType().NetworkShort() {
|
||||
or := other.Candidate
|
||||
if or.Typ == webrtc.ICECandidateTypeRelay &&
|
||||
remoteCandidate.Address == or.Address &&
|
||||
// NOTE: port is not compared as relayed address reported by TURN ALLOCATE from
|
||||
// pion/turn server -> client and later sent from client -> server via ICE Trickle does not
|
||||
// match port of `prflx` candidate learnt via TURN path. TODO-INVESTIGATE: how and why doesn't
|
||||
// port match?
|
||||
// remoteCandidate.Port == or.Port &&
|
||||
remoteCandidate.Protocol == or.Protocol {
|
||||
d.Type = ICEConnectionTypeTURN
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,39 +336,6 @@ func isCandidateEqualTo(c1, c2 *webrtc.ICECandidate) bool {
|
||||
c1.TCPType == c2.TCPType
|
||||
}
|
||||
|
||||
func isICECandidateEqualTo(c1, c2 ice.Candidate) bool {
|
||||
if c1 == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
|
||||
return false
|
||||
}
|
||||
return c1.Type() == c2.Type() &&
|
||||
c1.NetworkType() == c2.NetworkType() &&
|
||||
c1.Address() == c2.Address() &&
|
||||
c1.Port() == c2.Port() &&
|
||||
c1.Foundation() == c2.Foundation() &&
|
||||
c1.Priority() == c2.Priority() &&
|
||||
c1.RelatedAddress().Equal(c2.RelatedAddress()) &&
|
||||
c1.TCPType() == c2.TCPType()
|
||||
}
|
||||
|
||||
func isICECandidateEqualToCandidate(c1 ice.Candidate, c2 *webrtc.ICECandidate) bool {
|
||||
if c1 == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
|
||||
return false
|
||||
}
|
||||
return c1.Type().String() == c2.Typ.String() &&
|
||||
c1.NetworkType().NetworkShort() == c2.Protocol.String() &&
|
||||
c1.Address() == c2.Address &&
|
||||
c1.Port() == int(c2.Port) &&
|
||||
c1.Foundation() == c2.Foundation &&
|
||||
c1.Priority() == c2.Priority &&
|
||||
c1.TCPType().String() == c2.TCPType
|
||||
}
|
||||
|
||||
func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
|
||||
candidateValue := strings.TrimPrefix(c.Candidate, "candidate:")
|
||||
if candidateValue == "" {
|
||||
@@ -306,28 +351,14 @@ func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
|
||||
}
|
||||
|
||||
func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
|
||||
var typ webrtc.ICECandidateType
|
||||
switch i.Type() {
|
||||
case ice.CandidateTypeHost:
|
||||
typ = webrtc.ICECandidateTypeHost
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
typ = webrtc.ICECandidateTypeSrflx
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
typ = webrtc.ICECandidateTypePrflx
|
||||
case ice.CandidateTypeRelay:
|
||||
typ = webrtc.ICECandidateTypeRelay
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown candidate type: %s", i.Type())
|
||||
typ, err := convertTypeFromICE(i.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var protocol webrtc.ICEProtocol
|
||||
switch strings.ToLower(i.NetworkType().NetworkShort()) {
|
||||
case "udp":
|
||||
protocol = webrtc.ICEProtocolUDP
|
||||
case "tcp":
|
||||
protocol = webrtc.ICEProtocolTCP
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown network type: %s", i.NetworkType())
|
||||
protocol, err := webrtc.NewICEProtocol(i.NetworkType().NetworkShort())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := webrtc.ICECandidate{
|
||||
@@ -349,6 +380,21 @@ func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func convertTypeFromICE(t ice.CandidateType) (webrtc.ICECandidateType, error) {
|
||||
switch t {
|
||||
case ice.CandidateTypeHost:
|
||||
return webrtc.ICECandidateTypeHost, nil
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
return webrtc.ICECandidateTypeSrflx, nil
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
return webrtc.ICECandidateTypePrflx, nil
|
||||
case ice.CandidateTypeRelay:
|
||||
return webrtc.ICECandidateTypeRelay, nil
|
||||
default:
|
||||
return webrtc.ICECandidateType(t), fmt.Errorf("unknown ice candidate type: %s", t)
|
||||
}
|
||||
}
|
||||
|
||||
func IsCandidateMDNS(candidate webrtc.ICECandidateInit) bool {
|
||||
c, err := unmarshalICECandidate(candidate)
|
||||
if err != nil {
|
||||
@@ -364,5 +410,5 @@ func IsICECandidateMDNS(candidate ice.Candidate) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.HasSuffix(candidate.Address(), ".local")
|
||||
return strings.HasSuffix(candidate.Address(), ".local") || strings.HasSuffix(candidate.Address(), ".invalid")
|
||||
}
|
||||
|
||||
@@ -22,15 +22,20 @@ import (
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/protocol/auth"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"github.com/livekit/protocol/observability/roomobs"
|
||||
"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"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/pacer"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
|
||||
@@ -111,6 +116,8 @@ const (
|
||||
ParticipantCloseReasonRoomClosed
|
||||
ParticipantCloseReasonUserUnavailable
|
||||
ParticipantCloseReasonUserRejected
|
||||
ParticipantCloseReasonMoveFailed
|
||||
ParticipantCloseReasonAgentError
|
||||
)
|
||||
|
||||
func (p ParticipantCloseReason) String() string {
|
||||
@@ -169,6 +176,10 @@ func (p ParticipantCloseReason) String() string {
|
||||
return "USER_UNAVAILABLE"
|
||||
case ParticipantCloseReasonUserRejected:
|
||||
return "USER_REJECTED"
|
||||
case ParticipantCloseReasonMoveFailed:
|
||||
return "MOVE_FAILED"
|
||||
case ParticipantCloseReasonAgentError:
|
||||
return "AGENT_ERROR"
|
||||
default:
|
||||
return fmt.Sprintf("%d", int(p))
|
||||
}
|
||||
@@ -184,7 +195,7 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
|
||||
// expected to be connected but is not
|
||||
return livekit.DisconnectReason_JOIN_FAILURE
|
||||
case ParticipantCloseReasonPeerConnectionDisconnected:
|
||||
return livekit.DisconnectReason_STATE_MISMATCH
|
||||
return livekit.DisconnectReason_CONNECTION_TIMEOUT
|
||||
case ParticipantCloseReasonDuplicateIdentity, ParticipantCloseReasonStale:
|
||||
return livekit.DisconnectReason_DUPLICATE_IDENTITY
|
||||
case ParticipantCloseReasonMigrationRequested, ParticipantCloseReasonMigrationComplete, ParticipantCloseReasonSimulateMigration:
|
||||
@@ -195,7 +206,8 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
|
||||
return livekit.DisconnectReason_ROOM_DELETED
|
||||
case ParticipantCloseReasonSimulateNodeFailure, ParticipantCloseReasonSimulateServerLeave:
|
||||
return livekit.DisconnectReason_SERVER_SHUTDOWN
|
||||
case ParticipantCloseReasonNegotiateFailed, ParticipantCloseReasonPublicationError, ParticipantCloseReasonSubscriptionError, ParticipantCloseReasonDataChannelError, ParticipantCloseReasonMigrateCodecMismatch:
|
||||
case ParticipantCloseReasonNegotiateFailed, ParticipantCloseReasonPublicationError, ParticipantCloseReasonSubscriptionError,
|
||||
ParticipantCloseReasonDataChannelError, ParticipantCloseReasonMigrateCodecMismatch, ParticipantCloseReasonMoveFailed:
|
||||
return livekit.DisconnectReason_STATE_MISMATCH
|
||||
case ParticipantCloseReasonSignalSourceClose:
|
||||
return livekit.DisconnectReason_SIGNAL_CLOSE
|
||||
@@ -205,12 +217,31 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
|
||||
return livekit.DisconnectReason_USER_UNAVAILABLE
|
||||
case ParticipantCloseReasonUserRejected:
|
||||
return livekit.DisconnectReason_USER_REJECTED
|
||||
case ParticipantCloseReasonAgentError:
|
||||
return livekit.DisconnectReason_AGENT_ERROR
|
||||
default:
|
||||
// the other types will map to unknown reason
|
||||
return livekit.DisconnectReason_UNKNOWN_REASON
|
||||
}
|
||||
}
|
||||
|
||||
// IsIntentionalDisconnect reports whether a disconnect reason represents an
|
||||
// intentional/expected closure (client leaving, admin action, room teardown,
|
||||
// migration, etc.) as opposed to a connection failure.
|
||||
func IsIntentionalDisconnect(reason livekit.DisconnectReason) bool {
|
||||
switch reason {
|
||||
case livekit.DisconnectReason_CLIENT_INITIATED,
|
||||
livekit.DisconnectReason_SERVER_SHUTDOWN,
|
||||
livekit.DisconnectReason_DUPLICATE_IDENTITY,
|
||||
livekit.DisconnectReason_MIGRATION,
|
||||
livekit.DisconnectReason_PARTICIPANT_REMOVED,
|
||||
livekit.DisconnectReason_ROOM_DELETED,
|
||||
livekit.DisconnectReason_ROOM_CLOSED:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
type SignallingCloseReason int
|
||||
@@ -258,6 +289,12 @@ func (s SignallingCloseReason) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
const (
|
||||
ParticipantCloseKeyNormal = "normal"
|
||||
ParticipantCloseKeyWHIP = "whip"
|
||||
)
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
//counterfeiter:generate . Participant
|
||||
@@ -268,18 +305,26 @@ type Participant interface {
|
||||
ConnectedAt() time.Time
|
||||
CloseReason() ParticipantCloseReason
|
||||
Kind() livekit.ParticipantInfo_Kind
|
||||
KindDetails() []livekit.ParticipantInfo_KindDetail
|
||||
IsRecorder() bool
|
||||
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, shouldClose bool)
|
||||
RemovePublishedTrack(track MediaTrack, isExpectedToResume bool)
|
||||
|
||||
GetPublishedDataTracks() []DataTrack
|
||||
GetPublishedDataTrack(handle uint16) DataTrack
|
||||
RemovePublishedDataTrack(track DataTrack)
|
||||
|
||||
GetAudioLevel() (smoothedLevel float64, active bool)
|
||||
|
||||
@@ -290,7 +335,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)
|
||||
|
||||
@@ -301,9 +350,11 @@ type Participant interface {
|
||||
resolverBySid func(participantID livekit.ParticipantID) LocalParticipant,
|
||||
) error
|
||||
|
||||
DebugInfo() map[string]interface{}
|
||||
DebugInfo() map[string]any
|
||||
|
||||
OnMetrics(callback func(Participant, *livekit.DataPacket))
|
||||
HandleReceivedDataTrackMessage([]byte, *datatrack.Packet, int64)
|
||||
|
||||
GetParticipantListener() ParticipantListener
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
@@ -313,22 +364,51 @@ type AddTrackParams struct {
|
||||
Red bool
|
||||
}
|
||||
|
||||
type MoveToRoomParams struct {
|
||||
RoomName livekit.RoomName
|
||||
ParticipantID livekit.ParticipantID
|
||||
Listener LocalParticipantListener
|
||||
Helper LocalParticipantHelper
|
||||
}
|
||||
|
||||
type DataMessageCache struct {
|
||||
Data []byte
|
||||
SenderID livekit.ParticipantID
|
||||
Seq uint32
|
||||
DestIdentities []livekit.ParticipantIdentity
|
||||
}
|
||||
|
||||
//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)
|
||||
ShouldRegressCodec() bool
|
||||
GetCachedReliableDataMessage(seqs map[livekit.ParticipantID]uint32) []*DataMessageCache
|
||||
}
|
||||
|
||||
//counterfeiter:generate . LocalParticipant
|
||||
type LocalParticipant interface {
|
||||
Participant
|
||||
|
||||
ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion)
|
||||
TelemetryGuard() *telemetry.ReferenceGuard
|
||||
GetTelemetryListener() ParticipantTelemetryListener
|
||||
|
||||
// getters
|
||||
GetCountry() string
|
||||
GetTrailer() []byte
|
||||
GetLogger() logger.Logger
|
||||
GetLoggerResolver() logger.DeferredFieldResolver
|
||||
GetReporter() roomobs.ParticipantSessionReporter
|
||||
GetReporterResolver() roomobs.ParticipantReporterResolver
|
||||
GetAdaptiveStream() bool
|
||||
ProtocolVersion() ProtocolVersion
|
||||
SupportsSyncStreamID() bool
|
||||
SupportsTransceiverReuse() bool
|
||||
IsClosed() bool
|
||||
SupportsTransceiverReuse(mt MediaTrack) bool
|
||||
IsUsingSinglePeerConnection() bool
|
||||
IsReady() bool
|
||||
IsDisconnected() bool
|
||||
ActiveAt() time.Time
|
||||
Disconnected() <-chan struct{}
|
||||
IsIdle() bool
|
||||
SubscriberAsPrimary() bool
|
||||
@@ -340,15 +420,19 @@ type LocalParticipant interface {
|
||||
GetICEConnectionInfo() []*ICEConnectionInfo
|
||||
HasConnected() bool
|
||||
GetEnabledPublishCodecs() []*livekit.Codec
|
||||
GetPublisherICESessionUfrag() (string, error)
|
||||
SupportsMoving() error
|
||||
GetLastReliableSequence(migrateOut bool) uint32
|
||||
|
||||
SetResponseSink(sink routing.MessageSink)
|
||||
SwapResponseSink(sink routing.MessageSink, reason SignallingCloseReason)
|
||||
GetResponseSink() routing.MessageSink
|
||||
CloseSignalConnection(reason SignallingCloseReason)
|
||||
UpdateLastSeenSignal()
|
||||
SetSignalSourceValid(valid bool)
|
||||
HandleSignalSourceClose()
|
||||
|
||||
// updates
|
||||
CheckMetadataLimits(name string, metadata string, attributes map[string]string) error
|
||||
UpdateMetadata(update *livekit.UpdateParticipantMetadata, fromAdmin bool) error
|
||||
SetName(name string)
|
||||
SetMetadata(metadata string)
|
||||
SetAttributes(attributes map[string]string)
|
||||
@@ -364,13 +448,15 @@ type LocalParticipant interface {
|
||||
CanPublishData() bool
|
||||
|
||||
// PeerConnection
|
||||
AddICECandidate(candidate webrtc.ICECandidateInit, target livekit.SignalTarget)
|
||||
HandleOffer(sdp webrtc.SessionDescription) error
|
||||
GetAnswer() (webrtc.SessionDescription, error)
|
||||
HandleICETrickle(trickleRequest *livekit.TrickleRequest)
|
||||
HandleOffer(sd *livekit.SessionDescription) error
|
||||
GetAnswer() (webrtc.SessionDescription, uint32, error)
|
||||
HandleICETrickleSDPFragment(sdpFragment string) error
|
||||
HandleICERestartSDPFragment(sdpFragment string) (string, error)
|
||||
AddTrack(req *livekit.AddTrackRequest)
|
||||
SetTrackMuted(trackID livekit.TrackID, muted bool, fromAdmin bool) *livekit.TrackInfo
|
||||
SetTrackMuted(mute *livekit.MuteTrackRequest, fromAdmin bool) *livekit.TrackInfo
|
||||
|
||||
HandleAnswer(sdp webrtc.SessionDescription)
|
||||
HandleAnswer(sd *livekit.SessionDescription)
|
||||
Negotiate(force bool)
|
||||
ICERestart(iceConfig *livekit.ICEConfig)
|
||||
AddTrackLocal(trackLocal webrtc.TrackLocal, params AddTrackParams) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error)
|
||||
@@ -380,11 +466,14 @@ type LocalParticipant interface {
|
||||
WriteSubscriberRTCP(pkts []rtcp.Packet) error
|
||||
|
||||
// subscriptions
|
||||
SubscribeToTrack(trackID livekit.TrackID)
|
||||
SubscribeToTrack(trackID livekit.TrackID, isSync bool)
|
||||
UnsubscribeFromTrack(trackID livekit.TrackID)
|
||||
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
|
||||
@@ -403,29 +492,18 @@ type LocalParticipant interface {
|
||||
SendJoinResponse(joinResponse *livekit.JoinResponse) error
|
||||
SendParticipantUpdate(participants []*livekit.ParticipantInfo) error
|
||||
SendSpeakerUpdate(speakers []*livekit.SpeakerInfo, force bool) error
|
||||
SendDataPacket(kind livekit.DataPacket_Kind, encoded []byte) error
|
||||
SendDataMessage(kind livekit.DataPacket_Kind, data []byte, senderID livekit.ParticipantID, seq uint32) error
|
||||
SendDataMessageUnlabeled(data []byte, useRaw bool, sender livekit.ParticipantIdentity) error
|
||||
SendRoomUpdate(room *livekit.Room) error
|
||||
SendConnectionQualityUpdate(update *livekit.ConnectionQualityUpdate) error
|
||||
SubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool)
|
||||
SendSubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool) error
|
||||
SendRefreshToken(token string) error
|
||||
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, state livekit.ParticipantInfo_State))
|
||||
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))
|
||||
OnSubscribeStatusChanged(fn func(publisherID livekit.ParticipantID, subscribed bool))
|
||||
OnClose(callback func(LocalParticipant))
|
||||
AddOnClose(key string, callback func(LocalParticipant))
|
||||
OnClaimsChanged(callback func(LocalParticipant))
|
||||
|
||||
HandleReceiverReport(dt *sfu.DownTrack, report *rtcp.ReceiverReport)
|
||||
@@ -434,13 +512,16 @@ type LocalParticipant interface {
|
||||
MaybeStartMigration(force bool, onStart func()) bool
|
||||
NotifyMigration()
|
||||
SetMigrateState(s MigrateState)
|
||||
MigrateState() MigrateState
|
||||
SetMigrateInfo(
|
||||
previousOffer, previousAnswer *webrtc.SessionDescription,
|
||||
previousOffer *webrtc.SessionDescription,
|
||||
previousAnswer *webrtc.SessionDescription,
|
||||
mediaTracks []*livekit.TrackPublishedResponse,
|
||||
dataChannels []*livekit.DataChannelInfo,
|
||||
dataChannelReceiveState []*livekit.DataChannelReceiveState,
|
||||
dataTracks []*livekit.PublishDataTrackResponse,
|
||||
)
|
||||
IsReconnect() bool
|
||||
MoveToRoom(params MoveToRoomParams)
|
||||
|
||||
UpdateMediaRTT(rtt uint32)
|
||||
UpdateSignalingRTT(rtt uint32)
|
||||
@@ -454,6 +535,7 @@ type LocalParticipant interface {
|
||||
OnICEConfigChanged(callback func(participant LocalParticipant, iceConfig *livekit.ICEConfig))
|
||||
|
||||
UpdateSubscribedQuality(nodeID livekit.NodeID, trackID livekit.TrackID, maxQualities []SubscribedCodecQuality) error
|
||||
UpdateSubscribedAudioCodecs(nodeID livekit.NodeID, trackID livekit.TrackID, codecs []*livekit.SubscribedAudioCodec) error
|
||||
UpdateMediaLoss(nodeID livekit.NodeID, trackID livekit.TrackID, fractionalLoss uint32) error
|
||||
|
||||
// down stream bandwidth management
|
||||
@@ -465,8 +547,177 @@ type LocalParticipant interface {
|
||||
GetDisableSenderReportPassThrough() bool
|
||||
|
||||
HandleMetrics(senderParticipantID livekit.ParticipantID, batch *livekit.MetricsBatch) error
|
||||
HandleUpdateSubscriptions(
|
||||
[]livekit.TrackID,
|
||||
[]*livekit.ParticipantTracks,
|
||||
bool,
|
||||
)
|
||||
HandleUpdateSubscriptionPermission(*livekit.SubscriptionPermission) error
|
||||
HandleSyncState(*livekit.SyncState) error
|
||||
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
|
||||
|
||||
ClearParticipantListener()
|
||||
|
||||
GetNextSubscribedDataTrackHandle() uint16
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
//counterfeiter:generate . ParticipantListener
|
||||
type ParticipantListener interface {
|
||||
OnParticipantUpdate(Participant)
|
||||
OnTrackPublished(Participant, MediaTrack)
|
||||
OnTrackUpdated(Participant, MediaTrack)
|
||||
OnTrackUnpublished(Participant, MediaTrack)
|
||||
OnDataTrackPublished(Participant, DataTrack)
|
||||
OnDataTrackUnpublished(Participant, DataTrack)
|
||||
OnDataTrackMessage(Participant, []byte, *datatrack.Packet)
|
||||
OnMetrics(Participant, *livekit.DataPacket)
|
||||
}
|
||||
|
||||
var _ ParticipantListener = (*NullParticipantListener)(nil)
|
||||
|
||||
type NullParticipantListener struct{}
|
||||
|
||||
func (*NullParticipantListener) OnParticipantUpdate(Participant) {}
|
||||
func (*NullParticipantListener) OnTrackPublished(Participant, MediaTrack) {}
|
||||
func (*NullParticipantListener) OnTrackUpdated(Participant, MediaTrack) {}
|
||||
func (*NullParticipantListener) OnTrackUnpublished(Participant, MediaTrack) {}
|
||||
func (*NullParticipantListener) OnDataTrackPublished(Participant, DataTrack) {}
|
||||
func (*NullParticipantListener) OnDataTrackUnpublished(Participant, DataTrack) {}
|
||||
func (*NullParticipantListener) OnDataTrackMessage(Participant, []byte, *datatrack.Packet) {}
|
||||
func (*NullParticipantListener) OnMetrics(Participant, *livekit.DataPacket) {}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
//counterfeiter:generate . LocalParticipantListener
|
||||
type LocalParticipantListener interface {
|
||||
ParticipantListener
|
||||
|
||||
OnStateChange(LocalParticipant)
|
||||
OnSubscriberReady(LocalParticipant)
|
||||
OnMigrateStateChange(LocalParticipant, MigrateState)
|
||||
OnDataMessage(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)
|
||||
OnDataMessageUnlabeled(LocalParticipant, []byte)
|
||||
OnSubscribeStatusChanged(LocalParticipant, livekit.ParticipantID, bool)
|
||||
OnUpdateSubscriptions(
|
||||
LocalParticipant,
|
||||
[]livekit.TrackID,
|
||||
[]*livekit.ParticipantTracks,
|
||||
bool,
|
||||
)
|
||||
OnUpdateSubscriptionPermission(LocalParticipant, *livekit.SubscriptionPermission) error
|
||||
OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription)
|
||||
OnSyncState(LocalParticipant, *livekit.SyncState) error
|
||||
OnSimulateScenario(LocalParticipant, *livekit.SimulateScenario) error
|
||||
OnLeave(LocalParticipant, ParticipantCloseReason)
|
||||
}
|
||||
|
||||
var _ LocalParticipantListener = (*NullLocalParticipantListener)(nil)
|
||||
|
||||
type NullLocalParticipantListener struct {
|
||||
NullParticipantListener
|
||||
}
|
||||
|
||||
func (*NullLocalParticipantListener) OnStateChange(LocalParticipant) {}
|
||||
func (*NullLocalParticipantListener) OnSubscriberReady(LocalParticipant) {}
|
||||
func (*NullLocalParticipantListener) OnMigrateStateChange(LocalParticipant, MigrateState) {}
|
||||
func (*NullLocalParticipantListener) OnDataMessage(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnDataMessageUnlabeled(LocalParticipant, []byte) {}
|
||||
func (*NullLocalParticipantListener) OnSubscribeStatusChanged(LocalParticipant, livekit.ParticipantID, bool) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnUpdateSubscriptions(
|
||||
LocalParticipant,
|
||||
[]livekit.TrackID,
|
||||
[]*livekit.ParticipantTracks,
|
||||
bool,
|
||||
) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnUpdateSubscriptionPermission(LocalParticipant, *livekit.SubscriptionPermission) error {
|
||||
return nil
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription) {
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnSyncState(LocalParticipant, *livekit.SyncState) error {
|
||||
return nil
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnSimulateScenario(LocalParticipant, *livekit.SimulateScenario) error {
|
||||
return nil
|
||||
}
|
||||
func (*NullLocalParticipantListener) OnLeave(LocalParticipant, ParticipantCloseReason) {}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
//counterfeiter:generate . ParticipantTelemetryListener
|
||||
type ParticipantTelemetryListener interface {
|
||||
OnTrackPublishRequested(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo)
|
||||
OnTrackPublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool)
|
||||
OnTrackUnpublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool)
|
||||
OnTrackSubscribeRequested(pID livekit.ParticipantID, ti *livekit.TrackInfo)
|
||||
OnTrackSubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, publisherInfo *livekit.ParticipantInfo, shouldSendEvent bool)
|
||||
OnTrackUnsubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, shouldSendEvent bool)
|
||||
OnTrackSubscribeFailed(pID livekit.ParticipantID, trackID livekit.TrackID, err error, isUserError bool)
|
||||
OnTrackSubscribeStreamStarted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
|
||||
OnTrackMuted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
|
||||
OnTrackUnmuted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
|
||||
OnTrackPublishedUpdate(pID livekit.ParticipantID, ti *livekit.TrackInfo)
|
||||
OnTrackMaxSubscribedVideoQuality(pID livekit.ParticipantID, ti *livekit.TrackInfo, mime mime.MimeType, maxQuality livekit.VideoQuality)
|
||||
OnTrackPublishRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, layer int, stats *livekit.RTPStats)
|
||||
OnTrackSubscribeRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, stats *livekit.RTPStats)
|
||||
|
||||
OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat)
|
||||
}
|
||||
|
||||
var _ ParticipantTelemetryListener = (*NullParticipantTelemetryListener)(nil)
|
||||
|
||||
type NullParticipantTelemetryListener struct{}
|
||||
|
||||
func (NullParticipantTelemetryListener) OnTrackPublishRequested(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackPublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackUnpublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackSubscribeRequested(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackSubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, publisherInfo *livekit.ParticipantInfo, shouldSendEvent bool) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackUnsubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, shouldSendEvent bool) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackSubscribeFailed(pID livekit.ParticipantID, trackID livekit.TrackID, err error, isUserError bool) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackSubscribeStreamStarted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackMuted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackUnmuted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackPublishedUpdate(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackMaxSubscribedVideoQuality(pID livekit.ParticipantID, ti *livekit.TrackInfo, mime mime.MimeType, maxQuality livekit.VideoQuality) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackPublishRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, layer int, stats *livekit.RTPStats) {
|
||||
}
|
||||
func (NullParticipantTelemetryListener) OnTrackSubscribeRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, stats *livekit.RTPStats) {
|
||||
}
|
||||
|
||||
func (NullParticipantTelemetryListener) OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
// Room is a container of participants, and can provide room-level actions
|
||||
//
|
||||
//counterfeiter:generate . Room
|
||||
@@ -474,11 +725,14 @@ type Room interface {
|
||||
Name() livekit.RoomName
|
||||
ID() livekit.RoomID
|
||||
RemoveParticipant(identity livekit.ParticipantIdentity, pID livekit.ParticipantID, reason ParticipantCloseReason)
|
||||
UpdateSubscriptions(participant LocalParticipant, trackIDs []livekit.TrackID, participantTracks []*livekit.ParticipantTracks, subscribe bool)
|
||||
UpdateSubscriptionPermission(participant LocalParticipant, permissions *livekit.SubscriptionPermission) error
|
||||
SyncState(participant LocalParticipant, state *livekit.SyncState) error
|
||||
SimulateScenario(participant LocalParticipant, scenario *livekit.SimulateScenario) error
|
||||
UpdateSubscriptions(
|
||||
participant LocalParticipant,
|
||||
trackIDs []livekit.TrackID,
|
||||
participantTracks []*livekit.ParticipantTracks,
|
||||
subscribe bool,
|
||||
)
|
||||
ResolveMediaTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) MediaResolverResult
|
||||
ResolveDataTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) DataResolverResult
|
||||
GetLocalParticipants() []LocalParticipant
|
||||
IsDataMessageUserPacketDuplicate(ip *livekit.UserPacket) bool
|
||||
}
|
||||
@@ -501,12 +755,11 @@ type MediaTrack interface {
|
||||
PublisherID() livekit.ParticipantID
|
||||
PublisherIdentity() livekit.ParticipantIdentity
|
||||
PublisherVersion() uint32
|
||||
Logger() logger.Logger
|
||||
|
||||
IsMuted() bool
|
||||
SetMuted(muted bool)
|
||||
|
||||
IsSimulcast() bool
|
||||
|
||||
GetAudioLevel() (level float64, active bool)
|
||||
|
||||
Close(isExpectedToResume bool)
|
||||
@@ -525,15 +778,16 @@ type MediaTrack interface {
|
||||
OnTrackSubscribed()
|
||||
|
||||
// returns quality information that's appropriate for width & height
|
||||
GetQualityForDimension(width, height uint32) livekit.VideoQuality
|
||||
GetQualityForDimension(mimeType mime.MimeType, width, height uint32) livekit.VideoQuality
|
||||
|
||||
// returns temporal layer that's appropriate for fps
|
||||
GetTemporalLayerForSpatialFps(spatial int32, fps uint32, mime mime.MimeType) int32
|
||||
GetTemporalLayerForSpatialFps(mimeType mime.MimeType, spatial int32, fps uint32) int32
|
||||
|
||||
Receivers() []sfu.TrackReceiver
|
||||
ClearAllReceivers(isExpectedToResume bool)
|
||||
|
||||
IsEncrypted() bool
|
||||
HasPacketTrailer() bool
|
||||
}
|
||||
|
||||
//counterfeiter:generate . LocalMediaTrack
|
||||
@@ -542,7 +796,7 @@ type LocalMediaTrack interface {
|
||||
|
||||
Restart()
|
||||
|
||||
SignalCid() string
|
||||
HasSignalCid(cid string) bool
|
||||
HasSdpCid(cid string) bool
|
||||
|
||||
GetConnectionScoreAndQuality() (float32, livekit.ConnectionQuality)
|
||||
@@ -551,9 +805,57 @@ type LocalMediaTrack interface {
|
||||
SetRTT(rtt uint32)
|
||||
|
||||
NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []SubscribedCodecQuality)
|
||||
NotifySubscriptionNode(nodeID livekit.NodeID, codecs []*livekit.SubscribedAudioCodec)
|
||||
ClearSubscriberNodes()
|
||||
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
|
||||
|
||||
PublisherID() livekit.ParticipantID
|
||||
PublisherIdentity() livekit.ParticipantIdentity
|
||||
|
||||
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, arrivalTime int64)
|
||||
|
||||
Close()
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataDownTrack
|
||||
type DataDownTrack interface {
|
||||
Close()
|
||||
|
||||
Handle() uint16
|
||||
PublishDataTrack() DataTrack
|
||||
|
||||
UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions)
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataTrackSender
|
||||
type DataTrackSender interface {
|
||||
SubscriberID() livekit.ParticipantID
|
||||
|
||||
WritePacket(data []byte, packet *datatrack.Packet, arrivalTime int64)
|
||||
}
|
||||
|
||||
//counterfeiter:generate . DataTrackTransport
|
||||
type DataTrackTransport interface {
|
||||
SendDataTrackMessage(data []byte) error
|
||||
}
|
||||
|
||||
//counterfeiter:generate . SubscribedTrack
|
||||
type SubscribedTrack interface {
|
||||
AddOnBind(f func(error))
|
||||
@@ -595,9 +897,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
|
||||
|
||||
@@ -626,7 +939,7 @@ func (o OperationMonitorEvent) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
type OperationMonitorData interface{}
|
||||
type OperationMonitorData any
|
||||
|
||||
type OperationMonitor interface {
|
||||
PostEvent(ome OperationMonitorEvent, omd OperationMonitorData)
|
||||
|
||||
@@ -16,7 +16,7 @@ package types
|
||||
|
||||
type ProtocolVersion int
|
||||
|
||||
const CurrentProtocol = 15
|
||||
const CurrentProtocol = 17
|
||||
|
||||
func (v ProtocolVersion) SupportsPackedStreamId() bool {
|
||||
return v > 0
|
||||
@@ -68,11 +68,11 @@ func (v ProtocolVersion) SupportFastStart() bool {
|
||||
return v > 7
|
||||
}
|
||||
|
||||
func (v ProtocolVersion) SupportHandlesDisconnectedUpdate() bool {
|
||||
func (v ProtocolVersion) SupportsDisconnectedUpdate() bool {
|
||||
return v > 8
|
||||
}
|
||||
|
||||
func (v ProtocolVersion) SupportSyncStreamID() bool {
|
||||
func (v ProtocolVersion) SupportsSyncStreamID() bool {
|
||||
return v > 9
|
||||
}
|
||||
|
||||
@@ -95,3 +95,11 @@ func (v ProtocolVersion) SupportsRegionsInLeaveRequest() bool {
|
||||
func (v ProtocolVersion) SupportsNonErrorSignalResponse() bool {
|
||||
return v > 14
|
||||
}
|
||||
|
||||
func (v ProtocolVersion) SupportsMoving() bool {
|
||||
return v > 15
|
||||
}
|
||||
|
||||
func (v ProtocolVersion) SupportsPacketTrailer() bool {
|
||||
return v > 16
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
// 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 {
|
||||
CloseStub func()
|
||||
closeMutex sync.RWMutex
|
||||
closeArgsForCall []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) 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 *FakeDataDownTrack) CloseCallCount() int {
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
return len(fake.closeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) CloseCalls(stub func()) {
|
||||
fake.closeMutex.Lock()
|
||||
defer fake.closeMutex.Unlock()
|
||||
fake.CloseStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) Handle() uint16 {
|
||||
fake.handleMutex.Lock()
|
||||
ret, specificReturn := fake.handleReturnsOnCall[len(fake.handleArgsForCall)]
|
||||
fake.handleArgsForCall = append(fake.handleArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.HandleStub
|
||||
fakeReturns := fake.handleReturns
|
||||
fake.recordInvocation("Handle", []interface{}{})
|
||||
fake.handleMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleCallCount() int {
|
||||
fake.handleMutex.RLock()
|
||||
defer fake.handleMutex.RUnlock()
|
||||
return len(fake.handleArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleCalls(stub func() uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleReturns(result1 uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = nil
|
||||
fake.handleReturns = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) HandleReturnsOnCall(i int, result1 uint16) {
|
||||
fake.handleMutex.Lock()
|
||||
defer fake.handleMutex.Unlock()
|
||||
fake.HandleStub = nil
|
||||
if fake.handleReturnsOnCall == nil {
|
||||
fake.handleReturnsOnCall = make(map[int]struct {
|
||||
result1 uint16
|
||||
})
|
||||
}
|
||||
fake.handleReturnsOnCall[i] = struct {
|
||||
result1 uint16
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrack() types.DataTrack {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
ret, specificReturn := fake.publishDataTrackReturnsOnCall[len(fake.publishDataTrackArgsForCall)]
|
||||
fake.publishDataTrackArgsForCall = append(fake.publishDataTrackArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.PublishDataTrackStub
|
||||
fakeReturns := fake.publishDataTrackReturns
|
||||
fake.recordInvocation("PublishDataTrack", []interface{}{})
|
||||
fake.publishDataTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackCallCount() int {
|
||||
fake.publishDataTrackMutex.RLock()
|
||||
defer fake.publishDataTrackMutex.RUnlock()
|
||||
return len(fake.publishDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackCalls(stub func() types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackReturns(result1 types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = nil
|
||||
fake.publishDataTrackReturns = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) PublishDataTrackReturnsOnCall(i int, result1 types.DataTrack) {
|
||||
fake.publishDataTrackMutex.Lock()
|
||||
defer fake.publishDataTrackMutex.Unlock()
|
||||
fake.PublishDataTrackStub = nil
|
||||
if fake.publishDataTrackReturnsOnCall == nil {
|
||||
fake.publishDataTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 types.DataTrack
|
||||
})
|
||||
}
|
||||
fake.publishDataTrackReturnsOnCall[i] = struct {
|
||||
result1 types.DataTrack
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptions(arg1 *livekit.DataTrackSubscriptionOptions) {
|
||||
fake.updateSubscriptionOptionsMutex.Lock()
|
||||
fake.updateSubscriptionOptionsArgsForCall = append(fake.updateSubscriptionOptionsArgsForCall, struct {
|
||||
arg1 *livekit.DataTrackSubscriptionOptions
|
||||
}{arg1})
|
||||
stub := fake.UpdateSubscriptionOptionsStub
|
||||
fake.recordInvocation("UpdateSubscriptionOptions", []interface{}{arg1})
|
||||
fake.updateSubscriptionOptionsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.UpdateSubscriptionOptionsStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCallCount() int {
|
||||
fake.updateSubscriptionOptionsMutex.RLock()
|
||||
defer fake.updateSubscriptionOptionsMutex.RUnlock()
|
||||
return len(fake.updateSubscriptionOptionsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCalls(stub func(*livekit.DataTrackSubscriptionOptions)) {
|
||||
fake.updateSubscriptionOptionsMutex.Lock()
|
||||
defer fake.updateSubscriptionOptionsMutex.Unlock()
|
||||
fake.UpdateSubscriptionOptionsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsArgsForCall(i int) *livekit.DataTrackSubscriptionOptions {
|
||||
fake.updateSubscriptionOptionsMutex.RLock()
|
||||
defer fake.updateSubscriptionOptionsMutex.RUnlock()
|
||||
argsForCall := fake.updateSubscriptionOptionsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *FakeDataDownTrack) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
||||
var _ types.DataDownTrack = new(FakeDataDownTrack)
|
||||
@@ -0,0 +1,786 @@
|
||||
// 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, int64)
|
||||
handlePacketMutex sync.RWMutex
|
||||
handlePacketArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
arg3 int64
|
||||
}
|
||||
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
|
||||
}
|
||||
PublisherIDStub func() livekit.ParticipantID
|
||||
publisherIDMutex sync.RWMutex
|
||||
publisherIDArgsForCall []struct {
|
||||
}
|
||||
publisherIDReturns struct {
|
||||
result1 livekit.ParticipantID
|
||||
}
|
||||
publisherIDReturnsOnCall map[int]struct {
|
||||
result1 livekit.ParticipantID
|
||||
}
|
||||
PublisherIdentityStub func() livekit.ParticipantIdentity
|
||||
publisherIdentityMutex sync.RWMutex
|
||||
publisherIdentityArgsForCall []struct {
|
||||
}
|
||||
publisherIdentityReturns struct {
|
||||
result1 livekit.ParticipantIdentity
|
||||
}
|
||||
publisherIdentityReturnsOnCall map[int]struct {
|
||||
result1 livekit.ParticipantIdentity
|
||||
}
|
||||
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, arg3 int64) {
|
||||
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
|
||||
arg3 int64
|
||||
}{arg1Copy, arg2, arg3})
|
||||
stub := fake.HandlePacketStub
|
||||
fake.recordInvocation("HandlePacket", []interface{}{arg1Copy, arg2, arg3})
|
||||
fake.handlePacketMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandlePacketStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
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, int64)) {
|
||||
fake.handlePacketMutex.Lock()
|
||||
defer fake.handlePacketMutex.Unlock()
|
||||
fake.HandlePacketStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) HandlePacketArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
|
||||
fake.handlePacketMutex.RLock()
|
||||
defer fake.handlePacketMutex.RUnlock()
|
||||
argsForCall := fake.handlePacketArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
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) PublisherID() livekit.ParticipantID {
|
||||
fake.publisherIDMutex.Lock()
|
||||
ret, specificReturn := fake.publisherIDReturnsOnCall[len(fake.publisherIDArgsForCall)]
|
||||
fake.publisherIDArgsForCall = append(fake.publisherIDArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.PublisherIDStub
|
||||
fakeReturns := fake.publisherIDReturns
|
||||
fake.recordInvocation("PublisherID", []interface{}{})
|
||||
fake.publisherIDMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIDCallCount() int {
|
||||
fake.publisherIDMutex.RLock()
|
||||
defer fake.publisherIDMutex.RUnlock()
|
||||
return len(fake.publisherIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIDCalls(stub func() livekit.ParticipantID) {
|
||||
fake.publisherIDMutex.Lock()
|
||||
defer fake.publisherIDMutex.Unlock()
|
||||
fake.PublisherIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIDReturns(result1 livekit.ParticipantID) {
|
||||
fake.publisherIDMutex.Lock()
|
||||
defer fake.publisherIDMutex.Unlock()
|
||||
fake.PublisherIDStub = nil
|
||||
fake.publisherIDReturns = struct {
|
||||
result1 livekit.ParticipantID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIDReturnsOnCall(i int, result1 livekit.ParticipantID) {
|
||||
fake.publisherIDMutex.Lock()
|
||||
defer fake.publisherIDMutex.Unlock()
|
||||
fake.PublisherIDStub = nil
|
||||
if fake.publisherIDReturnsOnCall == nil {
|
||||
fake.publisherIDReturnsOnCall = make(map[int]struct {
|
||||
result1 livekit.ParticipantID
|
||||
})
|
||||
}
|
||||
fake.publisherIDReturnsOnCall[i] = struct {
|
||||
result1 livekit.ParticipantID
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIdentity() livekit.ParticipantIdentity {
|
||||
fake.publisherIdentityMutex.Lock()
|
||||
ret, specificReturn := fake.publisherIdentityReturnsOnCall[len(fake.publisherIdentityArgsForCall)]
|
||||
fake.publisherIdentityArgsForCall = append(fake.publisherIdentityArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.PublisherIdentityStub
|
||||
fakeReturns := fake.publisherIdentityReturns
|
||||
fake.recordInvocation("PublisherIdentity", []interface{}{})
|
||||
fake.publisherIdentityMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIdentityCallCount() int {
|
||||
fake.publisherIdentityMutex.RLock()
|
||||
defer fake.publisherIdentityMutex.RUnlock()
|
||||
return len(fake.publisherIdentityArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIdentityCalls(stub func() livekit.ParticipantIdentity) {
|
||||
fake.publisherIdentityMutex.Lock()
|
||||
defer fake.publisherIdentityMutex.Unlock()
|
||||
fake.PublisherIdentityStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIdentityReturns(result1 livekit.ParticipantIdentity) {
|
||||
fake.publisherIdentityMutex.Lock()
|
||||
defer fake.publisherIdentityMutex.Unlock()
|
||||
fake.PublisherIdentityStub = nil
|
||||
fake.publisherIdentityReturns = struct {
|
||||
result1 livekit.ParticipantIdentity
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrack) PublisherIdentityReturnsOnCall(i int, result1 livekit.ParticipantIdentity) {
|
||||
fake.publisherIdentityMutex.Lock()
|
||||
defer fake.publisherIdentityMutex.Unlock()
|
||||
fake.PublisherIdentityStub = nil
|
||||
if fake.publisherIdentityReturnsOnCall == nil {
|
||||
fake.publisherIdentityReturnsOnCall = make(map[int]struct {
|
||||
result1 livekit.ParticipantIdentity
|
||||
})
|
||||
}
|
||||
fake.publisherIdentityReturnsOnCall[i] = struct {
|
||||
result1 livekit.ParticipantIdentity
|
||||
}{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,148 @@
|
||||
// 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, int64)
|
||||
writePacketMutex sync.RWMutex
|
||||
writePacketArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
arg3 int64
|
||||
}
|
||||
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, arg3 int64) {
|
||||
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
|
||||
arg3 int64
|
||||
}{arg1Copy, arg2, arg3})
|
||||
stub := fake.WritePacketStub
|
||||
fake.recordInvocation("WritePacket", []interface{}{arg1Copy, arg2, arg3})
|
||||
fake.writePacketMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.WritePacketStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
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, int64)) {
|
||||
fake.writePacketMutex.Lock()
|
||||
defer fake.writePacketMutex.Unlock()
|
||||
fake.WritePacketStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeDataTrackSender) WritePacketArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
|
||||
fake.writePacketMutex.RLock()
|
||||
defer fake.writePacketMutex.RUnlock()
|
||||
argsForCall := fake.writePacketArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type FakeLocalMediaTrack struct {
|
||||
@@ -34,6 +35,10 @@ type FakeLocalMediaTrack struct {
|
||||
clearAllReceiversArgsForCall []struct {
|
||||
arg1 bool
|
||||
}
|
||||
ClearSubscriberNodesStub func()
|
||||
clearSubscriberNodesMutex sync.RWMutex
|
||||
clearSubscriberNodesArgsForCall []struct {
|
||||
}
|
||||
CloseStub func(bool)
|
||||
closeMutex sync.RWMutex
|
||||
closeArgsForCall []struct {
|
||||
@@ -83,11 +88,12 @@ type FakeLocalMediaTrack struct {
|
||||
getNumSubscribersReturnsOnCall map[int]struct {
|
||||
result1 int
|
||||
}
|
||||
GetQualityForDimensionStub func(uint32, uint32) livekit.VideoQuality
|
||||
GetQualityForDimensionStub func(mime.MimeType, uint32, uint32) livekit.VideoQuality
|
||||
getQualityForDimensionMutex sync.RWMutex
|
||||
getQualityForDimensionArgsForCall []struct {
|
||||
arg1 uint32
|
||||
arg1 mime.MimeType
|
||||
arg2 uint32
|
||||
arg3 uint32
|
||||
}
|
||||
getQualityForDimensionReturns struct {
|
||||
result1 livekit.VideoQuality
|
||||
@@ -95,12 +101,12 @@ type FakeLocalMediaTrack struct {
|
||||
getQualityForDimensionReturnsOnCall map[int]struct {
|
||||
result1 livekit.VideoQuality
|
||||
}
|
||||
GetTemporalLayerForSpatialFpsStub func(int32, uint32, mime.MimeType) int32
|
||||
GetTemporalLayerForSpatialFpsStub func(mime.MimeType, int32, uint32) int32
|
||||
getTemporalLayerForSpatialFpsMutex sync.RWMutex
|
||||
getTemporalLayerForSpatialFpsArgsForCall []struct {
|
||||
arg1 int32
|
||||
arg2 uint32
|
||||
arg3 mime.MimeType
|
||||
arg1 mime.MimeType
|
||||
arg2 int32
|
||||
arg3 uint32
|
||||
}
|
||||
getTemporalLayerForSpatialFpsReturns struct {
|
||||
result1 int32
|
||||
@@ -118,6 +124,16 @@ type FakeLocalMediaTrack struct {
|
||||
getTrackStatsReturnsOnCall map[int]struct {
|
||||
result1 *livekit.RTPStats
|
||||
}
|
||||
HasPacketTrailerStub func() bool
|
||||
hasPacketTrailerMutex sync.RWMutex
|
||||
hasPacketTrailerArgsForCall []struct {
|
||||
}
|
||||
hasPacketTrailerReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
hasPacketTrailerReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
HasSdpCidStub func(string) bool
|
||||
hasSdpCidMutex sync.RWMutex
|
||||
hasSdpCidArgsForCall []struct {
|
||||
@@ -129,6 +145,17 @@ type FakeLocalMediaTrack struct {
|
||||
hasSdpCidReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
HasSignalCidStub func(string) bool
|
||||
hasSignalCidMutex sync.RWMutex
|
||||
hasSignalCidArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
hasSignalCidReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
hasSignalCidReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IDStub func() livekit.TrackID
|
||||
iDMutex sync.RWMutex
|
||||
iDArgsForCall []struct {
|
||||
@@ -169,16 +196,6 @@ type FakeLocalMediaTrack struct {
|
||||
isOpenReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsSimulcastStub func() bool
|
||||
isSimulcastMutex sync.RWMutex
|
||||
isSimulcastArgsForCall []struct {
|
||||
}
|
||||
isSimulcastReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
isSimulcastReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsSubscriberStub func(livekit.ParticipantID) bool
|
||||
isSubscriberMutex sync.RWMutex
|
||||
isSubscriberArgsForCall []struct {
|
||||
@@ -200,6 +217,16 @@ type FakeLocalMediaTrack struct {
|
||||
kindReturnsOnCall map[int]struct {
|
||||
result1 livekit.TrackType
|
||||
}
|
||||
LoggerStub func() logger.Logger
|
||||
loggerMutex sync.RWMutex
|
||||
loggerArgsForCall []struct {
|
||||
}
|
||||
loggerReturns struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
loggerReturnsOnCall map[int]struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
NameStub func() string
|
||||
nameMutex sync.RWMutex
|
||||
nameArgsForCall []struct {
|
||||
@@ -222,6 +249,12 @@ type FakeLocalMediaTrack struct {
|
||||
arg1 livekit.NodeID
|
||||
arg2 uint8
|
||||
}
|
||||
NotifySubscriptionNodeStub func(livekit.NodeID, []*livekit.SubscribedAudioCodec)
|
||||
notifySubscriptionNodeMutex sync.RWMutex
|
||||
notifySubscriptionNodeArgsForCall []struct {
|
||||
arg1 livekit.NodeID
|
||||
arg2 []*livekit.SubscribedAudioCodec
|
||||
}
|
||||
OnTrackSubscribedStub func()
|
||||
onTrackSubscribedMutex sync.RWMutex
|
||||
onTrackSubscribedArgsForCall []struct {
|
||||
@@ -297,16 +330,6 @@ type FakeLocalMediaTrack struct {
|
||||
setRTTArgsForCall []struct {
|
||||
arg1 uint32
|
||||
}
|
||||
SignalCidStub func() string
|
||||
signalCidMutex sync.RWMutex
|
||||
signalCidArgsForCall []struct {
|
||||
}
|
||||
signalCidReturns struct {
|
||||
result1 string
|
||||
}
|
||||
signalCidReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
}
|
||||
SourceStub func() livekit.TrackSource
|
||||
sourceMutex sync.RWMutex
|
||||
sourceArgsForCall []struct {
|
||||
@@ -484,6 +507,30 @@ func (fake *FakeLocalMediaTrack) ClearAllReceiversArgsForCall(i int) bool {
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) ClearSubscriberNodes() {
|
||||
fake.clearSubscriberNodesMutex.Lock()
|
||||
fake.clearSubscriberNodesArgsForCall = append(fake.clearSubscriberNodesArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ClearSubscriberNodesStub
|
||||
fake.recordInvocation("ClearSubscriberNodes", []interface{}{})
|
||||
fake.clearSubscriberNodesMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.ClearSubscriberNodesStub()
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) ClearSubscriberNodesCallCount() int {
|
||||
fake.clearSubscriberNodesMutex.RLock()
|
||||
defer fake.clearSubscriberNodesMutex.RUnlock()
|
||||
return len(fake.clearSubscriberNodesArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) ClearSubscriberNodesCalls(stub func()) {
|
||||
fake.clearSubscriberNodesMutex.Lock()
|
||||
defer fake.clearSubscriberNodesMutex.Unlock()
|
||||
fake.ClearSubscriberNodesStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) Close(arg1 bool) {
|
||||
fake.closeMutex.Lock()
|
||||
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
|
||||
@@ -734,19 +781,20 @@ func (fake *FakeLocalMediaTrack) GetNumSubscribersReturnsOnCall(i int, result1 i
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimension(arg1 uint32, arg2 uint32) livekit.VideoQuality {
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimension(arg1 mime.MimeType, arg2 uint32, arg3 uint32) livekit.VideoQuality {
|
||||
fake.getQualityForDimensionMutex.Lock()
|
||||
ret, specificReturn := fake.getQualityForDimensionReturnsOnCall[len(fake.getQualityForDimensionArgsForCall)]
|
||||
fake.getQualityForDimensionArgsForCall = append(fake.getQualityForDimensionArgsForCall, struct {
|
||||
arg1 uint32
|
||||
arg1 mime.MimeType
|
||||
arg2 uint32
|
||||
}{arg1, arg2})
|
||||
arg3 uint32
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.GetQualityForDimensionStub
|
||||
fakeReturns := fake.getQualityForDimensionReturns
|
||||
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2})
|
||||
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2, arg3})
|
||||
fake.getQualityForDimensionMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
return stub(arg1, arg2, arg3)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
@@ -760,17 +808,17 @@ func (fake *FakeLocalMediaTrack) GetQualityForDimensionCallCount() int {
|
||||
return len(fake.getQualityForDimensionArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimensionCalls(stub func(uint32, uint32) livekit.VideoQuality) {
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimensionCalls(stub func(mime.MimeType, uint32, uint32) livekit.VideoQuality) {
|
||||
fake.getQualityForDimensionMutex.Lock()
|
||||
defer fake.getQualityForDimensionMutex.Unlock()
|
||||
fake.GetQualityForDimensionStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimensionArgsForCall(i int) (uint32, uint32) {
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimensionArgsForCall(i int) (mime.MimeType, uint32, uint32) {
|
||||
fake.getQualityForDimensionMutex.RLock()
|
||||
defer fake.getQualityForDimensionMutex.RUnlock()
|
||||
argsForCall := fake.getQualityForDimensionArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetQualityForDimensionReturns(result1 livekit.VideoQuality) {
|
||||
@@ -796,13 +844,13 @@ func (fake *FakeLocalMediaTrack) GetQualityForDimensionReturnsOnCall(i int, resu
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFps(arg1 int32, arg2 uint32, arg3 mime.MimeType) int32 {
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFps(arg1 mime.MimeType, arg2 int32, arg3 uint32) int32 {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.Lock()
|
||||
ret, specificReturn := fake.getTemporalLayerForSpatialFpsReturnsOnCall[len(fake.getTemporalLayerForSpatialFpsArgsForCall)]
|
||||
fake.getTemporalLayerForSpatialFpsArgsForCall = append(fake.getTemporalLayerForSpatialFpsArgsForCall, struct {
|
||||
arg1 int32
|
||||
arg2 uint32
|
||||
arg3 mime.MimeType
|
||||
arg1 mime.MimeType
|
||||
arg2 int32
|
||||
arg3 uint32
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.GetTemporalLayerForSpatialFpsStub
|
||||
fakeReturns := fake.getTemporalLayerForSpatialFpsReturns
|
||||
@@ -823,13 +871,13 @@ func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCallCount() int {
|
||||
return len(fake.getTemporalLayerForSpatialFpsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(int32, uint32, mime.MimeType) int32) {
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(mime.MimeType, int32, uint32) int32) {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.Lock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.Unlock()
|
||||
fake.GetTemporalLayerForSpatialFpsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (int32, uint32, mime.MimeType) {
|
||||
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (mime.MimeType, int32, uint32) {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.RLock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
|
||||
argsForCall := fake.getTemporalLayerForSpatialFpsArgsForCall[i]
|
||||
@@ -912,6 +960,59 @@ func (fake *FakeLocalMediaTrack) GetTrackStatsReturnsOnCall(i int, result1 *live
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasPacketTrailer() bool {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
ret, specificReturn := fake.hasPacketTrailerReturnsOnCall[len(fake.hasPacketTrailerArgsForCall)]
|
||||
fake.hasPacketTrailerArgsForCall = append(fake.hasPacketTrailerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.HasPacketTrailerStub
|
||||
fakeReturns := fake.hasPacketTrailerReturns
|
||||
fake.recordInvocation("HasPacketTrailer", []interface{}{})
|
||||
fake.hasPacketTrailerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasPacketTrailerCallCount() int {
|
||||
fake.hasPacketTrailerMutex.RLock()
|
||||
defer fake.hasPacketTrailerMutex.RUnlock()
|
||||
return len(fake.hasPacketTrailerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasPacketTrailerCalls(stub func() bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasPacketTrailerReturns(result1 bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = nil
|
||||
fake.hasPacketTrailerReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasPacketTrailerReturnsOnCall(i int, result1 bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = nil
|
||||
if fake.hasPacketTrailerReturnsOnCall == nil {
|
||||
fake.hasPacketTrailerReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.hasPacketTrailerReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSdpCid(arg1 string) bool {
|
||||
fake.hasSdpCidMutex.Lock()
|
||||
ret, specificReturn := fake.hasSdpCidReturnsOnCall[len(fake.hasSdpCidArgsForCall)]
|
||||
@@ -973,6 +1074,67 @@ func (fake *FakeLocalMediaTrack) HasSdpCidReturnsOnCall(i int, result1 bool) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCid(arg1 string) bool {
|
||||
fake.hasSignalCidMutex.Lock()
|
||||
ret, specificReturn := fake.hasSignalCidReturnsOnCall[len(fake.hasSignalCidArgsForCall)]
|
||||
fake.hasSignalCidArgsForCall = append(fake.hasSignalCidArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
stub := fake.HasSignalCidStub
|
||||
fakeReturns := fake.hasSignalCidReturns
|
||||
fake.recordInvocation("HasSignalCid", []interface{}{arg1})
|
||||
fake.hasSignalCidMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCidCallCount() int {
|
||||
fake.hasSignalCidMutex.RLock()
|
||||
defer fake.hasSignalCidMutex.RUnlock()
|
||||
return len(fake.hasSignalCidArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCidCalls(stub func(string) bool) {
|
||||
fake.hasSignalCidMutex.Lock()
|
||||
defer fake.hasSignalCidMutex.Unlock()
|
||||
fake.HasSignalCidStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCidArgsForCall(i int) string {
|
||||
fake.hasSignalCidMutex.RLock()
|
||||
defer fake.hasSignalCidMutex.RUnlock()
|
||||
argsForCall := fake.hasSignalCidArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCidReturns(result1 bool) {
|
||||
fake.hasSignalCidMutex.Lock()
|
||||
defer fake.hasSignalCidMutex.Unlock()
|
||||
fake.HasSignalCidStub = nil
|
||||
fake.hasSignalCidReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) HasSignalCidReturnsOnCall(i int, result1 bool) {
|
||||
fake.hasSignalCidMutex.Lock()
|
||||
defer fake.hasSignalCidMutex.Unlock()
|
||||
fake.HasSignalCidStub = nil
|
||||
if fake.hasSignalCidReturnsOnCall == nil {
|
||||
fake.hasSignalCidReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.hasSignalCidReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) ID() livekit.TrackID {
|
||||
fake.iDMutex.Lock()
|
||||
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
|
||||
@@ -1185,59 +1347,6 @@ func (fake *FakeLocalMediaTrack) IsOpenReturnsOnCall(i int, result1 bool) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSimulcast() bool {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
ret, specificReturn := fake.isSimulcastReturnsOnCall[len(fake.isSimulcastArgsForCall)]
|
||||
fake.isSimulcastArgsForCall = append(fake.isSimulcastArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.IsSimulcastStub
|
||||
fakeReturns := fake.isSimulcastReturns
|
||||
fake.recordInvocation("IsSimulcast", []interface{}{})
|
||||
fake.isSimulcastMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSimulcastCallCount() int {
|
||||
fake.isSimulcastMutex.RLock()
|
||||
defer fake.isSimulcastMutex.RUnlock()
|
||||
return len(fake.isSimulcastArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSimulcastCalls(stub func() bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSimulcastReturns(result1 bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = nil
|
||||
fake.isSimulcastReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSimulcastReturnsOnCall(i int, result1 bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = nil
|
||||
if fake.isSimulcastReturnsOnCall == nil {
|
||||
fake.isSimulcastReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.isSimulcastReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
|
||||
@@ -1352,6 +1461,59 @@ func (fake *FakeLocalMediaTrack) KindReturnsOnCall(i int, result1 livekit.TrackT
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) Logger() logger.Logger {
|
||||
fake.loggerMutex.Lock()
|
||||
ret, specificReturn := fake.loggerReturnsOnCall[len(fake.loggerArgsForCall)]
|
||||
fake.loggerArgsForCall = append(fake.loggerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.LoggerStub
|
||||
fakeReturns := fake.loggerReturns
|
||||
fake.recordInvocation("Logger", []interface{}{})
|
||||
fake.loggerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) LoggerCallCount() int {
|
||||
fake.loggerMutex.RLock()
|
||||
defer fake.loggerMutex.RUnlock()
|
||||
return len(fake.loggerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) LoggerCalls(stub func() logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) LoggerReturns(result1 logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = nil
|
||||
fake.loggerReturns = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) LoggerReturnsOnCall(i int, result1 logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = nil
|
||||
if fake.loggerReturnsOnCall == nil {
|
||||
fake.loggerReturnsOnCall = make(map[int]struct {
|
||||
result1 logger.Logger
|
||||
})
|
||||
}
|
||||
fake.loggerReturnsOnCall[i] = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) Name() string {
|
||||
fake.nameMutex.Lock()
|
||||
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
|
||||
@@ -1476,6 +1638,44 @@ func (fake *FakeLocalMediaTrack) NotifySubscriberNodeMediaLossArgsForCall(i int)
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) NotifySubscriptionNode(arg1 livekit.NodeID, arg2 []*livekit.SubscribedAudioCodec) {
|
||||
var arg2Copy []*livekit.SubscribedAudioCodec
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]*livekit.SubscribedAudioCodec, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.notifySubscriptionNodeMutex.Lock()
|
||||
fake.notifySubscriptionNodeArgsForCall = append(fake.notifySubscriptionNodeArgsForCall, struct {
|
||||
arg1 livekit.NodeID
|
||||
arg2 []*livekit.SubscribedAudioCodec
|
||||
}{arg1, arg2Copy})
|
||||
stub := fake.NotifySubscriptionNodeStub
|
||||
fake.recordInvocation("NotifySubscriptionNode", []interface{}{arg1, arg2Copy})
|
||||
fake.notifySubscriptionNodeMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.NotifySubscriptionNodeStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeCallCount() int {
|
||||
fake.notifySubscriptionNodeMutex.RLock()
|
||||
defer fake.notifySubscriptionNodeMutex.RUnlock()
|
||||
return len(fake.notifySubscriptionNodeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeCalls(stub func(livekit.NodeID, []*livekit.SubscribedAudioCodec)) {
|
||||
fake.notifySubscriptionNodeMutex.Lock()
|
||||
defer fake.notifySubscriptionNodeMutex.Unlock()
|
||||
fake.NotifySubscriptionNodeStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeArgsForCall(i int) (livekit.NodeID, []*livekit.SubscribedAudioCodec) {
|
||||
fake.notifySubscriptionNodeMutex.RLock()
|
||||
defer fake.notifySubscriptionNodeMutex.RUnlock()
|
||||
argsForCall := fake.notifySubscriptionNodeArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) OnTrackSubscribed() {
|
||||
fake.onTrackSubscribedMutex.Lock()
|
||||
fake.onTrackSubscribedArgsForCall = append(fake.onTrackSubscribedArgsForCall, struct {
|
||||
@@ -1899,59 +2099,6 @@ func (fake *FakeLocalMediaTrack) SetRTTArgsForCall(i int) uint32 {
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) SignalCid() string {
|
||||
fake.signalCidMutex.Lock()
|
||||
ret, specificReturn := fake.signalCidReturnsOnCall[len(fake.signalCidArgsForCall)]
|
||||
fake.signalCidArgsForCall = append(fake.signalCidArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.SignalCidStub
|
||||
fakeReturns := fake.signalCidReturns
|
||||
fake.recordInvocation("SignalCid", []interface{}{})
|
||||
fake.signalCidMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) SignalCidCallCount() int {
|
||||
fake.signalCidMutex.RLock()
|
||||
defer fake.signalCidMutex.RUnlock()
|
||||
return len(fake.signalCidArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) SignalCidCalls(stub func() string) {
|
||||
fake.signalCidMutex.Lock()
|
||||
defer fake.signalCidMutex.Unlock()
|
||||
fake.SignalCidStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) SignalCidReturns(result1 string) {
|
||||
fake.signalCidMutex.Lock()
|
||||
defer fake.signalCidMutex.Unlock()
|
||||
fake.SignalCidStub = nil
|
||||
fake.signalCidReturns = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) SignalCidReturnsOnCall(i int, result1 string) {
|
||||
fake.signalCidMutex.Lock()
|
||||
defer fake.signalCidMutex.Unlock()
|
||||
fake.SignalCidStub = nil
|
||||
if fake.signalCidReturnsOnCall == nil {
|
||||
fake.signalCidReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
})
|
||||
}
|
||||
fake.signalCidReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalMediaTrack) Source() livekit.TrackSource {
|
||||
fake.sourceMutex.Lock()
|
||||
ret, specificReturn := fake.sourceReturnsOnCall[len(fake.sourceArgsForCall)]
|
||||
@@ -2210,84 +2357,6 @@ func (fake *FakeLocalMediaTrack) UpdateVideoTrackArgsForCall(i int) *livekit.Upd
|
||||
func (fake *FakeLocalMediaTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.addOnCloseMutex.RLock()
|
||||
defer fake.addOnCloseMutex.RUnlock()
|
||||
fake.addSubscriberMutex.RLock()
|
||||
defer fake.addSubscriberMutex.RUnlock()
|
||||
fake.clearAllReceiversMutex.RLock()
|
||||
defer fake.clearAllReceiversMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.getAllSubscribersMutex.RLock()
|
||||
defer fake.getAllSubscribersMutex.RUnlock()
|
||||
fake.getAudioLevelMutex.RLock()
|
||||
defer fake.getAudioLevelMutex.RUnlock()
|
||||
fake.getConnectionScoreAndQualityMutex.RLock()
|
||||
defer fake.getConnectionScoreAndQualityMutex.RUnlock()
|
||||
fake.getNumSubscribersMutex.RLock()
|
||||
defer fake.getNumSubscribersMutex.RUnlock()
|
||||
fake.getQualityForDimensionMutex.RLock()
|
||||
defer fake.getQualityForDimensionMutex.RUnlock()
|
||||
fake.getTemporalLayerForSpatialFpsMutex.RLock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
|
||||
fake.getTrackStatsMutex.RLock()
|
||||
defer fake.getTrackStatsMutex.RUnlock()
|
||||
fake.hasSdpCidMutex.RLock()
|
||||
defer fake.hasSdpCidMutex.RUnlock()
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
fake.isEncryptedMutex.RLock()
|
||||
defer fake.isEncryptedMutex.RUnlock()
|
||||
fake.isMutedMutex.RLock()
|
||||
defer fake.isMutedMutex.RUnlock()
|
||||
fake.isOpenMutex.RLock()
|
||||
defer fake.isOpenMutex.RUnlock()
|
||||
fake.isSimulcastMutex.RLock()
|
||||
defer fake.isSimulcastMutex.RUnlock()
|
||||
fake.isSubscriberMutex.RLock()
|
||||
defer fake.isSubscriberMutex.RUnlock()
|
||||
fake.kindMutex.RLock()
|
||||
defer fake.kindMutex.RUnlock()
|
||||
fake.nameMutex.RLock()
|
||||
defer fake.nameMutex.RUnlock()
|
||||
fake.notifySubscriberNodeMaxQualityMutex.RLock()
|
||||
defer fake.notifySubscriberNodeMaxQualityMutex.RUnlock()
|
||||
fake.notifySubscriberNodeMediaLossMutex.RLock()
|
||||
defer fake.notifySubscriberNodeMediaLossMutex.RUnlock()
|
||||
fake.onTrackSubscribedMutex.RLock()
|
||||
defer fake.onTrackSubscribedMutex.RUnlock()
|
||||
fake.publisherIDMutex.RLock()
|
||||
defer fake.publisherIDMutex.RUnlock()
|
||||
fake.publisherIdentityMutex.RLock()
|
||||
defer fake.publisherIdentityMutex.RUnlock()
|
||||
fake.publisherVersionMutex.RLock()
|
||||
defer fake.publisherVersionMutex.RUnlock()
|
||||
fake.receiversMutex.RLock()
|
||||
defer fake.receiversMutex.RUnlock()
|
||||
fake.removeSubscriberMutex.RLock()
|
||||
defer fake.removeSubscriberMutex.RUnlock()
|
||||
fake.restartMutex.RLock()
|
||||
defer fake.restartMutex.RUnlock()
|
||||
fake.revokeDisallowedSubscribersMutex.RLock()
|
||||
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
|
||||
fake.setMutedMutex.RLock()
|
||||
defer fake.setMutedMutex.RUnlock()
|
||||
fake.setRTTMutex.RLock()
|
||||
defer fake.setRTTMutex.RUnlock()
|
||||
fake.signalCidMutex.RLock()
|
||||
defer fake.signalCidMutex.RUnlock()
|
||||
fake.sourceMutex.RLock()
|
||||
defer fake.sourceMutex.RUnlock()
|
||||
fake.streamMutex.RLock()
|
||||
defer fake.streamMutex.RUnlock()
|
||||
fake.toProtoMutex.RLock()
|
||||
defer fake.toProtoMutex.RUnlock()
|
||||
fake.updateAudioTrackMutex.RLock()
|
||||
defer fake.updateAudioTrackMutex.RUnlock()
|
||||
fake.updateTrackInfoMutex.RLock()
|
||||
defer fake.updateTrackInfoMutex.RUnlock()
|
||||
fake.updateVideoTrackMutex.RLock()
|
||||
defer fake.updateVideoTrackMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,542 @@
|
||||
// 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 FakeLocalParticipantHelper struct {
|
||||
GetCachedReliableDataMessageStub func(map[livekit.ParticipantID]uint32) []*types.DataMessageCache
|
||||
getCachedReliableDataMessageMutex sync.RWMutex
|
||||
getCachedReliableDataMessageArgsForCall []struct {
|
||||
arg1 map[livekit.ParticipantID]uint32
|
||||
}
|
||||
getCachedReliableDataMessageReturns struct {
|
||||
result1 []*types.DataMessageCache
|
||||
}
|
||||
getCachedReliableDataMessageReturnsOnCall map[int]struct {
|
||||
result1 []*types.DataMessageCache
|
||||
}
|
||||
GetParticipantInfoStub func(livekit.ParticipantID) *livekit.ParticipantInfo
|
||||
getParticipantInfoMutex sync.RWMutex
|
||||
getParticipantInfoArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}
|
||||
getParticipantInfoReturns struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
}
|
||||
getParticipantInfoReturnsOnCall map[int]struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
}
|
||||
GetRegionSettingsStub func(string) *livekit.RegionSettings
|
||||
getRegionSettingsMutex sync.RWMutex
|
||||
getRegionSettingsArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
getRegionSettingsReturns struct {
|
||||
result1 *livekit.RegionSettings
|
||||
}
|
||||
getRegionSettingsReturnsOnCall map[int]struct {
|
||||
result1 *livekit.RegionSettings
|
||||
}
|
||||
GetSubscriberForwarderStateStub func(types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)
|
||||
getSubscriberForwarderStateMutex sync.RWMutex
|
||||
getSubscriberForwarderStateArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
}
|
||||
getSubscriberForwarderStateReturns struct {
|
||||
result1 map[livekit.TrackID]*livekit.RTPForwarderState
|
||||
result2 error
|
||||
}
|
||||
getSubscriberForwarderStateReturnsOnCall map[int]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 {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}
|
||||
resolveMediaTrackReturns struct {
|
||||
result1 types.MediaResolverResult
|
||||
}
|
||||
resolveMediaTrackReturnsOnCall map[int]struct {
|
||||
result1 types.MediaResolverResult
|
||||
}
|
||||
ShouldRegressCodecStub func() bool
|
||||
shouldRegressCodecMutex sync.RWMutex
|
||||
shouldRegressCodecArgsForCall []struct {
|
||||
}
|
||||
shouldRegressCodecReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
shouldRegressCodecReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessage(arg1 map[livekit.ParticipantID]uint32) []*types.DataMessageCache {
|
||||
fake.getCachedReliableDataMessageMutex.Lock()
|
||||
ret, specificReturn := fake.getCachedReliableDataMessageReturnsOnCall[len(fake.getCachedReliableDataMessageArgsForCall)]
|
||||
fake.getCachedReliableDataMessageArgsForCall = append(fake.getCachedReliableDataMessageArgsForCall, struct {
|
||||
arg1 map[livekit.ParticipantID]uint32
|
||||
}{arg1})
|
||||
stub := fake.GetCachedReliableDataMessageStub
|
||||
fakeReturns := fake.getCachedReliableDataMessageReturns
|
||||
fake.recordInvocation("GetCachedReliableDataMessage", []interface{}{arg1})
|
||||
fake.getCachedReliableDataMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageCallCount() int {
|
||||
fake.getCachedReliableDataMessageMutex.RLock()
|
||||
defer fake.getCachedReliableDataMessageMutex.RUnlock()
|
||||
return len(fake.getCachedReliableDataMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageCalls(stub func(map[livekit.ParticipantID]uint32) []*types.DataMessageCache) {
|
||||
fake.getCachedReliableDataMessageMutex.Lock()
|
||||
defer fake.getCachedReliableDataMessageMutex.Unlock()
|
||||
fake.GetCachedReliableDataMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageArgsForCall(i int) map[livekit.ParticipantID]uint32 {
|
||||
fake.getCachedReliableDataMessageMutex.RLock()
|
||||
defer fake.getCachedReliableDataMessageMutex.RUnlock()
|
||||
argsForCall := fake.getCachedReliableDataMessageArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageReturns(result1 []*types.DataMessageCache) {
|
||||
fake.getCachedReliableDataMessageMutex.Lock()
|
||||
defer fake.getCachedReliableDataMessageMutex.Unlock()
|
||||
fake.GetCachedReliableDataMessageStub = nil
|
||||
fake.getCachedReliableDataMessageReturns = struct {
|
||||
result1 []*types.DataMessageCache
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageReturnsOnCall(i int, result1 []*types.DataMessageCache) {
|
||||
fake.getCachedReliableDataMessageMutex.Lock()
|
||||
defer fake.getCachedReliableDataMessageMutex.Unlock()
|
||||
fake.GetCachedReliableDataMessageStub = nil
|
||||
if fake.getCachedReliableDataMessageReturnsOnCall == nil {
|
||||
fake.getCachedReliableDataMessageReturnsOnCall = make(map[int]struct {
|
||||
result1 []*types.DataMessageCache
|
||||
})
|
||||
}
|
||||
fake.getCachedReliableDataMessageReturnsOnCall[i] = struct {
|
||||
result1 []*types.DataMessageCache
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfo(arg1 livekit.ParticipantID) *livekit.ParticipantInfo {
|
||||
fake.getParticipantInfoMutex.Lock()
|
||||
ret, specificReturn := fake.getParticipantInfoReturnsOnCall[len(fake.getParticipantInfoArgsForCall)]
|
||||
fake.getParticipantInfoArgsForCall = append(fake.getParticipantInfoArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
}{arg1})
|
||||
stub := fake.GetParticipantInfoStub
|
||||
fakeReturns := fake.getParticipantInfoReturns
|
||||
fake.recordInvocation("GetParticipantInfo", []interface{}{arg1})
|
||||
fake.getParticipantInfoMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfoCallCount() int {
|
||||
fake.getParticipantInfoMutex.RLock()
|
||||
defer fake.getParticipantInfoMutex.RUnlock()
|
||||
return len(fake.getParticipantInfoArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfoCalls(stub func(livekit.ParticipantID) *livekit.ParticipantInfo) {
|
||||
fake.getParticipantInfoMutex.Lock()
|
||||
defer fake.getParticipantInfoMutex.Unlock()
|
||||
fake.GetParticipantInfoStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfoArgsForCall(i int) livekit.ParticipantID {
|
||||
fake.getParticipantInfoMutex.RLock()
|
||||
defer fake.getParticipantInfoMutex.RUnlock()
|
||||
argsForCall := fake.getParticipantInfoArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfoReturns(result1 *livekit.ParticipantInfo) {
|
||||
fake.getParticipantInfoMutex.Lock()
|
||||
defer fake.getParticipantInfoMutex.Unlock()
|
||||
fake.GetParticipantInfoStub = nil
|
||||
fake.getParticipantInfoReturns = struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetParticipantInfoReturnsOnCall(i int, result1 *livekit.ParticipantInfo) {
|
||||
fake.getParticipantInfoMutex.Lock()
|
||||
defer fake.getParticipantInfoMutex.Unlock()
|
||||
fake.GetParticipantInfoStub = nil
|
||||
if fake.getParticipantInfoReturnsOnCall == nil {
|
||||
fake.getParticipantInfoReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
})
|
||||
}
|
||||
fake.getParticipantInfoReturnsOnCall[i] = struct {
|
||||
result1 *livekit.ParticipantInfo
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettings(arg1 string) *livekit.RegionSettings {
|
||||
fake.getRegionSettingsMutex.Lock()
|
||||
ret, specificReturn := fake.getRegionSettingsReturnsOnCall[len(fake.getRegionSettingsArgsForCall)]
|
||||
fake.getRegionSettingsArgsForCall = append(fake.getRegionSettingsArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
stub := fake.GetRegionSettingsStub
|
||||
fakeReturns := fake.getRegionSettingsReturns
|
||||
fake.recordInvocation("GetRegionSettings", []interface{}{arg1})
|
||||
fake.getRegionSettingsMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettingsCallCount() int {
|
||||
fake.getRegionSettingsMutex.RLock()
|
||||
defer fake.getRegionSettingsMutex.RUnlock()
|
||||
return len(fake.getRegionSettingsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettingsCalls(stub func(string) *livekit.RegionSettings) {
|
||||
fake.getRegionSettingsMutex.Lock()
|
||||
defer fake.getRegionSettingsMutex.Unlock()
|
||||
fake.GetRegionSettingsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettingsArgsForCall(i int) string {
|
||||
fake.getRegionSettingsMutex.RLock()
|
||||
defer fake.getRegionSettingsMutex.RUnlock()
|
||||
argsForCall := fake.getRegionSettingsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettingsReturns(result1 *livekit.RegionSettings) {
|
||||
fake.getRegionSettingsMutex.Lock()
|
||||
defer fake.getRegionSettingsMutex.Unlock()
|
||||
fake.GetRegionSettingsStub = nil
|
||||
fake.getRegionSettingsReturns = struct {
|
||||
result1 *livekit.RegionSettings
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetRegionSettingsReturnsOnCall(i int, result1 *livekit.RegionSettings) {
|
||||
fake.getRegionSettingsMutex.Lock()
|
||||
defer fake.getRegionSettingsMutex.Unlock()
|
||||
fake.GetRegionSettingsStub = nil
|
||||
if fake.getRegionSettingsReturnsOnCall == nil {
|
||||
fake.getRegionSettingsReturnsOnCall = make(map[int]struct {
|
||||
result1 *livekit.RegionSettings
|
||||
})
|
||||
}
|
||||
fake.getRegionSettingsReturnsOnCall[i] = struct {
|
||||
result1 *livekit.RegionSettings
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderState(arg1 types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error) {
|
||||
fake.getSubscriberForwarderStateMutex.Lock()
|
||||
ret, specificReturn := fake.getSubscriberForwarderStateReturnsOnCall[len(fake.getSubscriberForwarderStateArgsForCall)]
|
||||
fake.getSubscriberForwarderStateArgsForCall = append(fake.getSubscriberForwarderStateArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
}{arg1})
|
||||
stub := fake.GetSubscriberForwarderStateStub
|
||||
fakeReturns := fake.getSubscriberForwarderStateReturns
|
||||
fake.recordInvocation("GetSubscriberForwarderState", []interface{}{arg1})
|
||||
fake.getSubscriberForwarderStateMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateCallCount() int {
|
||||
fake.getSubscriberForwarderStateMutex.RLock()
|
||||
defer fake.getSubscriberForwarderStateMutex.RUnlock()
|
||||
return len(fake.getSubscriberForwarderStateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateCalls(stub func(types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)) {
|
||||
fake.getSubscriberForwarderStateMutex.Lock()
|
||||
defer fake.getSubscriberForwarderStateMutex.Unlock()
|
||||
fake.GetSubscriberForwarderStateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateArgsForCall(i int) types.LocalParticipant {
|
||||
fake.getSubscriberForwarderStateMutex.RLock()
|
||||
defer fake.getSubscriberForwarderStateMutex.RUnlock()
|
||||
argsForCall := fake.getSubscriberForwarderStateArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateReturns(result1 map[livekit.TrackID]*livekit.RTPForwarderState, result2 error) {
|
||||
fake.getSubscriberForwarderStateMutex.Lock()
|
||||
defer fake.getSubscriberForwarderStateMutex.Unlock()
|
||||
fake.GetSubscriberForwarderStateStub = nil
|
||||
fake.getSubscriberForwarderStateReturns = struct {
|
||||
result1 map[livekit.TrackID]*livekit.RTPForwarderState
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateReturnsOnCall(i int, result1 map[livekit.TrackID]*livekit.RTPForwarderState, result2 error) {
|
||||
fake.getSubscriberForwarderStateMutex.Lock()
|
||||
defer fake.getSubscriberForwarderStateMutex.Unlock()
|
||||
fake.GetSubscriberForwarderStateStub = nil
|
||||
if fake.getSubscriberForwarderStateReturnsOnCall == nil {
|
||||
fake.getSubscriberForwarderStateReturnsOnCall = make(map[int]struct {
|
||||
result1 map[livekit.TrackID]*livekit.RTPForwarderState
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getSubscriberForwarderStateReturnsOnCall[i] = struct {
|
||||
result1 map[livekit.TrackID]*livekit.RTPForwarderState
|
||||
result2 error
|
||||
}{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)]
|
||||
fake.resolveMediaTrackArgsForCall = append(fake.resolveMediaTrackArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.TrackID
|
||||
}{arg1, arg2})
|
||||
stub := fake.ResolveMediaTrackStub
|
||||
fakeReturns := fake.resolveMediaTrackReturns
|
||||
fake.recordInvocation("ResolveMediaTrack", []interface{}{arg1, arg2})
|
||||
fake.resolveMediaTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackCallCount() int {
|
||||
fake.resolveMediaTrackMutex.RLock()
|
||||
defer fake.resolveMediaTrackMutex.RUnlock()
|
||||
return len(fake.resolveMediaTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackCalls(stub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult) {
|
||||
fake.resolveMediaTrackMutex.Lock()
|
||||
defer fake.resolveMediaTrackMutex.Unlock()
|
||||
fake.ResolveMediaTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
|
||||
fake.resolveMediaTrackMutex.RLock()
|
||||
defer fake.resolveMediaTrackMutex.RUnlock()
|
||||
argsForCall := fake.resolveMediaTrackArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackReturns(result1 types.MediaResolverResult) {
|
||||
fake.resolveMediaTrackMutex.Lock()
|
||||
defer fake.resolveMediaTrackMutex.Unlock()
|
||||
fake.ResolveMediaTrackStub = nil
|
||||
fake.resolveMediaTrackReturns = struct {
|
||||
result1 types.MediaResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackReturnsOnCall(i int, result1 types.MediaResolverResult) {
|
||||
fake.resolveMediaTrackMutex.Lock()
|
||||
defer fake.resolveMediaTrackMutex.Unlock()
|
||||
fake.ResolveMediaTrackStub = nil
|
||||
if fake.resolveMediaTrackReturnsOnCall == nil {
|
||||
fake.resolveMediaTrackReturnsOnCall = make(map[int]struct {
|
||||
result1 types.MediaResolverResult
|
||||
})
|
||||
}
|
||||
fake.resolveMediaTrackReturnsOnCall[i] = struct {
|
||||
result1 types.MediaResolverResult
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ShouldRegressCodec() bool {
|
||||
fake.shouldRegressCodecMutex.Lock()
|
||||
ret, specificReturn := fake.shouldRegressCodecReturnsOnCall[len(fake.shouldRegressCodecArgsForCall)]
|
||||
fake.shouldRegressCodecArgsForCall = append(fake.shouldRegressCodecArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.ShouldRegressCodecStub
|
||||
fakeReturns := fake.shouldRegressCodecReturns
|
||||
fake.recordInvocation("ShouldRegressCodec", []interface{}{})
|
||||
fake.shouldRegressCodecMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecCallCount() int {
|
||||
fake.shouldRegressCodecMutex.RLock()
|
||||
defer fake.shouldRegressCodecMutex.RUnlock()
|
||||
return len(fake.shouldRegressCodecArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecCalls(stub func() bool) {
|
||||
fake.shouldRegressCodecMutex.Lock()
|
||||
defer fake.shouldRegressCodecMutex.Unlock()
|
||||
fake.ShouldRegressCodecStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecReturns(result1 bool) {
|
||||
fake.shouldRegressCodecMutex.Lock()
|
||||
defer fake.shouldRegressCodecMutex.Unlock()
|
||||
fake.ShouldRegressCodecStub = nil
|
||||
fake.shouldRegressCodecReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecReturnsOnCall(i int, result1 bool) {
|
||||
fake.shouldRegressCodecMutex.Lock()
|
||||
defer fake.shouldRegressCodecMutex.Unlock()
|
||||
fake.ShouldRegressCodecStub = nil
|
||||
if fake.shouldRegressCodecReturnsOnCall == nil {
|
||||
fake.shouldRegressCodecReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.shouldRegressCodecReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantHelper) 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 *FakeLocalParticipantHelper) 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.LocalParticipantHelper = new(FakeLocalParticipantHelper)
|
||||
@@ -0,0 +1,948 @@
|
||||
// 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 FakeLocalParticipantListener struct {
|
||||
OnDataMessageStub func(types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)
|
||||
onDataMessageMutex sync.RWMutex
|
||||
onDataMessageArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.DataPacket_Kind
|
||||
arg3 *livekit.DataPacket
|
||||
}
|
||||
OnDataMessageUnlabeledStub func(types.LocalParticipant, []byte)
|
||||
onDataMessageUnlabeledMutex sync.RWMutex
|
||||
onDataMessageUnlabeledArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 []byte
|
||||
}
|
||||
OnDataTrackMessageStub func(types.Participant, []byte, *datatrack.Packet)
|
||||
onDataTrackMessageMutex sync.RWMutex
|
||||
onDataTrackMessageArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 []byte
|
||||
arg3 *datatrack.Packet
|
||||
}
|
||||
OnDataTrackPublishedStub func(types.Participant, types.DataTrack)
|
||||
onDataTrackPublishedMutex sync.RWMutex
|
||||
onDataTrackPublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}
|
||||
OnDataTrackUnpublishedStub func(types.Participant, types.DataTrack)
|
||||
onDataTrackUnpublishedMutex sync.RWMutex
|
||||
onDataTrackUnpublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}
|
||||
OnLeaveStub func(types.LocalParticipant, types.ParticipantCloseReason)
|
||||
onLeaveMutex sync.RWMutex
|
||||
onLeaveArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 types.ParticipantCloseReason
|
||||
}
|
||||
OnMetricsStub func(types.Participant, *livekit.DataPacket)
|
||||
onMetricsMutex sync.RWMutex
|
||||
onMetricsArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 *livekit.DataPacket
|
||||
}
|
||||
OnMigrateStateChangeStub func(types.LocalParticipant, types.MigrateState)
|
||||
onMigrateStateChangeMutex sync.RWMutex
|
||||
onMigrateStateChangeArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 types.MigrateState
|
||||
}
|
||||
OnParticipantUpdateStub func(types.Participant)
|
||||
onParticipantUpdateMutex sync.RWMutex
|
||||
onParticipantUpdateArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
}
|
||||
OnSimulateScenarioStub func(types.LocalParticipant, *livekit.SimulateScenario) error
|
||||
onSimulateScenarioMutex sync.RWMutex
|
||||
onSimulateScenarioArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SimulateScenario
|
||||
}
|
||||
onSimulateScenarioReturns struct {
|
||||
result1 error
|
||||
}
|
||||
onSimulateScenarioReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
OnStateChangeStub func(types.LocalParticipant)
|
||||
onStateChangeMutex sync.RWMutex
|
||||
onStateChangeArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
}
|
||||
OnSubscribeStatusChangedStub func(types.LocalParticipant, livekit.ParticipantID, bool)
|
||||
onSubscribeStatusChangedMutex sync.RWMutex
|
||||
onSubscribeStatusChangedArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.ParticipantID
|
||||
arg3 bool
|
||||
}
|
||||
OnSubscriberReadyStub func(types.LocalParticipant)
|
||||
onSubscriberReadyMutex sync.RWMutex
|
||||
onSubscriberReadyArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
}
|
||||
OnSyncStateStub func(types.LocalParticipant, *livekit.SyncState) error
|
||||
onSyncStateMutex sync.RWMutex
|
||||
onSyncStateArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SyncState
|
||||
}
|
||||
onSyncStateReturns struct {
|
||||
result1 error
|
||||
}
|
||||
onSyncStateReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
OnTrackPublishedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackPublishedMutex sync.RWMutex
|
||||
onTrackPublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
OnTrackUnpublishedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackUnpublishedMutex sync.RWMutex
|
||||
onTrackUnpublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
OnTrackUpdatedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackUpdatedMutex sync.RWMutex
|
||||
onTrackUpdatedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
OnUpdateDataSubscriptionsStub func(types.LocalParticipant, *livekit.UpdateDataSubscription)
|
||||
onUpdateDataSubscriptionsMutex sync.RWMutex
|
||||
onUpdateDataSubscriptionsArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.UpdateDataSubscription
|
||||
}
|
||||
OnUpdateSubscriptionPermissionStub func(types.LocalParticipant, *livekit.SubscriptionPermission) error
|
||||
onUpdateSubscriptionPermissionMutex sync.RWMutex
|
||||
onUpdateSubscriptionPermissionArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SubscriptionPermission
|
||||
}
|
||||
onUpdateSubscriptionPermissionReturns struct {
|
||||
result1 error
|
||||
}
|
||||
onUpdateSubscriptionPermissionReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
OnUpdateSubscriptionsStub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)
|
||||
onUpdateSubscriptionsMutex sync.RWMutex
|
||||
onUpdateSubscriptionsArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 []livekit.TrackID
|
||||
arg3 []*livekit.ParticipantTracks
|
||||
arg4 bool
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessage(arg1 types.LocalParticipant, arg2 livekit.DataPacket_Kind, arg3 *livekit.DataPacket) {
|
||||
fake.onDataMessageMutex.Lock()
|
||||
fake.onDataMessageArgsForCall = append(fake.onDataMessageArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.DataPacket_Kind
|
||||
arg3 *livekit.DataPacket
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnDataMessageStub
|
||||
fake.recordInvocation("OnDataMessage", []interface{}{arg1, arg2, arg3})
|
||||
fake.onDataMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataMessageStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageCallCount() int {
|
||||
fake.onDataMessageMutex.RLock()
|
||||
defer fake.onDataMessageMutex.RUnlock()
|
||||
return len(fake.onDataMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageCalls(stub func(types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)) {
|
||||
fake.onDataMessageMutex.Lock()
|
||||
defer fake.onDataMessageMutex.Unlock()
|
||||
fake.OnDataMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageArgsForCall(i int) (types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket) {
|
||||
fake.onDataMessageMutex.RLock()
|
||||
defer fake.onDataMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeled(arg1 types.LocalParticipant, arg2 []byte) {
|
||||
var arg2Copy []byte
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]byte, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.onDataMessageUnlabeledMutex.Lock()
|
||||
fake.onDataMessageUnlabeledArgsForCall = append(fake.onDataMessageUnlabeledArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 []byte
|
||||
}{arg1, arg2Copy})
|
||||
stub := fake.OnDataMessageUnlabeledStub
|
||||
fake.recordInvocation("OnDataMessageUnlabeled", []interface{}{arg1, arg2Copy})
|
||||
fake.onDataMessageUnlabeledMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataMessageUnlabeledStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledCallCount() int {
|
||||
fake.onDataMessageUnlabeledMutex.RLock()
|
||||
defer fake.onDataMessageUnlabeledMutex.RUnlock()
|
||||
return len(fake.onDataMessageUnlabeledArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledCalls(stub func(types.LocalParticipant, []byte)) {
|
||||
fake.onDataMessageUnlabeledMutex.Lock()
|
||||
defer fake.onDataMessageUnlabeledMutex.Unlock()
|
||||
fake.OnDataMessageUnlabeledStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledArgsForCall(i int) (types.LocalParticipant, []byte) {
|
||||
fake.onDataMessageUnlabeledMutex.RLock()
|
||||
defer fake.onDataMessageUnlabeledMutex.RUnlock()
|
||||
argsForCall := fake.onDataMessageUnlabeledArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackMessage(arg1 types.Participant, arg2 []byte, arg3 *datatrack.Packet) {
|
||||
var arg2Copy []byte
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]byte, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 []byte
|
||||
arg3 *datatrack.Packet
|
||||
}{arg1, arg2Copy, arg3})
|
||||
stub := fake.OnDataTrackMessageStub
|
||||
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1, arg2Copy, arg3})
|
||||
fake.onDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackMessageStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackMessageCallCount() int {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.onDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackMessageCalls(stub func(types.Participant, []byte, *datatrack.Packet)) {
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
defer fake.onDataTrackMessageMutex.Unlock()
|
||||
fake.OnDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackMessageArgsForCall(i int) (types.Participant, []byte, *datatrack.Packet) {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackPublished(arg1 types.Participant, arg2 types.DataTrack) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
fake.onDataTrackPublishedArgsForCall = append(fake.onDataTrackPublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnDataTrackPublishedStub
|
||||
fake.recordInvocation("OnDataTrackPublished", []interface{}{arg1, arg2})
|
||||
fake.onDataTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackPublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedCallCount() int {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedCalls(stub func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
defer fake.onDataTrackPublishedMutex.Unlock()
|
||||
fake.OnDataTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublished(arg1 types.Participant, arg2 types.DataTrack) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
fake.onDataTrackUnpublishedArgsForCall = append(fake.onDataTrackUnpublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnDataTrackUnpublishedStub
|
||||
fake.recordInvocation("OnDataTrackUnpublished", []interface{}{arg1, arg2})
|
||||
fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackUnpublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedCallCount() int {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedCalls(stub func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
defer fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
fake.OnDataTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackUnpublishedArgsForCall[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 {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 types.ParticipantCloseReason
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnLeaveStub
|
||||
fake.recordInvocation("OnLeave", []interface{}{arg1, arg2})
|
||||
fake.onLeaveMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnLeaveStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnLeaveCallCount() int {
|
||||
fake.onLeaveMutex.RLock()
|
||||
defer fake.onLeaveMutex.RUnlock()
|
||||
return len(fake.onLeaveArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnLeaveCalls(stub func(types.LocalParticipant, types.ParticipantCloseReason)) {
|
||||
fake.onLeaveMutex.Lock()
|
||||
defer fake.onLeaveMutex.Unlock()
|
||||
fake.OnLeaveStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnLeaveArgsForCall(i int) (types.LocalParticipant, types.ParticipantCloseReason) {
|
||||
fake.onLeaveMutex.RLock()
|
||||
defer fake.onLeaveMutex.RUnlock()
|
||||
argsForCall := fake.onLeaveArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMetrics(arg1 types.Participant, arg2 *livekit.DataPacket) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 *livekit.DataPacket
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnMetricsStub
|
||||
fake.recordInvocation("OnMetrics", []interface{}{arg1, arg2})
|
||||
fake.onMetricsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnMetricsStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMetricsCallCount() int {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
return len(fake.onMetricsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMetricsCalls(stub func(types.Participant, *livekit.DataPacket)) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
defer fake.onMetricsMutex.Unlock()
|
||||
fake.OnMetricsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMetricsArgsForCall(i int) (types.Participant, *livekit.DataPacket) {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
argsForCall := fake.onMetricsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMigrateStateChange(arg1 types.LocalParticipant, arg2 types.MigrateState) {
|
||||
fake.onMigrateStateChangeMutex.Lock()
|
||||
fake.onMigrateStateChangeArgsForCall = append(fake.onMigrateStateChangeArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 types.MigrateState
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnMigrateStateChangeStub
|
||||
fake.recordInvocation("OnMigrateStateChange", []interface{}{arg1, arg2})
|
||||
fake.onMigrateStateChangeMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnMigrateStateChangeStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeCallCount() int {
|
||||
fake.onMigrateStateChangeMutex.RLock()
|
||||
defer fake.onMigrateStateChangeMutex.RUnlock()
|
||||
return len(fake.onMigrateStateChangeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeCalls(stub func(types.LocalParticipant, types.MigrateState)) {
|
||||
fake.onMigrateStateChangeMutex.Lock()
|
||||
defer fake.onMigrateStateChangeMutex.Unlock()
|
||||
fake.OnMigrateStateChangeStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeArgsForCall(i int) (types.LocalParticipant, types.MigrateState) {
|
||||
fake.onMigrateStateChangeMutex.RLock()
|
||||
defer fake.onMigrateStateChangeMutex.RUnlock()
|
||||
argsForCall := fake.onMigrateStateChangeArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnParticipantUpdate(arg1 types.Participant) {
|
||||
fake.onParticipantUpdateMutex.Lock()
|
||||
fake.onParticipantUpdateArgsForCall = append(fake.onParticipantUpdateArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
}{arg1})
|
||||
stub := fake.OnParticipantUpdateStub
|
||||
fake.recordInvocation("OnParticipantUpdate", []interface{}{arg1})
|
||||
fake.onParticipantUpdateMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnParticipantUpdateStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnParticipantUpdateCallCount() int {
|
||||
fake.onParticipantUpdateMutex.RLock()
|
||||
defer fake.onParticipantUpdateMutex.RUnlock()
|
||||
return len(fake.onParticipantUpdateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnParticipantUpdateCalls(stub func(types.Participant)) {
|
||||
fake.onParticipantUpdateMutex.Lock()
|
||||
defer fake.onParticipantUpdateMutex.Unlock()
|
||||
fake.OnParticipantUpdateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnParticipantUpdateArgsForCall(i int) types.Participant {
|
||||
fake.onParticipantUpdateMutex.RLock()
|
||||
defer fake.onParticipantUpdateMutex.RUnlock()
|
||||
argsForCall := fake.onParticipantUpdateArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenario(arg1 types.LocalParticipant, arg2 *livekit.SimulateScenario) error {
|
||||
fake.onSimulateScenarioMutex.Lock()
|
||||
ret, specificReturn := fake.onSimulateScenarioReturnsOnCall[len(fake.onSimulateScenarioArgsForCall)]
|
||||
fake.onSimulateScenarioArgsForCall = append(fake.onSimulateScenarioArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SimulateScenario
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnSimulateScenarioStub
|
||||
fakeReturns := fake.onSimulateScenarioReturns
|
||||
fake.recordInvocation("OnSimulateScenario", []interface{}{arg1, arg2})
|
||||
fake.onSimulateScenarioMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenarioCallCount() int {
|
||||
fake.onSimulateScenarioMutex.RLock()
|
||||
defer fake.onSimulateScenarioMutex.RUnlock()
|
||||
return len(fake.onSimulateScenarioArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenarioCalls(stub func(types.LocalParticipant, *livekit.SimulateScenario) error) {
|
||||
fake.onSimulateScenarioMutex.Lock()
|
||||
defer fake.onSimulateScenarioMutex.Unlock()
|
||||
fake.OnSimulateScenarioStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenarioArgsForCall(i int) (types.LocalParticipant, *livekit.SimulateScenario) {
|
||||
fake.onSimulateScenarioMutex.RLock()
|
||||
defer fake.onSimulateScenarioMutex.RUnlock()
|
||||
argsForCall := fake.onSimulateScenarioArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenarioReturns(result1 error) {
|
||||
fake.onSimulateScenarioMutex.Lock()
|
||||
defer fake.onSimulateScenarioMutex.Unlock()
|
||||
fake.OnSimulateScenarioStub = nil
|
||||
fake.onSimulateScenarioReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSimulateScenarioReturnsOnCall(i int, result1 error) {
|
||||
fake.onSimulateScenarioMutex.Lock()
|
||||
defer fake.onSimulateScenarioMutex.Unlock()
|
||||
fake.OnSimulateScenarioStub = nil
|
||||
if fake.onSimulateScenarioReturnsOnCall == nil {
|
||||
fake.onSimulateScenarioReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.onSimulateScenarioReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnStateChange(arg1 types.LocalParticipant) {
|
||||
fake.onStateChangeMutex.Lock()
|
||||
fake.onStateChangeArgsForCall = append(fake.onStateChangeArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
}{arg1})
|
||||
stub := fake.OnStateChangeStub
|
||||
fake.recordInvocation("OnStateChange", []interface{}{arg1})
|
||||
fake.onStateChangeMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnStateChangeStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnStateChangeCallCount() int {
|
||||
fake.onStateChangeMutex.RLock()
|
||||
defer fake.onStateChangeMutex.RUnlock()
|
||||
return len(fake.onStateChangeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnStateChangeCalls(stub func(types.LocalParticipant)) {
|
||||
fake.onStateChangeMutex.Lock()
|
||||
defer fake.onStateChangeMutex.Unlock()
|
||||
fake.OnStateChangeStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnStateChangeArgsForCall(i int) types.LocalParticipant {
|
||||
fake.onStateChangeMutex.RLock()
|
||||
defer fake.onStateChangeMutex.RUnlock()
|
||||
argsForCall := fake.onStateChangeArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChanged(arg1 types.LocalParticipant, arg2 livekit.ParticipantID, arg3 bool) {
|
||||
fake.onSubscribeStatusChangedMutex.Lock()
|
||||
fake.onSubscribeStatusChangedArgsForCall = append(fake.onSubscribeStatusChangedArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 livekit.ParticipantID
|
||||
arg3 bool
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnSubscribeStatusChangedStub
|
||||
fake.recordInvocation("OnSubscribeStatusChanged", []interface{}{arg1, arg2, arg3})
|
||||
fake.onSubscribeStatusChangedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnSubscribeStatusChangedStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedCallCount() int {
|
||||
fake.onSubscribeStatusChangedMutex.RLock()
|
||||
defer fake.onSubscribeStatusChangedMutex.RUnlock()
|
||||
return len(fake.onSubscribeStatusChangedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedCalls(stub func(types.LocalParticipant, livekit.ParticipantID, bool)) {
|
||||
fake.onSubscribeStatusChangedMutex.Lock()
|
||||
defer fake.onSubscribeStatusChangedMutex.Unlock()
|
||||
fake.OnSubscribeStatusChangedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedArgsForCall(i int) (types.LocalParticipant, livekit.ParticipantID, bool) {
|
||||
fake.onSubscribeStatusChangedMutex.RLock()
|
||||
defer fake.onSubscribeStatusChangedMutex.RUnlock()
|
||||
argsForCall := fake.onSubscribeStatusChangedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscriberReady(arg1 types.LocalParticipant) {
|
||||
fake.onSubscriberReadyMutex.Lock()
|
||||
fake.onSubscriberReadyArgsForCall = append(fake.onSubscriberReadyArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
}{arg1})
|
||||
stub := fake.OnSubscriberReadyStub
|
||||
fake.recordInvocation("OnSubscriberReady", []interface{}{arg1})
|
||||
fake.onSubscriberReadyMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnSubscriberReadyStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscriberReadyCallCount() int {
|
||||
fake.onSubscriberReadyMutex.RLock()
|
||||
defer fake.onSubscriberReadyMutex.RUnlock()
|
||||
return len(fake.onSubscriberReadyArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscriberReadyCalls(stub func(types.LocalParticipant)) {
|
||||
fake.onSubscriberReadyMutex.Lock()
|
||||
defer fake.onSubscriberReadyMutex.Unlock()
|
||||
fake.OnSubscriberReadyStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSubscriberReadyArgsForCall(i int) types.LocalParticipant {
|
||||
fake.onSubscriberReadyMutex.RLock()
|
||||
defer fake.onSubscriberReadyMutex.RUnlock()
|
||||
argsForCall := fake.onSubscriberReadyArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncState(arg1 types.LocalParticipant, arg2 *livekit.SyncState) error {
|
||||
fake.onSyncStateMutex.Lock()
|
||||
ret, specificReturn := fake.onSyncStateReturnsOnCall[len(fake.onSyncStateArgsForCall)]
|
||||
fake.onSyncStateArgsForCall = append(fake.onSyncStateArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SyncState
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnSyncStateStub
|
||||
fakeReturns := fake.onSyncStateReturns
|
||||
fake.recordInvocation("OnSyncState", []interface{}{arg1, arg2})
|
||||
fake.onSyncStateMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncStateCallCount() int {
|
||||
fake.onSyncStateMutex.RLock()
|
||||
defer fake.onSyncStateMutex.RUnlock()
|
||||
return len(fake.onSyncStateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncStateCalls(stub func(types.LocalParticipant, *livekit.SyncState) error) {
|
||||
fake.onSyncStateMutex.Lock()
|
||||
defer fake.onSyncStateMutex.Unlock()
|
||||
fake.OnSyncStateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncStateArgsForCall(i int) (types.LocalParticipant, *livekit.SyncState) {
|
||||
fake.onSyncStateMutex.RLock()
|
||||
defer fake.onSyncStateMutex.RUnlock()
|
||||
argsForCall := fake.onSyncStateArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncStateReturns(result1 error) {
|
||||
fake.onSyncStateMutex.Lock()
|
||||
defer fake.onSyncStateMutex.Unlock()
|
||||
fake.OnSyncStateStub = nil
|
||||
fake.onSyncStateReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnSyncStateReturnsOnCall(i int, result1 error) {
|
||||
fake.onSyncStateMutex.Lock()
|
||||
defer fake.onSyncStateMutex.Unlock()
|
||||
fake.OnSyncStateStub = nil
|
||||
if fake.onSyncStateReturnsOnCall == nil {
|
||||
fake.onSyncStateReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.onSyncStateReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackPublished(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackPublishedStub
|
||||
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2})
|
||||
fake.onTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackPublishedCallCount() int {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackPublishedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
defer fake.onTrackPublishedMutex.Unlock()
|
||||
fake.OnTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackPublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUnpublished(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackUnpublishedStub
|
||||
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2})
|
||||
fake.onTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnpublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedCallCount() int {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
defer fake.onTrackUnpublishedMutex.Unlock()
|
||||
fake.OnTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUpdated(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
fake.onTrackUpdatedArgsForCall = append(fake.onTrackUpdatedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackUpdatedStub
|
||||
fake.recordInvocation("OnTrackUpdated", []interface{}{arg1, arg2})
|
||||
fake.onTrackUpdatedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUpdatedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUpdatedCallCount() int {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
return len(fake.onTrackUpdatedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUpdatedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
defer fake.onTrackUpdatedMutex.Unlock()
|
||||
fake.OnTrackUpdatedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnTrackUpdatedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUpdatedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptions(arg1 types.LocalParticipant, arg2 *livekit.UpdateDataSubscription) {
|
||||
fake.onUpdateDataSubscriptionsMutex.Lock()
|
||||
fake.onUpdateDataSubscriptionsArgsForCall = append(fake.onUpdateDataSubscriptionsArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.UpdateDataSubscription
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnUpdateDataSubscriptionsStub
|
||||
fake.recordInvocation("OnUpdateDataSubscriptions", []interface{}{arg1, arg2})
|
||||
fake.onUpdateDataSubscriptionsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnUpdateDataSubscriptionsStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsCallCount() int {
|
||||
fake.onUpdateDataSubscriptionsMutex.RLock()
|
||||
defer fake.onUpdateDataSubscriptionsMutex.RUnlock()
|
||||
return len(fake.onUpdateDataSubscriptionsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsCalls(stub func(types.LocalParticipant, *livekit.UpdateDataSubscription)) {
|
||||
fake.onUpdateDataSubscriptionsMutex.Lock()
|
||||
defer fake.onUpdateDataSubscriptionsMutex.Unlock()
|
||||
fake.OnUpdateDataSubscriptionsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsArgsForCall(i int) (types.LocalParticipant, *livekit.UpdateDataSubscription) {
|
||||
fake.onUpdateDataSubscriptionsMutex.RLock()
|
||||
defer fake.onUpdateDataSubscriptionsMutex.RUnlock()
|
||||
argsForCall := fake.onUpdateDataSubscriptionsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermission(arg1 types.LocalParticipant, arg2 *livekit.SubscriptionPermission) error {
|
||||
fake.onUpdateSubscriptionPermissionMutex.Lock()
|
||||
ret, specificReturn := fake.onUpdateSubscriptionPermissionReturnsOnCall[len(fake.onUpdateSubscriptionPermissionArgsForCall)]
|
||||
fake.onUpdateSubscriptionPermissionArgsForCall = append(fake.onUpdateSubscriptionPermissionArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SubscriptionPermission
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnUpdateSubscriptionPermissionStub
|
||||
fakeReturns := fake.onUpdateSubscriptionPermissionReturns
|
||||
fake.recordInvocation("OnUpdateSubscriptionPermission", []interface{}{arg1, arg2})
|
||||
fake.onUpdateSubscriptionPermissionMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionCallCount() int {
|
||||
fake.onUpdateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.onUpdateSubscriptionPermissionMutex.RUnlock()
|
||||
return len(fake.onUpdateSubscriptionPermissionArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionCalls(stub func(types.LocalParticipant, *livekit.SubscriptionPermission) error) {
|
||||
fake.onUpdateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
|
||||
fake.OnUpdateSubscriptionPermissionStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionArgsForCall(i int) (types.LocalParticipant, *livekit.SubscriptionPermission) {
|
||||
fake.onUpdateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.onUpdateSubscriptionPermissionMutex.RUnlock()
|
||||
argsForCall := fake.onUpdateSubscriptionPermissionArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionReturns(result1 error) {
|
||||
fake.onUpdateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
|
||||
fake.OnUpdateSubscriptionPermissionStub = nil
|
||||
fake.onUpdateSubscriptionPermissionReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionReturnsOnCall(i int, result1 error) {
|
||||
fake.onUpdateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
|
||||
fake.OnUpdateSubscriptionPermissionStub = nil
|
||||
if fake.onUpdateSubscriptionPermissionReturnsOnCall == nil {
|
||||
fake.onUpdateSubscriptionPermissionReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.onUpdateSubscriptionPermissionReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptions(arg1 types.LocalParticipant, arg2 []livekit.TrackID, arg3 []*livekit.ParticipantTracks, arg4 bool) {
|
||||
var arg2Copy []livekit.TrackID
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]livekit.TrackID, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
var arg3Copy []*livekit.ParticipantTracks
|
||||
if arg3 != nil {
|
||||
arg3Copy = make([]*livekit.ParticipantTracks, len(arg3))
|
||||
copy(arg3Copy, arg3)
|
||||
}
|
||||
fake.onUpdateSubscriptionsMutex.Lock()
|
||||
fake.onUpdateSubscriptionsArgsForCall = append(fake.onUpdateSubscriptionsArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 []livekit.TrackID
|
||||
arg3 []*livekit.ParticipantTracks
|
||||
arg4 bool
|
||||
}{arg1, arg2Copy, arg3Copy, arg4})
|
||||
stub := fake.OnUpdateSubscriptionsStub
|
||||
fake.recordInvocation("OnUpdateSubscriptions", []interface{}{arg1, arg2Copy, arg3Copy, arg4})
|
||||
fake.onUpdateSubscriptionsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnUpdateSubscriptionsStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsCallCount() int {
|
||||
fake.onUpdateSubscriptionsMutex.RLock()
|
||||
defer fake.onUpdateSubscriptionsMutex.RUnlock()
|
||||
return len(fake.onUpdateSubscriptionsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsCalls(stub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)) {
|
||||
fake.onUpdateSubscriptionsMutex.Lock()
|
||||
defer fake.onUpdateSubscriptionsMutex.Unlock()
|
||||
fake.OnUpdateSubscriptionsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsArgsForCall(i int) (types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool) {
|
||||
fake.onUpdateSubscriptionsMutex.RLock()
|
||||
defer fake.onUpdateSubscriptionsMutex.RUnlock()
|
||||
argsForCall := fake.onUpdateSubscriptionsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeLocalParticipantListener) 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 *FakeLocalParticipantListener) 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.LocalParticipantListener = new(FakeLocalParticipantListener)
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
|
||||
type FakeMediaTrack struct {
|
||||
@@ -71,11 +72,12 @@ type FakeMediaTrack struct {
|
||||
getNumSubscribersReturnsOnCall map[int]struct {
|
||||
result1 int
|
||||
}
|
||||
GetQualityForDimensionStub func(uint32, uint32) livekit.VideoQuality
|
||||
GetQualityForDimensionStub func(mime.MimeType, uint32, uint32) livekit.VideoQuality
|
||||
getQualityForDimensionMutex sync.RWMutex
|
||||
getQualityForDimensionArgsForCall []struct {
|
||||
arg1 uint32
|
||||
arg1 mime.MimeType
|
||||
arg2 uint32
|
||||
arg3 uint32
|
||||
}
|
||||
getQualityForDimensionReturns struct {
|
||||
result1 livekit.VideoQuality
|
||||
@@ -83,12 +85,12 @@ type FakeMediaTrack struct {
|
||||
getQualityForDimensionReturnsOnCall map[int]struct {
|
||||
result1 livekit.VideoQuality
|
||||
}
|
||||
GetTemporalLayerForSpatialFpsStub func(int32, uint32, mime.MimeType) int32
|
||||
GetTemporalLayerForSpatialFpsStub func(mime.MimeType, int32, uint32) int32
|
||||
getTemporalLayerForSpatialFpsMutex sync.RWMutex
|
||||
getTemporalLayerForSpatialFpsArgsForCall []struct {
|
||||
arg1 int32
|
||||
arg2 uint32
|
||||
arg3 mime.MimeType
|
||||
arg1 mime.MimeType
|
||||
arg2 int32
|
||||
arg3 uint32
|
||||
}
|
||||
getTemporalLayerForSpatialFpsReturns struct {
|
||||
result1 int32
|
||||
@@ -96,6 +98,16 @@ type FakeMediaTrack struct {
|
||||
getTemporalLayerForSpatialFpsReturnsOnCall map[int]struct {
|
||||
result1 int32
|
||||
}
|
||||
HasPacketTrailerStub func() bool
|
||||
hasPacketTrailerMutex sync.RWMutex
|
||||
hasPacketTrailerArgsForCall []struct {
|
||||
}
|
||||
hasPacketTrailerReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
hasPacketTrailerReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IDStub func() livekit.TrackID
|
||||
iDMutex sync.RWMutex
|
||||
iDArgsForCall []struct {
|
||||
@@ -136,16 +148,6 @@ type FakeMediaTrack struct {
|
||||
isOpenReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsSimulcastStub func() bool
|
||||
isSimulcastMutex sync.RWMutex
|
||||
isSimulcastArgsForCall []struct {
|
||||
}
|
||||
isSimulcastReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
isSimulcastReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
IsSubscriberStub func(livekit.ParticipantID) bool
|
||||
isSubscriberMutex sync.RWMutex
|
||||
isSubscriberArgsForCall []struct {
|
||||
@@ -167,6 +169,16 @@ type FakeMediaTrack struct {
|
||||
kindReturnsOnCall map[int]struct {
|
||||
result1 livekit.TrackType
|
||||
}
|
||||
LoggerStub func() logger.Logger
|
||||
loggerMutex sync.RWMutex
|
||||
loggerArgsForCall []struct {
|
||||
}
|
||||
loggerReturns struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
loggerReturnsOnCall map[int]struct {
|
||||
result1 logger.Logger
|
||||
}
|
||||
NameStub func() string
|
||||
nameMutex sync.RWMutex
|
||||
nameArgsForCall []struct {
|
||||
@@ -614,19 +626,20 @@ func (fake *FakeMediaTrack) GetNumSubscribersReturnsOnCall(i int, result1 int) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetQualityForDimension(arg1 uint32, arg2 uint32) livekit.VideoQuality {
|
||||
func (fake *FakeMediaTrack) GetQualityForDimension(arg1 mime.MimeType, arg2 uint32, arg3 uint32) livekit.VideoQuality {
|
||||
fake.getQualityForDimensionMutex.Lock()
|
||||
ret, specificReturn := fake.getQualityForDimensionReturnsOnCall[len(fake.getQualityForDimensionArgsForCall)]
|
||||
fake.getQualityForDimensionArgsForCall = append(fake.getQualityForDimensionArgsForCall, struct {
|
||||
arg1 uint32
|
||||
arg1 mime.MimeType
|
||||
arg2 uint32
|
||||
}{arg1, arg2})
|
||||
arg3 uint32
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.GetQualityForDimensionStub
|
||||
fakeReturns := fake.getQualityForDimensionReturns
|
||||
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2})
|
||||
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2, arg3})
|
||||
fake.getQualityForDimensionMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
return stub(arg1, arg2, arg3)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
@@ -640,17 +653,17 @@ func (fake *FakeMediaTrack) GetQualityForDimensionCallCount() int {
|
||||
return len(fake.getQualityForDimensionArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetQualityForDimensionCalls(stub func(uint32, uint32) livekit.VideoQuality) {
|
||||
func (fake *FakeMediaTrack) GetQualityForDimensionCalls(stub func(mime.MimeType, uint32, uint32) livekit.VideoQuality) {
|
||||
fake.getQualityForDimensionMutex.Lock()
|
||||
defer fake.getQualityForDimensionMutex.Unlock()
|
||||
fake.GetQualityForDimensionStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetQualityForDimensionArgsForCall(i int) (uint32, uint32) {
|
||||
func (fake *FakeMediaTrack) GetQualityForDimensionArgsForCall(i int) (mime.MimeType, uint32, uint32) {
|
||||
fake.getQualityForDimensionMutex.RLock()
|
||||
defer fake.getQualityForDimensionMutex.RUnlock()
|
||||
argsForCall := fake.getQualityForDimensionArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetQualityForDimensionReturns(result1 livekit.VideoQuality) {
|
||||
@@ -676,13 +689,13 @@ func (fake *FakeMediaTrack) GetQualityForDimensionReturnsOnCall(i int, result1 l
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFps(arg1 int32, arg2 uint32, arg3 mime.MimeType) int32 {
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFps(arg1 mime.MimeType, arg2 int32, arg3 uint32) int32 {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.Lock()
|
||||
ret, specificReturn := fake.getTemporalLayerForSpatialFpsReturnsOnCall[len(fake.getTemporalLayerForSpatialFpsArgsForCall)]
|
||||
fake.getTemporalLayerForSpatialFpsArgsForCall = append(fake.getTemporalLayerForSpatialFpsArgsForCall, struct {
|
||||
arg1 int32
|
||||
arg2 uint32
|
||||
arg3 mime.MimeType
|
||||
arg1 mime.MimeType
|
||||
arg2 int32
|
||||
arg3 uint32
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.GetTemporalLayerForSpatialFpsStub
|
||||
fakeReturns := fake.getTemporalLayerForSpatialFpsReturns
|
||||
@@ -703,13 +716,13 @@ func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCallCount() int {
|
||||
return len(fake.getTemporalLayerForSpatialFpsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(int32, uint32, mime.MimeType) int32) {
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(mime.MimeType, int32, uint32) int32) {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.Lock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.Unlock()
|
||||
fake.GetTemporalLayerForSpatialFpsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (int32, uint32, mime.MimeType) {
|
||||
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (mime.MimeType, int32, uint32) {
|
||||
fake.getTemporalLayerForSpatialFpsMutex.RLock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
|
||||
argsForCall := fake.getTemporalLayerForSpatialFpsArgsForCall[i]
|
||||
@@ -739,6 +752,59 @@ func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsReturnsOnCall(i int, re
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) HasPacketTrailer() bool {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
ret, specificReturn := fake.hasPacketTrailerReturnsOnCall[len(fake.hasPacketTrailerArgsForCall)]
|
||||
fake.hasPacketTrailerArgsForCall = append(fake.hasPacketTrailerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.HasPacketTrailerStub
|
||||
fakeReturns := fake.hasPacketTrailerReturns
|
||||
fake.recordInvocation("HasPacketTrailer", []interface{}{})
|
||||
fake.hasPacketTrailerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) HasPacketTrailerCallCount() int {
|
||||
fake.hasPacketTrailerMutex.RLock()
|
||||
defer fake.hasPacketTrailerMutex.RUnlock()
|
||||
return len(fake.hasPacketTrailerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) HasPacketTrailerCalls(stub func() bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) HasPacketTrailerReturns(result1 bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = nil
|
||||
fake.hasPacketTrailerReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) HasPacketTrailerReturnsOnCall(i int, result1 bool) {
|
||||
fake.hasPacketTrailerMutex.Lock()
|
||||
defer fake.hasPacketTrailerMutex.Unlock()
|
||||
fake.HasPacketTrailerStub = nil
|
||||
if fake.hasPacketTrailerReturnsOnCall == nil {
|
||||
fake.hasPacketTrailerReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.hasPacketTrailerReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) ID() livekit.TrackID {
|
||||
fake.iDMutex.Lock()
|
||||
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
|
||||
@@ -951,59 +1017,6 @@ func (fake *FakeMediaTrack) IsOpenReturnsOnCall(i int, result1 bool) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSimulcast() bool {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
ret, specificReturn := fake.isSimulcastReturnsOnCall[len(fake.isSimulcastArgsForCall)]
|
||||
fake.isSimulcastArgsForCall = append(fake.isSimulcastArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.IsSimulcastStub
|
||||
fakeReturns := fake.isSimulcastReturns
|
||||
fake.recordInvocation("IsSimulcast", []interface{}{})
|
||||
fake.isSimulcastMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSimulcastCallCount() int {
|
||||
fake.isSimulcastMutex.RLock()
|
||||
defer fake.isSimulcastMutex.RUnlock()
|
||||
return len(fake.isSimulcastArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSimulcastCalls(stub func() bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSimulcastReturns(result1 bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = nil
|
||||
fake.isSimulcastReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSimulcastReturnsOnCall(i int, result1 bool) {
|
||||
fake.isSimulcastMutex.Lock()
|
||||
defer fake.isSimulcastMutex.Unlock()
|
||||
fake.IsSimulcastStub = nil
|
||||
if fake.isSimulcastReturnsOnCall == nil {
|
||||
fake.isSimulcastReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.isSimulcastReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
|
||||
fake.isSubscriberMutex.Lock()
|
||||
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
|
||||
@@ -1118,6 +1131,59 @@ func (fake *FakeMediaTrack) KindReturnsOnCall(i int, result1 livekit.TrackType)
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) Logger() logger.Logger {
|
||||
fake.loggerMutex.Lock()
|
||||
ret, specificReturn := fake.loggerReturnsOnCall[len(fake.loggerArgsForCall)]
|
||||
fake.loggerArgsForCall = append(fake.loggerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.LoggerStub
|
||||
fakeReturns := fake.loggerReturns
|
||||
fake.recordInvocation("Logger", []interface{}{})
|
||||
fake.loggerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) LoggerCallCount() int {
|
||||
fake.loggerMutex.RLock()
|
||||
defer fake.loggerMutex.RUnlock()
|
||||
return len(fake.loggerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) LoggerCalls(stub func() logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) LoggerReturns(result1 logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = nil
|
||||
fake.loggerReturns = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) LoggerReturnsOnCall(i int, result1 logger.Logger) {
|
||||
fake.loggerMutex.Lock()
|
||||
defer fake.loggerMutex.Unlock()
|
||||
fake.LoggerStub = nil
|
||||
if fake.loggerReturnsOnCall == nil {
|
||||
fake.loggerReturnsOnCall = make(map[int]struct {
|
||||
result1 logger.Logger
|
||||
})
|
||||
}
|
||||
fake.loggerReturnsOnCall[i] = struct {
|
||||
result1 logger.Logger
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeMediaTrack) Name() string {
|
||||
fake.nameMutex.Lock()
|
||||
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
|
||||
@@ -1796,68 +1862,6 @@ func (fake *FakeMediaTrack) UpdateVideoTrackArgsForCall(i int) *livekit.UpdateLo
|
||||
func (fake *FakeMediaTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.addOnCloseMutex.RLock()
|
||||
defer fake.addOnCloseMutex.RUnlock()
|
||||
fake.addSubscriberMutex.RLock()
|
||||
defer fake.addSubscriberMutex.RUnlock()
|
||||
fake.clearAllReceiversMutex.RLock()
|
||||
defer fake.clearAllReceiversMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.getAllSubscribersMutex.RLock()
|
||||
defer fake.getAllSubscribersMutex.RUnlock()
|
||||
fake.getAudioLevelMutex.RLock()
|
||||
defer fake.getAudioLevelMutex.RUnlock()
|
||||
fake.getNumSubscribersMutex.RLock()
|
||||
defer fake.getNumSubscribersMutex.RUnlock()
|
||||
fake.getQualityForDimensionMutex.RLock()
|
||||
defer fake.getQualityForDimensionMutex.RUnlock()
|
||||
fake.getTemporalLayerForSpatialFpsMutex.RLock()
|
||||
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
fake.isEncryptedMutex.RLock()
|
||||
defer fake.isEncryptedMutex.RUnlock()
|
||||
fake.isMutedMutex.RLock()
|
||||
defer fake.isMutedMutex.RUnlock()
|
||||
fake.isOpenMutex.RLock()
|
||||
defer fake.isOpenMutex.RUnlock()
|
||||
fake.isSimulcastMutex.RLock()
|
||||
defer fake.isSimulcastMutex.RUnlock()
|
||||
fake.isSubscriberMutex.RLock()
|
||||
defer fake.isSubscriberMutex.RUnlock()
|
||||
fake.kindMutex.RLock()
|
||||
defer fake.kindMutex.RUnlock()
|
||||
fake.nameMutex.RLock()
|
||||
defer fake.nameMutex.RUnlock()
|
||||
fake.onTrackSubscribedMutex.RLock()
|
||||
defer fake.onTrackSubscribedMutex.RUnlock()
|
||||
fake.publisherIDMutex.RLock()
|
||||
defer fake.publisherIDMutex.RUnlock()
|
||||
fake.publisherIdentityMutex.RLock()
|
||||
defer fake.publisherIdentityMutex.RUnlock()
|
||||
fake.publisherVersionMutex.RLock()
|
||||
defer fake.publisherVersionMutex.RUnlock()
|
||||
fake.receiversMutex.RLock()
|
||||
defer fake.receiversMutex.RUnlock()
|
||||
fake.removeSubscriberMutex.RLock()
|
||||
defer fake.removeSubscriberMutex.RUnlock()
|
||||
fake.revokeDisallowedSubscribersMutex.RLock()
|
||||
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
|
||||
fake.setMutedMutex.RLock()
|
||||
defer fake.setMutedMutex.RUnlock()
|
||||
fake.sourceMutex.RLock()
|
||||
defer fake.sourceMutex.RUnlock()
|
||||
fake.streamMutex.RLock()
|
||||
defer fake.streamMutex.RUnlock()
|
||||
fake.toProtoMutex.RLock()
|
||||
defer fake.toProtoMutex.RUnlock()
|
||||
fake.updateAudioTrackMutex.RLock()
|
||||
defer fake.updateAudioTrackMutex.RUnlock()
|
||||
fake.updateTrackInfoMutex.RLock()
|
||||
defer fake.updateTrackInfoMutex.RUnlock()
|
||||
fake.updateVideoTrackMutex.RLock()
|
||||
defer fake.updateVideoTrackMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -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,47 @@ 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
|
||||
}
|
||||
GetParticipantListenerStub func() types.ParticipantListener
|
||||
getParticipantListenerMutex sync.RWMutex
|
||||
getParticipantListenerArgsForCall []struct {
|
||||
}
|
||||
getParticipantListenerReturns struct {
|
||||
result1 types.ParticipantListener
|
||||
}
|
||||
getParticipantListenerReturnsOnCall map[int]struct {
|
||||
result1 types.ParticipantListener
|
||||
}
|
||||
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 +140,13 @@ type FakeParticipant struct {
|
||||
getPublishedTracksReturnsOnCall map[int]struct {
|
||||
result1 []types.MediaTrack
|
||||
}
|
||||
HandleReceivedDataTrackMessageStub func([]byte, *datatrack.Packet, int64)
|
||||
handleReceivedDataTrackMessageMutex sync.RWMutex
|
||||
handleReceivedDataTrackMessageArgsForCall []struct {
|
||||
arg1 []byte
|
||||
arg2 *datatrack.Packet
|
||||
arg3 int64
|
||||
}
|
||||
HasPermissionStub func(livekit.TrackID, livekit.ParticipantIdentity) bool
|
||||
hasPermissionMutex sync.RWMutex
|
||||
hasPermissionArgsForCall []struct {
|
||||
@@ -149,6 +199,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 +219,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,17 +259,36 @@ type FakeParticipant struct {
|
||||
kindReturnsOnCall map[int]struct {
|
||||
result1 livekit.ParticipantInfo_Kind
|
||||
}
|
||||
OnMetricsStub func(func(types.Participant, *livekit.DataPacket))
|
||||
onMetricsMutex sync.RWMutex
|
||||
onMetricsArgsForCall []struct {
|
||||
arg1 func(types.Participant, *livekit.DataPacket)
|
||||
KindDetailsStub func() []livekit.ParticipantInfo_KindDetail
|
||||
kindDetailsMutex sync.RWMutex
|
||||
kindDetailsArgsForCall []struct {
|
||||
}
|
||||
RemovePublishedTrackStub func(types.MediaTrack, bool, bool)
|
||||
kindDetailsReturns struct {
|
||||
result1 []livekit.ParticipantInfo_KindDetail
|
||||
}
|
||||
kindDetailsReturnsOnCall map[int]struct {
|
||||
result1 []livekit.ParticipantInfo_KindDetail
|
||||
}
|
||||
MigrateStateStub func() types.MigrateState
|
||||
migrateStateMutex sync.RWMutex
|
||||
migrateStateArgsForCall []struct {
|
||||
}
|
||||
migrateStateReturns struct {
|
||||
result1 types.MigrateState
|
||||
}
|
||||
migrateStateReturnsOnCall map[int]struct {
|
||||
result1 types.MigrateState
|
||||
}
|
||||
RemovePublishedDataTrackStub func(types.DataTrack)
|
||||
removePublishedDataTrackMutex sync.RWMutex
|
||||
removePublishedDataTrackArgsForCall []struct {
|
||||
arg1 types.DataTrack
|
||||
}
|
||||
RemovePublishedTrackStub func(types.MediaTrack, bool)
|
||||
removePublishedTrackMutex sync.RWMutex
|
||||
removePublishedTrackArgsForCall []struct {
|
||||
arg1 types.MediaTrack
|
||||
arg2 bool
|
||||
arg3 bool
|
||||
}
|
||||
StateStub func() livekit.ParticipantInfo_State
|
||||
stateMutex sync.RWMutex
|
||||
@@ -233,6 +322,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 {
|
||||
@@ -482,7 +583,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 {
|
||||
@@ -506,32 +607,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}
|
||||
}
|
||||
|
||||
@@ -591,6 +692,226 @@ 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) GetParticipantListener() types.ParticipantListener {
|
||||
fake.getParticipantListenerMutex.Lock()
|
||||
ret, specificReturn := fake.getParticipantListenerReturnsOnCall[len(fake.getParticipantListenerArgsForCall)]
|
||||
fake.getParticipantListenerArgsForCall = append(fake.getParticipantListenerArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.GetParticipantListenerStub
|
||||
fakeReturns := fake.getParticipantListenerReturns
|
||||
fake.recordInvocation("GetParticipantListener", []interface{}{})
|
||||
fake.getParticipantListenerMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetParticipantListenerCallCount() int {
|
||||
fake.getParticipantListenerMutex.RLock()
|
||||
defer fake.getParticipantListenerMutex.RUnlock()
|
||||
return len(fake.getParticipantListenerArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetParticipantListenerCalls(stub func() types.ParticipantListener) {
|
||||
fake.getParticipantListenerMutex.Lock()
|
||||
defer fake.getParticipantListenerMutex.Unlock()
|
||||
fake.GetParticipantListenerStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetParticipantListenerReturns(result1 types.ParticipantListener) {
|
||||
fake.getParticipantListenerMutex.Lock()
|
||||
defer fake.getParticipantListenerMutex.Unlock()
|
||||
fake.GetParticipantListenerStub = nil
|
||||
fake.getParticipantListenerReturns = struct {
|
||||
result1 types.ParticipantListener
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) GetParticipantListenerReturnsOnCall(i int, result1 types.ParticipantListener) {
|
||||
fake.getParticipantListenerMutex.Lock()
|
||||
defer fake.getParticipantListenerMutex.Unlock()
|
||||
fake.GetParticipantListenerStub = nil
|
||||
if fake.getParticipantListenerReturnsOnCall == nil {
|
||||
fake.getParticipantListenerReturnsOnCall = make(map[int]struct {
|
||||
result1 types.ParticipantListener
|
||||
})
|
||||
}
|
||||
fake.getParticipantListenerReturnsOnCall[i] = struct {
|
||||
result1 types.ParticipantListener
|
||||
}{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)]
|
||||
@@ -705,6 +1026,45 @@ func (fake *FakeParticipant) GetPublishedTracksReturnsOnCall(i int, result1 []ty
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessage(arg1 []byte, arg2 *datatrack.Packet, arg3 int64) {
|
||||
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
|
||||
arg3 int64
|
||||
}{arg1Copy, arg2, arg3})
|
||||
stub := fake.HandleReceivedDataTrackMessageStub
|
||||
fake.recordInvocation("HandleReceivedDataTrackMessage", []interface{}{arg1Copy, arg2, arg3})
|
||||
fake.handleReceivedDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.HandleReceivedDataTrackMessageStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
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, int64)) {
|
||||
fake.handleReceivedDataTrackMessageMutex.Lock()
|
||||
defer fake.handleReceivedDataTrackMessageMutex.Unlock()
|
||||
fake.HandleReceivedDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HandleReceivedDataTrackMessageArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
|
||||
fake.handleReceivedDataTrackMessageMutex.RLock()
|
||||
defer fake.handleReceivedDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.handleReceivedDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) HasPermission(arg1 livekit.TrackID, arg2 livekit.ParticipantIdentity) bool {
|
||||
fake.hasPermissionMutex.Lock()
|
||||
ret, specificReturn := fake.hasPermissionReturnsOnCall[len(fake.hasPermissionArgsForCall)]
|
||||
@@ -979,6 +1339,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)]
|
||||
@@ -1032,6 +1445,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)]
|
||||
@@ -1191,50 +1657,155 @@ func (fake *FakeParticipant) KindReturnsOnCall(i int, result1 livekit.Participan
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnMetrics(arg1 func(types.Participant, *livekit.DataPacket)) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
|
||||
arg1 func(types.Participant, *livekit.DataPacket)
|
||||
}{arg1})
|
||||
stub := fake.OnMetricsStub
|
||||
fake.recordInvocation("OnMetrics", []interface{}{arg1})
|
||||
fake.onMetricsMutex.Unlock()
|
||||
func (fake *FakeParticipant) KindDetails() []livekit.ParticipantInfo_KindDetail {
|
||||
fake.kindDetailsMutex.Lock()
|
||||
ret, specificReturn := fake.kindDetailsReturnsOnCall[len(fake.kindDetailsArgsForCall)]
|
||||
fake.kindDetailsArgsForCall = append(fake.kindDetailsArgsForCall, struct {
|
||||
}{})
|
||||
stub := fake.KindDetailsStub
|
||||
fakeReturns := fake.kindDetailsReturns
|
||||
fake.recordInvocation("KindDetails", []interface{}{})
|
||||
fake.kindDetailsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnMetricsStub(arg1)
|
||||
return stub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) KindDetailsCallCount() int {
|
||||
fake.kindDetailsMutex.RLock()
|
||||
defer fake.kindDetailsMutex.RUnlock()
|
||||
return len(fake.kindDetailsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) KindDetailsCalls(stub func() []livekit.ParticipantInfo_KindDetail) {
|
||||
fake.kindDetailsMutex.Lock()
|
||||
defer fake.kindDetailsMutex.Unlock()
|
||||
fake.KindDetailsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) KindDetailsReturns(result1 []livekit.ParticipantInfo_KindDetail) {
|
||||
fake.kindDetailsMutex.Lock()
|
||||
defer fake.kindDetailsMutex.Unlock()
|
||||
fake.KindDetailsStub = nil
|
||||
fake.kindDetailsReturns = struct {
|
||||
result1 []livekit.ParticipantInfo_KindDetail
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) KindDetailsReturnsOnCall(i int, result1 []livekit.ParticipantInfo_KindDetail) {
|
||||
fake.kindDetailsMutex.Lock()
|
||||
defer fake.kindDetailsMutex.Unlock()
|
||||
fake.KindDetailsStub = nil
|
||||
if fake.kindDetailsReturnsOnCall == nil {
|
||||
fake.kindDetailsReturnsOnCall = make(map[int]struct {
|
||||
result1 []livekit.ParticipantInfo_KindDetail
|
||||
})
|
||||
}
|
||||
fake.kindDetailsReturnsOnCall[i] = struct {
|
||||
result1 []livekit.ParticipantInfo_KindDetail
|
||||
}{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) 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) OnMetricsCallCount() int {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
return len(fake.onMetricsArgsForCall)
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrackCallCount() int {
|
||||
fake.removePublishedDataTrackMutex.RLock()
|
||||
defer fake.removePublishedDataTrackMutex.RUnlock()
|
||||
return len(fake.removePublishedDataTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnMetricsCalls(stub func(func(types.Participant, *livekit.DataPacket))) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
defer fake.onMetricsMutex.Unlock()
|
||||
fake.OnMetricsStub = stub
|
||||
func (fake *FakeParticipant) RemovePublishedDataTrackCalls(stub func(types.DataTrack)) {
|
||||
fake.removePublishedDataTrackMutex.Lock()
|
||||
defer fake.removePublishedDataTrackMutex.Unlock()
|
||||
fake.RemovePublishedDataTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) OnMetricsArgsForCall(i int) func(types.Participant, *livekit.DataPacket) {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
argsForCall := fake.onMetricsArgsForCall[i]
|
||||
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, arg3 bool) {
|
||||
func (fake *FakeParticipant) RemovePublishedTrack(arg1 types.MediaTrack, arg2 bool) {
|
||||
fake.removePublishedTrackMutex.Lock()
|
||||
fake.removePublishedTrackArgsForCall = append(fake.removePublishedTrackArgsForCall, struct {
|
||||
arg1 types.MediaTrack
|
||||
arg2 bool
|
||||
arg3 bool
|
||||
}{arg1, arg2, arg3})
|
||||
}{arg1, arg2})
|
||||
stub := fake.RemovePublishedTrackStub
|
||||
fake.recordInvocation("RemovePublishedTrack", []interface{}{arg1, arg2, arg3})
|
||||
fake.recordInvocation("RemovePublishedTrack", []interface{}{arg1, arg2})
|
||||
fake.removePublishedTrackMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.RemovePublishedTrackStub(arg1, arg2, arg3)
|
||||
fake.RemovePublishedTrackStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,17 +1815,17 @@ func (fake *FakeParticipant) RemovePublishedTrackCallCount() int {
|
||||
return len(fake.removePublishedTrackArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedTrackCalls(stub func(types.MediaTrack, bool, bool)) {
|
||||
func (fake *FakeParticipant) RemovePublishedTrackCalls(stub func(types.MediaTrack, bool)) {
|
||||
fake.removePublishedTrackMutex.Lock()
|
||||
defer fake.removePublishedTrackMutex.Unlock()
|
||||
fake.RemovePublishedTrackStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) RemovePublishedTrackArgsForCall(i int) (types.MediaTrack, bool, bool) {
|
||||
func (fake *FakeParticipant) RemovePublishedTrackArgsForCall(i int) (types.MediaTrack, bool) {
|
||||
fake.removePublishedTrackMutex.RLock()
|
||||
defer fake.removePublishedTrackMutex.RUnlock()
|
||||
argsForCall := fake.removePublishedTrackArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipant) State() livekit.ParticipantInfo_State {
|
||||
@@ -1419,6 +1990,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)]
|
||||
@@ -1538,54 +2165,6 @@ func (fake *FakeParticipant) VersionReturnsOnCall(i int, result1 utils.TimedVers
|
||||
func (fake *FakeParticipant) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.canSkipBroadcastMutex.RLock()
|
||||
defer fake.canSkipBroadcastMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.closeReasonMutex.RLock()
|
||||
defer fake.closeReasonMutex.RUnlock()
|
||||
fake.connectedAtMutex.RLock()
|
||||
defer fake.connectedAtMutex.RUnlock()
|
||||
fake.debugInfoMutex.RLock()
|
||||
defer fake.debugInfoMutex.RUnlock()
|
||||
fake.getAudioLevelMutex.RLock()
|
||||
defer fake.getAudioLevelMutex.RUnlock()
|
||||
fake.getPublishedTrackMutex.RLock()
|
||||
defer fake.getPublishedTrackMutex.RUnlock()
|
||||
fake.getPublishedTracksMutex.RLock()
|
||||
defer fake.getPublishedTracksMutex.RUnlock()
|
||||
fake.hasPermissionMutex.RLock()
|
||||
defer fake.hasPermissionMutex.RUnlock()
|
||||
fake.hiddenMutex.RLock()
|
||||
defer fake.hiddenMutex.RUnlock()
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
fake.identityMutex.RLock()
|
||||
defer fake.identityMutex.RUnlock()
|
||||
fake.isAgentMutex.RLock()
|
||||
defer fake.isAgentMutex.RUnlock()
|
||||
fake.isDependentMutex.RLock()
|
||||
defer fake.isDependentMutex.RUnlock()
|
||||
fake.isPublisherMutex.RLock()
|
||||
defer fake.isPublisherMutex.RUnlock()
|
||||
fake.isRecorderMutex.RLock()
|
||||
defer fake.isRecorderMutex.RUnlock()
|
||||
fake.kindMutex.RLock()
|
||||
defer fake.kindMutex.RUnlock()
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
fake.removePublishedTrackMutex.RLock()
|
||||
defer fake.removePublishedTrackMutex.RUnlock()
|
||||
fake.stateMutex.RLock()
|
||||
defer fake.stateMutex.RUnlock()
|
||||
fake.subscriptionPermissionMutex.RLock()
|
||||
defer fake.subscriptionPermissionMutex.RUnlock()
|
||||
fake.toProtoMutex.RLock()
|
||||
defer fake.toProtoMutex.RUnlock()
|
||||
fake.updateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.updateSubscriptionPermissionMutex.RUnlock()
|
||||
fake.versionMutex.RLock()
|
||||
defer fake.versionMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
// 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 FakeParticipantListener struct {
|
||||
OnDataTrackMessageStub func(types.Participant, []byte, *datatrack.Packet)
|
||||
onDataTrackMessageMutex sync.RWMutex
|
||||
onDataTrackMessageArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 []byte
|
||||
arg3 *datatrack.Packet
|
||||
}
|
||||
OnDataTrackPublishedStub func(types.Participant, types.DataTrack)
|
||||
onDataTrackPublishedMutex sync.RWMutex
|
||||
onDataTrackPublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}
|
||||
OnDataTrackUnpublishedStub func(types.Participant, types.DataTrack)
|
||||
onDataTrackUnpublishedMutex sync.RWMutex
|
||||
onDataTrackUnpublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}
|
||||
OnMetricsStub func(types.Participant, *livekit.DataPacket)
|
||||
onMetricsMutex sync.RWMutex
|
||||
onMetricsArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 *livekit.DataPacket
|
||||
}
|
||||
OnParticipantUpdateStub func(types.Participant)
|
||||
onParticipantUpdateMutex sync.RWMutex
|
||||
onParticipantUpdateArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
}
|
||||
OnTrackPublishedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackPublishedMutex sync.RWMutex
|
||||
onTrackPublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
OnTrackUnpublishedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackUnpublishedMutex sync.RWMutex
|
||||
onTrackUnpublishedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
OnTrackUpdatedStub func(types.Participant, types.MediaTrack)
|
||||
onTrackUpdatedMutex sync.RWMutex
|
||||
onTrackUpdatedArgsForCall []struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackMessage(arg1 types.Participant, arg2 []byte, arg3 *datatrack.Packet) {
|
||||
var arg2Copy []byte
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]byte, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 []byte
|
||||
arg3 *datatrack.Packet
|
||||
}{arg1, arg2Copy, arg3})
|
||||
stub := fake.OnDataTrackMessageStub
|
||||
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1, arg2Copy, arg3})
|
||||
fake.onDataTrackMessageMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackMessageStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackMessageCallCount() int {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
return len(fake.onDataTrackMessageArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackMessageCalls(stub func(types.Participant, []byte, *datatrack.Packet)) {
|
||||
fake.onDataTrackMessageMutex.Lock()
|
||||
defer fake.onDataTrackMessageMutex.Unlock()
|
||||
fake.OnDataTrackMessageStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackMessageArgsForCall(i int) (types.Participant, []byte, *datatrack.Packet) {
|
||||
fake.onDataTrackMessageMutex.RLock()
|
||||
defer fake.onDataTrackMessageMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackMessageArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackPublished(arg1 types.Participant, arg2 types.DataTrack) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
fake.onDataTrackPublishedArgsForCall = append(fake.onDataTrackPublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnDataTrackPublishedStub
|
||||
fake.recordInvocation("OnDataTrackPublished", []interface{}{arg1, arg2})
|
||||
fake.onDataTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackPublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackPublishedCallCount() int {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackPublishedCalls(stub func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackPublishedMutex.Lock()
|
||||
defer fake.onDataTrackPublishedMutex.Unlock()
|
||||
fake.OnDataTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackPublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackPublishedMutex.RLock()
|
||||
defer fake.onDataTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackUnpublished(arg1 types.Participant, arg2 types.DataTrack) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
fake.onDataTrackUnpublishedArgsForCall = append(fake.onDataTrackUnpublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.DataTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnDataTrackUnpublishedStub
|
||||
fake.recordInvocation("OnDataTrackUnpublished", []interface{}{arg1, arg2})
|
||||
fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnDataTrackUnpublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackUnpublishedCallCount() int {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onDataTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackUnpublishedCalls(stub func(types.Participant, types.DataTrack)) {
|
||||
fake.onDataTrackUnpublishedMutex.Lock()
|
||||
defer fake.onDataTrackUnpublishedMutex.Unlock()
|
||||
fake.OnDataTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnDataTrackUnpublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
|
||||
fake.onDataTrackUnpublishedMutex.RLock()
|
||||
defer fake.onDataTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onDataTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnMetrics(arg1 types.Participant, arg2 *livekit.DataPacket) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 *livekit.DataPacket
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnMetricsStub
|
||||
fake.recordInvocation("OnMetrics", []interface{}{arg1, arg2})
|
||||
fake.onMetricsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnMetricsStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnMetricsCallCount() int {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
return len(fake.onMetricsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnMetricsCalls(stub func(types.Participant, *livekit.DataPacket)) {
|
||||
fake.onMetricsMutex.Lock()
|
||||
defer fake.onMetricsMutex.Unlock()
|
||||
fake.OnMetricsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnMetricsArgsForCall(i int) (types.Participant, *livekit.DataPacket) {
|
||||
fake.onMetricsMutex.RLock()
|
||||
defer fake.onMetricsMutex.RUnlock()
|
||||
argsForCall := fake.onMetricsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnParticipantUpdate(arg1 types.Participant) {
|
||||
fake.onParticipantUpdateMutex.Lock()
|
||||
fake.onParticipantUpdateArgsForCall = append(fake.onParticipantUpdateArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
}{arg1})
|
||||
stub := fake.OnParticipantUpdateStub
|
||||
fake.recordInvocation("OnParticipantUpdate", []interface{}{arg1})
|
||||
fake.onParticipantUpdateMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnParticipantUpdateStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnParticipantUpdateCallCount() int {
|
||||
fake.onParticipantUpdateMutex.RLock()
|
||||
defer fake.onParticipantUpdateMutex.RUnlock()
|
||||
return len(fake.onParticipantUpdateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnParticipantUpdateCalls(stub func(types.Participant)) {
|
||||
fake.onParticipantUpdateMutex.Lock()
|
||||
defer fake.onParticipantUpdateMutex.Unlock()
|
||||
fake.OnParticipantUpdateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnParticipantUpdateArgsForCall(i int) types.Participant {
|
||||
fake.onParticipantUpdateMutex.RLock()
|
||||
defer fake.onParticipantUpdateMutex.RUnlock()
|
||||
argsForCall := fake.onParticipantUpdateArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackPublished(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackPublishedStub
|
||||
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2})
|
||||
fake.onTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackPublishedCallCount() int {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackPublishedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
defer fake.onTrackPublishedMutex.Unlock()
|
||||
fake.OnTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackPublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUnpublished(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackUnpublishedStub
|
||||
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2})
|
||||
fake.onTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnpublishedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUnpublishedCallCount() int {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUnpublishedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
defer fake.onTrackUnpublishedMutex.Unlock()
|
||||
fake.OnTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUnpublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUpdated(arg1 types.Participant, arg2 types.MediaTrack) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
fake.onTrackUpdatedArgsForCall = append(fake.onTrackUpdatedArgsForCall, struct {
|
||||
arg1 types.Participant
|
||||
arg2 types.MediaTrack
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackUpdatedStub
|
||||
fake.recordInvocation("OnTrackUpdated", []interface{}{arg1, arg2})
|
||||
fake.onTrackUpdatedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUpdatedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUpdatedCallCount() int {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
return len(fake.onTrackUpdatedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUpdatedCalls(stub func(types.Participant, types.MediaTrack)) {
|
||||
fake.onTrackUpdatedMutex.Lock()
|
||||
defer fake.onTrackUpdatedMutex.Unlock()
|
||||
fake.OnTrackUpdatedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) OnTrackUpdatedArgsForCall(i int) (types.Participant, types.MediaTrack) {
|
||||
fake.onTrackUpdatedMutex.RLock()
|
||||
defer fake.onTrackUpdatedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUpdatedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantListener) 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 *FakeParticipantListener) 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.ParticipantListener = new(FakeParticipantListener)
|
||||
@@ -0,0 +1,659 @@
|
||||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package typesfakes
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/livekit-server/pkg/telemetry"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
)
|
||||
|
||||
type FakeParticipantTelemetryListener struct {
|
||||
OnTrackMaxSubscribedVideoQualityStub func(livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality)
|
||||
onTrackMaxSubscribedVideoQualityMutex sync.RWMutex
|
||||
onTrackMaxSubscribedVideoQualityArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 mime.MimeType
|
||||
arg4 livekit.VideoQuality
|
||||
}
|
||||
OnTrackMutedStub func(livekit.ParticipantID, *livekit.TrackInfo)
|
||||
onTrackMutedMutex sync.RWMutex
|
||||
onTrackMutedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackPublishRTPStatsStub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats)
|
||||
onTrackPublishRTPStatsMutex sync.RWMutex
|
||||
onTrackPublishRTPStatsArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 mime.MimeType
|
||||
arg4 int
|
||||
arg5 *livekit.RTPStats
|
||||
}
|
||||
OnTrackPublishRequestedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo)
|
||||
onTrackPublishRequestedMutex sync.RWMutex
|
||||
onTrackPublishRequestedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackPublishedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)
|
||||
onTrackPublishedMutex sync.RWMutex
|
||||
onTrackPublishedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
arg4 bool
|
||||
}
|
||||
OnTrackPublishedUpdateStub func(livekit.ParticipantID, *livekit.TrackInfo)
|
||||
onTrackPublishedUpdateMutex sync.RWMutex
|
||||
onTrackPublishedUpdateArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackStatsStub func(telemetry.StatsKey, *livekit.AnalyticsStat)
|
||||
onTrackStatsMutex sync.RWMutex
|
||||
onTrackStatsArgsForCall []struct {
|
||||
arg1 telemetry.StatsKey
|
||||
arg2 *livekit.AnalyticsStat
|
||||
}
|
||||
OnTrackSubscribeFailedStub func(livekit.ParticipantID, livekit.TrackID, error, bool)
|
||||
onTrackSubscribeFailedMutex sync.RWMutex
|
||||
onTrackSubscribeFailedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 error
|
||||
arg4 bool
|
||||
}
|
||||
OnTrackSubscribeRTPStatsStub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats)
|
||||
onTrackSubscribeRTPStatsMutex sync.RWMutex
|
||||
onTrackSubscribeRTPStatsArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 mime.MimeType
|
||||
arg4 *livekit.RTPStats
|
||||
}
|
||||
OnTrackSubscribeRequestedStub func(livekit.ParticipantID, *livekit.TrackInfo)
|
||||
onTrackSubscribeRequestedMutex sync.RWMutex
|
||||
onTrackSubscribeRequestedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackSubscribeStreamStartedStub func(livekit.ParticipantID, *livekit.TrackInfo)
|
||||
onTrackSubscribeStreamStartedMutex sync.RWMutex
|
||||
onTrackSubscribeStreamStartedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackSubscribedStub func(livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool)
|
||||
onTrackSubscribedMutex sync.RWMutex
|
||||
onTrackSubscribedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 *livekit.ParticipantInfo
|
||||
arg4 bool
|
||||
}
|
||||
OnTrackUnmutedStub func(livekit.ParticipantID, *livekit.TrackInfo)
|
||||
onTrackUnmutedMutex sync.RWMutex
|
||||
onTrackUnmutedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}
|
||||
OnTrackUnpublishedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)
|
||||
onTrackUnpublishedMutex sync.RWMutex
|
||||
onTrackUnpublishedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
arg4 bool
|
||||
}
|
||||
OnTrackUnsubscribedStub func(livekit.ParticipantID, *livekit.TrackInfo, bool)
|
||||
onTrackUnsubscribedMutex sync.RWMutex
|
||||
onTrackUnsubscribedArgsForCall []struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 bool
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQuality(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 mime.MimeType, arg4 livekit.VideoQuality) {
|
||||
fake.onTrackMaxSubscribedVideoQualityMutex.Lock()
|
||||
fake.onTrackMaxSubscribedVideoQualityArgsForCall = append(fake.onTrackMaxSubscribedVideoQualityArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 mime.MimeType
|
||||
arg4 livekit.VideoQuality
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackMaxSubscribedVideoQualityStub
|
||||
fake.recordInvocation("OnTrackMaxSubscribedVideoQuality", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackMaxSubscribedVideoQualityMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackMaxSubscribedVideoQualityStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityCallCount() int {
|
||||
fake.onTrackMaxSubscribedVideoQualityMutex.RLock()
|
||||
defer fake.onTrackMaxSubscribedVideoQualityMutex.RUnlock()
|
||||
return len(fake.onTrackMaxSubscribedVideoQualityArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality)) {
|
||||
fake.onTrackMaxSubscribedVideoQualityMutex.Lock()
|
||||
defer fake.onTrackMaxSubscribedVideoQualityMutex.Unlock()
|
||||
fake.OnTrackMaxSubscribedVideoQualityStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality) {
|
||||
fake.onTrackMaxSubscribedVideoQualityMutex.RLock()
|
||||
defer fake.onTrackMaxSubscribedVideoQualityMutex.RUnlock()
|
||||
argsForCall := fake.onTrackMaxSubscribedVideoQualityArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMuted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
|
||||
fake.onTrackMutedMutex.Lock()
|
||||
fake.onTrackMutedArgsForCall = append(fake.onTrackMutedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackMutedStub
|
||||
fake.recordInvocation("OnTrackMuted", []interface{}{arg1, arg2})
|
||||
fake.onTrackMutedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackMutedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMutedCallCount() int {
|
||||
fake.onTrackMutedMutex.RLock()
|
||||
defer fake.onTrackMutedMutex.RUnlock()
|
||||
return len(fake.onTrackMutedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMutedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
|
||||
fake.onTrackMutedMutex.Lock()
|
||||
defer fake.onTrackMutedMutex.Unlock()
|
||||
fake.OnTrackMutedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackMutedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
|
||||
fake.onTrackMutedMutex.RLock()
|
||||
defer fake.onTrackMutedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackMutedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStats(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 mime.MimeType, arg4 int, arg5 *livekit.RTPStats) {
|
||||
fake.onTrackPublishRTPStatsMutex.Lock()
|
||||
fake.onTrackPublishRTPStatsArgsForCall = append(fake.onTrackPublishRTPStatsArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 mime.MimeType
|
||||
arg4 int
|
||||
arg5 *livekit.RTPStats
|
||||
}{arg1, arg2, arg3, arg4, arg5})
|
||||
stub := fake.OnTrackPublishRTPStatsStub
|
||||
fake.recordInvocation("OnTrackPublishRTPStats", []interface{}{arg1, arg2, arg3, arg4, arg5})
|
||||
fake.onTrackPublishRTPStatsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishRTPStatsStub(arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsCallCount() int {
|
||||
fake.onTrackPublishRTPStatsMutex.RLock()
|
||||
defer fake.onTrackPublishRTPStatsMutex.RUnlock()
|
||||
return len(fake.onTrackPublishRTPStatsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsCalls(stub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats)) {
|
||||
fake.onTrackPublishRTPStatsMutex.Lock()
|
||||
defer fake.onTrackPublishRTPStatsMutex.Unlock()
|
||||
fake.OnTrackPublishRTPStatsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats) {
|
||||
fake.onTrackPublishRTPStatsMutex.RLock()
|
||||
defer fake.onTrackPublishRTPStatsMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishRTPStatsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequested(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo) {
|
||||
fake.onTrackPublishRequestedMutex.Lock()
|
||||
fake.onTrackPublishRequestedArgsForCall = append(fake.onTrackPublishRequestedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnTrackPublishRequestedStub
|
||||
fake.recordInvocation("OnTrackPublishRequested", []interface{}{arg1, arg2, arg3})
|
||||
fake.onTrackPublishRequestedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishRequestedStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedCallCount() int {
|
||||
fake.onTrackPublishRequestedMutex.RLock()
|
||||
defer fake.onTrackPublishRequestedMutex.RUnlock()
|
||||
return len(fake.onTrackPublishRequestedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo)) {
|
||||
fake.onTrackPublishRequestedMutex.Lock()
|
||||
defer fake.onTrackPublishRequestedMutex.Unlock()
|
||||
fake.OnTrackPublishRequestedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo) {
|
||||
fake.onTrackPublishRequestedMutex.RLock()
|
||||
defer fake.onTrackPublishRequestedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishRequestedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublished(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo, arg4 bool) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
arg4 bool
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackPublishedStub
|
||||
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackPublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishedStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedCallCount() int {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
return len(fake.onTrackPublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)) {
|
||||
fake.onTrackPublishedMutex.Lock()
|
||||
defer fake.onTrackPublishedMutex.Unlock()
|
||||
fake.OnTrackPublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool) {
|
||||
fake.onTrackPublishedMutex.RLock()
|
||||
defer fake.onTrackPublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdate(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
|
||||
fake.onTrackPublishedUpdateMutex.Lock()
|
||||
fake.onTrackPublishedUpdateArgsForCall = append(fake.onTrackPublishedUpdateArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackPublishedUpdateStub
|
||||
fake.recordInvocation("OnTrackPublishedUpdate", []interface{}{arg1, arg2})
|
||||
fake.onTrackPublishedUpdateMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackPublishedUpdateStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateCallCount() int {
|
||||
fake.onTrackPublishedUpdateMutex.RLock()
|
||||
defer fake.onTrackPublishedUpdateMutex.RUnlock()
|
||||
return len(fake.onTrackPublishedUpdateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
|
||||
fake.onTrackPublishedUpdateMutex.Lock()
|
||||
defer fake.onTrackPublishedUpdateMutex.Unlock()
|
||||
fake.OnTrackPublishedUpdateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
|
||||
fake.onTrackPublishedUpdateMutex.RLock()
|
||||
defer fake.onTrackPublishedUpdateMutex.RUnlock()
|
||||
argsForCall := fake.onTrackPublishedUpdateArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackStats(arg1 telemetry.StatsKey, arg2 *livekit.AnalyticsStat) {
|
||||
fake.onTrackStatsMutex.Lock()
|
||||
fake.onTrackStatsArgsForCall = append(fake.onTrackStatsArgsForCall, struct {
|
||||
arg1 telemetry.StatsKey
|
||||
arg2 *livekit.AnalyticsStat
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackStatsStub
|
||||
fake.recordInvocation("OnTrackStats", []interface{}{arg1, arg2})
|
||||
fake.onTrackStatsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackStatsStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackStatsCallCount() int {
|
||||
fake.onTrackStatsMutex.RLock()
|
||||
defer fake.onTrackStatsMutex.RUnlock()
|
||||
return len(fake.onTrackStatsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackStatsCalls(stub func(telemetry.StatsKey, *livekit.AnalyticsStat)) {
|
||||
fake.onTrackStatsMutex.Lock()
|
||||
defer fake.onTrackStatsMutex.Unlock()
|
||||
fake.OnTrackStatsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackStatsArgsForCall(i int) (telemetry.StatsKey, *livekit.AnalyticsStat) {
|
||||
fake.onTrackStatsMutex.RLock()
|
||||
defer fake.onTrackStatsMutex.RUnlock()
|
||||
argsForCall := fake.onTrackStatsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailed(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 error, arg4 bool) {
|
||||
fake.onTrackSubscribeFailedMutex.Lock()
|
||||
fake.onTrackSubscribeFailedArgsForCall = append(fake.onTrackSubscribeFailedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 error
|
||||
arg4 bool
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackSubscribeFailedStub
|
||||
fake.recordInvocation("OnTrackSubscribeFailed", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackSubscribeFailedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackSubscribeFailedStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedCallCount() int {
|
||||
fake.onTrackSubscribeFailedMutex.RLock()
|
||||
defer fake.onTrackSubscribeFailedMutex.RUnlock()
|
||||
return len(fake.onTrackSubscribeFailedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedCalls(stub func(livekit.ParticipantID, livekit.TrackID, error, bool)) {
|
||||
fake.onTrackSubscribeFailedMutex.Lock()
|
||||
defer fake.onTrackSubscribeFailedMutex.Unlock()
|
||||
fake.OnTrackSubscribeFailedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, error, bool) {
|
||||
fake.onTrackSubscribeFailedMutex.RLock()
|
||||
defer fake.onTrackSubscribeFailedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackSubscribeFailedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStats(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 mime.MimeType, arg4 *livekit.RTPStats) {
|
||||
fake.onTrackSubscribeRTPStatsMutex.Lock()
|
||||
fake.onTrackSubscribeRTPStatsArgsForCall = append(fake.onTrackSubscribeRTPStatsArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.TrackID
|
||||
arg3 mime.MimeType
|
||||
arg4 *livekit.RTPStats
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackSubscribeRTPStatsStub
|
||||
fake.recordInvocation("OnTrackSubscribeRTPStats", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackSubscribeRTPStatsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackSubscribeRTPStatsStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsCallCount() int {
|
||||
fake.onTrackSubscribeRTPStatsMutex.RLock()
|
||||
defer fake.onTrackSubscribeRTPStatsMutex.RUnlock()
|
||||
return len(fake.onTrackSubscribeRTPStatsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsCalls(stub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats)) {
|
||||
fake.onTrackSubscribeRTPStatsMutex.Lock()
|
||||
defer fake.onTrackSubscribeRTPStatsMutex.Unlock()
|
||||
fake.OnTrackSubscribeRTPStatsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats) {
|
||||
fake.onTrackSubscribeRTPStatsMutex.RLock()
|
||||
defer fake.onTrackSubscribeRTPStatsMutex.RUnlock()
|
||||
argsForCall := fake.onTrackSubscribeRTPStatsArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequested(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
|
||||
fake.onTrackSubscribeRequestedMutex.Lock()
|
||||
fake.onTrackSubscribeRequestedArgsForCall = append(fake.onTrackSubscribeRequestedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackSubscribeRequestedStub
|
||||
fake.recordInvocation("OnTrackSubscribeRequested", []interface{}{arg1, arg2})
|
||||
fake.onTrackSubscribeRequestedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackSubscribeRequestedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedCallCount() int {
|
||||
fake.onTrackSubscribeRequestedMutex.RLock()
|
||||
defer fake.onTrackSubscribeRequestedMutex.RUnlock()
|
||||
return len(fake.onTrackSubscribeRequestedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
|
||||
fake.onTrackSubscribeRequestedMutex.Lock()
|
||||
defer fake.onTrackSubscribeRequestedMutex.Unlock()
|
||||
fake.OnTrackSubscribeRequestedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
|
||||
fake.onTrackSubscribeRequestedMutex.RLock()
|
||||
defer fake.onTrackSubscribeRequestedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackSubscribeRequestedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStarted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
|
||||
fake.onTrackSubscribeStreamStartedMutex.Lock()
|
||||
fake.onTrackSubscribeStreamStartedArgsForCall = append(fake.onTrackSubscribeStreamStartedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackSubscribeStreamStartedStub
|
||||
fake.recordInvocation("OnTrackSubscribeStreamStarted", []interface{}{arg1, arg2})
|
||||
fake.onTrackSubscribeStreamStartedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackSubscribeStreamStartedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedCallCount() int {
|
||||
fake.onTrackSubscribeStreamStartedMutex.RLock()
|
||||
defer fake.onTrackSubscribeStreamStartedMutex.RUnlock()
|
||||
return len(fake.onTrackSubscribeStreamStartedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
|
||||
fake.onTrackSubscribeStreamStartedMutex.Lock()
|
||||
defer fake.onTrackSubscribeStreamStartedMutex.Unlock()
|
||||
fake.OnTrackSubscribeStreamStartedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
|
||||
fake.onTrackSubscribeStreamStartedMutex.RLock()
|
||||
defer fake.onTrackSubscribeStreamStartedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackSubscribeStreamStartedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribed(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 *livekit.ParticipantInfo, arg4 bool) {
|
||||
fake.onTrackSubscribedMutex.Lock()
|
||||
fake.onTrackSubscribedArgsForCall = append(fake.onTrackSubscribedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 *livekit.ParticipantInfo
|
||||
arg4 bool
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackSubscribedStub
|
||||
fake.recordInvocation("OnTrackSubscribed", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackSubscribedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackSubscribedStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedCallCount() int {
|
||||
fake.onTrackSubscribedMutex.RLock()
|
||||
defer fake.onTrackSubscribedMutex.RUnlock()
|
||||
return len(fake.onTrackSubscribedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool)) {
|
||||
fake.onTrackSubscribedMutex.Lock()
|
||||
defer fake.onTrackSubscribedMutex.Unlock()
|
||||
fake.OnTrackSubscribedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool) {
|
||||
fake.onTrackSubscribedMutex.RLock()
|
||||
defer fake.onTrackSubscribedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackSubscribedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnmuted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
|
||||
fake.onTrackUnmutedMutex.Lock()
|
||||
fake.onTrackUnmutedArgsForCall = append(fake.onTrackUnmutedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
}{arg1, arg2})
|
||||
stub := fake.OnTrackUnmutedStub
|
||||
fake.recordInvocation("OnTrackUnmuted", []interface{}{arg1, arg2})
|
||||
fake.onTrackUnmutedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnmutedStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedCallCount() int {
|
||||
fake.onTrackUnmutedMutex.RLock()
|
||||
defer fake.onTrackUnmutedMutex.RUnlock()
|
||||
return len(fake.onTrackUnmutedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
|
||||
fake.onTrackUnmutedMutex.Lock()
|
||||
defer fake.onTrackUnmutedMutex.Unlock()
|
||||
fake.OnTrackUnmutedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
|
||||
fake.onTrackUnmutedMutex.RLock()
|
||||
defer fake.onTrackUnmutedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnmutedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublished(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo, arg4 bool) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 livekit.ParticipantIdentity
|
||||
arg3 *livekit.TrackInfo
|
||||
arg4 bool
|
||||
}{arg1, arg2, arg3, arg4})
|
||||
stub := fake.OnTrackUnpublishedStub
|
||||
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2, arg3, arg4})
|
||||
fake.onTrackUnpublishedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnpublishedStub(arg1, arg2, arg3, arg4)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedCallCount() int {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
return len(fake.onTrackUnpublishedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)) {
|
||||
fake.onTrackUnpublishedMutex.Lock()
|
||||
defer fake.onTrackUnpublishedMutex.Unlock()
|
||||
fake.OnTrackUnpublishedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool) {
|
||||
fake.onTrackUnpublishedMutex.RLock()
|
||||
defer fake.onTrackUnpublishedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribed(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 bool) {
|
||||
fake.onTrackUnsubscribedMutex.Lock()
|
||||
fake.onTrackUnsubscribedArgsForCall = append(fake.onTrackUnsubscribedArgsForCall, struct {
|
||||
arg1 livekit.ParticipantID
|
||||
arg2 *livekit.TrackInfo
|
||||
arg3 bool
|
||||
}{arg1, arg2, arg3})
|
||||
stub := fake.OnTrackUnsubscribedStub
|
||||
fake.recordInvocation("OnTrackUnsubscribed", []interface{}{arg1, arg2, arg3})
|
||||
fake.onTrackUnsubscribedMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.OnTrackUnsubscribedStub(arg1, arg2, arg3)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedCallCount() int {
|
||||
fake.onTrackUnsubscribedMutex.RLock()
|
||||
defer fake.onTrackUnsubscribedMutex.RUnlock()
|
||||
return len(fake.onTrackUnsubscribedArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, bool)) {
|
||||
fake.onTrackUnsubscribedMutex.Lock()
|
||||
defer fake.onTrackUnsubscribedMutex.Unlock()
|
||||
fake.OnTrackUnsubscribedStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, bool) {
|
||||
fake.onTrackUnsubscribedMutex.RLock()
|
||||
defer fake.onTrackUnsubscribedMutex.RUnlock()
|
||||
argsForCall := fake.onTrackUnsubscribedArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
|
||||
}
|
||||
|
||||
func (fake *FakeParticipantTelemetryListener) 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 *FakeParticipantTelemetryListener) 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.ParticipantTelemetryListener = new(FakeParticipantTelemetryListener)
|
||||
@@ -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 {
|
||||
@@ -69,42 +81,6 @@ type FakeRoom struct {
|
||||
resolveMediaTrackForSubscriberReturnsOnCall map[int]struct {
|
||||
result1 types.MediaResolverResult
|
||||
}
|
||||
SimulateScenarioStub func(types.LocalParticipant, *livekit.SimulateScenario) error
|
||||
simulateScenarioMutex sync.RWMutex
|
||||
simulateScenarioArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SimulateScenario
|
||||
}
|
||||
simulateScenarioReturns struct {
|
||||
result1 error
|
||||
}
|
||||
simulateScenarioReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
SyncStateStub func(types.LocalParticipant, *livekit.SyncState) error
|
||||
syncStateMutex sync.RWMutex
|
||||
syncStateArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SyncState
|
||||
}
|
||||
syncStateReturns struct {
|
||||
result1 error
|
||||
}
|
||||
syncStateReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
UpdateSubscriptionPermissionStub func(types.LocalParticipant, *livekit.SubscriptionPermission) error
|
||||
updateSubscriptionPermissionMutex sync.RWMutex
|
||||
updateSubscriptionPermissionArgsForCall []struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SubscriptionPermission
|
||||
}
|
||||
updateSubscriptionPermissionReturns struct {
|
||||
result1 error
|
||||
}
|
||||
updateSubscriptionPermissionReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
UpdateSubscriptionsStub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)
|
||||
updateSubscriptionsMutex sync.RWMutex
|
||||
updateSubscriptionsArgsForCall []struct {
|
||||
@@ -371,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)]
|
||||
@@ -433,192 +471,6 @@ func (fake *FakeRoom) ResolveMediaTrackForSubscriberReturnsOnCall(i int, result1
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenario(arg1 types.LocalParticipant, arg2 *livekit.SimulateScenario) error {
|
||||
fake.simulateScenarioMutex.Lock()
|
||||
ret, specificReturn := fake.simulateScenarioReturnsOnCall[len(fake.simulateScenarioArgsForCall)]
|
||||
fake.simulateScenarioArgsForCall = append(fake.simulateScenarioArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SimulateScenario
|
||||
}{arg1, arg2})
|
||||
stub := fake.SimulateScenarioStub
|
||||
fakeReturns := fake.simulateScenarioReturns
|
||||
fake.recordInvocation("SimulateScenario", []interface{}{arg1, arg2})
|
||||
fake.simulateScenarioMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenarioCallCount() int {
|
||||
fake.simulateScenarioMutex.RLock()
|
||||
defer fake.simulateScenarioMutex.RUnlock()
|
||||
return len(fake.simulateScenarioArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenarioCalls(stub func(types.LocalParticipant, *livekit.SimulateScenario) error) {
|
||||
fake.simulateScenarioMutex.Lock()
|
||||
defer fake.simulateScenarioMutex.Unlock()
|
||||
fake.SimulateScenarioStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenarioArgsForCall(i int) (types.LocalParticipant, *livekit.SimulateScenario) {
|
||||
fake.simulateScenarioMutex.RLock()
|
||||
defer fake.simulateScenarioMutex.RUnlock()
|
||||
argsForCall := fake.simulateScenarioArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenarioReturns(result1 error) {
|
||||
fake.simulateScenarioMutex.Lock()
|
||||
defer fake.simulateScenarioMutex.Unlock()
|
||||
fake.SimulateScenarioStub = nil
|
||||
fake.simulateScenarioReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SimulateScenarioReturnsOnCall(i int, result1 error) {
|
||||
fake.simulateScenarioMutex.Lock()
|
||||
defer fake.simulateScenarioMutex.Unlock()
|
||||
fake.SimulateScenarioStub = nil
|
||||
if fake.simulateScenarioReturnsOnCall == nil {
|
||||
fake.simulateScenarioReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.simulateScenarioReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncState(arg1 types.LocalParticipant, arg2 *livekit.SyncState) error {
|
||||
fake.syncStateMutex.Lock()
|
||||
ret, specificReturn := fake.syncStateReturnsOnCall[len(fake.syncStateArgsForCall)]
|
||||
fake.syncStateArgsForCall = append(fake.syncStateArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SyncState
|
||||
}{arg1, arg2})
|
||||
stub := fake.SyncStateStub
|
||||
fakeReturns := fake.syncStateReturns
|
||||
fake.recordInvocation("SyncState", []interface{}{arg1, arg2})
|
||||
fake.syncStateMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncStateCallCount() int {
|
||||
fake.syncStateMutex.RLock()
|
||||
defer fake.syncStateMutex.RUnlock()
|
||||
return len(fake.syncStateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncStateCalls(stub func(types.LocalParticipant, *livekit.SyncState) error) {
|
||||
fake.syncStateMutex.Lock()
|
||||
defer fake.syncStateMutex.Unlock()
|
||||
fake.SyncStateStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncStateArgsForCall(i int) (types.LocalParticipant, *livekit.SyncState) {
|
||||
fake.syncStateMutex.RLock()
|
||||
defer fake.syncStateMutex.RUnlock()
|
||||
argsForCall := fake.syncStateArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncStateReturns(result1 error) {
|
||||
fake.syncStateMutex.Lock()
|
||||
defer fake.syncStateMutex.Unlock()
|
||||
fake.SyncStateStub = nil
|
||||
fake.syncStateReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) SyncStateReturnsOnCall(i int, result1 error) {
|
||||
fake.syncStateMutex.Lock()
|
||||
defer fake.syncStateMutex.Unlock()
|
||||
fake.SyncStateStub = nil
|
||||
if fake.syncStateReturnsOnCall == nil {
|
||||
fake.syncStateReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.syncStateReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermission(arg1 types.LocalParticipant, arg2 *livekit.SubscriptionPermission) error {
|
||||
fake.updateSubscriptionPermissionMutex.Lock()
|
||||
ret, specificReturn := fake.updateSubscriptionPermissionReturnsOnCall[len(fake.updateSubscriptionPermissionArgsForCall)]
|
||||
fake.updateSubscriptionPermissionArgsForCall = append(fake.updateSubscriptionPermissionArgsForCall, struct {
|
||||
arg1 types.LocalParticipant
|
||||
arg2 *livekit.SubscriptionPermission
|
||||
}{arg1, arg2})
|
||||
stub := fake.UpdateSubscriptionPermissionStub
|
||||
fakeReturns := fake.updateSubscriptionPermissionReturns
|
||||
fake.recordInvocation("UpdateSubscriptionPermission", []interface{}{arg1, arg2})
|
||||
fake.updateSubscriptionPermissionMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermissionCallCount() int {
|
||||
fake.updateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.updateSubscriptionPermissionMutex.RUnlock()
|
||||
return len(fake.updateSubscriptionPermissionArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermissionCalls(stub func(types.LocalParticipant, *livekit.SubscriptionPermission) error) {
|
||||
fake.updateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.updateSubscriptionPermissionMutex.Unlock()
|
||||
fake.UpdateSubscriptionPermissionStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermissionArgsForCall(i int) (types.LocalParticipant, *livekit.SubscriptionPermission) {
|
||||
fake.updateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.updateSubscriptionPermissionMutex.RUnlock()
|
||||
argsForCall := fake.updateSubscriptionPermissionArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermissionReturns(result1 error) {
|
||||
fake.updateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.updateSubscriptionPermissionMutex.Unlock()
|
||||
fake.UpdateSubscriptionPermissionStub = nil
|
||||
fake.updateSubscriptionPermissionReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptionPermissionReturnsOnCall(i int, result1 error) {
|
||||
fake.updateSubscriptionPermissionMutex.Lock()
|
||||
defer fake.updateSubscriptionPermissionMutex.Unlock()
|
||||
fake.UpdateSubscriptionPermissionStub = nil
|
||||
if fake.updateSubscriptionPermissionReturnsOnCall == nil {
|
||||
fake.updateSubscriptionPermissionReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.updateSubscriptionPermissionReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *FakeRoom) UpdateSubscriptions(arg1 types.LocalParticipant, arg2 []livekit.TrackID, arg3 []*livekit.ParticipantTracks, arg4 bool) {
|
||||
var arg2Copy []livekit.TrackID
|
||||
if arg2 != nil {
|
||||
@@ -667,26 +519,6 @@ func (fake *FakeRoom) UpdateSubscriptionsArgsForCall(i int) (types.LocalParticip
|
||||
func (fake *FakeRoom) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.getLocalParticipantsMutex.RLock()
|
||||
defer fake.getLocalParticipantsMutex.RUnlock()
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
fake.isDataMessageUserPacketDuplicateMutex.RLock()
|
||||
defer fake.isDataMessageUserPacketDuplicateMutex.RUnlock()
|
||||
fake.nameMutex.RLock()
|
||||
defer fake.nameMutex.RUnlock()
|
||||
fake.removeParticipantMutex.RLock()
|
||||
defer fake.removeParticipantMutex.RUnlock()
|
||||
fake.resolveMediaTrackForSubscriberMutex.RLock()
|
||||
defer fake.resolveMediaTrackForSubscriberMutex.RUnlock()
|
||||
fake.simulateScenarioMutex.RLock()
|
||||
defer fake.simulateScenarioMutex.RUnlock()
|
||||
fake.syncStateMutex.RLock()
|
||||
defer fake.syncStateMutex.RUnlock()
|
||||
fake.updateSubscriptionPermissionMutex.RLock()
|
||||
defer fake.updateSubscriptionPermissionMutex.RUnlock()
|
||||
fake.updateSubscriptionsMutex.RLock()
|
||||
defer fake.updateSubscriptionsMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -1052,44 +1052,6 @@ func (fake *FakeSubscribedTrack) UpdateVideoLayerCalls(stub func()) {
|
||||
func (fake *FakeSubscribedTrack) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.addOnBindMutex.RLock()
|
||||
defer fake.addOnBindMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.downTrackMutex.RLock()
|
||||
defer fake.downTrackMutex.RUnlock()
|
||||
fake.iDMutex.RLock()
|
||||
defer fake.iDMutex.RUnlock()
|
||||
fake.isBoundMutex.RLock()
|
||||
defer fake.isBoundMutex.RUnlock()
|
||||
fake.isMutedMutex.RLock()
|
||||
defer fake.isMutedMutex.RUnlock()
|
||||
fake.mediaTrackMutex.RLock()
|
||||
defer fake.mediaTrackMutex.RUnlock()
|
||||
fake.needsNegotiationMutex.RLock()
|
||||
defer fake.needsNegotiationMutex.RUnlock()
|
||||
fake.onCloseMutex.RLock()
|
||||
defer fake.onCloseMutex.RUnlock()
|
||||
fake.publisherIDMutex.RLock()
|
||||
defer fake.publisherIDMutex.RUnlock()
|
||||
fake.publisherIdentityMutex.RLock()
|
||||
defer fake.publisherIdentityMutex.RUnlock()
|
||||
fake.publisherVersionMutex.RLock()
|
||||
defer fake.publisherVersionMutex.RUnlock()
|
||||
fake.rTPSenderMutex.RLock()
|
||||
defer fake.rTPSenderMutex.RUnlock()
|
||||
fake.setPublisherMutedMutex.RLock()
|
||||
defer fake.setPublisherMutedMutex.RUnlock()
|
||||
fake.subscriberMutex.RLock()
|
||||
defer fake.subscriberMutex.RUnlock()
|
||||
fake.subscriberIDMutex.RLock()
|
||||
defer fake.subscriberIDMutex.RUnlock()
|
||||
fake.subscriberIdentityMutex.RLock()
|
||||
defer fake.subscriberIdentityMutex.RUnlock()
|
||||
fake.updateSubscriberSettingsMutex.RLock()
|
||||
defer fake.updateSubscriberSettingsMutex.RUnlock()
|
||||
fake.updateVideoLayerMutex.RLock()
|
||||
defer fake.updateVideoLayerMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -384,16 +384,6 @@ func (fake *FakeWebsocketClient) WriteMessageReturnsOnCall(i int, result1 error)
|
||||
func (fake *FakeWebsocketClient) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.readMessageMutex.RLock()
|
||||
defer fake.readMessageMutex.RUnlock()
|
||||
fake.setReadDeadlineMutex.RLock()
|
||||
defer fake.setReadDeadlineMutex.RUnlock()
|
||||
fake.writeControlMutex.RLock()
|
||||
defer fake.writeControlMutex.RUnlock()
|
||||
fake.writeMessageMutex.RLock()
|
||||
defer fake.writeMessageMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
// 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/pion/interceptor"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/sdp/v3"
|
||||
"github.com/pion/webrtc/v4"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
simulcastProbeCount = 10
|
||||
)
|
||||
|
||||
type UnhandleSimulcastOption func(r *UnhandleSimulcastInterceptor) error
|
||||
|
||||
func UnhandleSimulcastTracks(tracks map[uint32]SimulcastTrackInfo) UnhandleSimulcastOption {
|
||||
return func(r *UnhandleSimulcastInterceptor) error {
|
||||
r.simTracks = tracks
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type UnhandleSimulcastInterceptorFactory struct {
|
||||
opts []UnhandleSimulcastOption
|
||||
}
|
||||
|
||||
func (f *UnhandleSimulcastInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
|
||||
i := &UnhandleSimulcastInterceptor{simTracks: map[uint32]SimulcastTrackInfo{}}
|
||||
for _, o := range f.opts {
|
||||
if err := o(i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func NewUnhandleSimulcastInterceptorFactory(opts ...UnhandleSimulcastOption) (*UnhandleSimulcastInterceptorFactory, error) {
|
||||
return &UnhandleSimulcastInterceptorFactory{opts: opts}, nil
|
||||
}
|
||||
|
||||
type unhandleSimulcastRTPReader struct {
|
||||
SimulcastTrackInfo
|
||||
tryTimes int
|
||||
reader interceptor.RTPReader
|
||||
midExtensionID uint8
|
||||
streamIDExtensionID uint8
|
||||
}
|
||||
|
||||
func (r *unhandleSimulcastRTPReader) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
|
||||
n, a, err := r.reader.Read(b, a)
|
||||
if r.tryTimes < 0 || err != nil {
|
||||
return n, a, err
|
||||
}
|
||||
|
||||
header := rtp.Header{}
|
||||
hsize, err := header.Unmarshal(b[:n])
|
||||
if err != nil {
|
||||
return n, a, nil
|
||||
}
|
||||
var mid, rid string
|
||||
if payload := header.GetExtension(r.midExtensionID); payload != nil {
|
||||
mid = string(payload)
|
||||
}
|
||||
|
||||
if payload := header.GetExtension(r.streamIDExtensionID); payload != nil {
|
||||
rid = string(payload)
|
||||
}
|
||||
|
||||
if mid != "" && rid != "" {
|
||||
r.tryTimes = -1
|
||||
return n, a, nil
|
||||
}
|
||||
|
||||
r.tryTimes--
|
||||
|
||||
if mid == "" {
|
||||
header.SetExtension(r.midExtensionID, []byte(r.Mid))
|
||||
}
|
||||
if rid == "" {
|
||||
header.SetExtension(r.streamIDExtensionID, []byte(r.Rid))
|
||||
}
|
||||
|
||||
hsize2 := header.MarshalSize()
|
||||
|
||||
if hsize2-hsize+n > len(b) { // no enough buf to set extension
|
||||
return n, a, nil
|
||||
}
|
||||
copy(b[hsize2:], b[hsize:n])
|
||||
header.MarshalTo(b)
|
||||
return hsize2 - hsize + n, a, nil
|
||||
}
|
||||
|
||||
type UnhandleSimulcastInterceptor struct {
|
||||
interceptor.NoOp
|
||||
simTracks map[uint32]SimulcastTrackInfo
|
||||
}
|
||||
|
||||
func (u *UnhandleSimulcastInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
|
||||
if t, ok := u.simTracks[info.SSRC]; ok {
|
||||
// if we support fec for simulcast streams at future, should get rsid extensions
|
||||
midExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI})
|
||||
streamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI})
|
||||
if midExtensionID == 0 || streamIDExtensionID == 0 {
|
||||
return reader
|
||||
}
|
||||
|
||||
return &unhandleSimulcastRTPReader{
|
||||
SimulcastTrackInfo: t,
|
||||
reader: reader,
|
||||
tryTimes: simulcastProbeCount,
|
||||
midExtensionID: uint8(midExtensionID),
|
||||
streamIDExtensionID: uint8(streamIDExtensionID),
|
||||
}
|
||||
}
|
||||
return reader
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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 (
|
||||
"maps"
|
||||
"slices"
|
||||
"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"
|
||||
)
|
||||
|
||||
type UpDataTrackManagerParams struct {
|
||||
Logger logger.Logger
|
||||
Participant types.Participant
|
||||
}
|
||||
|
||||
type UpDataTrackManager struct {
|
||||
params UpDataTrackManagerParams
|
||||
|
||||
lock sync.RWMutex
|
||||
dataTracks map[uint16]types.DataTrack
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewUpDataTrackManager(params UpDataTrackManagerParams) *UpDataTrackManager {
|
||||
return &UpDataTrackManager{
|
||||
params: params,
|
||||
dataTracks: make(map[uint16]types.DataTrack),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) Close() {
|
||||
u.lock.Lock()
|
||||
if u.closed {
|
||||
u.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
u.closed = true
|
||||
|
||||
dataTracks := u.dataTracks
|
||||
u.dataTracks = make(map[uint16]types.DataTrack)
|
||||
u.lock.Unlock()
|
||||
|
||||
for _, t := range dataTracks {
|
||||
t.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) AddPublishedDataTrack(dt types.DataTrack) {
|
||||
u.lock.Lock()
|
||||
u.dataTracks[dt.PubHandle()] = dt
|
||||
u.lock.Unlock()
|
||||
|
||||
u.params.Participant.GetParticipantListener().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()
|
||||
|
||||
u.params.Participant.GetParticipantListener().OnDataTrackUnpublished(u.params.Participant, dt)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpDataTrackManager) GetPublishedDataTracks() []types.DataTrack {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
return slices.Collect(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, arrivalTime int64) {
|
||||
u.lock.RLock()
|
||||
dt := u.dataTracks[packet.Handle]
|
||||
u.lock.RUnlock()
|
||||
if dt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dt.HandlePacket(data, packet, arrivalTime)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -16,11 +16,12 @@ package rtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"maps"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc/types"
|
||||
"github.com/livekit/protocol/utils"
|
||||
@@ -116,13 +117,15 @@ func (u *UpTrackManager) OnPublishedTrackUpdated(f func(track types.MediaTrack))
|
||||
u.onTrackUpdated = f
|
||||
}
|
||||
|
||||
func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted bool) types.MediaTrack {
|
||||
func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted bool) (types.MediaTrack, bool) {
|
||||
changed := false
|
||||
track := u.GetPublishedTrack(trackID)
|
||||
if track != nil {
|
||||
currentMuted := track.IsMuted()
|
||||
track.SetMuted(muted)
|
||||
|
||||
if currentMuted != track.IsMuted() {
|
||||
changed = true
|
||||
u.params.Logger.Debugw("publisher mute status changed", "trackID", trackID, "muted", track.IsMuted())
|
||||
if u.onTrackUpdated != nil {
|
||||
u.onTrackUpdated(track)
|
||||
@@ -130,7 +133,7 @@ func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted b
|
||||
}
|
||||
}
|
||||
|
||||
return track
|
||||
return track, changed
|
||||
}
|
||||
|
||||
func (u *UpTrackManager) GetPublishedTrack(trackID livekit.TrackID) types.MediaTrack {
|
||||
@@ -144,7 +147,7 @@ func (u *UpTrackManager) GetPublishedTracks() []types.MediaTrack {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
return maps.Values(u.publishedTracks)
|
||||
return slices.Collect(maps.Values(u.publishedTracks))
|
||||
}
|
||||
|
||||
func (u *UpTrackManager) UpdateSubscriptionPermission(
|
||||
@@ -272,12 +275,9 @@ func (u *UpTrackManager) AddPublishedTrack(track types.MediaTrack) {
|
||||
})
|
||||
}
|
||||
|
||||
func (u *UpTrackManager) RemovePublishedTrack(track types.MediaTrack, isExpectedToResume bool, shouldClose bool) {
|
||||
if shouldClose {
|
||||
track.Close(isExpectedToResume)
|
||||
} else {
|
||||
track.ClearAllReceivers(isExpectedToResume)
|
||||
}
|
||||
func (u *UpTrackManager) RemovePublishedTrack(track types.MediaTrack, isExpectedToResume bool) {
|
||||
track.Close(isExpectedToResume)
|
||||
|
||||
u.lock.Lock()
|
||||
delete(u.publishedTracks, track.ID())
|
||||
u.lock.Unlock()
|
||||
@@ -311,7 +311,11 @@ func (u *UpTrackManager) parseSubscriptionPermissionsLocked(
|
||||
|
||||
sub := resolver(livekit.ParticipantID(trackPerms.ParticipantSid))
|
||||
if sub == nil {
|
||||
u.params.Logger.Warnw("could not find subscriber for permissions update", nil, "subscriberID", trackPerms.ParticipantSid)
|
||||
u.params.Logger.Warnw(
|
||||
"could not find subscriber for permissions update", nil,
|
||||
"subscriberID", trackPerms.ParticipantSid,
|
||||
"subscriptionPermission", logger.Proto(subscriptionPermission),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -320,10 +324,19 @@ func (u *UpTrackManager) parseSubscriptionPermissionsLocked(
|
||||
if trackPerms.ParticipantSid != "" {
|
||||
sub := resolver(livekit.ParticipantID(trackPerms.ParticipantSid))
|
||||
if sub != nil && sub.Identity() != subscriberIdentity {
|
||||
u.params.Logger.Errorw("participant identity mismatch", nil, "expected", subscriberIdentity, "got", sub.Identity())
|
||||
u.params.Logger.Errorw(
|
||||
"participant identity mismatch", nil,
|
||||
"expected", subscriberIdentity,
|
||||
"got", sub.Identity(),
|
||||
"subscriptionPermission", logger.Proto(subscriptionPermission),
|
||||
)
|
||||
}
|
||||
if sub == nil {
|
||||
u.params.Logger.Warnw("could not find subscriber for permissions update", nil, "subscriberID", trackPerms.ParticipantSid)
|
||||
u.params.Logger.Warnw(
|
||||
"could not find subscriber for permissions update", nil,
|
||||
"subscriberID", trackPerms.ParticipantSid,
|
||||
"subscriptionPermission", logger.Proto(subscriptionPermission),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,16 +412,16 @@ func (u *UpTrackManager) maybeRevokeSubscriptions() {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpTrackManager) DebugInfo() map[string]interface{} {
|
||||
info := map[string]interface{}{}
|
||||
publishedTrackInfo := make(map[livekit.TrackID]interface{})
|
||||
func (u *UpTrackManager) DebugInfo() map[string]any {
|
||||
info := map[string]any{}
|
||||
publishedTrackInfo := make(map[livekit.TrackID]any)
|
||||
|
||||
u.lock.RLock()
|
||||
for trackID, track := range u.publishedTracks {
|
||||
if mt, ok := track.(*MediaTrack); ok {
|
||||
publishedTrackInfo[trackID] = mt.DebugInfo()
|
||||
} else {
|
||||
publishedTrackInfo[trackID] = map[string]interface{}{
|
||||
publishedTrackInfo[trackID] = map[string]any{
|
||||
"ID": track.ID(),
|
||||
"Kind": track.Kind().String(),
|
||||
"PubMuted": track.IsMuted(),
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/webrtc/v4"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
)
|
||||
@@ -80,49 +80,6 @@ func UnpackDataTrackLabel(packed string) (participantID livekit.ParticipantID, t
|
||||
return
|
||||
}
|
||||
|
||||
func ToProtoSessionDescription(sd webrtc.SessionDescription) *livekit.SessionDescription {
|
||||
return &livekit.SessionDescription{
|
||||
Type: sd.Type.String(),
|
||||
Sdp: sd.SDP,
|
||||
}
|
||||
}
|
||||
|
||||
func FromProtoSessionDescription(sd *livekit.SessionDescription) webrtc.SessionDescription {
|
||||
var sdType webrtc.SDPType
|
||||
switch sd.Type {
|
||||
case webrtc.SDPTypeOffer.String():
|
||||
sdType = webrtc.SDPTypeOffer
|
||||
case webrtc.SDPTypeAnswer.String():
|
||||
sdType = webrtc.SDPTypeAnswer
|
||||
case webrtc.SDPTypePranswer.String():
|
||||
sdType = webrtc.SDPTypePranswer
|
||||
case webrtc.SDPTypeRollback.String():
|
||||
sdType = webrtc.SDPTypeRollback
|
||||
}
|
||||
return webrtc.SessionDescription{
|
||||
Type: sdType,
|
||||
SDP: sd.Sdp,
|
||||
}
|
||||
}
|
||||
|
||||
func ToProtoTrickle(candidateInit webrtc.ICECandidateInit, target livekit.SignalTarget, final bool) *livekit.TrickleRequest {
|
||||
data, _ := json.Marshal(candidateInit)
|
||||
return &livekit.TrickleRequest{
|
||||
CandidateInit: string(data),
|
||||
Target: target,
|
||||
Final: final,
|
||||
}
|
||||
}
|
||||
|
||||
func FromProtoTrickle(trickle *livekit.TrickleRequest) (webrtc.ICECandidateInit, error) {
|
||||
ci := webrtc.ICECandidateInit{}
|
||||
err := json.Unmarshal([]byte(trickle.CandidateInit), &ci)
|
||||
if err != nil {
|
||||
return webrtc.ICECandidateInit{}, err
|
||||
}
|
||||
return ci, nil
|
||||
}
|
||||
|
||||
func ToProtoTrackKind(kind webrtc.RTPCodecType) livekit.TrackType {
|
||||
switch kind {
|
||||
case webrtc.RTPCodecTypeVideo:
|
||||
@@ -160,12 +117,12 @@ func Recover(l logger.Logger) any {
|
||||
|
||||
// logger helpers
|
||||
func LoggerWithParticipant(l logger.Logger, identity livekit.ParticipantIdentity, sid livekit.ParticipantID, isRemote bool) logger.Logger {
|
||||
values := make([]interface{}, 0, 4)
|
||||
values := make([]any, 0, 4)
|
||||
if identity != "" {
|
||||
values = append(values, "participant", identity)
|
||||
}
|
||||
if sid != "" {
|
||||
values = append(values, "pID", sid)
|
||||
values = append(values, "participantID", sid)
|
||||
}
|
||||
values = append(values, "remote", isRemote)
|
||||
// enable sampling per participant
|
||||
@@ -173,7 +130,7 @@ func LoggerWithParticipant(l logger.Logger, identity livekit.ParticipantIdentity
|
||||
}
|
||||
|
||||
func LoggerWithRoom(l logger.Logger, name livekit.RoomName, roomID livekit.RoomID) logger.Logger {
|
||||
values := make([]interface{}, 0, 2)
|
||||
values := make([]any, 0, 2)
|
||||
if name != "" {
|
||||
values = append(values, "room", name)
|
||||
}
|
||||
@@ -215,3 +172,31 @@ func MaybeTruncateIP(addr string) string {
|
||||
|
||||
return addr[:len(addr)-3] + "..."
|
||||
}
|
||||
|
||||
func ChunkProtoBatch[T proto.Message](batch []T, target int) [][]T {
|
||||
var chunks [][]T
|
||||
var start, size int
|
||||
for i, m := range batch {
|
||||
s := proto.Size(m)
|
||||
if size+s > target {
|
||||
if start < i {
|
||||
chunks = append(chunks, batch[start:i])
|
||||
}
|
||||
start = i
|
||||
size = 0
|
||||
}
|
||||
size += s
|
||||
}
|
||||
if start < len(batch) {
|
||||
chunks = append(chunks, batch[start:])
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
func IsRedEnabled(ti *livekit.TrackInfo) bool {
|
||||
if len(ti.Codecs) != 0 && ti.Codecs[0].MimeType != "" {
|
||||
return mime.IsMimeTypeStringRED(ti.Codecs[0].MimeType)
|
||||
}
|
||||
|
||||
return !ti.GetDisableRed()
|
||||
}
|
||||
|
||||
@@ -15,11 +15,16 @@
|
||||
package rtc
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/utils/guid"
|
||||
)
|
||||
|
||||
func TestPackStreamId(t *testing.T) {
|
||||
@@ -43,3 +48,28 @@ func TestPackDataTrackLabel(t *testing.T) {
|
||||
require.Equal(t, trackID, tr)
|
||||
require.Equal(t, label, l)
|
||||
}
|
||||
|
||||
func TestChunkProtoBatch(t *testing.T) {
|
||||
rng := rand.New(rand.NewPCG(1, 2))
|
||||
var updates []*livekit.ParticipantInfo
|
||||
for range 32 {
|
||||
updates = append(updates, &livekit.ParticipantInfo{
|
||||
Sid: guid.New(guid.ParticipantPrefix),
|
||||
Identity: uuid.NewString(),
|
||||
Metadata: strings.Repeat("x", rng.IntN(128*1024)),
|
||||
})
|
||||
}
|
||||
|
||||
target := 64 * 1024
|
||||
batches := ChunkProtoBatch(updates, target)
|
||||
var count int
|
||||
for _, b := range batches {
|
||||
var sum int
|
||||
for _, m := range b {
|
||||
sum += proto.Size(m)
|
||||
count++
|
||||
}
|
||||
require.True(t, sum < target || len(b) == 1, "batch size exceeds target")
|
||||
}
|
||||
require.Equal(t, len(updates), count)
|
||||
}
|
||||
|
||||
@@ -16,18 +16,21 @@ package rtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"maps"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/webrtc/v4"
|
||||
"go.uber.org/atomic"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/livekit/mediatransportutil/pkg/codec"
|
||||
protoCodecs "github.com/livekit/protocol/codecs"
|
||||
"github.com/livekit/protocol/codecs/mime"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/livekit/protocol/logger"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/sfu"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/mime"
|
||||
"github.com/livekit/livekit-server/pkg/sfu/buffer"
|
||||
)
|
||||
|
||||
// wrapper around WebRTC receiver, overriding its ID
|
||||
@@ -39,6 +42,7 @@ type WrappedReceiverParams struct {
|
||||
UpstreamCodecs []webrtc.RTPCodecParameters
|
||||
Logger logger.Logger
|
||||
DisableRed bool
|
||||
IsEncrypted bool
|
||||
}
|
||||
|
||||
type WrappedReceiver struct {
|
||||
@@ -62,16 +66,10 @@ func NewWrappedReceiver(params WrappedReceiverParams) *WrappedReceiver {
|
||||
normalizedMimeType := mime.NormalizeMimeType(codecs[0].MimeType)
|
||||
if normalizedMimeType == mime.MimeTypeRED {
|
||||
// if upstream is opus/red, then add opus to match clients that don't support red
|
||||
codecs = append(codecs, webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: OpusCodecCapability,
|
||||
PayloadType: 111,
|
||||
})
|
||||
codecs = append(codecs, protoCodecs.OpusCodecParameters)
|
||||
} else if !params.DisableRed && normalizedMimeType == mime.MimeTypeOpus {
|
||||
// if upstream is opus only and red enabled, add red to match clients that support red
|
||||
codecs = append(codecs, webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: RedCodecCapability,
|
||||
PayloadType: 63,
|
||||
})
|
||||
codecs = append(codecs, protoCodecs.RedCodecParameters)
|
||||
// prefer red codec
|
||||
codecs[0], codecs[1] = codecs[1], codecs[0]
|
||||
}
|
||||
@@ -92,54 +90,67 @@ func (r *WrappedReceiver) StreamID() string {
|
||||
return r.params.StreamId
|
||||
}
|
||||
|
||||
// DetermineReceiver determines the receiver of negotiated codec and return ready state of the receiver
|
||||
func (r *WrappedReceiver) DetermineReceiver(codec webrtc.RTPCodecCapability) bool {
|
||||
// DetermineReceiver determines the receiver of negotiated codec and returns
|
||||
//
|
||||
// isAvailable: returns true if given codec is a potential codec from publisher or if an existing published codec can be translated
|
||||
// needsPublish: indicates if the codec is needed from publisher, some combinations can be achieved via codec translation internally,
|
||||
//
|
||||
// example: unencrypted opus -> RED translation and vice-versa can be done without the need for publisher to send the other codec.
|
||||
func (r *WrappedReceiver) DetermineReceiver(codec webrtc.RTPCodecCapability) (isAvailable bool, needsPublish bool) {
|
||||
r.lock.Lock()
|
||||
|
||||
reason := "no matching receiver"
|
||||
codecMimeType := mime.NormalizeMimeType(codec.MimeType)
|
||||
var trackReceiver sfu.TrackReceiver
|
||||
for _, receiver := range r.receivers {
|
||||
receiverMimeType := receiver.Mime()
|
||||
if receiverMimeType == codecMimeType {
|
||||
trackReceiver = receiver
|
||||
break
|
||||
} else if receiverMimeType == mime.MimeTypeRED && codecMimeType == mime.MimeTypeOpus {
|
||||
// audio opus/red can match opus only
|
||||
trackReceiver = receiver.GetPrimaryReceiverForRed()
|
||||
break
|
||||
} else if receiverMimeType == mime.MimeTypeOpus && codecMimeType == mime.MimeTypeRED {
|
||||
trackReceiver = receiver.GetRedReceiver()
|
||||
isAvailable = true
|
||||
needsPublish = true
|
||||
break
|
||||
}
|
||||
|
||||
if receiverMimeType == mime.MimeTypeRED && codecMimeType == mime.MimeTypeOpus {
|
||||
// audio opus/red can match opus only
|
||||
if !r.params.IsEncrypted { // cannot match encrypted source
|
||||
trackReceiver = receiver.GetPrimaryReceiverForRed()
|
||||
isAvailable = true
|
||||
break
|
||||
} else {
|
||||
reason = "encrypted source"
|
||||
}
|
||||
} else if receiverMimeType == mime.MimeTypeOpus && codecMimeType == mime.MimeTypeRED {
|
||||
if !r.params.IsEncrypted { // cannot match encrypted source
|
||||
trackReceiver = receiver.GetRedReceiver()
|
||||
isAvailable = true
|
||||
break
|
||||
} else {
|
||||
reason = "encrypted source"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if trackReceiver == nil {
|
||||
r.params.Logger.Errorw("can't determine receiver for codec", nil, "codec", codec.MimeType)
|
||||
if len(r.receivers) > 0 {
|
||||
trackReceiver = r.receivers[0]
|
||||
}
|
||||
r.lock.Unlock()
|
||||
r.params.Logger.Warnw(
|
||||
"can't determine receiver for codec", nil,
|
||||
"codec", codec.MimeType,
|
||||
"reason", reason,
|
||||
)
|
||||
return
|
||||
}
|
||||
r.TrackReceiver = trackReceiver
|
||||
|
||||
var onReadyCallbacks []func()
|
||||
if trackReceiver != nil {
|
||||
onReadyCallbacks = r.onReadyCallbacks
|
||||
r.onReadyCallbacks = nil
|
||||
}
|
||||
onReadyCallbacks := r.onReadyCallbacks
|
||||
r.onReadyCallbacks = nil
|
||||
r.lock.Unlock()
|
||||
|
||||
if trackReceiver != nil {
|
||||
for _, f := range onReadyCallbacks {
|
||||
trackReceiver.AddOnReady(f)
|
||||
}
|
||||
|
||||
if s, ok := trackReceiver.(*simulcastReceiver); ok {
|
||||
if d, ok := s.TrackReceiver.(*DummyReceiver); ok {
|
||||
return d.IsReady()
|
||||
}
|
||||
}
|
||||
return true
|
||||
for _, f := range onReadyCallbacks {
|
||||
trackReceiver.AddOnReady(f)
|
||||
}
|
||||
return false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *WrappedReceiver) Codecs() []webrtc.RTPCodecParameters {
|
||||
@@ -186,8 +197,6 @@ type DummyReceiver struct {
|
||||
settingsLock sync.Mutex
|
||||
maxExpectedLayerValid bool
|
||||
maxExpectedLayer int32
|
||||
pausedValid bool
|
||||
paused bool
|
||||
|
||||
redReceiver, primaryReceiver *DummyRedReceiver
|
||||
}
|
||||
@@ -217,8 +226,10 @@ func (d *DummyReceiver) Upgrade(receiver sfu.TrackReceiver) {
|
||||
receiver.AddDownTrack(t)
|
||||
}
|
||||
d.downTracks = make(map[livekit.ParticipantID]sfu.TrackSender)
|
||||
|
||||
onReadyCallbacks := d.onReadyCallbacks
|
||||
d.onReadyCallbacks = nil
|
||||
|
||||
codecChange := d.onCodecStateChange
|
||||
d.onCodecStateChange = nil
|
||||
d.downTrackLock.Unlock()
|
||||
@@ -232,16 +243,15 @@ func (d *DummyReceiver) Upgrade(receiver sfu.TrackReceiver) {
|
||||
}
|
||||
|
||||
d.settingsLock.Lock()
|
||||
if d.maxExpectedLayerValid {
|
||||
maxExpectedLayerValid := d.maxExpectedLayerValid
|
||||
d.maxExpectedLayerValid = false
|
||||
d.settingsLock.Unlock()
|
||||
|
||||
if maxExpectedLayerValid {
|
||||
receiver.SetMaxExpectedSpatialLayer(d.maxExpectedLayer)
|
||||
}
|
||||
d.maxExpectedLayerValid = false
|
||||
|
||||
if d.pausedValid {
|
||||
receiver.SetUpTrackPaused(d.paused)
|
||||
}
|
||||
d.pausedValid = false
|
||||
|
||||
d.settingsLock.Lock()
|
||||
if d.primaryReceiver != nil {
|
||||
d.primaryReceiver.upgrade(receiver)
|
||||
}
|
||||
@@ -260,83 +270,82 @@ func (d *DummyReceiver) StreamID() string {
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) Codec() webrtc.RTPCodecParameters {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.Codec()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.Codec()
|
||||
}
|
||||
return d.codec
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) Mime() mime.MimeType {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.Mime()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.Mime()
|
||||
}
|
||||
return mime.NormalizeMimeType(d.codec.MimeType)
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) VideoLayerMode() livekit.VideoLayer_Mode {
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.VideoLayerMode()
|
||||
}
|
||||
return buffer.GetVideoLayerModeForMimeType(d.Mime(), d.TrackInfo())
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) HeaderExtensions() []webrtc.RTPHeaderExtensionParameter {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.HeaderExtensions()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.HeaderExtensions()
|
||||
}
|
||||
return d.headerExtensions
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) ReadRTP(buf []byte, layer uint8, esn uint64) (int, error) {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.ReadRTP(buf, layer, esn)
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.ReadRTP(buf, layer, esn)
|
||||
}
|
||||
return 0, errors.New("no receiver")
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) GetLayeredBitrate() ([]int32, sfu.Bitrates) {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetLayeredBitrate()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.GetLayeredBitrate()
|
||||
}
|
||||
return nil, sfu.Bitrates{}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) GetAudioLevel() (float64, bool) {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetAudioLevel()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.GetAudioLevel()
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) SendPLI(layer int32, force bool) {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
r.SendPLI(layer, force)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) SetUpTrackPaused(paused bool) {
|
||||
d.settingsLock.Lock()
|
||||
defer d.settingsLock.Unlock()
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
d.pausedValid = false
|
||||
r.SetUpTrackPaused(paused)
|
||||
} else {
|
||||
d.pausedValid = true
|
||||
d.paused = paused
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
receiver.SendPLI(layer, force)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) SetMaxExpectedSpatialLayer(layer int32) {
|
||||
d.settingsLock.Lock()
|
||||
defer d.settingsLock.Unlock()
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
receiver := d.getReceiver()
|
||||
if receiver != nil {
|
||||
d.maxExpectedLayerValid = false
|
||||
r.SetMaxExpectedSpatialLayer(layer)
|
||||
} else {
|
||||
d.maxExpectedLayerValid = true
|
||||
d.maxExpectedLayer = layer
|
||||
}
|
||||
d.settingsLock.Unlock()
|
||||
|
||||
if receiver != nil {
|
||||
receiver.SetMaxExpectedSpatialLayer(layer)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) AddDownTrack(track sfu.TrackSender) error {
|
||||
d.downTrackLock.Lock()
|
||||
defer d.downTrackLock.Unlock()
|
||||
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
r.AddDownTrack(track)
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
receiver.AddDownTrack(track)
|
||||
} else {
|
||||
d.downTracks[track.SubscriberID()] = track
|
||||
}
|
||||
@@ -347,8 +356,8 @@ func (d *DummyReceiver) DeleteDownTrack(subscriberID livekit.ParticipantID) {
|
||||
d.downTrackLock.Lock()
|
||||
defer d.downTrackLock.Unlock()
|
||||
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
r.DeleteDownTrack(subscriberID)
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
receiver.DeleteDownTrack(subscriberID)
|
||||
} else {
|
||||
delete(d.downTracks, subscriberID)
|
||||
}
|
||||
@@ -358,42 +367,42 @@ func (d *DummyReceiver) GetDownTracks() []sfu.TrackSender {
|
||||
d.downTrackLock.Lock()
|
||||
defer d.downTrackLock.Unlock()
|
||||
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetDownTracks()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.GetDownTracks()
|
||||
}
|
||||
return maps.Values(d.downTracks)
|
||||
return slices.Collect(maps.Values(d.downTracks))
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) DebugInfo() map[string]interface{} {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.DebugInfo()
|
||||
func (d *DummyReceiver) DebugInfo() map[string]any {
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.DebugInfo()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) GetTemporalLayerFpsForSpatial(spatial int32) []float32 {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetTemporalLayerFpsForSpatial(spatial)
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.GetTemporalLayerFpsForSpatial(spatial)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) TrackInfo() *livekit.TrackInfo {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.TrackInfo()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.TrackInfo()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
r.UpdateTrackInfo(ti)
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
receiver.UpdateTrackInfo(ti)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) IsClosed() bool {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.IsClosed()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.IsClosed()
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -424,18 +433,16 @@ func (d *DummyReceiver) GetRedReceiver() sfu.TrackReceiver {
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) GetTrackStats() *livekit.RTPStats {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetTrackStats()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.GetTrackStats()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) AddOnReady(f func()) {
|
||||
var receiver sfu.TrackReceiver
|
||||
d.downTrackLock.Lock()
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
receiver = r
|
||||
} else {
|
||||
receiver := d.getReceiver()
|
||||
if receiver == nil {
|
||||
d.onReadyCallbacks = append(d.onReadyCallbacks, f)
|
||||
}
|
||||
d.downTrackLock.Unlock()
|
||||
@@ -444,16 +451,10 @@ func (d *DummyReceiver) AddOnReady(f func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) IsReady() bool {
|
||||
return d.receiver.Load() != nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) AddOnCodecStateChange(f func(codec webrtc.RTPCodecParameters, state sfu.ReceiverCodecState)) {
|
||||
var receiver sfu.TrackReceiver
|
||||
d.downTrackLock.Lock()
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
receiver = r
|
||||
} else {
|
||||
receiver := d.getReceiver()
|
||||
if receiver == nil {
|
||||
d.onCodecStateChange = append(d.onCodecStateChange, f)
|
||||
}
|
||||
d.downTrackLock.Unlock()
|
||||
@@ -463,12 +464,34 @@ func (d *DummyReceiver) AddOnCodecStateChange(f func(codec webrtc.RTPCodecParame
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) CodecState() sfu.ReceiverCodecState {
|
||||
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.CodecState()
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.CodecState()
|
||||
}
|
||||
return sfu.ReceiverCodecStateNormal
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) VideoSizes() []codec.VideoSize {
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
return receiver.VideoSizes()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) Restart(reason string) {
|
||||
if receiver := d.getReceiver(); receiver != nil {
|
||||
receiver.Restart(reason)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DummyReceiver) getReceiver() sfu.TrackReceiver {
|
||||
if receiver, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
|
||||
return receiver
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
|
||||
type DummyRedReceiver struct {
|
||||
@@ -520,7 +543,7 @@ func (d *DummyRedReceiver) GetDownTracks() []sfu.TrackSender {
|
||||
if r, ok := d.redReceiver.Load().(sfu.TrackReceiver); ok {
|
||||
return r.GetDownTracks()
|
||||
}
|
||||
return maps.Values(d.downTracks)
|
||||
return slices.Collect(maps.Values(d.downTracks))
|
||||
}
|
||||
|
||||
func (d *DummyRedReceiver) ReadRTP(buf []byte, layer uint8, esn uint64) (int, error) {
|
||||
|
||||
Reference in New Issue
Block a user