From 35ac5f561aa86c01f7ca521197b03feec6842dc6 Mon Sep 17 00:00:00 2001 From: Raja Subramanian Date: Wed, 9 Apr 2025 00:20:58 +0530 Subject: [PATCH] Add support for WHIP ICE Trickle/Restart. (#3596) * Add support for WHIP ICE Trickle/Restart. Tested a bit using the WHIP client at https://github.com/Eyevinn/whip, but needs a lot more testing. ICERestart is not tested yet. * comment * clean up --- go.mod | 8 +- go.sum | 16 +- pkg/rtc/participant.go | 6 +- pkg/rtc/transport.go | 191 ++++++++++++++- pkg/rtc/transportmanager.go | 12 + pkg/rtc/types/interfaces.go | 3 + .../typesfakes/fake_local_participant.go | 223 ++++++++++++++++++ 7 files changed, 440 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index be61439bf..2cdf9a258 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/mediatransportutil v0.0.0-20250310153736-45596af895b6 - github.com/livekit/protocol v1.36.2-0.20250408143132-c193b8d080da + github.com/livekit/protocol v1.36.2-0.20250408183714-0975d348643e github.com/livekit/psrpc v0.6.1-0.20250205181828-a0beed2e4126 github.com/mackerelio/go-osstat v0.2.5 github.com/magefile/mage v1.15.0 @@ -55,7 +55,7 @@ require ( go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/mod v0.24.0 golang.org/x/sync v0.13.0 google.golang.org/protobuf v1.36.6 @@ -133,10 +133,10 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.38.0 // indirect + golang.org/x/net v0.39.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.31.0 // indirect + golang.org/x/tools v0.32.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250407143221-ac9807e6c755 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250407143221-ac9807e6c755 // indirect google.golang.org/grpc v1.71.1 // indirect diff --git a/go.sum b/go.sum index 46b213e6a..8bb4a4338 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20250310153736-45596af895b6 h1:6ZhtnY9I9knfm3ieIPpznQSEU2rDECO8yliW/ANLQ7U= github.com/livekit/mediatransportutil v0.0.0-20250310153736-45596af895b6/go.mod h1:36s+wwmU3O40IAhE+MjBWP3W71QRiEE9SfooSBvtBqY= -github.com/livekit/protocol v1.36.2-0.20250408143132-c193b8d080da h1:KZ2almsG7lNk0/4FhpLEuX5JOq5QVh8eCVhfsf4821M= -github.com/livekit/protocol v1.36.2-0.20250408143132-c193b8d080da/go.mod h1:WrT/CYRxtMNOVUjnIPm5OjWtEkmreffTeE1PRZwlRg4= +github.com/livekit/protocol v1.36.2-0.20250408183714-0975d348643e h1:uCdTqLPDVdJeJdXkSx7hlfUlY1MBmfBo8fOk2TF28cU= +github.com/livekit/protocol v1.36.2-0.20250408183714-0975d348643e/go.mod h1:WrT/CYRxtMNOVUjnIPm5OjWtEkmreffTeE1PRZwlRg4= github.com/livekit/psrpc v0.6.1-0.20250205181828-a0beed2e4126 h1:fzuYpAQbCid7ySPpQWWePfQOWUrs8x6dJ0T3Wl07n+Y= github.com/livekit/psrpc v0.6.1-0.20250205181828-a0beed2e4126/go.mod h1:X5WtEZ7OnEs72Fi5/J+i0on3964F1aynQpCalcgMqRo= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= @@ -367,8 +367,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -399,8 +399,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -471,8 +471,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 2f3193b03..3af11474b 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -903,11 +903,7 @@ func (p *ParticipantImpl) HandleOffer(offer webrtc.SessionDescription) error { } offer = p.setCodecPreferencesForPublisher(offer) - err := p.TransportManager.HandleOffer(offer, shouldPend) - if p.params.UseOneShotSignallingMode { - p.updateState(livekit.ParticipantInfo_ACTIVE) - } - return err + return p.TransportManager.HandleOffer(offer, shouldPend) } func (p *ParticipantImpl) onPublisherAnswer(answer webrtc.SessionDescription) error { diff --git a/pkg/rtc/transport.go b/pkg/rtc/transport.go index ca56e2f56..0a5a7fda6 100644 --- a/pkg/rtc/transport.go +++ b/pkg/rtc/transport.go @@ -93,6 +93,11 @@ var ( ErrMidNotFound = errors.New("mid not found") ErrNotSynchronousPeerConnectionMode = errors.New("not using synchronous peer connection mode") ErrNoRemoteDescription = errors.New("no remote description") + ErrNoLocalDescription = errors.New("no local description") + ErrInvalidSDPFragment = errors.New("invalid sdp fragment") + ErrNoBundleMid = errors.New("could not get bundle mid") + ErrMidMismatch = errors.New("media mid does not match bundle mid") + ErrICECredentialMismatch = errors.New("ice credential mismatch") ) // ------------------------------------------------------------------------- @@ -871,7 +876,7 @@ func (t *PCTransport) isFullyEstablished() bool { t.lock.RLock() defer t.lock.RUnlock() - dataChannelReady := t.firstOfferNoDataChannel || (t.reliableDCOpened && t.lossyDCOpened) + dataChannelReady := t.params.UseOneShotSignallingMode || t.firstOfferNoDataChannel || (t.reliableDCOpened && t.lossyDCOpened) return dataChannelReady && !t.connectedAt.IsZero() } @@ -1317,6 +1322,188 @@ func (t *PCTransport) GetAnswer() (webrtc.SessionDescription, error) { return *cld, nil } +func (t *PCTransport) GetICESessionUfrag() (string, error) { + cld := t.pc.CurrentLocalDescription() + if cld == nil { + return "", ErrNoLocalDescription + } + + parsed, err := cld.Unmarshal() + if err != nil { + return "", err + } + + ufrag, _, err := lksdp.ExtractICECredential(parsed) + if err != nil { + return "", err + } + + return ufrag, nil +} + +// Handles SDP Fragment for ICE Trickle in WHIP +func (t *PCTransport) HandleICETrickleSDPFragment(sdpFragment string) error { + if !t.params.UseOneShotSignallingMode { + return ErrNotSynchronousPeerConnectionMode + } + + parsedFragment := &lksdp.SDPFragment{} + if err := parsedFragment.Unmarshal(sdpFragment); err != nil { + t.params.Logger.Warnw("could not parse SDP fragment", err, "sdpFragment", sdpFragment) + return ErrInvalidSDPFragment + } + + crd := t.pc.CurrentRemoteDescription() + if crd == nil { + t.params.Logger.Warnw("no remote description", nil) + return ErrNoRemoteDescription + } + + parsedRemote, err := crd.Unmarshal() + if err != nil { + t.params.Logger.Warnw("could not parse remote description", err, "offer", crd) + return err + } + + // check if BUNDLE mid matches the "mid" in the SDP fragment + bundleMid, found := lksdp.GetBundleMid(parsedRemote) + if !found { + return ErrNoBundleMid + } + + if parsedFragment.Mid() != bundleMid { + t.params.Logger.Warnw("incorrect mid", nil, "sdpFragment", sdpFragment) + return ErrMidMismatch + } + + fragmentICEUfrag, fragmentICEPwd, err := parsedFragment.ExtractICECredential() + if err != nil { + t.params.Logger.Warnw( + "could not get ICE crendential from fragment", err, + "sdpFragment", sdpFragment, + ) + return ErrInvalidSDPFragment + } + remoteICEUfrag, remoteICEPwd, err := lksdp.ExtractICECredential(parsedRemote) + if err != nil { + t.params.Logger.Warnw("could not get ICE crendential from remote description", err, "sdpFragment", sdpFragment, "remoteDescription", crd) + return err + } + if fragmentICEUfrag != "" && fragmentICEUfrag != remoteICEUfrag { + t.params.Logger.Warnw( + "ice ufrag mismatch", nil, + "remoteICEUfrag", remoteICEUfrag, + "fragmentICEUfrag", fragmentICEUfrag, + "sdpFragment", sdpFragment, + "remoteDescription", crd, + ) + return ErrICECredentialMismatch + } + if fragmentICEPwd != "" && fragmentICEPwd != remoteICEPwd { + t.params.Logger.Warnw( + "ice pwd mismatch", nil, + "remoteICEPwd", remoteICEPwd, + "fragmentICEPwd", fragmentICEPwd, + "sdpFragment", sdpFragment, + "remoteDescription", crd, + ) + return ErrICECredentialMismatch + } + + // add candidates from media description + for _, ic := range parsedFragment.Candidates() { + c, err := ice.UnmarshalCandidate(ic) + if err == nil { + t.connectionDetails.AddRemoteICECandidate(c, false, false, false) + } + + candidate := webrtc.ICECandidateInit{ + Candidate: ic, + } + if err := t.pc.AddICECandidate(candidate); err != nil { + t.params.Logger.Warnw("failed to add ICE candidate", err, "candidate", candidate) + } else { + t.params.Logger.Debugw("added ICE candidate", "candidate", candidate) + } + } + return nil +} + +// Handles SDP Fragment for ICE Restart in WHIP +func (t *PCTransport) HandleICERestartSDPFragment(sdpFragment string) (string, error) { + if !t.params.UseOneShotSignallingMode { + return "", ErrNotSynchronousPeerConnectionMode + } + + parsedFragment := &lksdp.SDPFragment{} + if err := parsedFragment.Unmarshal(sdpFragment); err != nil { + t.params.Logger.Warnw("could not parse SDP fragment", err, "sdpFragment", sdpFragment) + return "", ErrInvalidSDPFragment + } + + crd := t.pc.CurrentRemoteDescription() + if crd == nil { + t.params.Logger.Warnw("no remote description", nil) + return "", ErrNoRemoteDescription + } + + parsedRemote, err := crd.Unmarshal() + if err != nil { + t.params.Logger.Warnw("could not parse remote description", err, "offer", crd) + return "", err + } + + if err := parsedFragment.PatchICECredentialIntoSDP(parsedRemote); err != nil { + t.params.Logger.Warnw("could not patch SDP fragment into remote description", err, "offer", crd, "sdpFragment", sdpFragment) + return "", err + } + + bytes, err := parsedRemote.Marshal() + if err != nil { + t.params.Logger.Warnw("could not marshal SDP with patched remote", err) + return "", err + } + sd := webrtc.SessionDescription{ + SDP: string(bytes), + Type: webrtc.SDPTypeOffer, + } + if err := t.pc.SetRemoteDescription(sd); err != nil { + t.params.Logger.Warnw("could not set remote description", err) + return "", err + } + + ans, err := t.pc.CreateAnswer(nil) + if err != nil { + t.params.Logger.Warnw("could not create answer", err) + return "", err + } + + if err = t.pc.SetLocalDescription(ans); err != nil { + t.params.Logger.Warnw("could not set local description", err) + return "", err + } + + parsedAnswer, err := ans.Unmarshal() + if err != nil { + t.params.Logger.Warnw("could not parse local description", err) + return "", err + } + + parsedFragmentAnswer, err := lksdp.ExtractSDPFragment(parsedAnswer) + if err != nil { + t.params.Logger.Warnw("could not extract SDP fragment", err) + return "", err + } + + answerFragment, err := parsedFragmentAnswer.Marshal() + if err != nil { + t.params.Logger.Warnw("could not marshal answer SDP fragment", err) + return "", err + } + + return answerFragment, nil +} + func (t *PCTransport) OnNegotiationStateChanged(f func(state transport.NegotiationState)) { t.lock.Lock() t.onNegotiationStateChanged = f @@ -1752,7 +1939,7 @@ func (t *PCTransport) handleRemoteICECandidate(e event) error { } if err := t.pc.AddICECandidate(*c); err != nil { - t.params.Logger.Warnw("failed to add cached ICE candidate", err, "candidate", c) + t.params.Logger.Warnw("failed to add ICE candidate", err, "candidate", c) return errors.Wrap(err, "add ice candidate failed") } else { t.params.Logger.Debugw("added ICE candidate", "candidate", c) diff --git a/pkg/rtc/transportmanager.go b/pkg/rtc/transportmanager.go index 671ed6fbf..68cc05b60 100644 --- a/pkg/rtc/transportmanager.go +++ b/pkg/rtc/transportmanager.go @@ -462,6 +462,18 @@ func (t *TransportManager) GetAnswer() (webrtc.SessionDescription, error) { return answer, err } +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 diff --git a/pkg/rtc/types/interfaces.go b/pkg/rtc/types/interfaces.go index 7783c066f..c94c343ee 100644 --- a/pkg/rtc/types/interfaces.go +++ b/pkg/rtc/types/interfaces.go @@ -340,6 +340,7 @@ type LocalParticipant interface { GetICEConnectionInfo() []*ICEConnectionInfo HasConnected() bool GetEnabledPublishCodecs() []*livekit.Codec + GetPublisherICESessionUfrag() (string, error) SetResponseSink(sink routing.MessageSink) CloseSignalConnection(reason SignallingCloseReason) @@ -367,6 +368,8 @@ type LocalParticipant interface { AddICECandidate(candidate webrtc.ICECandidateInit, target livekit.SignalTarget) HandleOffer(sdp webrtc.SessionDescription) error GetAnswer() (webrtc.SessionDescription, error) + HandleICETrickleSDPFragment(sdpFragment string) error + HandleICERestartSDPFragment(sdpFragment string) (string, error) AddTrack(req *livekit.AddTrackRequest) SetTrackMuted(trackID livekit.TrackID, muted bool, fromAdmin bool) *livekit.TrackInfo diff --git a/pkg/rtc/types/typesfakes/fake_local_participant.go b/pkg/rtc/types/typesfakes/fake_local_participant.go index 55c675f00..81e860387 100644 --- a/pkg/rtc/types/typesfakes/fake_local_participant.go +++ b/pkg/rtc/types/typesfakes/fake_local_participant.go @@ -390,6 +390,18 @@ type FakeLocalParticipant struct { getPublishedTracksReturnsOnCall map[int]struct { result1 []types.MediaTrack } + GetPublisherICESessionUfragStub func() (string, error) + getPublisherICESessionUfragMutex sync.RWMutex + getPublisherICESessionUfragArgsForCall []struct { + } + getPublisherICESessionUfragReturns struct { + result1 string + result2 error + } + getPublisherICESessionUfragReturnsOnCall map[int]struct { + result1 string + result2 error + } GetSubscribedParticipantsStub func() []livekit.ParticipantID getSubscribedParticipantsMutex sync.RWMutex getSubscribedParticipantsArgsForCall []struct { @@ -425,6 +437,30 @@ type FakeLocalParticipant struct { handleAnswerArgsForCall []struct { arg1 webrtc.SessionDescription } + HandleICERestartSDPFragmentStub func(string) (string, error) + handleICERestartSDPFragmentMutex sync.RWMutex + handleICERestartSDPFragmentArgsForCall []struct { + arg1 string + } + handleICERestartSDPFragmentReturns struct { + result1 string + result2 error + } + handleICERestartSDPFragmentReturnsOnCall map[int]struct { + result1 string + result2 error + } + HandleICETrickleSDPFragmentStub func(string) error + handleICETrickleSDPFragmentMutex sync.RWMutex + handleICETrickleSDPFragmentArgsForCall []struct { + arg1 string + } + handleICETrickleSDPFragmentReturns struct { + result1 error + } + handleICETrickleSDPFragmentReturnsOnCall map[int]struct { + result1 error + } HandleMetricsStub func(livekit.ParticipantID, *livekit.MetricsBatch) error handleMetricsMutex sync.RWMutex handleMetricsArgsForCall []struct { @@ -3124,6 +3160,62 @@ func (fake *FakeLocalParticipant) GetPublishedTracksReturnsOnCall(i int, result1 }{result1} } +func (fake *FakeLocalParticipant) GetPublisherICESessionUfrag() (string, error) { + fake.getPublisherICESessionUfragMutex.Lock() + ret, specificReturn := fake.getPublisherICESessionUfragReturnsOnCall[len(fake.getPublisherICESessionUfragArgsForCall)] + fake.getPublisherICESessionUfragArgsForCall = append(fake.getPublisherICESessionUfragArgsForCall, struct { + }{}) + stub := fake.GetPublisherICESessionUfragStub + fakeReturns := fake.getPublisherICESessionUfragReturns + fake.recordInvocation("GetPublisherICESessionUfrag", []interface{}{}) + fake.getPublisherICESessionUfragMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeLocalParticipant) GetPublisherICESessionUfragCallCount() int { + fake.getPublisherICESessionUfragMutex.RLock() + defer fake.getPublisherICESessionUfragMutex.RUnlock() + return len(fake.getPublisherICESessionUfragArgsForCall) +} + +func (fake *FakeLocalParticipant) GetPublisherICESessionUfragCalls(stub func() (string, error)) { + fake.getPublisherICESessionUfragMutex.Lock() + defer fake.getPublisherICESessionUfragMutex.Unlock() + fake.GetPublisherICESessionUfragStub = stub +} + +func (fake *FakeLocalParticipant) GetPublisherICESessionUfragReturns(result1 string, result2 error) { + fake.getPublisherICESessionUfragMutex.Lock() + defer fake.getPublisherICESessionUfragMutex.Unlock() + fake.GetPublisherICESessionUfragStub = nil + fake.getPublisherICESessionUfragReturns = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeLocalParticipant) GetPublisherICESessionUfragReturnsOnCall(i int, result1 string, result2 error) { + fake.getPublisherICESessionUfragMutex.Lock() + defer fake.getPublisherICESessionUfragMutex.Unlock() + fake.GetPublisherICESessionUfragStub = nil + if fake.getPublisherICESessionUfragReturnsOnCall == nil { + fake.getPublisherICESessionUfragReturnsOnCall = make(map[int]struct { + result1 string + result2 error + }) + } + fake.getPublisherICESessionUfragReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} +} + func (fake *FakeLocalParticipant) GetSubscribedParticipants() []livekit.ParticipantID { fake.getSubscribedParticipantsMutex.Lock() ret, specificReturn := fake.getSubscribedParticipantsReturnsOnCall[len(fake.getSubscribedParticipantsArgsForCall)] @@ -3315,6 +3407,131 @@ func (fake *FakeLocalParticipant) HandleAnswerArgsForCall(i int) webrtc.SessionD return argsForCall.arg1 } +func (fake *FakeLocalParticipant) HandleICERestartSDPFragment(arg1 string) (string, error) { + fake.handleICERestartSDPFragmentMutex.Lock() + ret, specificReturn := fake.handleICERestartSDPFragmentReturnsOnCall[len(fake.handleICERestartSDPFragmentArgsForCall)] + fake.handleICERestartSDPFragmentArgsForCall = append(fake.handleICERestartSDPFragmentArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.HandleICERestartSDPFragmentStub + fakeReturns := fake.handleICERestartSDPFragmentReturns + fake.recordInvocation("HandleICERestartSDPFragment", []interface{}{arg1}) + fake.handleICERestartSDPFragmentMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeLocalParticipant) HandleICERestartSDPFragmentCallCount() int { + fake.handleICERestartSDPFragmentMutex.RLock() + defer fake.handleICERestartSDPFragmentMutex.RUnlock() + return len(fake.handleICERestartSDPFragmentArgsForCall) +} + +func (fake *FakeLocalParticipant) HandleICERestartSDPFragmentCalls(stub func(string) (string, error)) { + fake.handleICERestartSDPFragmentMutex.Lock() + defer fake.handleICERestartSDPFragmentMutex.Unlock() + fake.HandleICERestartSDPFragmentStub = stub +} + +func (fake *FakeLocalParticipant) HandleICERestartSDPFragmentArgsForCall(i int) string { + fake.handleICERestartSDPFragmentMutex.RLock() + defer fake.handleICERestartSDPFragmentMutex.RUnlock() + argsForCall := fake.handleICERestartSDPFragmentArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeLocalParticipant) HandleICERestartSDPFragmentReturns(result1 string, result2 error) { + fake.handleICERestartSDPFragmentMutex.Lock() + defer fake.handleICERestartSDPFragmentMutex.Unlock() + fake.HandleICERestartSDPFragmentStub = nil + fake.handleICERestartSDPFragmentReturns = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeLocalParticipant) HandleICERestartSDPFragmentReturnsOnCall(i int, result1 string, result2 error) { + fake.handleICERestartSDPFragmentMutex.Lock() + defer fake.handleICERestartSDPFragmentMutex.Unlock() + fake.HandleICERestartSDPFragmentStub = nil + if fake.handleICERestartSDPFragmentReturnsOnCall == nil { + fake.handleICERestartSDPFragmentReturnsOnCall = make(map[int]struct { + result1 string + result2 error + }) + } + fake.handleICERestartSDPFragmentReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragment(arg1 string) error { + fake.handleICETrickleSDPFragmentMutex.Lock() + ret, specificReturn := fake.handleICETrickleSDPFragmentReturnsOnCall[len(fake.handleICETrickleSDPFragmentArgsForCall)] + fake.handleICETrickleSDPFragmentArgsForCall = append(fake.handleICETrickleSDPFragmentArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.HandleICETrickleSDPFragmentStub + fakeReturns := fake.handleICETrickleSDPFragmentReturns + fake.recordInvocation("HandleICETrickleSDPFragment", []interface{}{arg1}) + fake.handleICETrickleSDPFragmentMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragmentCallCount() int { + fake.handleICETrickleSDPFragmentMutex.RLock() + defer fake.handleICETrickleSDPFragmentMutex.RUnlock() + return len(fake.handleICETrickleSDPFragmentArgsForCall) +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragmentCalls(stub func(string) error) { + fake.handleICETrickleSDPFragmentMutex.Lock() + defer fake.handleICETrickleSDPFragmentMutex.Unlock() + fake.HandleICETrickleSDPFragmentStub = stub +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragmentArgsForCall(i int) string { + fake.handleICETrickleSDPFragmentMutex.RLock() + defer fake.handleICETrickleSDPFragmentMutex.RUnlock() + argsForCall := fake.handleICETrickleSDPFragmentArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragmentReturns(result1 error) { + fake.handleICETrickleSDPFragmentMutex.Lock() + defer fake.handleICETrickleSDPFragmentMutex.Unlock() + fake.HandleICETrickleSDPFragmentStub = nil + fake.handleICETrickleSDPFragmentReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeLocalParticipant) HandleICETrickleSDPFragmentReturnsOnCall(i int, result1 error) { + fake.handleICETrickleSDPFragmentMutex.Lock() + defer fake.handleICETrickleSDPFragmentMutex.Unlock() + fake.HandleICETrickleSDPFragmentStub = nil + if fake.handleICETrickleSDPFragmentReturnsOnCall == nil { + fake.handleICETrickleSDPFragmentReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.handleICETrickleSDPFragmentReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeLocalParticipant) HandleMetrics(arg1 livekit.ParticipantID, arg2 *livekit.MetricsBatch) error { fake.handleMetricsMutex.Lock() ret, specificReturn := fake.handleMetricsReturnsOnCall[len(fake.handleMetricsArgsForCall)] @@ -7702,6 +7919,8 @@ func (fake *FakeLocalParticipant) Invocations() map[string][][]interface{} { defer fake.getPublishedTrackMutex.RUnlock() fake.getPublishedTracksMutex.RLock() defer fake.getPublishedTracksMutex.RUnlock() + fake.getPublisherICESessionUfragMutex.RLock() + defer fake.getPublisherICESessionUfragMutex.RUnlock() fake.getSubscribedParticipantsMutex.RLock() defer fake.getSubscribedParticipantsMutex.RUnlock() fake.getSubscribedTracksMutex.RLock() @@ -7710,6 +7929,10 @@ func (fake *FakeLocalParticipant) Invocations() map[string][][]interface{} { defer fake.getTrailerMutex.RUnlock() fake.handleAnswerMutex.RLock() defer fake.handleAnswerMutex.RUnlock() + fake.handleICERestartSDPFragmentMutex.RLock() + defer fake.handleICERestartSDPFragmentMutex.RUnlock() + fake.handleICETrickleSDPFragmentMutex.RLock() + defer fake.handleICETrickleSDPFragmentMutex.RUnlock() fake.handleMetricsMutex.RLock() defer fake.handleMetricsMutex.RUnlock() fake.handleOfferMutex.RLock()