Merge commit '6bd7fac875d9e9009915053d8a590abb372c5679' into feature/livekit-upgrade-1.13.1

This commit is contained in:
2026-06-25 23:54:33 +09:00
291 changed files with 37631 additions and 15381 deletions
+30 -10
View File
@@ -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])
}
+70 -29
View File
@@ -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{
+109
View File
@@ -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
}
+190
View File
@@ -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())
}
+301
View File
@@ -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
}
+110
View File
@@ -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))
}
@@ -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)
}
@@ -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) {}
+22 -20
View File
@@ -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
-3
View File
@@ -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")
+117 -122
View File
@@ -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
}
+1 -1
View File
@@ -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"
)
+348 -122
View File
@@ -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)
}
}
+97 -74
View File
@@ -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))
})
}
+327 -94
View File
@@ -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
}
+36 -136
View File
@@ -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)
}
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)
+268 -134
View File
@@ -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
+124 -155
View File
@@ -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,
}))
}
File diff suppressed because it is too large Load Diff
+82 -57
View File
@@ -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)
+153 -15
View File
@@ -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))
}
-159
View File
@@ -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
}
+210 -24
View File
@@ -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,
})
+12 -16
View File
@@ -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
}
File diff suppressed because it is too large Load Diff
+16 -6
View File
@@ -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
+209 -56
View File
@@ -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)
}
}
+398 -179
View File
@@ -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
View File
@@ -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")
}
+361 -48
View File
@@ -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
-132
View File
@@ -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
}
+30 -17
View File
@@ -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(),
+33 -48
View File
@@ -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()
}
+30
View File
@@ -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)
}
+137 -114
View File
@@ -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) {