diff --git a/cmd/cli/client/client.go b/cmd/cli/client/client.go index fb13d50a1..cf9f9ead6 100644 --- a/cmd/cli/client/client.go +++ b/cmd/cli/client/client.go @@ -27,7 +27,6 @@ import ( type RTCClient struct { id string - identity string conn *websocket.Conn PeerConn *webrtc.PeerConnection // sid => track @@ -128,7 +127,8 @@ func NewRTCClient(conn *websocket.Conn) (*RTCClient, error) { }) peerConn.OnTrack(func(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver) { - logger.Debugw("track received", "label", track.StreamID(), "id", track.ID()) + logger.Debugw("track received", "label", track.StreamID(), "id", track.ID(), + "participant", c.localParticipant.Identity) go c.processTrack(track) }) @@ -144,7 +144,7 @@ func NewRTCClient(conn *websocket.Conn) (*RTCClient, error) { peerConn.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { logger.Debugw("ICE state has changed", "state", connectionState.String(), - "participant", c.localParticipant.Sid) + "participant", c.localParticipant.Identity) if connectionState == webrtc.ICEConnectionStateConnected { // flush peers c.lock.Lock() @@ -195,8 +195,8 @@ func (c *RTCClient) Run() error { } switch msg := res.Message.(type) { case *livekit.SignalResponse_Join: + c.localParticipant = msg.Join.Participant c.id = msg.Join.Participant.Sid - c.identity = msg.Join.Participant.Identity c.lock.Lock() for _, p := range msg.Join.OtherParticipants { @@ -205,7 +205,6 @@ func (c *RTCClient) Run() error { c.lock.Unlock() logger.Debugw("join accepted, sending offer..", "participant", msg.Join.Participant.Identity) - c.localParticipant = msg.Join.Participant logger.Debugw("other participants", "count", len(msg.Join.OtherParticipants)) // Create an offer to send to the other process @@ -214,7 +213,7 @@ func (c *RTCClient) Run() error { return err } - logger.Debugw("created offer", "offer", offer.SDP) + logger.Debugw("created offer", "participant", c.localParticipant.Identity) // Sets the LocalDescription, and starts our UDP listeners // Note: this will start the gathering of ICE candidates @@ -228,7 +227,6 @@ func (c *RTCClient) Run() error { Offer: rtc.ToProtoSessionDescription(offer), }, } - logger.Debugw("connecting to remote...") if err = c.SendRequest(req); err != nil { return err } @@ -254,7 +252,6 @@ func (c *RTCClient) Run() error { c.lock.Lock() for _, p := range msg.Update.Participants { c.remoteParticipants[p.Sid] = p - logger.Debugw("participant update", "id", p.Identity, "state", p.State.String()) } c.lock.Unlock() case *livekit.SignalResponse_TrackPublished: @@ -277,7 +274,11 @@ func (c *RTCClient) WaitUntilConnected() error { for { select { case <-ctx.Done(): - return errors.New("could not connect after timeout") + id := c.ID() + if c.localParticipant != nil { + id = c.localParticipant.Identity + } + return fmt.Errorf("%s could not connect after timeout", id) case <-time.After(10 * time.Millisecond): if c.iceConnected.Get() { return nil @@ -476,7 +477,7 @@ func (c *RTCClient) handleOffer(desc webrtc.SessionDescription) error { } func (c *RTCClient) handleAnswer(desc webrtc.SessionDescription) error { - logger.Debugw("handling server answer") + logger.Debugw("handling server answer", "participant", c.localParticipant.Identity) // remote answered the offer, establish connection err := c.PeerConn.SetRemoteDescription(desc) if err != nil { @@ -499,7 +500,7 @@ func (c *RTCClient) handleAnswer(desc webrtc.SessionDescription) error { } func (c *RTCClient) requestNegotiation() error { - logger.Debugw("requesting negotiation") + logger.Debugw("requesting negotiation", "participant", c.localParticipant.Identity) return c.SendRequest(&livekit.SignalRequest{ Message: &livekit.SignalRequest_Negotiate{ Negotiate: &livekit.NegotiationRequest{}, @@ -508,7 +509,7 @@ func (c *RTCClient) requestNegotiation() error { } func (c *RTCClient) negotiate() error { - logger.Debugw("starting negotiation") + logger.Debugw("starting negotiation", "participant", c.localParticipant.Identity) offer, err := c.PeerConn.CreateOffer(nil) if err != nil { return err diff --git a/go.mod b/go.mod index da8d2ee76..cf89ca0b2 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.15 require ( github.com/bep/debounce v1.2.0 - github.com/gammazero/workerpool v1.1.1 github.com/go-redis/redis/v8 v8.4.8 github.com/golang/protobuf v1.4.3 github.com/google/wire v0.4.0 @@ -15,13 +14,12 @@ require ( github.com/magefile/mage v1.10.0 github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/pion/ion-log v1.0.0 - github.com/pion/ion-sfu v1.8.1 + github.com/pion/ion-sfu v1.8.3 github.com/pion/rtcp v1.2.6 github.com/pion/rtp v1.6.2 + github.com/pion/sdp/v3 v3.0.4 github.com/pion/stun v0.3.5 - github.com/pion/transport v0.12.2 - github.com/pion/webrtc/v3 v3.0.4 + github.com/pion/webrtc/v3 v3.0.5 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 github.com/thoas/go-funk v0.7.0 @@ -35,3 +33,5 @@ require ( gopkg.in/square/go-jose.v2 v2.5.1 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) + +replace github.com/pion/ion-sfu => github.com/pion/ion-sfu v1.8.4-0.20210205220645-c9ec7ad1db86 diff --git a/go.sum b/go.sum index 8ec9360dd..6692af481 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,7 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo= github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= @@ -70,6 +71,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidzhao/ion-sfu v1.8.3-0.20210205220645-c9ec7ad1db86 h1:POGjEQ8fyEQn96DMXMG7xhvWxHjNRMQ5aoiRzQOzOdY= +github.com/davidzhao/ion-sfu v1.8.3-0.20210205220645-c9ec7ad1db86/go.mod h1:MKDh4JSLpBA/hFeYAYALb6nYpe6Ruknd3pmxPMbdpM8= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -158,6 +161,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -233,6 +238,7 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/shortuuid/v3 v3.0.4 h1:uj4xhotfY92Y1Oa6n6HUiFn87CdoEHYUlTy0+IgbLrs= github.com/lithammer/shortuuid/v3 v3.0.4/go.mod h1:RviRjexKqIzx/7r1peoAITm6m7gnif/h+0zmolKJjzw= +github.com/lucsky/cuid v1.0.2 h1:z4XlExeoderxoPj2/dxKOyPxe9RCOu7yNq9/XWxIUMQ= github.com/lucsky/cuid v1.0.2/go.mod h1:QaaJqckboimOmhRSJXSx/+IT+VTfxfPGSo/6mfgUfmE= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/lytics/base62 v0.0.0-20180808010106-0ee4de5a5d6d h1:HONlVR8gkr5QibYNVFtj8ajfqRAKt21DmdzeZXZl6VQ= @@ -247,6 +253,7 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 h1:8E6DrFvII6QR4eJ3PkFvV+lc03P+2qwqTPLm1ax7694= github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0/go.mod h1:fcEyUyXZXoV4Abw8DX0t7wyL8mCDxXyU4iAFZfT3IHw= @@ -316,12 +323,16 @@ github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI= github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI= github.com/pion/ice/v2 v2.0.14 h1:FxXxauyykf89SWAtkQCfnHkno6G8+bhRkNguSh9zU+4= github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM= +github.com/pion/ice/v2 v2.0.15 h1:KZrwa2ciL9od8+TUVJiYTNsCW9J5lktBjGwW1MacEnQ= +github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI= github.com/pion/interceptor v0.0.9 h1:fk5hTdyLO3KURQsf/+RjMpEm4NE3yeTY9Kh97b5BvwA= github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c= github.com/pion/ion-log v1.0.0 h1:2lJLImCmfCWCR38hLWsjQfBWe6NFz/htbqiYHwvOP/Q= github.com/pion/ion-log v1.0.0/go.mod h1:jwcla9KoB9bB/4FxYDSRJPcPYSLp5XiUUMnOLaqwl4E= -github.com/pion/ion-sfu v1.8.1 h1:N3xaDZ2Om2pCzPLlusPmV7BJYVedE5VEx8um6d0Crns= -github.com/pion/ion-sfu v1.8.1/go.mod h1:b6FI2WFTBsifw9EhxvfDks5/9SGYmVvm00IogZEgaik= +github.com/pion/ion-sfu v1.8.3 h1:ZPmNgBRvrrYY5POjRYglCrradFMw/SBLPh0WXJYPm8E= +github.com/pion/ion-sfu v1.8.3/go.mod h1:MKDh4JSLpBA/hFeYAYALb6nYpe6Ruknd3pmxPMbdpM8= +github.com/pion/ion-sfu v1.8.4-0.20210205220645-c9ec7ad1db86 h1:HfWQIv0mJ5MYbKLaWlQ3nG/ToRTE96dpBmts97nra5o= +github.com/pion/ion-sfu v1.8.4-0.20210205220645-c9ec7ad1db86/go.mod h1:MKDh4JSLpBA/hFeYAYALb6nYpe6Ruknd3pmxPMbdpM8= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY= @@ -347,6 +358,7 @@ github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= github.com/pion/transport v0.12.0 h1:UFmOBBZkTZ3LgvLRf/NGrfWdZEubcU6zkLU3PsA9YvU= github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys= github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA= @@ -355,6 +367,8 @@ github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI= github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= github.com/pion/webrtc/v3 v3.0.4 h1:Tiw3H9fpfcwkvaxonB+Gv1DG9tmgYBQaM1vBagDHP40= github.com/pion/webrtc/v3 v3.0.4/go.mod h1:1TmFSLpPYFTFXFHPtoq9eGP1ASTa9LC6FBh7sUY8cd4= +github.com/pion/webrtc/v3 v3.0.5 h1:utennp7RwX+2mtyMzoOXE03IUIckiHBigjarRJZ2DqY= +github.com/pion/webrtc/v3 v3.0.5/go.mod h1:/EDCREM8y+JrJSkoCRHpoz//qtuBCOYV4E96vEK3bz0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -369,12 +383,14 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -390,6 +406,7 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -548,6 +565,8 @@ golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/pkg/config/config.go b/pkg/config/config.go index b14900538..7a531d287 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,7 +25,7 @@ type RTCConfig struct { StunServers []string `yaml:"stun_servers"` UseExternalIP bool `yaml:"use_external_ip"` - MaxBitrate uint64 `yaml:"max_bandwidth"` + MaxBitrate uint64 `yaml:"max_bitrate"` MaxBufferTime int `yaml:"max_buffer_time"` } @@ -44,6 +44,7 @@ func NewConfig(confString string) (*Config, error) { StunServers: []string{ "stun.l.google.com:19302", }, + MaxBitrate: 2 * 1024 * 1024, // 3 mbps }, Redis: RedisConfig{}, Keys: map[string]string{}, diff --git a/pkg/routing/interfaces.go b/pkg/routing/interfaces.go index 6d15cc0e9..04640d111 100644 --- a/pkg/routing/interfaces.go +++ b/pkg/routing/interfaces.go @@ -33,6 +33,7 @@ type Router interface { ClearRoomState(roomName string) error RegisterNode() error UnregisterNode() error + RemoveDeadNodes() error GetNode(nodeId string) (*livekit.Node, error) ListNodes() ([]*livekit.Node, error) diff --git a/pkg/routing/localrouter.go b/pkg/routing/localrouter.go index 62ffd9a28..476fa792d 100644 --- a/pkg/routing/localrouter.go +++ b/pkg/routing/localrouter.go @@ -46,6 +46,10 @@ func (r *LocalRouter) UnregisterNode() error { return nil } +func (r *LocalRouter) RemoveDeadNodes() error { + return nil +} + func (r *LocalRouter) GetNode(nodeId string) (*livekit.Node, error) { if nodeId == r.currentNode.Id { return r.currentNode, nil diff --git a/pkg/routing/redis.go b/pkg/routing/redis.go index 069a8a292..1025149a0 100644 --- a/pkg/routing/redis.go +++ b/pkg/routing/redis.go @@ -58,6 +58,9 @@ func publishRTCMessage(rc *redis.Client, nodeId string, participantKey string, m if err != nil { return err } + + //logger.Debugw("publishing to", "rtcChannel", rtcNodeChannel(nodeId), + // "message", rm.Message) return rc.Publish(redisCtx, rtcNodeChannel(nodeId), data).Err() } diff --git a/pkg/routing/redisrouter.go b/pkg/routing/redisrouter.go index 3c9d78291..5cba088e7 100644 --- a/pkg/routing/redisrouter.go +++ b/pkg/routing/redisrouter.go @@ -2,7 +2,6 @@ package routing import ( "context" - "sync" "time" "github.com/go-redis/redis/v8" @@ -17,7 +16,7 @@ import ( const ( // expire participant mappings after a day participantMappingTTL = 24 * time.Hour - statsUpdateInterval = 10 * time.Second + statsUpdateInterval = 2 * time.Second ) // RedisRouter uses Redis pub/sub to route signaling messages across different nodes @@ -25,28 +24,27 @@ const ( // Because type RedisRouter struct { LocalRouter - rc *redis.Client - cr *utils.CachedRedis - ctx context.Context - once sync.Once + rc *redis.Client + ctx context.Context + isStarted utils.AtomicFlag // map of participantKey => RTCNodeSink rtcSinks map[string]*RTCNodeSink // map of connectionId => SignalNodeSink signalSinks map[string]*SignalNodeSink - cancel func() + + pubsub *redis.PubSub + cancel func() } func NewRedisRouter(currentNode LocalNode, rc *redis.Client) *RedisRouter { rr := &RedisRouter{ LocalRouter: *NewLocalRouter(currentNode), rc: rc, - once: sync.Once{}, rtcSinks: make(map[string]*RTCNodeSink), signalSinks: make(map[string]*SignalNodeSink), } rr.ctx, rr.cancel = context.WithCancel(context.Background()) - rr.cr = utils.NewCachedRedis(rr.ctx, rr.rc) return rr } @@ -55,7 +53,6 @@ func (r *RedisRouter) RegisterNode() error { if err != nil { return err } - r.cr.ExpireHash(NodesKey, r.currentNode.Id) if err := r.rc.HSet(r.ctx, NodesKey, r.currentNode.Id, data).Err(); err != nil { return errors.Wrap(err, "could not register node") } @@ -67,8 +64,23 @@ func (r *RedisRouter) UnregisterNode() error { return r.rc.HDel(context.Background(), NodesKey, r.currentNode.Id).Err() } +func (r *RedisRouter) RemoveDeadNodes() error { + nodes, err := r.ListNodes() + if err != nil { + return err + } + for _, n := range nodes { + if !IsAvailable(n) { + if err := r.rc.HDel(context.Background(), NodesKey, n.Id).Err(); err != nil { + return err + } + } + } + return nil +} + func (r *RedisRouter) GetNodeForRoom(roomName string) (string, error) { - val, err := r.cr.CachedHGet(NodeRoomKey, roomName) + val, err := r.rc.HGet(r.ctx, NodeRoomKey, roomName).Result() if err != nil { err = errors.Wrap(err, "could not get node for room") } @@ -87,7 +99,7 @@ func (r *RedisRouter) ClearRoomState(roomName string) error { } func (r *RedisRouter) GetNode(nodeId string) (*livekit.Node, error) { - data, err := r.cr.CachedHGet(NodesKey, nodeId) + data, err := r.rc.HGet(r.ctx, NodesKey, nodeId).Result() if err != nil { return nil, err } @@ -203,20 +215,24 @@ func (r *RedisRouter) startParticipantRTC(ss *livekit.StartSession, participantK } func (r *RedisRouter) Start() error { - r.once.Do(func() { - go r.statsWorker() - go r.rtcWorker() - go r.signalWorker() - }) + if !r.isStarted.TrySet(true) { + return nil + } + go r.statsWorker() + go r.redisWorker() return nil } func (r *RedisRouter) Stop() { + if !r.isStarted.TrySet(false) { + return + } + logger.Debugw("stopping RedisRouter") + r.pubsub.Close() r.cancel() } func (r *RedisRouter) setParticipantRTCNode(participantKey, nodeId string) error { - r.cr.Expire(participantRTCKey(participantKey)) err := r.rc.Set(r.ctx, participantRTCKey(participantKey), nodeId, participantMappingTTL).Err() if err != nil { err = errors.Wrap(err, "could not set rtc node") @@ -225,7 +241,6 @@ func (r *RedisRouter) setParticipantRTCNode(participantKey, nodeId string) error } func (r *RedisRouter) setParticipantSignalNode(connectionId, nodeId string) error { - r.cr.Expire(participantSignalKey(connectionId)) if err := r.rc.Set(r.ctx, participantSignalKey(connectionId), nodeId, participantMappingTTL).Err(); err != nil { return errors.Wrap(err, "could not set signal node") } @@ -271,126 +286,106 @@ func (r *RedisRouter) getOrCreateSignalSink(nodeId string, connectionId string) } func (r *RedisRouter) getParticipantRTCNode(participantKey string) (string, error) { - return r.cr.CachedGet(participantRTCKey(participantKey)) + return r.rc.Get(r.ctx, participantRTCKey(participantKey)).Result() } func (r *RedisRouter) getParticipantSignalNode(connectionId string) (nodeId string, err error) { - return r.cr.CachedGet(participantSignalKey(connectionId)) + return r.rc.Get(r.ctx, participantSignalKey(connectionId)).Result() } // update node stats and cleanup func (r *RedisRouter) statsWorker() { for r.ctx.Err() == nil { - // update every 10 seconds - <-time.After(statsUpdateInterval) - r.currentNode.Stats.UpdatedAt = time.Now().Unix() - if err := r.RegisterNode(); err != nil { - logger.Errorw("could not update node", "error", err) + // update periodically seconds + select { + case <-time.After(statsUpdateInterval): + r.currentNode.Stats.UpdatedAt = time.Now().Unix() + if err := r.RegisterNode(); err != nil { + logger.Errorw("could not update node", "error", err) + } + case <-r.ctx.Done(): + return } } } -// worker that consumes signal channel and processes -func (r *RedisRouter) signalWorker() { - sub := r.rc.Subscribe(redisCtx, signalNodeChannel(r.currentNode.Id)) +// worker that consumes redis messages intended for this node +func (r *RedisRouter) redisWorker() { defer func() { - logger.Debugw("finishing redis signalWorker", "node", r.currentNode.Id) + logger.Debugw("finishing redisWorker", "node", r.currentNode.Id) }() - logger.Debugw("starting redis signalWorker", "node", r.currentNode.Id) - for r.ctx.Err() == nil { - obj, err := sub.Receive(r.ctx) - if err != nil { - logger.Warnw("error receiving redis message", "error", err) - // TODO: retry? ignore? at a minimum need to sleep here to retry - time.Sleep(time.Second) - continue - } - if obj == nil { + logger.Debugw("starting redisWorker", "node", r.currentNode.Id) + + sigChannel := signalNodeChannel(r.currentNode.Id) + rtcChannel := rtcNodeChannel(r.currentNode.Id) + r.pubsub = r.rc.Subscribe(r.ctx, sigChannel, rtcChannel) + for msg := range r.pubsub.Channel() { + if msg == nil { return } - msg, ok := obj.(*redis.Message) - if !ok { - continue - } - - rm := livekit.SignalNodeMessage{} - err = proto.Unmarshal([]byte(msg.Payload), &rm) - connectionId := rm.ConnectionId - - switch rmb := rm.Message.(type) { - - case *livekit.SignalNodeMessage_Response: - // in the event the current node is an Signal node, push to response channels - resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId) - err = resSink.WriteMessage(rmb.Response) - if err != nil { - logger.Errorw("could not write to response channel", - "connectionId", connectionId, - "error", err) - } - - case *livekit.SignalNodeMessage_EndSession: - //signalNode, err := r.getParticipantSignalNode(connectionId) - if err != nil { - logger.Errorw("could not get participant RTC node", - "error", err) + if msg.Channel == sigChannel { + sm := livekit.SignalNodeMessage{} + if err := proto.Unmarshal([]byte(msg.Payload), &sm); err != nil { + logger.Errorw("could not unmarshal signal message on sigchan", "error", err) continue } - // EndSession can only be initiated on an RTC node, is handled on the signal node - //if signalNode == r.currentNode.Id { - resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId) - resSink.Close() - //} - } - } -} - -// worker that consumes RTC channel and processes -func (r *RedisRouter) rtcWorker() { - sub := r.rc.Subscribe(redisCtx, rtcNodeChannel(r.currentNode.Id)) - - defer func() { - logger.Debugw("finishing redis rtcWorker", "node", r.currentNode.Id) - }() - logger.Debugw("starting redis rtcWorker", "node", r.currentNode.Id) - for r.ctx.Err() == nil { - obj, err := sub.Receive(r.ctx) - if err != nil { - logger.Warnw("error receiving redis message", "error", err) - // TODO: retry? ignore? at a minimum need to sleep here to retry - time.Sleep(time.Second) - continue - } - if obj == nil { - return - } - - msg, ok := obj.(*redis.Message) - if !ok { - continue - } - - rm := livekit.RTCNodeMessage{} - err = proto.Unmarshal([]byte(msg.Payload), &rm) - pKey := rm.ParticipantKey - - switch rmb := rm.Message.(type) { - case *livekit.RTCNodeMessage_StartSession: - // RTC session should start on this node - err = r.startParticipantRTC(rmb.StartSession, pKey) - if err != nil { - logger.Errorw("could not start participant", "error", err) + if err := r.handleSignalMessage(&sm); err != nil { + logger.Errorw("error processing signal message", "error", err) + continue } - - case *livekit.RTCNodeMessage_Request: - requestChan := r.getOrCreateMessageChannel(r.requestChannels, pKey) - err = requestChan.WriteMessage(rmb.Request) - if err != nil { - logger.Errorw("could not write to request channel", - "participant", pKey, - "error", err) + } else if msg.Channel == rtcChannel { + rm := livekit.RTCNodeMessage{} + if err := proto.Unmarshal([]byte(msg.Payload), &rm); err != nil { + logger.Errorw("could not unmarshal RTC message on rtcchan", "error", err) + continue + } + if err := r.handleRTCMessage(&rm); err != nil { + logger.Errorw("error processing RTC message", "error", err) + continue } } } } + +func (r *RedisRouter) handleSignalMessage(sm *livekit.SignalNodeMessage) error { + connectionId := sm.ConnectionId + + switch rmb := sm.Message.(type) { + case *livekit.SignalNodeMessage_Response: + //logger.Debugw("forwarding signal message", + // "connectionId", connectionId, + // "type", fmt.Sprintf("%T", rmb.Response.Message)) + // in the event the current node is an Signal node, push to response channels + resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId) + if err := resSink.WriteMessage(rmb.Response); err != nil { + return err + } + + case *livekit.SignalNodeMessage_EndSession: + logger.Debugw("received EndSession, closing signal connection", + "connectionId", connectionId) + resSink := r.getOrCreateMessageChannel(r.responseChannels, connectionId) + resSink.Close() + } + return nil +} + +func (r *RedisRouter) handleRTCMessage(rm *livekit.RTCNodeMessage) error { + pKey := rm.ParticipantKey + + switch rmb := rm.Message.(type) { + case *livekit.RTCNodeMessage_StartSession: + // RTC session should start on this node + if err := r.startParticipantRTC(rmb.StartSession, pKey); err != nil { + return errors.Wrap(err, "could not start participant") + } + + case *livekit.RTCNodeMessage_Request: + requestChan := r.getOrCreateMessageChannel(r.requestChannels, pKey) + if err := requestChan.WriteMessage(rmb.Request); err != nil { + return err + } + } + return nil +} diff --git a/pkg/routing/routingfakes/fake_router.go b/pkg/routing/routingfakes/fake_router.go index 882bc3d20..afd88bb5c 100644 --- a/pkg/routing/routingfakes/fake_router.go +++ b/pkg/routing/routingfakes/fake_router.go @@ -73,6 +73,16 @@ type FakeRouter struct { registerNodeReturnsOnCall map[int]struct { result1 error } + RemoveDeadNodesStub func() error + removeDeadNodesMutex sync.RWMutex + removeDeadNodesArgsForCall []struct { + } + removeDeadNodesReturns struct { + result1 error + } + removeDeadNodesReturnsOnCall map[int]struct { + result1 error + } SetNodeForRoomStub func(string, string) error setNodeForRoomMutex sync.RWMutex setNodeForRoomArgsForCall []struct { @@ -460,6 +470,59 @@ func (fake *FakeRouter) RegisterNodeReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeRouter) RemoveDeadNodes() error { + fake.removeDeadNodesMutex.Lock() + ret, specificReturn := fake.removeDeadNodesReturnsOnCall[len(fake.removeDeadNodesArgsForCall)] + fake.removeDeadNodesArgsForCall = append(fake.removeDeadNodesArgsForCall, struct { + }{}) + stub := fake.RemoveDeadNodesStub + fakeReturns := fake.removeDeadNodesReturns + fake.recordInvocation("RemoveDeadNodes", []interface{}{}) + fake.removeDeadNodesMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeRouter) RemoveDeadNodesCallCount() int { + fake.removeDeadNodesMutex.RLock() + defer fake.removeDeadNodesMutex.RUnlock() + return len(fake.removeDeadNodesArgsForCall) +} + +func (fake *FakeRouter) RemoveDeadNodesCalls(stub func() error) { + fake.removeDeadNodesMutex.Lock() + defer fake.removeDeadNodesMutex.Unlock() + fake.RemoveDeadNodesStub = stub +} + +func (fake *FakeRouter) RemoveDeadNodesReturns(result1 error) { + fake.removeDeadNodesMutex.Lock() + defer fake.removeDeadNodesMutex.Unlock() + fake.RemoveDeadNodesStub = nil + fake.removeDeadNodesReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRouter) RemoveDeadNodesReturnsOnCall(i int, result1 error) { + fake.removeDeadNodesMutex.Lock() + defer fake.removeDeadNodesMutex.Unlock() + fake.RemoveDeadNodesStub = nil + if fake.removeDeadNodesReturnsOnCall == nil { + fake.removeDeadNodesReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.removeDeadNodesReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeRouter) SetNodeForRoom(arg1 string, arg2 string) error { fake.setNodeForRoomMutex.Lock() ret, specificReturn := fake.setNodeForRoomReturnsOnCall[len(fake.setNodeForRoomArgsForCall)] @@ -736,6 +799,8 @@ func (fake *FakeRouter) Invocations() map[string][][]interface{} { defer fake.onNewParticipantRTCMutex.RUnlock() fake.registerNodeMutex.RLock() defer fake.registerNodeMutex.RUnlock() + fake.removeDeadNodesMutex.RLock() + defer fake.removeDeadNodesMutex.RUnlock() fake.setNodeForRoomMutex.RLock() defer fake.setNodeForRoomMutex.RUnlock() fake.startMutex.RLock() diff --git a/pkg/routing/utils_test.go b/pkg/routing/utils_test.go index 689f2be4c..ca48993c2 100644 --- a/pkg/routing/utils_test.go +++ b/pkg/routing/utils_test.go @@ -14,7 +14,7 @@ func TestIsAvailable(t *testing.T) { t.Run("still available", func(t *testing.T) { n := &livekit.Node{ Stats: &livekit.NodeStats{ - UpdatedAt: time.Now().Unix() - 10, + UpdatedAt: time.Now().Unix() - 3, }, } assert.True(t, routing.IsAvailable(n)) diff --git a/pkg/rtc/datatrack.go b/pkg/rtc/datatrack.go index 812649562..bdbeb7164 100644 --- a/pkg/rtc/datatrack.go +++ b/pkg/rtc/datatrack.go @@ -80,6 +80,10 @@ func (t *DataTrack) IsMuted() bool { return false } +func (t *DataTrack) SetMuted(muted bool) { + +} + func (t *DataTrack) OnClose(f func()) { t.onClose = f } diff --git a/pkg/rtc/mediaengine.go b/pkg/rtc/mediaengine.go new file mode 100644 index 000000000..5647824b8 --- /dev/null +++ b/pkg/rtc/mediaengine.go @@ -0,0 +1,88 @@ +package rtc + +import ( + "github.com/pion/sdp/v3" + "github.com/pion/webrtc/v3" +) + +const ( + repairedRTP = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id" + frameMarking = "urn:ietf:params:rtp-hdrext:framemarking" +) + +func createMediaEngine() (*webrtc.MediaEngine, error) { + me := &webrtc.MediaEngine{} + if err := me.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "minptime=10;useinbandfec=1", RTCPFeedback: nil}, + PayloadType: 111, + }, webrtc.RTPCodecTypeAudio); err != nil { + return nil, err + } + + videoRTCPFeedback := []webrtc.RTCPFeedback{ + {webrtc.TypeRTCPFBGoogREMB, ""}, + //{webrtc.TypeRTCPFBCCM, "fir"}, + {webrtc.TypeRTCPFBNACK, ""}, + {webrtc.TypeRTCPFBNACK, "pli"}} + for _, codec := range []webrtc.RTPCodecParameters{ + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, RTCPFeedback: videoRTCPFeedback}, + PayloadType: 96, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=0", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 98, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP9, ClockRate: 90000, SDPFmtpLine: "profile-id=1", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 100, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 102, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 127, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 125, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 108, + }, + { + RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032", RTCPFeedback: videoRTCPFeedback}, + PayloadType: 123, + }, + } { + if err := me.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil { + return nil, err + } + } + + for _, extension := range []string{ + sdp.SDESMidURI, + sdp.SDESRTPStreamIDURI, + repairedRTP, + //sdp.TransportCCURI, + frameMarking, + } { + if err := me.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeVideo); err != nil { + return nil, err + } + } + for _, extension := range []string{ + sdp.SDESMidURI, + sdp.SDESRTPStreamIDURI, + sdp.AudioLevelURI, + } { + if err := me.RegisterHeaderExtension(webrtc.RTPHeaderExtensionCapability{URI: extension}, webrtc.RTPCodecTypeAudio); err != nil { + return nil, err + } + } + + return me, nil +} diff --git a/pkg/rtc/mediatrack.go b/pkg/rtc/mediatrack.go index 737677afd..cca0445b6 100644 --- a/pkg/rtc/mediatrack.go +++ b/pkg/rtc/mediatrack.go @@ -1,20 +1,18 @@ package rtc import ( - "io" "sync" "time" - "github.com/gammazero/workerpool" "github.com/pion/ion-sfu/pkg/buffer" + "github.com/pion/ion-sfu/pkg/sfu" + "github.com/pion/ion-sfu/pkg/twcc" "github.com/pion/rtcp" - "github.com/pion/transport/packetio" "github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3/pkg/rtcerr" "github.com/livekit/livekit-server/pkg/logger" "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/livekit/livekit-server/pkg/sfu" "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/livekit-server/proto/livekit" ) @@ -23,7 +21,6 @@ var ( maxPLIFrequency = 1 * time.Second feedbackTypes = []webrtc.RTCPFeedback{ {webrtc.TypeRTCPFBGoogREMB, ""}, - {webrtc.TypeRTCPFBTransportCC, ""}, {webrtc.TypeRTCPFBNACK, ""}, {webrtc.TypeRTCPFBNACK, "pli"}} ) @@ -40,44 +37,38 @@ type MediaTrack struct { streamID string kind livekit.TrackType codec webrtc.RTPCodecParameters + conf ReceiverConfig onClose func() // channel to send RTCP packets to the source - rtcpCh *utils.CalmChannel + rtcpCh chan []rtcp.Packet lock sync.RWMutex - once sync.Once - // map of target participantId -> DownTrack - downtracks map[string]types.DownTrack - receiver types.Receiver - nackWorker *workerpool.WorkerPool + // map of target participantId -> *SubscribedTrack + subscribedTracks map[string]*SubscribedTrack + twcc *twcc.Responder + receiver sfu.Receiver //lastNack int64 lastPLI time.Time } -func NewMediaTrack(trackId string, pId string, rtcpCh *utils.CalmChannel, track *webrtc.TrackRemote, receiver types.Receiver) *MediaTrack { +func NewMediaTrack(trackId string, pId string, rtcpCh chan []rtcp.Packet, conf ReceiverConfig, track *webrtc.TrackRemote) *MediaTrack { t := &MediaTrack{ - id: trackId, - participantId: pId, - ssrc: track.SSRC(), - streamID: track.StreamID(), - kind: ToProtoTrackKind(track.Kind()), - codec: track.Codec(), - rtcpCh: rtcpCh, - lock: sync.RWMutex{}, - once: sync.Once{}, - downtracks: make(map[string]types.DownTrack), - receiver: receiver, - nackWorker: workerpool.New(1), + id: trackId, + participantId: pId, + ssrc: track.SSRC(), + streamID: track.StreamID(), + kind: ToProtoTrackKind(track.Kind()), + codec: track.Codec(), + conf: conf, + rtcpCh: rtcpCh, + lock: sync.RWMutex{}, + subscribedTracks: make(map[string]*SubscribedTrack), } return t } func (t *MediaTrack) Start() { - t.once.Do(func() { - // start worker - go t.forwardRTPWorker() - }) } func (t *MediaTrack) ID() string { @@ -96,98 +87,171 @@ func (t *MediaTrack) IsMuted() bool { return t.muted.Get() } +func (t *MediaTrack) SetMuted(muted bool) { + t.muted.TrySet(muted) + + // mute all of the subscribedtracks + t.lock.RLock() + for id, st := range t.subscribedTracks { + logger.Debugw("setting muted", "dstParticipant", id, "muted", muted, "track", t.ID()) + st.SetPublisherMuted(muted) + } + t.lock.RUnlock() +} + func (t *MediaTrack) OnClose(f func()) { t.onClose = f } // subscribes participant to current remoteTrack // creates and add necessary forwarders and starts them -func (t *MediaTrack) AddSubscriber(participant types.Participant) error { - t.lock.RLock() - existingDt := t.downtracks[participant.ID()] - t.lock.RUnlock() +func (t *MediaTrack) AddSubscriber(sub types.Participant) error { + t.lock.Lock() + defer t.lock.Unlock() + existingSt := t.subscribedTracks[sub.ID()] // don't subscribe to the same track multiple times - if existingDt != nil { + if existingSt != nil { logger.Warnw("participant already subscribed to track", - "participant", participant.Identity(), - "track", existingDt.ID()) + "sub", sub.Identity(), + "track", t.ID()) return nil } - codec := t.codec - // pack ID to identify all publishedTracks - packedId := PackTrackId(t.participantId, t.id) + codec := t.receiver.Codec() // using DownTrack from ion-sfu - outTrack, err := sfu.NewDownTrack(webrtc.RTPCodecCapability{ + downTrack, err := sfu.NewDownTrack(webrtc.RTPCodecCapability{ MimeType: codec.MimeType, ClockRate: codec.ClockRate, Channels: codec.Channels, SDPFmtpLine: codec.SDPFmtpLine, RTCPFeedback: feedbackTypes, - }, packedId, t.streamID) + }, t.receiver, sub.ID()) if err != nil { return err } + subTrack := NewSubscribedTrack(downTrack) - transceiver, err := participant.PeerConnection().AddTransceiverFromTrack(outTrack, webrtc.RTPTransceiverInit{ + transceiver, err := sub.PeerConnection().AddTransceiverFromTrack(downTrack, webrtc.RTPTransceiverInit{ Direction: webrtc.RTPTransceiverDirectionSendrecv, }) if err != nil { return err } - outTrack.SetTransceiver(transceiver) + downTrack.SetTransceiver(transceiver) // when outtrack is bound, start loop to send reports - outTrack.OnBind(func() { - if rr := bufferFactory.GetOrNew(packetio.RTCPBufferPacket, outTrack.SSRC()).(*buffer.RTCPReader); rr != nil { - rr.OnPacket(func(pkt []byte) { - t.handleRTCP(outTrack, participant.Identity(), pkt) - }) - } - t.sendDownTrackBindingReports(participant.ID(), participant.RTCPChan()) + downTrack.OnBind(func() { + subTrack.SetPublisherMuted(t.IsMuted()) + t.sendDownTrackBindingReports(sub.ID(), sub.RTCPChan()) }) - outTrack.OnCloseHandler(func() { + downTrack.OnCloseHandler(func() { t.lock.Lock() - delete(t.downtracks, participant.ID()) + delete(t.subscribedTracks, sub.ID()) t.lock.Unlock() - // ignore if the subscribing participant is not connected - if participant.PeerConnection().ConnectionState() != webrtc.PeerConnectionStateConnected { + // ignore if the subscribing sub is not connected + if sub.PeerConnection().ConnectionState() == webrtc.PeerConnectionStateClosed { return } - // if the source has been terminated, we'll need to terminate all of the downtracks - // however, if the dest participant has disconnected, then we can skip + // if the source has been terminated, we'll need to terminate all of the subscribedtracks + // however, if the dest sub has disconnected, then we can skip sender := transceiver.Sender() - if sender != nil { - logger.Debugw("removing peerconnection track", - "track", t.id, - "participantId", t.participantId, - "destParticipant", participant.Identity()) - if err := participant.PeerConnection().RemoveTrack(sender); err != nil { - if err == webrtc.ErrConnectionClosed { - // participant closing, can skip removing downtracks - return - } - if _, ok := err.(*rtcerr.InvalidStateError); !ok { - logger.Warnw("could not remove remoteTrack from forwarder", - "participant", participant.Identity(), - "err", err) - } + if sender == nil { + return + } + logger.Debugw("removing peerconnection track", + "track", t.id, + "participantId", t.participantId, + "destParticipant", sub.Identity()) + if err := sub.PeerConnection().RemoveTrack(sender); err != nil { + if err == webrtc.ErrConnectionClosed { + // sub closing, can skip removing subscribedtracks + return + } + if _, ok := err.(*rtcerr.InvalidStateError); !ok { + logger.Warnw("could not remove remoteTrack from forwarder", + "sub", sub.Identity(), + "err", err) } } - participant.RemoveDownTrack(t.streamID, outTrack) + sub.RemoveSubscribedTrack(t.participantId, subTrack) + }) + + t.subscribedTracks[sub.ID()] = subTrack + + sub.AddSubscribedTrack(t.participantId, subTrack) + t.receiver.AddDownTrack(downTrack, true) + + return nil +} + +// adds a new RTP receiver to the track, returns true if this is a new track +func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track *webrtc.TrackRemote) { + //rid := track.RID() + + buff, rtcpReader := bufferFactory.GetBufferPair(uint32(track.SSRC())) + buff.OnFeedback(func(fb []rtcp.Packet) { + t.rtcpCh <- fb + }) + + if t.Kind() == livekit.TrackType_AUDIO { + // TODO: audio level stuff + } else if t.Kind() == livekit.TrackType_VIDEO { + //if t.twcc == nil { + // t.twcc = twcc.NewTransportWideCCResponder(uint32(track.SSRC())) + // t.twcc.OnFeedback(func(p rtcp.RawPacket) { + // t.rtcpCh <- []rtcp.Packet{&p} + // }) + //} + buff.OnTransportWideCC(func(sn uint16, timeNS int64, marker bool) { + //t.twcc.Push(sn, timeNS, marker) + }) + } + + rtcpReader.OnPacket(func(bytes []byte) { + pkts, err := rtcp.Unmarshal(bytes) + if err != nil { + logger.Errorw("could not unmarshal RTCP", "error", err) + return + } + + for _, pkt := range pkts { + switch pkt := pkt.(type) { + case *rtcp.SourceDescription: + // do nothing for now + case *rtcp.SenderReport: + buff.SetSenderReportData(pkt.RTPTime, pkt.NTPTime) + } + } }) - participant.AddDownTrack(t.streamID, outTrack) t.lock.Lock() defer t.lock.Unlock() - t.downtracks[participant.ID()] = outTrack + if t.receiver == nil { + // pack ID to identify all publishedTracks + packedId := PackTrackId(t.participantId, track.ID()) + t.receiver = NewWrappedReceiver(sfu.NewWebRTCReceiver(receiver, track, t.participantId), packedId) + t.receiver.SetRTCPCh(t.rtcpCh) + t.receiver.OnCloseHandler(func() { + t.lock.Lock() + defer t.lock.Unlock() + // source track closed + if t.Kind() == livekit.TrackType_AUDIO { + // TODO: remove audio level observer + } + t.receiver = nil + }) + } + t.receiver.AddUpTrack(track, buff) - return nil + buff.Bind(receiver.GetParameters(), buffer.Options{ + BufferTime: t.conf.maxBufferTime, + MaxBitRate: t.conf.maxBitrate, + }) } // removes peer from subscription @@ -196,8 +260,8 @@ func (t *MediaTrack) RemoveSubscriber(participantId string) { t.lock.RLock() defer t.lock.RUnlock() - if dt := t.downtracks[participantId]; dt != nil { - go dt.Close() + if subTrack := t.subscribedTracks[participantId]; subTrack != nil { + go subTrack.DownTrack().Close() } } @@ -205,22 +269,27 @@ func (t *MediaTrack) RemoveAllSubscribers() { logger.Debugw("removing all subscribers", "track", t.id) t.lock.RLock() defer t.lock.RUnlock() - for _, dt := range t.downtracks { - go dt.Close() + for _, subTrack := range t.subscribedTracks { + go subTrack.DownTrack().Close() } - t.downtracks = make(map[string]types.DownTrack) + t.subscribedTracks = make(map[string]*SubscribedTrack) } -func (t *MediaTrack) sendDownTrackBindingReports(participantId string, rtcpCh *utils.CalmChannel) { +func (t *MediaTrack) sendDownTrackBindingReports(participantId string, rtcpCh chan []rtcp.Packet) { var sd []rtcp.SourceDescriptionChunk t.lock.RLock() - dt := t.downtracks[participantId] + subTrack := t.subscribedTracks[participantId] t.lock.RUnlock() - if !dt.IsBound() { + + if subTrack == nil { + return + } + + chunks := subTrack.DownTrack().CreateSourceDescriptionChunks() + if chunks == nil { return } - chunks := dt.CreateSourceDescriptionChunks() if chunks != nil { sd = append(sd, chunks...) } @@ -234,7 +303,7 @@ func (t *MediaTrack) sendDownTrackBindingReports(participantId string, rtcpCh *u batch := pkts i := 0 for { - rtcpCh.Write(batch) + rtcpCh <- batch if i > 5 { return } @@ -243,129 +312,3 @@ func (t *MediaTrack) sendDownTrackBindingReports(participantId string, rtcpCh *u } }() } - -// b reads from the receiver and writes to each sender -func (t *MediaTrack) forwardRTPWorker() { - defer Recover() - defer func() { - t.RemoveAllSubscribers() - // TODO: send unpublished events? - t.nackWorker.Stop() - - if t.onClose != nil { - t.onClose() - } - }() - - for pkt := range t.receiver.RTPChan() { - //logger.Debugw("read packet from remoteTrack", - // "participant", t.participantId, - // "track", t.ID()) - // when track is muted, it's "disabled" on the client side, and will still be sending black frames - // when our metadata is updated as such, we shortcircuit forwarding of black frames - if t.muted.Get() { - continue - } - - t.lock.RLock() - for dstId, dt := range t.downtracks { - //logger.Debugw("read packet from remoteTrack", - // "srcParticipant", t.participantId, - // "destParticipant", dstId, - // "track", t.ID()) - err := dt.WriteRTP(pkt) - if IsEOF(err) { - // this participant unsubscribed, remove it - t.RemoveSubscriber(dstId) - continue - } - - if err == sfu.ErrRequiresKeyFrame { - delta := time.Now().Sub(t.lastPLI) - if delta < maxPLIFrequency { - continue - } - logger.Infow("keyframe required, sending PLI", - "srcParticipant", t.participantId) - rtcpPkts := []rtcp.Packet{ - &rtcp.PictureLossIndication{SenderSSRC: uint32(t.ssrc), MediaSSRC: pkt.SSRC}, - } - t.rtcpCh.Write(rtcpPkts) - t.lastPLI = time.Now() - } else if err != nil { - logger.Warnw("could not forward packet to participant", - "src", t.participantId, - "dest", dstId, - "remoteTrack", t.id, - "err", err) - } - } - t.lock.RUnlock() - } -} - -func (t *MediaTrack) handleRTCP(dt *sfu.DownTrack, identity string, rtcpBuf []byte) { - defer Recover() - pkts, err := rtcp.Unmarshal(rtcpBuf) - if err != nil { - logger.Warnw("could not decode RTCP packet", "err", err) - } - - var fwdPkts []rtcp.Packet - // sufficient to send this only once - pliOnce := true - firOnce := true - for _, pkt := range pkts { - switch p := pkt.(type) { - case *rtcp.PictureLossIndication: - if pliOnce { - p.MediaSSRC = dt.LastSSRC() - p.SenderSSRC = dt.SSRC() - fwdPkts = append(fwdPkts, p) - pliOnce = false - } - case *rtcp.FullIntraRequest: - if firOnce { - p.MediaSSRC = dt.LastSSRC() - p.SenderSSRC = dt.SSRC() - fwdPkts = append(fwdPkts, p) - firOnce = false - } - case *rtcp.ReceiverReport: - if len(p.Reports) > 0 && p.Reports[0].FractionLost > 25 { - //log.Tracef("Slow link for sender %s, fraction packet lost %.2f", f.track.peerID, float64(p.Reports[0].FractionLost)/256) - } - case *rtcp.TransportLayerNack: - logger.Debugw("forwarder got nack", - "participant", identity, - "packet", p) - var nackedPackets []uint16 - for _, pair := range p.Nacks { - nackedPackets = append(nackedPackets, dt.GetNACKSeqNo(pair.PacketList())...) - } - - if t.nackWorker.Stopped() { - return - } - t.nackWorker.Submit(func() { - pktBuf := packetFactory.Get().([]byte) - for _, sn := range nackedPackets { - pkt, err := t.receiver.GetBufferedPacket(pktBuf, sn, dt.SnOffset()) - if err == io.EOF { - break - } else if err != nil { - logger.Warnw("error getting buffered packet", "error", err) - continue - } - // what about handling write errors such as no packet found - dt.WriteRTP(pkt) - } - packetFactory.Put(pktBuf) - }) - } - } - - if len(fwdPkts) > 0 { - t.rtcpCh.Write(fwdPkts) - } -} diff --git a/pkg/rtc/mediatrack_internal_test.go b/pkg/rtc/mediatrack_internal_test.go deleted file mode 100644 index 0b2624609..000000000 --- a/pkg/rtc/mediatrack_internal_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package rtc - -import ( - "sync" - "testing" - "time" - - "github.com/gammazero/workerpool" - "github.com/pion/rtcp" - "github.com/pion/rtp" - "github.com/pion/webrtc/v3" - "github.com/stretchr/testify/assert" - - "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/livekit/livekit-server/pkg/rtc/types/typesfakes" - "github.com/livekit/livekit-server/pkg/sfu" - "github.com/livekit/livekit-server/pkg/utils" - "github.com/livekit/livekit-server/proto/livekit" -) - -const ( - testWaitDuration = 10 * time.Millisecond -) - -func TestForwardRTP(t *testing.T) { - t.Run("ensure that forwarders are getting packets", func(t *testing.T) { - mt := newMediaTrackWithReceiver() - receiver := mt.receiver.(*typesfakes.FakeReceiver) - packet := rtp.Packet{} - receiver.RTPChanReturns(packetGenerator(packet)) - - dt := &typesfakes.FakeDownTrack{} - mt.downtracks["test"] = dt - - mt.Start() - time.Sleep(testWaitDuration) - - assert.Equal(t, 1, dt.WriteRTPCallCount(), "WriteRTP wasn't called on Forwarder") - assert.EqualValues(t, packet, dt.WriteRTPArgsForCall(0)) - }) - - t.Run("muted tracks do not forward data", func(t *testing.T) { - mt := newMediaTrackWithReceiver() - mt.muted.TrySet(true) - - dt := &typesfakes.FakeDownTrack{} - mt.downtracks["test"] = dt - - mt.Start() - time.Sleep(testWaitDuration) - assert.Zero(t, dt.WriteRTPCallCount()) - }) -} - -func TestMissingKeyFrames(t *testing.T) { - t.Run("PLI packet is sent when forwarder misses keyframe", func(t *testing.T) { - mt := newMediaTrackWithReceiver() - - dt := &typesfakes.FakeDownTrack{} - mt.downtracks["test"] = dt - dt.WriteRTPReturns(sfu.ErrRequiresKeyFrame) - - mt.Start() - time.Sleep(testWaitDuration) - - select { - case o := <-mt.rtcpCh.ReadChan(): - if o == nil { - return - } - pkts, ok := o.([]rtcp.Packet) - assert.True(t, ok) - assert.Len(t, pkts, 1, "a single RTCP packet should be returned") - assert.IsType(t, &rtcp.PictureLossIndication{}, pkts[0]) - default: - t.Fatalf("did not receive RTCP packets") - } - }) -} - -// returns a receiver that reads a packet then returns EOF -func newMediaTrackWithReceiver() *MediaTrack { - packet := rtp.Packet{} - receiver := &typesfakes.FakeReceiver{ - RTPChanStub: func() <-chan rtp.Packet { - return packetGenerator(packet) - }, - } - return &MediaTrack{ - id: utils.NewGuid(utils.TrackPrefix), - participantId: "PAtest", - kind: livekit.TrackType_VIDEO, - codec: webrtc.RTPCodecParameters{}, - rtcpCh: utils.NewCalmChannel(5), - lock: sync.RWMutex{}, - once: sync.Once{}, - downtracks: map[string]types.DownTrack{}, - receiver: receiver, - nackWorker: workerpool.New(1), - } -} - -func packetGenerator(packets ...rtp.Packet) <-chan rtp.Packet { - pc := make(chan rtp.Packet) - go func() { - defer close(pc) - for _, p := range packets { - pc <- p - } - }() - return pc -} diff --git a/pkg/rtc/participant.go b/pkg/rtc/participant.go index 0dba3cb48..6fc1a1057 100644 --- a/pkg/rtc/participant.go +++ b/pkg/rtc/participant.go @@ -15,7 +15,6 @@ import ( "github.com/livekit/livekit-server/pkg/logger" "github.com/livekit/livekit-server/pkg/routing" "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/livekit/livekit-server/pkg/sfu" "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/livekit-server/proto/livekit" ) @@ -33,16 +32,17 @@ const ( ) type ParticipantImpl struct { - id string - peerConn types.PeerConnection - responseSink routing.MessageSink - receiverConfig ReceiverConfig - isClosed utils.AtomicFlag - mediaEngine *webrtc.MediaEngine - identity string - state atomic.Value // livekit.ParticipantInfo_State - rtcpCh *utils.CalmChannel - subscribedTracks map[string][]*sfu.DownTrack + id string + peerConn types.PeerConnection + responseSink routing.MessageSink + receiverConfig ReceiverConfig + isClosed utils.AtomicFlag + mediaEngine *webrtc.MediaEngine + identity string + state atomic.Value // livekit.ParticipantInfo_State + rtcpCh chan []rtcp.Packet + // tracks the current participant is subscribed to, map of otherParticipantId => []DownTrack + subscribedTracks map[string][]types.SubscribedTrack // publishedTracks that participant is publishing publishedTracks map[string]types.PublishedTrack // client intended to publish, yet to be reconciled @@ -64,8 +64,10 @@ type ParticipantImpl struct { } func NewPeerConnection(conf *WebRTCConfig) (*webrtc.PeerConnection, error) { - me := &webrtc.MediaEngine{} - me.RegisterDefaultCodecs() + me, err := createMediaEngine() + if err != nil { + return nil, err + } se := conf.SettingEngine se.BufferFactory = bufferFactory.GetOrNew @@ -76,8 +78,6 @@ func NewPeerConnection(conf *WebRTCConfig) (*webrtc.PeerConnection, error) { func NewParticipant(identity string, pc types.PeerConnection, rs routing.MessageSink, receiverConfig ReceiverConfig) (*ParticipantImpl, error) { // TODO: check to ensure params are valid, id and identity can't be empty - me := &webrtc.MediaEngine{} - me.RegisterDefaultCodecs() participant := &ParticipantImpl{ id: utils.NewGuid(utils.ParticipantPrefix), @@ -85,13 +85,12 @@ func NewParticipant(identity string, pc types.PeerConnection, rs routing.Message peerConn: pc, responseSink: rs, receiverConfig: receiverConfig, - rtcpCh: utils.NewCalmChannel(50), - subscribedTracks: make(map[string][]*sfu.DownTrack), + rtcpCh: make(chan []rtcp.Packet, 50), + subscribedTracks: make(map[string][]types.SubscribedTrack), lock: sync.RWMutex{}, negotiationCond: sync.NewCond(&sync.Mutex{}), publishedTracks: make(map[string]types.PublishedTrack, 0), pendingTracks: make(map[string]*livekit.TrackInfo), - mediaEngine: me, debouncedNegotiate: debounce.New(negotiationFrequency), } participant.state.Store(livekit.ParticipantInfo_JOINING) @@ -113,7 +112,8 @@ func NewParticipant(identity string, pc types.PeerConnection, rs routing.Message }, }) if err != nil { - logger.Errorw("could not send trickle", "err", err) + logger.Errorw("could not send trickle", "err", err, + "participant", identity) } if participant.onICECandidate != nil { @@ -162,7 +162,7 @@ func (p *ParticipantImpl) IsReady() bool { return state == livekit.ParticipantInfo_JOINED || state == livekit.ParticipantInfo_ACTIVE } -func (p *ParticipantImpl) RTCPChan() *utils.CalmChannel { +func (p *ParticipantImpl) RTCPChan() chan []rtcp.Packet { return p.rtcpCh } @@ -246,9 +246,9 @@ func (p *ParticipantImpl) Answer(sdp webrtc.SessionDescription) (answer webrtc.S } p.negotiationCond.L.Unlock() - logger.Debugw("sending to client answer", + logger.Debugw("sending answer to client", "participant", p.Identity(), - //"sdp", sdp.SDP, + //"sdp", sdp.SDP, ) err = p.responseSink.WriteMessage(&livekit.SignalResponse{ Message: &livekit.SignalResponse_Answer{ @@ -279,7 +279,7 @@ func (p *ParticipantImpl) AddTrack(clientId, name string, trackType livekit.Trac } p.pendingTracks[clientId] = ti - p.responseSink.WriteMessage(&livekit.SignalResponse{ + err := p.responseSink.WriteMessage(&livekit.SignalResponse{ Message: &livekit.SignalResponse_TrackPublished{ TrackPublished: &livekit.TrackPublishedResponse{ Cid: clientId, @@ -287,6 +287,10 @@ func (p *ParticipantImpl) AddTrack(clientId, name string, trackType livekit.Trac }, }, }) + if err != nil { + logger.Errorw("could not write message", "error", err, + "participant", p.identity) + } } func (p *ParticipantImpl) HandleAnswer(sdp webrtc.SessionDescription) error { @@ -295,7 +299,7 @@ func (p *ParticipantImpl) HandleAnswer(sdp webrtc.SessionDescription) error { } logger.Debugw("setting participant answer", "participant", p.Identity(), - //"sdp", sdp.SDP, + //"sdp", sdp.SDP, ) if err := p.peerConn.SetRemoteDescription(sdp); err != nil { return errors.Wrap(err, "could not set remote description") @@ -323,11 +327,15 @@ func (p *ParticipantImpl) HandleClientNegotiation() { logger.Debugw("allowing participant to negotiate", "participant", p.Identity()) - p.responseSink.WriteMessage(&livekit.SignalResponse{ + err := p.responseSink.WriteMessage(&livekit.SignalResponse{ Message: &livekit.SignalResponse_Negotiate{ Negotiate: &livekit.NegotiationResponse{}, }, }) + if err != nil { + logger.Errorw("could not write message", "error", err, + "participant", p.identity) + } } // AddICECandidate adds candidates for remote peer @@ -361,7 +369,7 @@ func (p *ParticipantImpl) Close() error { p.onClose(p) } p.peerConn.Close() - p.rtcpCh.Close() + close(p.rtcpCh) return nil } @@ -369,11 +377,16 @@ func (p *ParticipantImpl) Close() error { func (p *ParticipantImpl) AddSubscriber(op types.Participant) error { p.lock.RLock() tracks := funk.Values(p.publishedTracks).([]types.PublishedTrack) - defer p.lock.RUnlock() + p.lock.RUnlock() + + if len(tracks) == 0 { + return nil + } logger.Debugw("subscribing new participant to tracks", "srcParticipant", p.Identity(), - "dstParticipant", op.Identity()) + "newParticipant", op.Identity(), + "numTracks", len(tracks)) for _, track := range tracks { if err := track.AddSubscriber(op); err != nil { @@ -442,23 +455,28 @@ func (p *ParticipantImpl) PeerConnection() types.PeerConnection { return p.peerConn } -func (p *ParticipantImpl) AddDownTrack(streamId string, dt *sfu.DownTrack) { +// add a track to the participant's subscribed list +func (p *ParticipantImpl) AddSubscribedTrack(pubId string, subTrack types.SubscribedTrack) { + logger.Debugw("added subscribedTrack", "srcParticipant", pubId, + "participant", p.Identity()) p.lock.Lock() - p.subscribedTracks[streamId] = append(p.subscribedTracks[streamId], dt) + p.subscribedTracks[pubId] = append(p.subscribedTracks[pubId], subTrack) p.lock.Unlock() } -func (p *ParticipantImpl) RemoveDownTrack(streamId string, dt *sfu.DownTrack) { +// remove a track to the participant's subscribed list +func (p *ParticipantImpl) RemoveSubscribedTrack(pubId string, subTrack types.SubscribedTrack) { + logger.Debugw("removed subscribedTrack", "srcParticipant", pubId, + "participant", p.Identity()) p.lock.Lock() defer p.lock.Unlock() - tracks := p.subscribedTracks[streamId] - newTracks := make([]*sfu.DownTrack, 0, len(tracks)) - for _, track := range tracks { - if track != dt { - newTracks = append(newTracks, track) + tracks := make([]types.SubscribedTrack, 0, len(p.subscribedTracks[pubId])) + for _, tr := range p.subscribedTracks[pubId] { + if tr != subTrack { + tracks = append(tracks, tr) } } - p.subscribedTracks[streamId] = newTracks + p.subscribedTracks[pubId] = tracks } func (p *ParticipantImpl) scheduleNegotiate() { @@ -472,13 +490,13 @@ func (p *ParticipantImpl) negotiate() { return } - logger.Debugw("starting server negotiation", "participant", p.Identity()) p.negotiationCond.L.Lock() for p.negotiationState != negotiationStateNone { p.negotiationCond.Wait() - p.negotiationState = negotiationStateServer } + p.negotiationState = negotiationStateServer p.negotiationCond.L.Unlock() + logger.Debugw("starting server negotiation", "participant", p.Identity()) offer, err := p.peerConn.CreateOffer(nil) if err != nil { @@ -508,8 +526,9 @@ func (p *ParticipantImpl) negotiate() { }, }) if err != nil { - logger.Errorw("could not send offer to peer", - "err", err) + logger.Errorw("could not send offer to participant", + "err", err, + "participant", p.identity) } } @@ -538,11 +557,39 @@ func (p *ParticipantImpl) onMediaTrack(track *webrtc.TrackRemote, rtpReceiver *w } // create ReceiverImpl - receiver := NewReceiver(p.rtcpCh, rtpReceiver, track, p.receiverConfig) - mt := NewMediaTrack(ti.Sid, p.id, p.rtcpCh, track, receiver) - mt.name = ti.Name + //receiver := NewReceiver(p.rtcpCh, rtpReceiver, track, p.receiverConfig) - p.handleTrackPublished(mt) + // use existing mediatrack to handle simulcast + p.lock.RLock() + ptrack := p.publishedTracks[ti.Sid] + p.lock.RUnlock() + + var mt *MediaTrack + var newTrack bool + if trk, ok := ptrack.(*MediaTrack); ok { + logger.Debugw("using existing mediatrack, simulcast", "rid", track.RID()) + mt = trk + } else { + mt = NewMediaTrack(ti.Sid, p.id, p.rtcpCh, p.receiverConfig, track) + mt.name = ti.Name + newTrack = true + } + + mt.AddReceiver(rtpReceiver, track) + + if newTrack { + p.handleTrackPublished(mt) + } + + // TODO: video tracks the current participant is subscribed to tends to freeze when the participant adds tracks + // to get around this, we'll trigger a resync on all tracks it's subscribed to + p.lock.RLock() + defer p.lock.RUnlock() + for _, tracks := range p.subscribedTracks { + for _, subTrack := range tracks { + subTrack.Resync() + } + } } func (p *ParticipantImpl) onDataChannel(dc *webrtc.DataChannel) { @@ -623,16 +670,15 @@ func (p *ParticipantImpl) downTracksRTCPWorker() { var pkts []rtcp.Packet var sd []rtcp.SourceDescriptionChunk p.lock.RLock() - for _, dts := range p.subscribedTracks { - for _, dt := range dts { - if !dt.IsBound() { + for _, tracks := range p.subscribedTracks { + for _, subTrack := range tracks { + sr := subTrack.DownTrack().CreateSenderReport() + chunks := subTrack.DownTrack().CreateSourceDescriptionChunks() + if sr == nil || chunks == nil { continue } - pkts = append(pkts, dt.CreateSenderReport()) - chunks := dt.CreateSourceDescriptionChunks() - if chunks != nil { - sd = append(sd, chunks...) - } + pkts = append(pkts, sr) + sd = append(sd, chunks...) } } p.lock.RUnlock() @@ -664,14 +710,10 @@ func (p *ParticipantImpl) downTracksRTCPWorker() { func (p *ParticipantImpl) rtcpSendWorker() { defer Recover() // read from rtcpChan - for o := range p.rtcpCh.ReadChan() { - if o == nil { + for pkts := range p.rtcpCh { + if pkts == nil { return } - pkts, ok := o.([]rtcp.Packet) - if !ok { - logger.Errorw("unexpected obj from rtcpCh", "object", o) - } //for _, pkt := range pkts { // logger.Debugw("writing RTCP", "packet", pkt) //} diff --git a/pkg/rtc/receiver.go b/pkg/rtc/receiver.go deleted file mode 100644 index b2959bcb4..000000000 --- a/pkg/rtc/receiver.go +++ /dev/null @@ -1,99 +0,0 @@ -package rtc - -import ( - "github.com/pion/ion-sfu/pkg/buffer" - "github.com/pion/rtcp" - "github.com/pion/rtp" - "github.com/pion/webrtc/v3" - - "github.com/livekit/livekit-server/pkg/logger" - "github.com/livekit/livekit-server/pkg/utils" -) - -const ( - // TODO: could probably increase this depending on Configuration/memory - maxChanSize = 1024 -) - -// A receiver is responsible for pulling from a single logical track -type ReceiverImpl struct { - rtpReceiver *webrtc.RTPReceiver - track *webrtc.TrackRemote - buffer *buffer.Buffer - rtcpReader *buffer.RTCPReader - rtcpChan *utils.CalmChannel -} - -func NewReceiver(rtcpCh *utils.CalmChannel, rtpReceiver *webrtc.RTPReceiver, track *webrtc.TrackRemote, config ReceiverConfig) *ReceiverImpl { - r := &ReceiverImpl{ - rtpReceiver: rtpReceiver, - rtcpChan: rtcpCh, - track: track, - } - - r.buffer, r.rtcpReader = bufferFactory.GetBufferPair(uint32(track.SSRC())) - - // when we have feedback for the sender, send through the rtcp channel - r.buffer.OnFeedback(func(fb []rtcp.Packet) { - RecoverSilent() - // rtcpChan could be closed - if r.rtcpChan != nil { - r.rtcpChan.Write(fb) - } - }) - - r.buffer.OnTransportWideCC(func(sn uint16, timeNS int64, marker bool) { - // TODO: figure out how to handle this - }) - - r.buffer.Bind(rtpReceiver.GetParameters(), buffer.Options{ - BufferTime: config.maxBufferTime, - MaxBitRate: config.maxBitrate, - }) - - // received sender updates - r.rtcpReader.OnPacket(func(bytes []byte) { - pkts, err := rtcp.Unmarshal(bytes) - if err != nil { - logger.Warnw("could not unmarshal RTCP packet") - return - } - for _, pkt := range pkts { - switch p := pkt.(type) { - case *rtcp.SenderReport: - r.buffer.SetSenderReportData(p.RTPTime, p.NTPTime) - case *rtcp.SourceDescription: - // TODO: see what we'd want to do with this - } - } - }) - - return r -} - -func (r *ReceiverImpl) Close() { - r.rtcpChan = nil - r.buffer.OnFeedback(nil) - r.buffer.Close() - r.rtpReceiver.Stop() - r.rtcpReader.Close() -} - -// PacketBuffer interface, retrieves a packet from buffer and deserializes -// it's possible that the packet can't be found, or the connection has been closed (io.EOF) -func (r *ReceiverImpl) GetBufferedPacket(pktBuf []byte, sn uint16, snOffset uint16) (p rtp.Packet, err error) { - if pktBuf == nil { - pktBuf = make([]byte, rtpPacketMaxSize) - } - size, err := r.buffer.GetPacket(pktBuf, sn+snOffset) - if err != nil { - return - } - - err = p.Unmarshal(pktBuf[:size]) - return -} - -func (r *ReceiverImpl) RTPChan() <-chan rtp.Packet { - return r.buffer.PacketChan() -} diff --git a/pkg/rtc/room.go b/pkg/rtc/room.go index cda3774c0..f036a2e4e 100644 --- a/pkg/rtc/room.go +++ b/pkg/rtc/room.go @@ -102,7 +102,7 @@ func (r *Room) Join(participant types.Participant) error { "oldState", oldState) r.broadcastParticipantState(p) - if oldState == livekit.ParticipantInfo_JOINING && p.State() == livekit.ParticipantInfo_JOINED { + if p.State() == livekit.ParticipantInfo_ACTIVE { // subscribe participant to existing publishedTracks for _, op := range r.GetParticipants() { if p.ID() == op.ID() { @@ -215,7 +215,7 @@ func (r *Room) onTrackAdded(participant types.Participant, track types.Published // skip publishing participant continue } - if !existingParticipant.IsReady() { + if existingParticipant.State() != livekit.ParticipantInfo_ACTIVE { // not fully joined. don't subscribe yet continue } diff --git a/pkg/rtc/room_test.go b/pkg/rtc/room_test.go index bab8119a5..382a06d70 100644 --- a/pkg/rtc/room_test.go +++ b/pkg/rtc/room_test.go @@ -76,8 +76,8 @@ func TestRoomJoin(t *testing.T) { stateChangeCB := p.OnStateChangeArgsForCall(0) assert.NotNil(t, stateChangeCB) - p.StateReturns(livekit.ParticipantInfo_JOINED) - stateChangeCB(p, livekit.ParticipantInfo_JOINING) + p.StateReturns(livekit.ParticipantInfo_ACTIVE) + stateChangeCB(p, livekit.ParticipantInfo_JOINED) // it should become a subscriber when connectivity changes for _, op := range rm.GetParticipants() { @@ -176,9 +176,9 @@ func TestNewTrack(t *testing.T) { rm := newRoomWithParticipants(t, 3) participants := rm.GetParticipants() p0 := participants[0].(*typesfakes.FakeParticipant) - p0.IsReadyReturns(false) + p0.StateReturns(livekit.ParticipantInfo_JOINED) p1 := participants[1].(*typesfakes.FakeParticipant) - p1.IsReadyReturns(true) + p1.StateReturns(livekit.ParticipantInfo_ACTIVE) pub := participants[2].(*typesfakes.FakeParticipant) diff --git a/pkg/rtc/subscribedtrack.go b/pkg/rtc/subscribedtrack.go new file mode 100644 index 000000000..c5eb1dbc9 --- /dev/null +++ b/pkg/rtc/subscribedtrack.go @@ -0,0 +1,52 @@ +package rtc + +import ( + "github.com/pion/ion-sfu/pkg/sfu" + + "github.com/livekit/livekit-server/pkg/utils" +) + +type SubscribedTrack struct { + dt *sfu.DownTrack + subMuted utils.AtomicFlag + pubMuted utils.AtomicFlag +} + +func NewSubscribedTrack(dt *sfu.DownTrack) *SubscribedTrack { + return &SubscribedTrack{ + dt: dt, + } +} + +func (t *SubscribedTrack) DownTrack() *sfu.DownTrack { + return t.dt +} + +// has subscriber indicated it wants to mute this track +func (t *SubscribedTrack) IsMuted() bool { + return t.subMuted.Get() +} + +// set subscriber mute preference +func (t *SubscribedTrack) SetMuted(muted bool) { + t.subMuted.TrySet(muted) + t.updateDownTrackMute() +} + +func (t *SubscribedTrack) SetPublisherMuted(muted bool) { + t.pubMuted.TrySet(muted) + t.updateDownTrackMute() +} + +// cause downtrack to resync and request a keyframe +// this is sort of a hack to prevent a subscriber from having tracks freeze on the client due to missing +// keyframes +func (t *SubscribedTrack) Resync() { + t.dt.Mute(true) + t.updateDownTrackMute() +} + +func (t *SubscribedTrack) updateDownTrackMute() { + muted := t.subMuted.Get() || t.pubMuted.Get() + t.dt.Mute(muted) +} diff --git a/pkg/rtc/types/interfaces.go b/pkg/rtc/types/interfaces.go index 6eeadff4d..e06b0e8b3 100644 --- a/pkg/rtc/types/interfaces.go +++ b/pkg/rtc/types/interfaces.go @@ -3,13 +3,11 @@ package types import ( "time" + "github.com/pion/ion-sfu/pkg/sfu" "github.com/pion/rtcp" - "github.com/pion/rtp" "github.com/pion/webrtc/v3" "github.com/livekit/livekit-server/pkg/routing" - "github.com/livekit/livekit-server/pkg/sfu" - "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/livekit-server/proto/livekit" ) @@ -53,7 +51,7 @@ type Participant interface { State() livekit.ParticipantInfo_State IsReady() bool ToProto() *livekit.ParticipantInfo - RTCPChan() *utils.CalmChannel + RTCPChan() chan []rtcp.Packet GetResponseSink() routing.MessageSink SetResponseSink(sink routing.MessageSink) @@ -82,8 +80,8 @@ type Participant interface { OnClose(func(Participant)) // package methods - AddDownTrack(streamId string, dt *sfu.DownTrack) - RemoveDownTrack(streamId string, dt *sfu.DownTrack) + AddSubscribedTrack(participantId string, st SubscribedTrack) + RemoveSubscribedTrack(participantId string, st SubscribedTrack) PeerConnection() PeerConnection } @@ -96,6 +94,7 @@ type PublishedTrack interface { Kind() livekit.TrackType Name() string IsMuted() bool + SetMuted(muted bool) AddSubscriber(participant Participant) error RemoveSubscriber(participantId string) RemoveAllSubscribers() @@ -104,28 +103,13 @@ type PublishedTrack interface { OnClose(func()) } -//counterfeiter:generate . Receiver -type Receiver interface { - RTPChan() <-chan rtp.Packet - GetBufferedPacket(pktBuf []byte, sn uint16, snOffset uint16) (rtp.Packet, error) -} - -// DownTrack publishes data to a target participant -// using this interface to make testing more practical -//counterfeiter:generate . DownTrack -type DownTrack interface { - ID() string - WriteRTP(p rtp.Packet) error - IsBound() bool - Close() - OnCloseHandler(fn func()) - OnBind(fn func()) - SSRC() uint32 - LastSSRC() uint32 - SnOffset() uint16 - TsOffset() uint32 - GetNACKSeqNo(seqNo []uint16) []uint16 - CreateSourceDescriptionChunks() []rtcp.SourceDescriptionChunk +//counterfeiter:generate . SubscribedTrack +type SubscribedTrack interface { + DownTrack() *sfu.DownTrack + IsMuted() bool + SetMuted(muted bool) + SetPublisherMuted(muted bool) + Resync() } // interface for properties of webrtc.TrackRemote diff --git a/pkg/rtc/types/typesfakes/fake_down_track.go b/pkg/rtc/types/typesfakes/fake_down_track.go deleted file mode 100644 index b96a0b06d..000000000 --- a/pkg/rtc/types/typesfakes/fake_down_track.go +++ /dev/null @@ -1,755 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package typesfakes - -import ( - "sync" - - "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/pion/rtcp" - "github.com/pion/rtp" -) - -type FakeDownTrack struct { - CloseStub func() - closeMutex sync.RWMutex - closeArgsForCall []struct { - } - CreateSourceDescriptionChunksStub func() []rtcp.SourceDescriptionChunk - createSourceDescriptionChunksMutex sync.RWMutex - createSourceDescriptionChunksArgsForCall []struct { - } - createSourceDescriptionChunksReturns struct { - result1 []rtcp.SourceDescriptionChunk - } - createSourceDescriptionChunksReturnsOnCall map[int]struct { - result1 []rtcp.SourceDescriptionChunk - } - GetNACKSeqNoStub func([]uint16) []uint16 - getNACKSeqNoMutex sync.RWMutex - getNACKSeqNoArgsForCall []struct { - arg1 []uint16 - } - getNACKSeqNoReturns struct { - result1 []uint16 - } - getNACKSeqNoReturnsOnCall map[int]struct { - result1 []uint16 - } - IDStub func() string - iDMutex sync.RWMutex - iDArgsForCall []struct { - } - iDReturns struct { - result1 string - } - iDReturnsOnCall map[int]struct { - result1 string - } - IsBoundStub func() bool - isBoundMutex sync.RWMutex - isBoundArgsForCall []struct { - } - isBoundReturns struct { - result1 bool - } - isBoundReturnsOnCall map[int]struct { - result1 bool - } - LastSSRCStub func() uint32 - lastSSRCMutex sync.RWMutex - lastSSRCArgsForCall []struct { - } - lastSSRCReturns struct { - result1 uint32 - } - lastSSRCReturnsOnCall map[int]struct { - result1 uint32 - } - OnBindStub func(func()) - onBindMutex sync.RWMutex - onBindArgsForCall []struct { - arg1 func() - } - OnCloseHandlerStub func(func()) - onCloseHandlerMutex sync.RWMutex - onCloseHandlerArgsForCall []struct { - arg1 func() - } - SSRCStub func() uint32 - sSRCMutex sync.RWMutex - sSRCArgsForCall []struct { - } - sSRCReturns struct { - result1 uint32 - } - sSRCReturnsOnCall map[int]struct { - result1 uint32 - } - SnOffsetStub func() uint16 - snOffsetMutex sync.RWMutex - snOffsetArgsForCall []struct { - } - snOffsetReturns struct { - result1 uint16 - } - snOffsetReturnsOnCall map[int]struct { - result1 uint16 - } - TsOffsetStub func() uint32 - tsOffsetMutex sync.RWMutex - tsOffsetArgsForCall []struct { - } - tsOffsetReturns struct { - result1 uint32 - } - tsOffsetReturnsOnCall map[int]struct { - result1 uint32 - } - WriteRTPStub func(rtp.Packet) error - writeRTPMutex sync.RWMutex - writeRTPArgsForCall []struct { - arg1 rtp.Packet - } - writeRTPReturns struct { - result1 error - } - writeRTPReturnsOnCall map[int]struct { - result1 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *FakeDownTrack) 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 *FakeDownTrack) CloseCallCount() int { - fake.closeMutex.RLock() - defer fake.closeMutex.RUnlock() - return len(fake.closeArgsForCall) -} - -func (fake *FakeDownTrack) CloseCalls(stub func()) { - fake.closeMutex.Lock() - defer fake.closeMutex.Unlock() - fake.CloseStub = stub -} - -func (fake *FakeDownTrack) CreateSourceDescriptionChunks() []rtcp.SourceDescriptionChunk { - fake.createSourceDescriptionChunksMutex.Lock() - ret, specificReturn := fake.createSourceDescriptionChunksReturnsOnCall[len(fake.createSourceDescriptionChunksArgsForCall)] - fake.createSourceDescriptionChunksArgsForCall = append(fake.createSourceDescriptionChunksArgsForCall, struct { - }{}) - stub := fake.CreateSourceDescriptionChunksStub - fakeReturns := fake.createSourceDescriptionChunksReturns - fake.recordInvocation("CreateSourceDescriptionChunks", []interface{}{}) - fake.createSourceDescriptionChunksMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) CreateSourceDescriptionChunksCallCount() int { - fake.createSourceDescriptionChunksMutex.RLock() - defer fake.createSourceDescriptionChunksMutex.RUnlock() - return len(fake.createSourceDescriptionChunksArgsForCall) -} - -func (fake *FakeDownTrack) CreateSourceDescriptionChunksCalls(stub func() []rtcp.SourceDescriptionChunk) { - fake.createSourceDescriptionChunksMutex.Lock() - defer fake.createSourceDescriptionChunksMutex.Unlock() - fake.CreateSourceDescriptionChunksStub = stub -} - -func (fake *FakeDownTrack) CreateSourceDescriptionChunksReturns(result1 []rtcp.SourceDescriptionChunk) { - fake.createSourceDescriptionChunksMutex.Lock() - defer fake.createSourceDescriptionChunksMutex.Unlock() - fake.CreateSourceDescriptionChunksStub = nil - fake.createSourceDescriptionChunksReturns = struct { - result1 []rtcp.SourceDescriptionChunk - }{result1} -} - -func (fake *FakeDownTrack) CreateSourceDescriptionChunksReturnsOnCall(i int, result1 []rtcp.SourceDescriptionChunk) { - fake.createSourceDescriptionChunksMutex.Lock() - defer fake.createSourceDescriptionChunksMutex.Unlock() - fake.CreateSourceDescriptionChunksStub = nil - if fake.createSourceDescriptionChunksReturnsOnCall == nil { - fake.createSourceDescriptionChunksReturnsOnCall = make(map[int]struct { - result1 []rtcp.SourceDescriptionChunk - }) - } - fake.createSourceDescriptionChunksReturnsOnCall[i] = struct { - result1 []rtcp.SourceDescriptionChunk - }{result1} -} - -func (fake *FakeDownTrack) GetNACKSeqNo(arg1 []uint16) []uint16 { - var arg1Copy []uint16 - if arg1 != nil { - arg1Copy = make([]uint16, len(arg1)) - copy(arg1Copy, arg1) - } - fake.getNACKSeqNoMutex.Lock() - ret, specificReturn := fake.getNACKSeqNoReturnsOnCall[len(fake.getNACKSeqNoArgsForCall)] - fake.getNACKSeqNoArgsForCall = append(fake.getNACKSeqNoArgsForCall, struct { - arg1 []uint16 - }{arg1Copy}) - stub := fake.GetNACKSeqNoStub - fakeReturns := fake.getNACKSeqNoReturns - fake.recordInvocation("GetNACKSeqNo", []interface{}{arg1Copy}) - fake.getNACKSeqNoMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) GetNACKSeqNoCallCount() int { - fake.getNACKSeqNoMutex.RLock() - defer fake.getNACKSeqNoMutex.RUnlock() - return len(fake.getNACKSeqNoArgsForCall) -} - -func (fake *FakeDownTrack) GetNACKSeqNoCalls(stub func([]uint16) []uint16) { - fake.getNACKSeqNoMutex.Lock() - defer fake.getNACKSeqNoMutex.Unlock() - fake.GetNACKSeqNoStub = stub -} - -func (fake *FakeDownTrack) GetNACKSeqNoArgsForCall(i int) []uint16 { - fake.getNACKSeqNoMutex.RLock() - defer fake.getNACKSeqNoMutex.RUnlock() - argsForCall := fake.getNACKSeqNoArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeDownTrack) GetNACKSeqNoReturns(result1 []uint16) { - fake.getNACKSeqNoMutex.Lock() - defer fake.getNACKSeqNoMutex.Unlock() - fake.GetNACKSeqNoStub = nil - fake.getNACKSeqNoReturns = struct { - result1 []uint16 - }{result1} -} - -func (fake *FakeDownTrack) GetNACKSeqNoReturnsOnCall(i int, result1 []uint16) { - fake.getNACKSeqNoMutex.Lock() - defer fake.getNACKSeqNoMutex.Unlock() - fake.GetNACKSeqNoStub = nil - if fake.getNACKSeqNoReturnsOnCall == nil { - fake.getNACKSeqNoReturnsOnCall = make(map[int]struct { - result1 []uint16 - }) - } - fake.getNACKSeqNoReturnsOnCall[i] = struct { - result1 []uint16 - }{result1} -} - -func (fake *FakeDownTrack) ID() string { - 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 *FakeDownTrack) IDCallCount() int { - fake.iDMutex.RLock() - defer fake.iDMutex.RUnlock() - return len(fake.iDArgsForCall) -} - -func (fake *FakeDownTrack) IDCalls(stub func() string) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = stub -} - -func (fake *FakeDownTrack) IDReturns(result1 string) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = nil - fake.iDReturns = struct { - result1 string - }{result1} -} - -func (fake *FakeDownTrack) IDReturnsOnCall(i int, result1 string) { - fake.iDMutex.Lock() - defer fake.iDMutex.Unlock() - fake.IDStub = nil - if fake.iDReturnsOnCall == nil { - fake.iDReturnsOnCall = make(map[int]struct { - result1 string - }) - } - fake.iDReturnsOnCall[i] = struct { - result1 string - }{result1} -} - -func (fake *FakeDownTrack) IsBound() bool { - fake.isBoundMutex.Lock() - ret, specificReturn := fake.isBoundReturnsOnCall[len(fake.isBoundArgsForCall)] - fake.isBoundArgsForCall = append(fake.isBoundArgsForCall, struct { - }{}) - stub := fake.IsBoundStub - fakeReturns := fake.isBoundReturns - fake.recordInvocation("IsBound", []interface{}{}) - fake.isBoundMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) IsBoundCallCount() int { - fake.isBoundMutex.RLock() - defer fake.isBoundMutex.RUnlock() - return len(fake.isBoundArgsForCall) -} - -func (fake *FakeDownTrack) IsBoundCalls(stub func() bool) { - fake.isBoundMutex.Lock() - defer fake.isBoundMutex.Unlock() - fake.IsBoundStub = stub -} - -func (fake *FakeDownTrack) IsBoundReturns(result1 bool) { - fake.isBoundMutex.Lock() - defer fake.isBoundMutex.Unlock() - fake.IsBoundStub = nil - fake.isBoundReturns = struct { - result1 bool - }{result1} -} - -func (fake *FakeDownTrack) IsBoundReturnsOnCall(i int, result1 bool) { - fake.isBoundMutex.Lock() - defer fake.isBoundMutex.Unlock() - fake.IsBoundStub = nil - if fake.isBoundReturnsOnCall == nil { - fake.isBoundReturnsOnCall = make(map[int]struct { - result1 bool - }) - } - fake.isBoundReturnsOnCall[i] = struct { - result1 bool - }{result1} -} - -func (fake *FakeDownTrack) LastSSRC() uint32 { - fake.lastSSRCMutex.Lock() - ret, specificReturn := fake.lastSSRCReturnsOnCall[len(fake.lastSSRCArgsForCall)] - fake.lastSSRCArgsForCall = append(fake.lastSSRCArgsForCall, struct { - }{}) - stub := fake.LastSSRCStub - fakeReturns := fake.lastSSRCReturns - fake.recordInvocation("LastSSRC", []interface{}{}) - fake.lastSSRCMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) LastSSRCCallCount() int { - fake.lastSSRCMutex.RLock() - defer fake.lastSSRCMutex.RUnlock() - return len(fake.lastSSRCArgsForCall) -} - -func (fake *FakeDownTrack) LastSSRCCalls(stub func() uint32) { - fake.lastSSRCMutex.Lock() - defer fake.lastSSRCMutex.Unlock() - fake.LastSSRCStub = stub -} - -func (fake *FakeDownTrack) LastSSRCReturns(result1 uint32) { - fake.lastSSRCMutex.Lock() - defer fake.lastSSRCMutex.Unlock() - fake.LastSSRCStub = nil - fake.lastSSRCReturns = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) LastSSRCReturnsOnCall(i int, result1 uint32) { - fake.lastSSRCMutex.Lock() - defer fake.lastSSRCMutex.Unlock() - fake.LastSSRCStub = nil - if fake.lastSSRCReturnsOnCall == nil { - fake.lastSSRCReturnsOnCall = make(map[int]struct { - result1 uint32 - }) - } - fake.lastSSRCReturnsOnCall[i] = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) OnBind(arg1 func()) { - fake.onBindMutex.Lock() - fake.onBindArgsForCall = append(fake.onBindArgsForCall, struct { - arg1 func() - }{arg1}) - stub := fake.OnBindStub - fake.recordInvocation("OnBind", []interface{}{arg1}) - fake.onBindMutex.Unlock() - if stub != nil { - fake.OnBindStub(arg1) - } -} - -func (fake *FakeDownTrack) OnBindCallCount() int { - fake.onBindMutex.RLock() - defer fake.onBindMutex.RUnlock() - return len(fake.onBindArgsForCall) -} - -func (fake *FakeDownTrack) OnBindCalls(stub func(func())) { - fake.onBindMutex.Lock() - defer fake.onBindMutex.Unlock() - fake.OnBindStub = stub -} - -func (fake *FakeDownTrack) OnBindArgsForCall(i int) func() { - fake.onBindMutex.RLock() - defer fake.onBindMutex.RUnlock() - argsForCall := fake.onBindArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeDownTrack) OnCloseHandler(arg1 func()) { - fake.onCloseHandlerMutex.Lock() - fake.onCloseHandlerArgsForCall = append(fake.onCloseHandlerArgsForCall, struct { - arg1 func() - }{arg1}) - stub := fake.OnCloseHandlerStub - fake.recordInvocation("OnCloseHandler", []interface{}{arg1}) - fake.onCloseHandlerMutex.Unlock() - if stub != nil { - fake.OnCloseHandlerStub(arg1) - } -} - -func (fake *FakeDownTrack) OnCloseHandlerCallCount() int { - fake.onCloseHandlerMutex.RLock() - defer fake.onCloseHandlerMutex.RUnlock() - return len(fake.onCloseHandlerArgsForCall) -} - -func (fake *FakeDownTrack) OnCloseHandlerCalls(stub func(func())) { - fake.onCloseHandlerMutex.Lock() - defer fake.onCloseHandlerMutex.Unlock() - fake.OnCloseHandlerStub = stub -} - -func (fake *FakeDownTrack) OnCloseHandlerArgsForCall(i int) func() { - fake.onCloseHandlerMutex.RLock() - defer fake.onCloseHandlerMutex.RUnlock() - argsForCall := fake.onCloseHandlerArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeDownTrack) SSRC() uint32 { - fake.sSRCMutex.Lock() - ret, specificReturn := fake.sSRCReturnsOnCall[len(fake.sSRCArgsForCall)] - fake.sSRCArgsForCall = append(fake.sSRCArgsForCall, struct { - }{}) - stub := fake.SSRCStub - fakeReturns := fake.sSRCReturns - fake.recordInvocation("SSRC", []interface{}{}) - fake.sSRCMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) SSRCCallCount() int { - fake.sSRCMutex.RLock() - defer fake.sSRCMutex.RUnlock() - return len(fake.sSRCArgsForCall) -} - -func (fake *FakeDownTrack) SSRCCalls(stub func() uint32) { - fake.sSRCMutex.Lock() - defer fake.sSRCMutex.Unlock() - fake.SSRCStub = stub -} - -func (fake *FakeDownTrack) SSRCReturns(result1 uint32) { - fake.sSRCMutex.Lock() - defer fake.sSRCMutex.Unlock() - fake.SSRCStub = nil - fake.sSRCReturns = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) SSRCReturnsOnCall(i int, result1 uint32) { - fake.sSRCMutex.Lock() - defer fake.sSRCMutex.Unlock() - fake.SSRCStub = nil - if fake.sSRCReturnsOnCall == nil { - fake.sSRCReturnsOnCall = make(map[int]struct { - result1 uint32 - }) - } - fake.sSRCReturnsOnCall[i] = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) SnOffset() uint16 { - fake.snOffsetMutex.Lock() - ret, specificReturn := fake.snOffsetReturnsOnCall[len(fake.snOffsetArgsForCall)] - fake.snOffsetArgsForCall = append(fake.snOffsetArgsForCall, struct { - }{}) - stub := fake.SnOffsetStub - fakeReturns := fake.snOffsetReturns - fake.recordInvocation("SnOffset", []interface{}{}) - fake.snOffsetMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) SnOffsetCallCount() int { - fake.snOffsetMutex.RLock() - defer fake.snOffsetMutex.RUnlock() - return len(fake.snOffsetArgsForCall) -} - -func (fake *FakeDownTrack) SnOffsetCalls(stub func() uint16) { - fake.snOffsetMutex.Lock() - defer fake.snOffsetMutex.Unlock() - fake.SnOffsetStub = stub -} - -func (fake *FakeDownTrack) SnOffsetReturns(result1 uint16) { - fake.snOffsetMutex.Lock() - defer fake.snOffsetMutex.Unlock() - fake.SnOffsetStub = nil - fake.snOffsetReturns = struct { - result1 uint16 - }{result1} -} - -func (fake *FakeDownTrack) SnOffsetReturnsOnCall(i int, result1 uint16) { - fake.snOffsetMutex.Lock() - defer fake.snOffsetMutex.Unlock() - fake.SnOffsetStub = nil - if fake.snOffsetReturnsOnCall == nil { - fake.snOffsetReturnsOnCall = make(map[int]struct { - result1 uint16 - }) - } - fake.snOffsetReturnsOnCall[i] = struct { - result1 uint16 - }{result1} -} - -func (fake *FakeDownTrack) TsOffset() uint32 { - fake.tsOffsetMutex.Lock() - ret, specificReturn := fake.tsOffsetReturnsOnCall[len(fake.tsOffsetArgsForCall)] - fake.tsOffsetArgsForCall = append(fake.tsOffsetArgsForCall, struct { - }{}) - stub := fake.TsOffsetStub - fakeReturns := fake.tsOffsetReturns - fake.recordInvocation("TsOffset", []interface{}{}) - fake.tsOffsetMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) TsOffsetCallCount() int { - fake.tsOffsetMutex.RLock() - defer fake.tsOffsetMutex.RUnlock() - return len(fake.tsOffsetArgsForCall) -} - -func (fake *FakeDownTrack) TsOffsetCalls(stub func() uint32) { - fake.tsOffsetMutex.Lock() - defer fake.tsOffsetMutex.Unlock() - fake.TsOffsetStub = stub -} - -func (fake *FakeDownTrack) TsOffsetReturns(result1 uint32) { - fake.tsOffsetMutex.Lock() - defer fake.tsOffsetMutex.Unlock() - fake.TsOffsetStub = nil - fake.tsOffsetReturns = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) TsOffsetReturnsOnCall(i int, result1 uint32) { - fake.tsOffsetMutex.Lock() - defer fake.tsOffsetMutex.Unlock() - fake.TsOffsetStub = nil - if fake.tsOffsetReturnsOnCall == nil { - fake.tsOffsetReturnsOnCall = make(map[int]struct { - result1 uint32 - }) - } - fake.tsOffsetReturnsOnCall[i] = struct { - result1 uint32 - }{result1} -} - -func (fake *FakeDownTrack) WriteRTP(arg1 rtp.Packet) error { - fake.writeRTPMutex.Lock() - ret, specificReturn := fake.writeRTPReturnsOnCall[len(fake.writeRTPArgsForCall)] - fake.writeRTPArgsForCall = append(fake.writeRTPArgsForCall, struct { - arg1 rtp.Packet - }{arg1}) - stub := fake.WriteRTPStub - fakeReturns := fake.writeRTPReturns - fake.recordInvocation("WriteRTP", []interface{}{arg1}) - fake.writeRTPMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDownTrack) WriteRTPCallCount() int { - fake.writeRTPMutex.RLock() - defer fake.writeRTPMutex.RUnlock() - return len(fake.writeRTPArgsForCall) -} - -func (fake *FakeDownTrack) WriteRTPCalls(stub func(rtp.Packet) error) { - fake.writeRTPMutex.Lock() - defer fake.writeRTPMutex.Unlock() - fake.WriteRTPStub = stub -} - -func (fake *FakeDownTrack) WriteRTPArgsForCall(i int) rtp.Packet { - fake.writeRTPMutex.RLock() - defer fake.writeRTPMutex.RUnlock() - argsForCall := fake.writeRTPArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeDownTrack) WriteRTPReturns(result1 error) { - fake.writeRTPMutex.Lock() - defer fake.writeRTPMutex.Unlock() - fake.WriteRTPStub = nil - fake.writeRTPReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeDownTrack) WriteRTPReturnsOnCall(i int, result1 error) { - fake.writeRTPMutex.Lock() - defer fake.writeRTPMutex.Unlock() - fake.WriteRTPStub = nil - if fake.writeRTPReturnsOnCall == nil { - fake.writeRTPReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.writeRTPReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *FakeDownTrack) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.closeMutex.RLock() - defer fake.closeMutex.RUnlock() - fake.createSourceDescriptionChunksMutex.RLock() - defer fake.createSourceDescriptionChunksMutex.RUnlock() - fake.getNACKSeqNoMutex.RLock() - defer fake.getNACKSeqNoMutex.RUnlock() - fake.iDMutex.RLock() - defer fake.iDMutex.RUnlock() - fake.isBoundMutex.RLock() - defer fake.isBoundMutex.RUnlock() - fake.lastSSRCMutex.RLock() - defer fake.lastSSRCMutex.RUnlock() - fake.onBindMutex.RLock() - defer fake.onBindMutex.RUnlock() - fake.onCloseHandlerMutex.RLock() - defer fake.onCloseHandlerMutex.RUnlock() - fake.sSRCMutex.RLock() - defer fake.sSRCMutex.RUnlock() - fake.snOffsetMutex.RLock() - defer fake.snOffsetMutex.RUnlock() - fake.tsOffsetMutex.RLock() - defer fake.tsOffsetMutex.RUnlock() - fake.writeRTPMutex.RLock() - defer fake.writeRTPMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *FakeDownTrack) 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.DownTrack = new(FakeDownTrack) diff --git a/pkg/rtc/types/typesfakes/fake_participant.go b/pkg/rtc/types/typesfakes/fake_participant.go index 8d31fb64d..acedc6451 100644 --- a/pkg/rtc/types/typesfakes/fake_participant.go +++ b/pkg/rtc/types/typesfakes/fake_participant.go @@ -6,19 +6,12 @@ import ( "github.com/livekit/livekit-server/pkg/routing" "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/livekit/livekit-server/pkg/sfu" - "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/livekit-server/proto/livekit" + "github.com/pion/rtcp" webrtc "github.com/pion/webrtc/v3" ) type FakeParticipant struct { - AddDownTrackStub func(string, *sfu.DownTrack) - addDownTrackMutex sync.RWMutex - addDownTrackArgsForCall []struct { - arg1 string - arg2 *sfu.DownTrack - } AddICECandidateStub func(webrtc.ICECandidateInit) error addICECandidateMutex sync.RWMutex addICECandidateArgsForCall []struct { @@ -30,6 +23,12 @@ type FakeParticipant struct { addICECandidateReturnsOnCall map[int]struct { result1 error } + AddSubscribedTrackStub func(string, types.SubscribedTrack) + addSubscribedTrackMutex sync.RWMutex + addSubscribedTrackArgsForCall []struct { + arg1 string + arg2 types.SubscribedTrack + } AddSubscriberStub func(types.Participant) error addSubscriberMutex sync.RWMutex addSubscriberArgsForCall []struct { @@ -161,21 +160,21 @@ type FakeParticipant struct { peerConnectionReturnsOnCall map[int]struct { result1 types.PeerConnection } - RTCPChanStub func() *utils.CalmChannel + RTCPChanStub func() chan []rtcp.Packet rTCPChanMutex sync.RWMutex rTCPChanArgsForCall []struct { } rTCPChanReturns struct { - result1 *utils.CalmChannel + result1 chan []rtcp.Packet } rTCPChanReturnsOnCall map[int]struct { - result1 *utils.CalmChannel + result1 chan []rtcp.Packet } - RemoveDownTrackStub func(string, *sfu.DownTrack) - removeDownTrackMutex sync.RWMutex - removeDownTrackArgsForCall []struct { + RemoveSubscribedTrackStub func(string, types.SubscribedTrack) + removeSubscribedTrackMutex sync.RWMutex + removeSubscribedTrackArgsForCall []struct { arg1 string - arg2 *sfu.DownTrack + arg2 types.SubscribedTrack } RemoveSubscriberStub func(string) removeSubscriberMutex sync.RWMutex @@ -244,39 +243,6 @@ type FakeParticipant struct { invocationsMutex sync.RWMutex } -func (fake *FakeParticipant) AddDownTrack(arg1 string, arg2 *sfu.DownTrack) { - fake.addDownTrackMutex.Lock() - fake.addDownTrackArgsForCall = append(fake.addDownTrackArgsForCall, struct { - arg1 string - arg2 *sfu.DownTrack - }{arg1, arg2}) - stub := fake.AddDownTrackStub - fake.recordInvocation("AddDownTrack", []interface{}{arg1, arg2}) - fake.addDownTrackMutex.Unlock() - if stub != nil { - fake.AddDownTrackStub(arg1, arg2) - } -} - -func (fake *FakeParticipant) AddDownTrackCallCount() int { - fake.addDownTrackMutex.RLock() - defer fake.addDownTrackMutex.RUnlock() - return len(fake.addDownTrackArgsForCall) -} - -func (fake *FakeParticipant) AddDownTrackCalls(stub func(string, *sfu.DownTrack)) { - fake.addDownTrackMutex.Lock() - defer fake.addDownTrackMutex.Unlock() - fake.AddDownTrackStub = stub -} - -func (fake *FakeParticipant) AddDownTrackArgsForCall(i int) (string, *sfu.DownTrack) { - fake.addDownTrackMutex.RLock() - defer fake.addDownTrackMutex.RUnlock() - argsForCall := fake.addDownTrackArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2 -} - func (fake *FakeParticipant) AddICECandidate(arg1 webrtc.ICECandidateInit) error { fake.addICECandidateMutex.Lock() ret, specificReturn := fake.addICECandidateReturnsOnCall[len(fake.addICECandidateArgsForCall)] @@ -338,6 +304,39 @@ func (fake *FakeParticipant) AddICECandidateReturnsOnCall(i int, result1 error) }{result1} } +func (fake *FakeParticipant) AddSubscribedTrack(arg1 string, arg2 types.SubscribedTrack) { + fake.addSubscribedTrackMutex.Lock() + fake.addSubscribedTrackArgsForCall = append(fake.addSubscribedTrackArgsForCall, struct { + arg1 string + arg2 types.SubscribedTrack + }{arg1, arg2}) + stub := fake.AddSubscribedTrackStub + fake.recordInvocation("AddSubscribedTrack", []interface{}{arg1, arg2}) + fake.addSubscribedTrackMutex.Unlock() + if stub != nil { + fake.AddSubscribedTrackStub(arg1, arg2) + } +} + +func (fake *FakeParticipant) AddSubscribedTrackCallCount() int { + fake.addSubscribedTrackMutex.RLock() + defer fake.addSubscribedTrackMutex.RUnlock() + return len(fake.addSubscribedTrackArgsForCall) +} + +func (fake *FakeParticipant) AddSubscribedTrackCalls(stub func(string, types.SubscribedTrack)) { + fake.addSubscribedTrackMutex.Lock() + defer fake.addSubscribedTrackMutex.Unlock() + fake.AddSubscribedTrackStub = stub +} + +func (fake *FakeParticipant) AddSubscribedTrackArgsForCall(i int) (string, types.SubscribedTrack) { + fake.addSubscribedTrackMutex.RLock() + defer fake.addSubscribedTrackMutex.RUnlock() + argsForCall := fake.addSubscribedTrackArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + func (fake *FakeParticipant) AddSubscriber(arg1 types.Participant) error { fake.addSubscriberMutex.Lock() ret, specificReturn := fake.addSubscriberReturnsOnCall[len(fake.addSubscriberArgsForCall)] @@ -1060,7 +1059,7 @@ func (fake *FakeParticipant) PeerConnectionReturnsOnCall(i int, result1 types.Pe }{result1} } -func (fake *FakeParticipant) RTCPChan() *utils.CalmChannel { +func (fake *FakeParticipant) RTCPChan() chan []rtcp.Packet { fake.rTCPChanMutex.Lock() ret, specificReturn := fake.rTCPChanReturnsOnCall[len(fake.rTCPChanArgsForCall)] fake.rTCPChanArgsForCall = append(fake.rTCPChanArgsForCall, struct { @@ -1084,65 +1083,65 @@ func (fake *FakeParticipant) RTCPChanCallCount() int { return len(fake.rTCPChanArgsForCall) } -func (fake *FakeParticipant) RTCPChanCalls(stub func() *utils.CalmChannel) { +func (fake *FakeParticipant) RTCPChanCalls(stub func() chan []rtcp.Packet) { fake.rTCPChanMutex.Lock() defer fake.rTCPChanMutex.Unlock() fake.RTCPChanStub = stub } -func (fake *FakeParticipant) RTCPChanReturns(result1 *utils.CalmChannel) { +func (fake *FakeParticipant) RTCPChanReturns(result1 chan []rtcp.Packet) { fake.rTCPChanMutex.Lock() defer fake.rTCPChanMutex.Unlock() fake.RTCPChanStub = nil fake.rTCPChanReturns = struct { - result1 *utils.CalmChannel + result1 chan []rtcp.Packet }{result1} } -func (fake *FakeParticipant) RTCPChanReturnsOnCall(i int, result1 *utils.CalmChannel) { +func (fake *FakeParticipant) RTCPChanReturnsOnCall(i int, result1 chan []rtcp.Packet) { fake.rTCPChanMutex.Lock() defer fake.rTCPChanMutex.Unlock() fake.RTCPChanStub = nil if fake.rTCPChanReturnsOnCall == nil { fake.rTCPChanReturnsOnCall = make(map[int]struct { - result1 *utils.CalmChannel + result1 chan []rtcp.Packet }) } fake.rTCPChanReturnsOnCall[i] = struct { - result1 *utils.CalmChannel + result1 chan []rtcp.Packet }{result1} } -func (fake *FakeParticipant) RemoveDownTrack(arg1 string, arg2 *sfu.DownTrack) { - fake.removeDownTrackMutex.Lock() - fake.removeDownTrackArgsForCall = append(fake.removeDownTrackArgsForCall, struct { +func (fake *FakeParticipant) RemoveSubscribedTrack(arg1 string, arg2 types.SubscribedTrack) { + fake.removeSubscribedTrackMutex.Lock() + fake.removeSubscribedTrackArgsForCall = append(fake.removeSubscribedTrackArgsForCall, struct { arg1 string - arg2 *sfu.DownTrack + arg2 types.SubscribedTrack }{arg1, arg2}) - stub := fake.RemoveDownTrackStub - fake.recordInvocation("RemoveDownTrack", []interface{}{arg1, arg2}) - fake.removeDownTrackMutex.Unlock() + stub := fake.RemoveSubscribedTrackStub + fake.recordInvocation("RemoveSubscribedTrack", []interface{}{arg1, arg2}) + fake.removeSubscribedTrackMutex.Unlock() if stub != nil { - fake.RemoveDownTrackStub(arg1, arg2) + fake.RemoveSubscribedTrackStub(arg1, arg2) } } -func (fake *FakeParticipant) RemoveDownTrackCallCount() int { - fake.removeDownTrackMutex.RLock() - defer fake.removeDownTrackMutex.RUnlock() - return len(fake.removeDownTrackArgsForCall) +func (fake *FakeParticipant) RemoveSubscribedTrackCallCount() int { + fake.removeSubscribedTrackMutex.RLock() + defer fake.removeSubscribedTrackMutex.RUnlock() + return len(fake.removeSubscribedTrackArgsForCall) } -func (fake *FakeParticipant) RemoveDownTrackCalls(stub func(string, *sfu.DownTrack)) { - fake.removeDownTrackMutex.Lock() - defer fake.removeDownTrackMutex.Unlock() - fake.RemoveDownTrackStub = stub +func (fake *FakeParticipant) RemoveSubscribedTrackCalls(stub func(string, types.SubscribedTrack)) { + fake.removeSubscribedTrackMutex.Lock() + defer fake.removeSubscribedTrackMutex.Unlock() + fake.RemoveSubscribedTrackStub = stub } -func (fake *FakeParticipant) RemoveDownTrackArgsForCall(i int) (string, *sfu.DownTrack) { - fake.removeDownTrackMutex.RLock() - defer fake.removeDownTrackMutex.RUnlock() - argsForCall := fake.removeDownTrackArgsForCall[i] +func (fake *FakeParticipant) RemoveSubscribedTrackArgsForCall(i int) (string, types.SubscribedTrack) { + fake.removeSubscribedTrackMutex.RLock() + defer fake.removeSubscribedTrackMutex.RUnlock() + argsForCall := fake.removeSubscribedTrackArgsForCall[i] return argsForCall.arg1, argsForCall.arg2 } @@ -1509,10 +1508,10 @@ func (fake *FakeParticipant) ToProtoReturnsOnCall(i int, result1 *livekit.Partic func (fake *FakeParticipant) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() - fake.addDownTrackMutex.RLock() - defer fake.addDownTrackMutex.RUnlock() fake.addICECandidateMutex.RLock() defer fake.addICECandidateMutex.RUnlock() + fake.addSubscribedTrackMutex.RLock() + defer fake.addSubscribedTrackMutex.RUnlock() fake.addSubscriberMutex.RLock() defer fake.addSubscriberMutex.RUnlock() fake.addTrackMutex.RLock() @@ -1547,8 +1546,8 @@ func (fake *FakeParticipant) Invocations() map[string][][]interface{} { defer fake.peerConnectionMutex.RUnlock() fake.rTCPChanMutex.RLock() defer fake.rTCPChanMutex.RUnlock() - fake.removeDownTrackMutex.RLock() - defer fake.removeDownTrackMutex.RUnlock() + fake.removeSubscribedTrackMutex.RLock() + defer fake.removeSubscribedTrackMutex.RUnlock() fake.removeSubscriberMutex.RLock() defer fake.removeSubscriberMutex.RUnlock() fake.sendJoinResponseMutex.RLock() diff --git a/pkg/rtc/types/typesfakes/fake_published_track.go b/pkg/rtc/types/typesfakes/fake_published_track.go index e4e448108..f7fa375ac 100644 --- a/pkg/rtc/types/typesfakes/fake_published_track.go +++ b/pkg/rtc/types/typesfakes/fake_published_track.go @@ -74,6 +74,11 @@ type FakePublishedTrack struct { removeSubscriberArgsForCall []struct { arg1 string } + SetMutedStub func(bool) + setMutedMutex sync.RWMutex + setMutedArgsForCall []struct { + arg1 bool + } StartStub func() startMutex sync.RWMutex startArgsForCall []struct { @@ -443,6 +448,38 @@ func (fake *FakePublishedTrack) RemoveSubscriberArgsForCall(i int) string { return argsForCall.arg1 } +func (fake *FakePublishedTrack) SetMuted(arg1 bool) { + fake.setMutedMutex.Lock() + fake.setMutedArgsForCall = append(fake.setMutedArgsForCall, struct { + arg1 bool + }{arg1}) + stub := fake.SetMutedStub + fake.recordInvocation("SetMuted", []interface{}{arg1}) + fake.setMutedMutex.Unlock() + if stub != nil { + fake.SetMutedStub(arg1) + } +} + +func (fake *FakePublishedTrack) SetMutedCallCount() int { + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() + return len(fake.setMutedArgsForCall) +} + +func (fake *FakePublishedTrack) SetMutedCalls(stub func(bool)) { + fake.setMutedMutex.Lock() + defer fake.setMutedMutex.Unlock() + fake.SetMutedStub = stub +} + +func (fake *FakePublishedTrack) SetMutedArgsForCall(i int) bool { + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() + argsForCall := fake.setMutedArgsForCall[i] + return argsForCall.arg1 +} + func (fake *FakePublishedTrack) Start() { fake.startMutex.Lock() fake.startArgsForCall = append(fake.startArgsForCall, struct { @@ -486,6 +523,8 @@ func (fake *FakePublishedTrack) Invocations() map[string][][]interface{} { defer fake.removeAllSubscribersMutex.RUnlock() fake.removeSubscriberMutex.RLock() defer fake.removeSubscriberMutex.RUnlock() + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() fake.startMutex.RLock() defer fake.startMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/pkg/rtc/types/typesfakes/fake_receiver.go b/pkg/rtc/types/typesfakes/fake_receiver.go deleted file mode 100644 index ec350ef5b..000000000 --- a/pkg/rtc/types/typesfakes/fake_receiver.go +++ /dev/null @@ -1,191 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package typesfakes - -import ( - "sync" - - "github.com/livekit/livekit-server/pkg/rtc/types" - "github.com/pion/rtp" -) - -type FakeReceiver struct { - GetBufferedPacketStub func([]byte, uint16, uint16) (rtp.Packet, error) - getBufferedPacketMutex sync.RWMutex - getBufferedPacketArgsForCall []struct { - arg1 []byte - arg2 uint16 - arg3 uint16 - } - getBufferedPacketReturns struct { - result1 rtp.Packet - result2 error - } - getBufferedPacketReturnsOnCall map[int]struct { - result1 rtp.Packet - result2 error - } - RTPChanStub func() <-chan rtp.Packet - rTPChanMutex sync.RWMutex - rTPChanArgsForCall []struct { - } - rTPChanReturns struct { - result1 <-chan rtp.Packet - } - rTPChanReturnsOnCall map[int]struct { - result1 <-chan rtp.Packet - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *FakeReceiver) GetBufferedPacket(arg1 []byte, arg2 uint16, arg3 uint16) (rtp.Packet, error) { - var arg1Copy []byte - if arg1 != nil { - arg1Copy = make([]byte, len(arg1)) - copy(arg1Copy, arg1) - } - fake.getBufferedPacketMutex.Lock() - ret, specificReturn := fake.getBufferedPacketReturnsOnCall[len(fake.getBufferedPacketArgsForCall)] - fake.getBufferedPacketArgsForCall = append(fake.getBufferedPacketArgsForCall, struct { - arg1 []byte - arg2 uint16 - arg3 uint16 - }{arg1Copy, arg2, arg3}) - stub := fake.GetBufferedPacketStub - fakeReturns := fake.getBufferedPacketReturns - fake.recordInvocation("GetBufferedPacket", []interface{}{arg1Copy, arg2, arg3}) - fake.getBufferedPacketMutex.Unlock() - if stub != nil { - return stub(arg1, arg2, arg3) - } - if specificReturn { - return ret.result1, ret.result2 - } - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *FakeReceiver) GetBufferedPacketCallCount() int { - fake.getBufferedPacketMutex.RLock() - defer fake.getBufferedPacketMutex.RUnlock() - return len(fake.getBufferedPacketArgsForCall) -} - -func (fake *FakeReceiver) GetBufferedPacketCalls(stub func([]byte, uint16, uint16) (rtp.Packet, error)) { - fake.getBufferedPacketMutex.Lock() - defer fake.getBufferedPacketMutex.Unlock() - fake.GetBufferedPacketStub = stub -} - -func (fake *FakeReceiver) GetBufferedPacketArgsForCall(i int) ([]byte, uint16, uint16) { - fake.getBufferedPacketMutex.RLock() - defer fake.getBufferedPacketMutex.RUnlock() - argsForCall := fake.getBufferedPacketArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 -} - -func (fake *FakeReceiver) GetBufferedPacketReturns(result1 rtp.Packet, result2 error) { - fake.getBufferedPacketMutex.Lock() - defer fake.getBufferedPacketMutex.Unlock() - fake.GetBufferedPacketStub = nil - fake.getBufferedPacketReturns = struct { - result1 rtp.Packet - result2 error - }{result1, result2} -} - -func (fake *FakeReceiver) GetBufferedPacketReturnsOnCall(i int, result1 rtp.Packet, result2 error) { - fake.getBufferedPacketMutex.Lock() - defer fake.getBufferedPacketMutex.Unlock() - fake.GetBufferedPacketStub = nil - if fake.getBufferedPacketReturnsOnCall == nil { - fake.getBufferedPacketReturnsOnCall = make(map[int]struct { - result1 rtp.Packet - result2 error - }) - } - fake.getBufferedPacketReturnsOnCall[i] = struct { - result1 rtp.Packet - result2 error - }{result1, result2} -} - -func (fake *FakeReceiver) RTPChan() <-chan rtp.Packet { - fake.rTPChanMutex.Lock() - ret, specificReturn := fake.rTPChanReturnsOnCall[len(fake.rTPChanArgsForCall)] - fake.rTPChanArgsForCall = append(fake.rTPChanArgsForCall, struct { - }{}) - stub := fake.RTPChanStub - fakeReturns := fake.rTPChanReturns - fake.recordInvocation("RTPChan", []interface{}{}) - fake.rTPChanMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeReceiver) RTPChanCallCount() int { - fake.rTPChanMutex.RLock() - defer fake.rTPChanMutex.RUnlock() - return len(fake.rTPChanArgsForCall) -} - -func (fake *FakeReceiver) RTPChanCalls(stub func() <-chan rtp.Packet) { - fake.rTPChanMutex.Lock() - defer fake.rTPChanMutex.Unlock() - fake.RTPChanStub = stub -} - -func (fake *FakeReceiver) RTPChanReturns(result1 <-chan rtp.Packet) { - fake.rTPChanMutex.Lock() - defer fake.rTPChanMutex.Unlock() - fake.RTPChanStub = nil - fake.rTPChanReturns = struct { - result1 <-chan rtp.Packet - }{result1} -} - -func (fake *FakeReceiver) RTPChanReturnsOnCall(i int, result1 <-chan rtp.Packet) { - fake.rTPChanMutex.Lock() - defer fake.rTPChanMutex.Unlock() - fake.RTPChanStub = nil - if fake.rTPChanReturnsOnCall == nil { - fake.rTPChanReturnsOnCall = make(map[int]struct { - result1 <-chan rtp.Packet - }) - } - fake.rTPChanReturnsOnCall[i] = struct { - result1 <-chan rtp.Packet - }{result1} -} - -func (fake *FakeReceiver) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.getBufferedPacketMutex.RLock() - defer fake.getBufferedPacketMutex.RUnlock() - fake.rTPChanMutex.RLock() - defer fake.rTPChanMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *FakeReceiver) 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.Receiver = new(FakeReceiver) diff --git a/pkg/rtc/types/typesfakes/fake_subscribed_track.go b/pkg/rtc/types/typesfakes/fake_subscribed_track.go new file mode 100644 index 000000000..a4f0eaef8 --- /dev/null +++ b/pkg/rtc/types/typesfakes/fake_subscribed_track.go @@ -0,0 +1,276 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package typesfakes + +import ( + "sync" + + "github.com/livekit/livekit-server/pkg/rtc/types" + "github.com/pion/ion-sfu/pkg/sfu" +) + +type FakeSubscribedTrack struct { + DownTrackStub func() *sfu.DownTrack + downTrackMutex sync.RWMutex + downTrackArgsForCall []struct { + } + downTrackReturns struct { + result1 *sfu.DownTrack + } + downTrackReturnsOnCall map[int]struct { + result1 *sfu.DownTrack + } + IsMutedStub func() bool + isMutedMutex sync.RWMutex + isMutedArgsForCall []struct { + } + isMutedReturns struct { + result1 bool + } + isMutedReturnsOnCall map[int]struct { + result1 bool + } + ResyncStub func() + resyncMutex sync.RWMutex + resyncArgsForCall []struct { + } + SetMutedStub func(bool) + setMutedMutex sync.RWMutex + setMutedArgsForCall []struct { + arg1 bool + } + SetPublisherMutedStub func(bool) + setPublisherMutedMutex sync.RWMutex + setPublisherMutedArgsForCall []struct { + arg1 bool + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeSubscribedTrack) DownTrack() *sfu.DownTrack { + fake.downTrackMutex.Lock() + ret, specificReturn := fake.downTrackReturnsOnCall[len(fake.downTrackArgsForCall)] + fake.downTrackArgsForCall = append(fake.downTrackArgsForCall, struct { + }{}) + stub := fake.DownTrackStub + fakeReturns := fake.downTrackReturns + fake.recordInvocation("DownTrack", []interface{}{}) + fake.downTrackMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeSubscribedTrack) DownTrackCallCount() int { + fake.downTrackMutex.RLock() + defer fake.downTrackMutex.RUnlock() + return len(fake.downTrackArgsForCall) +} + +func (fake *FakeSubscribedTrack) DownTrackCalls(stub func() *sfu.DownTrack) { + fake.downTrackMutex.Lock() + defer fake.downTrackMutex.Unlock() + fake.DownTrackStub = stub +} + +func (fake *FakeSubscribedTrack) DownTrackReturns(result1 *sfu.DownTrack) { + fake.downTrackMutex.Lock() + defer fake.downTrackMutex.Unlock() + fake.DownTrackStub = nil + fake.downTrackReturns = struct { + result1 *sfu.DownTrack + }{result1} +} + +func (fake *FakeSubscribedTrack) DownTrackReturnsOnCall(i int, result1 *sfu.DownTrack) { + fake.downTrackMutex.Lock() + defer fake.downTrackMutex.Unlock() + fake.DownTrackStub = nil + if fake.downTrackReturnsOnCall == nil { + fake.downTrackReturnsOnCall = make(map[int]struct { + result1 *sfu.DownTrack + }) + } + fake.downTrackReturnsOnCall[i] = struct { + result1 *sfu.DownTrack + }{result1} +} + +func (fake *FakeSubscribedTrack) IsMuted() bool { + fake.isMutedMutex.Lock() + ret, specificReturn := fake.isMutedReturnsOnCall[len(fake.isMutedArgsForCall)] + fake.isMutedArgsForCall = append(fake.isMutedArgsForCall, struct { + }{}) + stub := fake.IsMutedStub + fakeReturns := fake.isMutedReturns + fake.recordInvocation("IsMuted", []interface{}{}) + fake.isMutedMutex.Unlock() + if stub != nil { + return stub() + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeSubscribedTrack) IsMutedCallCount() int { + fake.isMutedMutex.RLock() + defer fake.isMutedMutex.RUnlock() + return len(fake.isMutedArgsForCall) +} + +func (fake *FakeSubscribedTrack) IsMutedCalls(stub func() bool) { + fake.isMutedMutex.Lock() + defer fake.isMutedMutex.Unlock() + fake.IsMutedStub = stub +} + +func (fake *FakeSubscribedTrack) IsMutedReturns(result1 bool) { + fake.isMutedMutex.Lock() + defer fake.isMutedMutex.Unlock() + fake.IsMutedStub = nil + fake.isMutedReturns = struct { + result1 bool + }{result1} +} + +func (fake *FakeSubscribedTrack) IsMutedReturnsOnCall(i int, result1 bool) { + fake.isMutedMutex.Lock() + defer fake.isMutedMutex.Unlock() + fake.IsMutedStub = nil + if fake.isMutedReturnsOnCall == nil { + fake.isMutedReturnsOnCall = make(map[int]struct { + result1 bool + }) + } + fake.isMutedReturnsOnCall[i] = struct { + result1 bool + }{result1} +} + +func (fake *FakeSubscribedTrack) Resync() { + fake.resyncMutex.Lock() + fake.resyncArgsForCall = append(fake.resyncArgsForCall, struct { + }{}) + stub := fake.ResyncStub + fake.recordInvocation("Resync", []interface{}{}) + fake.resyncMutex.Unlock() + if stub != nil { + fake.ResyncStub() + } +} + +func (fake *FakeSubscribedTrack) ResyncCallCount() int { + fake.resyncMutex.RLock() + defer fake.resyncMutex.RUnlock() + return len(fake.resyncArgsForCall) +} + +func (fake *FakeSubscribedTrack) ResyncCalls(stub func()) { + fake.resyncMutex.Lock() + defer fake.resyncMutex.Unlock() + fake.ResyncStub = stub +} + +func (fake *FakeSubscribedTrack) SetMuted(arg1 bool) { + fake.setMutedMutex.Lock() + fake.setMutedArgsForCall = append(fake.setMutedArgsForCall, struct { + arg1 bool + }{arg1}) + stub := fake.SetMutedStub + fake.recordInvocation("SetMuted", []interface{}{arg1}) + fake.setMutedMutex.Unlock() + if stub != nil { + fake.SetMutedStub(arg1) + } +} + +func (fake *FakeSubscribedTrack) SetMutedCallCount() int { + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() + return len(fake.setMutedArgsForCall) +} + +func (fake *FakeSubscribedTrack) SetMutedCalls(stub func(bool)) { + fake.setMutedMutex.Lock() + defer fake.setMutedMutex.Unlock() + fake.SetMutedStub = stub +} + +func (fake *FakeSubscribedTrack) SetMutedArgsForCall(i int) bool { + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() + argsForCall := fake.setMutedArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeSubscribedTrack) SetPublisherMuted(arg1 bool) { + fake.setPublisherMutedMutex.Lock() + fake.setPublisherMutedArgsForCall = append(fake.setPublisherMutedArgsForCall, struct { + arg1 bool + }{arg1}) + stub := fake.SetPublisherMutedStub + fake.recordInvocation("SetPublisherMuted", []interface{}{arg1}) + fake.setPublisherMutedMutex.Unlock() + if stub != nil { + fake.SetPublisherMutedStub(arg1) + } +} + +func (fake *FakeSubscribedTrack) SetPublisherMutedCallCount() int { + fake.setPublisherMutedMutex.RLock() + defer fake.setPublisherMutedMutex.RUnlock() + return len(fake.setPublisherMutedArgsForCall) +} + +func (fake *FakeSubscribedTrack) SetPublisherMutedCalls(stub func(bool)) { + fake.setPublisherMutedMutex.Lock() + defer fake.setPublisherMutedMutex.Unlock() + fake.SetPublisherMutedStub = stub +} + +func (fake *FakeSubscribedTrack) SetPublisherMutedArgsForCall(i int) bool { + fake.setPublisherMutedMutex.RLock() + defer fake.setPublisherMutedMutex.RUnlock() + argsForCall := fake.setPublisherMutedArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeSubscribedTrack) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.downTrackMutex.RLock() + defer fake.downTrackMutex.RUnlock() + fake.isMutedMutex.RLock() + defer fake.isMutedMutex.RUnlock() + fake.resyncMutex.RLock() + defer fake.resyncMutex.RUnlock() + fake.setMutedMutex.RLock() + defer fake.setMutedMutex.RUnlock() + fake.setPublisherMutedMutex.RLock() + defer fake.setPublisherMutedMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeSubscribedTrack) 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.SubscribedTrack = new(FakeSubscribedTrack) diff --git a/pkg/rtc/wrappedreceiver.go b/pkg/rtc/wrappedreceiver.go new file mode 100644 index 000000000..ed924c550 --- /dev/null +++ b/pkg/rtc/wrappedreceiver.go @@ -0,0 +1,23 @@ +package rtc + +import ( + "github.com/pion/ion-sfu/pkg/sfu" +) + +// wrapper around WebRTC receiver, overriding its ID + +type WrappedReceiver struct { + sfu.Receiver + trackId string +} + +func NewWrappedReceiver(receiver sfu.Receiver, trackId string) WrappedReceiver { + return WrappedReceiver{ + Receiver: receiver, + trackId: trackId, + } +} + +func (r WrappedReceiver) TrackID() string { + return r.trackId +} diff --git a/pkg/service/roommanager.go b/pkg/service/roommanager.go index 3d007e7be..500ad078a 100644 --- a/pkg/service/roommanager.go +++ b/pkg/service/roommanager.go @@ -253,7 +253,7 @@ func (r *RoomManager) rtcSessionWorker(room *rtc.Room, participant types.Partici case *livekit.SignalRequest_Offer: _, err := participant.Answer(rtc.FromProtoSessionDescription(msg.Offer)) if err != nil { - logger.Errorw("could not handle join", "err", err, "participant", participant.ID()) + logger.Errorw("could not handle offer", "err", err, "participant", participant.Identity()) return } case *livekit.SignalRequest_AddTrack: @@ -262,13 +262,13 @@ func (r *RoomManager) rtcSessionWorker(room *rtc.Room, participant types.Partici participant.AddTrack(msg.AddTrack.Cid, msg.AddTrack.Name, msg.AddTrack.Type) case *livekit.SignalRequest_Answer: if participant.State() == livekit.ParticipantInfo_JOINING { - logger.Errorw("cannot negotiate before peer offer", "participant", participant.ID()) + logger.Errorw("cannot negotiate before peer offer", "participant", participant.Identity()) //conn.WriteJSON(jsonError(http.StatusNotAcceptable, "cannot negotiate before peer offer")) return } sd := rtc.FromProtoSessionDescription(msg.Answer) if err := participant.HandleAnswer(sd); err != nil { - logger.Errorw("could not handle answer", "participant", participant.ID(), "err", err) + logger.Errorw("could not handle answer", "participant", participant.Identity(), "err", err) //conn.WriteJSON( // jsonError(http.StatusInternalServerError, "could not handle negotiate", err.Error())) return @@ -277,7 +277,7 @@ func (r *RoomManager) rtcSessionWorker(room *rtc.Room, participant types.Partici participant.HandleClientNegotiation() case *livekit.SignalRequest_Trickle: if participant.State() == livekit.ParticipantInfo_JOINING { - logger.Errorw("cannot trickle before peer offer", "participant", participant.ID()) + logger.Errorw("cannot trickle before offer", "participant", participant.Identity()) //conn.WriteJSON(jsonError(http.StatusNotAcceptable, "cannot trickle before peer offer")) return } @@ -285,7 +285,7 @@ func (r *RoomManager) rtcSessionWorker(room *rtc.Room, participant types.Partici candidateInit := rtc.FromProtoTrickle(msg.Trickle) //logger.Debugw("adding peer candidate", "participant", participant.ID()) if err := participant.AddICECandidate(candidateInit); err != nil { - logger.Errorw("could not handle trickle", "participant", participant.ID(), "err", err) + logger.Errorw("could not handle trickle", "participant", participant.Identity(), "err", err) //conn.WriteJSON( // jsonError(http.StatusInternalServerError, "could not handle trickle", err.Error())) return diff --git a/pkg/service/rtcservice.go b/pkg/service/rtcservice.go index dcc708fc6..898635397 100644 --- a/pkg/service/rtcservice.go +++ b/pkg/service/rtcservice.go @@ -118,11 +118,14 @@ func (s *RTCService) ServeHTTP(w http.ResponseWriter, r *http.Request) { return case msg := <-resSource.ReadChan(): if msg == nil { + logger.Errorw("source closed connection", "participant", identity) return } res, ok := msg.(*livekit.SignalResponse) if !ok { - logger.Errorw("unexpected message type", "type", fmt.Sprintf("%T", msg)) + logger.Errorw("unexpected message type", + "type", fmt.Sprintf("%T", msg), + "participant", identity) continue } diff --git a/pkg/service/server.go b/pkg/service/server.go index 1c5ca4aa3..5e2fc1c5f 100644 --- a/pkg/service/server.go +++ b/pkg/service/server.go @@ -69,7 +69,12 @@ func NewLivekitServer(conf *config.Config, router.OnNewParticipantRTC(roomManager.StartSession) // clean up old rooms on startup - err = roomManager.CleanupRooms() + if err = roomManager.CleanupRooms(); err != nil { + return + } + if err = router.RemoveDeadNodes(); err != nil { + return + } return } diff --git a/pkg/sfu/README.md b/pkg/sfu/README.md deleted file mode 100644 index 4c21f06a1..000000000 --- a/pkg/sfu/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This package is largely files from the wonderful ion-sfu project. - -https://github.com/pion/ion-sfu - -It's duplicated here with modifications to reference only classes that we need \ No newline at end of file diff --git a/pkg/sfu/downtrack.go b/pkg/sfu/downtrack.go deleted file mode 100644 index 63d155fb8..000000000 --- a/pkg/sfu/downtrack.go +++ /dev/null @@ -1,271 +0,0 @@ -package sfu - -import ( - "bytes" - "encoding/binary" - "strings" - "sync" - "sync/atomic" - "time" - - log "github.com/pion/ion-log" - "github.com/pion/rtcp" - "github.com/pion/rtp" - "github.com/pion/webrtc/v3" -) - -// DownTrack implements TrackLocal, is the track used to write packets -// to SFU Subscriber, the track handle the packets for simple, simulcast -// and SVC Publisher. -type DownTrack struct { - id string - bound atomicBool - mime string - nList *nackList - ssrc uint32 - payload uint8 - streamID string - - //enabled atomicBool - reSync atomicBool - snOffset uint16 - tsOffset uint32 - lastSSRC uint32 - lastSN uint16 - lastTS uint32 - - codec webrtc.RTPCodecCapability - transceiver *webrtc.RTPTransceiver - writeStream webrtc.TrackLocalWriter - onCloseHandler func() - onBind func() - closeOnce sync.Once - - // Report helpers - octetCount uint32 - packetCount uint32 - maxPacketTs uint32 - lastPacketMs int64 -} - -// NewDownTrack returns a DownTrack. -func NewDownTrack(c webrtc.RTPCodecCapability, id, streamID string) (*DownTrack, error) { - return &DownTrack{ - id: id, - nList: newNACKList(), - codec: c, - streamID: streamID, - }, nil -} - -// Bind is called by the PeerConnection after negotiation is complete -// This asserts that the code requested is supported by the remote peer. -// If so it setups all the state (SSRC and PayloadType) to have a call -func (d *DownTrack) Bind(t webrtc.TrackLocalContext) (webrtc.RTPCodecParameters, error) { - parameters := webrtc.RTPCodecParameters{RTPCodecCapability: d.codec} - if codec, err := codecParametersFuzzySearch(parameters, t.CodecParameters()); err == nil { - d.ssrc = uint32(t.SSRC()) - d.payload = uint8(codec.PayloadType) - d.writeStream = t.WriteStream() - d.mime = strings.ToLower(codec.MimeType) - d.bound.set(true) - d.reSync.set(true) - if d.onBind != nil { - d.onBind() - } - return codec, nil - } - return webrtc.RTPCodecParameters{}, webrtc.ErrUnsupportedCodec -} - -// Unbind implements the teardown logic when the track is no longer needed. This happens -// because a track has been stopped. -func (d *DownTrack) Unbind(_ webrtc.TrackLocalContext) error { - d.bound.set(false) - return nil -} - -func (d *DownTrack) IsBound() bool { - return d.bound.get() -} - -// ID is the unique identifier for this Track. This should be unique for the -// stream, but doesn't have to globally unique. A common example would be 'audio' or 'video' -// and StreamID would be 'desktop' or 'webcam' -func (d *DownTrack) ID() string { return d.id } - -// Codec returns current track codec capability -func (d *DownTrack) Codec() webrtc.RTPCodecCapability { return d.codec } - -// StreamID is the group this track belongs too. This must be unique -func (d *DownTrack) StreamID() string { return d.streamID } - -// Kind controls if this TrackLocal is audio or video -func (d *DownTrack) Kind() webrtc.RTPCodecType { - switch { - case strings.HasPrefix(d.codec.MimeType, "audio/"): - return webrtc.RTPCodecTypeAudio - case strings.HasPrefix(d.codec.MimeType, "video/"): - return webrtc.RTPCodecTypeVideo - default: - return webrtc.RTPCodecType(0) - } -} - -// WriteRTP writes a RTP Packet to the DownTrack -func (d *DownTrack) WriteRTP(p rtp.Packet) error { - if !d.IsBound() { - return nil - } - return d.writeSimpleRTP(p) -} - -func (d *DownTrack) CreateSourceDescriptionChunks() []rtcp.SourceDescriptionChunk { - if !d.IsBound() { - return nil - } - return []rtcp.SourceDescriptionChunk{ - { - Source: d.ssrc, - Items: []rtcp.SourceDescriptionItem{{ - Type: rtcp.SDESCNAME, - Text: d.streamID, - }}, - }, { - Source: d.ssrc, - Items: []rtcp.SourceDescriptionItem{{ - Type: rtcp.SDESType(15), - Text: d.transceiver.Mid(), - }}, - }, - } -} - -func (d *DownTrack) CreateSenderReport() *rtcp.SenderReport { - now := time.Now().UnixNano() - nowNTP := timeToNtp(now) - lastPktMs := atomic.LoadInt64(&d.lastPacketMs) - maxPktTs := atomic.LoadUint32(&d.lastTS) - diffTs := uint32((now/1e6)-lastPktMs) * d.codec.ClockRate / 1000 - octets, packets := d.getSRStats() - return &rtcp.SenderReport{ - SSRC: d.ssrc, - NTPTime: nowNTP, - RTPTime: maxPktTs + diffTs, - PacketCount: packets, - OctetCount: octets, - } -} - -// Close track -func (d *DownTrack) Close() { - d.closeOnce.Do(func() { - if d.onCloseHandler != nil { - d.onCloseHandler() - } - }) -} - -// OnCloseHandler method to be called on remote tracked removed -func (d *DownTrack) OnCloseHandler(fn func()) { - d.onCloseHandler = fn -} - -func (d *DownTrack) OnBind(fn func()) { - d.onBind = fn -} - -// -- start -- accessors to avoid accessing private vars - -func (d *DownTrack) SetTransceiver(transceiver *webrtc.RTPTransceiver) { - d.transceiver = transceiver -} - -func (d *DownTrack) RTPSender() *webrtc.RTPSender { - return d.transceiver.Sender() -} - -func (d *DownTrack) SSRC() uint32 { - return d.ssrc -} - -func (d *DownTrack) LastSSRC() uint32 { - return d.lastSSRC -} - -func (d *DownTrack) SnOffset() uint16 { - return d.snOffset -} - -func (d *DownTrack) TsOffset() uint32 { - return d.tsOffset -} - -func (d *DownTrack) GetNACKSeqNo(seqNo []uint16) []uint16 { - return d.nList.getNACKSeqNo(seqNo) -} - -// -- end -- accessors to avoid accessing private vars - -func (d *DownTrack) writeSimpleRTP(pkt rtp.Packet) error { - if d.reSync.get() { - if d.Kind() == webrtc.RTPCodecTypeVideo { - relay := false - // Wait for a keyframe to sync new source - switch d.mime { - case "video/vp8": - vp8Packet := VP8Helper{} - if err := vp8Packet.Unmarshal(pkt.Payload); err == nil { - relay = vp8Packet.IsKeyFrame - } - case "video/h264": - var word uint32 - payload := bytes.NewReader(pkt.Payload) - err := binary.Read(payload, binary.BigEndian, &word) - if err != nil || (word&0x1F000000)>>24 != 24 { - relay = false - } else { - relay = word&0x1F == 7 - } - } - if !relay { - // when we are writing to a new client and there isn't a keyframe, it makes it impossible - // for clients to render a frame. we'll send an error for the track writer. - return ErrRequiresKeyFrame - } - } - d.snOffset = pkt.SequenceNumber - d.lastSN - 1 - d.tsOffset = pkt.Timestamp - d.lastTS - 1 - d.lastSSRC = pkt.SSRC - d.reSync.set(false) - } - - atomic.AddUint32(&d.octetCount, uint32(len(pkt.Payload))) - atomic.AddUint32(&d.packetCount, 1) - - d.lastSSRC = pkt.SSRC - newSN := pkt.SequenceNumber - d.snOffset - newTS := pkt.Timestamp - d.tsOffset - if (newSN-d.lastSN)&0x8000 == 0 || d.lastSN == 0 { - d.lastSN = newSN - atomic.StoreInt64(&d.lastPacketMs, time.Now().UnixNano()/1e6) - atomic.StoreUint32(&d.lastTS, newTS) - } - pkt.PayloadType = d.payload - pkt.Timestamp = d.lastTS - pkt.SequenceNumber = d.lastSN - pkt.SSRC = d.ssrc - - _, err := d.writeStream.WriteRTP(&pkt.Header, pkt.Payload) - if err != nil { - log.Errorf("Write packet err %v", err) - } - - return err -} - -func (d *DownTrack) getSRStats() (octets, packets uint32) { - octets = atomic.LoadUint32(&d.octetCount) - packets = atomic.LoadUint32(&d.packetCount) - return -} diff --git a/pkg/sfu/errors.go b/pkg/sfu/errors.go deleted file mode 100644 index bbc8a6124..000000000 --- a/pkg/sfu/errors.go +++ /dev/null @@ -1,19 +0,0 @@ -package sfu - -import "errors" - -var ( - errPeerConnectionInitFailed = errors.New("pc init failed") - errPtNotSupported = errors.New("payload type not supported") - errCreatingDataChannel = errors.New("failed to create data channel") - // routing errors - errNoReceiverFound = errors.New("no receiver found") - // Helpers errors - errShortPacket = errors.New("packet is not large enough") - errNilPacket = errors.New("invalid nil packet") - // buffer errors - errPacketNotFound = errors.New("packet not found in cache") - errPacketTooOld = errors.New("packet not found in cache, too old") - - ErrRequiresKeyFrame = errors.New("requires a keyframe to send down first") -) diff --git a/pkg/sfu/helpers.go b/pkg/sfu/helpers.go deleted file mode 100644 index 9c5987902..000000000 --- a/pkg/sfu/helpers.go +++ /dev/null @@ -1,148 +0,0 @@ -package sfu - -import ( - "encoding/binary" - "strings" - "sync/atomic" - - "github.com/pion/webrtc/v3" -) - -const ntpEpoch = 2208988800 - -type atomicBool struct { - val int32 -} - -func (b *atomicBool) set(value bool) { // nolint: unparam - var i int32 - if value { - i = 1 - } - - atomic.StoreInt32(&(b.val), i) -} - -func (b *atomicBool) get() bool { - return atomic.LoadInt32(&(b.val)) != 0 -} - -// VP8Helper is a helper to get temporal data from VP8 packet header -/* - VP8Helper Payload Descriptor - 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - |X|R|N|S|R| PID | (REQUIRED) |X|R|N|S|R| PID | (REQUIRED) - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - X: |I|L|T|K| RSV | (OPTIONAL) X: |I|L|T|K| RSV | (OPTIONAL) - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - I: |M| PictureID | (OPTIONAL) I: |M| PictureID | (OPTIONAL) - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - L: | TL0PICIDX | (OPTIONAL) | PictureID | - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - T/K:|TID|Y| KEYIDX | (OPTIONAL) L: | TL0PICIDX | (OPTIONAL) - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - T/K:|TID|Y| KEYIDX | (OPTIONAL) - +-+-+-+-+-+-+-+-+ -*/ -type VP8Helper struct { - TemporalSupported bool - // Optional Header - PictureID uint16 /* 8 or 16 bits, picture ID */ - picIDIdx uint8 - mBit bool - TL0PICIDX uint8 /* 8 bits temporal level zero index */ - tlzIdx uint8 - - // Optional Header If either of the T or K bits are set to 1, - // the TID/Y/KEYIDX extension field MUST be present. - TID uint8 /* 2 bits temporal layer idx*/ - // IsKeyFrame is a helper to detect if current packet is a keyframe - IsKeyFrame bool -} - -// Unmarshal parses the passed byte slice and stores the result in the VP8Helper this method is called upon -func (p *VP8Helper) Unmarshal(payload []byte) error { - if payload == nil { - return errNilPacket - } - - payloadLen := len(payload) - - if payloadLen < 4 { - return errShortPacket - } - - var idx uint8 - S := payload[idx]&0x10 > 0 - // Check for extended bit control - if payload[idx]&0x80 > 0 { - idx++ - // Check if T is present, if not, no temporal layer is available - p.TemporalSupported = payload[idx]&0x20 > 0 - K := payload[idx]&0x10 > 0 - L := payload[idx]&0x40 > 0 - // Check for PictureID - if payload[idx]&0x80 > 0 { - idx++ - p.picIDIdx = idx - pid := payload[idx] & 0x7f - // Check if m is 1, then Picture ID is 15 bits - if payload[idx]&0x80 > 0 { - idx++ - p.mBit = true - p.PictureID = binary.BigEndian.Uint16([]byte{pid, payload[idx]}) - } else { - p.PictureID = uint16(pid) - } - } - // Check if TL0PICIDX is present - if L { - idx++ - p.tlzIdx = idx - p.TL0PICIDX = payload[idx] - } - if p.TemporalSupported || K { - idx++ - p.TID = (payload[idx] & 0xc0) >> 6 - } - if int(idx) >= payloadLen { - return errShortPacket - } - idx++ - // Check is packet is a keyframe by looking at P bit in vp8 payload - p.IsKeyFrame = payload[idx]&0x01 == 0 && S - } else { - idx++ - // Check is packet is a keyframe by looking at P bit in vp8 payload - p.IsKeyFrame = payload[idx]&0x01 == 0 && S - } - return nil -} - -func timeToNtp(ns int64) uint64 { - seconds := uint64(ns/1e9 + ntpEpoch) - fraction := uint64(((ns % 1e9) << 32) / 1e9) - return seconds<<32 | fraction -} - -// Do a fuzzy find for a codec in the list of codecs -// Used for lookup up a codec in an existing list to find a match -func codecParametersFuzzySearch(needle webrtc.RTPCodecParameters, haystack []webrtc.RTPCodecParameters) (webrtc.RTPCodecParameters, error) { - // First attempt to match on MimeType + SDPFmtpLine - for _, c := range haystack { - if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) && - c.RTPCodecCapability.SDPFmtpLine == needle.RTPCodecCapability.SDPFmtpLine { - return c, nil - } - } - - // Fallback to just MimeType - for _, c := range haystack { - if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) { - return c, nil - } - } - - return webrtc.RTPCodecParameters{}, webrtc.ErrCodecNotFound -} diff --git a/pkg/sfu/nacklist.go b/pkg/sfu/nacklist.go deleted file mode 100644 index 4b5c068f3..000000000 --- a/pkg/sfu/nacklist.go +++ /dev/null @@ -1,49 +0,0 @@ -package sfu - -import ( - "container/list" - "time" -) - -const ( - ignoreRetransmission = 5e8 // Ignore packet retransmission after ignoreRetransmission milliseconds - maxNackQueue = 100 -) - -type NACK struct { - SN uint16 - LRX int64 -} - -type nackList struct { - nacks map[uint16]*list.Element - ll *list.List -} - -func newNACKList() *nackList { - return &nackList{ - nacks: make(map[uint16]*list.Element), - ll: list.New(), - } -} - -func (n *nackList) getNACKSeqNo(seqno []uint16) []uint16 { - packets := make([]uint16, 0, 17) - for _, sn := range seqno { - if nack, ok := n.nacks[sn]; !ok { - n.nacks[sn] = n.ll.PushBack(NACK{sn, time.Now().UnixNano()}) - packets = append(packets, sn) - } else if time.Now().UnixNano()-nack.Value.(NACK).LRX > ignoreRetransmission { - nack.Value = NACK{sn, time.Now().UnixNano()} - packets = append(packets, sn) - } - } - - for len(n.nacks) > maxNackQueue { - el := n.ll.Back() - delete(n.nacks, el.Value.(NACK).SN) - n.ll.Remove(el) - } - - return packets -} diff --git a/test/integration_helpers.go b/test/integration_helpers.go index 1ad5e9f8a..c57bd2f3c 100644 --- a/test/integration_helpers.go +++ b/test/integration_helpers.go @@ -9,7 +9,6 @@ import ( "time" "github.com/go-redis/redis/v8" - "github.com/stretchr/testify/assert" "github.com/twitchtv/twirp" "github.com/livekit/livekit-server/cmd/cli/client" @@ -18,6 +17,7 @@ import ( "github.com/livekit/livekit-server/pkg/logger" "github.com/livekit/livekit-server/pkg/routing" "github.com/livekit/livekit-server/pkg/service" + "github.com/livekit/livekit-server/pkg/utils" "github.com/livekit/livekit-server/proto/livekit" ) @@ -62,8 +62,8 @@ func setupSingleNodeTest(roomName string) *service.LivekitServer { } func setupMultiNodeTest() (*service.LivekitServer, *service.LivekitServer) { - s1 := createMultiNodeServer(nodeId1, defaultServerPort) - s2 := createMultiNodeServer(nodeId2, secondServerPort) + s1 := createMultiNodeServer(utils.NewGuid(nodeId1), defaultServerPort) + s2 := createMultiNodeServer(utils.NewGuid(nodeId2), secondServerPort) go s1.Start() go s2.Start() @@ -122,17 +122,28 @@ func withTimeout(t *testing.T, description string, f func() bool) bool { func waitUntilConnected(t *testing.T, clients ...*client.RTCClient) { logger.Infow("waiting for clients to become connected") wg := sync.WaitGroup{} + errChan := make(chan error, len(clients)) for i := range clients { c := clients[i] wg.Add(1) go func() { defer wg.Done() - if !assert.NoError(t, c.WaitUntilConnected()) { - t.Fatal("client could not connect", c.ID()) + err := c.WaitUntilConnected() + if err != nil { + errChan <- err } }() } wg.Wait() + close(errChan) + hasError := false + for err := range errChan { + t.Fatal(err) + hasError = true + } + if hasError { + t.FailNow() + } } func createSingleNodeServer() *service.LivekitServer { @@ -143,7 +154,7 @@ func createSingleNodeServer() *service.LivekitServer { } currentNode, err := routing.NewLocalNode(conf) - currentNode.Id = nodeId1 + currentNode.Id = utils.NewGuid(nodeId1) if err != nil { panic(err) } @@ -151,6 +162,7 @@ func createSingleNodeServer() *service.LivekitServer { // local routing and store router := routing.NewLocalRouter(currentNode) roomStore := service.NewLocalRoomStore() + roomStore.DeleteRoom(testRoom) s, err := service.InitializeServer(conf, &StaticKeyProvider{}, roomStore, router, currentNode, &routing.RandomSelector{}) if err != nil { panic(fmt.Sprintf("could not create server: %v", err)) @@ -180,9 +192,13 @@ func createMultiNodeServer(nodeId string, port uint32) *service.LivekitServer { if err = rc.Ping(context.Background()).Err(); err != nil { panic(err) } + if err = rc.Del(context.Background(), routing.NodesKey).Err(); err != nil { + panic(err) + } router := routing.NewRedisRouter(currentNode, rc) roomStore := service.NewRedisRoomStore(rc) + roomStore.DeleteRoom(testRoom) s, err := service.InitializeServer(conf, &StaticKeyProvider{}, roomStore, router, currentNode, &routing.RandomSelector{}) if err != nil { panic(fmt.Sprintf("could not create server: %v", err)) diff --git a/test/multinode_test.go b/test/multinode_test.go index dddc8a22b..e95ece2d6 100644 --- a/test/multinode_test.go +++ b/test/multinode_test.go @@ -15,7 +15,7 @@ func TestMultiNodeRouting(t *testing.T) { return } - logger.Infow("---Starting TestMultiNodeRouting---") + logger.Infow("\n\n---Starting TestMultiNodeRouting---") defer logger.Infow("---Finishing TestMultiNodeRouting---") s1, s2 := setupMultiNodeTest() @@ -24,8 +24,7 @@ func TestMultiNodeRouting(t *testing.T) { // creating room on node 1 _, err := roomClient.CreateRoom(contextWithCreateRoomToken(), &livekit.CreateRoomRequest{ - Name: testRoom, - NodeId: nodeId1, + Name: testRoom, }) assert.NoError(t, err) @@ -80,7 +79,7 @@ func TestConnectWithoutCreation(t *testing.T) { t.SkipNow() return } - logger.Infow("---Starting TestConnectWithoutCreation---") + logger.Infow("\n\n---Starting TestConnectWithoutCreation---") defer logger.Infow("---Finishing TestConnectWithoutCreation---") s1, s2 := setupMultiNodeTest() @@ -100,7 +99,7 @@ func TestMultinodePublishingUponJoining(t *testing.T) { return } - logger.Infow("---Starting TestMultinodePublishingUponJoining---") + logger.Infow("\n\n---Starting TestMultinodePublishingUponJoining---") defer logger.Infow("---Finishing TestMultinodePublishingUponJoining---") s1, s2 := setupMultiNodeTest() @@ -116,7 +115,7 @@ func TestMultinodeReceiveBeforePublish(t *testing.T) { return } - logger.Infow("---Starting TestMultinodeReceiveBeforePublish---") + logger.Infow("\n\n---Starting TestMultinodeReceiveBeforePublish---") defer logger.Infow("---Finishing TestMultinodeReceiveBeforePublish---") s1, s2 := setupMultiNodeTest() diff --git a/test/scenarios.go b/test/scenarios.go index e821081dc..5a7b7f3ee 100644 --- a/test/scenarios.go +++ b/test/scenarios.go @@ -1,7 +1,6 @@ package test import ( - "math/rand" "testing" "time" @@ -13,9 +12,11 @@ import ( // a scenario with lots of clients connecting, publishing, and leaving at random periods func scenarioPublishingUponJoining(t *testing.T, ports ...int) { - c1 := createRTCClient("puj_1", ports[rand.Intn(len(ports))]) - c2 := createRTCClient("puj_2", ports[rand.Intn(len(ports))]) - c3 := createRTCClient("puj_3", ports[rand.Intn(len(ports))]) + firstPort := ports[0] + lastPort := ports[len(ports)-1] + c1 := createRTCClient("puj_1", firstPort) + c2 := createRTCClient("puj_2", lastPort) + c3 := createRTCClient("puj_3", firstPort) defer stopClients(c1, c2, c3) waitUntilConnected(t, c1, c2, c3) @@ -63,7 +64,8 @@ func scenarioPublishingUponJoining(t *testing.T, ports ...int) { } logger.Infow("c2 reconnecting") - c2 = createRTCClient("puj_2", ports[rand.Intn(len(ports))]) + // connect to a diff port + c2 = createRTCClient("puj_2", firstPort) defer c2.Stop() waitUntilConnected(t, c2) writers = publishTracksForClients(t, c2)