Squashed 'livekit-server/' changes from 154b4d26..46c43095

46c43095 fix goreleaser workflow, version 1.13.1 (#4577)
e0815be2 chore: improve docker test shutdown reliability (#4576)
bfd9deff expose TCPFallbackRTTThreshold and AllowUDPUnstableFallback via config (#4556)
b93c1e16 Release v1.13.0. (#4573)
fd452212 Update mediatransportutil to get ICE candidate timeout config (#4572)
8be8c74a Update github workflows (#4463)
c4e41872 Update go deps to v1.17.2 (#4462)
dc8e0310 Update go deps to v4 (#4482)
20fd1ad2 turn: allow for providing secret via file (#4564)
6590570d Pin pion/dtls to v3.1.2 (#4570)
cdbbee1f deps: bump protocol + psrpc to latest tips (#4565)
d290de81 Correct config comment (#4563)
77ecf920 rtc: report participant session end time on room move (#4561)
63be96f6 Prevent panic from nil(illegal) syncState.Subscriptions message (#4560)
835ef1b3 Metrics for participant active, i. e. fully established. (#4557)
5bd42534 Document of advertise_internal_ip and external_ip_only (#4554)
356ae211 Config documentation for advertise_internal_ip and skip_external_ip_validation (#4552)
7c319a67 rtc: prevent duration reporting for inactive participants (#4550)
2dd5e632 telemetry: split webhook-processed hook out of NewTelemetryService (#4548)
222177a9 service: prevent nil deref in validate with wrapped join request (#4547)
dd7580b4 Protect against nil clientInfo (#4546)
145689e6 Start tracking Twirp method request latency in prometheus too, not just in logs (#4545)
cde89627 rtc: emit per-data-track bytes via BytesTrackStats (#4540)
2e22911d Remove backwards compatibility support for TURN auth. (#4539)
062d1219 Use NACKQuueInterface type. (#4538)
7f08b04c Add IsIntentionalDisconnect helper (#4537)
1ab2bf04 Clean up packet size logging (#4536)
8ab92a80 Don't require media sections when joining (#4535)
019a6640 rtc: report participant kind code and details (#4534)
77595d38 TEL-336: fix sip error categorization (#4528)
f303f499 Always enable rtx codec (#4533)
e4a8a55c Check Less and LessEq in version compare. (#4532)
37eb7a32 Release v1.12.0 (#4529)
4a7b1e85 Create NACK tracker only once. (#4527)
89faaeba Apply ttl check only when authenticate allocation creating (#4526)
b32933b0 Log details of RTCP packets. (#4525)
8b79ec9e Support SIP auth realm for inbound. (#4522)
4b8db3cf Add integration test for TURN auth failures (#4524)
ef2e5efe Log large packets receive/send. (#4521)
d1236750 feat: auto create rooms for tokens with the RoomCreate grant (#4320)
7a3e595b apply room tags from JWT grant room configuration (#4518)
ab7fdeab add AssignmentHook to AssignJob; propagate websocket write errors (#4516)
cf20c9cd Add expiry to TURN password. (#4515)
20d4a3a1 Populate data track loggers with context (#4514)
12fff29a allow setting agent job assignment url (#4512)
ba366fc7 Fix SIP media config upgrade. (#4511)
8fbc5adf update protocol for protojson (#4510)
3de6f517 Add TURN permission handler. (#4505)
8ffcef93 Update protocol to support SIP media config. (#4509)
1ab1e072 test: verify upstream and downstream connection stats end-to-end (#4508)
c4fd71a5 Fix sense check in DeltaInfo gathering (#4507)
803999ef rename agent environment to deployment (#4506)
bacc21e6 add helper to check for agent worker endpoint (#4503)
253f977d add duration seconds reporting (#4500)
ffab3bd3 add agent environment (#4498)
ccdf23c8 Use mediatransportutil/codec package, no functional change (#4497)
680703f2 Include reception reoprts in receiver report callback. (#4496)
f51798bc Fix publish-only limitations being incorrectly applied to receivers (#4495)
a002337d Legacy TrackInfo.Simulcast flag. (#4493)
af1dcc88 Add CloseWithReason to agent SignalConn interface (#4492)
d7c2daf1 report all simulcast layers (#4491)
c1ad2b22 Misc optimisations. (#4490)
19b9e8c0 Additional data tracks logging (#4489)
743d9c8b add support for client capabilities (#4461)
fc47e478 Close peer connection unconditionally to unblock set local/remote (#4485)
639406eb Update module github.com/pion/ice/v4 to v4.2.3 (#4481)
dc6b7505 reduce some heap use in packet path (#4478)
f3b80b28 fix: wrap IPv6 addresses in brackets in UDP TURN URLs (RFC 3986) (#4476)
3a7f2628 Turn off transceiver re-use on Safari. (#4474)
d84f3d7a add more types to signum (#4473)
701a37c2 Convert sort.Slice -> slices.SortFunc (#4472)
85be9d70 Avoid stream allocator event data cast to interface and back. (#4471)
b43685e8 Keep a shadow copy of tracks for use by different stream allocator state (#4470)
27c2b149 Consolidate RTCP packets and do RTCP callback outside lock. (#4469)
31083307 do not log data track stats if not started (#4468)
9ee06635 feat(pion/ice): replace deprecated NAT1To1 with SetAddressRewriteRules (#4466)
8ccad68d Release v1.11.0 (#4459)
dbf5cf61 Store concrete ICE candidate for remote candidates. (#4458)
2a04bc3c fix publisher frame count reporting for simulcast streams (#4457)
1d804737 fix: limit join request and WHIP request body to http.DefaultMaxHeaderBytes (#4450)
3cfb71e7 Use Muted in TrackInfo to propagated published track muted. (#4453)
69aa9479 Some drive-by clean up (#4452)
6c81f678 Add subscriber stream start event notification (#4449)
ce1bf47b Revert "fix: ensure num_participants is accurate in webhook events (#4265) (#…" (#4448)
cdb0769c fix: ensure num_participants is accurate in webhook events (#4265) (#4422)
c91e79af Switch to stdlib maps, slices (#4445)
ea7b9c6f Update module github.com/livekit/protocol to v1.45.3 (#4435)
97378368 Update go deps (major) (#3179)
d6aef547 Update go deps (#3862)
afc9feae Update github workflows (#4331)
4b385612 chore: pin GH commits and switch to golangci-lint (#4444)
2974ba87 Unsubscribe from data track on close (#4443)
5dc4e90d Apply IPFilter when get local ip (#4440)
88c77dc6 compute agent dispatch affinity from target load (#4442)
8fe99377 Log join duration. (#4433)
0a503a57 Add `Close` method for UpDataTrackManager and call it on participant (#4432)
55912dff Add some simple data track stats (#4431)
050909e6 Enable data tracks by default. (#4429)
72c7e65c chore: log API key during worker registration (#4428)
8a67dd1b Do not close publisher peer connection to aid migration. (#4427)
91e90c10 Add some more logging around migration. (#4426)
c6ddc879 isExpectedToResume is based on whether flushing or not. (#4425)
7d06cfca Keep subscription synchronous when publisher is expected to resume. (#4424)
934f8598 Clean up data track observers on unsubscribe. (#4421)
9674ac48 Cleaning up some logs and standardising log frequency. (#4420)
7b925304 Drop time inverted packets in RED -> Opus conversion. (#4418)
4d8d232a ensure participant init is correctly serialized for logging (#4417)
4fe80877 Log time inversion between incoming packets (#4415)
248d7394 Guard against timestamp inversion in RED -> Opus conversion. (#4414)
9ab8c1d5 clear track notifier observers on subscription teardown (#4413)
397cd09a Embedded turn test (#4412)
56326654 Prepare release v1.10.1 (#4408)
e9b113c8 Make the TURN bind address configurable and allow for multiple addresses. (#4315)
4bc5e6bb Address malformed H264/H265 parsing issues. (#4407)
77a0a4fc AV1 parser overflow fix. (#4405)
ff7fd7ed feat(agent-dispatch): add job restart policy (#4401)
34bd1e08 do not log roll over for padding only packets (#4396)
13d02ee9 add deadline to dtls connect context (#4395)
9055a349 Path check helpers (#4392)
1f1eeb68 Fallback to servicestore if rpc is unavailable (#4391)
59e9bb41 Fix TURN server URL (#4389)
9e0a7e54 Close both peer connections to aid migration. (#4382)
9474c807 route participant reads through PSRPC instead of Redis (#4387)
a5333a86 add packet trailer stripping support (#4361)
0e3d765d Release v1.10.0 (#4380)
bc3aeaf3 Update grpc to address CVE-2026-33186 (#4381)
8cdd6f4c Replace deprecated io/ioutil with io in whipservice (#4375)
89410df7 handle AGENT_ERROR disconnect reason (#4339)
8f984c77 Fix repair stream ID reporting for RTX pairing. (#4369)
cdfaacfc Restart nacker on OOB sequence number restart. (#4368)
4a9e0045 Update protocol. (#4367)
750d5904 Add API to restart lite stats. (#4366)
c8bb2578 Rename log field "pID" to "participantID" for consistency (#4365)
77fc74a7 Do not block all ext ID determination on stream allocator listener (#4364)
90a46fab Do not kick off migration of closed participant (#4363)
5dc2e7b1 Switch data track extension to 1-byte ID/length. (#4362)
7323ad02 Sample data send error logging. (#4358)
0d34e455 Add option to not re-use transceiver in e2ee. (#4356)
95225ff2 don't require media section for dual peerconnection mode (#4354)
e9639538 Refine ipv6 support (#4352)
b34b0472 Add StopEgress function to the EgressLauncher interface (#4353)
69235ed2 update readme (#4340)
db1a8046 defensive check for peer connection instance (#4350)
cb7dc2d0 TEL-405: support originating calls from custom domains (#4349)
7eaaaada Mark last run of grow bucket outside goroutine. (#4348)
caa47522 Add option to require media sections when participant joining (#4347)
087050d1 Release v1.9.12 (#4346)
493e87df Fix SIP client timeout. (#4345)
52c28a93 Log a bit more details of out-of-order TWCC feedback report. (#4343)
516aeabf Use ParticipantTelemetryListener of LocalParticipant. (#4342)
b3510565 Exclude ice restart case from offer answer id mismatch warning (#4341)
303657bc feat: make INSTALL_PATH overridable in install script (#3954)
9d418689 Send participant left event after track unpublished for moved (#4334)
bab91868 do not discount packets lost on duplicate packets (#4333)
939794cf mark + restart (#4329)
75f9c462 Add debug for receiver restart. (#4328)
74891f30 Protect against incorrect temporal layer. (#4327)
f51b2732 Update pion/webrtc and deps to update dtls (#4326)
b81bac0e Key telemetry stats worker using combination of roomID, participantID  (#4323)
2d40449f Update self-hosting deployment documentation link (#4312)
03e90dd7 Fix for some CodeQL reported issues (#4314)
77c858f0 Log stats worker when it not closable. (#4313)
a6035212 ESP32 client info (#4267)
478e486a Copilot suggested improvement to Github Actions permissions (#4310)
cbd2f82d Copilot suggested improvement to Github Actions permissions (#4311)
a9b8d40d Publish is always on publisher peer connection. (#4307)
8ae56406 generate & log egressID for start egress request (#4303)
35d7ef88 Avoid alloc in RTPStatsReceiver.Update (#4302)
bb744916 More optimisation in RTPStats module. (#4298)
cefd5da9 Optimise some bits in rtpstats_receiver (#4297)
52a4b848 Read client protocol from query param (#4294)
195b17f6 Populate client_protocol field in ParticipantInfo (#4293)
370e0a4d Set up audio config in audio level module when config is updated. (#4290)
f3e9b688 Do not increase max expected layer on track info update. (#4285)
a9849340 Avoid logger data race. (#4284)
97016627 Do not hold lock when creating buffer (#4283)
6b68e3d5 Create buffer if needed when a PLI is requested. (#4282)
3cca7180 use separate allocation for signal stats telemetry guard (#4281)
d2bae34d refresh telemetry guard on participant move (#4280)
333f0349 clear reference guard when resetting signal stats (#4279)
d1bab17b Add session duration and participant kind to closing log. (#4277)
1e689e1a Reducing some info level logs. (#4274)
88facc02 adds a test to ensure agent worker errors cause disconnection (#4273)
76a41a7a Generate config flags (#4268)
700e1788 require participant broadcast when metadata/attributes are set in token (#4266)
b61799ec Ignore parse addr error when add remote candidate (#4264)
01bd966f Add silent frame for pcmu/a (#4258)
0c33b8c6 chore: move codecs/mime stuff to protocol (#4255)
165c1735 Update livekit protocol to v1.44.0 (#4254)
343f12b8 Wrapping SIP errors for invalid argument and not found (#4253)
aea044c5 Defer setting clock rate in RTPStats module till codec is bound. (#4250)
d9f716c1 FIx receiver restart race (#4248)
0508ee9f remove copy/paste left over line (#4246)
40408407 Release v1.9.11 (#4245)
f8675507 Do not remove participant from cache on disconnect. (#4241)
a35a6ae7 Add participant option for data track auto-subscribe. (#4240)
07572511 Wrapping the invalid request errors for CreateSipParticipant (#4239)
843d8c3e Update Pion transport package. (#4237)
a05690d2 Changing field naming of data track packet (#4235)
c4015008 Clear participant version cache on disconnect (#4234)
18db4ec1 Log modified timeout of API context. (#4232)
ac20ccda Log timeout in API. (#4231)
541a7d6c Change some logs to debugw (#4229)
52ab3374 Return on SDP fragment read error. (#4228)
7fae5ac9 Do not restart receiver on codec change mid-session. (#4225)
dafdc36e Add option to force simuclast codec. (#4226)
0a7dd40b Use only layer 0 for SVC codecs. (#4224)
4ec0f8f4 Support OpenTelemetry tracing. Add Jaeger support. (#4222)
80ba93fa Do NACK updates as soon as flow state is processed. (#4221)
4405afe2 Use atomic pointer and return interface from RED transformer constructors (#4220)
b649c2fe Remove method not needed from REDTransformer. (#4219)
f0080f35 Remove enable arrival time forwarding method. (#4217)
335f4c33 Swap result sink atomically rather than closing and setting. (#4216)
46651c19 Release v1.9.10 (#4214)
08ac4ecd Support preserving external supplied time. (#4212)
f6efccce report video size from media data for whip (#4211)
d92f6a79 return iceservers for whip (#4210)
1a4758ed Skip restart callback if external. (#4208)
dde4fb49 configurable dependency descriptor restart (#4207)
08793bea Use active at time to check for track not bound timeout. (#4206)
3606ce54 Do not warn about track not bound if participant is not ready. (#4205)
b8ddd0f9 Taking interface{} -> any modernize bits (#4204)
b91cd2e4 Rework receiver restart. (#4202)
bb00c864 Restart API on receiver. (#4200)
25ece1e9 Minor refactor in buffer base and audio level (#4198)
599002f8 ignore PLI requests for non-video (#4196)
2510b946 Taking a bunch of go modernize suggestions. (#4194)
ed8e6afc Handle repair SSRC of simulcast tracks during migration. (#4193)
c6bf7a27 Fix logging key and other clean up around stream restart. (#4192)
3cb9abb6 Update pion/webrtc to v4.2.1 (#4191)
8b0efb8c Resolve RTX pair via OnTrack also. (#4190)
381bce03 Return extended sequence number only and not packet. (#4189)
6bcbf54e Always instantiate nacker when using out-of-band sequence numbers. (#4187)
e71184de Store buffer after creating it.  (#4186)
7c8ea115 Refactor receiver and buffer into Base and higher layer.  (#4185)
4104b827 update protocol (#4183)
cd99fec2 Make new path for signalling v1.5 support. (#4180)
32cd0370 Flush the ext packets on restart/close and release packets. (#4179)
1df1316b Move OnDataTrackMessage to participant listener to support replay. (#4178)
e7601251 Make data message naming a bit more consistent. (#4177)
a04e566d Use published track for model access in data down track. (#4176)
47c86be1 Add support for TURN static auth secret credentials (#3796)
24559a28 chore(deps): update github workflows to v6 (#3866)
0a824386 add explicit room exists servicestore op (#4175)
39bd077d Release v1.9.9 (#4174)
cbb2c617 Publish/Unpublish counter match. (#4173)
fb849edc Minor clean up (#4172)
c28e5e45 fix(kindToProto): update protocol (#4171)
47324abd Drop run away receiver reports. (#4170)
462ec324 prevent uint overflow setting packet not found count (#4169)
5c841b8e Some logging changes. (#4168)
2f2d0a57 skip lost sequence number ranges in getIntervalStats (#4166)
898ebe05 clean up manual roomservice log redaction (#4165)
3e417253 move delete to oss service store (#4164)
5964efbb Ensure subscribe data track handles are unique (#4162)
a26c4830 Add support for RTP stream restart. (#4161)
386f0b38 fix typo in clearing index when removing track from room track manager (#4158)
0abfb251 deregister observability function when participant is closed (#4157)
97aba5e7 Consistently undo update to sequence number and timestamp when the (#4156)
2317c295 Fix panic while removing track from room track manager. (#4153)
a0a28ac5 Avoid duplicate track add to room track manager.  (#4152)
f01008f8 Revert telemetry stats worker wait configuration. (#4151)
ca4b56d2 Handle case of sequence number jump just after start. (#4150)
97099cae Configurable telemetry stats worker clean up wait. (#4148)
d7db7cb3 chore: fix a large number of spelling issues (#4147)
cadf2ad9 Release v1.9.8 (#4145)
498304cd defensive nil check (#4144)
20f6a497 Store ddParser in atomic.Pointer (#4143)
037cb906 release ext packet if patching fails (#4142)
dd598ef2 Release ExtPacket if dependency descriptor or other parsing fails (#4141)
35b0e2bc update webrtc to 4.1.8 to pick up DTLS fingerprint check during handshake (#4140)
1c1a836c Mark RTCP buffer Write as noinline. (#4138)
ea9b2177 protocol deps to get inactive file adjusted memory usage. (#4137)
64f3d1e9 switch participant callbacks to room to listener interface (#4136)
c6e6c021 add debug metric for tracking references (#4134)
a30c79fa Use isEnding to indicate if down track could be resumed. (#4132)
8c241ecf Fix RTCP reader leak in DownTrack. (#4131)
3eef869a Do not pause rid in SDP (#4129)
8e01e595 Release 1.9.7 (#4128)
7c1a0fab Fix concurrent map access. (#4127)
14446b1c Let participant close remove the published tracks. (#4125)
fa0633aa move utils.WrapAround to mediatransportutil (#4124)
f8706cd4 Update pion/ice to stop gather first on close (#4123)
7954748d Data tracks (#4089)
7158d983 log bucket growth (#4122)
04b35eb6 Release v1.9.6 (#4121)
ebdcead5 Update mediatransportutil to get bucket packet size limit. (#4120)
411b09f6 Release v1.9.5 (#4119)
8dcf235a Update pion/ice - attempt to address tcp packet conn close hang (#4116)
64c65143 Update mediatransportutil (#4115)
0a2943bb Clean up bits added to debug peer connection close hang. (#4114)
9c483a69 Use released version v1.8.41 of pion/sctp (#4113)
35c79a57 Update SCTP hash, had the wrong one in previous PR (#4111)
e0fbbef1 Update pion/sctp with RFC9260 revert (#4110)
f3c80917 Try SCTP with read deadline to unblock abort. (#4109)
bd5382da Splitting transport close timeout logs. (#4108)
6d4154b8 Update pion/ice. (#4107)
a6418ae2 Log more peer conenction state on close timeout. (#4105)
06d99974 Check for cancel on unsubscription/source track going away. (#4104)
7f10e18b Record join/publish/subscribe cancellations. (#4102)
40293632 Clear stereo=1 if stereo is not enabled. (#4101)
70f6def3 Add checks for participant and sub-components close. (#4100)
ffbabcc7 Switch forwarding latency log to Debugw (#4098)
27d82a72 Fix "address" typo in transport logs (addddress → address) (#4097)
37a06821 logger proto redaction. (#4090)
54cf7d46 Control latency of lossy data channel (#4088)
5175c1af Lock x/tools at 0.37.0 (#4085)
d510fff1 Downgrade x/tools to be able to make a release (#4084)
c3ea5890 Prepare release v1.9.4. (#4083)
3a128e61 protocol bump for SIP error mapping and validation (#4081)
c3964ba2 Use sync.Pool for objects in packet path. (#4066)
f8b994d4 Forwarding latency measurement tweaks. (#4080)
f4929f09 Revert "Revert pion/transpor to v3.0.8 (#4073)" (#4074)
a04d9c48 Revert pion/transpor to v3.0.8 (#4073)
2d5054ad kind details for connector (#4072)
a272e28a Log raeson for subscriber not being to determine codec. (#4071)
b9b4eec9 Update pion/transport to v3.1.1 (#4070)
b23d093c update protocol (#4069)
4ce07bed Higher resolution forwarding latency histogram. (#4067)
858db7ab fix(deps): update module github.com/livekit/protocol to v1.43.0 (#4015)
1dc9b8fc Use buffered indicator to exclude from forwarding latency.  (#4062)
f117ee51 Track start up delay.  (#4061)
4872f205 Return write count from WriteRTP. (#4059)
d0ba46b4 Log write count atomic. (#4057)
ae5fb7e8 Add packet to forwarding stats only if packet is forwarded. (#4056)
f6909192 Update PsRPC to get redis pipeliner implementation. (#4055)
ca3c507b Prevent invalid track access while peer connection is shutting down. (#4054)
9ca6ee00 Use replace so that x/tools does not get overridden (#4048)
b9323eab chore(deps): downgrade x/tools for counterfeiter (#4047)
2f1e6c36 Prep release v1.9.3 (#4046)
9d5c351d Fix prom units for forwarding latency/jitter. (#4045)
e183657c Add prom histogram for forwarding latency and jitter. (#4044)
1eefeb30 Enable AbsCaptureTimeURI in RTC configuration (#4043)
075a7576 Use simulcast codec as default policy for audio track (#4040)
c264b504 Don't warn 0 payload type for PCMU (#4039)
32fc3525 Broadcast cond var on RTX write. (#4038)
061eb8b4 AddDownTrack to regressed codec after restarting forwarder. (#4037)
c87eb8ed fix: add missing Unlock() in AddReceiver (#4036)
70444924 if RingingTimeout is provided, deadline should be set to that timeout. (#4018)
ab906d71 Prevent leakage of previous codec after codec regression. (#4035)
79b03f97 Log queueing latency when encountering high forwarding latency (#4034)
29117b14 set max layer in allocation (#4033)
15b19ccd Remove ~ from rid which indicates disabled layer to get the actual rid (#4032)
34e16a87 Check more conditions for opportunistic alloc. (#4031)
81fbd355 Use the optimal allocation function for opportunistic allocation. (#4030)
a2ce73e0 Do not bind buffer if codec is invalid. (#4028)
cef6fdb7 Correct direction for request/response for prom counters. (#4027)
5042c06c Use rtp converter from protocol/utils/rtputil (#4020)
5a426d15 Use rtp converter from protocol/utils (#4019)
35fb8877 feat: use env var for GOARCH (#4012)
c0397696 Issue #1 only: Fix spatial layer initialization in Forwarder (#4003)
2afbf0e8 Some golang modernisation bits. (#4016)
484f784a Prepare release v1.9.2 (#4011)
ad074ed2 counterfeiter needs an older version of x/tools (#4009)
e63e8b6f Include mid -> trackID in both SDP offer and answer. (#4007)
781dfede Do not call receiver methods under settings lock. (#4006)
69ff25a0 Use answer with mid -> trackID mapping when in single peer connection (#4005)
fe912acf Update pion/webrtc to prevent GetStats panic. (#4004)
7930dcde Do not try to read stats from peer connection after close. (#4002)
ca0d5ee9 Count request/response packets on both client and server side. (#4001)
dd62eb00 Resort to full search for requested quality is not available. (#4000)
f6ca82d1 Revert to using silence packets for audio dummy start. (#3999)
0e2c59c8 Sort codec layers when adding track (#3998)
100bb46a Adding ProviderInfo to GetSIPTrunkAuthenticationResponse (#3993)
a8d4df66 "Power of Two Random Choices" option for node selection  (#3785)
a20bbe34 Log RPC details. (#3991)
158496bc Increment RTP timestamp on padding when using dummy start. (#3989)
4f6ed65d Limit check to red + opus when looking for primary codec match. (#3988)
a87f6c4b Allow passing inline trunk for outbound calls. (#3987)
bf06596f Support Opus mixed with RED when encrypted. (#3986)
ea208a1c Add encryption datapacket type (#3869)
2a6adbe8 Use padding only packets for dummy start of audio. (#3984)
be018f97 Provide the InputVideo/AudioState to Ingress in WHIPRTCConnectionNotify (#3982)
146bd969 Do not panic of redis is not configured (#3981)
01337ba7 Do not start forawarding on out-of-order packet. (#3985)
c7f625d6 Do not force codec regression between opus and red. (#3980)
3bd20ddb Revert unintentional change to not handle transport fallback on (#3970)
89a2f46c Update deps to fix redis issue when 1 cluster address is provided (#3969)
060719d1 add config for user data recording (#3966)
b3ee219c fix stats worker closed condition (#3965)
3d737031 add idempotent reference count to telemetry stats worker (#3964)
735c663a Update protocol for EventKey helper. (#3963)
646b9de8 Add node_ip to config-sample.yaml (#3960)
0bf7b178 avoid logging on small values (#3958)
00ff2ab9 Adjust for hold time when fowarding RTCP report. (#3956)
e180be06 short circuit participant broadcast filter in livestream mode (#3955)
bfba6fee Adjust stream allocator ping interval based on state. (#3951)
3837006b Revert "Switch ops queue a singly linked list. (#3949)" (#3950)
990c5faf feat: server rpc apis (#3904)
80b11662 Switch ops queue a singly linked list. (#3949)
56ee2328 handle terminated job requests (#3948)
49f9b9c8 Flush stats when there are no packets. (#3947)
e6a3df1e ForwarStats.GetStats needs to be public (#3946)
824d116b Tweaks tresholds for logging high forwarding latency/jitter. (#3945)
408492e0 Log some information around high forwarding latency. (#3944)
6a41fae5 Use microseconds for forwarding stats. (#3943)
856e0871 mediatransportutil to log local address when validating external IP (#3942)
40101cf7 Update protocol for SipCreateParticipant (#3939)
b07e7a38 Use difference in key frame counter to stop seeder. (#3936)
d7f92878 Avoid matching on empty track id. (#3937)
56fb2885 Do DD restart only if DD structure is present. (#3935)
86facce9 More debugging of DD jump (#3934)
6058a3f6 Add debugging from DD frame number wrap around. (#3933)
dc6825c0 mediatransportutil crash fix for logging local address (#3930)
d6f0588f Update mediatransportutil to log external IP found via STUN. (#3929)
2c30a064 Fix dynacast subscriber node clearing on move participant. (#3926)
6489237e Simulcast audio fixes (#3925)
9f0ab870 Wait for `SetRemoteDescription` before configuring senders. (#3924)
df6c26db Subscrbed audio codecs - update from remote nodes. (#3921)
798fa761 Support simulcasting of audio (#3920)
f4a06cf0 Clean code as there is no oss sweeper for ingress (#3918)
5f561b4f Include agent_name as a participant attribute (#3914)
782a35e8 update protocol for psrpc (#3915)
eee2001a Set publisher codec preferences after setting remote description (#3913)
fc995533 add incoming request id to request response message (#3912)
76645fad Rpcs for ingress proxy WHIP (#3911)
991a4a4f Refactor subscribedTrack + mediaTrackSubscriptions. (#3908)
e16b3ba9 Use gzip reader pool (#3903)
17c34921 update protocol for sip api change (#3902)
2f43a575 Release candidate for v1.9.1 (#3899)
07c40cf3 Use `RequestResponse` to report protocol handling errors (#3895)
98352fd0 Prevent race in determining BWE type. (#3891)
f7291fda Do not send both asb-send-time and twcc. (#3890)
21b42fa6 Do not advertise NACK for RED. (#3889)
6633bf93 Use departure timeout from room preset. (#3888)
38f7906e Handle migration better in single peer connection case. (#3886)
5026de2b handle frame number wrap back in svc (#3885)
091e3c13 Revert to using answer for migration case. (#3884)
2aeadf14 init ua parser once (#3883)
998a9f94 Switch known rids from 012 -> 210, used by OBS. (#3882)
890fd942 Single peer connection mode (#3873)
bfe98eaa fix: ensure the participant kind is set on refresh tokens (#3881)
8d270e2a chunk room updates (#3880)
b4e146c5 update mediatransport util for ice port 3478 (#3877)
dc3a7753 Fix timeout handing in StopEgress (#3876)
d62336e1 Remove unnecessary check (#3870)
c58e5d23 Update golang Docker tag to v1.25 (#3864)
98d577ee Update module github.com/livekit/protocol to v1.40.0 (#3865)
afbf541e Update pion deps (#3863)
b660c3b5 Extract video size from media stream (#3856)
456b8709 Fix missed unlock (#3861)
d500806e Handle no codecs in track info. (#3859)
11b240d6 Log track settings more. (#3853)
1aa0f963 Log signal messages on media node. (#3852)
b182d07b Log signal messages as debug. (#3851)
a370bb20 Support G.711 A-law and U-law (#3849)
fa5f4ef3 Populate SDP cid in track info when available. (#3845)
eed27885 Send `participant_connection_aborted` when participant session is closed (#3848)
61e59346 Update go deps (#3439)
1b228913 Support video layer mode from client and make most of the code mime aware (#3843)
f2da4444 Support per simulcast codec layers. (#3840)
f275f592 handle SyncState in join request (#3839)
5d44cf6d Use wrapped join request to be able to support compressed and uncompressed. (#3838)
5ca16264 Support join request as proto + base64 encoded query param (#3836)
7dea1012 Clean up missed v2 pieces (#3837)
34a49130 Delete v2 signalling (#3835)
1fe33716 Fix: RingingTimeout was being skipped for transferParticipant (#3831)
5751692a deps (#3829)
db4bc127 Get to the point of connecting publisher PC and using it for async signalling (#3822)
5e483e75 update readme (#3809)
e3155b14 Get to the point of establishing subscriber peer connection. (#3821)
a7ce1382 HTTP DELETE of participant session (#3819)
01de0e36 Do not send leave if nil (to older clients) (#3817)
10103449 Add country label to edge prom stats. (#3816)
68387b41 Minor tweak to keep RPC type at service level. (#3815)
a75295fc More v2 signalling changes (#3814)
b20db94d Validation end point for v2 signalling. (#3811)
f2f595f4 update readme (#3808)
fffc2ac0 Use signalling utils from protocol (#3807)
f5fc82d3 Filling out messages unlikely to change in v2. (#3806)
1c99b9ad Split signal segmenter and reassembler. (#3805)
0a1bfd30 Signal handling interfaces and participant specific HTTP PATCH. (#3804)
7837c8e5 starting signaller interface (#3802)
18ce5244 Handle Metadata field from RoomConfig (#3798)
2a6a9b8a Grouping all signal messages into participant_signal. (#3801)
078c01fa Signal v2: envelope and fragments as wire message format. (#3800)
b9a44c3f Signalling V2 protocol implementation start (#3794)
ba702a53 forward agent id to job state (#3786)
1f31d430 Map ErrNoResponse to ErrRequestTimedOut in StopEgress to avoid returning 503 (#3788)
51bbe8c5 Set participant active when peerconnection connected (#3790)
40028dc3 Normalize known rids. (#3779)
ddd92329 Return default layer for invalid rid + track info combination. (#3778)
8c033ce9 Enable H265 by default (#3773)
7678e087 Set rids for all codecs. (#3772)
5d636acf Limit taking rids from SDP only in WHIP path. (#3771)
4d09e5b5 Log SDP rids to understand the mapping better. (#3770)
c69f1aae Revert "Temporary change: use pre-defined rids" (#3769)
8197438e bounds check layer index (#3768)
d11da5f5 Temporary change: use pre-defined rids (#3767)
cb4da533 fix(deps): update module github.com/livekit/protocol to v1.39.3 (#3733)
d6d2b6d8 feat(cli-flags): add option for cpu profiling (#3765)
9fc4ddbe ClearAllReceivers interface is used to pause relay tracks. (#3761)
1216113b Do not need to just clean up receivers. Remove that interface. (#3760)
ef6c38ce Log previous allocation to see changes. (#3759)
01bf9685 SVC with RID -> spatial layer mapping (#3754)
c481396f offer could be nil when migrating. (#3752)
8c2fc0bc Fix svc encoding for chrome mobile on iOS (#3751)
e467daa0 move egress roomID load to launcher (#3748)
3783ebb3 feat(cli): update to urfave/cli/v3 (#3745)
03d3fcab Fix data packet ParticipantIdentity override logic in participant.go (#3735)
068b4366 reuse compiled client config scripts (#3743)
e754a860 return error when moving egree/agent participant (#3741)
7542cf07 remove unused code (#3740)
9d569e2f Take ClientInfo from request. (#3738)
80774263 chore: set workerid on job creation (#3737)
5549ab55 Revert clearing RIDs. (#3732)
ae967313 Clear rids if not present in SDP. (#3731)
0e033907 Return highest available layer if requested quality is higher than (#3729)
9ce737db Add log for dropping out of order reliable message (#3728)
1b95e818 Don't check bindState on downtrack.Bind (#3726)
670f927f Set and use rid/spatial layer in TrackInfo. (#3724)
a9e29116 Add Id to SDP signalling messages. (#3722)
4ec828ce Fix bug with SDP rid, clear only overflow. (#3723)
8f6c3a9b Clear rids from default for layers not published. (#3721)
ce07740e Add simulcast support for WHIP. (#3719)
e98fb94f Create client config manager in room manager constructor. (#3718)
fdf9b852 e2e reliability for data channel (#3716)
35dda8ea swap pub/sub track metrics (#3717)
1d9a4366 Do not require create permission for WHIP participant. (#3715)
e0aea17a Flush stats on close (#3713)
630aa7d9 implement observability for room metrics (#3712)
e7f0294e remove unused ws signal read loop (#3709)
77f70b18 for real, pick up protocol change for webhooks queue length bucnkets (#3707)
7b180646 protocol dep for webhook stats buckets (#3706)
b0ab95ba warn about credentials when used in tokens (#3705)
a72ce30f Small changes to add/use helper functions for length checks. (#3704)
425f6bb3 Allow passing extra attributes to RTC endpoint. (#3693)
758e1762 Add a trend check before declaring joint queuing region. (#3701)
fe81e411 Adds Devin to readme so it auto updates DeepWiki weekly (#3699)
09dede35 version bump v1.9.0 (#3698)
fc867c5b Webhook prom stats (#3697)
0e17916f Do not use Redis pipeline for SIP delete. Fixes Redis clustering support. (#3694)
1b760393 WHIP support. (#3692)
e4f7d81b add client ip to agent worker registration (#3675)
6b849a87 update mediatransportutil for sctp congestion control (#3673)
83b189b0 Add ServerInfo to ReconnectResponse (#3671)
5f87a35b Prevent operating on swapped out map. (#3670)
13b55a80 move agent token (#3669)
c9385edd handle agent worker jwt (#3668)
3b359d8b Use logger resolver reset to reset contexts. (#3665)
dbb70e0f Fix dynacast quality for moving out tracks (#3664)
0a5f3c2a resolve new room name logger earlier when moving participant (#3662)
2df05517 Revert unbound transceiver stop. (#3661)
5172af15 ~Send initial participant update only after a participant becomes active.~ - General clean up (#3655)
5d4d86f8 protocol to pick resolver values replace (#3659)
7f8e6323 Send self participant update immediately. (#3656)
11630878 Use unordered for lossy data channel. (#3653)
aee34ffe log request for agent dispatch api (#3650)
793b383a Add Moving participant to another room (#3648)
2fff36cb Stub MoveParticipant so that cloud can include the latest protocol. (#3646)
d4ab1142 Redact address (#3643)
b83190a3 protocol update to fix memory stats path (#3642)
a1f4e88e Update protocol to latest, got bit by tag (#3641)
8d3902af Protocol to pick up cgroups v2 memory path fix (#3640)
2f002388 Use participant close reason in remove. (#3639)
58822c26 Include clientInfo in connectivity logs. (#3638)
6d6393a6 Take AudioFeatures from AddTrack. (#3635)
08670412 Limit buffer queue before Bind. (#3634)
9f5bc9b9 Avoid synthesising duplicate feature. (#3632)
847239c3 Disable vp9 for safari 18.4 (#3631)
f69ab680 Populate the sender identity when translating to user packet. (#3628)
e1490558 Forward data between WHIP client and non-WHIP client (#3627)
6739e7bc Broadcast inside lock (#3626)
f24152b4 Call Broadcast in lock scope. (#3625)
b760918a Use logger from request context. (#3623)
34a2e2c1 Check for multiple layers for managed track. (#3622)
4955ebe4 Forward transfer headers to internal request (#3615)
d9ee9214 Set up RTX for WHIP publish (#3619)
d8cf5439 Determine TURN connection type and no fallback for TURN/TLS. (#3612)
d0d212fd Fix WHIP ICE restart. (#3616)
5e7f8a12 Update mediatransportutil for max sctp message (65K) (#3611)
28dfac14 Use exported GetEgressNotifyOptions (#3604)
75236bef protocol update to fix IPv6 SDP fragment parsing (#3603)
2130980d Add basic video support to WHIP. (#3602)
e5cbb227 Allow specifying extra webhooks with egress requests (#3597)
7e16106a Add OnSubscirberReady callback on LocalParticipant. (#3600)
5c2d96b9 Check DestinationRoom of VideoGrant for participant forwarding (#3599)
2e236a19 Revert participant state ACTIVE change. (#3598)
35ac5f56 Add support for WHIP ICE Trickle/Restart. (#3596)
ec2dff96 Fix SIP updates when replacing slices. (#3592)
e24fe77b map PEER_CONNECTION_DISCONNECTED -> CONNECTION_TIMEOUT (#3591)
6ee6eb43 Do not drop audio codecs (#3590)
68357ba6 List audio codecs after video codecs. (#3589)
05a891ff Fix rule (had an extra bracket) (#3588)
d7c41091 Exclude RED from enabled codecs for Flutter + 2.4.2 + Android. (#3587)
ee08aede skip out of order participant state updates (#3583)
15a8d9a2 Break track published fuse when there are no tracks (#3581)
35f83c51 Replace Promise with Fuse. (#3580)
07fe9b72 Prevent migration race. (#3579)
ac8082ff Use older SDP module to accommodate bad SDP. (#3578)
1c8307c7 Use cgroup for memstats. (#3573)
e9be0fca log SDP offer on error (#3577)
3238ab8d Calculate rates for memory used and total. (#3570)
d08487bf Unlabeled (pass through) data channels. (#3567)
52ce18d5 fix: revert recent changes to determine simulcast from sdp (#3565)
cdfbb106 Audio uses signal SignalCid and SdpCid. (#3564)
ed5e2f16 Keep simulcast information tied to receiver. (#3563)
ad010cfc chore(logs): log VLS type for VP9/AV1 (#3561)
8cc17f8f Rework node stats a bit. (#3555)
15f56551 fix(video): determine svc/simulcast from SDP for advanced codecs (#3549)
2b6a46f4 Handle `prefer_regression` for backup codec (#3554)
b0abb0ae Add option to use different pacer with send side bwe. (#3552)
26822b6b ParseUsername utility for TURN user name. (#3547)
55909ed7 log the initial join response (#3546)
97fcb82a Fix: Return NotFoundErr instead of Unavailable when the participant does not exist in UpdateParticipant. (#3543)
75d0e18e Implement SIP update API. (#3141)
e118aff1 Fire track subscribed when the subscriber connected (#3540)
13417c01 Send mute event only on change (#3537)
7f4c4597 Stubs for SIP update API. (#3533)
fe673bb2 Send regressed codec upstream stats to analytics. (#3532)
8eb81388 Use a generation to counter to stop key frame seeder on codec change (#3531)
188470a2 Do not accept unsupported track type in AddTrack (#3530)
507fc9cf Do not instantiate 0 sized sequencer. (#3529)
20bddfea Clean up published track on participant removal. (#3527)
65d8aa28 Handle subscribe race with track close better. (#3526)
a6cb00b3 Reduce seeder duration to 30s and also do not force send PLI. (#3525)
c8233205 Add a key frame seeder in up track. (#3524)
0f61ff3a Remove redundant log (#3523)
7685cd25 Log ParticipantInit on signal start to get a picture of join params (#3522)
ac9e62ef add server agent load threshold config (#3520)
cd5d32f0 Add pID and connID to log context to make it easier to search using pID. (#3518)
2d9aa6dd Update api call info method (#3515)
b3779a90 WebHookConfig (#3517)
6121b9af Check ForwardParticipant room name (#3514)
9a7c9442 mediatransportutil update (#3511)
50ab47c1 Log packet drops/forward. (#3510)
139d1b13 Add ForwardParticipant method to room service (#3507)
6c04909f Use atomic to store codec. (#3505)
7f6afe05 Prevent bind lock deadlock on muted. (#3504)
48063df5 load mime type before calling writeBlankFrameRTP (#3502)
d2e6cd15 Do not bind lock across flush which could take time (#3501)
47896f50 Update protocol and IO service. (#3499)
1dc42eef Bump github.com/go-jose/go-jose/v3 from 3.0.3 to 3.0.4 (#3497)
3a35cbc4 Log migration complete only when coming from sync (#3496)
c2f17a10 refactor: using slices.Contains to simplify the code (#3495)
01e51dbd fix: fix the wrong error return value (#3493)
ff9115b2 Disable dd parser for vp8 if extension is not found (#3492)
c3e06f05 Do not attempt to create objects for URL ingresses as the ingress service will do so (#3491)
f0edfbba Fix receiver rtt/jitter. (#3487)
05dfd30d Take RTT and jitter from receiver view while reporting track stats for (#3483)
04ed5683 Don't issue TrackPublished/Unpublished event on migrated track (#3482)
1cffe30c Use a RED transformer to consolidate both RED -> Opus OR Opus -> RED (#3481)
591888f7 Fix missing RTCP sender report when forwarding RED as Opus. (#3480)

git-subtree-dir: livekit-server
git-subtree-split: 46c4309554d37d23ee8da88a8a7e02a68fba09c1
This commit is contained in:
2026-06-25 23:54:33 +09:00
parent 0da97ebd21
commit 6bd7fac875
291 changed files with 37631 additions and 15381 deletions
+30 -10
View File
@@ -15,6 +15,7 @@
package rtc
import (
"slices"
"strconv"
"strings"
@@ -45,11 +46,15 @@ func (c ClientInfo) isAndroid() bool {
return c.ClientInfo != nil && strings.EqualFold(c.ClientInfo.Os, "android")
}
func (c ClientInfo) isOBS() bool {
return c.ClientInfo != nil && strings.Contains(c.ClientInfo.Browser, "OBS")
}
func (c ClientInfo) SupportsAudioRED() bool {
return !c.isFirefox() && !c.isSafari()
}
func (c ClientInfo) SupportPrflxOverRelay() bool {
func (c ClientInfo) SupportsPrflxOverRelay() bool {
return !c.isFirefox()
}
@@ -93,21 +98,36 @@ func (c ClientInfo) SupportsChangeRTPSenderEncodingActive() bool {
}
func (c ClientInfo) ComplyWithCodecOrderInSDPAnswer() bool {
return !((c.isLinux() || c.isAndroid()) && c.isFirefox())
return (!c.isLinux() && !c.isAndroid()) || !c.isFirefox()
}
// Rust SDK can't decode unknown signal message (TrackSubscribed and ErrorResponse)
func (c ClientInfo) SupportTrackSubscribedEvent() bool {
return !(c.ClientInfo.GetSdk() == livekit.ClientInfo_RUST && c.ClientInfo.GetProtocol() < 10)
func (c ClientInfo) SupportsTrackSubscribedEvent() bool {
return c.ClientInfo.GetSdk() != livekit.ClientInfo_RUST || c.ClientInfo.GetProtocol() >= 10
}
func (c ClientInfo) SupportErrorResponse() bool {
return c.SupportTrackSubscribedEvent()
func (c ClientInfo) SupportsRequestResponse() bool {
return c.SupportsTrackSubscribedEvent()
}
func (c ClientInfo) SupportSctpZeroChecksum() bool {
return !(c.ClientInfo.GetSdk() == livekit.ClientInfo_UNKNOWN ||
(c.isGo() && c.compareVersion("2.4.0") < 0))
func (c ClientInfo) SupportsSctpZeroChecksum() bool {
return c.ClientInfo.GetSdk() != livekit.ClientInfo_UNKNOWN &&
(!c.isGo() || c.compareVersion("2.4.0") >= 0)
}
func (c ClientInfo) SupportsTransceiverReuse() bool {
return !c.isSafari()
}
func (c ClientInfo) HasCapability(cap livekit.ClientInfo_Capability) bool {
if c.ClientInfo == nil {
return false
}
return slices.Contains(c.ClientInfo.Capabilities, cap)
}
func (c ClientInfo) SupportsPacketTrailer() bool {
return c.HasCapability(livekit.ClientInfo_CAP_PACKET_TRAILER)
}
// compareVersion compares a semver against the current client SDK version
@@ -121,7 +141,7 @@ func (c ClientInfo) compareVersion(version string) int {
parts1 := strings.Split(version, ".")
ints0 := make([]int, 3)
ints1 := make([]int, 3)
for i := 0; i < 3; i++ {
for i := range 3 {
if len(parts0) > i {
ints0[i], _ = strconv.Atoi(parts0[i])
}
+70 -29
View File
@@ -20,13 +20,14 @@ import (
"github.com/livekit/livekit-server/pkg/config"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
act "github.com/livekit/livekit-server/pkg/sfu/rtpextension/abscapturetime"
dd "github.com/livekit/livekit-server/pkg/sfu/rtpextension/dependencydescriptor"
"github.com/livekit/mediatransportutil/pkg/rtcconfig"
)
const (
frameMarking = "urn:ietf:params:rtp-hdrext:framemarking"
repairedRTPStreamID = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
frameMarkingURI = "urn:ietf:params:rtp-hdrext:framemarking"
repairedRTPStreamIDURI = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
)
type WebRTCConfig struct {
@@ -79,23 +80,82 @@ func NewWebRTCConfig(conf *config.Config) (*WebRTCConfig, error) {
rtcConf.PacketBufferSizeAudio = rtcConf.PacketBufferSize
}
// publisher configuration
publisherConfig := DirectionConfig{
return &WebRTCConfig{
WebRTCConfig: *webRTCConfig,
Receiver: ReceiverConfig{
PacketBufferSizeVideo: rtcConf.PacketBufferSizeVideo,
PacketBufferSizeAudio: rtcConf.PacketBufferSizeAudio,
},
Publisher: getPublisherConfig(false),
Subscriber: getSubscriberConfig(rtcConf.CongestionControl.UseSendSideBWEInterceptor || rtcConf.CongestionControl.UseSendSideBWE),
}, nil
}
func (c *WebRTCConfig) UpdatePublisherConfig(consolidated bool) {
c.Publisher = getPublisherConfig(consolidated)
}
func (c *WebRTCConfig) UpdateSubscriberConfig(ccConf config.CongestionControlConfig) {
c.Subscriber = getSubscriberConfig(ccConf.UseSendSideBWEInterceptor || ccConf.UseSendSideBWE)
}
func (c *WebRTCConfig) SetBufferFactory(factory *buffer.Factory) {
c.BufferFactory = factory
c.SettingEngine.BufferFactory = factory.GetOrNew
}
func getPublisherConfig(consolidated bool) DirectionConfig {
if consolidated {
return DirectionConfig{
RTPHeaderExtension: RTPHeaderExtensionConfig{
Audio: []string{
sdp.SDESMidURI,
sdp.SDESRTPStreamIDURI,
sdp.AudioLevelURI,
act.AbsCaptureTimeURI,
},
Video: []string{
sdp.SDESMidURI,
sdp.SDESRTPStreamIDURI,
sdp.TransportCCURI,
sdp.ABSSendTimeURI,
frameMarkingURI,
dd.ExtensionURI,
repairedRTPStreamIDURI,
act.AbsCaptureTimeURI,
},
},
RTCPFeedback: RTCPFeedbackConfig{
Audio: []webrtc.RTCPFeedback{
{Type: webrtc.TypeRTCPFBNACK},
},
Video: []webrtc.RTCPFeedback{
{Type: webrtc.TypeRTCPFBTransportCC},
{Type: webrtc.TypeRTCPFBGoogREMB},
{Type: webrtc.TypeRTCPFBCCM, Parameter: "fir"},
{Type: webrtc.TypeRTCPFBNACK},
{Type: webrtc.TypeRTCPFBNACK, Parameter: "pli"},
},
},
}
}
return DirectionConfig{
RTPHeaderExtension: RTPHeaderExtensionConfig{
Audio: []string{
sdp.SDESMidURI,
sdp.SDESRTPStreamIDURI,
sdp.AudioLevelURI,
//act.AbsCaptureTimeURI,
act.AbsCaptureTimeURI,
},
Video: []string{
sdp.SDESMidURI,
sdp.SDESRTPStreamIDURI,
sdp.TransportCCURI,
frameMarking,
frameMarkingURI,
dd.ExtensionURI,
repairedRTPStreamID,
//act.AbsCaptureTimeURI,
repairedRTPStreamIDURI,
act.AbsCaptureTimeURI,
},
},
RTCPFeedback: RTCPFeedbackConfig{
@@ -110,25 +170,6 @@ func NewWebRTCConfig(conf *config.Config) (*WebRTCConfig, error) {
},
},
}
return &WebRTCConfig{
WebRTCConfig: *webRTCConfig,
Receiver: ReceiverConfig{
PacketBufferSizeVideo: rtcConf.PacketBufferSizeVideo,
PacketBufferSizeAudio: rtcConf.PacketBufferSizeAudio,
},
Publisher: publisherConfig,
Subscriber: getSubscriberConfig(rtcConf.CongestionControl.UseSendSideBWEInterceptor || rtcConf.CongestionControl.UseSendSideBWE),
}, nil
}
func (c *WebRTCConfig) UpdateCongestionControl(ccConf config.CongestionControlConfig) {
c.Subscriber = getSubscriberConfig(ccConf.UseSendSideBWEInterceptor || ccConf.UseSendSideBWE)
}
func (c *WebRTCConfig) SetBufferFactory(factory *buffer.Factory) {
c.BufferFactory = factory
c.SettingEngine.BufferFactory = factory.GetOrNew
}
func getSubscriberConfig(enableTWCC bool) DirectionConfig {
@@ -136,10 +177,10 @@ func getSubscriberConfig(enableTWCC bool) DirectionConfig {
RTPHeaderExtension: RTPHeaderExtensionConfig{
Video: []string{
dd.ExtensionURI,
//act.AbsCaptureTimeURI,
act.AbsCaptureTimeURI,
},
Audio: []string{
//act.AbsCaptureTimeURI,
act.AbsCaptureTimeURI,
},
},
RTCPFeedback: RTCPFeedbackConfig{
+109
View File
@@ -0,0 +1,109 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"fmt"
"time"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
var _ types.DataDownTrack = (*DataDownTrack)(nil)
var _ types.DataTrackSender = (*DataDownTrack)(nil)
type DataDownTrackParams struct {
Logger logger.Logger
SubscriberID livekit.ParticipantID
PublishDataTrack types.DataTrack
Handle uint16
Transport types.DataTrackTransport
BytesTrackStats *BytesTrackStats
}
type DataDownTrack struct {
params DataDownTrackParams
logger logger.Logger
createdAt int64
}
func NewDataDownTrack(params DataDownTrackParams) (*DataDownTrack, error) {
d := &DataDownTrack{
params: params,
createdAt: time.Now().UnixNano(),
}
d.logger = params.Logger.WithValues("name", d.Name(), "handle", d.Handle())
if err := d.params.PublishDataTrack.AddDataDownTrack(d); err != nil {
d.logger.Warnw("could not add data down track", err)
return nil, err
}
d.logger.Infow("created data down track")
return d, nil
}
func (d *DataDownTrack) Close() {
d.logger.Infow("closing data down track")
if d.params.BytesTrackStats != nil {
d.params.BytesTrackStats.Stop()
}
d.params.PublishDataTrack.DeleteDataDownTrack(d.SubscriberID())
}
func (d *DataDownTrack) Handle() uint16 {
return d.params.Handle
}
func (d *DataDownTrack) PublishDataTrack() types.DataTrack {
return d.params.PublishDataTrack
}
func (d *DataDownTrack) ID() livekit.TrackID {
return d.params.PublishDataTrack.ID()
}
func (d *DataDownTrack) Name() string {
return d.params.PublishDataTrack.Name()
}
func (d *DataDownTrack) SubscriberID() livekit.ParticipantID {
// add `createdAt` to ensure repeated subscriptions from same subscriber to same publisher does not collide
return livekit.ParticipantID(fmt.Sprintf("%s:%d", d.params.SubscriberID, d.createdAt))
}
func (d *DataDownTrack) WritePacket(data []byte, packet *datatrack.Packet, _arrivalTime int64) {
forwardedPacket := *packet
forwardedPacket.Handle = d.params.Handle
buf, err := forwardedPacket.Marshal()
if err != nil {
d.logger.Warnw("could not marshal data track message", err)
return
}
if err := d.params.Transport.SendDataTrackMessage(buf); err != nil {
d.logger.Warnw("could not send data track message", err)
return
}
if d.params.BytesTrackStats != nil {
d.params.BytesTrackStats.AddBytes(uint64(len(buf)), true)
}
}
func (d *DataDownTrack) UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions) {
// DT-TODO
}
+190
View File
@@ -0,0 +1,190 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"errors"
"sync"
"github.com/frostbyte73/core"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
sfuutils "github.com/livekit/livekit-server/pkg/sfu/utils"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
)
var (
errReceiverClosed = errors.New("datatrack is closed")
)
var _ types.DataTrack = (*DataTrack)(nil)
type DataTrackParams struct {
Logger logger.Logger
ParticipantID func() livekit.ParticipantID
ParticipantIdentity livekit.ParticipantIdentity
BytesTrackStats *BytesTrackStats
}
type DataTrack struct {
params DataTrackParams
logger logger.Logger
lock sync.Mutex
dti *livekit.DataTrackInfo
subscribedTracks map[livekit.ParticipantID]types.DataDownTrack
downTrackSpreader *sfuutils.DownTrackSpreader[types.DataTrackSender]
stats *dataTrackStats
closed core.Fuse
}
func NewDataTrack(params DataTrackParams, dti *livekit.DataTrackInfo) *DataTrack {
d := &DataTrack{
params: params,
dti: dti,
subscribedTracks: make(map[livekit.ParticipantID]types.DataDownTrack),
}
d.logger = params.Logger.WithValues("name", d.Name(), "handle", dti.PubHandle)
d.downTrackSpreader = sfuutils.NewDownTrackSpreader[types.DataTrackSender](sfuutils.DownTrackSpreaderParams{
Threshold: 20,
Logger: d.logger,
})
d.stats = newDataTrackStats(dataTrackStatsParams{Logger: d.logger})
d.logger.Infow("created data track", "dataTrackInfo", logger.Proto(d.dti))
return d
}
func (d *DataTrack) Close() {
d.logger.Infow("closing data track")
d.closed.Break()
d.stats.Close()
if d.params.BytesTrackStats != nil {
d.params.BytesTrackStats.Stop()
}
}
func (d *DataTrack) PublisherID() livekit.ParticipantID {
return d.params.ParticipantID()
}
func (d *DataTrack) PublisherIdentity() livekit.ParticipantIdentity {
return d.params.ParticipantIdentity
}
func (d *DataTrack) ToProto() *livekit.DataTrackInfo {
return utils.CloneProto(d.dti)
}
func (d *DataTrack) PubHandle() uint16 {
return uint16(d.dti.PubHandle)
}
func (d *DataTrack) ID() livekit.TrackID {
return livekit.TrackID(d.dti.Sid)
}
func (d *DataTrack) Name() string {
return d.dti.Name
}
func (d *DataTrack) AddSubscriber(sub types.LocalParticipant) (types.DataDownTrack, error) {
d.lock.Lock()
defer d.lock.Unlock()
if _, ok := d.subscribedTracks[sub.ID()]; ok {
return nil, errAlreadySubscribed
}
bytesStats := NewBytesTrackStats(
sub.GetCountry(),
d.ID(),
sub.ID(),
sub.Kind(),
sub.KindDetails(),
sub.GetTelemetryListener(),
sub.GetReporter(),
)
dataDownTrack, err := NewDataDownTrack(DataDownTrackParams{
Logger: sub.GetLogger().WithValues("trackID", d.ID()),
SubscriberID: sub.ID(),
PublishDataTrack: d,
Handle: sub.GetNextSubscribedDataTrackHandle(),
Transport: sub.GetDataTrackTransport(),
BytesTrackStats: bytesStats,
})
if err != nil {
bytesStats.Stop()
return nil, err
}
d.subscribedTracks[sub.ID()] = dataDownTrack
return dataDownTrack, nil
}
func (d *DataTrack) RemoveSubscriber(subID livekit.ParticipantID) {
d.lock.Lock()
dataDownTrack, ok := d.subscribedTracks[subID]
delete(d.subscribedTracks, subID)
d.lock.Unlock()
if ok {
dataDownTrack.Close()
}
}
func (d *DataTrack) IsSubscriber(subID livekit.ParticipantID) bool {
d.lock.Lock()
defer d.lock.Unlock()
_, ok := d.subscribedTracks[subID]
return ok
}
func (d *DataTrack) AddDataDownTrack(dts types.DataTrackSender) error {
if d.closed.IsBroken() {
return errReceiverClosed
}
if d.downTrackSpreader.HasDownTrack(dts.SubscriberID()) {
d.logger.Infow("subscriberID already exists, replacing data downtrack", "subscriberID", dts.SubscriberID())
}
d.downTrackSpreader.Store(dts)
d.logger.Infow("data downtrack added", "subscriberID", dts.SubscriberID())
return nil
}
func (d *DataTrack) DeleteDataDownTrack(subscriberID livekit.ParticipantID) {
d.downTrackSpreader.Free(subscriberID)
d.logger.Infow("data downtrack deleted", "subscriberID", subscriberID)
}
func (d *DataTrack) HandlePacket(data []byte, packet *datatrack.Packet, arrivalTime int64) {
d.stats.Update(packet, arrivalTime, len(data))
if d.params.BytesTrackStats != nil {
d.params.BytesTrackStats.AddBytes(uint64(len(data)), false)
}
d.downTrackSpreader.Broadcast(func(dts types.DataTrackSender) {
dts.WritePacket(data, packet, arrivalTime)
})
}
@@ -0,0 +1,59 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package datatrack
import (
"errors"
"github.com/livekit/protocol/livekit"
)
type ExtensionParticipantSid struct {
participantID livekit.ParticipantID
}
func NewExtensionParticipantSid(participantID livekit.ParticipantID) (*ExtensionParticipantSid, error) {
if len(participantID) >= 256 {
return nil, errors.New("participantID too long")
}
return &ExtensionParticipantSid{participantID}, nil
}
func (e *ExtensionParticipantSid) ParticipantID() livekit.ParticipantID {
return e.participantID
}
func (e *ExtensionParticipantSid) Marshal() (Extension, error) {
data := make([]byte, len(e.participantID))
copy(data, e.participantID)
return Extension{
id: uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
data: data,
}, nil
}
func (e *ExtensionParticipantSid) Unmarshal(ext Extension) error {
if ext.id != uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID) {
return errors.New("invalid extension ID")
}
if len(ext.data) == 0 {
return errors.New("empty extension data")
}
e.participantID = livekit.ParticipantID(ext.data)
return nil
}
@@ -0,0 +1,46 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package datatrack
import (
"testing"
"github.com/livekit/protocol/livekit"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestExtensionParticipantSid(t *testing.T) {
longTestParticipantID := livekit.ParticipantID(make([]byte, 256))
_, err := NewExtensionParticipantSid(longTestParticipantID)
require.Error(t, err)
testParticipantID := livekit.ParticipantID("test")
extParticipantSid, err := NewExtensionParticipantSid(testParticipantID)
require.NoError(t, err)
expectedExt := Extension{
id: uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID),
data: []byte{'t', 'e', 's', 't'},
}
ext, err := extParticipantSid.Marshal()
require.NoError(t, err)
require.Equal(t, expectedExt, ext)
var unmarshaled ExtensionParticipantSid
err = unmarshaled.Unmarshal(ext)
require.NoError(t, err)
assert.Equal(t, testParticipantID, unmarshaled.ParticipantID())
}
+301
View File
@@ -0,0 +1,301 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package datatrack
import (
"encoding/binary"
"errors"
"fmt"
)
var (
errHeaderSizeInsufficient = errors.New("data track packet header size insufficient")
errBufferSizeInsufficient = errors.New("data track packet buffer size insufficient")
errExtensionSizeInsufficient = errors.New("data track packet extension size insufficient")
errExtensionNotFound = errors.New("data track packet extension not found")
errExtensionSizeTooBig = errors.New("extension size is too big")
)
const (
headerLength = 12
versionShift = 5
versionMask = (1 << 3) - 1
startOfFrameShift = 4
startOfFrameMask = (1 << 1) - 1
finalOfFrameShift = 3
finalOfFrameMask = (1 << 1) - 1
extensionsShift = 2
extensionsMask = (1 << 1) - 1
handleOffset = 2
handleLength = 2
seqNumOffset = 4
seqNumLength = 2
frameNumOffset = 6
frameNumLength = 2
timestampOffset = 8
timestampLength = 4
extensionsSizeOffset = headerLength
extensionsSizeLength = 2
extensionIDLength = 1
extensionSizeLength = 1
)
type Extension struct {
id uint8
data []byte
}
type Header struct {
Version uint8
IsStartOfFrame bool
IsFinalOfFrame bool
HasExtensions bool
Handle uint16
SequenceNumber uint16
FrameNumber uint16
Timestamp uint32
ExtensionsSize uint16
Extensions []Extension
}
/*
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* 0 1 2 3
┆* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* |V |S|F|X| reserved | handle |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* | sequence number | frame number |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* | timestamp |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|* Extensions Size if X=1 | Extensions... |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Each extension
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* 0 1 2 3
┆* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
┆* | Extension ID | Extension size| Extension payload |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
End of all extensions
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|* padded to 4 byte boundary if aggregate of `Extensions Size` |
|* field and all extensions do not end on a 4 byte boundary |
┆* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
func (h *Header) Unmarshal(buf []byte) (int, error) {
if len(buf) < headerLength {
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
}
hdrSize := headerLength
h.Version = buf[0] >> versionShift & versionMask
h.IsStartOfFrame = (buf[0] >> startOfFrameShift & startOfFrameMask) > 0
h.IsFinalOfFrame = (buf[0] >> finalOfFrameShift & finalOfFrameMask) > 0
h.HasExtensions = (buf[0] >> extensionsShift & extensionsMask) > 0
h.Handle = binary.BigEndian.Uint16(buf[handleOffset : handleOffset+handleLength])
h.SequenceNumber = binary.BigEndian.Uint16(buf[seqNumOffset : seqNumOffset+seqNumLength])
h.FrameNumber = binary.BigEndian.Uint16(buf[frameNumOffset : frameNumOffset+frameNumLength])
h.Timestamp = binary.BigEndian.Uint32(buf[timestampOffset : timestampOffset+timestampLength])
if h.HasExtensions {
extensionsSize := (binary.BigEndian.Uint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength])+1)*4 - extensionsSizeLength
hdrSize += extensionsSizeLength
extensionHeaderSize := extensionIDLength + extensionSizeLength
remainingSize := int(extensionsSize)
idx := extensionsSizeOffset + extensionsSizeLength
for remainingSize != 0 {
// read extension header
if len(buf[idx:]) < extensionIDLength || remainingSize < extensionIDLength {
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), extensionIDLength)
}
id := buf[idx]
if id == 0 {
// end of extensions, padding has started
hdrSize += remainingSize
break
}
if len(buf[idx+1:]) < extensionSizeLength || remainingSize < extensionSizeLength {
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), extensionSizeLength)
}
size := int(buf[idx+1])
remainingSize -= extensionHeaderSize
idx += extensionHeaderSize
hdrSize += extensionHeaderSize
// read extension data
if len(buf[idx:]) < size || remainingSize < size {
return 0, fmt.Errorf("%w: %d/%d < %d", errExtensionSizeInsufficient, remainingSize, len(buf[idx:]), size)
}
h.Extensions = append(h.Extensions, Extension{id: id, data: buf[idx : idx+size]})
remainingSize -= size
idx += size
hdrSize += size
}
h.ExtensionsSize = extensionsSize - uint16(remainingSize)
}
return hdrSize, nil
}
func (h *Header) MarshalSize() int {
extensionsSize := 0
if h.HasExtensions {
extensionsSize += extensionsSizeLength
for _, ext := range h.Extensions {
extensionsSize += len(ext.data) + extensionIDLength + extensionSizeLength
}
}
return headerLength + (extensionsSize+3)/4*4
}
func (h *Header) MarshalTo(buf []byte) (int, error) {
if len(buf) < headerLength {
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
}
hdrSize := headerLength
buf[0] = h.Version << versionShift
if h.IsStartOfFrame {
buf[0] |= (1 << startOfFrameShift)
}
if h.IsFinalOfFrame {
buf[0] |= (1 << finalOfFrameShift)
}
if h.HasExtensions {
buf[0] |= (1 << extensionsShift)
}
binary.BigEndian.PutUint16(buf[handleOffset:handleOffset+handleLength], h.Handle)
binary.BigEndian.PutUint16(buf[seqNumOffset:seqNumOffset+seqNumLength], h.SequenceNumber)
binary.BigEndian.PutUint16(buf[frameNumOffset:frameNumOffset+frameNumLength], h.FrameNumber)
binary.BigEndian.PutUint32(buf[timestampOffset:timestampOffset+timestampLength], h.Timestamp)
if h.HasExtensions {
extensionsSize := (extensionsSizeLength + h.ExtensionsSize + 3) / 4 * 4
binary.BigEndian.PutUint16(buf[extensionsSizeOffset:extensionsSizeOffset+extensionsSizeLength], (extensionsSize/4)-1)
hdrSize += extensionsSizeLength
addedSize := 0
idx := extensionsSizeOffset + extensionsSizeLength
for _, ext := range h.Extensions {
buf[idx] = ext.id
if len(ext.data) > 255 {
return 0, fmt.Errorf("%w: %d > 255", errExtensionSizeTooBig, len(ext.data))
}
buf[idx+extensionIDLength] = byte(len(ext.data))
copy(buf[idx+extensionIDLength+extensionSizeLength:], ext.data)
extSize := len(ext.data) + extensionIDLength + extensionSizeLength
idx += extSize
hdrSize += extSize
addedSize += extSize
}
paddingSize := extensionsSize - extensionsSizeLength - uint16(addedSize)
for i := range paddingSize {
buf[idx+int(i)] = 0
}
idx += int(paddingSize)
hdrSize += int(paddingSize)
}
return hdrSize, nil
}
func (h *Header) AddExtension(ext Extension) {
for i, existingExt := range h.Extensions {
if existingExt.id == ext.id {
h.ExtensionsSize -= uint16(len(existingExt.data) + extensionIDLength + extensionSizeLength)
h.Extensions[i].data = ext.data
h.ExtensionsSize += uint16(len(h.Extensions[i].data) + extensionIDLength + extensionSizeLength)
return
}
}
h.Extensions = append(h.Extensions, ext)
h.ExtensionsSize += uint16(len(ext.data) + extensionIDLength + extensionSizeLength)
h.HasExtensions = true
}
func (h *Header) GetExtension(id uint8) (Extension, error) {
for _, ext := range h.Extensions {
if ext.id == id {
return ext, nil
}
}
return Extension{}, fmt.Errorf("%w, id: %d", errExtensionNotFound, id)
}
// ----------------------------------------------------
type Packet struct {
Header
Payload []byte
}
func (p *Packet) Unmarshal(buf []byte) error {
hdrSize, err := p.Header.Unmarshal(buf)
if err != nil {
return err
}
p.Payload = buf[hdrSize:]
return nil
}
func (p *Packet) Marshal() ([]byte, error) {
buf := make([]byte, p.Header.MarshalSize()+len(p.Payload))
if err := p.MarshalTo(buf); err != nil {
return nil, err
}
return buf, nil
}
func (p *Packet) MarshalTo(buf []byte) error {
size := p.Header.MarshalSize() + len(p.Payload)
if len(buf) < size {
return fmt.Errorf("%w: %d < %d", errBufferSizeInsufficient, len(buf), size)
}
hdrSize, err := p.Header.MarshalTo(buf)
if err != nil {
return err
}
copy(buf[hdrSize:], p.Payload)
return nil
}
+259
View File
@@ -0,0 +1,259 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package datatrack
import (
"testing"
"github.com/livekit/protocol/livekit"
"github.com/stretchr/testify/require"
)
func TestPacket(t *testing.T) {
t.Run("without extension", func(t *testing.T) {
payload := make([]byte, 6)
for i := range len(payload) {
payload[i] = byte(255 - i)
}
packet := &Packet{
Header: Header{
Version: 0,
IsStartOfFrame: true,
IsFinalOfFrame: true,
Handle: 3333,
SequenceNumber: 6666,
FrameNumber: 9999,
Timestamp: 0xdeadbeef,
},
Payload: payload,
}
rawPacket, err := packet.Marshal()
require.NoError(t, err)
expectedRawPacket := []byte{
0x18, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0xff, 0xfe, 0xfd, 0xfc,
0xfb, 0xfa,
}
require.Equal(t, expectedRawPacket, rawPacket)
var unmarshaled Packet
err = unmarshaled.Unmarshal(rawPacket)
require.NoError(t, err)
require.Equal(t, packet, &unmarshaled)
})
t.Run("with extension", func(t *testing.T) {
payload := make([]byte, 4)
for i := range len(payload) {
payload[i] = byte(255 - i)
}
packet := &Packet{
Header: Header{
Version: 0,
IsStartOfFrame: true,
IsFinalOfFrame: false,
Handle: 3333,
SequenceNumber: 6666,
FrameNumber: 9999,
Timestamp: 0xdeadbeef,
},
Payload: payload,
}
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
if ext, err := extParticipantSid.Marshal(); err == nil {
packet.AddExtension(ext)
}
}
rawPacket, err := packet.Marshal()
require.NoError(t, err)
expectedRawPacket := []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x04, 0x01, 0x10,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x72,
0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74,
0xff, 0xfe, 0xfd, 0xfc,
}
require.Equal(t, expectedRawPacket, rawPacket)
var unmarshaled Packet
err = unmarshaled.Unmarshal(rawPacket)
require.NoError(t, err)
require.Equal(t, packet, &unmarshaled)
ext, err := unmarshaled.GetExtension(uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
require.NoError(t, err)
var extParticipantSid ExtensionParticipantSid
require.NoError(t, extParticipantSid.Unmarshal(ext))
require.Equal(t, livekit.ParticipantID("test_participant"), extParticipantSid.ParticipantID())
})
t.Run("with extension padding", func(t *testing.T) {
payload := make([]byte, 4)
for i := range len(payload) {
payload[i] = byte(255 - i)
}
packet := &Packet{
Header: Header{
Version: 0,
IsStartOfFrame: true,
IsFinalOfFrame: false,
Handle: 3333,
SequenceNumber: 6666,
FrameNumber: 9999,
Timestamp: 0xdeadbeef,
},
Payload: payload,
}
if extParticipantSid, err := NewExtensionParticipantSid("participant"); err == nil {
if ext, err := extParticipantSid.Marshal(); err == nil {
packet.AddExtension(ext)
}
}
rawPacket, err := packet.Marshal()
require.NoError(t, err)
expectedRawPacket := []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x01, 0x0b,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
require.Equal(t, expectedRawPacket, rawPacket)
var unmarshaled Packet
err = unmarshaled.Unmarshal(rawPacket)
require.NoError(t, err)
require.Equal(t, packet, &unmarshaled)
ext, err := unmarshaled.GetExtension(uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
require.NoError(t, err)
var extParticipantSid ExtensionParticipantSid
require.NoError(t, extParticipantSid.Unmarshal(ext))
require.Equal(t, livekit.ParticipantID("participant"), extParticipantSid.ParticipantID())
})
t.Run("replace extension", func(t *testing.T) {
payload := make([]byte, 4)
for i := range len(payload) {
payload[i] = byte(255 - i)
}
packet := &Packet{
Header: Header{
Version: 0,
IsStartOfFrame: true,
IsFinalOfFrame: false,
Handle: 3333,
SequenceNumber: 6666,
FrameNumber: 9999,
Timestamp: 0xdeadbeef,
},
Payload: payload,
}
if extParticipantSid, err := NewExtensionParticipantSid("participant"); err == nil {
if ext, err := extParticipantSid.Marshal(); err == nil {
packet.AddExtension(ext)
}
}
rawPacket, err := packet.Marshal()
require.NoError(t, err)
expectedRawPacket := []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x01, 0x0b,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
require.Equal(t, expectedRawPacket, rawPacket)
// replace existing extension ID and ensure that marshalled packet is updated
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
if ext, err := extParticipantSid.Marshal(); err == nil {
packet.AddExtension(ext)
}
}
rawPacket, err = packet.Marshal()
require.NoError(t, err)
expectedRawPacket = []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x04, 0x01, 0x10,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x72,
0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74,
0xff, 0xfe, 0xfd, 0xfc,
}
require.Equal(t, expectedRawPacket, rawPacket)
var unmarshaled Packet
err = unmarshaled.Unmarshal(rawPacket)
require.NoError(t, err)
require.Equal(t, packet, &unmarshaled)
ext, err := unmarshaled.GetExtension(uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
require.NoError(t, err)
var extParticipantSid ExtensionParticipantSid
require.NoError(t, extParticipantSid.Unmarshal(ext))
require.Equal(t, livekit.ParticipantID("test_participant"), extParticipantSid.ParticipantID())
})
t.Run("bad packet", func(t *testing.T) {
var unmarshaled Packet
// extensions size too small
badPacket := []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x02, 0x01, 0x0b,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
err := unmarshaled.Unmarshal(badPacket)
require.Error(t, err)
// get an invalid extension id
badPacket = []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x02, 0x0b,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
err = unmarshaled.Unmarshal(badPacket)
require.NoError(t, err)
_, err = unmarshaled.GetExtension(uint8(livekit.DataTrackExtensionID_DTEI_PARTICIPANT_SID))
require.Error(t, err)
// extension payload size bigger than payload
badPacket = []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x01, 0x0d,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
err = unmarshaled.Unmarshal(badPacket)
require.Error(t, err)
// extension payload size smaller than payload
badPacket = []byte{
0x14, 0x00, 0x0d, 0x05, 0x1a, 0x0a, 0x27, 0x0f,
0xde, 0xad, 0xbe, 0xef, 0x00, 0x03, 0x01, 0x07,
0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70,
0x61, 0x6e, 0x74, 0x00, 0xff, 0xfe, 0xfd, 0xfc,
}
err = unmarshaled.Unmarshal(badPacket)
require.Error(t, err)
})
}
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package datatrack
import (
"math/rand"
"time"
)
func GenerateRawDataPackets(handle uint16, seqNum uint16, frameNum uint16, numFrames int, frameSize int, frameDuration time.Duration) [][]byte {
if seqNum == 0 {
seqNum = uint16(rand.Intn(256) + 1)
}
if frameNum == 0 {
frameNum = uint16(rand.Intn(256) + 1)
}
timestamp := uint32(rand.Intn(1024))
packetsPerFrame := (frameSize + 255) / 256 // using 256 bytes of payload per packet
if packetsPerFrame == 0 {
return nil
}
numPackets := packetsPerFrame * numFrames
rawPackets := make([][]byte, 0, numPackets)
for range numFrames {
remainingSize := frameSize
for packetIdx := range packetsPerFrame {
payloadSize := min(remainingSize, 256)
payload := make([]byte, payloadSize)
for i := range len(payload) {
payload[i] = byte(255 - i)
}
packet := &Packet{
Header: Header{
Version: 0,
IsStartOfFrame: packetIdx == 0,
IsFinalOfFrame: packetIdx == packetsPerFrame-1,
Handle: handle,
SequenceNumber: seqNum,
FrameNumber: frameNum,
Timestamp: timestamp,
},
Payload: payload,
}
if extParticipantSid, err := NewExtensionParticipantSid("test_participant"); err == nil {
if ext, err := extParticipantSid.Marshal(); err == nil {
packet.AddExtension(ext)
}
}
rawPacket, err := packet.Marshal()
if err == nil {
rawPackets = append(rawPackets, rawPacket)
}
seqNum++
remainingSize -= payloadSize
}
frameNum++
timestamp += uint32(90000 * frameDuration.Seconds())
}
return rawPackets
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"sync"
"time"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils/mono"
)
type dataTrackStatsParams struct {
Logger logger.Logger
}
type dataTrackStats struct {
params dataTrackStatsParams
lock sync.Mutex
startTime int64
endTime int64
highestSequenceNumber uint16
numPackets int
numPacketsLost int
numPacketsOutOfOrder int
numFrames int // count of `F` tagged packets, i. e. packets with final packet of frame marker
numBytes int
}
func newDataTrackStats(params dataTrackStatsParams) *dataTrackStats {
return &dataTrackStats{
params: params,
}
}
func (d *dataTrackStats) Update(packet *datatrack.Packet, arrivalTime int64, payloadLength int) {
d.lock.Lock()
defer d.lock.Unlock()
d.numBytes += payloadLength
if d.endTime != 0 {
return
}
if d.startTime == 0 {
d.startTime = arrivalTime
d.highestSequenceNumber = packet.SequenceNumber
d.numPackets = 1
} else {
diff := packet.SequenceNumber - d.highestSequenceNumber
switch {
case diff == 0: // duplicate
return
case diff > (1 << 15): // out of order
d.numPackets++
d.numPacketsOutOfOrder++
if d.numPacketsLost > 0 {
d.numPacketsLost--
}
default: // in order
d.numPackets++
d.numPacketsLost += int(diff) - 1
d.highestSequenceNumber = packet.SequenceNumber
}
}
if packet.IsFinalOfFrame {
d.numFrames++
}
}
func (d *dataTrackStats) Close() {
d.lock.Lock()
defer d.lock.Unlock()
d.endTime = mono.UnixNano()
if d.startTime != 0 {
duration := time.Duration(d.endTime - d.startTime).Seconds()
fps := float64(d.numFrames) / duration
d.params.Logger.Infow(
"data track stats",
"duration", duration,
"numPackets", d.numPackets,
"numPacketsLost", d.numPacketsLost,
"numPacketsOutOfOrder", d.numPacketsOutOfOrder,
"numFrames", d.numFrames,
"fps", fps,
"numBytes", d.numBytes,
)
}
}
+244 -97
View File
@@ -15,7 +15,8 @@
package dynacast
import (
"sort"
"slices"
"strings"
"sync"
"testing"
"time"
@@ -23,20 +24,41 @@ import (
"github.com/stretchr/testify/require"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
)
func TestSubscribedMaxQuality(t *testing.T) {
type testDynacastManagerListener struct {
onSubscribedMaxQualityChange func(subscribedQualties []*livekit.SubscribedCodec)
onSubscribedAudioCodecChange func(subscribedCodecs []*livekit.SubscribedAudioCodec)
}
func (t *testDynacastManagerListener) OnDynacastSubscribedMaxQualityChange(
subscribedQualities []*livekit.SubscribedCodec,
_maxSubscribedQualities []types.SubscribedCodecQuality,
) {
t.onSubscribedMaxQualityChange(subscribedQualities)
}
func (t *testDynacastManagerListener) OnDynacastSubscribedAudioCodecChange(
codecs []*livekit.SubscribedAudioCodec,
) {
t.onSubscribedAudioCodecChange(codecs)
}
func TestSubscribedMaxQuality(t *testing.T) {
t.Run("subscribers muted", func(t *testing.T) {
dm := NewDynacastManager(DynacastManagerParams{})
var lock sync.Mutex
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
Listener: &testDynacastManagerListener{
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
},
},
})
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeVP8, livekit.VideoQuality_HIGH)
@@ -72,21 +94,20 @@ func TestSubscribedMaxQuality(t *testing.T) {
})
t.Run("subscribers max quality", func(t *testing.T) {
dm := NewDynacastManager(DynacastManagerParams{
DynacastPauseDelay: 100 * time.Millisecond,
})
lock := sync.RWMutex{}
lock.Lock()
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
lock.Unlock()
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
Listener: &testDynacastManagerListener{
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
},
},
})
dm.maxSubscribedQuality = map[mime.MimeType]livekit.VideoQuality{
dm.(*dynacastManagerVideo).maxSubscribedQuality = map[mime.MimeType]livekit.VideoQuality{
mime.MimeTypeVP8: livekit.VideoQuality_LOW,
mime.MimeTypeAV1: livekit.VideoQuality_LOW,
}
@@ -279,98 +300,224 @@ func TestSubscribedMaxQuality(t *testing.T) {
}
func TestCodecRegression(t *testing.T) {
dm := NewDynacastManager(DynacastManagerParams{})
var lock sync.Mutex
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
dm.OnSubscribedMaxQualityChange(func(subscribedQualities []*livekit.SubscribedCodec, _maxSubscribedQualities []types.SubscribedCodecQuality) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
t.Run("codec regression video", func(t *testing.T) {
var lock sync.Mutex
actualSubscribedQualities := make([]*livekit.SubscribedCodec, 0)
dm := NewDynacastManagerVideo(DynacastManagerVideoParams{
Listener: &testDynacastManagerListener{
onSubscribedMaxQualityChange: func(subscribedQualities []*livekit.SubscribedCodec) {
lock.Lock()
actualSubscribedQualities = subscribedQualities
lock.Unlock()
},
},
})
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_HIGH)
expectedSubscribedQualities := []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
dm.HandleCodecRegression(mime.MimeTypeAV1, mime.MimeTypeVP8)
expectedSubscribedQualities = []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
},
},
{
Codec: mime.MimeTypeVP8.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
// av1 quality change should be forwarded to vp8
// av1 quality change of node should be ignored
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_MEDIUM)
dm.NotifySubscriberNodeMaxQuality("n1", []types.SubscribedCodecQuality{
{CodecMime: mime.MimeTypeAV1, Quality: livekit.VideoQuality_HIGH},
})
expectedSubscribedQualities = []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
},
},
{
Codec: mime.MimeTypeVP8.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
})
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_HIGH)
t.Run("codec regression audio", func(t *testing.T) {
var lock sync.Mutex
actualSubscribedCodecs := make([]*livekit.SubscribedAudioCodec, 0)
expectedSubscribedQualities := []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
dm := NewDynacastManagerAudio(DynacastManagerAudioParams{
Listener: &testDynacastManagerListener{
onSubscribedAudioCodecChange: func(subscribedCodecs []*livekit.SubscribedAudioCodec) {
lock.Lock()
actualSubscribedCodecs = subscribedCodecs
lock.Unlock()
},
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
})
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
dm.NotifySubscription("s1", mime.MimeTypeRED, true)
dm.HandleCodecRegression(mime.MimeTypeAV1, mime.MimeTypeVP8)
expectedSubscribedQualities = []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
expectedSubscribedCodecs := []*livekit.SubscribedAudioCodec{
{
Codec: mime.MimeTypeRED.String(),
Enabled: true,
},
},
{
Codec: mime.MimeTypeVP8.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: true},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
}, 10*time.Second, 100*time.Millisecond)
dm.HandleCodecRegression(mime.MimeTypeRED, mime.MimeTypeOpus)
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
{
Codec: mime.MimeTypeRED.String(),
Enabled: false,
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
{
Codec: mime.MimeTypeOpus.String(),
Enabled: true,
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
}, 10*time.Second, 100*time.Millisecond)
// RED disable as subscriber or subscriber node should be ignored as it has been regressed
dm.NotifySubscription("s1", mime.MimeTypeRED, false)
dm.NotifySubscriptionNode("n1", []*livekit.SubscribedAudioCodec{
{Codec: mime.MimeTypeRED.String(), Enabled: false},
})
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
}, 10*time.Second, 100*time.Millisecond)
// `s1` unsubscription should turn off `opus`
dm.NotifySubscription("s1", mime.MimeTypeOpus, false)
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
{
Codec: mime.MimeTypeRED.String(),
Enabled: false,
},
{
Codec: mime.MimeTypeOpus.String(),
Enabled: false,
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
}, 10*time.Second, 100*time.Millisecond)
// a node subscription should turn `opus` back on
dm.NotifySubscriptionNode("n1", []*livekit.SubscribedAudioCodec{
{
Codec: mime.MimeTypeOpus.String(),
Enabled: true,
},
})
expectedSubscribedCodecs = []*livekit.SubscribedAudioCodec{
{
Codec: mime.MimeTypeRED.String(),
Enabled: false,
},
{
Codec: mime.MimeTypeOpus.String(),
Enabled: true,
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedAudioCodecsAsString(expectedSubscribedCodecs) == subscribedAudioCodecsAsString(actualSubscribedCodecs)
}, 10*time.Second, 100*time.Millisecond)
// av1 quality change should be forwarded to vp8
// av1 quality change of node should be ignored
dm.NotifySubscriberMaxQuality("s1", mime.MimeTypeAV1, livekit.VideoQuality_MEDIUM)
dm.NotifySubscriberNodeMaxQuality("n1", []types.SubscribedCodecQuality{
{CodecMime: mime.MimeTypeAV1, Quality: livekit.VideoQuality_HIGH},
})
expectedSubscribedQualities = []*livekit.SubscribedCodec{
{
Codec: mime.MimeTypeAV1.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: false},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: false},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
},
},
{
Codec: mime.MimeTypeVP8.String(),
Qualities: []*livekit.SubscribedQuality{
{Quality: livekit.VideoQuality_LOW, Enabled: true},
{Quality: livekit.VideoQuality_MEDIUM, Enabled: true},
{Quality: livekit.VideoQuality_HIGH, Enabled: false},
},
},
}
require.Eventually(t, func() bool {
lock.Lock()
defer lock.Unlock()
return subscribedCodecsAsString(expectedSubscribedQualities) == subscribedCodecsAsString(actualSubscribedQualities)
}, 10*time.Second, 100*time.Millisecond)
}
func subscribedCodecsAsString(c1 []*livekit.SubscribedCodec) string {
sort.Slice(c1, func(i, j int) bool { return c1[i].Codec < c1[j].Codec })
var s1 string
slices.SortFunc(c1, func(a, b *livekit.SubscribedCodec) int {
return strings.Compare(a.Codec, b.Codec)
})
var s1 strings.Builder
for _, c := range c1 {
s1 += c.String()
s1.WriteString(c.String())
}
return s1
return s1.String()
}
func subscribedAudioCodecsAsString(c1 []*livekit.SubscribedAudioCodec) string {
slices.SortFunc(c1, func(a, b *livekit.SubscribedAudioCodec) int {
return strings.Compare(a.Codec, b.Codec)
})
var s1 strings.Builder
for _, c := range c1 {
s1.WriteString(c.String())
}
return s1.String()
}
+198
View File
@@ -0,0 +1,198 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dynacast
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/codecs/mime"
)
var _ DynacastManager = (*dynacastManagerAudio)(nil)
var _ dynacastQualityListener = (*dynacastManagerAudio)(nil)
type DynacastManagerAudioParams struct {
Listener DynacastManagerListener
Logger logger.Logger
}
type dynacastManagerAudio struct {
params DynacastManagerAudioParams
subscribedCodecs map[mime.MimeType]bool
committedSubscribedCodecs map[mime.MimeType]bool
isClosed bool
*dynacastManagerBase
}
func NewDynacastManagerAudio(params DynacastManagerAudioParams) DynacastManager {
if params.Logger == nil {
params.Logger = logger.GetLogger()
}
d := &dynacastManagerAudio{
params: params,
subscribedCodecs: make(map[mime.MimeType]bool),
committedSubscribedCodecs: make(map[mime.MimeType]bool),
}
d.dynacastManagerBase = newDynacastManagerBase(dynacastManagerBaseParams{
Logger: params.Logger,
OpsQueueDepth: 4,
OnRestart: func() {
d.committedSubscribedCodecs = make(map[mime.MimeType]bool)
},
OnDynacastQualityCreate: func(mimeType mime.MimeType) dynacastQuality {
dq := newDynacastQualityAudio(dynacastQualityAudioParams{
MimeType: mimeType,
Listener: d,
Logger: d.params.Logger,
})
return dq
},
OnRegressCodec: func(fromMime, toMime mime.MimeType) {
d.subscribedCodecs[fromMime] = false
// if the new codec is not added, notify the publisher to start publishing
if _, ok := d.subscribedCodecs[toMime]; !ok {
d.subscribedCodecs[toMime] = true
}
},
OnUpdateNeeded: d.update,
})
return d
}
// It is possible for tracks to be in pending close state. When track
// is waiting to be closed, a node is not streaming a track. This can
// be used to force an update announcing that subscribed codec is disabled,
// i.e. indicating not pulling track any more.
func (d *dynacastManagerAudio) ForceEnable(enabled bool) {
d.lock.Lock()
defer d.lock.Unlock()
for mime := range d.committedSubscribedCodecs {
d.committedSubscribedCodecs[mime] = enabled
}
d.enqueueSubscribedChange()
}
func (d *dynacastManagerAudio) NotifySubscription(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
enabled bool,
) {
dq := d.getOrCreateDynacastQuality(mime)
if dq != nil {
dq.NotifySubscription(subscriberID, enabled)
}
}
func (d *dynacastManagerAudio) NotifySubscriptionNode(
nodeID livekit.NodeID,
codecs []*livekit.SubscribedAudioCodec,
) {
for _, codec := range codecs {
dq := d.getOrCreateDynacastQuality(mime.NormalizeMimeType(codec.Codec))
if dq != nil {
dq.NotifySubscriptionNode(nodeID, codec.Enabled)
}
}
}
func (d *dynacastManagerAudio) OnUpdateAudioCodecForMime(mime mime.MimeType, enabled bool) {
d.lock.Lock()
if _, ok := d.regressedCodec[mime]; !ok {
d.subscribedCodecs[mime] = enabled
}
d.lock.Unlock()
d.update(false)
}
func (d *dynacastManagerAudio) update(force bool) {
d.lock.Lock()
d.params.Logger.Debugw(
"processing subscribed codec change",
"force", force,
"committedSubscribedCodecs", d.committedSubscribedCodecs,
"subscribedCodecs", d.subscribedCodecs,
)
if len(d.subscribedCodecs) == 0 {
// no mime has been added, nothing to update
d.lock.Unlock()
return
}
// add or remove of a mime triggers an update
changed := len(d.subscribedCodecs) != len(d.committedSubscribedCodecs)
if !changed {
for mime, enabled := range d.subscribedCodecs {
if ce, ok := d.committedSubscribedCodecs[mime]; ok {
if ce != enabled {
changed = true
break
}
}
}
}
if !force && !changed {
d.lock.Unlock()
return
}
d.params.Logger.Debugw(
"committing subscribed codec change",
"force", force,
"committedSubscribedCoecs", d.committedSubscribedCodecs,
"subscribedcodecs", d.subscribedCodecs,
)
// commit change
d.committedSubscribedCodecs = make(map[mime.MimeType]bool, len(d.subscribedCodecs))
for mime, enabled := range d.subscribedCodecs {
d.committedSubscribedCodecs[mime] = enabled
}
d.enqueueSubscribedChange()
d.lock.Unlock()
}
func (d *dynacastManagerAudio) enqueueSubscribedChange() {
if d.isClosed || d.params.Listener == nil {
return
}
subscribedCodecs := make([]*livekit.SubscribedAudioCodec, 0, len(d.committedSubscribedCodecs))
for mime, enabled := range d.committedSubscribedCodecs {
subscribedCodecs = append(subscribedCodecs, &livekit.SubscribedAudioCodec{
Codec: mime.String(),
Enabled: enabled,
})
}
d.params.Logger.Debugw(
"subscribedAudioCodecChange",
"subscribedCodecs", logger.ProtoSlice(subscribedCodecs),
)
d.notifyOpsQueue.Enqueue(func() {
d.params.Listener.OnDynacastSubscribedAudioCodecChange(subscribedCodecs)
})
}
+165
View File
@@ -0,0 +1,165 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dynacast
import (
"maps"
"slices"
"sync"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/utils"
)
type dynacastManagerBaseParams struct {
Logger logger.Logger
OpsQueueDepth uint
OnRestart func()
OnDynacastQualityCreate func(mimeType mime.MimeType) dynacastQuality
OnRegressCodec func(fromMime, toMime mime.MimeType)
OnUpdateNeeded func(force bool)
}
type dynacastManagerBase struct {
params dynacastManagerBaseParams
lock sync.RWMutex
regressedCodec map[mime.MimeType]struct{}
dynacastQuality map[mime.MimeType]dynacastQuality
notifyOpsQueue *utils.OpsQueue
isClosed bool
dynacastManagerNull
dynacastQualityListenerNull
}
func newDynacastManagerBase(params dynacastManagerBaseParams) *dynacastManagerBase {
if params.OpsQueueDepth == 0 {
params.OpsQueueDepth = 4
}
d := &dynacastManagerBase{
params: params,
regressedCodec: make(map[mime.MimeType]struct{}),
dynacastQuality: make(map[mime.MimeType]dynacastQuality),
notifyOpsQueue: utils.NewOpsQueue(utils.OpsQueueParams{
Name: "dynacast-notify",
MinSize: params.OpsQueueDepth,
FlushOnStop: true,
Logger: params.Logger,
}),
}
d.notifyOpsQueue.Start()
return d
}
func (d *dynacastManagerBase) AddCodec(mime mime.MimeType) {
d.getOrCreateDynacastQuality(mime)
}
func (d *dynacastManagerBase) HandleCodecRegression(fromMime, toMime mime.MimeType) {
fromDq := d.getOrCreateDynacastQuality(fromMime)
d.lock.Lock()
if d.isClosed {
d.lock.Unlock()
return
}
if fromDq == nil {
// should not happen as we have added the codec on setup receiver
d.params.Logger.Warnw("regression from codec not found", nil, "mime", fromMime, "toMime", toMime)
d.lock.Unlock()
return
}
d.regressedCodec[fromMime] = struct{}{}
d.params.OnRegressCodec(fromMime, toMime)
d.lock.Unlock()
d.params.OnUpdateNeeded(false)
fromDq.Stop()
fromDq.RegressTo(d.getOrCreateDynacastQuality(toMime))
}
func (d *dynacastManagerBase) Restart() {
d.lock.Lock()
d.params.OnRestart()
dqs := d.getDynacastQualitiesLocked()
d.lock.Unlock()
for _, dq := range dqs {
dq.Restart()
}
}
func (d *dynacastManagerBase) Close() {
d.notifyOpsQueue.Stop()
d.lock.Lock()
dqs := d.getDynacastQualitiesLocked()
d.dynacastQuality = make(map[mime.MimeType]dynacastQuality)
d.isClosed = true
d.lock.Unlock()
for _, dq := range dqs {
dq.Stop()
}
}
// There are situations like track unmute or streaming from a different node
// where subscription changes needs to sent to the provider immediately.
// This bypasses any debouncing and forces a subscription change update
// with immediate effect.
func (d *dynacastManagerBase) ForceUpdate() {
d.params.OnUpdateNeeded(true)
}
func (d *dynacastManagerBase) ClearSubscriberNodes() {
d.lock.Lock()
dqs := d.getDynacastQualitiesLocked()
d.lock.Unlock()
for _, dq := range dqs {
dq.ClearSubscriberNodes()
}
}
func (d *dynacastManagerBase) getOrCreateDynacastQuality(mimeType mime.MimeType) dynacastQuality {
d.lock.Lock()
defer d.lock.Unlock()
if d.isClosed || mimeType == mime.MimeTypeUnknown {
return nil
}
if dq := d.dynacastQuality[mimeType]; dq != nil {
return dq
}
dq := d.params.OnDynacastQualityCreate(mimeType)
dq.Start()
d.dynacastQuality[mimeType] = dq
return dq
}
func (d *dynacastManagerBase) getDynacastQualitiesLocked() []dynacastQuality {
return slices.Collect(maps.Values(d.dynacastQuality))
}
@@ -15,148 +15,85 @@
package dynacast
import (
"sync"
"maps"
"time"
"github.com/bep/debounce"
"golang.org/x/exp/maps"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/utils"
)
type DynacastManagerParams struct {
var _ DynacastManager = (*dynacastManagerVideo)(nil)
var _ dynacastQualityListener = (*dynacastManagerVideo)(nil)
type DynacastManagerVideoParams struct {
DynacastPauseDelay time.Duration
Listener DynacastManagerListener
Logger logger.Logger
}
type DynacastManager struct {
params DynacastManagerParams
type dynacastManagerVideo struct {
params DynacastManagerVideoParams
lock sync.RWMutex
regressedCodec map[mime.MimeType]struct{}
dynacastQuality map[mime.MimeType]*DynacastQuality
maxSubscribedQuality map[mime.MimeType]livekit.VideoQuality
committedMaxSubscribedQuality map[mime.MimeType]livekit.VideoQuality
maxSubscribedQualityDebounce func(func())
maxSubscribedQualityDebouncePending bool
qualityNotifyOpQueue *utils.OpsQueue
isClosed bool
onSubscribedMaxQualityChange func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality)
*dynacastManagerBase
}
func NewDynacastManager(params DynacastManagerParams) *DynacastManager {
func NewDynacastManagerVideo(params DynacastManagerVideoParams) DynacastManager {
if params.Logger == nil {
params.Logger = logger.GetLogger()
}
d := &DynacastManager{
d := &dynacastManagerVideo{
params: params,
regressedCodec: make(map[mime.MimeType]struct{}),
dynacastQuality: make(map[mime.MimeType]*DynacastQuality),
maxSubscribedQuality: make(map[mime.MimeType]livekit.VideoQuality),
committedMaxSubscribedQuality: make(map[mime.MimeType]livekit.VideoQuality),
qualityNotifyOpQueue: utils.NewOpsQueue(utils.OpsQueueParams{
Name: "quality-notify",
MinSize: 64,
FlushOnStop: true,
Logger: params.Logger,
}),
}
if params.DynacastPauseDelay > 0 {
d.maxSubscribedQualityDebounce = debounce.New(params.DynacastPauseDelay)
}
d.qualityNotifyOpQueue.Start()
d.dynacastManagerBase = newDynacastManagerBase(dynacastManagerBaseParams{
Logger: params.Logger,
OpsQueueDepth: 64,
OnRestart: func() {
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality)
},
OnDynacastQualityCreate: func(mimeType mime.MimeType) dynacastQuality {
dq := newDynacastQualityVideo(dynacastQualityVideoParams{
MimeType: mimeType,
Listener: d,
Logger: d.params.Logger,
})
return dq
},
OnRegressCodec: func(fromMime, toMime mime.MimeType) {
d.maxSubscribedQuality[fromMime] = livekit.VideoQuality_OFF
// if the new codec is not added, notify the publisher to start publishing
if _, ok := d.maxSubscribedQuality[toMime]; !ok {
d.maxSubscribedQuality[toMime] = livekit.VideoQuality_HIGH
}
},
OnUpdateNeeded: d.update,
})
return d
}
func (d *DynacastManager) OnSubscribedMaxQualityChange(f func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality)) {
d.lock.Lock()
d.onSubscribedMaxQualityChange = f
d.lock.Unlock()
}
func (d *DynacastManager) AddCodec(mime mime.MimeType) {
d.getOrCreateDynacastQuality(mime)
}
func (d *DynacastManager) HandleCodecRegression(fromMime, toMime mime.MimeType) {
fromDq := d.getOrCreateDynacastQuality(fromMime)
d.lock.Lock()
if d.isClosed {
d.lock.Unlock()
return
}
if fromDq == nil {
// should not happen as we have added the codec on setup receiver
d.params.Logger.Warnw("regression from codec not found", nil, "mime", fromMime)
d.lock.Unlock()
return
}
d.regressedCodec[fromMime] = struct{}{}
d.maxSubscribedQuality[fromMime] = livekit.VideoQuality_OFF
// if the new codec is not added, notify the publisher to start publishing
if _, ok := d.maxSubscribedQuality[toMime]; !ok {
d.maxSubscribedQuality[toMime] = livekit.VideoQuality_HIGH
}
d.lock.Unlock()
d.update(false)
fromDq.Stop()
fromDq.RegressTo(d.getOrCreateDynacastQuality(toMime))
}
func (d *DynacastManager) Restart() {
d.lock.Lock()
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality)
dqs := d.getDynacastQualitiesLocked()
d.lock.Unlock()
for _, dq := range dqs {
dq.Restart()
}
}
func (d *DynacastManager) Close() {
d.qualityNotifyOpQueue.Stop()
d.lock.Lock()
dqs := d.getDynacastQualitiesLocked()
d.dynacastQuality = make(map[mime.MimeType]*DynacastQuality)
d.isClosed = true
d.lock.Unlock()
for _, dq := range dqs {
dq.Stop()
}
}
// THere are situations like track unmute or streaming from a different node
// where subscribed quality needs to sent to the provider immediately.
// This bypasses any debouncing and forces a subscribed quality update
// with immediate effect.
func (d *DynacastManager) ForceUpdate() {
d.update(true)
}
// It is possible for tracks to be in pending close state. When track
// is waiting to be closed, a node is not streaming a track. This can
// be used to force an update announcing that subscribed quality is OFF,
// i.e. indicating not pulling track any more.
func (d *DynacastManager) ForceQuality(quality livekit.VideoQuality) {
func (d *dynacastManagerVideo) ForceQuality(quality livekit.VideoQuality) {
d.lock.Lock()
defer d.lock.Unlock()
@@ -167,14 +104,21 @@ func (d *DynacastManager) ForceQuality(quality livekit.VideoQuality) {
d.enqueueSubscribedQualityChange()
}
func (d *DynacastManager) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, mime mime.MimeType, quality livekit.VideoQuality) {
func (d *dynacastManagerVideo) NotifySubscriberMaxQuality(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
quality livekit.VideoQuality,
) {
dq := d.getOrCreateDynacastQuality(mime)
if dq != nil {
dq.NotifySubscriberMaxQuality(subscriberID, quality)
}
}
func (d *DynacastManager) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []types.SubscribedCodecQuality) {
func (d *dynacastManagerVideo) NotifySubscriberNodeMaxQuality(
nodeID livekit.NodeID,
qualities []types.SubscribedCodecQuality,
) {
for _, quality := range qualities {
dq := d.getOrCreateDynacastQuality(quality.CodecMime)
if dq != nil {
@@ -183,34 +127,10 @@ func (d *DynacastManager) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
}
}
func (d *DynacastManager) getOrCreateDynacastQuality(mimeType mime.MimeType) *DynacastQuality {
d.lock.Lock()
defer d.lock.Unlock()
if d.isClosed || mimeType == mime.MimeTypeUnknown {
return nil
}
if dq := d.dynacastQuality[mimeType]; dq != nil {
return dq
}
dq := NewDynacastQuality(DynacastQualityParams{
MimeType: mimeType,
Logger: d.params.Logger,
})
dq.OnSubscribedMaxQualityChange(d.updateMaxQualityForMime)
dq.Start()
d.dynacastQuality[mimeType] = dq
return dq
}
func (d *DynacastManager) getDynacastQualitiesLocked() []*DynacastQuality {
return maps.Values(d.dynacastQuality)
}
func (d *DynacastManager) updateMaxQualityForMime(mime mime.MimeType, maxQuality livekit.VideoQuality) {
func (d *dynacastManagerVideo) OnUpdateMaxQualityForMime(
mime mime.MimeType,
maxQuality livekit.VideoQuality,
) {
d.lock.Lock()
if _, ok := d.regressedCodec[mime]; !ok {
d.maxSubscribedQuality[mime] = maxQuality
@@ -220,10 +140,11 @@ func (d *DynacastManager) updateMaxQualityForMime(mime mime.MimeType, maxQuality
d.update(false)
}
func (d *DynacastManager) update(force bool) {
func (d *dynacastManagerVideo) update(force bool) {
d.lock.Lock()
d.params.Logger.Debugw("processing quality change",
d.params.Logger.Debugw(
"processing quality change",
"force", force,
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
"maxSubscribedQuality", d.maxSubscribedQuality,
@@ -260,7 +181,8 @@ func (d *DynacastManager) update(force bool) {
if downgradesOnly && d.maxSubscribedQualityDebounce != nil {
if !d.maxSubscribedQualityDebouncePending {
d.params.Logger.Debugw("debouncing quality downgrade",
d.params.Logger.Debugw(
"debouncing quality downgrade",
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
"maxSubscribedQuality", d.maxSubscribedQuality,
)
@@ -269,7 +191,8 @@ func (d *DynacastManager) update(force bool) {
})
d.maxSubscribedQualityDebouncePending = true
} else {
d.params.Logger.Debugw("quality downgrade waiting for debounce",
d.params.Logger.Debugw(
"quality downgrade waiting for debounce",
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
"maxSubscribedQuality", d.maxSubscribedQuality,
)
@@ -285,7 +208,8 @@ func (d *DynacastManager) update(force bool) {
d.maxSubscribedQualityDebouncePending = false
}
d.params.Logger.Debugw("committing quality change",
d.params.Logger.Debugw(
"committing quality change",
"force", force,
"committedMaxSubscribedQuality", d.committedMaxSubscribedQuality,
"maxSubscribedQuality", d.maxSubscribedQuality,
@@ -293,16 +217,14 @@ func (d *DynacastManager) update(force bool) {
// commit change
d.committedMaxSubscribedQuality = make(map[mime.MimeType]livekit.VideoQuality, len(d.maxSubscribedQuality))
for mime, quality := range d.maxSubscribedQuality {
d.committedMaxSubscribedQuality[mime] = quality
}
maps.Copy(d.committedMaxSubscribedQuality, d.maxSubscribedQuality)
d.enqueueSubscribedQualityChange()
d.lock.Unlock()
}
func (d *DynacastManager) enqueueSubscribedQualityChange() {
if d.isClosed || d.onSubscribedMaxQualityChange == nil {
func (d *dynacastManagerVideo) enqueueSubscribedQualityChange() {
if d.isClosed || d.params.Listener == nil {
return
}
@@ -343,7 +265,7 @@ func (d *DynacastManager) enqueueSubscribedQualityChange() {
"subscribedCodecs", subscribedCodecs,
"maxSubscribedQualities", maxSubscribedQualities,
)
d.qualityNotifyOpQueue.Enqueue(func() {
d.onSubscribedMaxQualityChange(subscribedCodecs, maxSubscribedQualities)
d.notifyOpsQueue.Enqueue(func() {
d.params.Listener.OnDynacastSubscribedMaxQualityChange(subscribedCodecs, maxSubscribedQualities)
})
}
+168
View File
@@ -0,0 +1,168 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dynacast
import (
"sync"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
var _ dynacastQuality = (*dynacastQualityAudio)(nil)
type dynacastQualityAudioParams struct {
MimeType mime.MimeType
Listener dynacastQualityListener
Logger logger.Logger
}
// dynacastQualityAudio manages enable a single receiver of a media track
type dynacastQualityAudio struct {
params dynacastQualityAudioParams
// quality level enable/disable
lock sync.RWMutex
initialized bool
subscriberEnables map[livekit.ParticipantID]bool
subscriberNodeEnables map[livekit.NodeID]bool
enabled bool
regressTo dynacastQuality
dynacastQualityNull
}
func newDynacastQualityAudio(params dynacastQualityAudioParams) dynacastQuality {
return &dynacastQualityAudio{
params: params,
subscriberEnables: make(map[livekit.ParticipantID]bool),
subscriberNodeEnables: make(map[livekit.NodeID]bool),
}
}
func (d *dynacastQualityAudio) Start() {
d.reset()
}
func (d *dynacastQualityAudio) Restart() {
d.reset()
}
func (d *dynacastQualityAudio) Stop() {
}
func (d *dynacastQualityAudio) NotifySubscription(subscriberID livekit.ParticipantID, enabled bool) {
d.params.Logger.Debugw(
"setting subscriber codec enable",
"mime", d.params.MimeType,
"subscriberID", subscriberID,
"enabled", enabled,
)
d.lock.Lock()
if r := d.regressTo; r != nil {
d.lock.Unlock()
return
}
if !enabled {
delete(d.subscriberEnables, subscriberID)
} else {
d.subscriberEnables[subscriberID] = true
}
d.lock.Unlock()
d.updateQualityChange(false)
}
func (d *dynacastQualityAudio) NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool) {
d.params.Logger.Debugw(
"setting subscriber node codec enabled",
"mime", d.params.MimeType,
"subscriberNodeID", nodeID,
"enabled", enabled,
)
d.lock.Lock()
if r := d.regressTo; r != nil {
// the downstream node will synthesize correct enable (its dynacast manager has codec regression), just ignore it
d.params.Logger.Debugw(
"ignoring node codec change, regressed to another dynacast quality",
"mime", d.params.MimeType,
"regressedMime", d.regressTo.Mime(),
)
d.lock.Unlock()
return
}
if !enabled {
delete(d.subscriberNodeEnables, nodeID)
} else {
d.subscriberNodeEnables[nodeID] = true
}
d.lock.Unlock()
d.updateQualityChange(false)
}
func (d *dynacastQualityAudio) ClearSubscriberNodes() {
d.lock.Lock()
d.subscriberNodeEnables = make(map[livekit.NodeID]bool)
d.lock.Unlock()
d.updateQualityChange(false)
}
func (d *dynacastQualityAudio) Mime() mime.MimeType {
return d.params.MimeType
}
func (d *dynacastQualityAudio) RegressTo(other dynacastQuality) {
d.lock.Lock()
d.regressTo = other
d.lock.Unlock()
other.Restart()
}
func (d *dynacastQualityAudio) reset() {
d.lock.Lock()
d.initialized = false
d.lock.Unlock()
}
func (d *dynacastQualityAudio) updateQualityChange(force bool) {
d.lock.Lock()
enabled := len(d.subscriberEnables) != 0 || len(d.subscriberNodeEnables) != 0
if enabled == d.enabled && d.initialized && !force {
d.lock.Unlock()
return
}
d.initialized = true
d.enabled = enabled
d.params.Logger.Debugw(
"notifying enabled change",
"mime", d.params.MimeType,
"enabled", d.enabled,
"subscriberNodeEnables", d.subscriberNodeEnables,
"subscribedEnables", d.subscriberEnables,
"force", force,
)
d.lock.Unlock()
d.params.Listener.OnUpdateAudioCodecForMime(d.params.MimeType, enabled)
}
@@ -18,23 +18,26 @@ import (
"sync"
"time"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
var _ dynacastQuality = (*dynacastQualityVideo)(nil)
const (
initialQualityUpdateWait = 10 * time.Second
)
type DynacastQualityParams struct {
type dynacastQualityVideoParams struct {
MimeType mime.MimeType
Listener dynacastQualityListener
Logger logger.Logger
}
// DynacastQuality manages max subscribed quality of a single receiver of a media track
type DynacastQuality struct {
params DynacastQualityParams
// dynacastQualityVideo manages max subscribed quality of a single receiver of a media track
type dynacastQualityVideo struct {
params dynacastQualityVideoParams
// quality level enable/disable
lock sync.RWMutex
@@ -43,38 +46,32 @@ type DynacastQuality struct {
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality
maxSubscribedQuality livekit.VideoQuality
maxQualityTimer *time.Timer
regressTo *DynacastQuality
regressTo dynacastQuality
onSubscribedMaxQualityChange func(mimeType mime.MimeType, maxSubscribedQuality livekit.VideoQuality)
dynacastQualityNull
}
func NewDynacastQuality(params DynacastQualityParams) *DynacastQuality {
return &DynacastQuality{
func newDynacastQualityVideo(params dynacastQualityVideoParams) dynacastQuality {
return &dynacastQualityVideo{
params: params,
maxSubscriberQuality: make(map[livekit.ParticipantID]livekit.VideoQuality),
maxSubscriberNodeQuality: make(map[livekit.NodeID]livekit.VideoQuality),
}
}
func (d *DynacastQuality) Start() {
func (d *dynacastQualityVideo) Start() {
d.reset()
}
func (d *DynacastQuality) Restart() {
func (d *dynacastQualityVideo) Restart() {
d.reset()
}
func (d *DynacastQuality) Stop() {
func (d *dynacastQualityVideo) Stop() {
d.stopMaxQualityTimer()
}
func (d *DynacastQuality) OnSubscribedMaxQualityChange(f func(mimeType mime.MimeType, maxSubscribedQuality livekit.VideoQuality)) {
d.lock.Lock()
defer d.lock.Unlock()
d.onSubscribedMaxQualityChange = f
}
func (d *DynacastQuality) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
func (d *dynacastQualityVideo) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
d.params.Logger.Debugw(
"setting subscriber max quality",
"mime", d.params.MimeType,
@@ -99,7 +96,7 @@ func (d *DynacastQuality) NotifySubscriberMaxQuality(subscriberID livekit.Partic
d.updateQualityChange(false)
}
func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
func (d *dynacastQualityVideo) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
d.params.Logger.Debugw(
"setting subscriber node max quality",
"mime", d.params.MimeType,
@@ -110,8 +107,12 @@ func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
d.lock.Lock()
if r := d.regressTo; r != nil {
// the downstream node will synthesize correct quality notify (its dynacast manager has codec regression), just ignore it
d.params.Logger.Debugw(
"ignoring node quality change, regressed to another dynacast quality",
"mime", d.params.MimeType,
"regressedMime", d.regressTo.Mime(),
)
d.lock.Unlock()
r.params.Logger.Debugw("ignoring node quality change, regressed to another dynacast quality", "mime", d.params.MimeType)
return
}
@@ -125,7 +126,19 @@ func (d *DynacastQuality) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID,
d.updateQualityChange(false)
}
func (d *DynacastQuality) RegressTo(other *DynacastQuality) {
func (d *dynacastQualityVideo) ClearSubscriberNodes() {
d.lock.Lock()
d.maxSubscriberNodeQuality = make(map[livekit.NodeID]livekit.VideoQuality)
d.lock.Unlock()
d.updateQualityChange(false)
}
func (d *dynacastQualityVideo) Mime() mime.MimeType {
return d.params.MimeType
}
func (d *dynacastQualityVideo) RegressTo(other dynacastQuality) {
d.lock.Lock()
d.regressTo = other
maxSubscriberQuality := d.maxSubscriberQuality
@@ -134,33 +147,41 @@ func (d *DynacastQuality) RegressTo(other *DynacastQuality) {
d.maxSubscriberNodeQuality = make(map[livekit.NodeID]livekit.VideoQuality)
d.lock.Unlock()
other.lock.Lock()
other.Replace(maxSubscriberQuality, maxSubscriberNodeQuality)
}
func (d *dynacastQualityVideo) Replace(
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
) {
d.lock.Lock()
for subID, quality := range maxSubscriberQuality {
if otherQuality, ok := other.maxSubscriberQuality[subID]; ok {
if oldQuality, ok := d.maxSubscriberQuality[subID]; ok {
// no QUALITY_OFF in the map
if quality > otherQuality {
other.maxSubscriberQuality[subID] = quality
if quality > oldQuality {
d.maxSubscriberQuality[subID] = quality
}
} else {
other.maxSubscriberQuality[subID] = quality
d.maxSubscriberQuality[subID] = quality
}
}
for nodeID, quality := range maxSubscriberNodeQuality {
if otherQuality, ok := other.maxSubscriberNodeQuality[nodeID]; ok {
if oldQuality, ok := d.maxSubscriberNodeQuality[nodeID]; ok {
// no QUALITY_OFF in the map
if quality > otherQuality {
other.maxSubscriberNodeQuality[nodeID] = quality
if quality > oldQuality {
d.maxSubscriberNodeQuality[nodeID] = quality
}
} else {
other.maxSubscriberNodeQuality[nodeID] = quality
d.maxSubscriberNodeQuality[nodeID] = quality
}
}
other.lock.Unlock()
other.Restart()
d.lock.Unlock()
d.Restart()
}
func (d *DynacastQuality) reset() {
func (d *dynacastQualityVideo) reset() {
d.lock.Lock()
d.initialized = false
d.lock.Unlock()
@@ -168,7 +189,7 @@ func (d *DynacastQuality) reset() {
d.startMaxQualityTimer()
}
func (d *DynacastQuality) updateQualityChange(force bool) {
func (d *dynacastQualityVideo) updateQualityChange(force bool) {
d.lock.Lock()
maxSubscribedQuality := livekit.VideoQuality_OFF
for _, subQuality := range d.maxSubscriberQuality {
@@ -189,22 +210,20 @@ func (d *DynacastQuality) updateQualityChange(force bool) {
d.initialized = true
d.maxSubscribedQuality = maxSubscribedQuality
d.params.Logger.Debugw("notifying quality change",
d.params.Logger.Debugw(
"notifying quality change",
"mime", d.params.MimeType,
"maxSubscriberQuality", d.maxSubscriberQuality,
"maxSubscriberNodeQuality", d.maxSubscriberNodeQuality,
"maxSubscribedQuality", d.maxSubscribedQuality,
"force", force,
)
onSubscribedMaxQualityChange := d.onSubscribedMaxQualityChange
d.lock.Unlock()
if onSubscribedMaxQualityChange != nil {
onSubscribedMaxQualityChange(d.params.MimeType, maxSubscribedQuality)
}
d.params.Listener.OnUpdateMaxQualityForMime(d.params.MimeType, maxSubscribedQuality)
}
func (d *DynacastQuality) startMaxQualityTimer() {
func (d *dynacastQualityVideo) startMaxQualityTimer() {
d.lock.Lock()
defer d.lock.Unlock()
@@ -219,7 +238,7 @@ func (d *DynacastQuality) startMaxQualityTimer() {
})
}
func (d *DynacastQuality) stopMaxQualityTimer() {
func (d *dynacastQualityVideo) stopMaxQualityTimer() {
d.lock.Lock()
defer d.lock.Unlock()
+185
View File
@@ -0,0 +1,185 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dynacast
import (
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
type DynacastManagerListener interface {
OnDynacastSubscribedMaxQualityChange(
subscribedQualities []*livekit.SubscribedCodec,
maxSubscribedQualities []types.SubscribedCodecQuality,
)
OnDynacastSubscribedAudioCodecChange(codecs []*livekit.SubscribedAudioCodec)
}
var _ DynacastManagerListener = (*DynacastManagerListenerNull)(nil)
type DynacastManagerListenerNull struct {
}
func (d *DynacastManagerListenerNull) OnDynacastSubscribedMaxQualityChange(
subscribedQualities []*livekit.SubscribedCodec,
maxSubscribedQualities []types.SubscribedCodecQuality,
) {
}
func (d *DynacastManagerListenerNull) OnDynacastSubscribedAudioCodecChange(
codecs []*livekit.SubscribedAudioCodec,
) {
}
// -----------------------------------------
type DynacastManager interface {
AddCodec(mime mime.MimeType)
HandleCodecRegression(fromMime, toMime mime.MimeType)
Restart()
Close()
ForceUpdate()
ForceQuality(quality livekit.VideoQuality)
ForceEnable(enabled bool)
NotifySubscriberMaxQuality(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
quality livekit.VideoQuality,
)
NotifySubscription(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
enabled bool,
)
NotifySubscriberNodeMaxQuality(
nodeID livekit.NodeID,
qualities []types.SubscribedCodecQuality,
)
NotifySubscriptionNode(
nodeID livekit.NodeID,
codecs []*livekit.SubscribedAudioCodec,
)
ClearSubscriberNodes()
}
var _ DynacastManager = (*dynacastManagerNull)(nil)
type dynacastManagerNull struct {
}
func (d *dynacastManagerNull) AddCodec(mime mime.MimeType) {}
func (d *dynacastManagerNull) HandleCodecRegression(fromMime, toMime mime.MimeType) {}
func (d *dynacastManagerNull) Restart() {}
func (d *dynacastManagerNull) Close() {}
func (d *dynacastManagerNull) ForceUpdate() {}
func (d *dynacastManagerNull) ForceQuality(quality livekit.VideoQuality) {}
func (d *dynacastManagerNull) ForceEnable(enabled bool) {}
func (d *dynacastManagerNull) NotifySubscriberMaxQuality(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
quality livekit.VideoQuality,
) {
}
func (d *dynacastManagerNull) NotifySubscription(
subscriberID livekit.ParticipantID,
mime mime.MimeType,
enabled bool,
) {
}
func (d *dynacastManagerNull) NotifySubscriberNodeMaxQuality(
nodeID livekit.NodeID,
qualities []types.SubscribedCodecQuality,
) {
}
func (d *dynacastManagerNull) NotifySubscriptionNode(
nodeID livekit.NodeID,
codecs []*livekit.SubscribedAudioCodec,
) {
}
func (d *dynacastManagerNull) ClearSubscriberNodes() {}
// ------------------------------------------------
type dynacastQualityListener interface {
OnUpdateMaxQualityForMime(mimeType mime.MimeType, maxQuality livekit.VideoQuality)
OnUpdateAudioCodecForMime(mimeType mime.MimeType, enabled bool)
}
var _ dynacastQualityListener = (*dynacastQualityListenerNull)(nil)
type dynacastQualityListenerNull struct {
}
func (d *dynacastQualityListenerNull) OnUpdateMaxQualityForMime(
mimeType mime.MimeType,
maxQuality livekit.VideoQuality,
) {
}
func (d *dynacastQualityListenerNull) OnUpdateAudioCodecForMime(
mimeType mime.MimeType,
enabled bool,
) {
}
// ------------------------------------------------
type dynacastQuality interface {
Start()
Restart()
Stop()
NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality)
NotifySubscription(subscriberID livekit.ParticipantID, enabled bool)
NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality)
NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool)
ClearSubscriberNodes()
Replace(
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
)
Mime() mime.MimeType
RegressTo(other dynacastQuality)
}
var _ dynacastQuality = (*dynacastQualityNull)(nil)
type dynacastQualityNull struct {
}
func (d *dynacastQualityNull) Start() {}
func (d *dynacastQualityNull) Restart() {}
func (d *dynacastQualityNull) Stop() {}
func (d *dynacastQualityNull) NotifySubscriberMaxQuality(subscriberID livekit.ParticipantID, quality livekit.VideoQuality) {
}
func (d *dynacastQualityNull) NotifySubscription(subscriberID livekit.ParticipantID, enabled bool) {}
func (d *dynacastQualityNull) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quality livekit.VideoQuality) {
}
func (d *dynacastQualityNull) NotifySubscriptionNode(nodeID livekit.NodeID, enabled bool) {}
func (d *dynacastQualityNull) ClearSubscriberNodes() {}
func (d *dynacastQualityNull) Replace(
maxSubscriberQuality map[livekit.ParticipantID]livekit.VideoQuality,
maxSubscriberNodeQuality map[livekit.NodeID]livekit.VideoQuality,
) {
}
func (d *dynacastQualityNull) Mime() mime.MimeType { return mime.MimeTypeUnknown }
func (d *dynacastQualityNull) RegressTo(other dynacastQuality) {}
+22 -20
View File
@@ -29,6 +29,7 @@ import (
type EgressLauncher interface {
StartEgress(context.Context, *rpc.StartEgressRequest) (*livekit.EgressInfo, error)
StopEgress(context.Context, *livekit.StopEgressRequest) (*livekit.EgressInfo, error)
}
func StartParticipantEgress(
@@ -42,16 +43,17 @@ func StartParticipantEgress(
) error {
if req, err := startParticipantEgress(ctx, launcher, opts, identity, roomName, roomID); err != nil {
// send egress failed webhook
ts.NotifyEvent(ctx, &livekit.WebhookEvent{
Event: webhook.EventEgressEnded,
EgressInfo: &livekit.EgressInfo{
RoomId: string(roomID),
RoomName: string(roomName),
Status: livekit.EgressStatus_EGRESS_FAILED,
Error: err.Error(),
Request: &livekit.EgressInfo_Participant{Participant: req},
},
})
info := &livekit.EgressInfo{
RoomId: string(roomID),
RoomName: string(roomName),
Status: livekit.EgressStatus_EGRESS_FAILED,
Error: err.Error(),
Request: &livekit.EgressInfo_Participant{Participant: req},
}
ts.NotifyEgressEvent(ctx, webhook.EventEgressEnded, info)
return err
}
return nil
@@ -103,16 +105,16 @@ func StartTrackEgress(
) error {
if req, err := startTrackEgress(ctx, launcher, opts, track, roomName, roomID); err != nil {
// send egress failed webhook
ts.NotifyEvent(ctx, &livekit.WebhookEvent{
Event: webhook.EventEgressEnded,
EgressInfo: &livekit.EgressInfo{
RoomId: string(roomID),
RoomName: string(roomName),
Status: livekit.EgressStatus_EGRESS_FAILED,
Error: err.Error(),
Request: &livekit.EgressInfo_Track{Track: req},
},
})
info := &livekit.EgressInfo{
RoomId: string(roomID),
RoomName: string(roomName),
Status: livekit.EgressStatus_EGRESS_FAILED,
Error: err.Error(),
Request: &livekit.EgressInfo_Track{Track: req},
}
ts.NotifyEgressEvent(ctx, webhook.EventEgressEnded, info)
return err
}
return nil
-3
View File
@@ -32,9 +32,6 @@ var (
ErrEmptyParticipantID = errors.New("participant ID cannot be empty")
ErrMissingGrants = errors.New("VideoGrant is missing")
ErrInternalError = errors.New("internal error")
ErrNameExceedsLimits = errors.New("name length exceeds limits")
ErrMetadataExceedsLimits = errors.New("metadata size exceeds limits")
ErrAttributesExceedsLimits = errors.New("attributes size exceeds limits")
// Track subscription related
ErrNoTrackPermission = errors.New("participant is not allowed to subscribe to this track")
+117 -122
View File
@@ -20,146 +20,67 @@ import (
"github.com/pion/webrtc/v4"
"github.com/livekit/livekit-server/pkg/sfu/mime"
protoCodecs "github.com/livekit/protocol/codecs"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
)
var OpusCodecCapability = webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeOpus.String(),
ClockRate: 48000,
Channels: 2,
SDPFmtpLine: "minptime=10;useinbandfec=1",
}
var RedCodecCapability = webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeRED.String(),
ClockRate: 48000,
Channels: 2,
SDPFmtpLine: "111/111",
}
var videoRTX = webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeRTX.String(),
ClockRate: 90000,
}
func registerCodecs(me *webrtc.MediaEngine, codecs []*livekit.Codec, rtcpFeedback RTCPFeedbackConfig, filterOutH264HighProfile bool) error {
opusCodec := OpusCodecCapability
opusCodec.RTCPFeedback = rtcpFeedback.Audio
var opusPayload webrtc.PayloadType
if IsCodecEnabled(codecs, opusCodec) {
opusPayload = 111
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: opusCodec,
PayloadType: opusPayload,
}, webrtc.RTPCodecTypeAudio); err != nil {
// audio codecs
if IsCodecEnabled(codecs, protoCodecs.OpusCodecParameters.RTPCodecCapability) {
cp := protoCodecs.OpusCodecParameters
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Audio
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeAudio); err != nil {
return err
}
if IsCodecEnabled(codecs, RedCodecCapability) {
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: RedCodecCapability,
PayloadType: 63,
}, webrtc.RTPCodecTypeAudio); err != nil {
if IsCodecEnabled(codecs, protoCodecs.RedCodecParameters.RTPCodecCapability) {
if err := me.RegisterCodec(protoCodecs.RedCodecParameters, webrtc.RTPCodecTypeAudio); err != nil {
return err
}
}
}
rtxEnabled := IsCodecEnabled(codecs, videoRTX)
for _, codec := range []webrtc.RTPCodecParameters{protoCodecs.PCMUCodecParameters, protoCodecs.PCMACodecParameters} {
if !IsCodecEnabled(codecs, codec.RTPCodecCapability) {
continue
}
h264HighProfileFmtp := "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032"
for _, codec := range []webrtc.RTPCodecParameters{
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP8.String(),
ClockRate: 90000,
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 96,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP9.String(),
ClockRate: 90000,
SDPFmtpLine: "profile-id=0",
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 98,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP9.String(),
ClockRate: 90000,
SDPFmtpLine: "profile-id=1",
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 100,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 125,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f",
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 108,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: h264HighProfileFmtp,
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 123,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeAV1.String(),
ClockRate: 90000,
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 35,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeH265,
ClockRate: 90000,
RTCPFeedback: rtcpFeedback.Video,
},
PayloadType: 116,
},
} {
if filterOutH264HighProfile && codec.RTPCodecCapability.SDPFmtpLine == h264HighProfileFmtp {
cp := codec
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Audio
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeAudio); err != nil {
return err
}
}
// video codecs
rtxEnabled := IsCodecEnabled(codecs, protoCodecs.VideoRTXCodecParameters.RTPCodecCapability)
for _, codec := range protoCodecs.VideoCodecsParameters {
if filterOutH264HighProfile && codec.RTPCodecCapability.SDPFmtpLine == protoCodecs.H264HighProfileFmtp {
continue
}
if mime.IsMimeTypeStringRTX(codec.MimeType) {
continue
}
if IsCodecEnabled(codecs, codec.RTPCodecCapability) {
if err := me.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
return err
}
if rtxEnabled {
if err := me.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeRTX.String(),
ClockRate: 90000,
SDPFmtpLine: fmt.Sprintf("apt=%d", codec.PayloadType),
},
PayloadType: codec.PayloadType + 1,
}, webrtc.RTPCodecTypeVideo); err != nil {
return err
}
}
if !IsCodecEnabled(codecs, codec.RTPCodecCapability) {
continue
}
cp := codec
cp.RTPCodecCapability.RTCPFeedback = rtcpFeedback.Video
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeVideo); err != nil {
return err
}
if !rtxEnabled {
continue
}
cp = protoCodecs.VideoRTXCodecParameters
cp.RTPCodecCapability.SDPFmtpLine = fmt.Sprintf("apt=%d", codec.PayloadType)
cp.PayloadType = codec.PayloadType + 1
if err := me.RegisterCodec(cp, webrtc.RTPCodecTypeVideo); err != nil {
return err
}
}
return nil
@@ -215,3 +136,77 @@ func selectAlternativeVideoCodec(enabledCodecs []*livekit.Codec) string {
// no viable codec in the list of enabled codecs, fall back to the most widely supported codec
return mime.MimeTypeVP8.String()
}
func selectAlternativeAudioCodec(enabledCodecs []*livekit.Codec) string {
for _, c := range enabledCodecs {
if mime.IsMimeTypeStringAudio(c.Mime) {
return c.Mime
}
}
// no viable codec in the list of enabled codecs, fall back to the most widely supported codec
return mime.MimeTypeOpus.String()
}
// mergeCodecsByMime returns a union b, deduplicated by mime type.
func mergeCodecsByMime(a, b []*livekit.Codec) []*livekit.Codec {
merged := make([]*livekit.Codec, 0, len(a)+len(b))
merged = append(merged, a...)
for _, c := range b {
seen := false
for _, existing := range a {
if mime.IsMimeTypeStringEqual(c.Mime, existing.Mime) {
seen = true
break
}
}
if !seen {
merged = append(merged, c)
}
}
return merged
}
func filterCodecs(
codecs []webrtc.RTPCodecParameters,
enabledCodecs []*livekit.Codec,
rtcpFeedbackConfig RTCPFeedbackConfig,
filterOutH264HighProfile bool,
) []webrtc.RTPCodecParameters {
filteredCodecs := make([]webrtc.RTPCodecParameters, 0, len(codecs))
for _, c := range codecs {
if filterOutH264HighProfile && isH264HighProfile(c.RTPCodecCapability.SDPFmtpLine) {
continue
}
for _, enabledCodec := range enabledCodecs {
if mime.NormalizeMimeType(enabledCodec.Mime) == mime.NormalizeMimeType(c.RTPCodecCapability.MimeType) {
if !mime.IsMimeTypeStringEqual(c.RTPCodecCapability.MimeType, mime.MimeTypeRTX.String()) {
if mime.IsMimeTypeStringVideo(c.RTPCodecCapability.MimeType) {
c.RTPCodecCapability.RTCPFeedback = rtcpFeedbackConfig.Video
} else {
c.RTPCodecCapability.RTCPFeedback = rtcpFeedbackConfig.Audio
}
}
filteredCodecs = append(filteredCodecs, c)
break
}
}
}
return filteredCodecs
}
func isH264HighProfile(fmtp string) bool {
params := strings.Split(fmtp, ";")
for _, param := range params {
parts := strings.Split(param, "=")
if len(parts) == 2 {
if parts[0] == "profile-level-id" {
// https://datatracker.ietf.org/doc/html/rfc6184#section-8.1
// hex value 0x64 for profile_idc is high profile
return strings.HasPrefix(parts[1], "64")
}
}
}
return false
}
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"github.com/pion/webrtc/v4"
"github.com/stretchr/testify/require"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
)
+348 -122
View File
@@ -15,7 +15,6 @@
package rtc
import (
"context"
"math"
"sync"
"time"
@@ -24,8 +23,11 @@ import (
"github.com/pion/webrtc/v4"
"go.uber.org/atomic"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
"github.com/livekit/protocol/utils/mono"
"github.com/livekit/livekit-server/pkg/config"
"github.com/livekit/livekit-server/pkg/rtc/dynacast"
@@ -33,77 +35,100 @@ import (
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/connectionquality"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/sfu/interceptor"
"github.com/livekit/livekit-server/pkg/telemetry"
util "github.com/livekit/mediatransportutil"
)
var _ types.LocalMediaTrack = (*MediaTrack)(nil)
// MediaTrack represents a WebRTC track that needs to be forwarded
// Implements MediaTrack and PublishedTrack interface
type MediaTrack struct {
params MediaTrackParams
numUpTracks atomic.Uint32
buffer *buffer.Buffer
everSubscribed atomic.Bool
*MediaTrackReceiver
*MediaLossProxy
dynacastManager *dynacast.DynacastManager
dynacastManager dynacast.DynacastManager
lock sync.RWMutex
rttFromXR atomic.Bool
enableRegression bool
backupCodecPolicy livekit.BackupCodecPolicy
regressionTargetCodec mime.MimeType
regressionTargetCodecReceived bool
onSubscribedMaxQualityChange func(
trackID livekit.TrackID,
trackInfo *livekit.TrackInfo,
subscribedQualities []*livekit.SubscribedCodec,
maxSubscribedQualities []types.SubscribedCodecQuality,
) error
onSubscribedAudioCodecChange func(
trackID livekit.TrackID,
codecs []*livekit.SubscribedAudioCodec,
) error
}
type MediaTrackParams struct {
SignalCid string
SdpCid string
ParticipantID livekit.ParticipantID
ParticipantIdentity livekit.ParticipantIdentity
ParticipantVersion uint32
BufferFactory *buffer.Factory
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
PLIThrottleConfig sfu.PLIThrottleConfig
AudioConfig sfu.AudioConfig
VideoConfig config.VideoConfig
Telemetry telemetry.TelemetryService
Logger logger.Logger
SimTracks map[uint32]SimulcastTrackInfo
OnRTCP func([]rtcp.Packet)
ForwardStats *sfu.ForwardStats
OnTrackEverSubscribed func(livekit.TrackID)
ParticipantID func() livekit.ParticipantID
ParticipantIdentity livekit.ParticipantIdentity
ParticipantVersion uint32
ParticipantCountry string
ParticipantKind livekit.ParticipantInfo_Kind
ParticipantKindDetails []livekit.ParticipantInfo_KindDetail
BufferFactory *buffer.Factory
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
PLIThrottleConfig sfu.PLIThrottleConfig
AudioConfig sfu.AudioConfig
VideoConfig config.VideoConfig
TelemetryListener types.ParticipantTelemetryListener
Logger logger.Logger
Reporter roomobs.TrackReporter
SimTracks map[uint32]interceptor.SimulcastTrackInfo
OnRTCP func([]rtcp.Packet)
ForwardStats *sfu.ForwardStats
OnTrackEverSubscribed func(livekit.TrackID)
ShouldRegressCodec func() bool
PreferVideoSizeFromMedia bool
EnableRTPStreamRestartDetection bool
UpdateTrackInfoByVideoSizeChange bool
ForceBackupCodecPolicySimulcast bool
}
func NewMediaTrack(params MediaTrackParams, ti *livekit.TrackInfo) *MediaTrack {
t := &MediaTrack{
params: params,
params: params,
backupCodecPolicy: ti.BackupCodecPolicy,
}
// TODO: disable codec regression until simulcast-codec clients knows that
if ti.BackupCodecPolicy == livekit.BackupCodecPolicy_REGRESSION && len(ti.Codecs) > 1 {
t.enableRegression = true
if t.params.ForceBackupCodecPolicySimulcast {
t.backupCodecPolicy = livekit.BackupCodecPolicy_SIMULCAST
}
if t.backupCodecPolicy != livekit.BackupCodecPolicy_SIMULCAST && len(ti.Codecs) > 1 {
t.regressionTargetCodec = mime.NormalizeMimeType(ti.Codecs[1].MimeType)
t.params.Logger.Debugw("track enabled codec regression", "regressionCodec", t.regressionTargetCodec)
}
t.MediaTrackReceiver = NewMediaTrackReceiver(MediaTrackReceiverParams{
MediaTrack: t,
IsRelayed: false,
ParticipantID: params.ParticipantID,
ParticipantIdentity: params.ParticipantIdentity,
ParticipantVersion: params.ParticipantVersion,
ReceiverConfig: params.ReceiverConfig,
SubscriberConfig: params.SubscriberConfig,
AudioConfig: params.AudioConfig,
Telemetry: params.Telemetry,
Logger: params.Logger,
RegressionTargetCodec: t.regressionTargetCodec,
MediaTrack: t,
IsRelayed: false,
ParticipantID: params.ParticipantID,
ParticipantIdentity: params.ParticipantIdentity,
ParticipantVersion: params.ParticipantVersion,
ReceiverConfig: params.ReceiverConfig,
SubscriberConfig: params.SubscriberConfig,
AudioConfig: params.AudioConfig,
TelemetryListener: params.TelemetryListener,
Logger: params.Logger,
RegressionTargetCodec: t.regressionTargetCodec,
PreferVideoSizeFromMedia: params.PreferVideoSizeFromMedia,
}, ti)
if ti.Type == livekit.TrackType_AUDIO {
@@ -118,27 +143,57 @@ func NewMediaTrack(params MediaTrackParams, ti *livekit.TrackInfo) *MediaTrack {
t.MediaTrackReceiver.OnMediaLossFeedback(t.MediaLossProxy.HandleMaxLossFeedback)
}
if ti.Type == livekit.TrackType_VIDEO {
t.dynacastManager = dynacast.NewDynacastManager(dynacast.DynacastManagerParams{
switch ti.Type {
case livekit.TrackType_VIDEO:
t.dynacastManager = dynacast.NewDynacastManagerVideo(dynacast.DynacastManagerVideoParams{
DynacastPauseDelay: params.VideoConfig.DynacastPauseDelay,
Listener: t,
Logger: params.Logger,
})
t.MediaTrackReceiver.OnSetupReceiver(func(mime mime.MimeType) {
case livekit.TrackType_AUDIO:
if len(ti.Codecs) > 1 {
t.dynacastManager = dynacast.NewDynacastManagerAudio(dynacast.DynacastManagerAudioParams{
Listener: t,
Logger: params.Logger,
})
}
}
t.MediaTrackReceiver.OnSetupReceiver(func(mime mime.MimeType) {
if t.dynacastManager != nil {
t.dynacastManager.AddCodec(mime)
})
t.MediaTrackReceiver.OnSubscriberMaxQualityChange(
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, layer int32) {
}
})
t.MediaTrackReceiver.OnSubscriberMaxQualityChange(
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, layer int32) {
if t.dynacastManager != nil {
t.dynacastManager.NotifySubscriberMaxQuality(
subscriberID,
mimeType,
buffer.SpatialLayerToVideoQuality(layer, t.MediaTrackReceiver.TrackInfo()),
buffer.GetVideoQualityForSpatialLayer(
mimeType,
layer,
t.MediaTrackReceiver.TrackInfo(),
),
)
},
)
t.MediaTrackReceiver.OnCodecRegression(func(old, new webrtc.RTPCodecParameters) {
t.dynacastManager.HandleCodecRegression(mime.NormalizeMimeType(old.MimeType), mime.NormalizeMimeType(new.MimeType))
})
}
}
},
)
t.MediaTrackReceiver.OnSubscriberAudioCodecChange(
func(subscriberID livekit.ParticipantID, mimeType mime.MimeType, enabled bool) {
if t.dynacastManager != nil {
t.dynacastManager.NotifySubscription(subscriberID, mimeType, enabled)
}
},
)
t.MediaTrackReceiver.OnCodecRegression(func(old, new webrtc.RTPCodecParameters) {
if t.dynacastManager != nil {
t.dynacastManager.HandleCodecRegression(
mime.NormalizeMimeType(old.MimeType),
mime.NormalizeMimeType(new.MimeType),
)
}
})
return t
}
@@ -151,24 +206,20 @@ func (t *MediaTrack) OnSubscribedMaxQualityChange(
maxSubscribedQualities []types.SubscribedCodecQuality,
) error,
) {
if t.dynacastManager == nil {
return
}
t.lock.Lock()
t.onSubscribedMaxQualityChange = f
t.lock.Unlock()
}
handler := func(subscribedQualities []*livekit.SubscribedCodec, maxSubscribedQualities []types.SubscribedCodecQuality) {
if f != nil && !t.IsMuted() {
_ = f(t.ID(), t.ToProto(), subscribedQualities, maxSubscribedQualities)
}
for _, q := range maxSubscribedQualities {
receiver := t.Receiver(q.CodecMime)
if receiver != nil {
receiver.SetMaxExpectedSpatialLayer(buffer.VideoQualityToSpatialLayer(q.Quality, t.MediaTrackReceiver.TrackInfo()))
}
}
}
t.dynacastManager.OnSubscribedMaxQualityChange(handler)
func (t *MediaTrack) OnSubscribedAudioCodecChange(
f func(
trackID livekit.TrackID,
codecs []*livekit.SubscribedAudioCodec,
) error,
) {
t.lock.Lock()
t.onSubscribedAudioCodecChange = f
t.lock.Unlock()
}
func (t *MediaTrack) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []types.SubscribedCodecQuality) {
@@ -177,47 +228,84 @@ func (t *MediaTrack) NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, quali
}
}
func (t *MediaTrack) SignalCid() string {
return t.params.SignalCid
func (t *MediaTrack) NotifySubscriptionNode(nodeID livekit.NodeID, codecs []*livekit.SubscribedAudioCodec) {
if t.dynacastManager != nil {
t.dynacastManager.NotifySubscriptionNode(nodeID, codecs)
}
}
func (t *MediaTrack) HasSdpCid(cid string) bool {
if t.params.SdpCid == cid {
return true
func (t *MediaTrack) ClearSubscriberNodes() {
if t.dynacastManager != nil {
t.dynacastManager.ClearSubscriberNodes()
}
}
ti := t.MediaTrackReceiver.TrackInfoClone()
for _, c := range ti.Codecs {
if c.Cid == cid {
return true
func (t *MediaTrack) HasSignalCid(cid string) bool {
if cid != "" {
ti := t.MediaTrackReceiver.TrackInfoClone()
for _, c := range ti.Codecs {
if c.Cid == cid {
return true
}
}
}
return false
}
func (t *MediaTrack) HasSdpCid(cid string) bool {
if cid != "" {
ti := t.MediaTrackReceiver.TrackInfoClone()
for _, c := range ti.Codecs {
if c.Cid == cid || c.SdpCid == cid {
return true
}
}
}
return false
}
func (t *MediaTrack) GetMimeTypeForSdpCid(cid string) mime.MimeType {
if cid != "" {
ti := t.MediaTrackReceiver.TrackInfoClone()
for _, c := range ti.Codecs {
if c.Cid == cid || c.SdpCid == cid {
return mime.NormalizeMimeType(c.MimeType)
}
}
}
return mime.MimeTypeUnknown
}
func (t *MediaTrack) GetCidsForMimeType(mimeType mime.MimeType) (string, string) {
ti := t.MediaTrackReceiver.TrackInfoClone()
for _, c := range ti.Codecs {
if mime.NormalizeMimeType(c.MimeType) == mimeType {
return c.Cid, c.SdpCid
}
}
return "", ""
}
func (t *MediaTrack) ToProto() *livekit.TrackInfo {
return t.MediaTrackReceiver.TrackInfoClone()
}
func (t *MediaTrack) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
t.MediaTrackReceiver.UpdateCodecCid(codecs)
}
// AddReceiver adds a new RTP receiver to the track, returns true when receiver represents a new codec
func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRemote, mid string) bool {
// and if a receiver was added successfully
func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRemote, mid string) (bool, bool) {
var newCodec bool
ssrc := uint32(track.SSRC())
buff, rtcpReader := t.params.BufferFactory.GetBufferPair(ssrc)
if buff == nil || rtcpReader == nil {
t.params.Logger.Errorw("could not retrieve buffer pair", nil)
return newCodec
return newCodec, false
}
var lastRR uint32
rtcpReader.OnPacket(func(bytes []byte) {
pkts, err := rtcp.Unmarshal(bytes)
if err != nil {
t.params.Logger.Errorw("could not unmarshal RTCP", err)
t.params.Logger.Errorw("could not unmarshal RTCP", err, "size", len(bytes))
return
}
@@ -226,7 +314,13 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
case *rtcp.SourceDescription:
case *rtcp.SenderReport:
if pkt.SSRC == uint32(track.SSRC()) {
buff.SetSenderReportData(pkt.RTPTime, pkt.NTPTime, pkt.PacketCount, pkt.OctetCount)
buff.SetSenderReportData(&livekit.RTCPSenderReportState{
RtpTimestamp: pkt.RTPTime,
NtpTimestamp: pkt.NTPTime,
Packets: pkt.PacketCount,
Octets: uint64(pkt.OctetCount),
At: mono.UnixNano(),
})
}
case *rtcp.ExtendedReport:
rttFromXR:
@@ -255,13 +349,27 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
t.lock.Lock()
var regressCodec bool
mimeType := mime.NormalizeMimeType(track.Codec().MimeType)
layer := buffer.RidToSpatialLayer(track.RID(), ti)
layer := buffer.GetSpatialLayerForRid(mimeType, track.RID(), ti)
if layer < 0 {
t.params.Logger.Warnw(
"AddReceiver failed due to negative layer", nil,
"rid", track.RID(),
"layer", layer,
"ssrc", track.SSRC(),
"codec", track.Codec(),
"trackInfo", logger.Proto(ti),
)
t.lock.Unlock()
return newCodec, false
}
t.params.Logger.Debugw(
"AddReceiver",
"rid", track.RID(),
"layer", layer,
"ssrc", track.SSRC(),
"codec", track.Codec(),
"trackInfo", logger.Proto(ti),
)
wr := t.MediaTrackReceiver.Receiver(mimeType)
if wr == nil {
@@ -276,6 +384,11 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
switch len(ti.Codecs) {
case 0:
// audio track
t.params.Logger.Warnw(
"unexpected 0 codecs in track info", nil,
"mime", mimeType,
"track", logger.Proto(ti),
)
priority = 0
case 1:
// older clients or non simulcast-codec, mime type only set later
@@ -285,9 +398,13 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
}
}
if priority < 0 {
t.params.Logger.Warnw("could not find codec for webrtc receiver", nil, "webrtcCodec", mimeType, "track", logger.Proto(ti))
t.params.Logger.Warnw(
"could not find codec for webrtc receiver", nil,
"mime", mimeType,
"track", logger.Proto(ti),
)
t.lock.Unlock()
return false
return newCodec, false
}
newWR := sfu.NewWebRTCReceiver(
@@ -300,11 +417,11 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
sfu.WithPliThrottleConfig(t.params.PLIThrottleConfig),
sfu.WithAudioConfig(t.params.AudioConfig),
sfu.WithLoadBalanceThreshold(20),
sfu.WithStreamTrackers(),
sfu.WithForwardStats(t.params.ForwardStats),
sfu.WithEnableRTPStreamRestartDetection(t.params.EnableRTPStreamRestartDetection),
)
newWR.OnCloseHandler(func() {
t.MediaTrackReceiver.SetClosing()
t.MediaTrackReceiver.SetClosing(false)
t.MediaTrackReceiver.ClearReceiver(mimeType, false)
if t.MediaTrackReceiver.TryClose() {
if t.dynacastManager != nil {
@@ -312,15 +429,61 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
}
}
})
// SIMULCAST-CODEC-TODO: these need to be receiver/mime aware, setting it up only for primary now
if priority == 0 {
newWR.OnStatsUpdate(func(_ *sfu.WebRTCReceiver, stat *livekit.AnalyticsStat) {
key := telemetry.StatsKeyForTrack(livekit.StreamType_UPSTREAM, t.PublisherID(), t.ID(), ti.Source, ti.Type)
t.params.Telemetry.TrackStats(key, stat)
})
newWR.OnMaxLayerChange(t.onMaxLayerChange)
// SIMULCAST-CODEC-TODO: these need to be receiver/mime aware, setting it up only for primary now
statsKey := telemetry.StatsKeyForTrack(
t.params.ParticipantCountry,
livekit.StreamType_UPSTREAM,
t.PublisherID(),
t.ID(),
ti.Source,
ti.Type,
)
for _, c := range ti.Codecs {
for _, l := range c.Layers {
t.params.Reporter.ReportLayer(roomobs.PackTrackLayer(l.Height, l.Width))
}
}
newWR.OnStatsUpdate(func(_ *sfu.WebRTCReceiver, stat *livekit.AnalyticsStat) {
// send for only one codec, either primary (priority == 0) OR regressed codec
t.lock.RLock()
regressionTargetCodecReceived := t.regressionTargetCodecReceived
t.lock.RUnlock()
if priority == 0 || regressionTargetCodecReceived {
t.params.TelemetryListener.OnTrackStats(statsKey, stat)
if cs, ok := telemetry.CondenseStat(stat); ok {
t.params.Reporter.Tx(func(tx roomobs.TrackTx) {
tx.ParticipantSession().ReportKind(t.params.ParticipantKind.String())
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(t.params.ParticipantKind))
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(t.params.ParticipantKindDetails))
tx.ReportName(ti.Name)
tx.ReportKind(roomobs.TrackKindPub)
tx.ReportType(roomobs.TrackTypeFromProto(ti.Type))
tx.ReportSource(roomobs.TrackSourceFromProto(ti.Source))
tx.ReportMime(mime.NormalizeMimeType(ti.MimeType).ReporterType())
tx.ReportDuration(uint16(cs.EndTime.Sub(cs.StartTime).Milliseconds()))
tx.ReportFrames(uint16(cs.Frames))
tx.ReportRecvBytes(uint32(cs.Bytes))
tx.ReportRecvPackets(cs.Packets)
tx.ReportPacketsLost(cs.PacketsLost)
tx.ReportScore(stat.Score)
})
}
}
})
newWR.OnMaxLayerChange(func(mimeType mime.MimeType, maxLayer int32) {
// send for only one codec, either primary (priority == 0) OR regressed codec
t.lock.RLock()
regressionTargetCodecReceived := t.regressionTargetCodecReceived
t.lock.RUnlock()
if priority == 0 || regressionTargetCodecReceived {
t.MediaTrackReceiver.NotifyMaxLayerChange(mimeType, maxLayer)
}
})
// SIMULCAST-CODEC-TODO END: these need to be receiver/mime aware, setting it up only for primary now
if t.PrimaryReceiver() == nil {
// primary codec published, set potential codecs
potentialCodecs := make([]webrtc.RTPCodecParameters, 0, len(ti.Codecs))
@@ -345,8 +508,8 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
t.MediaTrackReceiver.SetupReceiver(newWR, priority, mid)
for ssrc, info := range t.params.SimTracks {
if info.Mid == mid {
t.MediaTrackReceiver.SetLayerSsrc(mimeType, info.Rid, ssrc)
if info.Mid == mid && !info.IsRepairStream {
t.MediaTrackReceiver.SetLayerSsrcsForRid(mimeType, info.StreamID, ssrc, info.RepairSSRC)
}
}
wr = newWR
@@ -355,9 +518,18 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
newWR.AddOnCodecStateChange(func(codec webrtc.RTPCodecParameters, state sfu.ReceiverCodecState) {
t.MediaTrackReceiver.HandleReceiverCodecChange(newWR, codec, state)
})
// update subscriber video layers when video size changes
newWR.OnVideoSizeChanged(func() {
if t.params.UpdateTrackInfoByVideoSizeChange {
t.MediaTrackReceiver.UpdateVideoSize(mimeType, newWR.VideoSizes())
}
t.MediaTrackSubscriptions.UpdateVideoLayers()
})
}
if newCodec && t.enableRegression {
if newCodec && t.enableRegression() {
if mimeType == t.regressionTargetCodec {
t.params.Logger.Infow("regression target codec received", "codec", mimeType)
t.regressionTargetCodecReceived = true
@@ -377,21 +549,28 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
"newCodec", newCodec,
)
buff.Close()
return false
return newCodec, false
}
// LK-TODO: can remove this completely when VideoLayers protocol becomes the default as it has info from client or if we decide to use TrackInfo.Simulcast
if t.numUpTracks.Inc() > 1 || track.RID() != "" {
// cannot only rely on numUpTracks since we fire metadata events immediately after the first layer
t.SetSimulcast(true)
var expectedBitrate int
layers := buffer.GetVideoLayersForMimeType(mimeType, ti)
if layer >= 0 && len(layers) > int(layer) {
expectedBitrate = int(layers[layer].GetBitrate())
}
if err := buff.Bind(receiver.GetParameters(), track.Codec().RTPCodecCapability, expectedBitrate); err != nil {
t.params.Logger.Warnw(
"binding buffer failed", err,
"rid", track.RID(),
"layer", layer,
"ssrc", track.SSRC(),
"newCodec", newCodec,
)
buff.Close()
return newCodec, false
}
var bitrates int
if len(ti.Layers) > int(layer) {
bitrates = int(ti.Layers[layer].GetBitrate())
}
t.MediaTrackReceiver.SetLayerSsrc(mimeType, track.RID(), uint32(track.SSRC()))
t.MediaTrackReceiver.MaybeSetSimulcast()
t.MediaTrackReceiver.SetLayerSsrcsForRid(mimeType, track.RID(), uint32(track.SSRC()), 0)
if regressCodec {
for _, c := range ti.Codecs {
@@ -408,7 +587,7 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
}
}
buff.Bind(receiver.GetParameters(), track.Codec().RTPCodecCapability, bitrates)
buff.OnNotifyRTX(t.MediaTrackReceiver.setLayerRtxInfo)
// if subscriber request fps before fps calculated, update them after fps updated.
buff.OnFpsChanged(func() {
@@ -416,20 +595,19 @@ func (t *MediaTrack) AddReceiver(receiver *webrtc.RTPReceiver, track sfu.TrackRe
})
buff.OnFinalRtpStats(func(stats *livekit.RTPStats) {
t.params.Telemetry.TrackPublishRTPStats(
context.Background(),
t.params.ParticipantID,
t.params.TelemetryListener.OnTrackPublishRTPStats(
t.params.ParticipantID(),
t.ID(),
mimeType,
int(layer),
stats,
)
})
return newCodec
return newCodec, true
}
func (t *MediaTrack) GetConnectionScoreAndQuality() (float32, livekit.ConnectionQuality) {
receiver := t.PrimaryReceiver()
receiver := t.ActiveReceiver()
if rtcReceiver, ok := receiver.(*sfu.WebRTCReceiver); ok {
return rtcReceiver.GetConnectionScoreAndQuality()
}
@@ -447,10 +625,6 @@ func (t *MediaTrack) HasPendingCodec() bool {
return t.MediaTrackReceiver.PrimaryReceiver() == nil
}
func (t *MediaTrack) onMaxLayerChange(maxLayer int32) {
t.MediaTrackReceiver.NotifyMaxLayerChange(maxLayer)
}
func (t *MediaTrack) Restart() {
t.MediaTrackReceiver.Restart()
@@ -460,11 +634,10 @@ func (t *MediaTrack) Restart() {
}
func (t *MediaTrack) Close(isExpectedToResume bool) {
t.MediaTrackReceiver.SetClosing()
t.MediaTrackReceiver.SetClosing(isExpectedToResume)
if t.dynacastManager != nil {
t.dynacastManager.Close()
}
t.MediaTrackReceiver.ClearAllReceivers(isExpectedToResume)
t.MediaTrackReceiver.Close(isExpectedToResume)
}
@@ -486,3 +659,56 @@ func (t *MediaTrack) OnTrackSubscribed() {
go t.params.OnTrackEverSubscribed(t.ID())
}
}
func (t *MediaTrack) enableRegression() bool {
return t.backupCodecPolicy == livekit.BackupCodecPolicy_REGRESSION ||
(t.backupCodecPolicy == livekit.BackupCodecPolicy_PREFER_REGRESSION && t.params.ShouldRegressCodec())
}
func (t *MediaTrack) Logger() logger.Logger {
return t.params.Logger
}
// dynacast.DynacastManagerListtener implementation
var _ dynacast.DynacastManagerListener = (*MediaTrack)(nil)
func (t *MediaTrack) OnDynacastSubscribedMaxQualityChange(
subscribedQualities []*livekit.SubscribedCodec,
maxSubscribedQualities []types.SubscribedCodecQuality,
) {
t.lock.RLock()
onSubscribedMaxQualityChange := t.onSubscribedMaxQualityChange
t.lock.RUnlock()
if onSubscribedMaxQualityChange != nil && !t.IsMuted() {
_ = onSubscribedMaxQualityChange(
t.ID(),
t.ToProto(),
subscribedQualities,
maxSubscribedQualities,
)
}
for _, q := range maxSubscribedQualities {
receiver := t.Receiver(q.CodecMime)
if receiver != nil {
receiver.SetMaxExpectedSpatialLayer(
buffer.GetSpatialLayerForVideoQuality(
q.CodecMime,
q.Quality,
t.MediaTrackReceiver.TrackInfo(),
),
)
}
}
}
func (t *MediaTrack) OnDynacastSubscribedAudioCodecChange(codecs []*livekit.SubscribedAudioCodec) {
t.lock.RLock()
onSubscribedAudioCodecChange := t.onSubscribedAudioCodecChange
t.lock.RUnlock()
if onSubscribedAudioCodecChange != nil {
_ = onSubscribedAudioCodecChange(t.ID(), codecs)
}
}
+97 -74
View File
@@ -19,7 +19,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
func TestTrackInfo(t *testing.T) {
@@ -47,128 +49,149 @@ func TestTrackInfo(t *testing.T) {
require.Equal(t, ti.Width, outInfo.Width)
require.Equal(t, ti.Height, outInfo.Height)
require.Equal(t, ti.Simulcast, outInfo.Simulcast)
// make it simulcasted
mt.SetSimulcast(true)
require.True(t, mt.ToProto().Simulcast)
}
func TestGetQualityForDimension(t *testing.T) {
t.Run("landscape source", func(t *testing.T) {
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
mt := NewMediaTrack(MediaTrackParams{
Logger: logger.GetLogger(),
}, &livekit.TrackInfo{
Type: livekit.TrackType_VIDEO,
Width: 1080,
Height: 720,
})
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 200))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(200, 250))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(700, 480))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(500, 1000))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 300, 200))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 200, 250))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 700, 480))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 500, 1000))
})
t.Run("portrait source", func(t *testing.T) {
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
mt := NewMediaTrack(MediaTrackParams{
Logger: logger.GetLogger(),
}, &livekit.TrackInfo{
Type: livekit.TrackType_VIDEO,
Width: 540,
Height: 960,
})
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(200, 400))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(400, 400))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(400, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(600, 900))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeVP8, 200, 400))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 400, 400))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeVP8, 400, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeVP8, 600, 900))
})
t.Run("layers provided", func(t *testing.T) {
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
mt := NewMediaTrack(MediaTrackParams{
Logger: logger.GetLogger(),
}, &livekit.TrackInfo{
Type: livekit.TrackType_VIDEO,
Width: 1080,
Height: 720,
Layers: []*livekit.VideoLayer{
Codecs: []*livekit.SimulcastCodecInfo{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 960,
Height: 540,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
MimeType: mime.MimeTypeH264.String(),
Layers: []*livekit.VideoLayer{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 960,
Height: 540,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
},
},
},
},
})
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 300))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
})
t.Run("highest layer with smallest dimensions", func(t *testing.T) {
mt := NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
mt := NewMediaTrack(MediaTrackParams{
Logger: logger.GetLogger(),
}, &livekit.TrackInfo{
Type: livekit.TrackType_VIDEO,
Width: 1080,
Height: 720,
Layers: []*livekit.VideoLayer{
Codecs: []*livekit.SimulcastCodecInfo{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 1080,
Height: 720,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
MimeType: mime.MimeTypeH264.String(),
Layers: []*livekit.VideoLayer{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 1080,
Height: 720,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
},
},
},
},
})
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(300, 300))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1200, 800))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
require.Equal(t, livekit.VideoQuality_LOW, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1200, 800))
mt = NewMediaTrack(MediaTrackParams{}, &livekit.TrackInfo{
mt = NewMediaTrack(MediaTrackParams{
Logger: logger.GetLogger(),
}, &livekit.TrackInfo{
Type: livekit.TrackType_VIDEO,
Width: 1080,
Height: 720,
Layers: []*livekit.VideoLayer{
Codecs: []*livekit.SimulcastCodecInfo{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
MimeType: mime.MimeTypeH264.String(),
Layers: []*livekit.VideoLayer{
{
Quality: livekit.VideoQuality_LOW,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_MEDIUM,
Width: 480,
Height: 270,
},
{
Quality: livekit.VideoQuality_HIGH,
Width: 1080,
Height: 720,
},
},
},
},
})
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(120, 120))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(300, 300))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1000, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(1200, 800))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 120, 120))
require.Equal(t, livekit.VideoQuality_MEDIUM, mt.GetQualityForDimension(mime.MimeTypeH264, 300, 300))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 800, 500))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1000, 700))
require.Equal(t, livekit.VideoQuality_HIGH, mt.GetQualityForDimension(mime.MimeTypeH264, 1200, 800))
})
}
+327 -94
View File
@@ -15,19 +15,19 @@
package rtc
import (
"context"
"errors"
"fmt"
"sort"
"slices"
"strings"
"sync"
"github.com/pion/rtcp"
"github.com/pion/webrtc/v4"
"go.uber.org/atomic"
"golang.org/x/exp/slices"
"google.golang.org/protobuf/proto"
"github.com/livekit/mediatransportutil/pkg/codec"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
@@ -35,9 +35,8 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/sfu/rtpstats"
"github.com/livekit/livekit-server/pkg/telemetry"
sutils "github.com/livekit/livekit-server/pkg/utils"
)
const (
@@ -118,17 +117,18 @@ func (r *simulcastReceiver) IsRegressed() bool {
// -----------------------------------------------------
type MediaTrackReceiverParams struct {
MediaTrack types.MediaTrack
IsRelayed bool
ParticipantID livekit.ParticipantID
ParticipantIdentity livekit.ParticipantIdentity
ParticipantVersion uint32
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
AudioConfig sfu.AudioConfig
Telemetry telemetry.TelemetryService
Logger logger.Logger
RegressionTargetCodec mime.MimeType
MediaTrack types.MediaTrack
IsRelayed bool
ParticipantID func() livekit.ParticipantID
ParticipantIdentity livekit.ParticipantIdentity
ParticipantVersion uint32
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
AudioConfig sfu.AudioConfig
TelemetryListener types.ParticipantTelemetryListener
Logger logger.Logger
RegressionTargetCodec mime.MimeType
PreferVideoSizeFromMedia bool
}
type MediaTrackReceiver struct {
@@ -161,21 +161,15 @@ func NewMediaTrackReceiver(params MediaTrackReceiverParams, ti *livekit.TrackInf
IsRelayed: params.IsRelayed,
ReceiverConfig: params.ReceiverConfig,
SubscriberConfig: params.SubscriberConfig,
Telemetry: params.Telemetry,
Logger: params.Logger,
})
t.MediaTrackSubscriptions.OnDownTrackCreated(t.onDownTrackCreated)
if ti.Muted {
t.SetMuted(true)
}
return t
}
func (t *MediaTrackReceiver) Restart() {
hq := buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_HIGH, t.TrackInfo())
for _, receiver := range t.loadReceivers() {
hq := buffer.GetSpatialLayerForVideoQuality(receiver.Mime(), livekit.VideoQuality_HIGH, t.TrackInfo())
receiver.SetMaxExpectedSpatialLayer(hq)
}
}
@@ -213,8 +207,8 @@ func (t *MediaTrackReceiver) SetupReceiver(receiver sfu.TrackReceiver, priority
receivers = append(receivers, &simulcastReceiver{TrackReceiver: receiver, priority: priority})
}
sort.Slice(receivers, func(i, j int) bool {
return receivers[i].Priority() < receivers[j].Priority()
slices.SortFunc(receivers, func(a, b *simulcastReceiver) int {
return sutils.Signum(a.Priority() - b.Priority())
})
if mid != "" {
@@ -308,12 +302,16 @@ func (t *MediaTrackReceiver) HandleReceiverCodecChange(r sfu.TrackReceiver, code
return
}
t.params.Logger.Infow("regressing codec", "from", codec.MimeType, "to", backupCodecReceiver.Codec().MimeType)
t.params.Logger.Infow(
"regressing codec",
"from", codec.MimeType,
"to", backupCodecReceiver.Codec().MimeType,
)
// remove old codec from potential codecs
for i, c := range t.potentialCodecs {
if strings.EqualFold(c.MimeType, codec.MimeType) {
slices.Delete(t.potentialCodecs, i, i+1)
t.potentialCodecs = slices.Delete(t.potentialCodecs, i, i+1)
break
}
}
@@ -351,8 +349,8 @@ func (t *MediaTrackReceiver) SetPotentialCodecs(codecs []webrtc.RTPCodecParamete
})
}
}
sort.Slice(receivers, func(i, j int) bool {
return receivers[i].Priority() < receivers[j].Priority()
slices.SortFunc(receivers, func(a, b *simulcastReceiver) int {
return sutils.Signum(a.Priority() - b.Priority())
})
t.receivers = receivers
t.lock.Unlock()
@@ -376,7 +374,7 @@ func (t *MediaTrackReceiver) ClearReceiver(mime mime.MimeType, isExpectedToResum
}
func (t *MediaTrackReceiver) ClearAllReceivers(isExpectedToResume bool) {
t.params.Logger.Debugw("clearing all receivers")
t.params.Logger.Debugw("clearing all receivers", "isExpectedToResume", isExpectedToResume)
t.lock.Lock()
receivers := t.receivers
t.receivers = nil
@@ -408,12 +406,15 @@ func (t *MediaTrackReceiver) IsOpen() bool {
return true
}
func (t *MediaTrackReceiver) SetClosing() {
func (t *MediaTrackReceiver) SetClosing(isExpectedToResume bool) {
t.lock.Lock()
defer t.lock.Unlock()
if t.state == mediaTrackReceiverStateOpen {
t.state = mediaTrackReceiverStateClosing
}
t.isExpectedToResume = isExpectedToResume
}
func (t *MediaTrackReceiver) TryClose() bool {
@@ -444,6 +445,8 @@ func (t *MediaTrackReceiver) TryClose() bool {
}
func (t *MediaTrackReceiver) Close(isExpectedToResume bool) {
t.ClearAllReceivers(isExpectedToResume)
t.lock.Lock()
if t.state == mediaTrackReceiverStateClosed {
t.lock.Unlock()
@@ -476,7 +479,7 @@ func (t *MediaTrackReceiver) Stream() string {
}
func (t *MediaTrackReceiver) PublisherID() livekit.ParticipantID {
return t.params.ParticipantID
return t.params.ParticipantID()
}
func (t *MediaTrackReceiver) PublisherIdentity() livekit.ParticipantIdentity {
@@ -487,19 +490,6 @@ func (t *MediaTrackReceiver) PublisherVersion() uint32 {
return t.params.ParticipantVersion
}
func (t *MediaTrackReceiver) IsSimulcast() bool {
return t.TrackInfo().Simulcast
}
func (t *MediaTrackReceiver) SetSimulcast(simulcast bool) {
t.lock.Lock()
defer t.lock.Unlock()
trackInfo := t.TrackInfoClone()
trackInfo.Simulcast = simulcast
t.trackInfo.Store(trackInfo)
}
func (t *MediaTrackReceiver) Name() string {
return t.TrackInfo().Name
}
@@ -513,21 +503,19 @@ func (t *MediaTrackReceiver) SetMuted(muted bool) {
trackInfo := t.TrackInfoClone()
trackInfo.Muted = muted
t.trackInfo.Store(trackInfo)
receivers := t.receivers
t.lock.Unlock()
for _, receiver := range receivers {
receiver.SetUpTrackPaused(muted)
}
t.MediaTrackSubscriptions.SetMuted(muted)
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) IsEncrypted() bool {
return t.TrackInfo().Encryption != livekit.Encryption_NONE
}
func (t *MediaTrackReceiver) HasPacketTrailer() bool {
return len(t.TrackInfo().GetPacketTrailerFeatures()) > 0
}
func (t *MediaTrackReceiver) AddOnClose(f func(isExpectedToResume bool)) {
if f == nil {
return
@@ -588,8 +576,10 @@ func (t *MediaTrackReceiver) AddSubscriber(sub types.LocalParticipant) (types.Su
StreamId: streamId,
UpstreamCodecs: potentialCodecs,
Logger: tLogger,
DisableRed: t.TrackInfo().GetDisableRed() || !t.params.AudioConfig.ActiveREDEncoding,
DisableRed: !IsRedEnabled(t.TrackInfo()) || !t.params.AudioConfig.ActiveREDEncoding,
IsEncrypted: t.IsEncrypted(),
})
subID := sub.ID()
subTrack, err := t.MediaTrackSubscriptions.AddSubscriber(sub, wr)
// media track could have been closed while adding subscription
@@ -603,7 +593,12 @@ func (t *MediaTrackReceiver) AddSubscriber(sub types.LocalParticipant) (types.Su
t.lock.RUnlock()
if remove {
_ = t.MediaTrackSubscriptions.RemoveSubscriber(sub.ID(), isExpectedToResume)
t.params.Logger.Debugw(
"removing subscriber on a not-open track",
"subscriberID", subID,
"isExpectedToResume", isExpectedToResume,
)
_ = t.MediaTrackSubscriptions.RemoveSubscriber(subID, isExpectedToResume)
return nil, ErrNotOpen
}
@@ -617,7 +612,7 @@ func (t *MediaTrackReceiver) RemoveSubscriber(subscriberID livekit.ParticipantID
}
func (t *MediaTrackReceiver) removeAllSubscribersForMime(mime mime.MimeType, isExpectedToResume bool) {
t.params.Logger.Debugw("removing all subscribers for mime", "mime", mime)
t.params.Logger.Debugw("removing all subscribers for mime", "mime", mime, "isExpectedToResume", isExpectedToResume)
for _, subscriberID := range t.MediaTrackSubscriptions.GetAllSubscribersForMime(mime) {
t.RemoveSubscriber(subscriberID, isExpectedToResume)
}
@@ -632,14 +627,7 @@ func (t *MediaTrackReceiver) RevokeDisallowedSubscribers(allowedSubscriberIdenti
continue
}
found := false
for _, allowedIdentity := range allowedSubscriberIdentities {
if subTrack.SubscriberIdentity() == allowedIdentity {
found = true
break
}
}
found := slices.Contains(allowedSubscriberIdentities, subTrack.SubscriberIdentity())
if !found {
t.params.Logger.Infow("revoking subscription",
"subscriber", subTrack.SubscriberIdentity(),
@@ -658,17 +646,42 @@ func (t *MediaTrackReceiver) updateTrackInfoOfReceivers() {
for _, r := range t.loadReceivers() {
r.UpdateTrackInfo(ti)
}
t.MediaTrackSubscriptions.SetMuted(ti.GetMuted())
}
func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ssrc uint32) {
func (t *MediaTrackReceiver) MaybeSetSimulcast() {
// only primary receiver (i.e. receiver at index 0) for legacy use case
primaryReceiver := t.PrimaryReceiver()
if primaryReceiver == nil {
return
}
if wr, ok := primaryReceiver.(*sfu.WebRTCReceiver); !ok || wr.NumUpTracks() < 2 {
return
}
t.lock.Lock()
trackInfo := t.TrackInfoClone()
layer := buffer.RidToSpatialLayer(rid, trackInfo)
if trackInfo.Simulcast {
t.lock.Unlock()
return
}
trackInfo.Simulcast = true
t.trackInfo.Store(trackInfo)
t.lock.Unlock()
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) SetLayerSsrcsForRid(mimeType mime.MimeType, rid string, ssrc uint32, repairSSRC uint32) {
t.lock.Lock()
trackInfo := t.TrackInfoClone()
layer := buffer.GetSpatialLayerForRid(mimeType, rid, trackInfo)
if layer == buffer.InvalidLayerSpatial {
// non-simulcast case will not have `rid`
layer = 0
}
quality := buffer.SpatialLayerToVideoQuality(layer, trackInfo)
quality := buffer.GetVideoQualityForSpatialLayer(mimeType, layer, trackInfo)
// set video layer ssrc info
for i, ci := range trackInfo.Codecs {
if mime.NormalizeMimeType(ci.MimeType) != mimeType {
@@ -689,6 +702,20 @@ func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ss
}
if !ssrcFound && matchingLayer != nil {
matchingLayer.Ssrc = ssrc
if repairSSRC != 0 {
matchingLayer.RepairSsrc = repairSSRC
}
}
if ssrcFound && (matchingLayer.Ssrc != ssrc || matchingLayer.RepairSsrc != repairSSRC) {
t.params.Logger.Warnw(
"not overriding ssrc", nil,
"rid", rid,
"ssrc", ssrc,
"existingSSRC", matchingLayer.Ssrc,
"repairSSRC", repairSSRC,
"existingRepairSSRC", matchingLayer.RepairSsrc,
"trackInfo", logger.Proto(trackInfo),
)
}
// for client don't use simulcast codecs (old client version or single codec)
@@ -703,13 +730,99 @@ func (t *MediaTrackReceiver) SetLayerSsrc(mimeType mime.MimeType, rid string, ss
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
func (t *MediaTrackReceiver) setLayerRtxInfo(ssrc uint32, repairSSRC uint32, rsid string) {
t.params.Logger.Debugw("rtx notification", "ssrc", ssrc, "repairSSRC", repairSSRC, "rsid", rsid)
if ssrc == 0 || repairSSRC == 0 || rsid == "" {
return
}
t.lock.Lock()
trackInfo := t.TrackInfoClone()
done:
for _, ci := range trackInfo.Codecs {
for _, l := range ci.Layers {
if l.Ssrc == ssrc {
if (l.RepairSsrc != 0 && l.RepairSsrc != repairSSRC) || (l.Rid != "" && l.Rid != rsid) {
t.params.Logger.Warnw(
"not overriding rtx info", nil,
"ssrc", ssrc,
"repairSSRC", repairSSRC,
"existingRepairSSRC", l.RepairSsrc,
"rsid", rsid,
"existingRid", l.Rid,
"trackInfo", logger.Proto(trackInfo),
)
} else {
l.RepairSsrc = repairSSRC
t.params.Logger.Debugw(
"set rtx info",
"ssrc", ssrc,
"repairSSRC", repairSSRC,
"rsid", rsid,
"trackInfo", logger.Proto(trackInfo),
)
}
break done
}
}
}
// backwards compatibility
for _, l := range trackInfo.Layers {
if l.Ssrc == ssrc {
if (l.RepairSsrc != 0 && l.RepairSsrc != repairSSRC) || (l.Rid != "" && l.Rid != rsid) {
t.params.Logger.Warnw(
"not overriding rtx info", nil,
"ssrc", ssrc,
"repairSSRC", repairSSRC,
"existingRepairSSRC", l.RepairSsrc,
"rsid", rsid,
"existingRid", l.Rid,
"trackInfo", logger.Proto(trackInfo),
)
} else {
l.RepairSsrc = repairSSRC
t.params.Logger.Debugw(
"set rtx info",
"ssrc", ssrc,
"repairSSRC", repairSSRC,
"rsid", rsid,
"trackInfo", logger.Proto(trackInfo),
)
}
break
}
}
t.trackInfo.Store(trackInfo)
t.lock.Unlock()
// change not propagated as it is internal
}
func (t *MediaTrackReceiver) UpdateCodecInfo(codecs []*livekit.SimulcastCodec) {
t.lock.Lock()
trackInfo := t.TrackInfoClone()
for _, c := range codecs {
for _, origin := range trackInfo.Codecs {
if mime.GetMimeTypeCodec(origin.MimeType) == mime.NormalizeMimeTypeCodec(c.Codec) {
origin.Cid = c.Cid
if len(c.Layers) != 0 {
clonedLayers := make([]*livekit.VideoLayer, 0, len(c.Layers))
for _, l := range c.Layers {
clonedLayers = append(clonedLayers, utils.CloneProto(l))
}
origin.Layers = clonedLayers
mimeType := mime.NormalizeMimeType(origin.MimeType)
for _, layer := range origin.Layers {
layer.SpatialLayer = buffer.VideoQualityToSpatialLayer(mimeType, layer.Quality, trackInfo)
layer.Rid = buffer.VideoQualityToRid(mimeType, layer.Quality, trackInfo, buffer.DefaultVideoLayersRid)
}
}
break
}
}
@@ -720,13 +833,49 @@ func (t *MediaTrackReceiver) UpdateCodecCid(codecs []*livekit.SimulcastCodec) {
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) UpdateCodecSdpCid(mimeType mime.MimeType, sdpCid string) {
t.lock.Lock()
trackInfo := t.TrackInfoClone()
for _, origin := range trackInfo.Codecs {
if mime.NormalizeMimeType(origin.MimeType) == mimeType {
if sdpCid != origin.Cid {
origin.SdpCid = sdpCid
}
}
}
t.trackInfo.Store(trackInfo)
t.lock.Unlock()
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) UpdateCodecRids(mimeType mime.MimeType, rids buffer.VideoLayersRid) {
t.lock.Lock()
trackInfo := t.TrackInfoClone()
for _, origin := range trackInfo.Codecs {
originMimeType := mime.NormalizeMimeType(origin.MimeType)
if originMimeType != mimeType {
continue
}
for _, layer := range origin.Layers {
layer.SpatialLayer = buffer.VideoQualityToSpatialLayer(mimeType, layer.Quality, trackInfo)
layer.Rid = buffer.VideoQualityToRid(mimeType, layer.Quality, trackInfo, rids)
}
break
}
t.trackInfo.Store(trackInfo)
t.lock.Unlock()
t.updateTrackInfoOfReceivers()
}
func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
updateMute := false
clonedInfo := utils.CloneProto(ti)
t.lock.Lock()
trackInfo := t.TrackInfo()
// patch Mid and SSRC of codecs/layers by keeping original if available
// patch Mid/Rid and Ssrc/RtxSsrc of codecs/layers by keeping original if available
for i, ci := range clonedInfo.Codecs {
for _, originCi := range trackInfo.Codecs {
if !mime.IsMimeTypeStringEqual(ci.MimeType, originCi.MimeType) {
@@ -743,6 +892,13 @@ func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
if originLayer.Ssrc != 0 {
layer.Ssrc = originLayer.Ssrc
}
if originLayer.Rid != "" {
layer.Rid = originLayer.Rid
}
if originLayer.RepairSsrc != 0 {
layer.RepairSsrc = originLayer.RepairSsrc
}
break
}
}
@@ -755,16 +911,9 @@ func (t *MediaTrackReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
clonedInfo.Layers = ci.Layers
}
}
if trackInfo.Muted != clonedInfo.Muted {
updateMute = true
}
t.trackInfo.Store(clonedInfo)
t.lock.Unlock()
if updateMute {
t.SetMuted(clonedInfo.Muted)
}
t.updateTrackInfoOfReceivers()
}
@@ -776,7 +925,9 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
t.lock.Lock()
trackInfo := t.TrackInfo()
clonedInfo := utils.CloneProto(trackInfo)
clonedInfo.AudioFeatures = update.Features
clonedInfo.AudioFeatures = sutils.DedupeSlice(update.Features)
clonedInfo.Stereo = false
clonedInfo.DisableDtx = false
for _, feature := range update.Features {
@@ -787,6 +938,7 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
clonedInfo.DisableDtx = true
}
}
if proto.Equal(trackInfo, clonedInfo) {
t.lock.Unlock()
return
@@ -797,7 +949,7 @@ func (t *MediaTrackReceiver) UpdateAudioTrack(update *livekit.UpdateLocalAudioTr
t.updateTrackInfoOfReceivers()
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), clonedInfo)
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
t.params.Logger.Debugw("updated audio track", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
}
@@ -821,10 +973,56 @@ func (t *MediaTrackReceiver) UpdateVideoTrack(update *livekit.UpdateLocalVideoTr
t.updateTrackInfoOfReceivers()
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), clonedInfo)
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
t.params.Logger.Debugw("updated video track", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
}
func (t *MediaTrackReceiver) UpdateVideoSize(mimeType mime.MimeType, sizes []codec.VideoSize) {
var changed bool
t.lock.Lock()
trackInfo := t.TrackInfo()
clonedInfo := utils.CloneProto(trackInfo)
var maxWidth, maxHeight uint32
for _, size := range sizes {
if size.Width > maxWidth {
maxWidth = size.Width
maxHeight = size.Height
}
}
if clonedInfo.Width != maxWidth || clonedInfo.Height != maxHeight {
clonedInfo.Width = maxWidth
clonedInfo.Height = maxHeight
changed = true
}
for _, c := range clonedInfo.Codecs {
if mime.NormalizeMimeType(c.MimeType) == mimeType {
for i, l := range c.Layers {
if i < len(sizes) && (sizes[i].Width != 0 || sizes[i].Height != 0) &&
(l.Width != sizes[i].Width || l.Height != sizes[i].Height) {
l.Width = sizes[i].Width
l.Height = sizes[i].Height
changed = true
}
}
}
}
if !changed {
t.lock.Unlock()
return
}
t.trackInfo.Store(clonedInfo)
t.lock.Unlock()
t.updateTrackInfoOfReceivers()
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), clonedInfo)
t.params.Logger.Debugw("updated video sizes", "before", logger.Proto(trackInfo), "after", logger.Proto(clonedInfo))
}
func (t *MediaTrackReceiver) TrackInfo() *livekit.TrackInfo {
return t.trackInfo.Load()
}
@@ -833,16 +1031,17 @@ func (t *MediaTrackReceiver) TrackInfoClone() *livekit.TrackInfo {
return utils.CloneProto(t.TrackInfo())
}
func (t *MediaTrackReceiver) NotifyMaxLayerChange(maxLayer int32) {
func (t *MediaTrackReceiver) NotifyMaxLayerChange(mimeType mime.MimeType, maxLayer int32) {
trackInfo := t.TrackInfo()
quality := buffer.SpatialLayerToVideoQuality(maxLayer, trackInfo)
quality := buffer.GetVideoQualityForSpatialLayer(mimeType, maxLayer, trackInfo)
ti := &livekit.TrackInfo{
Sid: trackInfo.Sid,
Type: trackInfo.Type,
Layers: []*livekit.VideoLayer{{Quality: quality}},
}
if quality != livekit.VideoQuality_OFF {
for _, layer := range trackInfo.Layers {
layers := buffer.GetVideoLayersForMimeType(mimeType, trackInfo)
for _, layer := range layers {
if layer.Quality == quality {
ti.Layers[0].Width = layer.Width
ti.Layers[0].Height = layer.Height
@@ -851,12 +1050,12 @@ func (t *MediaTrackReceiver) NotifyMaxLayerChange(maxLayer int32) {
}
}
t.params.Telemetry.TrackPublishedUpdate(context.Background(), t.PublisherID(), ti)
t.params.TelemetryListener.OnTrackPublishedUpdate(t.PublisherID(), ti)
}
// GetQualityForDimension finds the closest quality to use for desired dimensions
// affords a 20% tolerance on dimension
func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) livekit.VideoQuality {
func (t *MediaTrackReceiver) GetQualityForDimension(mimeType mime.MimeType, width, height uint32) livekit.VideoQuality {
quality := livekit.VideoQuality_HIGH
if t.Kind() == livekit.TrackType_AUDIO {
return quality
@@ -864,7 +1063,12 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
trackInfo := t.TrackInfo()
if trackInfo.Height == 0 {
var mediaSizes []codec.VideoSize
if receiver := t.Receiver(mimeType); receiver != nil {
mediaSizes = receiver.VideoSizes()
}
if trackInfo.Height == 0 && len(mediaSizes) == 0 {
return quality
}
origSize := trackInfo.Height
@@ -875,19 +1079,38 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
requestedSize = width
}
if origSize == 0 {
for i := len(mediaSizes) - 1; i >= 0; i-- {
if mediaSizes[i].Height > 0 {
origSize = min(mediaSizes[i].Width, mediaSizes[i].Height)
break
}
}
}
// default sizes representing qualities low - high
layerSizes := []uint32{180, 360, origSize}
var providedSizes []uint32
for _, layer := range trackInfo.Layers {
for _, layer := range buffer.GetVideoLayersForMimeType(mimeType, trackInfo) {
providedSizes = append(providedSizes, layer.Height)
}
if len(providedSizes) == 0 || providedSizes[0] == 0 || t.params.PreferVideoSizeFromMedia {
if len(mediaSizes) > 0 {
providedSizes = providedSizes[:0]
for _, size := range mediaSizes {
providedSizes = append(providedSizes, size.Height)
}
} else {
t.params.Logger.Debugw("no video sizes provided by receiver, using track info sizes")
}
}
if len(providedSizes) > 0 {
layerSizes = providedSizes
// comparing height always
requestedSize = height
sort.Slice(layerSizes, func(i, j int) bool {
return layerSizes[i] < layerSizes[j]
})
slices.Sort(layerSizes)
}
// finds the highest layer with smallest dimensions that still satisfy client demands
@@ -905,7 +1128,7 @@ func (t *MediaTrackReceiver) GetQualityForDimension(width, height uint32) liveki
}
func (t *MediaTrackReceiver) GetAudioLevel() (float64, bool) {
receiver := t.PrimaryReceiver()
receiver := t.ActiveReceiver()
if receiver == nil {
return 0, false
}
@@ -923,8 +1146,8 @@ func (t *MediaTrackReceiver) onDownTrackCreated(downTrack *sfu.DownTrack) {
}
}
func (t *MediaTrackReceiver) DebugInfo() map[string]interface{} {
info := map[string]interface{}{
func (t *MediaTrackReceiver) DebugInfo() map[string]any {
info := map[string]any{
"ID": t.ID(),
"Kind": t.Kind().String(),
"PubMuted": t.IsMuted(),
@@ -950,6 +1173,16 @@ func (t *MediaTrackReceiver) PrimaryReceiver() sfu.TrackReceiver {
return receivers[0].TrackReceiver
}
func (t *MediaTrackReceiver) ActiveReceiver() sfu.TrackReceiver {
for _, r := range t.loadReceivers() {
if r.IsRegressed() {
return r.TrackReceiver
}
}
return t.PrimaryReceiver()
}
func (t *MediaTrackReceiver) Receiver(mime mime.MimeType) sfu.TrackReceiver {
for _, r := range t.loadReceivers() {
if r.Mime() == mime {
@@ -985,8 +1218,8 @@ func (t *MediaTrackReceiver) SetRTT(rtt uint32) {
}
}
func (t *MediaTrackReceiver) GetTemporalLayerForSpatialFps(spatial int32, fps uint32, mime mime.MimeType) int32 {
receiver := t.Receiver(mime)
func (t *MediaTrackReceiver) GetTemporalLayerForSpatialFps(mimeType mime.MimeType, spatial int32, fps uint32) int32 {
receiver := t.Receiver(mimeType)
if receiver == nil {
return buffer.DefaultMaxLayerTemporal
}
+36 -136
View File
@@ -16,21 +16,17 @@ package rtc
import (
"errors"
"slices"
"sync"
"github.com/pion/rtcp"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/pion/webrtc/v4"
"go.uber.org/atomic"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/mime"
sutils "github.com/livekit/livekit-server/pkg/utils"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/telemetry"
)
var (
@@ -47,6 +43,7 @@ type MediaTrackSubscriptions struct {
onDownTrackCreated func(downTrack *sfu.DownTrack)
onSubscriberMaxQualityChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, layer int32)
onSubscriberAudioCodecChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)
}
type MediaTrackSubscriptionsParams struct {
@@ -56,8 +53,6 @@ type MediaTrackSubscriptionsParams struct {
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
Telemetry telemetry.TelemetryService
Logger logger.Logger
}
@@ -76,6 +71,10 @@ func (t *MediaTrackSubscriptions) OnSubscriberMaxQualityChange(f func(subscriber
t.onSubscriberMaxQualityChange = f
}
func (t *MediaTrackSubscriptions) OnSubscriberAudioCodecChange(f func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)) {
t.onSubscriberAudioCodecChange = f
}
func (t *MediaTrackSubscriptions) SetMuted(muted bool) {
// update mute of all subscribed tracks
for _, st := range t.getAllSubscribedTracks() {
@@ -104,86 +103,32 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
}
t.subscribedTracksMu.Unlock()
var rtcpFeedback []webrtc.RTCPFeedback
var maxTrack int
switch t.params.MediaTrack.Kind() {
case livekit.TrackType_AUDIO:
rtcpFeedback = t.params.SubscriberConfig.RTCPFeedback.Audio
maxTrack = t.params.ReceiverConfig.PacketBufferSizeAudio
case livekit.TrackType_VIDEO:
rtcpFeedback = t.params.SubscriberConfig.RTCPFeedback.Video
maxTrack = t.params.ReceiverConfig.PacketBufferSizeVideo
}
codecs := wr.Codecs()
for _, c := range codecs {
c.RTCPFeedback = rtcpFeedback
}
streamID := wr.StreamID()
if sub.SupportsSyncStreamID() && t.params.MediaTrack.Stream() != "" {
streamID = PackSyncStreamID(t.params.MediaTrack.PublisherID(), t.params.MediaTrack.Stream())
}
var trailer []byte
if t.params.MediaTrack.IsEncrypted() {
trailer = sub.GetTrailer()
}
downTrack, err := sfu.NewDownTrack(sfu.DowntrackParams{
Codecs: codecs,
Source: t.params.MediaTrack.Source(),
Receiver: wr,
BufferFactory: sub.GetBufferFactory(),
SubID: subscriberID,
StreamID: streamID,
MaxTrack: maxTrack,
PlayoutDelayLimit: sub.GetPlayoutDelayConfig(),
Pacer: sub.GetPacer(),
Trailer: trailer,
Logger: LoggerWithTrack(sub.GetLogger().WithComponent(sutils.ComponentSub), trackID, t.params.IsRelayed),
RTCPWriter: sub.WriteSubscriberRTCP,
DisableSenderReportPassThrough: sub.GetDisableSenderReportPassThrough(),
SupportsCodecChange: sub.SupportsCodecChange(),
subTrack, err := NewSubscribedTrack(SubscribedTrackParams{
ReceiverConfig: t.params.ReceiverConfig,
SubscriberConfig: t.params.SubscriberConfig,
Subscriber: sub,
MediaTrack: t.params.MediaTrack,
AdaptiveStream: sub.GetAdaptiveStream(),
TelemetryListener: sub.GetTelemetryListener(),
WrappedReceiver: wr,
IsRelayed: t.params.IsRelayed,
OnDownTrackCreated: t.onDownTrackCreated,
OnDownTrackClosed: func(subscriberID livekit.ParticipantID) {
t.subscribedTracksMu.Lock()
delete(t.subscribedTracks, subscriberID)
t.subscribedTracksMu.Unlock()
},
OnSubscriberMaxQualityChange: t.onSubscriberMaxQualityChange,
OnSubscriberAudioCodecChange: t.onSubscriberAudioCodecChange,
})
if err != nil {
return nil, err
}
if t.onDownTrackCreated != nil {
t.onDownTrackCreated(downTrack)
}
subTrack := NewSubscribedTrack(SubscribedTrackParams{
PublisherID: t.params.MediaTrack.PublisherID(),
PublisherIdentity: t.params.MediaTrack.PublisherIdentity(),
PublisherVersion: t.params.MediaTrack.PublisherVersion(),
Subscriber: sub,
MediaTrack: t.params.MediaTrack,
DownTrack: downTrack,
AdaptiveStream: sub.GetAdaptiveStream(),
})
if !sub.Hidden() {
subTrack.AddOnBind(func(err error) {
if err == nil {
t.params.MediaTrack.OnTrackSubscribed()
}
})
}
// Bind callback can happen from replaceTrack, so set it up early
var reusingTransceiver atomic.Bool
var dtState sfu.DownTrackState
downTrack.OnCodecNegotiated(func(codec webrtc.RTPCodecCapability) {
if !wr.DetermineReceiver(codec) {
if t.onSubscriberMaxQualityChange != nil {
go func() {
spatial := buffer.VideoQualityToSpatialLayer(livekit.VideoQuality_HIGH, t.params.MediaTrack.ToProto())
t.onSubscriberMaxQualityChange(downTrack.SubscriberID(), mime.NormalizeMimeType(codec.MimeType), spatial)
}()
}
}
})
downTrack := subTrack.DownTrack()
downTrack.OnBinding(func(err error) {
if err != nil {
go subTrack.Bound(err)
@@ -207,25 +152,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
subTrack.SetPublisherMuted(t.params.MediaTrack.IsMuted())
})
downTrack.OnStatsUpdate(func(_ *sfu.DownTrack, stat *livekit.AnalyticsStat) {
key := telemetry.StatsKeyForTrack(livekit.StreamType_DOWNSTREAM, subscriberID, trackID, t.params.MediaTrack.Source(), t.params.MediaTrack.Kind())
t.params.Telemetry.TrackStats(key, stat)
})
downTrack.OnMaxLayerChanged(func(dt *sfu.DownTrack, layer int32) {
if t.onSubscriberMaxQualityChange != nil {
t.onSubscriberMaxQualityChange(dt.SubscriberID(), dt.Mime(), layer)
}
})
downTrack.OnRttUpdate(func(_ *sfu.DownTrack, rtt uint32) {
go sub.UpdateMediaRTT(rtt)
})
downTrack.AddReceiverReportListener(func(dt *sfu.DownTrack, report *rtcp.ReceiverReport) {
sub.HandleReceiverReport(dt, report)
})
var transceiver *webrtc.RTPTransceiver
var sender *webrtc.RTPSender
@@ -277,15 +203,16 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
if transceiver == nil {
info := t.params.MediaTrack.ToProto()
addTrackParams := types.AddTrackParams{
Stereo: info.Stereo,
Red: !info.DisableRed,
Stereo: slices.Contains(info.AudioFeatures, livekit.AudioTrackFeature_TF_STEREO),
Red: IsRedEnabled(info),
}
codecs := wr.Codecs()
if addTrackParams.Red && (len(codecs) == 1 && mime.IsMimeTypeStringOpus(codecs[0].MimeType)) {
addTrackParams.Red = false
}
sub.VerifySubscribeParticipantInfo(subTrack.PublisherID(), subTrack.PublisherVersion())
if sub.SupportsTransceiverReuse() {
if sub.SupportsTransceiverReuse(t.params.MediaTrack) {
//
// AddTrack will create a new transceiver or re-use an unused one
// if the attributes match. This prevents SDP from bloating
@@ -320,6 +247,7 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
// But, the subscription could be removed early if the published track is closed
// while adding subscription. In those cases, subscription manager would not have set
// the `OnClose` callback. So, set it here to handle cases of early close.
// Subscription manager will reset this if this subscription proceeds till that point.
subTrack.OnClose(func(isExpectedToResume bool) {
if !isExpectedToResume {
if err := sub.RemoveTrackLocal(sender); err != nil {
@@ -330,10 +258,6 @@ func (t *MediaTrackSubscriptions) AddSubscriber(sub types.LocalParticipant, wr *
downTrack.SetTransceiver(transceiver)
downTrack.OnCloseHandler(func(isExpectedToResume bool) {
t.downTrackClosed(sub, subTrack, isExpectedToResume)
})
t.subscribedTracksMu.Lock()
t.subscribedTracks[subscriberID] = subTrack
t.subscribedTracksMu.Unlock()
@@ -361,10 +285,10 @@ func (t *MediaTrackSubscriptions) closeSubscribedTrack(subTrack types.Subscribed
}
if isExpectedToResume {
dt.CloseWithFlush(false)
dt.CloseWithFlush(false, false)
} else {
// flushing blocks, avoid blocking when publisher removes all its subscribers
go dt.CloseWithFlush(true)
go dt.CloseWithFlush(true, true)
}
}
@@ -429,8 +353,8 @@ func (t *MediaTrackSubscriptions) getAllSubscribedTracksLocked() []types.Subscri
return subTracks
}
func (t *MediaTrackSubscriptions) DebugInfo() []map[string]interface{} {
subscribedTrackInfo := make([]map[string]interface{}, 0)
func (t *MediaTrackSubscriptions) DebugInfo() []map[string]any {
subscribedTrackInfo := make([]map[string]any, 0)
for _, val := range t.getAllSubscribedTracks() {
if st, ok := val.(*SubscribedTrack); ok {
subscribedTrackInfo = append(subscribedTrackInfo, st.DownTrack().DebugInfo())
@@ -439,27 +363,3 @@ func (t *MediaTrackSubscriptions) DebugInfo() []map[string]interface{} {
return subscribedTrackInfo
}
func (t *MediaTrackSubscriptions) downTrackClosed(
sub types.LocalParticipant,
subTrack types.SubscribedTrack,
isExpectedToResume bool,
) {
// Cache transceiver for potential re-use on resume.
// To ensure subscription manager does not re-subscribe before caching,
// delete the subscribed track only after caching.
if isExpectedToResume {
dt := subTrack.DownTrack()
tr := dt.GetTransceiver()
if tr != nil {
sub.CacheDownTrack(subTrack.ID(), tr, dt.GetState())
}
}
go func() {
t.subscribedTracksMu.Lock()
delete(t.subscribedTracks, sub.ID())
t.subscribedTracksMu.Unlock()
subTrack.Close(isExpectedToResume)
}()
}
+59
View File
@@ -0,0 +1,59 @@
package rtc
import (
"time"
"github.com/livekit/protocol/livekit"
)
type MigrationDataCacheState int
const (
MigrationDataCacheStateWaiting MigrationDataCacheState = iota
MigrationDataCacheStateTimeout
MigrationDataCacheStateDone
)
type MigrationDataCache struct {
lastSeq uint32
pkts []*livekit.DataPacket
state MigrationDataCacheState
expiredAt time.Time
}
func NewMigrationDataCache(lastSeq uint32, expiredAt time.Time) *MigrationDataCache {
return &MigrationDataCache{
lastSeq: lastSeq,
expiredAt: expiredAt,
}
}
// Add adds a message to the cache if there is a gap between the last sequence number and cached messages then return the cache State:
// - MigrationDataCacheStateWaiting: waiting for the next packet (lastSeq + 1) of last sequence from old node
// - MigrationDataCacheStateTimeout: the next packet is not received before the expiredAt, participant will
// continue to process the reliable messages, subscribers will see the gap after the publisher migration
// - MigrationDataCacheStateDone: the next packet is received, participant can continue to process the reliable messages
func (c *MigrationDataCache) Add(pkt *livekit.DataPacket) MigrationDataCacheState {
if c.state == MigrationDataCacheStateDone || c.state == MigrationDataCacheStateTimeout {
return c.state
}
if pkt.Sequence <= c.lastSeq {
return c.state
}
if pkt.Sequence == c.lastSeq+1 {
c.state = MigrationDataCacheStateDone
return c.state
}
c.pkts = append(c.pkts, pkt)
if time.Now().After(c.expiredAt) {
c.state = MigrationDataCacheStateTimeout
}
return c.state
}
func (c *MigrationDataCache) Get() []*livekit.DataPacket {
return c.pkts
}
+38
View File
@@ -0,0 +1,38 @@
package rtc
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/livekit/protocol/livekit"
)
func TestMigrationDataCache_Add(t *testing.T) {
expiredAt := time.Now().Add(100 * time.Millisecond)
cache := NewMigrationDataCache(10, expiredAt)
pkt1 := &livekit.DataPacket{Sequence: 9}
state := cache.Add(pkt1)
require.Equal(t, MigrationDataCacheStateWaiting, state)
require.Empty(t, cache.Get())
pkt2 := &livekit.DataPacket{Sequence: 11}
state = cache.Add(pkt2)
require.Equal(t, MigrationDataCacheStateDone, state)
require.Empty(t, cache.Get())
pkt3 := &livekit.DataPacket{Sequence: 12}
state = cache.Add(pkt3)
require.Equal(t, MigrationDataCacheStateDone, state)
require.Empty(t, cache.Get())
cache2 := NewMigrationDataCache(20, time.Now().Add(10*time.Millisecond))
pkt4 := &livekit.DataPacket{Sequence: 22}
time.Sleep(20 * time.Millisecond)
state = cache2.Add(pkt4)
require.Equal(t, MigrationDataCacheStateTimeout, state)
require.Len(t, cache2.Get(), 1)
require.Equal(t, uint32(22), cache2.Get()[0].Sequence)
}
+1704 -585
View File
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
"github.com/livekit/protocol/utils/guid"
)
func (p *ParticipantImpl) HandlePublishDataTrackRequest(req *livekit.PublishDataTrackRequest) {
if !p.CanPublishData() || !p.params.EnableDataTracks {
p.pubLogger.Warnw("no permission to publish data track", nil, "req", logger.Proto(req))
p.sendRequestResponse(&livekit.RequestResponse{
Reason: livekit.RequestResponse_NOT_ALLOWED,
Message: "does not have permission to publish data",
Request: &livekit.RequestResponse_PublishDataTrack{
PublishDataTrack: utils.CloneProto(req),
},
})
return
}
if req.PubHandle == 0 || req.PubHandle > 65535 {
p.pubLogger.Warnw("invalid data track handle", nil, "req", logger.Proto(req))
p.sendRequestResponse(&livekit.RequestResponse{
Reason: livekit.RequestResponse_INVALID_HANDLE,
Message: "handle should be > 0 AND < 65536",
Request: &livekit.RequestResponse_PublishDataTrack{
PublishDataTrack: utils.CloneProto(req),
},
})
return
}
if len(req.Name) == 0 || len(req.Name) > 256 {
p.pubLogger.Warnw("invalid data track name", nil, "req", logger.Proto(req))
p.sendRequestResponse(&livekit.RequestResponse{
Reason: livekit.RequestResponse_INVALID_NAME,
Message: "name should not be empty and should not exceed 256 characters",
Request: &livekit.RequestResponse_PublishDataTrack{
PublishDataTrack: utils.CloneProto(req),
},
})
return
}
publishedDataTracks := p.UpDataTrackManager.GetPublishedDataTracks()
for _, dt := range publishedDataTracks {
message := ""
reason := livekit.RequestResponse_OK
switch {
case dt.PubHandle() == uint16(req.PubHandle):
message = "a data track with same handle already exists"
reason = livekit.RequestResponse_DUPLICATE_HANDLE
case dt.Name() == req.Name:
message = "a data track with same name already exists"
reason = livekit.RequestResponse_DUPLICATE_NAME
}
if message != "" {
p.pubLogger.Warnw(
"cannot publish duplicate data track", nil,
"req", logger.Proto(req),
"existing", logger.Proto(dt.ToProto()),
)
p.sendRequestResponse(&livekit.RequestResponse{
Reason: reason,
Message: message,
Request: &livekit.RequestResponse_PublishDataTrack{
PublishDataTrack: utils.CloneProto(req),
},
})
return
}
}
dti := &livekit.DataTrackInfo{
PubHandle: req.PubHandle,
Sid: guid.New(utils.DataTrackPrefix),
Name: req.Name,
Encryption: req.Encryption,
}
dt := NewDataTrack(
DataTrackParams{
Logger: p.params.Logger.WithValues("trackID", dti.Sid),
ParticipantID: p.ID,
ParticipantIdentity: p.params.Identity,
BytesTrackStats: NewBytesTrackStats(
p.params.Country,
livekit.TrackID(dti.Sid),
p.ID(),
p.Kind(),
p.KindDetails(),
p.params.TelemetryListener,
p.params.Reporter,
),
},
dti,
)
p.UpDataTrackManager.AddPublishedDataTrack(dt)
p.sendPublishDataTrackResponse(dti)
p.setIsPublisher(true)
p.dirty.Store(true)
}
func (p *ParticipantImpl) HandleUnpublishDataTrackRequest(req *livekit.UnpublishDataTrackRequest) {
dt := p.UpDataTrackManager.GetPublishedDataTrack(uint16(req.PubHandle))
if dt == nil {
p.pubLogger.Warnw("unpublish data track not found", nil, "req", logger.Proto(req))
p.sendRequestResponse(&livekit.RequestResponse{
Reason: livekit.RequestResponse_NOT_FOUND,
Request: &livekit.RequestResponse_UnpublishDataTrack{
UnpublishDataTrack: utils.CloneProto(req),
},
})
return
}
p.UpDataTrackManager.RemovePublishedDataTrack(dt)
p.sendUnpublishDataTrackResponse(dt.ToProto())
p.dirty.Store(true)
}
func (p *ParticipantImpl) HandleUpdateDataSubscription(req *livekit.UpdateDataSubscription) {
p.listener().OnUpdateDataSubscriptions(p, req)
}
func (p *ParticipantImpl) onReceivedDataTrackMessage(data []byte, arrivalTime int64) {
var packet datatrack.Packet
if err := packet.Unmarshal(data); err != nil {
p.params.Logger.Errorw("could not unmarshal data track message", err)
return
}
p.UpDataTrackManager.HandleReceivedDataTrackMessage(data, &packet, arrivalTime)
p.listener().OnDataTrackMessage(p, data, &packet)
}
func (p *ParticipantImpl) GetNextSubscribedDataTrackHandle() uint16 {
p.lock.Lock()
defer p.lock.Unlock()
p.nextSubscribedDataTrackHandle++
if p.nextSubscribedDataTrackHandle == 0 {
p.nextSubscribedDataTrackHandle++
}
return p.nextSubscribedDataTrackHandle
}
+232 -120
View File
@@ -26,11 +26,14 @@ import (
"google.golang.org/protobuf/proto"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/telemetry/telemetryfakes"
"github.com/livekit/protocol/auth"
protoCodecs "github.com/livekit/protocol/codecs"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
lksdp "github.com/livekit/protocol/sdp"
"github.com/livekit/protocol/signalling"
"github.com/livekit/protocol/utils"
"github.com/livekit/protocol/utils/guid"
@@ -81,14 +84,14 @@ func TestTrackPublishing(t *testing.T) {
track.IDReturns("id")
published := false
updated := false
p.OnTrackUpdated(func(p types.LocalParticipant, track types.MediaTrack) {
p.listener().(*typesfakes.FakeLocalParticipantListener).OnTrackUpdatedCalls(func(p types.Participant, track types.MediaTrack) {
updated = true
})
p.OnTrackPublished(func(p types.LocalParticipant, track types.MediaTrack) {
p.listener().(*typesfakes.FakeLocalParticipantListener).OnTrackPublishedCalls(func(p types.Participant, track types.MediaTrack) {
published = true
})
p.UpTrackManager.AddPublishedTrack(track)
p.handleTrackPublished(track)
p.handleTrackPublished(track, false)
require.True(t, published)
require.False(t, updated)
require.Len(t, p.UpTrackManager.publishedTracks, 1)
@@ -129,7 +132,8 @@ func TestTrackPublishing(t *testing.T) {
Type: livekit.TrackType_AUDIO,
})
require.Equal(t, 1, sink.WriteMessageCallCount())
// error response on duplicate adds a message
require.Equal(t, 2, sink.WriteMessageCallCount())
})
t.Run("should queue adding of duplicate tracks if already published by client id in signalling", func(t *testing.T) {
@@ -137,7 +141,7 @@ func TestTrackPublishing(t *testing.T) {
sink := p.params.Sink.(*routingfakes.FakeMessageSink)
track := &typesfakes.FakeLocalMediaTrack{}
track.SignalCidReturns("cid")
track.HasSignalCidCalls(func(s string) bool { return s == "cid" })
track.ToProtoReturns(&livekit.TrackInfo{})
// directly add to publishedTracks without lock - for testing purpose only
p.UpTrackManager.publishedTracks["cid"] = track
@@ -147,7 +151,8 @@ func TestTrackPublishing(t *testing.T) {
Name: "webcam",
Type: livekit.TrackType_VIDEO,
})
require.Equal(t, 0, sink.WriteMessageCallCount())
// `queued` `RequestResponse` should add a message
require.Equal(t, 1, sink.WriteMessageCallCount())
require.Equal(t, 1, len(p.pendingTracks["cid"].trackInfos))
// add again - it should be added to the queue
@@ -156,7 +161,8 @@ func TestTrackPublishing(t *testing.T) {
Name: "webcam",
Type: livekit.TrackType_VIDEO,
})
require.Equal(t, 0, sink.WriteMessageCallCount())
// `queued` `RequestResponse`s should have been sent for duplicate additions
require.Equal(t, 2, sink.WriteMessageCallCount())
require.Equal(t, 2, len(p.pendingTracks["cid"].trackInfos))
// check SID is the same
@@ -178,7 +184,8 @@ func TestTrackPublishing(t *testing.T) {
Name: "webcam",
Type: livekit.TrackType_VIDEO,
})
require.Equal(t, 0, sink.WriteMessageCallCount())
// `queued` `RequestResponse` should add a message
require.Equal(t, 1, sink.WriteMessageCallCount())
require.Equal(t, 1, len(p.pendingTracks["cid"].trackInfos))
// add again - it should be added to the queue
@@ -187,7 +194,8 @@ func TestTrackPublishing(t *testing.T) {
Name: "webcam",
Type: livekit.TrackType_VIDEO,
})
require.Equal(t, 0, sink.WriteMessageCallCount())
// `queued` `RequestResponse`s should have been sent for duplicate additions
require.Equal(t, 2, sink.WriteMessageCallCount())
require.Equal(t, 2, len(p.pendingTracks["cid"].trackInfos))
// check SID is the same
@@ -217,7 +225,8 @@ func TestTrackPublishing(t *testing.T) {
Type: livekit.TrackType_AUDIO,
Source: livekit.TrackSource_MICROPHONE,
})
require.Equal(t, 1, sink.WriteMessageCallCount())
// an error response for disallowed source should send a `RequestResponse`.
require.Equal(t, 2, sink.WriteMessageCallCount())
})
}
@@ -225,7 +234,7 @@ func TestOutOfOrderUpdates(t *testing.T) {
p := newParticipantForTest("test")
p.updateState(livekit.ParticipantInfo_JOINED)
p.SetMetadata("initial metadata")
sink := p.getResponseSink().(*routingfakes.FakeMessageSink)
sink := p.GetResponseSink().(*routingfakes.FakeMessageSink)
pi1 := p.ToProto()
p.SetMetadata("second update")
pi2 := p.ToProto()
@@ -255,7 +264,7 @@ func TestDisconnectTiming(t *testing.T) {
}()
track := &typesfakes.FakeMediaTrack{}
p.UpTrackManager.AddPublishedTrack(track)
p.handleTrackPublished(track)
p.handleTrackPublished(track, false)
// close channel and then try to Negotiate
msg.Close()
@@ -275,7 +284,10 @@ func TestMuteSetting(t *testing.T) {
ti := &livekit.TrackInfo{Sid: "testTrack"}
p.pendingTracks["cid"] = &pendingTrackInfo{trackInfos: []*livekit.TrackInfo{ti}}
p.SetTrackMuted(livekit.TrackID(ti.Sid), true, false)
p.SetTrackMuted(&livekit.MuteTrackRequest{
Sid: ti.Sid,
Muted: true,
}, false)
require.True(t, p.pendingTracks["cid"].trackInfos[0].Muted)
})
@@ -287,7 +299,7 @@ func TestMuteSetting(t *testing.T) {
Muted: true,
})
_, ti, _, _ := p.getPendingTrack("cid", livekit.TrackType_AUDIO, false)
_, ti, _, _, _ := p.getPendingTrack("cid", livekit.TrackType_AUDIO, false)
require.NotNil(t, ti)
require.True(t, ti.Muted)
})
@@ -362,28 +374,36 @@ func TestDisableCodecs(t *testing.T) {
}
}
require.True(t, found264)
offerId := uint32(42)
// negotiated codec should not contain h264
sink := &routingfakes.FakeMessageSink{}
participant.SetResponseSink(sink)
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
var answer webrtc.SessionDescription
var answerId uint32
var answerReceived atomic.Bool
var answerIdReceived atomic.Uint32
sink.WriteMessageCalls(func(msg proto.Message) error {
if res, ok := msg.(*livekit.SignalResponse); ok {
if res.GetAnswer() != nil {
answer = FromProtoSessionDescription(res.GetAnswer())
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
answerReceived.Store(true)
answerIdReceived.Store(answerId)
}
}
return nil
})
participant.HandleOffer(sdp)
participant.HandleOffer(&livekit.SessionDescription{
Type: webrtc.SDPTypeOffer.String(),
Sdp: sdp.SDP,
Id: offerId,
})
testutils.WithTimeout(t, func() string {
if answerReceived.Load() {
if answerReceived.Load() && answerIdReceived.Load() == offerId {
return ""
} else {
return "answer not received"
return "answer not received OR answer id mismatch"
}
})
require.NoError(t, pc.SetRemoteDescription(answer), answer.SDP, sdp.SDP)
@@ -415,7 +435,7 @@ func TestDisablePublishCodec(t *testing.T) {
}
sink := &routingfakes.FakeMessageSink{}
participant.SetResponseSink(sink)
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
var publishReceived atomic.Bool
sink.WriteMessageCalls(func(msg proto.Message) error {
if res, ok := msg.(*livekit.SignalResponse); ok {
@@ -463,89 +483,138 @@ func TestDisablePublishCodec(t *testing.T) {
require.Eventually(t, func() bool { return publishReceived.Load() }, 5*time.Second, 10*time.Millisecond)
}
func TestPreferVideoCodecForPublisher(t *testing.T) {
participant := newParticipantForTestWithOpts("123", &participantOpts{
publisher: true,
})
participant.SetMigrateState(types.MigrateStateComplete)
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
require.NoError(t, err)
defer pc.Close()
for i := 0; i < 2; i++ {
// publish h264 track without client preferred codec
trackCid := fmt.Sprintf("preferh264video%d", i)
participant.AddTrack(&livekit.AddTrackRequest{
Type: livekit.TrackType_VIDEO,
Name: "video",
Width: 1280,
Height: 720,
Source: livekit.TrackSource_CAMERA,
SimulcastCodecs: []*livekit.SimulcastCodec{
{
Codec: "h264",
Cid: trackCid,
},
func TestPreferMediaCodecForPublisher(t *testing.T) {
testCases := []struct {
name string
mediaKind string
trackBaseCid string
preferredCodec string
addTrack *livekit.AddTrackRequest
mimeTypeStringChecker func(string) bool
mimeTypeCodecStringChecker func(string) bool
transceiverMimeType mime.MimeType
}{
{
name: "video",
mediaKind: "video",
trackBaseCid: "preferH264Video",
preferredCodec: "h264",
addTrack: &livekit.AddTrackRequest{
Type: livekit.TrackType_VIDEO,
Name: "video",
Width: 1280,
Height: 720,
Source: livekit.TrackSource_CAMERA,
},
})
mimeTypeStringChecker: mime.IsMimeTypeStringH264,
mimeTypeCodecStringChecker: mime.IsMimeTypeCodecStringH264,
transceiverMimeType: mime.MimeTypeVP8,
},
{
name: "audio",
mediaKind: "audio",
trackBaseCid: "preferPCMAAudio",
preferredCodec: "pcma",
addTrack: &livekit.AddTrackRequest{
Type: livekit.TrackType_AUDIO,
Name: "audio",
Source: livekit.TrackSource_MICROPHONE,
},
mimeTypeStringChecker: mime.IsMimeTypeStringPCMA,
mimeTypeCodecStringChecker: mime.IsMimeTypeCodecStringPCMA,
transceiverMimeType: mime.MimeTypeOpus,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
participant := newParticipantForTestWithOpts("123", &participantOpts{
publisher: true,
})
participant.SetMigrateState(types.MigrateStateComplete)
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "video/vp8"}, trackCid, trackCid)
require.NoError(t, err)
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
require.NoError(t, err)
codecs := transceiver.Receiver().GetParameters().Codecs
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
require.NoError(t, err)
defer pc.Close()
if i > 0 {
// the negotiated codecs order could be updated by first negotiation, reorder to make h264 not preferred
for mime.IsMimeTypeStringH264(codecs[0].MimeType) {
codecs = append(codecs[1:], codecs[0])
}
}
// h264 should not be preferred
require.False(t, mime.IsMimeTypeStringH264(codecs[0].MimeType), "codecs", codecs)
sdp, err := pc.CreateOffer(nil)
require.NoError(t, err)
require.NoError(t, pc.SetLocalDescription(sdp))
sink := &routingfakes.FakeMessageSink{}
participant.SetResponseSink(sink)
var answer webrtc.SessionDescription
var answerReceived atomic.Bool
sink.WriteMessageCalls(func(msg proto.Message) error {
if res, ok := msg.(*livekit.SignalResponse); ok {
if res.GetAnswer() != nil {
answer = FromProtoSessionDescription(res.GetAnswer())
pc.SetRemoteDescription(answer)
answerReceived.Store(true)
for i := 0; i < 2; i++ {
// publish preferred track without client using setCodecPreferences()
trackCid := fmt.Sprintf("%s-%d", tc.trackBaseCid, i)
req := utils.CloneProto(tc.addTrack)
req.SimulcastCodecs = []*livekit.SimulcastCodec{
{
Codec: tc.preferredCodec,
Cid: trackCid,
},
}
}
return nil
})
participant.HandleOffer(sdp)
participant.AddTrack(req)
require.Eventually(t, func() bool { return answerReceived.Load() }, 5*time.Second, 10*time.Millisecond)
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: tc.transceiverMimeType.String()}, trackCid, trackCid)
require.NoError(t, err)
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
require.NoError(t, err)
codecs := transceiver.Receiver().GetParameters().Codecs
var h264Preferred bool
parsed, err := answer.Unmarshal()
require.NoError(t, err)
var videoSectionIndex int
for _, m := range parsed.MediaDescriptions {
if m.MediaName.Media == "video" {
if videoSectionIndex == i {
codecs, err := codecsFromMediaDescription(m)
require.NoError(t, err)
if mime.IsMimeTypeCodecStringH264(codecs[0].Name) {
h264Preferred = true
break
if i > 0 {
// the negotiated codecs order could be updated by first negotiation,
// reorder to make tested preferred codec not preferred
for tc.mimeTypeStringChecker(codecs[0].MimeType) {
codecs = append(codecs[1:], codecs[0])
}
}
videoSectionIndex++
}
}
// preferred codec should not be preferred in `offer`
require.False(t, tc.mimeTypeStringChecker(codecs[0].MimeType), "codecs", codecs)
require.Truef(t, h264Preferred, "h264 should be preferred for video section %d, answer sdp: \n%s", i, answer.SDP)
sdp, err := pc.CreateOffer(nil)
require.NoError(t, err)
require.NoError(t, pc.SetLocalDescription(sdp))
offerId := uint32(23)
sink := &routingfakes.FakeMessageSink{}
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
var answer webrtc.SessionDescription
var answerId uint32
var answerReceived atomic.Bool
var answerIdReceived atomic.Uint32
sink.WriteMessageCalls(func(msg proto.Message) error {
if res, ok := msg.(*livekit.SignalResponse); ok {
if res.GetAnswer() != nil {
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
pc.SetRemoteDescription(answer)
answerReceived.Store(true)
answerIdReceived.Store(answerId)
}
}
return nil
})
participant.HandleOffer(&livekit.SessionDescription{
Type: webrtc.SDPTypeOffer.String(),
Sdp: sdp.SDP,
Id: offerId,
})
require.Eventually(t, func() bool { return answerReceived.Load() && answerIdReceived.Load() == offerId }, 5*time.Second, 10*time.Millisecond)
var havePreferred bool
parsed, err := answer.Unmarshal()
require.NoError(t, err)
var mediaSectionIndex int
for _, m := range parsed.MediaDescriptions {
if m.MediaName.Media == tc.mediaKind {
if mediaSectionIndex == i {
codecs, err := lksdp.CodecsFromMediaDescription(m)
require.NoError(t, err)
if tc.mimeTypeCodecStringChecker(codecs[0].Name) {
havePreferred = true
break
}
}
mediaSectionIndex++
}
}
require.Truef(t, havePreferred, "%s should be preferred for %s section %d, answer sdp: \n%s", tc.preferredCodec, tc.mediaKind, i, answer.SDP)
}
})
}
}
@@ -556,33 +625,59 @@ func TestPreferAudioCodecForRed(t *testing.T) {
participant.SetMigrateState(types.MigrateStateComplete)
me := webrtc.MediaEngine{}
me.RegisterDefaultCodecs()
require.NoError(t, me.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: RedCodecCapability,
PayloadType: 63,
}, webrtc.RTPCodecTypeAudio))
opusCodecParameters := protoCodecs.OpusCodecParameters
opusCodecParameters.RTPCodecCapability.RTCPFeedback = []webrtc.RTCPFeedback{{Type: webrtc.TypeRTCPFBNACK}}
require.NoError(t, me.RegisterCodec(opusCodecParameters, webrtc.RTPCodecTypeAudio))
redCodecParameters := protoCodecs.RedCodecParameters
redCodecParameters.RTPCodecCapability.RTCPFeedback = []webrtc.RTCPFeedback{{Type: webrtc.TypeRTCPFBNACK}}
require.NoError(t, me.RegisterCodec(redCodecParameters, webrtc.RTPCodecTypeAudio))
api := webrtc.NewAPI(webrtc.WithMediaEngine(&me))
pc, err := api.NewPeerConnection(webrtc.Configuration{})
require.NoError(t, err)
defer pc.Close()
for i, disableRed := range []bool{false, true} {
for idx, disableRed := range []bool{false, true, false, true} {
t.Run(fmt.Sprintf("disableRed=%v", disableRed), func(t *testing.T) {
trackCid := fmt.Sprintf("audiotrack%d", i)
participant.AddTrack(&livekit.AddTrackRequest{
Type: livekit.TrackType_AUDIO,
DisableRed: disableRed,
Cid: trackCid,
})
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "audio/opus"}, trackCid, trackCid)
trackCid := fmt.Sprintf("audiotrack%d", idx)
req := &livekit.AddTrackRequest{
Type: livekit.TrackType_AUDIO,
Cid: trackCid,
}
if idx < 2 {
req.DisableRed = disableRed
} else {
codec := "red"
if disableRed {
codec = "opus"
}
req.SimulcastCodecs = []*livekit.SimulcastCodec{
{
Codec: codec,
Cid: trackCid,
},
}
}
participant.AddTrack(req)
track, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{MimeType: "audio/opus"},
trackCid,
trackCid,
)
require.NoError(t, err)
transceiver, err := pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv})
transceiver, err := pc.AddTransceiverFromTrack(
track,
webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendrecv},
)
require.NoError(t, err)
codecs := transceiver.Sender().GetParameters().Codecs
for i, c := range codecs {
if c.MimeType == "audio/opus" && i != 0 {
codecs[0], codecs[i] = codecs[i], codecs[0]
if c.MimeType == "audio/opus" {
if i != 0 {
codecs[0], codecs[i] = codecs[i], codecs[0]
}
break
}
}
@@ -592,24 +687,38 @@ func TestPreferAudioCodecForRed(t *testing.T) {
pc.SetLocalDescription(sdp)
// opus should be preferred
require.Equal(t, codecs[0].MimeType, "audio/opus", sdp)
offerId := uint32(0xffffff)
sink := &routingfakes.FakeMessageSink{}
participant.SetResponseSink(sink)
participant.SwapResponseSink(sink, types.SignallingCloseReasonUnknown)
var answer webrtc.SessionDescription
var answerId uint32
var answerReceived atomic.Bool
var answerIdReceived atomic.Uint32
sink.WriteMessageCalls(func(msg proto.Message) error {
if res, ok := msg.(*livekit.SignalResponse); ok {
if res.GetAnswer() != nil {
answer = FromProtoSessionDescription(res.GetAnswer())
answer, answerId, _ = signalling.FromProtoSessionDescription(res.GetAnswer())
pc.SetRemoteDescription(answer)
answerReceived.Store(true)
answerIdReceived.Store(answerId)
}
}
return nil
})
participant.HandleOffer(sdp)
require.Eventually(t, func() bool { return answerReceived.Load() }, 5*time.Second, 10*time.Millisecond)
participant.HandleOffer(&livekit.SessionDescription{
Type: webrtc.SDPTypeOffer.String(),
Sdp: sdp.SDP,
Id: offerId,
})
require.Eventually(
t,
func() bool {
return answerReceived.Load() && answerIdReceived.Load() == offerId
},
5*time.Second,
10*time.Millisecond,
)
var redPreferred bool
parsed, err := answer.Unmarshal()
@@ -617,8 +726,8 @@ func TestPreferAudioCodecForRed(t *testing.T) {
var audioSectionIndex int
for _, m := range parsed.MediaDescriptions {
if m.MediaName.Media == "audio" {
if audioSectionIndex == i {
codecs, err := codecsFromMediaDescription(m)
if audioSectionIndex == idx {
codecs, err := lksdp.CodecsFromMediaDescription(m)
require.NoError(t, err)
// nack is always enabled. if red is preferred, server will not generate nack request
var nackEnabled bool
@@ -702,8 +811,11 @@ func newParticipantForTestWithOpts(identity livekit.ParticipantIdentity, opts *p
ClientConf: opts.clientConf,
ClientInfo: ClientInfo{ClientInfo: opts.clientInfo},
Logger: LoggerWithParticipant(logger.GetLogger(), identity, sid, false),
Telemetry: &telemetryfakes.FakeTelemetryService{},
Reporter: roomobs.NewNoopParticipantSessionReporter(),
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
VersionGenerator: utils.NewDefaultTimedVersionGenerator(),
ParticipantListener: &typesfakes.FakeLocalParticipantListener{},
ParticipantHelper: &typesfakes.FakeLocalParticipantHelper{},
})
p.isPublisher.Store(opts.publisher)
p.updateState(livekit.ParticipantInfo_ACTIVE)
+268 -134
View File
@@ -16,44 +16,279 @@ package rtc
import (
"fmt"
"slices"
"strconv"
"strings"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v4"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
lksdp "github.com/livekit/protocol/sdp"
"github.com/livekit/protocol/utils"
)
func (p *ParticipantImpl) setCodecPreferencesForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
offer = p.setCodecPreferencesOpusRedForPublisher(offer)
offer = p.setCodecPreferencesVideoForPublisher(offer)
return offer
}
func (p *ParticipantImpl) populateSdpCid(parsedOffer *sdp.SessionDescription) ([]*sdp.MediaDescription, []*sdp.MediaDescription) {
processUnmatch := func(unmatches []*sdp.MediaDescription, trackType livekit.TrackType) {
for _, unmatch := range unmatches {
streamID, ok := lksdp.ExtractStreamID(unmatch)
if !ok {
continue
}
func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
parsed, unmatchAudios, err := p.TransportManager.GetUnmatchMediaForOffer(offer, "audio")
if err != nil || len(unmatchAudios) == 0 {
return offer
sdpCodecs, err := lksdp.CodecsFromMediaDescription(unmatch)
if err != nil || len(sdpCodecs) == 0 {
p.pubLogger.Errorw(
"extract codecs from media section failed", err,
"media", unmatch,
"parsedOffer", parsedOffer,
)
continue
}
p.pendingTracksLock.Lock()
signalCid, info, _, migrated, _ := p.getPendingTrack(streamID, trackType, false)
if migrated {
p.pendingTracksLock.Unlock()
continue
}
if info == nil {
p.pendingTracksLock.Unlock()
// could be already published track and the unmatch could be a back up codec publish
numUnmatchedTracks := 0
var unmatchedTrack types.MediaTrack
var unmatchedSdpMimeType mime.MimeType
found := false
for _, sdpCodec := range sdpCodecs {
sdpMimeType := mime.NormalizeMimeTypeCodec(sdpCodec.Name).ToMimeType()
for _, publishedTrack := range p.GetPublishedTracks() {
if sigCid, sdpCid := publishedTrack.(*MediaTrack).GetCidsForMimeType(sdpMimeType); sigCid != "" && sdpCid == "" {
// a back up codec has a SDP cid match
if sigCid == streamID {
found = true
break
} else {
numUnmatchedTracks++
unmatchedTrack = publishedTrack
unmatchedSdpMimeType = sdpMimeType
}
}
}
if found {
break
}
}
if !found && unmatchedTrack != nil {
if numUnmatchedTracks != 1 {
p.pubLogger.Warnw(
"too many unmatched tracks", nil,
"media", unmatch,
"parsedOffer", parsedOffer,
)
}
unmatchedTrack.(*MediaTrack).UpdateCodecSdpCid(unmatchedSdpMimeType, streamID)
p.pubLogger.Debugw(
"published track SDP cid updated",
"trackID", unmatchedTrack.ID(),
"track", logger.Proto(unmatchedTrack.ToProto()),
)
}
continue
}
if len(info.Codecs) == 0 {
p.pendingTracksLock.Unlock()
p.pubLogger.Warnw(
"track without codecs", nil,
"trackID", info.Sid,
"pendingTrack", p.pendingTracks[signalCid],
"media", unmatch,
"parsedOffer", parsedOffer,
)
continue
}
found := false
updated := false
for _, sdpCodec := range sdpCodecs {
if mime.NormalizeMimeTypeCodec(sdpCodec.Name) == mime.GetMimeTypeCodec(info.Codecs[0].MimeType) {
// set SdpCid only if different from SignalCid
if streamID != info.Codecs[0].Cid {
info.Codecs[0].SdpCid = streamID
updated = true
}
found = true
break
}
if found {
break
}
}
if !found {
// not using SimulcastCodec, i. e. mime type not available till track publish
if len(info.Codecs) == 1 {
// set SdpCid only if different from SignalCid
if streamID != info.Codecs[0].Cid {
info.Codecs[0].SdpCid = streamID
updated = true
}
}
}
if updated {
p.pendingTracks[signalCid].trackInfos[0] = utils.CloneProto(info)
p.pubLogger.Debugw(
"pending track SDP cid updated",
"signalCid", signalCid,
"trackID", info.Sid,
"pendingTrack", p.pendingTracks[signalCid],
)
}
p.pendingTracksLock.Unlock()
}
}
unmatchAudios, err := p.TransportManager.GetUnmatchMediaForOffer(parsedOffer, "audio")
if err != nil {
p.pubLogger.Warnw("could not get unmatch audios", err)
return nil, nil
}
unmatchVideos, err := p.TransportManager.GetUnmatchMediaForOffer(parsedOffer, "video")
if err != nil {
p.pubLogger.Warnw("could not get unmatch videos", err)
return nil, nil
}
processUnmatch(unmatchAudios, livekit.TrackType_AUDIO)
processUnmatch(unmatchVideos, livekit.TrackType_VIDEO)
return unmatchAudios, unmatchVideos
}
func (p *ParticipantImpl) setCodecPreferencesForPublisher(
parsedOffer *sdp.SessionDescription,
unmatchAudios []*sdp.MediaDescription,
unmatchVideos []*sdp.MediaDescription,
) {
unprocessedUnmatchAudios := p.setCodecPreferencesForPublisherMedia(
parsedOffer,
unmatchAudios,
livekit.TrackType_AUDIO,
)
p.setCodecPreferencesOpusRedForPublisher(parsedOffer, unprocessedUnmatchAudios)
_ = p.setCodecPreferencesForPublisherMedia(
parsedOffer,
unmatchVideos,
livekit.TrackType_VIDEO,
)
}
func (p *ParticipantImpl) setCodecPreferencesForPublisherMedia(
parsedOffer *sdp.SessionDescription,
unmatches []*sdp.MediaDescription,
trackType livekit.TrackType,
) []*sdp.MediaDescription {
unprocessed := make([]*sdp.MediaDescription, 0, len(unmatches))
for _, unmatch := range unmatches {
var ti *livekit.TrackInfo
var mimeType string
mid := lksdp.GetMidValue(unmatch)
if mid == "" {
unprocessed = append(unprocessed, unmatch)
continue
}
transceiver := p.TransportManager.GetPublisherRTPTransceiver(mid)
if transceiver == nil {
unprocessed = append(unprocessed, unmatch)
continue
}
streamID, ok := lksdp.ExtractStreamID(unmatch)
if !ok {
unprocessed = append(unprocessed, unmatch)
continue
}
p.pendingTracksLock.RLock()
mt := p.getPublishedTrackBySdpCid(streamID)
if mt != nil {
ti = mt.ToProto()
} else {
_, ti, _, _, _ = p.getPendingTrack(streamID, trackType, false)
}
p.pendingTracksLock.RUnlock()
if ti == nil {
unprocessed = append(unprocessed, unmatch)
continue
}
for _, c := range ti.Codecs {
if c.Cid == streamID || c.SdpCid == streamID {
mimeType = c.MimeType
break
}
}
if mimeType == "" && len(ti.Codecs) > 0 {
mimeType = ti.Codecs[0].MimeType
}
if mimeType == "" {
unprocessed = append(unprocessed, unmatch)
continue
}
configureReceiverCodecs(
transceiver,
mimeType,
p.params.ClientInfo.ComplyWithCodecOrderInSDPAnswer(),
)
}
return unprocessed
}
func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(
parsedOffer *sdp.SessionDescription,
unmatchAudios []*sdp.MediaDescription,
) {
for _, unmatchAudio := range unmatchAudios {
mid := lksdp.GetMidValue(unmatchAudio)
if mid == "" {
continue
}
transceiver := p.TransportManager.GetPublisherRTPTransceiver(mid)
if transceiver == nil {
continue
}
streamID, ok := lksdp.ExtractStreamID(unmatchAudio)
if !ok {
continue
}
p.pendingTracksLock.RLock()
_, info, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
// if RED is disabled for this track, don't prefer RED codec in offer
disableRed := info != nil && info.DisableRed
_, ti, _, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
p.pendingTracksLock.RUnlock()
if ti == nil {
continue
}
codecs, err := codecsFromMediaDescription(unmatchAudio)
codecs, err := lksdp.CodecsFromMediaDescription(unmatchAudio)
if err != nil {
p.pubLogger.Errorw("extract codecs from media section failed", err, "media", unmatchAudio)
p.pubLogger.Errorw(
"extract codecs from media section failed", err,
"media", unmatchAudio,
"parsedOffer", parsedOffer,
)
continue
}
@@ -68,122 +303,17 @@ func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(offer webrtc.Se
continue
}
var preferredCodecs, leftCodecs []string
preferRED := IsRedEnabled(ti)
// if RED is enabled for this track, prefer RED codec in offer
for _, codec := range codecs {
// codec contain opus/red
if !disableRed && mime.IsMimeTypeCodecStringRED(codec.Name) && strings.Contains(codec.Fmtp, strconv.FormatInt(int64(opusPayload), 10)) {
preferredCodecs = append(preferredCodecs, strconv.FormatInt(int64(codec.PayloadType), 10))
} else {
leftCodecs = append(leftCodecs, strconv.FormatInt(int64(codec.PayloadType), 10))
}
}
// ensure nack enabled for audio in publisher offer
var nackFound bool
for _, attr := range unmatchAudio.Attributes {
if attr.Key == "rtcp-fb" && strings.Contains(attr.Value, fmt.Sprintf("%d nack", opusPayload)) {
nackFound = true
if preferRED &&
mime.IsMimeTypeCodecStringRED(codec.Name) &&
strings.Contains(codec.Fmtp, strconv.FormatInt(int64(opusPayload), 10)) {
configureReceiverCodecs(transceiver, "audio/red", true)
break
}
}
if !nackFound {
unmatchAudio.Attributes = append(unmatchAudio.Attributes, sdp.Attribute{
Key: "rtcp-fb",
Value: fmt.Sprintf("%d nack", opusPayload),
})
}
// no opus/red found
if len(preferredCodecs) == 0 {
continue
}
unmatchAudio.MediaName.Formats = append(unmatchAudio.MediaName.Formats[:0], preferredCodecs...)
unmatchAudio.MediaName.Formats = append(unmatchAudio.MediaName.Formats, leftCodecs...)
}
bytes, err := parsed.Marshal()
if err != nil {
p.pubLogger.Errorw("failed to marshal offer", err)
return offer
}
return webrtc.SessionDescription{
Type: offer.Type,
SDP: string(bytes),
}
}
func (p *ParticipantImpl) setCodecPreferencesVideoForPublisher(offer webrtc.SessionDescription) webrtc.SessionDescription {
parsed, unmatchVideos, err := p.TransportManager.GetUnmatchMediaForOffer(offer, "video")
if err != nil || len(unmatchVideos) == 0 {
return offer
}
// unmatched video is pending for publish, set codec preference
for _, unmatchVideo := range unmatchVideos {
streamID, ok := lksdp.ExtractStreamID(unmatchVideo)
if !ok {
continue
}
var info *livekit.TrackInfo
p.pendingTracksLock.RLock()
mt := p.getPublishedTrackBySdpCid(streamID)
if mt != nil {
info = mt.ToProto()
} else {
_, info, _, _ = p.getPendingTrack(streamID, livekit.TrackType_VIDEO, false)
}
if info == nil {
p.pendingTracksLock.RUnlock()
continue
}
var mimeType string
for _, c := range info.Codecs {
if c.Cid == streamID {
mimeType = c.MimeType
break
}
}
if mimeType == "" && len(info.Codecs) > 0 {
mimeType = info.Codecs[0].MimeType
}
p.pendingTracksLock.RUnlock()
if mimeType != "" {
codecs, err := codecsFromMediaDescription(unmatchVideo)
if err != nil {
p.pubLogger.Errorw("extract codecs from media section failed", err, "media", unmatchVideo)
continue
}
var preferredCodecs, leftCodecs []string
for _, c := range codecs {
if mime.GetMimeTypeCodec(mimeType) == mime.NormalizeMimeTypeCodec(c.Name) {
preferredCodecs = append(preferredCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
} else {
leftCodecs = append(leftCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
}
}
unmatchVideo.MediaName.Formats = append(unmatchVideo.MediaName.Formats[:0], preferredCodecs...)
// if the client don't comply with codec order in SDP answer, only keep preferred codecs to force client to use it
if p.params.ClientInfo.ComplyWithCodecOrderInSDPAnswer() {
unmatchVideo.MediaName.Formats = append(unmatchVideo.MediaName.Formats, leftCodecs...)
}
}
}
bytes, err := parsed.Marshal()
if err != nil {
p.pubLogger.Errorw("failed to marshal offer", err)
return offer
}
return webrtc.SessionDescription{
Type: offer.Type,
SDP: string(bytes),
}
}
@@ -195,12 +325,12 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
return answer
}
parsed, err := answer.Unmarshal()
parsedAnswer, err := answer.Unmarshal()
if err != nil {
return answer
}
for _, m := range parsed.MediaDescriptions {
for _, m := range parsedAnswer.MediaDescriptions {
switch m.MediaName.Media {
case "audio":
_, ok := m.Attribute(sdp.AttrKeyInactive)
@@ -227,7 +357,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
track, _ := p.getPublishedTrackBySdpCid(streamID).(*MediaTrack)
if track == nil {
p.pendingTracksLock.RLock()
_, ti, _, _ = p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
_, ti, _, _, _ = p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
p.pendingTracksLock.RUnlock()
} else {
ti = track.ToProto()
@@ -236,12 +366,12 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
}
}
if ti == nil || (ti.DisableDtx && !ti.Stereo) {
if ti == nil {
// no need to configure
continue
}
opusPT, err := parsed.GetPayloadTypeForCodec(sdp.Codec{Name: mime.MimeTypeCodecOpus.String()})
opusPT, err := parsedAnswer.GetPayloadTypeForCodec(sdp.Codec{Name: mime.MimeTypeCodecOpus.String()})
if err != nil {
p.pubLogger.Infow("failed to get opus payload type", "error", err, "trackID", ti.Sid)
continue
@@ -249,11 +379,15 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
for i, attr := range m.Attributes {
if strings.HasPrefix(attr.String(), fmt.Sprintf("fmtp:%d", opusPT)) {
if !ti.DisableDtx {
if !slices.Contains(ti.AudioFeatures, livekit.AudioTrackFeature_TF_NO_DTX) {
attr.Value += ";usedtx=1"
} else {
attr.Value = strings.ReplaceAll(attr.Value, ";usedtx=1", "")
}
if ti.Stereo {
if slices.Contains(ti.AudioFeatures, livekit.AudioTrackFeature_TF_STEREO) {
attr.Value += ";stereo=1;maxaveragebitrate=510000"
} else {
attr.Value = strings.ReplaceAll(attr.Value, ";stereo=1", "")
}
m.Attributes[i] = attr
}
@@ -264,7 +398,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
}
}
bytes, err := parsed.Marshal()
bytes, err := parsedAnswer.Marshal()
if err != nil {
p.pubLogger.Infow("failed to marshal answer", "error", err)
return answer
+124 -155
View File
@@ -15,30 +15,28 @@
package rtc
import (
"fmt"
"time"
"github.com/pion/webrtc/v4"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
"github.com/livekit/psrpc"
protosignalling "github.com/livekit/protocol/signalling"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
func (p *ParticipantImpl) getResponseSink() routing.MessageSink {
p.resSinkMu.Lock()
defer p.resSinkMu.Unlock()
return p.resSink
func (p *ParticipantImpl) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
p.signaller.SwapResponseSink(sink, reason)
}
func (p *ParticipantImpl) SetResponseSink(sink routing.MessageSink) {
p.resSinkMu.Lock()
defer p.resSinkMu.Unlock()
p.resSink = sink
func (p *ParticipantImpl) GetResponseSink() routing.MessageSink {
return p.signaller.GetResponseSink()
}
func (p *ParticipantImpl) CloseSignalConnection(reason types.SignallingCloseReason) {
p.signaller.CloseSignalConnection(reason)
}
func (p *ParticipantImpl) SendJoinResponse(joinResponse *livekit.JoinResponse) error {
@@ -55,11 +53,7 @@ func (p *ParticipantImpl) SendJoinResponse(joinResponse *livekit.JoinResponse) e
p.updateLock.Unlock()
// send Join response
err := p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Join{
Join: joinResponse,
},
})
err := p.signaller.WriteMessage(p.signalling.SignalJoinResponse(joinResponse))
if err != nil {
return err
}
@@ -111,7 +105,7 @@ func (p *ParticipantImpl) SendParticipantUpdate(participantsToUpdate []*livekit.
isValid = false
}
}
if pi.Permission != nil && pi.Permission.Hidden && pi.Sid != string(p.params.SID) {
if pi.Permission != nil && pi.Permission.Hidden && pi.Sid != string(p.ID()) {
p.params.Logger.Debugw("skipping hidden participant update", "otherParticipant", pi.Identity)
isValid = false
}
@@ -127,17 +121,7 @@ func (p *ParticipantImpl) SendParticipantUpdate(participantsToUpdate []*livekit.
}
p.updateLock.Unlock()
if len(validUpdates) == 0 {
return nil
}
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Update{
Update: &livekit.ParticipantUpdate{
Participants: validUpdates,
},
},
})
return p.signaller.WriteMessage(p.signalling.SignalParticipantUpdate(validUpdates))
}
// SendSpeakerUpdate notifies participant changes to speakers. only send members that have changed since last update
@@ -158,47 +142,23 @@ func (p *ParticipantImpl) SendSpeakerUpdate(speakers []*livekit.SpeakerInfo, for
}
}
if len(scopedSpeakers) == 0 {
return nil
}
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_SpeakersChanged{
SpeakersChanged: &livekit.SpeakersChanged{
Speakers: scopedSpeakers,
},
},
})
return p.signaller.WriteMessage(p.signalling.SignalSpeakerUpdate(scopedSpeakers))
}
func (p *ParticipantImpl) SendRoomUpdate(room *livekit.Room) error {
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_RoomUpdate{
RoomUpdate: &livekit.RoomUpdate{
Room: room,
},
},
})
return p.signaller.WriteMessage(p.signalling.SignalRoomUpdate(room))
}
func (p *ParticipantImpl) SendConnectionQualityUpdate(update *livekit.ConnectionQualityUpdate) error {
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_ConnectionQuality{
ConnectionQuality: update,
},
})
return p.signaller.WriteMessage(p.signalling.SignalConnectionQualityUpdate(update))
}
func (p *ParticipantImpl) SendRefreshToken(token string) error {
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_RefreshToken{
RefreshToken: token,
},
})
return p.signaller.WriteMessage(p.signalling.SignalRefreshToken(token))
}
func (p *ParticipantImpl) SendRequestResponse(requestResponse *livekit.RequestResponse) error {
if requestResponse.RequestId == 0 || !p.params.ClientInfo.SupportErrorResponse() {
func (p *ParticipantImpl) sendRequestResponse(requestResponse *livekit.RequestResponse) error {
if !p.params.ClientInfo.SupportsRequestResponse() {
return nil
}
@@ -206,11 +166,11 @@ func (p *ParticipantImpl) SendRequestResponse(requestResponse *livekit.RequestRe
return nil
}
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_RequestResponse{
RequestResponse: requestResponse,
},
})
return p.signaller.WriteMessage(p.signalling.SignalRequestResponse(requestResponse))
}
func (p *ParticipantImpl) SendRoomMovedResponse(roomMovedResponse *livekit.RoomMovedResponse) error {
return p.signaller.WriteMessage(p.signalling.SignalRoomMovedResponse(roomMovedResponse))
}
func (p *ParticipantImpl) HandleReconnectAndSendResponse(reconnectReason livekit.ReconnectReason, reconnectResponse *livekit.ReconnectResponse) error {
@@ -219,15 +179,11 @@ func (p *ParticipantImpl) HandleReconnectAndSendResponse(reconnectReason livekit
if !p.params.ClientInfo.CanHandleReconnectResponse() {
return nil
}
if err := p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Reconnect{
Reconnect: reconnectResponse,
},
}); err != nil {
if err := p.signaller.WriteMessage(p.signalling.SignalReconnectResponse(reconnectResponse)); err != nil {
return err
}
if p.params.ProtocolVersion.SupportHandlesDisconnectedUpdate() {
if p.params.ProtocolVersion.SupportsDisconnectedUpdate() {
return p.sendDisconnectUpdatesForReconnect()
}
@@ -255,17 +211,7 @@ func (p *ParticipantImpl) sendDisconnectUpdatesForReconnect() error {
}
p.updateLock.Unlock()
if len(disconnectedParticipants) == 0 {
return nil
}
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Update{
Update: &livekit.ParticipantUpdate{
Participants: disconnectedParticipants,
},
},
})
return p.signaller.WriteMessage(p.signalling.SignalParticipantUpdate(disconnectedParticipants))
}
func (p *ParticipantImpl) sendICECandidate(ic *webrtc.ICECandidate, target livekit.SignalTarget) error {
@@ -274,90 +220,43 @@ func (p *ParticipantImpl) sendICECandidate(ic *webrtc.ICECandidate, target livek
return nil
}
trickle := ToProtoTrickle(prevIC.ToJSON(), target, ic == nil)
trickle := protosignalling.ToProtoTrickle(prevIC.ToJSON(), target, ic == nil)
p.params.Logger.Debugw("sending ICE candidate", "transport", target, "trickle", logger.Proto(trickle))
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Trickle{
Trickle: trickle,
},
})
return p.signaller.WriteMessage(p.signalling.SignalICECandidate(trickle))
}
func (p *ParticipantImpl) sendTrackMuted(trackID livekit.TrackID, muted bool) {
_ = p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Mute{
Mute: &livekit.MuteTrackRequest{
Sid: string(trackID),
Muted: muted,
},
},
})
_ = p.signaller.WriteMessage(p.signalling.SignalTrackMuted(&livekit.MuteTrackRequest{
Sid: string(trackID),
Muted: muted,
}))
}
func (p *ParticipantImpl) sendTrackPublished(cid string, ti *livekit.TrackInfo) error {
p.pubLogger.Debugw("sending track published", "cid", cid, "trackInfo", logger.Proto(ti))
return p.signaller.WriteMessage(p.signalling.SignalTrackPublished(&livekit.TrackPublishedResponse{
Cid: cid,
Track: ti,
}))
}
func (p *ParticipantImpl) sendTrackUnpublished(trackID livekit.TrackID) {
_ = p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_TrackUnpublished{
TrackUnpublished: &livekit.TrackUnpublishedResponse{
TrackSid: string(trackID),
},
},
})
_ = p.signaller.WriteMessage(p.signalling.SignalTrackUnpublished(&livekit.TrackUnpublishedResponse{
TrackSid: string(trackID),
}))
}
func (p *ParticipantImpl) sendTrackHasBeenSubscribed(trackID livekit.TrackID) {
if !p.params.ClientInfo.SupportTrackSubscribedEvent() {
if !p.params.ClientInfo.SupportsTrackSubscribedEvent() {
return
}
_ = p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_TrackSubscribed{
TrackSubscribed: &livekit.TrackSubscribed{
TrackSid: string(trackID),
},
},
})
_ = p.signaller.WriteMessage(p.signalling.SignalTrackSubscribed(&livekit.TrackSubscribed{
TrackSid: string(trackID),
}))
p.params.Logger.Debugw("track has been subscribed", "trackID", trackID)
}
func (p *ParticipantImpl) writeMessage(msg *livekit.SignalResponse) error {
if p.IsDisconnected() || (!p.IsReady() && msg.GetJoin() == nil) {
return nil
}
sink := p.getResponseSink()
if sink == nil {
p.params.Logger.Debugw("could not send message to participant", "messageType", fmt.Sprintf("%T", msg.Message))
return nil
}
err := sink.WriteMessage(msg)
if utils.ErrorIsOneOf(err, psrpc.Canceled, routing.ErrChannelClosed) {
p.params.Logger.Debugw(
"could not send message to participant",
"error", err,
"messageType", fmt.Sprintf("%T", msg.Message),
)
return nil
} else if err != nil {
p.params.Logger.Warnw(
"could not send message to participant", err,
"messageType", fmt.Sprintf("%T", msg.Message),
)
return err
}
return nil
}
// closes signal connection to notify client to resume/reconnect
func (p *ParticipantImpl) CloseSignalConnection(reason types.SignallingCloseReason) {
sink := p.getResponseSink()
if sink != nil {
p.params.Logger.Debugw("closing signal connection", "reason", reason, "connID", sink.ConnectionID())
sink.Close()
p.SetResponseSink(nil)
}
}
func (p *ParticipantImpl) sendLeaveRequest(
reason types.ParticipantCloseReason,
isExpectedToResume bool,
@@ -377,9 +276,9 @@ func (p *ParticipantImpl) sendLeaveRequest(
default:
leave.Action = livekit.LeaveRequest_DISCONNECT
}
if leave.Action != livekit.LeaveRequest_DISCONNECT && p.params.GetRegionSettings != nil {
if leave.Action != livekit.LeaveRequest_DISCONNECT {
// sending region settings even for RESUME just in case client wants to a full reconnect despite server saying RESUME
leave.Regions = p.params.GetRegionSettings(p.params.ClientInfo.Address)
leave.Regions = p.helper().GetRegionSettings(p.params.ClientInfo.Address)
}
} else {
if !sendOnlyIfSupportingLeaveRequestWithAction {
@@ -390,12 +289,82 @@ func (p *ParticipantImpl) sendLeaveRequest(
}
}
if leave != nil {
return p.writeMessage(&livekit.SignalResponse{
Message: &livekit.SignalResponse_Leave{
Leave: leave,
},
})
return p.signaller.WriteMessage(p.signalling.SignalLeaveRequest(leave))
}
return nil
}
func (p *ParticipantImpl) sendSdpAnswer(answer webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error {
return p.signaller.WriteMessage(p.signalling.SignalSdpAnswer(protosignalling.ToProtoSessionDescription(answer, answerId, midToTrackID)))
}
func (p *ParticipantImpl) sendSdpOffer(offer webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error {
return p.signaller.WriteMessage(p.signalling.SignalSdpOffer(protosignalling.ToProtoSessionDescription(offer, offerId, midToTrackID)))
}
func (p *ParticipantImpl) sendStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) error {
return p.signaller.WriteMessage(p.signalling.SignalStreamStateUpdate(streamStateUpdate))
}
func (p *ParticipantImpl) sendSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) error {
return p.signaller.WriteMessage(p.signalling.SignalSubscribedQualityUpdate(subscribedQualityUpdate))
}
func (p *ParticipantImpl) sendSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) error {
return p.signaller.WriteMessage(p.signalling.SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate))
}
func (p *ParticipantImpl) sendSubscriptionResponse(trackID livekit.TrackID, subErr livekit.SubscriptionError) error {
return p.signaller.WriteMessage(p.signalling.SignalSubscriptionResponse(&livekit.SubscriptionResponse{
TrackSid: string(trackID),
Err: subErr,
}))
}
func (p *ParticipantImpl) SendSubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool) error {
p.subLogger.Debugw("sending subscription permission update", "publisherID", publisherID, "trackID", trackID, "allowed", allowed)
err := p.signaller.WriteMessage(p.signalling.SignalSubscriptionPermissionUpdate(&livekit.SubscriptionPermissionUpdate{
ParticipantSid: string(publisherID),
TrackSid: string(trackID),
Allowed: allowed,
}))
if err != nil {
p.subLogger.Errorw("could not send subscription permission update", err)
}
return err
}
func (p *ParticipantImpl) sendMediaSectionsRequirement(numAudios uint32, numVideos uint32) error {
p.pubLogger.Debugw(
"sending media sections requirement",
"numAudios", numAudios,
"numVideos", numVideos,
)
err := p.signaller.WriteMessage(p.signalling.SignalMediaSectionsRequirement(&livekit.MediaSectionsRequirement{
NumAudios: numAudios,
NumVideos: numVideos,
}))
if err != nil {
p.subLogger.Errorw("could not send media sections requirement", err)
}
return err
}
func (p *ParticipantImpl) sendPublishDataTrackResponse(dti *livekit.DataTrackInfo) error {
return p.signaller.WriteMessage(p.signalling.SignalPublishDataTrackResponse(&livekit.PublishDataTrackResponse{
Info: dti,
}))
}
func (p *ParticipantImpl) sendUnpublishDataTrackResponse(dti *livekit.DataTrackInfo) error {
return p.signaller.WriteMessage(p.signalling.SignalUnpublishDataTrackResponse(&livekit.UnpublishDataTrackResponse{
Info: dti,
}))
}
func (p *ParticipantImpl) SendDataTrackSubscriberHandles(handles map[uint32]*livekit.DataTrackSubscriberHandles_PublishedDataTrack) error {
return p.signaller.WriteMessage(p.signalling.SignalDataTrackSubscriberHandles(&livekit.DataTrackSubscriberHandles{
SubHandles: handles,
}))
}
+892 -429
View File
File diff suppressed because it is too large Load Diff
+82 -57
View File
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"github.com/livekit/protocol/auth/authfakes"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/utils"
"github.com/livekit/protocol/webhook"
@@ -89,7 +90,7 @@ func TestJoinedState(t *testing.T) {
func TestRoomJoin(t *testing.T) {
t.Run("joining returns existing participant data", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: numParticipants})
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false)
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
_ = rm.Join(pNew, nil, nil, iceServersForRoom)
@@ -104,14 +105,14 @@ func TestRoomJoin(t *testing.T) {
t.Run("subscribe to existing channels upon join", func(t *testing.T) {
numExisting := 3
rm := newRoomWithParticipants(t, testRoomOpts{num: numExisting})
p := NewMockParticipant("new", types.CurrentProtocol, false, false)
lpl := rm.LocalParticipantListener()
p := NewMockParticipant("new", types.CurrentProtocol, false, false, lpl)
err := rm.Join(p, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
require.NoError(t, err)
stateChangeCB := p.OnStateChangeArgsForCall(0)
require.NotNil(t, stateChangeCB)
stateChangeCB(p, livekit.ParticipantInfo_ACTIVE)
p.StateReturns(livekit.ParticipantInfo_ACTIVE)
lpl.OnStateChange(p)
// it should become a subscriber when connectivity changes
numTracks := 0
@@ -128,7 +129,7 @@ func TestRoomJoin(t *testing.T) {
t.Run("participant state change is broadcasted to others", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: numParticipants})
var changedParticipant types.Participant
rm.OnParticipantChanged(func(participant types.LocalParticipant) {
rm.OnParticipantChanged(func(participant types.Participant) {
changedParticipant = participant
})
participants := rm.GetParticipants()
@@ -159,7 +160,7 @@ func TestRoomJoin(t *testing.T) {
rm.lock.Lock()
rm.protoRoom.MaxParticipants = 1
rm.lock.Unlock()
p := NewMockParticipant("second", types.ProtocolVersion(0), false, false)
p := NewMockParticipant("second", types.ProtocolVersion(0), false, false, rm.LocalParticipantListener())
err := rm.Join(p, nil, nil, iceServersForRoom)
require.Equal(t, ErrMaxParticipantsExceeded, err)
@@ -177,7 +178,7 @@ func TestParticipantUpdate(t *testing.T) {
"track mutes are sent to everyone",
true,
func(p types.LocalParticipant) {
p.SetTrackMuted("", true, false)
p.SetTrackMuted(&livekit.MuteTrackRequest{Muted: true}, false)
},
},
{
@@ -270,14 +271,14 @@ func TestPushAndDequeueUpdates(t *testing.T) {
pi *livekit.ParticipantInfo
closeReason types.ParticipantCloseReason
immediate bool
existing *participantUpdate
expected []*participantUpdate
validate func(t *testing.T, rm *Room, updates []*participantUpdate)
existing *ParticipantUpdate
expected []*ParticipantUpdate
validate func(t *testing.T, rm *Room, updates []*ParticipantUpdate)
}{
{
name: "publisher updates are immediate",
pi: publisher1v1,
expected: []*participantUpdate{{pi: publisher1v1}},
expected: []*ParticipantUpdate{{ParticipantInfo: publisher1v1}},
},
{
name: "subscriber updates are queued",
@@ -286,20 +287,20 @@ func TestPushAndDequeueUpdates(t *testing.T) {
{
name: "last version is enqueued",
pi: subscriber1v2,
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)}, // clone the existing value since it can be modified when setting to disconnected
validate: func(t *testing.T, rm *Room, _ []*participantUpdate) {
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)}, // clone the existing value since it can be modified when setting to disconnected
validate: func(t *testing.T, rm *Room, _ []*ParticipantUpdate) {
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
require.NotNil(t, queued)
requirePIEquals(t, subscriber1v2, queued.pi)
requirePIEquals(t, subscriber1v2, queued.ParticipantInfo)
},
},
{
name: "latest version when immediate",
pi: subscriber1v2,
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)},
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)},
immediate: true,
expected: []*participantUpdate{{pi: subscriber1v2}},
validate: func(t *testing.T, rm *Room, _ []*participantUpdate) {
expected: []*ParticipantUpdate{{ParticipantInfo: subscriber1v2}},
validate: func(t *testing.T, rm *Room, _ []*ParticipantUpdate) {
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
require.Nil(t, queued)
},
@@ -307,37 +308,37 @@ func TestPushAndDequeueUpdates(t *testing.T) {
{
name: "out of order updates are rejected",
pi: subscriber1v1,
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v2)},
validate: func(t *testing.T, rm *Room, updates []*participantUpdate) {
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v2)},
validate: func(t *testing.T, rm *Room, updates []*ParticipantUpdate) {
queued := rm.batchedUpdates[livekit.ParticipantIdentity(identity)]
requirePIEquals(t, subscriber1v2, queued.pi)
requirePIEquals(t, subscriber1v2, queued.ParticipantInfo)
},
},
{
name: "sid change is broadcasted immediately with synthsized disconnect",
pi: publisher2,
closeReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant, // just to test if update contain the close reason
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v2), closeReason: types.ParticipantCloseReasonStale},
expected: []*participantUpdate{
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v2), CloseReason: types.ParticipantCloseReasonStale},
expected: []*ParticipantUpdate{
{
pi: &livekit.ParticipantInfo{
ParticipantInfo: &livekit.ParticipantInfo{
Identity: identity,
Sid: "1",
Version: 2,
State: livekit.ParticipantInfo_DISCONNECTED,
},
isSynthesizedDisconnect: true,
closeReason: types.ParticipantCloseReasonStale,
IsSynthesizedDisconnect: true,
CloseReason: types.ParticipantCloseReasonStale,
},
{pi: publisher2, closeReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant},
{ParticipantInfo: publisher2, CloseReason: types.ParticipantCloseReasonServiceRequestRemoveParticipant},
},
},
{
name: "when switching to publisher, queue is cleared",
pi: publisher1v2,
existing: &participantUpdate{pi: utils.CloneProto(subscriber1v1)},
expected: []*participantUpdate{{pi: publisher1v2}},
validate: func(t *testing.T, rm *Room, updates []*participantUpdate) {
existing: &ParticipantUpdate{ParticipantInfo: utils.CloneProto(subscriber1v1)},
expected: []*ParticipantUpdate{{ParticipantInfo: publisher1v2}},
validate: func(t *testing.T, rm *Room, updates []*ParticipantUpdate) {
require.Empty(t, rm.batchedUpdates)
},
},
@@ -347,14 +348,22 @@ func TestPushAndDequeueUpdates(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 1})
if tc.existing != nil {
rm.batchedUpdates[livekit.ParticipantIdentity(tc.existing.pi.Identity)] = tc.existing
rm.batchedUpdates[livekit.ParticipantIdentity(tc.existing.ParticipantInfo.Identity)] = tc.existing
}
updates := rm.pushAndDequeueUpdates(tc.pi, tc.closeReason, tc.immediate)
rm.batchedUpdatesMu.Lock()
updates := PushAndDequeueUpdates(
tc.pi,
tc.closeReason,
tc.immediate,
rm.GetParticipant(livekit.ParticipantIdentity(tc.pi.Identity)),
rm.batchedUpdates,
)
rm.batchedUpdatesMu.Unlock()
require.Equal(t, len(tc.expected), len(updates))
for i, item := range tc.expected {
requirePIEquals(t, item.pi, updates[i].pi)
require.Equal(t, item.isSynthesizedDisconnect, updates[i].isSynthesizedDisconnect)
require.Equal(t, item.closeReason, updates[i].closeReason)
requirePIEquals(t, item.ParticipantInfo, updates[i].ParticipantInfo)
require.Equal(t, item.IsSynthesizedDisconnect, updates[i].IsSynthesizedDisconnect)
require.Equal(t, item.CloseReason, updates[i].CloseReason)
}
if tc.validate != nil {
@@ -417,6 +426,8 @@ func TestRoomClosure(t *testing.T) {
func TestNewTrack(t *testing.T) {
t.Run("new track should be added to ready participants", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 3})
lpl := rm.LocalParticipantListener()
participants := rm.GetParticipants()
p0 := participants[0].(*typesfakes.FakeLocalParticipant)
p0.StateReturns(livekit.ParticipantInfo_JOINED)
@@ -427,9 +438,8 @@ func TestNewTrack(t *testing.T) {
// pub adds track
track := NewMockTrack(livekit.TrackType_VIDEO, "webcam")
trackCB := pub.OnTrackPublishedArgsForCall(0)
require.NotNil(t, trackCB)
trackCB(pub, track)
lpl.OnTrackPublished(pub, track)
// only p1 should've been subscribed to
require.Equal(t, 0, p0.SubscribeToTrackCallCount())
require.Equal(t, 1, p1.SubscribeToTrackCallCount())
@@ -441,7 +451,7 @@ func TestActiveSpeakers(t *testing.T) {
getActiveSpeakerUpdates := func(p *typesfakes.FakeLocalParticipant) [][]*livekit.SpeakerInfo {
var updates [][]*livekit.SpeakerInfo
numCalls := p.SendSpeakerUpdateCallCount()
for i := 0; i < numCalls; i++ {
for i := range numCalls {
infos, _ := p.SendSpeakerUpdateArgsForCall(i)
updates = append(updates, infos)
}
@@ -518,6 +528,7 @@ func TestActiveSpeakers(t *testing.T) {
t.Run("audio level is smoothed", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 2, protocol: 3, audioSmoothIntervals: 3})
defer rm.Close(types.ParticipantCloseReasonNone)
participants := rm.GetParticipants()
p := participants[0].(*typesfakes.FakeLocalParticipant)
op := participants[1].(*typesfakes.FakeLocalParticipant)
@@ -611,6 +622,9 @@ func TestDataChannel(t *testing.T) {
t.Run(modeNames[mode], func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 3})
defer rm.Close(types.ParticipantCloseReasonNone)
lpl := rm.LocalParticipantListener()
participants := rm.GetParticipants()
p := participants[0].(*typesfakes.FakeLocalParticipant)
@@ -631,17 +645,17 @@ func TestDataChannel(t *testing.T) {
}
encoded, _ := proto.Marshal(packetExp)
p.OnDataPacketArgsForCall(0)(p, packet.Kind, packet)
lpl.OnDataMessage(p, packet.Kind, packet)
// ensure everyone has received the packet
for _, op := range participants {
fp := op.(*typesfakes.FakeLocalParticipant)
if fp == p {
require.Zero(t, fp.SendDataPacketCallCount())
require.Zero(t, fp.SendDataMessageCallCount())
continue
}
require.Equal(t, 1, fp.SendDataPacketCallCount())
_, got := fp.SendDataPacketArgsForCall(0)
require.Equal(t, 1, fp.SendDataMessageCallCount())
_, got, _, _ := fp.SendDataMessageArgsForCall(0)
require.Equal(t, encoded, got)
}
})
@@ -654,6 +668,9 @@ func TestDataChannel(t *testing.T) {
t.Run(modeNames[mode], func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 4})
defer rm.Close(types.ParticipantCloseReasonNone)
lpl := rm.LocalParticipantListener()
participants := rm.GetParticipants()
p := participants[0].(*typesfakes.FakeLocalParticipant)
p1 := participants[1].(*typesfakes.FakeLocalParticipant)
@@ -678,17 +695,17 @@ func TestDataChannel(t *testing.T) {
}
encoded, _ := proto.Marshal(packetExp)
p.OnDataPacketArgsForCall(0)(p, packet.Kind, packet)
lpl.OnDataMessage(p, packet.Kind, packet)
// only p1 should receive the data
for _, op := range participants {
fp := op.(*typesfakes.FakeLocalParticipant)
if fp != p1 {
require.Zero(t, fp.SendDataPacketCallCount())
require.Zero(t, fp.SendDataMessageCallCount())
}
}
require.Equal(t, 1, p1.SendDataPacketCallCount())
_, got := p1.SendDataPacketArgsForCall(0)
require.Equal(t, 1, p1.SendDataMessageCallCount())
_, got, _, _ := p1.SendDataMessageArgsForCall(0)
require.Equal(t, encoded, got)
})
}
@@ -697,6 +714,7 @@ func TestDataChannel(t *testing.T) {
t.Run("publishing disallowed", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 2})
defer rm.Close(types.ParticipantCloseReasonNone)
participants := rm.GetParticipants()
p := participants[0].(*typesfakes.FakeLocalParticipant)
p.CanPublishDataReturns(false)
@@ -710,13 +728,14 @@ func TestDataChannel(t *testing.T) {
},
}
if p.CanPublishData() {
p.OnDataPacketArgsForCall(0)(p, packet.Kind, &packet)
lpl := rm.LocalParticipantListener()
lpl.OnDataMessage(p, packet.Kind, &packet)
}
// no one should've been sent packet
for _, op := range participants {
fp := op.(*typesfakes.FakeLocalParticipant)
require.Zero(t, fp.SendDataPacketCallCount())
require.Zero(t, fp.SendDataMessageCallCount())
}
})
}
@@ -726,7 +745,7 @@ func TestHiddenParticipants(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 2, numHidden: 1})
defer rm.Close(types.ParticipantCloseReasonNone)
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false)
pNew := NewMockParticipant("new", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
rm.Join(pNew, nil, nil, iceServersForRoom)
// expect new participant to get a JoinReply
@@ -740,14 +759,14 @@ func TestHiddenParticipants(t *testing.T) {
t.Run("hidden participant subscribes to tracks", func(t *testing.T) {
rm := newRoomWithParticipants(t, testRoomOpts{num: 2})
hidden := NewMockParticipant("hidden", types.CurrentProtocol, true, false)
lpl := rm.LocalParticipantListener()
hidden := NewMockParticipant("hidden", types.CurrentProtocol, true, false, lpl)
err := rm.Join(hidden, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
require.NoError(t, err)
stateChangeCB := hidden.OnStateChangeArgsForCall(0)
require.NotNil(t, stateChangeCB)
stateChangeCB(hidden, livekit.ParticipantInfo_ACTIVE)
hidden.StateReturns(livekit.ParticipantInfo_ACTIVE)
lpl.OnStateChange(hidden)
require.Eventually(t, func() bool { return hidden.SubscribeToTrackCallCount() == 2 }, 5*time.Second, 10*time.Millisecond)
})
@@ -761,7 +780,7 @@ func TestRoomUpdate(t *testing.T) {
p1 := rm.GetParticipants()[0].(*typesfakes.FakeLocalParticipant)
require.Equal(t, 0, p1.SendRoomUpdateCallCount())
p2 := NewMockParticipant("p2", types.CurrentProtocol, false, false)
p2 := NewMockParticipant("p2", types.CurrentProtocol, false, false, rm.LocalParticipantListener())
require.NoError(t, rm.Join(p2, nil, nil, iceServersForRoom))
// p1 should have received an update
@@ -795,6 +814,12 @@ type testRoomOpts struct {
}
func newRoomWithParticipants(t *testing.T, opts testRoomOpts) *Room {
kp := &authfakes.FakeKeyProvider{}
kp.GetSecretReturns("testkey")
n, err := webhook.NewDefaultNotifier(webhook.DefaultWebHookConfig, kp)
require.NoError(t, err)
rm := NewRoom(
&livekit.Room{Name: "room"},
nil,
@@ -816,12 +841,12 @@ func newRoomWithParticipants(t *testing.T, opts testRoomOpts) *Room {
NodeId: "testnode",
Region: "testregion",
},
telemetry.NewTelemetryService(webhook.NewDefaultNotifier("", "", nil), &telemetryfakes.FakeAnalyticsService{}),
telemetry.NewTelemetryService(n, &telemetryfakes.FakeAnalyticsService{}),
nil, nil, nil,
)
for i := 0; i < opts.num+opts.numHidden; i++ {
identity := livekit.ParticipantIdentity(fmt.Sprintf("p%d", i))
participant := NewMockParticipant(identity, opts.protocol, i >= opts.num, true)
participant := NewMockParticipant(identity, opts.protocol, i >= opts.num, true, rm.LocalParticipantListener())
err := rm.Join(participant, nil, &ParticipantOptions{AutoSubscribe: true}, iceServersForRoom)
require.NoError(t, err)
participant.StateReturns(livekit.ParticipantInfo_ACTIVE)
+153 -15
View File
@@ -22,14 +22,18 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/utils"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
// RoomTrackManager holds tracks that are published to the room
type RoomTrackManager struct {
logger logger.Logger
lock sync.RWMutex
changedNotifier *utils.ChangeNotifierManager
removedNotifier *utils.ChangeNotifierManager
tracks map[livekit.TrackID]*TrackInfo
tracks map[livekit.TrackID][]*TrackInfo
dataTracks map[livekit.TrackID][]*DataTrackInfo
}
type TrackInfo struct {
@@ -38,54 +42,101 @@ type TrackInfo struct {
PublisherID livekit.ParticipantID
}
func NewRoomTrackManager() *RoomTrackManager {
type DataTrackInfo struct {
DataTrack types.DataTrack
PublisherIdentity livekit.ParticipantIdentity
PublisherID livekit.ParticipantID
}
func NewRoomTrackManager(logger logger.Logger) *RoomTrackManager {
return &RoomTrackManager{
tracks: make(map[livekit.TrackID]*TrackInfo),
logger: logger,
tracks: make(map[livekit.TrackID][]*TrackInfo),
dataTracks: make(map[livekit.TrackID][]*DataTrackInfo),
changedNotifier: utils.NewChangeNotifierManager(),
removedNotifier: utils.NewChangeNotifierManager(),
}
}
func (r *RoomTrackManager) AddTrack(track types.MediaTrack, publisherIdentity livekit.ParticipantIdentity, publisherID livekit.ParticipantID) {
trackID := track.ID()
r.lock.Lock()
r.tracks[track.ID()] = &TrackInfo{
infos, ok := r.tracks[trackID]
if ok {
for _, info := range infos {
if info.Track == track {
r.lock.Unlock()
r.logger.Infow("not adding duplicate track", "trackID", trackID)
return
}
}
}
r.tracks[trackID] = append(r.tracks[trackID], &TrackInfo{
Track: track,
PublisherIdentity: publisherIdentity,
PublisherID: publisherID,
}
})
r.lock.Unlock()
r.NotifyTrackChanged(track.ID())
r.NotifyTrackChanged(trackID)
}
func (r *RoomTrackManager) RemoveTrack(track types.MediaTrack) {
trackID := track.ID()
r.lock.Lock()
// ensure we are removing the same track as added
info, ok := r.tracks[track.ID()]
if !ok || info.Track != track {
infos, ok := r.tracks[trackID]
if !ok {
r.lock.Unlock()
return
}
delete(r.tracks, track.ID())
r.lock.Unlock()
n := r.removedNotifier.GetNotifier(string(track.ID()))
numRemoved := 0
idx := 0
for _, info := range infos {
if info.Track == track {
numRemoved++
} else {
r.tracks[trackID][idx] = info
idx++
}
}
for j := idx; j < len(infos); j++ {
r.tracks[trackID][j] = nil
}
r.tracks[trackID] = r.tracks[trackID][:idx]
if len(r.tracks[trackID]) == 0 {
delete(r.tracks, trackID)
}
r.lock.Unlock()
if numRemoved == 0 {
return
}
if numRemoved > 1 {
r.logger.Warnw("removed multiple tracks", nil, "trackID", trackID, "numRemoved", numRemoved)
}
n := r.removedNotifier.GetNotifier(string(trackID))
if n != nil {
n.NotifyChanged()
}
r.changedNotifier.RemoveNotifier(string(track.ID()), true)
r.removedNotifier.RemoveNotifier(string(track.ID()), true)
r.changedNotifier.RemoveNotifier(string(trackID), true)
r.removedNotifier.RemoveNotifier(string(trackID), true)
}
func (r *RoomTrackManager) GetTrackInfo(trackID livekit.TrackID) *TrackInfo {
r.lock.RLock()
defer r.lock.RUnlock()
info := r.tracks[trackID]
if info == nil {
infos := r.tracks[trackID]
if len(infos) == 0 {
return nil
}
// earliest added track is used till it is removed
info := infos[0]
// when track is about to close, do not resolve
if info.Track != nil && !info.Track.IsOpen() {
return nil
@@ -123,3 +174,90 @@ func (r *RoomTrackManager) GetOrCreateTrackChangeNotifier(trackID livekit.TrackI
func (r *RoomTrackManager) GetOrCreateTrackRemoveNotifier(trackID livekit.TrackID) *utils.ChangeNotifier {
return r.removedNotifier.GetOrCreateNotifier(string(trackID))
}
func (r *RoomTrackManager) AddDataTrack(dataTrack types.DataTrack, publisherIdentity livekit.ParticipantIdentity, publisherID livekit.ParticipantID) {
trackID := dataTrack.ID()
r.lock.Lock()
infos, ok := r.dataTracks[trackID]
if ok {
for _, info := range infos {
if info.DataTrack == dataTrack {
r.lock.Unlock()
r.logger.Infow("not adding duplicate data track", "trackID", trackID)
return
}
}
}
r.dataTracks[trackID] = append(r.dataTracks[trackID], &DataTrackInfo{
DataTrack: dataTrack,
PublisherIdentity: publisherIdentity,
PublisherID: publisherID,
})
r.lock.Unlock()
r.NotifyTrackChanged(trackID)
}
func (r *RoomTrackManager) RemoveDataTrack(dataTrack types.DataTrack) {
trackID := dataTrack.ID()
r.lock.Lock()
// ensure we are removing the same track as added
infos, ok := r.dataTracks[trackID]
if !ok {
r.lock.Unlock()
return
}
numRemoved := 0
idx := 0
for _, info := range infos {
if info.DataTrack == dataTrack {
numRemoved++
} else {
r.dataTracks[trackID][idx] = info
idx++
}
}
for j := idx; j < len(infos); j++ {
r.dataTracks[trackID][j] = nil
}
r.dataTracks[trackID] = r.dataTracks[trackID][:idx]
if len(r.dataTracks[trackID]) == 0 {
delete(r.dataTracks, trackID)
}
r.lock.Unlock()
if numRemoved == 0 {
return
}
if numRemoved > 1 {
r.logger.Warnw("removed multiple data tracks", nil, "trackID", trackID, "numRemoved", numRemoved)
}
n := r.removedNotifier.GetNotifier(string(trackID))
if n != nil {
n.NotifyChanged()
}
r.changedNotifier.RemoveNotifier(string(trackID), true)
r.removedNotifier.RemoveNotifier(string(trackID), true)
}
func (r *RoomTrackManager) GetDataTrackInfo(trackID livekit.TrackID) *DataTrackInfo {
r.lock.RLock()
defer r.lock.RUnlock()
infos := r.dataTracks[trackID]
if len(infos) == 0 {
return nil
}
// earliest added data track is used till it is removed
return infos[0]
}
func (r *RoomTrackManager) Report() (int, int) {
r.lock.RLock()
defer r.lock.RUnlock()
return len(r.tracks), len(r.dataTracks)
}
+307
View File
@@ -0,0 +1,307 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"context"
"fmt"
"sync"
"time"
"github.com/frostbyte73/core"
"go.uber.org/atomic"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/telemetry"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/observability/roomobs"
"github.com/livekit/protocol/utils"
)
type BytesTrackType string
const (
BytesTrackTypeData BytesTrackType = "DT"
BytesTrackTypeSignal BytesTrackType = "SG"
)
// -------------------------------
type TrafficTotals struct {
At time.Time
SendBytes uint64
SendMessages uint32
RecvBytes uint64
RecvMessages uint32
}
// --------------------------------
// stats for signal and data channel
type BytesTrackStats struct {
country string
pID livekit.ParticipantID
kind livekit.ParticipantInfo_Kind
kindDetails []livekit.ParticipantInfo_KindDetail
trackID livekit.TrackID
send, recv atomic.Uint64
sendMessages, recvMessages atomic.Uint32
totalSendBytes, totalRecvBytes atomic.Uint64
totalSendMessages, totalRecvMessages atomic.Uint32
telemetryListener types.ParticipantTelemetryListener
reporter roomobs.TrackReporter
done core.Fuse
}
func NewBytesTrackStats(
country string,
trackID livekit.TrackID,
pID livekit.ParticipantID,
kind livekit.ParticipantInfo_Kind,
kindDetails []livekit.ParticipantInfo_KindDetail,
telemetryListener types.ParticipantTelemetryListener,
participantReporter roomobs.ParticipantSessionReporter,
) *BytesTrackStats {
s := &BytesTrackStats{
country: country,
pID: pID,
kind: kind,
kindDetails: kindDetails,
trackID: trackID,
telemetryListener: telemetryListener,
reporter: participantReporter.WithTrack(trackID.String()),
}
go s.worker()
return s
}
func (s *BytesTrackStats) AddBytes(bytes uint64, isSend bool) {
if isSend {
s.send.Add(bytes)
s.sendMessages.Inc()
s.totalSendBytes.Add(bytes)
s.totalSendMessages.Inc()
s.reporter.Tx(func(tx roomobs.TrackTx) {
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(s.kind))
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(s.kindDetails))
tx.ReportType(roomobs.TrackTypeData)
tx.ReportSendBytes(uint32(bytes))
tx.ReportSendPackets(1)
})
} else {
s.recv.Add(bytes)
s.recvMessages.Inc()
s.totalRecvBytes.Add(bytes)
s.totalRecvMessages.Inc()
s.reporter.Tx(func(tx roomobs.TrackTx) {
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(s.kind))
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(s.kindDetails))
tx.ReportType(roomobs.TrackTypeData)
tx.ReportRecvBytes(uint32(bytes))
tx.ReportRecvPackets(1)
})
}
}
func (s *BytesTrackStats) GetTrafficTotals() *TrafficTotals {
return &TrafficTotals{
At: time.Now(),
SendBytes: s.totalSendBytes.Load(),
SendMessages: s.totalSendMessages.Load(),
RecvBytes: s.totalRecvBytes.Load(),
RecvMessages: s.totalRecvMessages.Load(),
}
}
func (s *BytesTrackStats) Stop() {
s.done.Break()
}
func (s *BytesTrackStats) report() {
if recv := s.recv.Swap(0); recv > 0 {
packets := s.recvMessages.Swap(0)
s.telemetryListener.OnTrackStats(
telemetry.StatsKeyForData(s.country, livekit.StreamType_UPSTREAM, s.pID, s.trackID),
&livekit.AnalyticsStat{
Streams: []*livekit.AnalyticsStream{
{
PrimaryBytes: recv,
PrimaryPackets: packets,
},
},
},
)
}
if send := s.send.Swap(0); send > 0 {
packets := s.sendMessages.Swap(0)
s.telemetryListener.OnTrackStats(
telemetry.StatsKeyForData(s.country, livekit.StreamType_DOWNSTREAM, s.pID, s.trackID),
&livekit.AnalyticsStat{
Streams: []*livekit.AnalyticsStream{
{
PrimaryBytes: send,
PrimaryPackets: packets,
},
},
},
)
}
}
func (s *BytesTrackStats) worker() {
ticker := time.NewTicker(5 * time.Second)
defer func() {
ticker.Stop()
s.report()
}()
for {
select {
case <-s.done.Watch():
return
case <-ticker.C:
s.report()
}
}
}
// -----------------------------------------------------------------------
var _ types.ParticipantTelemetryListener = (*BytesSignalStats)(nil)
type BytesSignalStats struct {
BytesTrackStats
ctx context.Context
telemetry telemetry.TelemetryService
guard *telemetry.ReferenceGuard
participantResolver roomobs.ParticipantReporterResolver
trackResolver roomobs.KeyResolver
mu sync.Mutex
ri *livekit.Room
pi *livekit.ParticipantInfo
stopped chan struct{}
types.NullParticipantTelemetryListener
}
func NewBytesSignalStats(
ctx context.Context,
telemetryService telemetry.TelemetryService,
) *BytesSignalStats {
projectReporter := telemetryService.RoomProjectReporter(ctx)
participantReporter, participantReporterResolver := roomobs.DeferredParticipantReporter(projectReporter)
trackReporter, trackReporterResolver := participantReporter.WithDeferredTrack()
b := &BytesSignalStats{
ctx: ctx,
telemetry: telemetryService,
guard: &telemetry.ReferenceGuard{},
participantResolver: participantReporterResolver,
trackResolver: trackReporterResolver,
}
b.BytesTrackStats = BytesTrackStats{
telemetryListener: b,
reporter: trackReporter,
}
return b
}
func (s *BytesSignalStats) ResolveRoom(ri *livekit.Room) {
s.mu.Lock()
defer s.mu.Unlock()
if s.ri == nil && ri.GetSid() != "" {
s.ri = &livekit.Room{
Sid: ri.Sid,
Name: ri.Name,
}
s.maybeStart()
}
}
func (s *BytesSignalStats) ResolveParticipant(pi *livekit.ParticipantInfo) {
s.mu.Lock()
defer s.mu.Unlock()
if s.pi == nil && pi != nil {
s.pi = &livekit.ParticipantInfo{
Sid: pi.Sid,
Identity: pi.Identity,
Kind: pi.Kind,
KindDetails: pi.KindDetails,
}
s.kind = pi.Kind
s.kindDetails = pi.KindDetails
s.maybeStart()
}
}
func (s *BytesSignalStats) Reset() {
s.mu.Lock()
defer s.mu.Unlock()
if s.stopped != nil {
s.done.Break()
<-s.stopped
s.stopped = nil
s.done = core.Fuse{}
s.guard = &telemetry.ReferenceGuard{}
}
s.ri = nil
s.pi = nil
s.participantResolver.Reset()
s.trackResolver.Reset()
}
func (s *BytesSignalStats) maybeStart() {
if s.ri == nil || s.pi == nil {
return
}
s.pID = livekit.ParticipantID(s.pi.Sid)
s.trackID = BytesTrackIDForParticipantID(BytesTrackTypeSignal, s.pID)
s.participantResolver.Resolve(
livekit.RoomName(s.ri.Name),
livekit.RoomID(s.ri.Sid),
livekit.ParticipantIdentity(s.pi.Identity),
livekit.ParticipantID(s.pi.Sid),
)
s.trackResolver.Resolve(string(s.trackID))
s.telemetry.ParticipantJoined(s.ctx, s.ri, s.pi, nil, nil, false, s.guard)
s.stopped = make(chan struct{})
go s.worker()
}
func (s *BytesSignalStats) worker() {
s.BytesTrackStats.worker()
s.telemetry.ParticipantLeft(s.ctx, s.ri, s.pi, false, s.guard)
close(s.stopped)
}
func (s *BytesSignalStats) OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat) {
stat.RoomId, stat.RoomName = s.ri.Sid, s.ri.Name
s.telemetry.TrackStats(livekit.RoomID(s.ri.Sid), livekit.RoomName(s.ri.Name), key, stat)
}
// -----------------------------------------------------------------------
func BytesTrackIDForParticipantID(typ BytesTrackType, participantID livekit.ParticipantID) livekit.TrackID {
return livekit.TrackID(fmt.Sprintf("%s%s%s", utils.TrackPrefix, typ, participantID))
}
-159
View File
@@ -1,159 +0,0 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
func HandleParticipantSignal(room types.Room, participant types.LocalParticipant, req *livekit.SignalRequest, pLogger logger.Logger) error {
participant.UpdateLastSeenSignal()
switch msg := req.GetMessage().(type) {
case *livekit.SignalRequest_Offer:
participant.HandleOffer(FromProtoSessionDescription(msg.Offer))
case *livekit.SignalRequest_Answer:
participant.HandleAnswer(FromProtoSessionDescription(msg.Answer))
case *livekit.SignalRequest_Trickle:
candidateInit, err := FromProtoTrickle(msg.Trickle)
if err != nil {
pLogger.Warnw("could not decode trickle", err)
return nil
}
participant.AddICECandidate(candidateInit, msg.Trickle.Target)
case *livekit.SignalRequest_AddTrack:
pLogger.Debugw("add track request", "trackID", msg.AddTrack.Cid)
participant.AddTrack(msg.AddTrack)
case *livekit.SignalRequest_Mute:
participant.SetTrackMuted(livekit.TrackID(msg.Mute.Sid), msg.Mute.Muted, false)
case *livekit.SignalRequest_Subscription:
// allow participant to indicate their interest in the subscription
// permission check happens later in SubscriptionManager
room.UpdateSubscriptions(
participant,
livekit.StringsAsIDs[livekit.TrackID](msg.Subscription.TrackSids),
msg.Subscription.ParticipantTracks,
msg.Subscription.Subscribe,
)
case *livekit.SignalRequest_TrackSetting:
for _, sid := range livekit.StringsAsIDs[livekit.TrackID](msg.TrackSetting.TrackSids) {
participant.UpdateSubscribedTrackSettings(sid, msg.TrackSetting)
}
case *livekit.SignalRequest_Leave:
reason := types.ParticipantCloseReasonClientRequestLeave
switch msg.Leave.Reason {
case livekit.DisconnectReason_CLIENT_INITIATED:
reason = types.ParticipantCloseReasonClientRequestLeave
case livekit.DisconnectReason_USER_UNAVAILABLE:
reason = types.ParticipantCloseReasonUserUnavailable
case livekit.DisconnectReason_USER_REJECTED:
reason = types.ParticipantCloseReasonUserRejected
}
pLogger.Debugw("client leaving room", "reason", reason)
room.RemoveParticipant(participant.Identity(), participant.ID(), reason)
case *livekit.SignalRequest_SubscriptionPermission:
err := room.UpdateSubscriptionPermission(participant, msg.SubscriptionPermission)
if err != nil {
pLogger.Warnw("could not update subscription permission", err,
"permissions", msg.SubscriptionPermission)
}
case *livekit.SignalRequest_SyncState:
err := room.SyncState(participant, msg.SyncState)
if err != nil {
pLogger.Warnw("could not sync state", err,
"state", msg.SyncState)
}
case *livekit.SignalRequest_Simulate:
err := room.SimulateScenario(participant, msg.Simulate)
if err != nil {
pLogger.Warnw("could not simulate scenario", err,
"simulate", msg.Simulate)
}
case *livekit.SignalRequest_PingReq:
if msg.PingReq.Rtt > 0 {
participant.UpdateSignalingRTT(uint32(msg.PingReq.Rtt))
}
case *livekit.SignalRequest_UpdateMetadata:
requestResponse := &livekit.RequestResponse{
RequestId: msg.UpdateMetadata.RequestId,
Reason: livekit.RequestResponse_OK,
}
if participant.ClaimGrants().Video.GetCanUpdateOwnMetadata() {
if err := participant.CheckMetadataLimits(
msg.UpdateMetadata.Name,
msg.UpdateMetadata.Metadata,
msg.UpdateMetadata.Attributes,
); err == nil {
if msg.UpdateMetadata.Name != "" {
participant.SetName(msg.UpdateMetadata.Name)
}
if msg.UpdateMetadata.Metadata != "" {
participant.SetMetadata(msg.UpdateMetadata.Metadata)
}
if msg.UpdateMetadata.Attributes != nil {
participant.SetAttributes(msg.UpdateMetadata.Attributes)
}
} else {
pLogger.Warnw("could not update metadata", err)
switch err {
case ErrNameExceedsLimits:
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
requestResponse.Message = "exceeds name length limit"
case ErrMetadataExceedsLimits:
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
requestResponse.Message = "exceeds metadata size limit"
case ErrAttributesExceedsLimits:
requestResponse.Reason = livekit.RequestResponse_LIMIT_EXCEEDED
requestResponse.Message = "exceeds attributes size limit"
}
}
} else {
requestResponse.Reason = livekit.RequestResponse_NOT_ALLOWED
requestResponse.Message = "does not have permission to update own metadata"
}
participant.SendRequestResponse(requestResponse)
case *livekit.SignalRequest_UpdateAudioTrack:
if err := participant.UpdateAudioTrack(msg.UpdateAudioTrack); err != nil {
pLogger.Warnw("could not update audio track", err, "update", msg.UpdateAudioTrack)
}
case *livekit.SignalRequest_UpdateVideoTrack:
if err := participant.UpdateVideoTrack(msg.UpdateVideoTrack); err != nil {
pLogger.Warnw("could not update video track", err, "update", msg.UpdateVideoTrack)
}
}
return nil
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"errors"
)
var (
ErrInvalidMessageType = errors.New("invalid message type")
ErrNameExceedsLimits = errors.New("name length exceeds limits")
ErrMetadataExceedsLimits = errors.New("metadata size exceeds limits")
ErrAttributesExceedsLimits = errors.New("attributes size exceeds limits")
ErrUpdateOwnMetadataNotAllowed = errors.New("update own metadata not allowed")
)
+65
View File
@@ -0,0 +1,65 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/types"
"google.golang.org/protobuf/proto"
)
type ParticipantSignalHandler interface {
HandleMessage(msg proto.Message) error
}
type ParticipantSignaller interface {
SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason)
GetResponseSink() routing.MessageSink
CloseSignalConnection(reason types.SignallingCloseReason)
WriteMessage(msg proto.Message) error
}
type ParticipantSignalling interface {
SignalJoinResponse(join *livekit.JoinResponse) proto.Message
SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message
SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message
SignalRoomUpdate(room *livekit.Room) proto.Message
SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message
SignalRefreshToken(token string) proto.Message
SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message
SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message
SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message
SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message
SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message
SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message
SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message
SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message
SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message
SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message
SignalSdpOffer(offer *livekit.SessionDescription) proto.Message
SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message
SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message
SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message
SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message
SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message
SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message
SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message
SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message
SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message
}
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"fmt"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"google.golang.org/protobuf/proto"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
var _ ParticipantSignalHandler = (*signalhandler)(nil)
type SignalHandlerParams struct {
Logger logger.Logger
Participant types.LocalParticipant
}
type signalhandler struct {
params SignalHandlerParams
}
func NewSignalHandler(params SignalHandlerParams) ParticipantSignalHandler {
return &signalhandler{
params: params,
}
}
func (s *signalhandler) HandleMessage(msg proto.Message) error {
req, ok := msg.(*livekit.SignalRequest)
if !ok {
s.params.Logger.Warnw(
"unknown message type", nil,
"messageType", fmt.Sprintf("%T", msg),
)
return ErrInvalidMessageType
}
s.params.Participant.UpdateLastSeenSignal()
s.params.Logger.Debugw("handling signal request", "request", logger.Proto(req))
switch msg := req.GetMessage().(type) {
case *livekit.SignalRequest_Offer:
s.params.Participant.HandleOffer(msg.Offer)
case *livekit.SignalRequest_Answer:
s.params.Participant.HandleAnswer(msg.Answer)
case *livekit.SignalRequest_Trickle:
s.params.Participant.HandleICETrickle(msg.Trickle)
case *livekit.SignalRequest_AddTrack:
s.params.Participant.AddTrack(msg.AddTrack)
case *livekit.SignalRequest_Mute:
s.params.Participant.SetTrackMuted(msg.Mute, false)
case *livekit.SignalRequest_Subscription:
// allow participant to indicate their interest in the subscription
// permission check happens later in SubscriptionManager
s.params.Participant.HandleUpdateSubscriptions(
livekit.StringsAsIDs[livekit.TrackID](msg.Subscription.TrackSids),
msg.Subscription.ParticipantTracks,
msg.Subscription.Subscribe,
)
case *livekit.SignalRequest_TrackSetting:
for _, sid := range livekit.StringsAsIDs[livekit.TrackID](msg.TrackSetting.TrackSids) {
s.params.Participant.UpdateSubscribedTrackSettings(sid, msg.TrackSetting)
}
case *livekit.SignalRequest_Leave:
reason := types.ParticipantCloseReasonClientRequestLeave
switch msg.Leave.Reason {
case livekit.DisconnectReason_CLIENT_INITIATED:
reason = types.ParticipantCloseReasonClientRequestLeave
case livekit.DisconnectReason_USER_UNAVAILABLE:
reason = types.ParticipantCloseReasonUserUnavailable
case livekit.DisconnectReason_USER_REJECTED:
reason = types.ParticipantCloseReasonUserRejected
case livekit.DisconnectReason_AGENT_ERROR:
reason = types.ParticipantCloseReasonAgentError
}
s.params.Logger.Debugw("client leaving room", "reason", reason)
s.params.Participant.HandleLeaveRequest(reason)
case *livekit.SignalRequest_SubscriptionPermission:
err := s.params.Participant.HandleUpdateSubscriptionPermission(msg.SubscriptionPermission)
if err != nil {
s.params.Logger.Warnw(
"could not update subscription permission", err,
"permissions", msg.SubscriptionPermission,
)
}
case *livekit.SignalRequest_SyncState:
err := s.params.Participant.HandleSyncState(msg.SyncState)
if err != nil {
s.params.Logger.Warnw(
"could not sync state", err,
"state", msg.SyncState,
)
}
case *livekit.SignalRequest_Simulate:
err := s.params.Participant.HandleSimulateScenario(msg.Simulate)
if err != nil {
s.params.Logger.Warnw(
"could not simulate scenario", err,
"simulate", msg.Simulate,
)
}
case *livekit.SignalRequest_PingReq:
if msg.PingReq.Rtt > 0 {
s.params.Participant.UpdateSignalingRTT(uint32(msg.PingReq.Rtt))
}
case *livekit.SignalRequest_UpdateMetadata:
s.params.Participant.UpdateMetadata(msg.UpdateMetadata, false)
case *livekit.SignalRequest_UpdateAudioTrack:
if err := s.params.Participant.UpdateAudioTrack(msg.UpdateAudioTrack); err != nil {
s.params.Logger.Warnw("could not update audio track", err, "update", msg.UpdateAudioTrack)
}
case *livekit.SignalRequest_UpdateVideoTrack:
if err := s.params.Participant.UpdateVideoTrack(msg.UpdateVideoTrack); err != nil {
s.params.Logger.Warnw("could not update video track", err, "update", msg.UpdateVideoTrack)
}
case *livekit.SignalRequest_PublishDataTrackRequest:
s.params.Participant.HandlePublishDataTrackRequest(msg.PublishDataTrackRequest)
case *livekit.SignalRequest_UnpublishDataTrackRequest:
s.params.Participant.HandleUnpublishDataTrackRequest(msg.UnpublishDataTrackRequest)
case *livekit.SignalRequest_UpdateDataSubscription:
s.params.Participant.HandleUpdateDataSubscription(msg.UpdateDataSubscription)
}
return nil
}
@@ -0,0 +1,27 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"google.golang.org/protobuf/proto"
)
var _ ParticipantSignalHandler = (*signalhandlerUnimplemented)(nil)
type signalhandlerUnimplemented struct{}
func (u *signalhandlerUnimplemented) HandleMessage(msg proto.Message) error {
return nil
}
+114
View File
@@ -0,0 +1,114 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"fmt"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
"github.com/livekit/psrpc"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/types"
"google.golang.org/protobuf/proto"
)
var _ ParticipantSignaller = (*signallerAsync)(nil)
type SignallerAsyncParams struct {
Logger logger.Logger
Participant types.LocalParticipant
}
type signallerAsync struct {
params SignallerAsyncParams
*signallerAsyncBase
}
func NewSignallerAsync(params SignallerAsyncParams) ParticipantSignaller {
return &signallerAsync{
params: params,
signallerAsyncBase: newSignallerAsyncBase(signallerAsyncBaseParams{Logger: params.Logger}),
}
}
func (s *signallerAsync) WriteMessage(msg proto.Message) error {
if msg == nil {
return nil
}
getMessageType := func(msg proto.Message) string {
messageType := "unknown"
if typed, ok := msg.(*livekit.SignalResponse); ok {
messageType = fmt.Sprintf("%T", typed.Message)
}
return messageType
}
if s.params.Participant.IsDisconnected() {
s.params.Logger.Debugw(
"could not send message to participant, participant disconnected",
"messageType", getMessageType(msg),
)
return nil
}
if !s.params.Participant.IsReady() {
if typed, ok := msg.(*livekit.SignalResponse); !ok {
s.params.Logger.Warnw(
"unknown message type", nil,
"messageType", fmt.Sprintf("%T", msg),
)
} else {
if typed.GetJoin() == nil {
return nil
}
}
}
sink := s.GetResponseSink()
if sink == nil {
s.params.Logger.Debugw(
"could not send message to participant, no sink",
"messageType", getMessageType(msg),
)
return nil
}
err := sink.WriteMessage(msg)
if err != nil {
if utils.ErrorIsOneOf(err, psrpc.Canceled, routing.ErrChannelClosed) {
s.params.Logger.Debugw(
"could not send message to participant",
"error", err,
"messageType", getMessageType(msg),
)
return nil
} else {
s.params.Logger.Warnw(
"could not send message to participant", err,
"messageType", getMessageType(msg),
)
return err
}
} else {
s.params.Logger.Debugw("sent signal response", "response", logger.Proto(msg))
}
return nil
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"sync"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
type signallerAsyncBaseParams struct {
Logger logger.Logger
}
type signallerAsyncBase struct {
signallerUnimplemented
params signallerAsyncBaseParams
resSinkMu sync.Mutex
resSink routing.MessageSink
}
func newSignallerAsyncBase(params signallerAsyncBaseParams) *signallerAsyncBase {
return &signallerAsyncBase{
params: params,
}
}
func (s *signallerAsyncBase) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
s.resSinkMu.Lock()
oldSink := s.resSink
s.resSink = sink
s.resSinkMu.Unlock()
if oldSink != nil {
if sink != nil {
s.params.Logger.Debugw(
"swapping signal connection",
"reason", reason,
"connID", oldSink.ConnectionID(),
"newConnID", sink.ConnectionID(),
)
} else {
s.params.Logger.Debugw(
"closing signal connection",
"reason", reason,
"connID", oldSink.ConnectionID(),
)
}
oldSink.Close()
}
}
func (s *signallerAsyncBase) GetResponseSink() routing.MessageSink {
s.resSinkMu.Lock()
defer s.resSinkMu.Unlock()
return s.resSink
}
// closes signal connection to notify client to resume/reconnect
func (s *signallerAsyncBase) CloseSignalConnection(reason types.SignallingCloseReason) {
s.SwapResponseSink(nil, reason)
}
@@ -0,0 +1,39 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/types"
"google.golang.org/protobuf/proto"
)
var _ ParticipantSignaller = (*signallerUnimplemented)(nil)
type signallerUnimplemented struct{}
func (u *signallerUnimplemented) SwapResponseSink(sink routing.MessageSink, reason types.SignallingCloseReason) {
}
func (u *signallerUnimplemented) GetResponseSink() routing.MessageSink {
return nil
}
func (u *signallerUnimplemented) CloseSignalConnection(reason types.SignallingCloseReason) {}
func (u *signallerUnimplemented) WriteMessage(msg proto.Message) error {
return nil
}
+260
View File
@@ -0,0 +1,260 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"google.golang.org/protobuf/proto"
)
var _ ParticipantSignalling = (*signalling)(nil)
type SignallingParams struct {
Logger logger.Logger
}
type signalling struct {
params SignallingParams
}
func NewSignalling(params SignallingParams) ParticipantSignalling {
return &signalling{
params: params,
}
}
func (s *signalling) SignalJoinResponse(join *livekit.JoinResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Join{
Join: join,
},
}
}
func (s *signalling) SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message {
if len(participants) == 0 {
return nil
}
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Update{
Update: &livekit.ParticipantUpdate{
Participants: participants,
},
},
}
}
func (s *signalling) SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message {
if len(speakers) == 0 {
return nil
}
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_SpeakersChanged{
SpeakersChanged: &livekit.SpeakersChanged{
Speakers: speakers,
},
},
}
}
func (s *signalling) SignalRoomUpdate(room *livekit.Room) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_RoomUpdate{
RoomUpdate: &livekit.RoomUpdate{
Room: room,
},
},
}
}
func (s *signalling) SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_ConnectionQuality{
ConnectionQuality: connectionQuality,
},
}
}
func (s *signalling) SignalRefreshToken(token string) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_RefreshToken{
RefreshToken: token,
},
}
}
func (s *signalling) SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_RequestResponse{
RequestResponse: requestResponse,
},
}
}
func (s *signalling) SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_RoomMoved{
RoomMoved: roomMoved,
},
}
}
func (s *signalling) SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Reconnect{
Reconnect: reconnect,
},
}
}
func (s *signalling) SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Trickle{
Trickle: trickle,
},
}
}
func (s *signalling) SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Mute{
Mute: mute,
},
}
}
func (s *signalling) SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_TrackPublished{
TrackPublished: trackPublished,
},
}
}
func (s *signalling) SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_TrackUnpublished{
TrackUnpublished: trackUnpublished,
},
}
}
func (s *signalling) SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_TrackSubscribed{
TrackSubscribed: trackSubscribed,
},
}
}
func (s *signalling) SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Leave{
Leave: leave,
},
}
}
func (s *signalling) SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Answer{
Answer: answer,
},
}
}
func (s *signalling) SignalSdpOffer(offer *livekit.SessionDescription) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_Offer{
Offer: offer,
},
}
}
func (s *signalling) SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_StreamStateUpdate{
StreamStateUpdate: streamStateUpdate,
},
}
}
func (s *signalling) SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_SubscribedQualityUpdate{
SubscribedQualityUpdate: subscribedQualityUpdate,
},
}
}
func (s *signalling) SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_SubscriptionResponse{
SubscriptionResponse: subscriptionResponse,
},
}
}
func (s *signalling) SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_SubscriptionPermissionUpdate{
SubscriptionPermissionUpdate: subscriptionPermissionUpdate,
},
}
}
func (s *signalling) SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_MediaSectionsRequirement{
MediaSectionsRequirement: mediaSectionsRequirement,
},
}
}
func (s *signalling) SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_SubscribedAudioCodecUpdate{
SubscribedAudioCodecUpdate: subscribedAudioCodecUpdate,
},
}
}
func (s *signalling) SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_PublishDataTrackResponse{
PublishDataTrackResponse: publishDataTrackResponse,
},
}
}
func (s *signalling) SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_UnpublishDataTrackResponse{
UnpublishDataTrackResponse: unpublishDataTrackResponse,
},
}
}
func (s *signalling) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
return &livekit.SignalResponse{
Message: &livekit.SignalResponse_DataTrackSubscriberHandles{
DataTrackSubscriberHandles: dataTrackSubscriberHandles,
},
}
}
@@ -0,0 +1,129 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signalling
import (
"github.com/livekit/protocol/livekit"
"google.golang.org/protobuf/proto"
)
var _ ParticipantSignalling = (*signallingUnimplemented)(nil)
type signallingUnimplemented struct{}
func (u *signallingUnimplemented) SignalJoinResponse(join *livekit.JoinResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalParticipantUpdate(participants []*livekit.ParticipantInfo) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSpeakerUpdate(speakers []*livekit.SpeakerInfo) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalRoomUpdate(room *livekit.Room) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalConnectionQualityUpdate(connectionQuality *livekit.ConnectionQualityUpdate) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalRefreshToken(token string) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalRequestResponse(requestResponse *livekit.RequestResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalRoomMovedResponse(roomMoved *livekit.RoomMovedResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalReconnectResponse(reconnect *livekit.ReconnectResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalICECandidate(trickle *livekit.TrickleRequest) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalTrackMuted(mute *livekit.MuteTrackRequest) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalTrackPublished(trackPublished *livekit.TrackPublishedResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalTrackUnpublished(trackUnpublished *livekit.TrackUnpublishedResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalTrackSubscribed(trackSubscribed *livekit.TrackSubscribed) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalLeaveRequest(leave *livekit.LeaveRequest) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSdpAnswer(answer *livekit.SessionDescription) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSdpOffer(offer *livekit.SessionDescription) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalStreamStateUpdate(streamStateUpdate *livekit.StreamStateUpdate) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSubscribedQualityUpdate(subscribedQualityUpdate *livekit.SubscribedQualityUpdate) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSubscriptionResponse(subscriptionResponse *livekit.SubscriptionResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSubscriptionPermissionUpdate(subscriptionPermissionUpdate *livekit.SubscriptionPermissionUpdate) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalMediaSectionsRequirement(mediaSectionsRequirement *livekit.MediaSectionsRequirement) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalSubscribedAudioCodecUpdate(subscribedAudioCodecUpdate *livekit.SubscribedAudioCodecUpdate) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalPublishDataTrackResponse(publishDataTrackResponse *livekit.PublishDataTrackResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalUnpublishDataTrackResponse(unpublishDataTrackResponse *livekit.UnpublishDataTrackResponse) proto.Message {
return nil
}
func (u *signallingUnimplemented) SignalDataTrackSubscriberHandles(dataTrackSubscriberHandles *livekit.DataTrackSubscriberHandles) proto.Message {
return nil
}
+210 -24
View File
@@ -23,9 +23,12 @@ import (
"go.uber.org/atomic"
"google.golang.org/protobuf/proto"
"github.com/livekit/livekit-server/pkg/telemetry"
sutils "github.com/livekit/livekit-server/pkg/utils"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
"github.com/livekit/protocol/utils"
"github.com/livekit/livekit-server/pkg/rtc/types"
@@ -37,19 +40,27 @@ const (
subscriptionDebounceInterval = 100 * time.Millisecond
)
var _ types.SubscribedTrack = (*SubscribedTrack)(nil)
type SubscribedTrackParams struct {
PublisherID livekit.ParticipantID
PublisherIdentity livekit.ParticipantIdentity
PublisherVersion uint32
Subscriber types.LocalParticipant
MediaTrack types.MediaTrack
DownTrack *sfu.DownTrack
AdaptiveStream bool
ReceiverConfig ReceiverConfig
SubscriberConfig DirectionConfig
Subscriber types.LocalParticipant
MediaTrack types.MediaTrack
AdaptiveStream bool
TelemetryListener types.ParticipantTelemetryListener
WrappedReceiver *WrappedReceiver
IsRelayed bool
OnDownTrackCreated func(downTrack *sfu.DownTrack)
OnDownTrackClosed func(subscriberID livekit.ParticipantID)
OnSubscriberMaxQualityChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, layer int32)
OnSubscriberAudioCodecChange func(subscriberID livekit.ParticipantID, mime mime.MimeType, enabled bool)
}
type SubscribedTrack struct {
params SubscribedTrackParams
logger logger.Logger
downTrack *sfu.DownTrack
sender atomic.Pointer[webrtc.RTPSender]
needsNegotiation atomic.Bool
@@ -65,21 +76,98 @@ type SubscribedTrack struct {
onClose atomic.Value // func(bool)
debouncer func(func())
statsKey telemetry.StatsKey
reporter roomobs.TrackReporter
}
func NewSubscribedTrack(params SubscribedTrackParams) *SubscribedTrack {
func NewSubscribedTrack(params SubscribedTrackParams) (*SubscribedTrack, error) {
s := &SubscribedTrack{
params: params,
logger: params.Subscriber.GetLogger().WithComponent(sutils.ComponentSub).WithValues(
"trackID", params.DownTrack.ID(),
"publisherID", params.PublisherID,
"publisher", params.PublisherIdentity,
"trackID", params.MediaTrack.ID(),
"publisherID", params.MediaTrack.PublisherID(),
"publisher", params.MediaTrack.PublisherIdentity(),
),
versionGenerator: utils.NewDefaultTimedVersionGenerator(),
debouncer: debounce.New(subscriptionDebounceInterval),
statsKey: telemetry.StatsKeyForTrack(
params.Subscriber.GetCountry(),
livekit.StreamType_DOWNSTREAM,
params.Subscriber.ID(),
params.MediaTrack.ID(),
params.MediaTrack.Source(),
params.MediaTrack.Kind(),
),
reporter: params.Subscriber.GetReporter().WithTrack(params.MediaTrack.ID().String()),
}
return s
var rtcpFeedback []webrtc.RTCPFeedback
var maxTrack int
switch params.MediaTrack.Kind() {
case livekit.TrackType_AUDIO:
rtcpFeedback = params.SubscriberConfig.RTCPFeedback.Audio
maxTrack = params.ReceiverConfig.PacketBufferSizeAudio
case livekit.TrackType_VIDEO:
rtcpFeedback = params.SubscriberConfig.RTCPFeedback.Video
maxTrack = params.ReceiverConfig.PacketBufferSizeVideo
default:
s.logger.Warnw("unexpected track type", nil, "kind", params.MediaTrack.Kind())
}
codecs := params.WrappedReceiver.Codecs()
for _, c := range codecs {
c.RTCPFeedback = rtcpFeedback
}
streamID := params.WrappedReceiver.StreamID()
if params.Subscriber.SupportsSyncStreamID() && params.MediaTrack.Stream() != "" {
streamID = PackSyncStreamID(params.MediaTrack.PublisherID(), params.MediaTrack.Stream())
}
isEncrypted := params.MediaTrack.IsEncrypted()
var trailer []byte
if isEncrypted {
trailer = params.Subscriber.GetTrailer()
}
subClientInfo := ClientInfo{ClientInfo: params.Subscriber.GetClientInfo()}
subSupportsPacketTrailer := subClientInfo.SupportsPacketTrailer()
// Strip packet trailer if track has packet trailer but subscriber does not have cap
stripPacketTrailer := params.MediaTrack.HasPacketTrailer() && !subSupportsPacketTrailer
downTrack, err := sfu.NewDownTrack(sfu.DownTrackParams{
Codecs: codecs,
IsEncrypted: isEncrypted,
Source: params.MediaTrack.Source(),
Receiver: params.WrappedReceiver,
BufferFactory: params.Subscriber.GetBufferFactory(),
SubID: params.Subscriber.ID(),
StreamID: streamID,
MaxTrack: maxTrack,
PlayoutDelayLimit: params.Subscriber.GetPlayoutDelayConfig(),
Pacer: params.Subscriber.GetPacer(),
Trailer: trailer,
StripPacketTrailer: stripPacketTrailer,
Logger: LoggerWithTrack(
params.Subscriber.GetLogger().WithComponent(sutils.ComponentSub),
params.MediaTrack.ID(),
params.IsRelayed,
),
RTCPWriter: params.Subscriber.WriteSubscriberRTCP,
DisableSenderReportPassThrough: params.Subscriber.GetDisableSenderReportPassThrough(),
SupportsCodecChange: params.Subscriber.SupportsCodecChange(),
Listener: s,
})
if err != nil {
return nil, err
}
if params.OnDownTrackCreated != nil {
params.OnDownTrackCreated(downTrack)
}
downTrack.AddReceiverReportListener(params.Subscriber.HandleReceiverReport)
s.downTrack = downTrack
return s, nil
}
func (t *SubscribedTrack) AddOnBind(f func(error)) {
@@ -121,6 +209,7 @@ func (t *SubscribedTrack) Bound(err error) {
if t.params.AdaptiveStream {
// remove `disabled` flag to force a visibility update
t.settings.Disabled = false
t.logger.Debugw("enabling subscriber track settings on bind", "settings", logger.Proto(t.settings))
}
} else {
if t.params.AdaptiveStream {
@@ -128,6 +217,7 @@ func (t *SubscribedTrack) Bound(err error) {
} else {
t.settings = &livekit.UpdateTrackSettings{Quality: livekit.VideoQuality_HIGH}
}
t.logger.Debugw("initializing subscriber track settings on bind", "settings", logger.Proto(t.settings))
}
t.settingsLock.Unlock()
t.applySettings()
@@ -141,7 +231,7 @@ func (t *SubscribedTrack) Bound(err error) {
// for DownTrack callback to notify us that it's closed
func (t *SubscribedTrack) Close(isExpectedToResume bool) {
if onClose := t.onClose.Load(); onClose != nil {
go onClose.(func(bool))(isExpectedToResume)
onClose.(func(bool))(isExpectedToResume)
}
}
@@ -157,19 +247,19 @@ func (t *SubscribedTrack) IsBound() bool {
}
func (t *SubscribedTrack) ID() livekit.TrackID {
return livekit.TrackID(t.params.DownTrack.ID())
return livekit.TrackID(t.downTrack.ID())
}
func (t *SubscribedTrack) PublisherID() livekit.ParticipantID {
return t.params.PublisherID
return t.params.MediaTrack.PublisherID()
}
func (t *SubscribedTrack) PublisherIdentity() livekit.ParticipantIdentity {
return t.params.PublisherIdentity
return t.params.MediaTrack.PublisherIdentity()
}
func (t *SubscribedTrack) PublisherVersion() uint32 {
return t.params.PublisherVersion
return t.params.MediaTrack.PublisherVersion()
}
func (t *SubscribedTrack) SubscriberID() livekit.ParticipantID {
@@ -185,7 +275,7 @@ func (t *SubscribedTrack) Subscriber() types.LocalParticipant {
}
func (t *SubscribedTrack) DownTrack() *sfu.DownTrack {
return t.params.DownTrack
return t.downTrack
}
func (t *SubscribedTrack) MediaTrack() types.MediaTrack {
@@ -209,18 +299,20 @@ func (t *SubscribedTrack) isMutedLocked() bool {
}
func (t *SubscribedTrack) SetPublisherMuted(muted bool) {
t.DownTrack().PubMute(muted)
t.downTrack.PubMute(muted)
}
func (t *SubscribedTrack) UpdateSubscriberSettings(settings *livekit.UpdateTrackSettings, isImmediate bool) {
t.settingsLock.Lock()
if proto.Equal(t.settings, settings) {
t.logger.Debugw("skipping subscriber track settings", "settings", logger.Proto(t.settings))
t.settingsLock.Unlock()
return
}
isImmediate = isImmediate || (!settings.Disabled && settings.Disabled != t.isMutedLocked())
t.settings = utils.CloneProto(settings)
t.logger.Debugw("saving subscriber track settings", "settings", logger.Proto(t.settings))
t.settingsLock.Unlock()
if isImmediate {
@@ -242,7 +334,6 @@ func (t *SubscribedTrack) applySettings() {
return
}
t.logger.Debugw("updating subscriber track settings", "settings", logger.Proto(t.settings))
t.settingsVersion = t.versionGenerator.Next()
settingsVersion := t.settingsVersion
t.settingsLock.Unlock()
@@ -253,23 +344,25 @@ func (t *SubscribedTrack) applySettings() {
if dt.Kind() == webrtc.RTPCodecTypeVideo {
mt := t.MediaTrack()
quality := t.settings.Quality
mimeType := dt.Mime()
if t.settings.Width > 0 {
quality = mt.GetQualityForDimension(t.settings.Width, t.settings.Height)
quality = mt.GetQualityForDimension(mimeType, t.settings.Width, t.settings.Height)
}
spatial = buffer.VideoQualityToSpatialLayer(quality, mt.ToProto())
spatial = buffer.GetSpatialLayerForVideoQuality(mimeType, quality, mt.ToProto())
if t.settings.Fps > 0 {
temporal = mt.GetTemporalLayerForSpatialFps(spatial, t.settings.Fps, dt.Mime())
temporal = mt.GetTemporalLayerForSpatialFps(mimeType, spatial, t.settings.Fps)
}
}
t.settingsLock.Lock()
if settingsVersion != t.settingsVersion {
// a newer settings has superceded this one
// a newer settings has superseded this one
t.settingsLock.Unlock()
return
}
t.logger.Debugw("applying subscriber track settings", "settings", logger.Proto(t.settings))
if t.settings.Disabled {
dt.Mute(true)
t.settingsLock.Unlock()
@@ -302,3 +395,96 @@ func (t *SubscribedTrack) RTPSender() *webrtc.RTPSender {
func (t *SubscribedTrack) SetRTPSender(sender *webrtc.RTPSender) {
t.sender.Store(sender)
}
// DownTrackListener implementation
var _ sfu.DownTrackListener = (*SubscribedTrack)(nil)
func (t *SubscribedTrack) OnBindAndConnected() {
if t.params.Subscriber.Hidden() {
return
}
t.params.MediaTrack.OnTrackSubscribed()
}
func (t *SubscribedTrack) OnStatsUpdate(stat *livekit.AnalyticsStat) {
t.params.TelemetryListener.OnTrackStats(t.statsKey, stat)
if cs, ok := telemetry.CondenseStat(stat); ok {
ti := t.params.WrappedReceiver.TrackInfo()
t.reporter.Tx(func(tx roomobs.TrackTx) {
tx.ParticipantSession().ReportKindCode(roomobs.ParticipantKindCode(t.params.Subscriber.Kind()))
tx.ParticipantSession().ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(t.params.Subscriber.KindDetails()))
tx.ReportName(ti.Name)
tx.ReportKind(roomobs.TrackKindSub)
tx.ReportType(roomobs.TrackTypeFromProto(ti.Type))
tx.ReportSource(roomobs.TrackSourceFromProto(ti.Source))
tx.ReportMime(mime.NormalizeMimeType(ti.MimeType).ReporterType())
tx.ReportLayer(roomobs.PackTrackLayer(ti.Height, ti.Width))
tx.ReportDuration(uint16(cs.EndTime.Sub(cs.StartTime).Milliseconds()))
tx.ReportFrames(uint16(cs.Frames))
tx.ReportSendBytes(uint32(cs.Bytes))
tx.ReportSendPackets(cs.Packets)
tx.ReportPacketsLost(cs.PacketsLost)
tx.ReportScore(stat.Score)
})
}
}
func (t *SubscribedTrack) OnMaxSubscribedLayerChanged(layer int32) {
if t.params.OnSubscriberMaxQualityChange != nil {
t.params.OnSubscriberMaxQualityChange(t.downTrack.SubscriberID(), t.downTrack.Mime(), layer)
}
}
func (t *SubscribedTrack) OnRttUpdate(rtt uint32) {
go t.params.Subscriber.UpdateMediaRTT(rtt)
}
func (t *SubscribedTrack) OnCodecNegotiated(codec webrtc.RTPCodecCapability) {
if isAvailable, needsPublish := t.params.WrappedReceiver.DetermineReceiver(codec); !isAvailable || !needsPublish {
return
}
if t.params.OnSubscriberMaxQualityChange != nil || t.params.OnSubscriberAudioCodecChange != nil {
go func() {
mimeType := mime.NormalizeMimeType(codec.MimeType)
switch t.params.MediaTrack.Kind() {
case livekit.TrackType_VIDEO:
spatial := buffer.GetSpatialLayerForVideoQuality(
mimeType,
livekit.VideoQuality_HIGH,
t.params.MediaTrack.ToProto(),
)
if t.params.OnSubscriberMaxQualityChange != nil {
t.params.OnSubscriberMaxQualityChange(t.downTrack.SubscriberID(), mimeType, spatial)
}
case livekit.TrackType_AUDIO:
if t.params.OnSubscriberAudioCodecChange != nil {
t.params.OnSubscriberAudioCodecChange(t.downTrack.SubscriberID(), mimeType, true)
}
}
}()
}
}
func (t *SubscribedTrack) OnDownTrackClose(isExpectedToResume bool) {
// Cache transceiver for potential re-use on resume.
// To ensure subscription manager does not re-subscribe before caching,
// delete the subscribed track only after caching.
if isExpectedToResume {
if tr := t.downTrack.GetTransceiver(); tr != nil {
t.params.Subscriber.CacheDownTrack(t.ID(), tr, t.downTrack.GetState())
}
}
if t.params.OnDownTrackClosed != nil {
t.params.OnDownTrackClosed(t.params.Subscriber.ID())
}
t.Close(isExpectedToResume)
}
func (t *SubscribedTrack) OnStreamStarted() {
t.params.TelemetryListener.OnTrackSubscribeStreamStarted(t.params.Subscriber.ID(), t.params.MediaTrack.ToProto())
}
File diff suppressed because it is too large Load Diff
+38 -40
View File
@@ -26,7 +26,6 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
"github.com/livekit/livekit-server/pkg/telemetry/telemetryfakes"
"github.com/livekit/livekit-server/pkg/utils"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
@@ -67,7 +66,7 @@ func TestSubscribe(t *testing.T) {
}
})
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s := sm.subscriptions["track"]
require.True(t, s.isDesired())
require.Eventually(t, func() bool {
@@ -83,8 +82,8 @@ func TestSubscribe(t *testing.T) {
require.Equal(t, "pubID", string(sm.GetSubscribedParticipants()[0]))
// ensure telemetry events are sent
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
// ensure bound
setTestSubscribedTrackBound(t, s.getSubscribedTrack())
@@ -93,7 +92,7 @@ func TestSubscribe(t *testing.T) {
}, subSettleTimeout, subCheckInterval, "track was not bound")
// telemetry event should have been sent
require.Equal(t, 1, tm.TrackSubscribedCallCount())
require.Equal(t, 1, tl.OnTrackSubscribedCallCount())
time.Sleep(notFoundTimeout)
require.False(t, failed.Load())
@@ -127,7 +126,7 @@ func TestSubscribe(t *testing.T) {
failed.Store(true)
}
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s := sm.subscriptions["track"]
require.Eventually(t, func() bool {
return !s.getHasPermission()
@@ -142,9 +141,9 @@ func TestSubscribe(t *testing.T) {
require.Len(t, sm.GetSubscribedTracks(), 0)
// trackSubscribed telemetry not sent
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
require.Equal(t, 0, tm.TrackSubscribedCallCount())
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
require.Equal(t, 0, tl.OnTrackSubscribedCallCount())
// give permissions now
resolver.lock.Lock()
@@ -168,7 +167,7 @@ func TestSubscribe(t *testing.T) {
failed.Store(true)
}
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s := sm.subscriptions["track"]
require.Eventually(t, func() bool {
return !s.needsSubscribe()
@@ -197,15 +196,17 @@ func TestUnsubscribe(t *testing.T) {
resolver := newTestResolver(true, true, "pub", "pubID")
s := &trackSubscription{
trackID: "track",
desired: true,
subscriberID: sm.params.Participant.ID(),
publisherID: "pubID",
publisherIdentity: "pub",
hasPermission: true,
bound: true,
logger: logger.GetLogger(),
s := &mediaTrackSubscription{
trackSubscription: trackSubscription{
trackID: "track",
desired: true,
subscriberID: sm.params.Participant.ID(),
publisherID: "pubID",
publisherIdentity: "pub",
logger: logger.GetLogger(),
},
hasPermission: true,
bound: true,
}
// a bunch of unfortunate manual wiring
res := resolver.Resolve(nil, s.trackID)
@@ -242,18 +243,15 @@ func TestUnsubscribe(t *testing.T) {
sm.lock.RLock()
subLen := len(sm.subscriptions)
sm.lock.RUnlock()
if subLen != 0 {
return false
}
return true
return subLen == 0
}, subSettleTimeout, subCheckInterval, "Track was not unsubscribed")
// no traces should be left
require.Len(t, sm.GetSubscribedTracks(), 0)
require.False(t, res.TrackChangedNotifier.HasObservers())
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
require.Equal(t, 1, tm.TrackUnsubscribedCallCount())
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
require.Equal(t, 1, tl.OnTrackUnsubscribedCallCount())
}
func TestSubscribeStatusChanged(t *testing.T) {
@@ -271,8 +269,8 @@ func TestSubscribeStatusChanged(t *testing.T) {
}
})
sm.SubscribeToTrack("track1")
sm.SubscribeToTrack("track2")
sm.SubscribeToTrack("track1", false)
sm.SubscribeToTrack("track2", false)
s1 := sm.subscriptions["track1"]
s2 := sm.subscriptions["track2"]
require.Eventually(t, func() bool {
@@ -332,7 +330,7 @@ func TestUpdateSettingsBeforeSubscription(t *testing.T) {
}
sm.UpdateSubscribedTrackSettings("track", settings)
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s := sm.subscriptions["track"]
require.Eventually(t, func() bool {
@@ -376,7 +374,7 @@ func TestSubscriptionLimits(t *testing.T) {
}
})
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s := sm.subscriptions["track"]
require.True(t, s.isDesired())
require.Eventually(t, func() bool {
@@ -392,8 +390,8 @@ func TestSubscriptionLimits(t *testing.T) {
require.Equal(t, "pubID", string(sm.GetSubscribedParticipants()[0]))
// ensure telemetry events are sent
tm := sm.params.Telemetry.(*telemetryfakes.FakeTelemetryService)
require.Equal(t, 1, tm.TrackSubscribeRequestedCallCount())
tl := sm.params.TelemetryListener.(*typesfakes.FakeParticipantTelemetryListener)
require.Equal(t, 1, tl.OnTrackSubscribeRequestedCallCount())
// ensure bound
setTestSubscribedTrackBound(t, s.getSubscribedTrack())
@@ -402,15 +400,15 @@ func TestSubscriptionLimits(t *testing.T) {
}, subSettleTimeout, subCheckInterval, "track was not bound")
// telemetry event should have been sent
require.Equal(t, 1, tm.TrackSubscribedCallCount())
require.Equal(t, 1, tl.OnTrackSubscribedCallCount())
// reach subscription limit, subscribe pending
sm.SubscribeToTrack("track2")
sm.SubscribeToTrack("track2", false)
s2 := sm.subscriptions["track2"]
time.Sleep(subscriptionTimeout * 2)
require.True(t, s2.needsSubscribe())
require.Equal(t, 2, tm.TrackSubscribeRequestedCallCount())
require.Equal(t, 1, tm.TrackSubscribeFailedCallCount())
require.Equal(t, 2, tl.OnTrackSubscribeRequestedCallCount())
require.Equal(t, 1, tl.OnTrackSubscribeFailedCallCount())
require.Len(t, sm.GetSubscribedTracks(), 1)
// unsubscribe track1, then track2 should be subscribed
@@ -427,7 +425,7 @@ func TestSubscriptionLimits(t *testing.T) {
require.False(t, s2.needsSubscribe())
require.EqualValues(t, 2, subCount.Load())
require.NotNil(t, s2.getSubscribedTrack())
require.Equal(t, 2, tm.TrackSubscribeRequestedCallCount())
require.Equal(t, 2, tl.OnTrackSubscribeRequestedCallCount())
require.Len(t, sm.GetSubscribedTracks(), 1)
// ensure bound
@@ -437,13 +435,13 @@ func TestSubscriptionLimits(t *testing.T) {
}, subSettleTimeout, subCheckInterval, "track was not bound")
// subscribe to track1 again, which should pending
sm.SubscribeToTrack("track")
sm.SubscribeToTrack("track", false)
s = sm.subscriptions["track"]
require.True(t, s.isDesired())
time.Sleep(subscriptionTimeout * 2)
require.True(t, s.needsSubscribe())
require.Equal(t, 3, tm.TrackSubscribeRequestedCallCount())
require.Equal(t, 2, tm.TrackSubscribeFailedCallCount())
require.Equal(t, 3, tl.OnTrackSubscribeRequestedCallCount())
require.Equal(t, 2, tl.OnTrackSubscribeFailedCallCount())
require.Len(t, sm.GetSubscribedTracks(), 1)
}
@@ -471,7 +469,7 @@ func newTestSubscriptionManagerWithParams(params testSubscriptionParams) *Subscr
TrackResolver: func(sub types.LocalParticipant, trackID livekit.TrackID) types.MediaResolverResult {
return types.MediaResolverResult{}
},
Telemetry: &telemetryfakes.FakeTelemetryService{},
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
SubscriptionLimitAudio: params.SubscriptionLimitAudio,
SubscriptionLimitVideo: params.SubscriptionLimitVideo,
})
+12 -16
View File
@@ -17,6 +17,7 @@ package rtc
import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
"github.com/livekit/protocol/utils"
"github.com/livekit/protocol/utils/guid"
@@ -24,7 +25,13 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
)
func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.ProtocolVersion, hidden bool, publisher bool) *typesfakes.FakeLocalParticipant {
func NewMockParticipant(
identity livekit.ParticipantIdentity,
protocol types.ProtocolVersion,
hidden bool,
publisher bool,
participantListener types.LocalParticipantListener,
) *typesfakes.FakeLocalParticipant {
p := &typesfakes.FakeLocalParticipant{}
sid := guid.New(utils.ParticipantPrefix)
p.IDReturns(livekit.ParticipantID(sid))
@@ -49,25 +56,13 @@ func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro
}, utils.TimedVersion(0))
p.SetMetadataCalls(func(m string) {
var f func(participant types.LocalParticipant)
if p.OnParticipantUpdateCallCount() > 0 {
f = p.OnParticipantUpdateArgsForCall(p.OnParticipantUpdateCallCount() - 1)
}
if f != nil {
f(p)
}
participantListener.OnParticipantUpdate(p)
})
updateTrack := func() {
var f func(participant types.LocalParticipant, track types.MediaTrack)
if p.OnTrackUpdatedCallCount() > 0 {
f = p.OnTrackUpdatedArgsForCall(p.OnTrackUpdatedCallCount() - 1)
}
if f != nil {
f(p, NewMockTrack(livekit.TrackType_VIDEO, "testcam"))
}
participantListener.OnTrackUpdated(p, NewMockTrack(livekit.TrackType_VIDEO, "testcam"))
}
p.SetTrackMutedCalls(func(sid livekit.TrackID, muted bool, fromServer bool) *livekit.TrackInfo {
p.SetTrackMutedCalls(func(mute *livekit.MuteTrackRequest, fromServer bool) *livekit.TrackInfo {
updateTrack()
return nil
})
@@ -75,6 +70,7 @@ func NewMockParticipant(identity livekit.ParticipantIdentity, protocol types.Pro
updateTrack()
})
p.GetLoggerReturns(logger.GetLogger())
p.GetReporterReturns(roomobs.NewNoopParticipantSessionReporter())
return p
}
+1241 -219
View File
File diff suppressed because it is too large Load Diff
+16 -6
View File
@@ -39,13 +39,17 @@ type Handler interface {
OnFullyEstablished()
OnFailed(isShortLived bool, iceConnectionInfo *types.ICEConnectionInfo)
OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver)
OnDataPacket(kind livekit.DataPacket_Kind, data []byte)
OnDataMessage(kind livekit.DataPacket_Kind, data []byte)
OnDataMessageUnlabeled(data []byte)
OnDataTrackMessage(data []byte, arrivalTime int64)
OnDataSendError(err error)
OnOffer(sd webrtc.SessionDescription) error
OnAnswer(sd webrtc.SessionDescription) error
OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error
OnSetRemoteDescriptionOffer()
OnAnswer(sd webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error
OnNegotiationStateChanged(state NegotiationState)
OnNegotiationFailed()
OnStreamStateChange(update *streamallocator.StreamStateUpdate) error
OnUnmatchedMedia(numAudios uint32, numVideos uint32) error
}
type UnimplementedHandler struct{}
@@ -57,12 +61,15 @@ func (h UnimplementedHandler) OnInitialConnected()
func (h UnimplementedHandler) OnFullyEstablished() {}
func (h UnimplementedHandler) OnFailed(isShortLived bool) {}
func (h UnimplementedHandler) OnTrack(track *webrtc.TrackRemote, rtpReceiver *webrtc.RTPReceiver) {}
func (h UnimplementedHandler) OnDataPacket(kind livekit.DataPacket_Kind, data []byte) {}
func (h UnimplementedHandler) OnDataMessage(kind livekit.DataPacket_Kind, data []byte) {}
func (h UnimplementedHandler) OnDataMessageUnlabeled(data []byte) {}
func (h UnimplementedHandler) OnDataTrackMessage(data []byte, arrivalTime int64) {}
func (h UnimplementedHandler) OnDataSendError(err error) {}
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription) error {
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription, offerId uint32, midToTrackID map[string]string) error {
return ErrNoOfferHandler
}
func (h UnimplementedHandler) OnAnswer(sd webrtc.SessionDescription) error {
func (h UnimplementedHandler) OnSetRemoteDescriptionOffer() {}
func (h UnimplementedHandler) OnAnswer(sd webrtc.SessionDescription, answerId uint32, midToTrackID map[string]string) error {
return ErrNoAnswerHandler
}
func (h UnimplementedHandler) OnNegotiationStateChanged(state NegotiationState) {}
@@ -70,3 +77,6 @@ func (h UnimplementedHandler) OnNegotiationFailed()
func (h UnimplementedHandler) OnStreamStateChange(update *streamallocator.StreamStateUpdate) error {
return nil
}
func (h UnimplementedHandler) OnUnmatchedMedia(numAudios uint32, numVideos uint32) error {
return nil
}
+234 -62
View File
@@ -12,10 +12,12 @@ import (
)
type FakeHandler struct {
OnAnswerStub func(webrtc.SessionDescription) error
OnAnswerStub func(webrtc.SessionDescription, uint32, map[string]string) error
onAnswerMutex sync.RWMutex
onAnswerArgsForCall []struct {
arg1 webrtc.SessionDescription
arg2 uint32
arg3 map[string]string
}
onAnswerReturns struct {
result1 error
@@ -23,17 +25,28 @@ type FakeHandler struct {
onAnswerReturnsOnCall map[int]struct {
result1 error
}
OnDataPacketStub func(livekit.DataPacket_Kind, []byte)
onDataPacketMutex sync.RWMutex
onDataPacketArgsForCall []struct {
OnDataMessageStub func(livekit.DataPacket_Kind, []byte)
onDataMessageMutex sync.RWMutex
onDataMessageArgsForCall []struct {
arg1 livekit.DataPacket_Kind
arg2 []byte
}
OnDataMessageUnlabeledStub func([]byte)
onDataMessageUnlabeledMutex sync.RWMutex
onDataMessageUnlabeledArgsForCall []struct {
arg1 []byte
}
OnDataSendErrorStub func(error)
onDataSendErrorMutex sync.RWMutex
onDataSendErrorArgsForCall []struct {
arg1 error
}
OnDataTrackMessageStub func([]byte, int64)
onDataTrackMessageMutex sync.RWMutex
onDataTrackMessageArgsForCall []struct {
arg1 []byte
arg2 int64
}
OnFailedStub func(bool, *types.ICEConnectionInfo)
onFailedMutex sync.RWMutex
onFailedArgsForCall []struct {
@@ -69,10 +82,12 @@ type FakeHandler struct {
onNegotiationStateChangedArgsForCall []struct {
arg1 transport.NegotiationState
}
OnOfferStub func(webrtc.SessionDescription) error
OnOfferStub func(webrtc.SessionDescription, uint32, map[string]string) error
onOfferMutex sync.RWMutex
onOfferArgsForCall []struct {
arg1 webrtc.SessionDescription
arg2 uint32
arg3 map[string]string
}
onOfferReturns struct {
result1 error
@@ -80,6 +95,10 @@ type FakeHandler struct {
onOfferReturnsOnCall map[int]struct {
result1 error
}
OnSetRemoteDescriptionOfferStub func()
onSetRemoteDescriptionOfferMutex sync.RWMutex
onSetRemoteDescriptionOfferArgsForCall []struct {
}
OnStreamStateChangeStub func(*streamallocator.StreamStateUpdate) error
onStreamStateChangeMutex sync.RWMutex
onStreamStateChangeArgsForCall []struct {
@@ -97,22 +116,36 @@ type FakeHandler struct {
arg1 *webrtc.TrackRemote
arg2 *webrtc.RTPReceiver
}
OnUnmatchedMediaStub func(uint32, uint32) error
onUnmatchedMediaMutex sync.RWMutex
onUnmatchedMediaArgsForCall []struct {
arg1 uint32
arg2 uint32
}
onUnmatchedMediaReturns struct {
result1 error
}
onUnmatchedMediaReturnsOnCall map[int]struct {
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeHandler) OnAnswer(arg1 webrtc.SessionDescription) error {
func (fake *FakeHandler) OnAnswer(arg1 webrtc.SessionDescription, arg2 uint32, arg3 map[string]string) error {
fake.onAnswerMutex.Lock()
ret, specificReturn := fake.onAnswerReturnsOnCall[len(fake.onAnswerArgsForCall)]
fake.onAnswerArgsForCall = append(fake.onAnswerArgsForCall, struct {
arg1 webrtc.SessionDescription
}{arg1})
arg2 uint32
arg3 map[string]string
}{arg1, arg2, arg3})
stub := fake.OnAnswerStub
fakeReturns := fake.onAnswerReturns
fake.recordInvocation("OnAnswer", []interface{}{arg1})
fake.recordInvocation("OnAnswer", []interface{}{arg1, arg2, arg3})
fake.onAnswerMutex.Unlock()
if stub != nil {
return stub(arg1)
return stub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1
@@ -126,17 +159,17 @@ func (fake *FakeHandler) OnAnswerCallCount() int {
return len(fake.onAnswerArgsForCall)
}
func (fake *FakeHandler) OnAnswerCalls(stub func(webrtc.SessionDescription) error) {
func (fake *FakeHandler) OnAnswerCalls(stub func(webrtc.SessionDescription, uint32, map[string]string) error) {
fake.onAnswerMutex.Lock()
defer fake.onAnswerMutex.Unlock()
fake.OnAnswerStub = stub
}
func (fake *FakeHandler) OnAnswerArgsForCall(i int) webrtc.SessionDescription {
func (fake *FakeHandler) OnAnswerArgsForCall(i int) (webrtc.SessionDescription, uint32, map[string]string) {
fake.onAnswerMutex.RLock()
defer fake.onAnswerMutex.RUnlock()
argsForCall := fake.onAnswerArgsForCall[i]
return argsForCall.arg1
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeHandler) OnAnswerReturns(result1 error) {
@@ -162,44 +195,81 @@ func (fake *FakeHandler) OnAnswerReturnsOnCall(i int, result1 error) {
}{result1}
}
func (fake *FakeHandler) OnDataPacket(arg1 livekit.DataPacket_Kind, arg2 []byte) {
func (fake *FakeHandler) OnDataMessage(arg1 livekit.DataPacket_Kind, arg2 []byte) {
var arg2Copy []byte
if arg2 != nil {
arg2Copy = make([]byte, len(arg2))
copy(arg2Copy, arg2)
}
fake.onDataPacketMutex.Lock()
fake.onDataPacketArgsForCall = append(fake.onDataPacketArgsForCall, struct {
fake.onDataMessageMutex.Lock()
fake.onDataMessageArgsForCall = append(fake.onDataMessageArgsForCall, struct {
arg1 livekit.DataPacket_Kind
arg2 []byte
}{arg1, arg2Copy})
stub := fake.OnDataPacketStub
fake.recordInvocation("OnDataPacket", []interface{}{arg1, arg2Copy})
fake.onDataPacketMutex.Unlock()
stub := fake.OnDataMessageStub
fake.recordInvocation("OnDataMessage", []interface{}{arg1, arg2Copy})
fake.onDataMessageMutex.Unlock()
if stub != nil {
fake.OnDataPacketStub(arg1, arg2)
fake.OnDataMessageStub(arg1, arg2)
}
}
func (fake *FakeHandler) OnDataPacketCallCount() int {
fake.onDataPacketMutex.RLock()
defer fake.onDataPacketMutex.RUnlock()
return len(fake.onDataPacketArgsForCall)
func (fake *FakeHandler) OnDataMessageCallCount() int {
fake.onDataMessageMutex.RLock()
defer fake.onDataMessageMutex.RUnlock()
return len(fake.onDataMessageArgsForCall)
}
func (fake *FakeHandler) OnDataPacketCalls(stub func(livekit.DataPacket_Kind, []byte)) {
fake.onDataPacketMutex.Lock()
defer fake.onDataPacketMutex.Unlock()
fake.OnDataPacketStub = stub
func (fake *FakeHandler) OnDataMessageCalls(stub func(livekit.DataPacket_Kind, []byte)) {
fake.onDataMessageMutex.Lock()
defer fake.onDataMessageMutex.Unlock()
fake.OnDataMessageStub = stub
}
func (fake *FakeHandler) OnDataPacketArgsForCall(i int) (livekit.DataPacket_Kind, []byte) {
fake.onDataPacketMutex.RLock()
defer fake.onDataPacketMutex.RUnlock()
argsForCall := fake.onDataPacketArgsForCall[i]
func (fake *FakeHandler) OnDataMessageArgsForCall(i int) (livekit.DataPacket_Kind, []byte) {
fake.onDataMessageMutex.RLock()
defer fake.onDataMessageMutex.RUnlock()
argsForCall := fake.onDataMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeHandler) OnDataMessageUnlabeled(arg1 []byte) {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.onDataMessageUnlabeledMutex.Lock()
fake.onDataMessageUnlabeledArgsForCall = append(fake.onDataMessageUnlabeledArgsForCall, struct {
arg1 []byte
}{arg1Copy})
stub := fake.OnDataMessageUnlabeledStub
fake.recordInvocation("OnDataMessageUnlabeled", []interface{}{arg1Copy})
fake.onDataMessageUnlabeledMutex.Unlock()
if stub != nil {
fake.OnDataMessageUnlabeledStub(arg1)
}
}
func (fake *FakeHandler) OnDataMessageUnlabeledCallCount() int {
fake.onDataMessageUnlabeledMutex.RLock()
defer fake.onDataMessageUnlabeledMutex.RUnlock()
return len(fake.onDataMessageUnlabeledArgsForCall)
}
func (fake *FakeHandler) OnDataMessageUnlabeledCalls(stub func([]byte)) {
fake.onDataMessageUnlabeledMutex.Lock()
defer fake.onDataMessageUnlabeledMutex.Unlock()
fake.OnDataMessageUnlabeledStub = stub
}
func (fake *FakeHandler) OnDataMessageUnlabeledArgsForCall(i int) []byte {
fake.onDataMessageUnlabeledMutex.RLock()
defer fake.onDataMessageUnlabeledMutex.RUnlock()
argsForCall := fake.onDataMessageUnlabeledArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeHandler) OnDataSendError(arg1 error) {
fake.onDataSendErrorMutex.Lock()
fake.onDataSendErrorArgsForCall = append(fake.onDataSendErrorArgsForCall, struct {
@@ -232,6 +302,44 @@ func (fake *FakeHandler) OnDataSendErrorArgsForCall(i int) error {
return argsForCall.arg1
}
func (fake *FakeHandler) OnDataTrackMessage(arg1 []byte, arg2 int64) {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.onDataTrackMessageMutex.Lock()
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
arg1 []byte
arg2 int64
}{arg1Copy, arg2})
stub := fake.OnDataTrackMessageStub
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1Copy, arg2})
fake.onDataTrackMessageMutex.Unlock()
if stub != nil {
fake.OnDataTrackMessageStub(arg1, arg2)
}
}
func (fake *FakeHandler) OnDataTrackMessageCallCount() int {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
return len(fake.onDataTrackMessageArgsForCall)
}
func (fake *FakeHandler) OnDataTrackMessageCalls(stub func([]byte, int64)) {
fake.onDataTrackMessageMutex.Lock()
defer fake.onDataTrackMessageMutex.Unlock()
fake.OnDataTrackMessageStub = stub
}
func (fake *FakeHandler) OnDataTrackMessageArgsForCall(i int) ([]byte, int64) {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
argsForCall := fake.onDataTrackMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeHandler) OnFailed(arg1 bool, arg2 *types.ICEConnectionInfo) {
fake.onFailedMutex.Lock()
fake.onFailedArgsForCall = append(fake.onFailedArgsForCall, struct {
@@ -431,18 +539,20 @@ func (fake *FakeHandler) OnNegotiationStateChangedArgsForCall(i int) transport.N
return argsForCall.arg1
}
func (fake *FakeHandler) OnOffer(arg1 webrtc.SessionDescription) error {
func (fake *FakeHandler) OnOffer(arg1 webrtc.SessionDescription, arg2 uint32, arg3 map[string]string) error {
fake.onOfferMutex.Lock()
ret, specificReturn := fake.onOfferReturnsOnCall[len(fake.onOfferArgsForCall)]
fake.onOfferArgsForCall = append(fake.onOfferArgsForCall, struct {
arg1 webrtc.SessionDescription
}{arg1})
arg2 uint32
arg3 map[string]string
}{arg1, arg2, arg3})
stub := fake.OnOfferStub
fakeReturns := fake.onOfferReturns
fake.recordInvocation("OnOffer", []interface{}{arg1})
fake.recordInvocation("OnOffer", []interface{}{arg1, arg2, arg3})
fake.onOfferMutex.Unlock()
if stub != nil {
return stub(arg1)
return stub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1
@@ -456,17 +566,17 @@ func (fake *FakeHandler) OnOfferCallCount() int {
return len(fake.onOfferArgsForCall)
}
func (fake *FakeHandler) OnOfferCalls(stub func(webrtc.SessionDescription) error) {
func (fake *FakeHandler) OnOfferCalls(stub func(webrtc.SessionDescription, uint32, map[string]string) error) {
fake.onOfferMutex.Lock()
defer fake.onOfferMutex.Unlock()
fake.OnOfferStub = stub
}
func (fake *FakeHandler) OnOfferArgsForCall(i int) webrtc.SessionDescription {
func (fake *FakeHandler) OnOfferArgsForCall(i int) (webrtc.SessionDescription, uint32, map[string]string) {
fake.onOfferMutex.RLock()
defer fake.onOfferMutex.RUnlock()
argsForCall := fake.onOfferArgsForCall[i]
return argsForCall.arg1
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeHandler) OnOfferReturns(result1 error) {
@@ -492,6 +602,30 @@ func (fake *FakeHandler) OnOfferReturnsOnCall(i int, result1 error) {
}{result1}
}
func (fake *FakeHandler) OnSetRemoteDescriptionOffer() {
fake.onSetRemoteDescriptionOfferMutex.Lock()
fake.onSetRemoteDescriptionOfferArgsForCall = append(fake.onSetRemoteDescriptionOfferArgsForCall, struct {
}{})
stub := fake.OnSetRemoteDescriptionOfferStub
fake.recordInvocation("OnSetRemoteDescriptionOffer", []interface{}{})
fake.onSetRemoteDescriptionOfferMutex.Unlock()
if stub != nil {
fake.OnSetRemoteDescriptionOfferStub()
}
}
func (fake *FakeHandler) OnSetRemoteDescriptionOfferCallCount() int {
fake.onSetRemoteDescriptionOfferMutex.RLock()
defer fake.onSetRemoteDescriptionOfferMutex.RUnlock()
return len(fake.onSetRemoteDescriptionOfferArgsForCall)
}
func (fake *FakeHandler) OnSetRemoteDescriptionOfferCalls(stub func()) {
fake.onSetRemoteDescriptionOfferMutex.Lock()
defer fake.onSetRemoteDescriptionOfferMutex.Unlock()
fake.OnSetRemoteDescriptionOfferStub = stub
}
func (fake *FakeHandler) OnStreamStateChange(arg1 *streamallocator.StreamStateUpdate) error {
fake.onStreamStateChangeMutex.Lock()
ret, specificReturn := fake.onStreamStateChangeReturnsOnCall[len(fake.onStreamStateChangeArgsForCall)]
@@ -586,33 +720,71 @@ func (fake *FakeHandler) OnTrackArgsForCall(i int) (*webrtc.TrackRemote, *webrtc
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeHandler) OnUnmatchedMedia(arg1 uint32, arg2 uint32) error {
fake.onUnmatchedMediaMutex.Lock()
ret, specificReturn := fake.onUnmatchedMediaReturnsOnCall[len(fake.onUnmatchedMediaArgsForCall)]
fake.onUnmatchedMediaArgsForCall = append(fake.onUnmatchedMediaArgsForCall, struct {
arg1 uint32
arg2 uint32
}{arg1, arg2})
stub := fake.OnUnmatchedMediaStub
fakeReturns := fake.onUnmatchedMediaReturns
fake.recordInvocation("OnUnmatchedMedia", []interface{}{arg1, arg2})
fake.onUnmatchedMediaMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeHandler) OnUnmatchedMediaCallCount() int {
fake.onUnmatchedMediaMutex.RLock()
defer fake.onUnmatchedMediaMutex.RUnlock()
return len(fake.onUnmatchedMediaArgsForCall)
}
func (fake *FakeHandler) OnUnmatchedMediaCalls(stub func(uint32, uint32) error) {
fake.onUnmatchedMediaMutex.Lock()
defer fake.onUnmatchedMediaMutex.Unlock()
fake.OnUnmatchedMediaStub = stub
}
func (fake *FakeHandler) OnUnmatchedMediaArgsForCall(i int) (uint32, uint32) {
fake.onUnmatchedMediaMutex.RLock()
defer fake.onUnmatchedMediaMutex.RUnlock()
argsForCall := fake.onUnmatchedMediaArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeHandler) OnUnmatchedMediaReturns(result1 error) {
fake.onUnmatchedMediaMutex.Lock()
defer fake.onUnmatchedMediaMutex.Unlock()
fake.OnUnmatchedMediaStub = nil
fake.onUnmatchedMediaReturns = struct {
result1 error
}{result1}
}
func (fake *FakeHandler) OnUnmatchedMediaReturnsOnCall(i int, result1 error) {
fake.onUnmatchedMediaMutex.Lock()
defer fake.onUnmatchedMediaMutex.Unlock()
fake.OnUnmatchedMediaStub = nil
if fake.onUnmatchedMediaReturnsOnCall == nil {
fake.onUnmatchedMediaReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.onUnmatchedMediaReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeHandler) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.onAnswerMutex.RLock()
defer fake.onAnswerMutex.RUnlock()
fake.onDataPacketMutex.RLock()
defer fake.onDataPacketMutex.RUnlock()
fake.onDataSendErrorMutex.RLock()
defer fake.onDataSendErrorMutex.RUnlock()
fake.onFailedMutex.RLock()
defer fake.onFailedMutex.RUnlock()
fake.onFullyEstablishedMutex.RLock()
defer fake.onFullyEstablishedMutex.RUnlock()
fake.onICECandidateMutex.RLock()
defer fake.onICECandidateMutex.RUnlock()
fake.onInitialConnectedMutex.RLock()
defer fake.onInitialConnectedMutex.RUnlock()
fake.onNegotiationFailedMutex.RLock()
defer fake.onNegotiationFailedMutex.RUnlock()
fake.onNegotiationStateChangedMutex.RLock()
defer fake.onNegotiationStateChangedMutex.RUnlock()
fake.onOfferMutex.RLock()
defer fake.onOfferMutex.RUnlock()
fake.onStreamStateChangeMutex.RLock()
defer fake.onStreamStateChangeMutex.RUnlock()
fake.onTrackMutex.RLock()
defer fake.onTrackMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
+209 -56
View File
@@ -28,17 +28,15 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/transport"
"github.com/livekit/livekit-server/pkg/rtc/transport/transportfakes"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/testutils"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
)
func TestMissingAnswerDuringICERestart(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
IsOfferer: true,
Config: &WebRTCConfig{},
IsOfferer: true,
}
paramsA := params
@@ -70,7 +68,7 @@ func TestMissingAnswerDuringICERestart(t *testing.T) {
// offer again, but missed
var offerReceived atomic.Bool
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, _offerId uint32, _midToTrackID map[string]string) error {
require.Equal(t, webrtc.SignalingStateHaveLocalOffer, transportA.pc.SignalingState())
require.Equal(t, transport.NegotiationStateRemote, negotiationState.Load().(transport.NegotiationState))
offerReceived.Store(true)
@@ -91,10 +89,8 @@ func TestMissingAnswerDuringICERestart(t *testing.T) {
func TestNegotiationTiming(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
IsOfferer: true,
Config: &WebRTCConfig{},
IsOfferer: true,
}
paramsA := params
@@ -116,9 +112,16 @@ func TestNegotiationTiming(t *testing.T) {
require.False(t, transportB.IsEstablished())
handleICEExchange(t, transportA, transportB, handlerA, handlerB)
offer := atomic.Value{}
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
offer.Store(&sd)
firstOffer := atomic.Value{}
firstOfferId := atomic.Uint32{}
secondOffer := atomic.Value{}
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
if _, ok := firstOffer.Load().(*webrtc.SessionDescription); !ok {
firstOffer.Store(&sd)
firstOfferId.Store(offerId)
} else {
secondOffer.Store(&sd)
}
return nil
})
@@ -161,15 +164,22 @@ func TestNegotiationTiming(t *testing.T) {
return state == transport.NegotiationStateRetry
}, 10*time.Second, 10*time.Millisecond, "negotiation state does not match NegotiateStateRetry")
time.Sleep(5 * time.Millisecond)
actualOffer, ok := offer.Load().(*webrtc.SessionDescription)
require.True(t, ok)
require.Eventually(t, func() bool {
_, ok := firstOffer.Load().(*webrtc.SessionDescription)
if !ok {
return false
}
if firstOfferId.Load() == 0 {
return false
}
return true
}, 10*time.Second, 10*time.Millisecond, "first offer not received yet")
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
transportA.HandleRemoteDescription(answer)
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
transportA.HandleRemoteDescription(answer, answerId)
return nil
})
transportB.HandleRemoteDescription(*actualOffer)
transportB.HandleRemoteDescription(*firstOffer.Load().(*webrtc.SessionDescription), firstOfferId.Load())
require.Eventually(t, func() bool {
return transportA.IsEstablished()
@@ -178,11 +188,18 @@ func TestNegotiationTiming(t *testing.T) {
return transportB.IsEstablished()
}, 10*time.Second, time.Millisecond*10, "transportB is not established")
// it should still be negotiating again
require.Equal(t, transport.NegotiationStateRemote, negotiationState.Load().(transport.NegotiationState))
offer2, ok := offer.Load().(*webrtc.SessionDescription)
// offerer should send another offer after processing the answer
// as there were forced negotiations a couple of time above
require.Eventually(t, func() bool {
state, ok := negotiationState.Load().(transport.NegotiationState)
if !ok {
return false
}
return state == transport.NegotiationStateRemote
}, 10*time.Second, 10*time.Millisecond, "negotiation state does not match NegotiateStateRemote")
_, ok := secondOffer.Load().(*webrtc.SessionDescription)
require.True(t, ok)
require.False(t, offer2 == actualOffer)
transportA.Close()
transportB.Close()
@@ -190,10 +207,8 @@ func TestNegotiationTiming(t *testing.T) {
func TestFirstOfferMissedDuringICERestart(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
IsOfferer: true,
Config: &WebRTCConfig{},
IsOfferer: true,
}
paramsA := params
@@ -216,7 +231,7 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
// first offer missed
var firstOfferReceived atomic.Bool
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, _offerId uint32, _midToTrackID map[string]string) error {
firstOfferReceived.Store(true)
return nil
})
@@ -228,13 +243,13 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
// set offer/answer with restart ICE, will negotiate twice,
// first one is recover from missed offer
// second one is restartICE
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
transportA.HandleRemoteDescription(answer)
handlerB.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
transportA.HandleRemoteDescription(answer, answerId)
return nil
})
var offerCount atomic.Int32
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
offerCount.Inc()
// the second offer is a ice restart offer, so we wait transportB complete the ice gathering
@@ -244,7 +259,7 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
}, 10*time.Second, time.Millisecond*10)
}
transportB.HandleRemoteDescription(sd)
transportB.HandleRemoteDescription(sd, offerId)
return nil
})
@@ -264,10 +279,8 @@ func TestFirstOfferMissedDuringICERestart(t *testing.T) {
func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
IsOfferer: true,
Config: &WebRTCConfig{},
IsOfferer: true,
}
paramsA := params
@@ -290,17 +303,17 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
// first answer missed
var firstAnswerReceived atomic.Bool
handlerB.OnAnswerCalls(func(sd webrtc.SessionDescription) error {
handlerB.OnAnswerCalls(func(sd webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
if firstAnswerReceived.Load() {
transportA.HandleRemoteDescription(sd)
transportA.HandleRemoteDescription(sd, answerId)
} else {
// do not send first answer so that remote misses the first answer
firstAnswerReceived.Store(true)
}
return nil
})
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
transportB.HandleRemoteDescription(sd)
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
transportB.HandleRemoteDescription(sd, offerId)
return nil
})
@@ -313,7 +326,7 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
// first one is recover from missed offer
// second one is restartICE
var offerCount atomic.Int32
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error {
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
offerCount.Inc()
// the second offer is a ice restart offer, so we wait for transportB to complete ICE gathering
@@ -323,7 +336,7 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
}, 10*time.Second, time.Millisecond*10)
}
transportB.HandleRemoteDescription(sd)
transportB.HandleRemoteDescription(sd, offerId)
return nil
})
@@ -343,10 +356,8 @@ func TestFirstAnswerMissedDuringICERestart(t *testing.T) {
func TestNegotiationFailed(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
IsOfferer: true,
Config: &WebRTCConfig{},
IsOfferer: true,
}
paramsA := params
@@ -371,7 +382,9 @@ func TestNegotiationFailed(t *testing.T) {
connectTransports(t, transportA, transportB, handlerA, handlerB, false, 1, 1)
// reset OnOffer to force a negotiation failure
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription) error { return nil })
handlerA.OnOfferCalls(func(sd webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
return nil
})
var failed atomic.Int32
handlerA.OnNegotiationFailedCalls(func() {
failed.Inc()
@@ -386,10 +399,8 @@ func TestNegotiationFailed(t *testing.T) {
func TestFilteringCandidates(t *testing.T) {
params := TransportParams{
ParticipantID: "id",
ParticipantIdentity: "identity",
Config: &WebRTCConfig{},
EnabledCodecs: []*livekit.Codec{
Config: &WebRTCConfig{},
EnabledPublishCodecs: []*livekit.Codec{
{Mime: mime.MimeTypeOpus.String()},
{Mime: mime.MimeTypeVP8.String()},
{Mime: mime.MimeTypeH264.String()},
@@ -527,15 +538,15 @@ func handleICEExchange(t *testing.T, a, b *PCTransport, ah, bh *transportfakes.F
func connectTransports(t *testing.T, offerer, answerer *PCTransport, offererHandler, answererHandler *transportfakes.FakeHandler, isICERestart bool, expectedOfferCount int32, expectedAnswerCount int32) {
var offerCount atomic.Int32
var answerCount atomic.Int32
answererHandler.OnAnswerCalls(func(answer webrtc.SessionDescription) error {
answererHandler.OnAnswerCalls(func(answer webrtc.SessionDescription, answerId uint32, _midToTrackID map[string]string) error {
answerCount.Inc()
offerer.HandleRemoteDescription(answer)
offerer.HandleRemoteDescription(answer, answerId)
return nil
})
offererHandler.OnOfferCalls(func(offer webrtc.SessionDescription) error {
offererHandler.OnOfferCalls(func(offer webrtc.SessionDescription, offerId uint32, _midToTrackID map[string]string) error {
offerCount.Inc()
answerer.HandleRemoteDescription(offer)
answerer.HandleRemoteDescription(offer, offerId)
return nil
})
@@ -606,7 +617,7 @@ func TestConfigureAudioTransceiver(t *testing.T) {
tr, err := pc.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
require.NoError(t, err)
configureAudioTransceiver(tr, testcase.stereo, testcase.nack)
configureSenderAudio(tr, testcase.stereo, testcase.nack)
codecs := tr.Sender().GetParameters().Codecs
for _, codec := range codecs {
if mime.IsMimeTypeStringOpus(codec.MimeType) {
@@ -624,3 +635,145 @@ func TestConfigureAudioTransceiver(t *testing.T) {
})
}
}
// In single-PC mode the publisher PC carries both publish and subscribe
// directions. If the MediaEngine were built only from the publish codec list,
// the SDP offer would not advertise some codecs in the m-section even though
// the subscribe direction is supposed to support it. This regression-tests
// the union behavior in newPeerConnection: build the MediaEngine from publish +
// subscribe codec lists.
func TestSinglePCMediaEngineUnionsCodecs(t *testing.T) {
videoMSectionCodecs := func(transport *PCTransport) []string {
_, err := transport.pc.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo)
require.NoError(t, err)
offer, err := transport.pc.CreateOffer(nil)
require.NoError(t, err)
parsed, err := offer.Unmarshal()
require.NoError(t, err)
var rtpmaps []string
for _, m := range parsed.MediaDescriptions {
if m.MediaName.Media != "video" {
continue
}
for _, a := range m.Attributes {
if a.Key == "rtpmap" {
rtpmaps = append(rtpmaps, a.Value)
}
}
}
return rtpmaps
}
sdpHasH264 := func(rtpmaps []string) bool {
for _, r := range rtpmaps {
if strings.Contains(r, "H264/") {
return true
}
}
return false
}
publishOnly := []*livekit.Codec{
{Mime: mime.MimeTypeOpus.String()},
{Mime: mime.MimeTypeVP8.String()},
}
subscribeOnly := []*livekit.Codec{
{Mime: mime.MimeTypeOpus.String()},
{Mime: mime.MimeTypeVP8.String()},
{Mime: mime.MimeTypeH264.String()},
}
// Control: only publish codecs set (dual-PC publisher PC). H.264 absent.
dualPC, err := NewPCTransport(TransportParams{
Config: &WebRTCConfig{},
EnabledPublishCodecs: publishOnly,
Handler: &transportfakes.FakeHandler{},
})
require.NoError(t, err)
require.False(t, sdpHasH264(videoMSectionCodecs(dualPC)),
"dual-PC publisher must not advertise H.264 when it's stripped from the publish list")
// Single-PC publisher PC: both lists set. H.264 must appear.
singlePC, err := NewPCTransport(TransportParams{
Config: &WebRTCConfig{},
EnabledPublishCodecs: publishOnly,
EnabledSubscribeCodecs: subscribeOnly,
IsSendSide: true,
Handler: &transportfakes.FakeHandler{},
})
require.NoError(t, err)
require.True(t, sdpHasH264(videoMSectionCodecs(singlePC)),
"single-PC publisher must advertise H.264 from the subscribe list even when it's stripped from the publish list")
}
// Regression test for restrictReceiverCodecsToPublishList: subscribe-only
// codecs (e.g., H.264) registered for subscriptions must not leak into the
// recv-side m-section of an answer, or the peer could publish them.
func TestSinglePCAnswerStripsSubscribeOnlyCodecsFromRecvSide(t *testing.T) {
publishCodecs := []*livekit.Codec{
{Mime: mime.MimeTypeOpus.String()},
{Mime: mime.MimeTypeVP8.String()},
}
subscribeCodecs := []*livekit.Codec{
{Mime: mime.MimeTypeOpus.String()},
{Mime: mime.MimeTypeVP8.String()},
{Mime: mime.MimeTypeH264.String()},
}
handler := &transportfakes.FakeHandler{}
server, err := NewPCTransport(TransportParams{
Config: &WebRTCConfig{},
EnabledPublishCodecs: publishCodecs,
EnabledSubscribeCodecs: subscribeCodecs,
IsSendSide: true,
Handler: handler,
})
require.NoError(t, err)
defer server.Close()
var clientME webrtc.MediaEngine
require.NoError(t, registerCodecs(&clientME, subscribeCodecs, RTCPFeedbackConfig{}, false))
client, err := webrtc.NewAPI(webrtc.WithMediaEngine(&clientME)).NewPeerConnection(webrtc.Configuration{})
require.NoError(t, err)
defer client.Close()
_, err = client.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RTPTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendonly,
})
require.NoError(t, err)
offer, err := client.CreateOffer(nil)
require.NoError(t, err)
require.Contains(t, offer.SDP, "H264/", "offer must advertise H.264")
require.NoError(t, client.SetLocalDescription(offer))
var answer atomic.Pointer[webrtc.SessionDescription]
handler.OnAnswerCalls(func(sd webrtc.SessionDescription, _ uint32, _ map[string]string) error {
answer.Store(&sd)
return nil
})
require.NoError(t, server.HandleRemoteDescription(*client.LocalDescription(), 1))
require.Eventually(t, func() bool {
return answer.Load() != nil
}, 5*time.Second, 10*time.Millisecond, "server did not produce answer")
parsed, err := answer.Load().Unmarshal()
require.NoError(t, err)
var videoSection *sdp.MediaDescription
for _, m := range parsed.MediaDescriptions {
if m.MediaName.Media == "video" {
videoSection = m
break
}
}
require.NotNil(t, videoSection, "answer missing video m-section")
for _, a := range videoSection.Attributes {
if a.Key != "rtpmap" {
continue
}
require.NotContains(t, a.Value, "H264/",
"answer must not advertise H.264 in recv-side m-section: %s", a.Value)
}
}
+398 -179
View File
@@ -39,8 +39,8 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/datachannel"
"github.com/livekit/livekit-server/pkg/sfu/interceptor"
"github.com/livekit/livekit-server/pkg/sfu/pacer"
"github.com/livekit/livekit-server/pkg/telemetry"
)
const (
@@ -71,43 +71,33 @@ func (h TransportManagerTransportHandler) OnFailed(isShortLived bool, iceConnect
// -------------------------------
type TransportManagerPublisherTransportHandler struct {
TransportManagerTransportHandler
}
func (h TransportManagerPublisherTransportHandler) OnAnswer(sd webrtc.SessionDescription) error {
h.t.lastPublisherAnswer.Store(sd)
return h.Handler.OnAnswer(sd)
}
// -------------------------------
type TransportManagerParams struct {
Identity livekit.ParticipantIdentity
SID livekit.ParticipantID
SubscriberAsPrimary bool
Config *WebRTCConfig
Twcc *twcc.Responder
ProtocolVersion types.ProtocolVersion
CongestionControlConfig config.CongestionControlConfig
EnabledSubscribeCodecs []*livekit.Codec
EnabledPublishCodecs []*livekit.Codec
SimTracks map[uint32]SimulcastTrackInfo
ClientInfo ClientInfo
Migration bool
AllowTCPFallback bool
TCPFallbackRTTThreshold int
AllowUDPUnstableFallback bool
TURNSEnabled bool
AllowPlayoutDelay bool
DataChannelMaxBufferedAmount uint64
DatachannelSlowThreshold int
Logger logger.Logger
PublisherHandler transport.Handler
SubscriberHandler transport.Handler
DataChannelStats *telemetry.BytesTrackStats
UseOneShotSignallingMode bool
FireOnTrackBySdp bool
SubscriberAsPrimary bool
UseSinglePeerConnection bool
Config *WebRTCConfig
Twcc *twcc.Responder
ProtocolVersion types.ProtocolVersion
CongestionControlConfig config.CongestionControlConfig
EnabledSubscribeCodecs []*livekit.Codec
EnabledPublishCodecs []*livekit.Codec
SimTracks map[uint32]interceptor.SimulcastTrackInfo
ClientInfo ClientInfo
Migration bool
AllowTCPFallback bool
TCPFallbackRTTThreshold int
AllowUDPUnstableFallback bool
TURNSEnabled bool
AllowPlayoutDelay bool
DataChannelMaxBufferedAmount uint64
DatachannelSlowThreshold int
DatachannelLossyTargetLatency time.Duration
Logger logger.Logger
PublisherHandler transport.Handler
SubscriberHandler transport.Handler
DataChannelStats *BytesTrackStats
UseOneShotSignallingMode bool
FireOnTrackBySdp bool
EnableDataTracks bool
}
type TransportManager struct {
@@ -124,9 +114,8 @@ type TransportManager struct {
signalSourceValid atomic.Bool
pendingOfferPublisher *webrtc.SessionDescription
pendingOfferIdPublisher uint32
pendingDataChannelsPublisher []*livekit.DataChannelInfo
lastPublisherAnswer atomic.Value
lastPublisherOffer atomic.Value
iceConfig *livekit.ICEConfig
mediaLossProxy *MediaLossProxy
@@ -135,7 +124,8 @@ type TransportManager struct {
onICEConfigChanged func(iceConfig *livekit.ICEConfig)
droppedBySlowReaderCount atomic.Uint32
dataChannelSendErrorDroppedBySlowReaderCount atomic.Uint32
dataChannelSendErrorCount atomic.Uint32
}
func NewTransportManager(params TransportManagerParams) (*TransportManager, error) {
@@ -150,53 +140,67 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
t.mediaLossProxy.OnMediaLossUpdate(t.onMediaLossUpdate)
lgr := LoggerWithPCTarget(params.Logger, livekit.SignalTarget_PUBLISHER)
// In single-PC mode the one PC carries both directions, so it needs both
// codec lists registered on its MediaEngine. In dual-PC mode the publisher
// PC is recvonly, so subscribe codecs are left nil.
var publisherSubscribeCodecs []*livekit.Codec
if params.UseSinglePeerConnection || params.UseOneShotSignallingMode {
publisherSubscribeCodecs = params.EnabledSubscribeCodecs
}
publisher, err := NewPCTransport(TransportParams{
ParticipantID: params.SID,
ParticipantIdentity: params.Identity,
ProtocolVersion: params.ProtocolVersion,
Config: params.Config,
Twcc: params.Twcc,
DirectionConfig: params.Config.Publisher,
CongestionControlConfig: params.CongestionControlConfig,
EnabledCodecs: params.EnabledPublishCodecs,
Logger: lgr,
SimTracks: params.SimTracks,
ClientInfo: params.ClientInfo,
Transport: livekit.SignalTarget_PUBLISHER,
Handler: TransportManagerPublisherTransportHandler{TransportManagerTransportHandler{params.PublisherHandler, t, lgr}},
UseOneShotSignallingMode: params.UseOneShotSignallingMode,
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
FireOnTrackBySdp: params.FireOnTrackBySdp,
ProtocolVersion: params.ProtocolVersion,
Config: params.Config,
Twcc: params.Twcc,
DirectionConfig: params.Config.Publisher,
CongestionControlConfig: params.CongestionControlConfig,
EnabledPublishCodecs: params.EnabledPublishCodecs,
EnabledSubscribeCodecs: publisherSubscribeCodecs,
Logger: lgr,
SimTracks: params.SimTracks,
ClientInfo: params.ClientInfo,
IsSendSide: params.UseOneShotSignallingMode || params.UseSinglePeerConnection,
AllowPlayoutDelay: params.AllowPlayoutDelay,
Transport: livekit.SignalTarget_PUBLISHER,
Handler: TransportManagerTransportHandler{params.PublisherHandler, t, lgr},
UseOneShotSignallingMode: params.UseOneShotSignallingMode,
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
DatachannelLossyTargetLatency: params.DatachannelLossyTargetLatency,
FireOnTrackBySdp: params.FireOnTrackBySdp,
EnableDataTracks: params.EnableDataTracks,
})
if err != nil {
return nil, err
}
t.publisher = publisher
lgr = LoggerWithPCTarget(params.Logger, livekit.SignalTarget_SUBSCRIBER)
subscriber, err := NewPCTransport(TransportParams{
ParticipantID: params.SID,
ParticipantIdentity: params.Identity,
ProtocolVersion: params.ProtocolVersion,
Config: params.Config,
DirectionConfig: params.Config.Subscriber,
CongestionControlConfig: params.CongestionControlConfig,
EnabledCodecs: params.EnabledSubscribeCodecs,
Logger: lgr,
ClientInfo: params.ClientInfo,
IsOfferer: true,
IsSendSide: true,
AllowPlayoutDelay: params.AllowPlayoutDelay,
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
Transport: livekit.SignalTarget_SUBSCRIBER,
Handler: TransportManagerTransportHandler{params.SubscriberHandler, t, lgr},
})
if err != nil {
return nil, err
if !t.params.UseOneShotSignallingMode && !t.params.UseSinglePeerConnection {
lgr := LoggerWithPCTarget(params.Logger, livekit.SignalTarget_SUBSCRIBER)
subscriber, err := NewPCTransport(TransportParams{
ProtocolVersion: params.ProtocolVersion,
Config: params.Config,
DirectionConfig: params.Config.Subscriber,
CongestionControlConfig: params.CongestionControlConfig,
EnabledSubscribeCodecs: params.EnabledSubscribeCodecs,
Logger: lgr,
ClientInfo: params.ClientInfo,
IsOfferer: true,
IsSendSide: true,
AllowPlayoutDelay: params.AllowPlayoutDelay,
DataChannelMaxBufferedAmount: params.DataChannelMaxBufferedAmount,
DatachannelSlowThreshold: params.DatachannelSlowThreshold,
DatachannelLossyTargetLatency: params.DatachannelLossyTargetLatency,
Transport: livekit.SignalTarget_SUBSCRIBER,
Handler: TransportManagerTransportHandler{params.SubscriberHandler, t, lgr},
FireOnTrackBySdp: params.FireOnTrackBySdp,
EnableDataTracks: params.EnableDataTracks,
})
if err != nil {
return nil, err
}
t.subscriber = subscriber
}
t.subscriber = subscriber
if !t.params.Migration {
if !t.params.Migration && t.params.SubscriberAsPrimary {
if err := t.createDataChannelsForSubscriber(nil); err != nil {
return nil, err
}
@@ -207,8 +211,12 @@ func NewTransportManager(params TransportManagerParams) (*TransportManager, erro
}
func (t *TransportManager) Close() {
t.publisher.Close()
t.subscriber.Close()
if t.publisher != nil {
t.publisher.Close()
}
if t.subscriber != nil {
t.subscriber.Close()
}
}
func (t *TransportManager) SubscriberClose() {
@@ -231,6 +239,10 @@ func (t *TransportManager) GetPublisherMid(rtpReceiver *webrtc.RTPReceiver) stri
return t.publisher.GetMid(rtpReceiver)
}
func (t *TransportManager) GetPublisherRTPTransceiver(mid string) *webrtc.RTPTransceiver {
return t.publisher.GetRTPTransceiver(mid)
}
func (t *TransportManager) GetPublisherRTPReceiver(mid string) *webrtc.RTPReceiver {
return t.publisher.GetRTPReceiver(mid)
}
@@ -240,37 +252,49 @@ func (t *TransportManager) WritePublisherRTCP(pkts []rtcp.Packet) error {
}
func (t *TransportManager) GetSubscriberRTT() (float64, bool) {
return t.subscriber.GetRTT()
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.GetRTT()
} else {
return t.subscriber.GetRTT()
}
}
func (t *TransportManager) HasSubscriberEverConnected() bool {
return t.subscriber.HasEverConnected()
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.HasEverConnected()
} else {
return t.subscriber.HasEverConnected()
}
}
func (t *TransportManager) AddTrackLocal(
trackLocal webrtc.TrackLocal,
params types.AddTrackParams,
enabledCodecs []*livekit.Codec,
rtcpFeedbackConfig RTCPFeedbackConfig,
) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error) {
if t.params.UseOneShotSignallingMode {
return t.publisher.AddTrack(trackLocal, params)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.AddTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
} else {
return t.subscriber.AddTrack(trackLocal, params)
return t.subscriber.AddTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
}
}
func (t *TransportManager) AddTransceiverFromTrackLocal(
trackLocal webrtc.TrackLocal,
params types.AddTrackParams,
enabledCodecs []*livekit.Codec,
rtcpFeedbackConfig RTCPFeedbackConfig,
) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error) {
if t.params.UseOneShotSignallingMode {
return t.publisher.AddTransceiverFromTrack(trackLocal, params)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.AddTransceiverFromTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
} else {
return t.subscriber.AddTransceiverFromTrack(trackLocal, params)
return t.subscriber.AddTransceiverFromTrack(trackLocal, params, enabledCodecs, rtcpFeedbackConfig)
}
}
func (t *TransportManager) RemoveTrackLocal(sender *webrtc.RTPSender) error {
if t.params.UseOneShotSignallingMode {
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.RemoveTrack(sender)
} else {
return t.subscriber.RemoveTrack(sender)
@@ -278,7 +302,7 @@ func (t *TransportManager) RemoveTrackLocal(sender *webrtc.RTPSender) error {
}
func (t *TransportManager) WriteSubscriberRTCP(pkts []rtcp.Packet) error {
if t.params.UseOneShotSignallingMode {
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.WriteRTCP(pkts)
} else {
return t.subscriber.WriteRTCP(pkts)
@@ -286,36 +310,85 @@ func (t *TransportManager) WriteSubscriberRTCP(pkts []rtcp.Packet) error {
}
func (t *TransportManager) GetSubscriberPacer() pacer.Pacer {
return t.subscriber.GetPacer()
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
return t.publisher.GetPacer()
} else {
return t.subscriber.GetPacer()
}
}
func (t *TransportManager) AddSubscribedTrack(subTrack types.SubscribedTrack) {
t.subscriber.AddTrackToStreamAllocator(subTrack)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
t.publisher.AddTrackToStreamAllocator(subTrack)
} else {
t.subscriber.AddTrackToStreamAllocator(subTrack)
}
}
func (t *TransportManager) RemoveSubscribedTrack(subTrack types.SubscribedTrack) {
t.subscriber.RemoveTrackFromStreamAllocator(subTrack)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
t.publisher.RemoveTrackFromStreamAllocator(subTrack)
} else {
t.subscriber.RemoveTrackFromStreamAllocator(subTrack)
}
}
func (t *TransportManager) SendDataPacket(kind livekit.DataPacket_Kind, encoded []byte) error {
func (t *TransportManager) SendDataMessage(kind livekit.DataPacket_Kind, data []byte) error {
// downstream data is sent via primary peer connection
err := t.getTransport(true).SendDataPacket(kind, encoded)
return t.handleSendDataResult(t.getTransport(true).SendDataMessage(kind, data), kind.String(), len(data))
}
func (t *TransportManager) SendDataMessageUnlabeled(data []byte, useRaw bool, sender livekit.ParticipantIdentity) error {
// downstream data is sent via primary peer connection
return t.handleSendDataResult(
t.getTransport(true).SendDataMessageUnlabeled(data, useRaw, sender),
"unlabeled",
len(data),
)
}
func (t *TransportManager) handleSendDataResult(err error, kind string, size int) error {
if err != nil {
if !utils.ErrorIsOneOf(err, io.ErrClosedPipe, sctp.ErrStreamClosed, ErrTransportFailure, ErrDataChannelBufferFull, context.DeadlineExceeded) {
if !utils.ErrorIsOneOf(
err,
io.ErrClosedPipe,
sctp.ErrStreamClosed,
ErrTransportFailure,
ErrDataChannelUnavailable,
context.DeadlineExceeded,
datachannel.ErrDataDroppedByHighBufferedAmount,
) {
if errors.Is(err, datachannel.ErrDataDroppedBySlowReader) {
droppedBySlowReaderCount := t.droppedBySlowReaderCount.Inc()
droppedBySlowReaderCount := t.dataChannelSendErrorDroppedBySlowReaderCount.Inc()
if (droppedBySlowReaderCount-1)%100 == 0 {
t.params.Logger.Infow("drop data packet by slow reader", "error", err, "kind", kind, "count", droppedBySlowReaderCount)
t.params.Logger.Infow(
"drop data message by slow reader",
"error", err,
"kind", kind,
"count", droppedBySlowReaderCount,
)
}
} else {
t.params.Logger.Warnw("send data packet error", err)
count := t.dataChannelSendErrorCount.Inc()
if (count-1)%100 == 0 {
t.params.Logger.Infow(
"send data message error",
"error", err,
"kind", kind,
"count", count,
)
}
}
}
if utils.ErrorIsOneOf(err, sctp.ErrStreamClosed, io.ErrClosedPipe) {
t.params.SubscriberHandler.OnDataSendError(err)
if t.params.SubscriberAsPrimary {
t.params.SubscriberHandler.OnDataSendError(err)
} else {
t.params.PublisherHandler.OnDataSendError(err)
}
}
} else {
t.params.DataChannelStats.AddBytes(uint64(len(encoded)), true)
t.params.DataChannelStats.AddBytes(uint64(size), true)
}
return err
@@ -323,8 +396,8 @@ func (t *TransportManager) SendDataPacket(kind livekit.DataPacket_Kind, encoded
func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels []*livekit.DataChannelInfo) error {
var (
reliableID, lossyID uint16
reliableIDPtr, lossyIDPtr *uint16
reliableID, lossyID, dataTrackID uint16
reliableIDPtr, lossyIDPtr, dataTrackIDPtr *uint16
)
//
@@ -335,13 +408,17 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
// For new version migration clients, we create data channels with new ID and negotiate with client
//
for _, dc := range pendingDataChannels {
if dc.Label == ReliableDataChannel {
// pion use step 2 for auto generated ID, so we need to add 4 to avoid conflict
reliableID = uint16(dc.Id) + 4
switch dc.Label {
case ReliableDataChannel:
// pion use step 2 for auto generated ID, so we need to add 6 to avoid conflict
reliableID = uint16(dc.Id) + 6
reliableIDPtr = &reliableID
} else if dc.Label == LossyDataChannel {
lossyID = uint16(dc.Id) + 4
case LossyDataChannel:
lossyID = uint16(dc.Id) + 6
lossyIDPtr = &lossyID
case DataTrackDataChannel:
dataTrackID = uint16(dc.Id) + 6
dataTrackIDPtr = &dataTrackID
}
}
@@ -355,6 +432,7 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
return err
}
ordered = false
retransmits := uint16(0)
negotiated = t.params.Migration && lossyIDPtr == nil
if err := t.subscriber.CreateDataChannel(LossyDataChannel, &webrtc.DataChannelInit{
@@ -365,26 +443,28 @@ func (t *TransportManager) createDataChannelsForSubscriber(pendingDataChannels [
}); err != nil {
return err
}
negotiated = t.params.Migration && dataTrackIDPtr == nil
if err := t.subscriber.CreateDataChannel(DataTrackDataChannel, &webrtc.DataChannelInit{
Ordered: &ordered,
MaxRetransmits: &retransmits,
ID: dataTrackIDPtr,
Negotiated: &negotiated,
}); err != nil {
return err
}
return nil
}
func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescription, mediaType string) (parsed *sdp.SessionDescription, unmatched []*sdp.MediaDescription, err error) {
// prefer codec from offer for clients that don't support setCodecPreferences
parsed, err = offer.Unmarshal()
if err != nil {
t.params.Logger.Errorw("failed to parse offer for codec preference", err)
return
}
func (t *TransportManager) GetUnmatchMediaForOffer(parsedOffer *sdp.SessionDescription, mediaType string) (unmatched []*sdp.MediaDescription, err error) {
var lastMatchedMid string
lastAnswer := t.lastPublisherAnswer.Load()
if lastAnswer != nil {
answer := lastAnswer.(webrtc.SessionDescription)
parsedAnswer, err1 := answer.Unmarshal()
if lastAnswer := t.publisher.CurrentLocalDescription(); lastAnswer != nil {
parsedAnswer, err1 := lastAnswer.Unmarshal()
if err1 != nil {
// should not happen
t.params.Logger.Errorw("failed to parse last answer", err)
return
t.params.Logger.Errorw("failed to parse last answer", err1)
return unmatched, err1
}
for i := len(parsedAnswer.MediaDescriptions) - 1; i >= 0; i-- {
@@ -396,8 +476,8 @@ func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescripti
}
}
for i := len(parsed.MediaDescriptions) - 1; i >= 0; i-- {
media := parsed.MediaDescriptions[i]
for i := len(parsedOffer.MediaDescriptions) - 1; i >= 0; i-- {
media := parsedOffer.MediaDescriptions[i]
if media.MediaName.Media == mediaType {
mid, _ := media.Attribute(sdp.AttrKeyMID)
if mid == lastMatchedMid {
@@ -410,56 +490,76 @@ func (t *TransportManager) GetUnmatchMediaForOffer(offer webrtc.SessionDescripti
return
}
func (t *TransportManager) LastPublisherOffer() webrtc.SessionDescription {
if sd := t.lastPublisherOffer.Load(); sd != nil {
return sd.(webrtc.SessionDescription)
}
return webrtc.SessionDescription{}
func (t *TransportManager) LastPublisherOffer() *webrtc.SessionDescription {
return t.publisher.CurrentRemoteDescription()
}
func (t *TransportManager) HandleOffer(offer webrtc.SessionDescription, shouldPend bool) error {
func (t *TransportManager) LastPublisherOfferPending() *webrtc.SessionDescription {
return t.publisher.PendingRemoteDescription()
}
func (t *TransportManager) HandleOffer(offer webrtc.SessionDescription, offerId uint32, shouldPend bool) error {
t.lock.Lock()
if shouldPend {
t.pendingOfferPublisher = &offer
t.pendingOfferIdPublisher = offerId
t.lock.Unlock()
return nil
}
t.lock.Unlock()
t.lastPublisherOffer.Store(offer)
return t.publisher.HandleRemoteDescription(offer)
return t.publisher.HandleRemoteDescription(offer, offerId)
}
func (t *TransportManager) GetAnswer() (webrtc.SessionDescription, error) {
answer, err := t.publisher.GetAnswer()
if err == nil {
t.lastPublisherAnswer.Store(answer)
}
return answer, err
func (t *TransportManager) GetAnswer() (webrtc.SessionDescription, uint32, error) {
return t.publisher.GetAnswer()
}
func (t *TransportManager) GetPublisherICESessionUfrag() (string, error) {
return t.publisher.GetICESessionUfrag()
}
func (t *TransportManager) HandleICETrickleSDPFragment(sdpFragment string) error {
return t.publisher.HandleICETrickleSDPFragment(sdpFragment)
}
func (t *TransportManager) HandleICERestartSDPFragment(sdpFragment string) (string, error) {
return t.publisher.HandleICERestartSDPFragment(sdpFragment)
}
func (t *TransportManager) ProcessPendingPublisherOffer() {
t.lock.Lock()
pendingOffer := t.pendingOfferPublisher
t.pendingOfferPublisher = nil
pendingOfferId := t.pendingOfferIdPublisher
t.pendingOfferIdPublisher = 0
t.lock.Unlock()
if pendingOffer != nil {
t.HandleOffer(*pendingOffer, false)
t.HandleOffer(*pendingOffer, pendingOfferId, false)
}
}
func (t *TransportManager) HandleAnswer(answer webrtc.SessionDescription) {
t.subscriber.HandleRemoteDescription(answer)
func (t *TransportManager) HandleAnswer(answer webrtc.SessionDescription, answerId uint32) {
t.subscriber.HandleRemoteDescription(answer, answerId)
}
// AddICECandidate adds candidates for remote peer
func (t *TransportManager) AddICECandidate(candidate webrtc.ICECandidateInit, target livekit.SignalTarget) {
switch target {
case livekit.SignalTarget_PUBLISHER:
t.publisher.AddICECandidate(candidate)
if t.publisher != nil {
t.publisher.AddICECandidate(candidate)
} else {
t.params.Logger.Warnw("ice candidate for publisher, but no peer connection", nil, "candidate", candidate)
}
case livekit.SignalTarget_SUBSCRIBER:
t.subscriber.AddICECandidate(candidate)
if t.subscriber != nil {
t.subscriber.AddICECandidate(candidate)
} else {
t.params.Logger.Warnw("ice candidate for subscriber, but no peer connection", nil, "candidate", candidate)
}
default:
err := errors.New("unknown signal target")
t.params.Logger.Errorw("ice candidate for unknown signal target", err, "target", target)
@@ -467,7 +567,11 @@ func (t *TransportManager) AddICECandidate(candidate webrtc.ICECandidateInit, ta
}
func (t *TransportManager) NegotiateSubscriber(force bool) {
t.subscriber.Negotiate(force)
if t.subscriber != nil {
t.subscriber.Negotiate(force)
} else {
t.publisher.Negotiate(force)
}
}
func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason) {
@@ -478,12 +582,16 @@ func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason)
)
switch reason {
case livekit.ReconnectReason_RR_PUBLISHER_FAILED:
resetShortConnection = true
isShort, duration = t.publisher.IsShortConnection(time.Now())
if t.publisher != nil {
resetShortConnection = true
isShort, duration = t.publisher.IsShortConnection(time.Now())
}
case livekit.ReconnectReason_RR_SUBSCRIBER_FAILED:
resetShortConnection = true
isShort, duration = t.subscriber.IsShortConnection(time.Now())
if t.subscriber != nil {
resetShortConnection = true
isShort, duration = t.subscriber.IsShortConnection(time.Now())
}
}
if isShort {
@@ -495,15 +603,23 @@ func (t *TransportManager) HandleClientReconnect(reason livekit.ReconnectReason)
}
if resetShortConnection {
t.publisher.ResetShortConnOnICERestart()
t.subscriber.ResetShortConnOnICERestart()
if t.publisher != nil {
t.publisher.ResetShortConnOnICERestart()
}
if t.subscriber != nil {
t.subscriber.ResetShortConnOnICERestart()
}
}
}
func (t *TransportManager) ICERestart(iceConfig *livekit.ICEConfig) error {
t.SetICEConfig(iceConfig)
return t.subscriber.ICERestart()
if t.subscriber != nil {
return t.subscriber.ICERestart()
}
return nil
}
func (t *TransportManager) OnICEConfigChanged(f func(iceConfig *livekit.ICEConfig)) {
@@ -555,8 +671,12 @@ func (t *TransportManager) configureICE(iceConfig *livekit.ICEConfig, reset bool
t.mediaLossProxy.OnMediaLossUpdate(nil)
}
t.publisher.SetPreferTCP(iceConfig.PreferencePublisher == livekit.ICECandidateType_ICT_TCP)
t.subscriber.SetPreferTCP(iceConfig.PreferenceSubscriber == livekit.ICECandidateType_ICT_TCP)
if t.publisher != nil {
t.publisher.SetPreferTCP(iceConfig.PreferencePublisher == livekit.ICECandidateType_ICT_TCP)
}
if t.subscriber != nil {
t.subscriber.SetPreferTCP(iceConfig.PreferenceSubscriber == livekit.ICECandidateType_ICT_TCP)
}
if onICEConfigChanged != nil {
onICEConfigChanged(iceConfig)
@@ -570,6 +690,10 @@ func (t *TransportManager) SubscriberAsPrimary() bool {
func (t *TransportManager) GetICEConnectionInfo() []*types.ICEConnectionInfo {
infos := make([]*types.ICEConnectionInfo, 0, 2)
for _, pc := range []*PCTransport{t.publisher, t.subscriber} {
if pc == nil {
continue
}
info := pc.GetICEConnectionInfo()
if info.HasCandidates() {
infos = append(infos, info)
@@ -578,13 +702,43 @@ func (t *TransportManager) GetICEConnectionInfo() []*types.ICEConnectionInfo {
return infos
}
func (t *TransportManager) getTransport(isPrimary bool) *PCTransport {
pcTransport := t.publisher
if (isPrimary && t.params.SubscriberAsPrimary) || (!isPrimary && !t.params.SubscriberAsPrimary) {
pcTransport = t.subscriber
}
func (t *TransportManager) GetDataTrackTransport() types.DataTrackTransport {
return t.getTransport(true)
}
return pcTransport
func (t *TransportManager) getTransport(isPrimary bool) *PCTransport {
switch {
case t.publisher == nil:
return t.subscriber
case t.subscriber == nil:
return t.publisher
default:
pcTransport := t.publisher
if (isPrimary && t.params.SubscriberAsPrimary) || (!isPrimary && !t.params.SubscriberAsPrimary) {
pcTransport = t.subscriber
}
return pcTransport
}
}
func (t *TransportManager) getLowestPriorityConnectionType() types.ICEConnectionType {
switch {
case t.publisher == nil:
return t.subscriber.GetICEConnectionType()
case t.subscriber == nil:
return t.publisher.GetICEConnectionType()
default:
ctype := t.publisher.GetICEConnectionType()
if stype := t.subscriber.GetICEConnectionType(); stype > ctype {
ctype = stype
}
return ctype
}
}
func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
@@ -614,6 +768,8 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
return
}
lowestPriorityConnectionType := t.getLowestPriorityConnectionType()
//
// Checking only `PreferenceSubscriber` field although any connection failure (PUBLISHER OR SUBSCRIBER) will
// flow through here.
@@ -621,13 +777,36 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
// As both transports are switched to the same type on any failure, checking just subscriber should be fine.
//
getNext := func(ic *livekit.ICEConfig) livekit.ICECandidateType {
if ic.PreferenceSubscriber == livekit.ICECandidateType_ICT_NONE && t.params.ClientInfo.SupportsICETCP() && t.canUseICETCP() {
return livekit.ICECandidateType_ICT_TCP
} else if ic.PreferenceSubscriber != livekit.ICECandidateType_ICT_TLS && t.params.TURNSEnabled {
return livekit.ICECandidateType_ICT_TLS
} else {
return livekit.ICECandidateType_ICT_NONE
switch lowestPriorityConnectionType {
case types.ICEConnectionTypeUDP:
// try ICE/TCP if ICE/UDP failed
if ic.PreferenceSubscriber == livekit.ICECandidateType_ICT_NONE {
if t.params.ClientInfo.SupportsICETCP() && t.canUseICETCP() {
return livekit.ICECandidateType_ICT_TCP
} else if t.params.TURNSEnabled {
// fallback to TURN/TLS if TCP is not supported
return livekit.ICECandidateType_ICT_TLS
}
}
case types.ICEConnectionTypeTCP:
// try TURN/TLS if ICE/TCP failed,
// the configuration could have been ICT_NONE or ICT_TCP,
// in either case, fallback to TURN/TLS
if t.params.TURNSEnabled {
return livekit.ICECandidateType_ICT_TLS
} else {
// keep the current config
return ic.PreferenceSubscriber
}
case types.ICEConnectionTypeTURN:
// TURN/TLS is the most permissive option, if that fails there is nowhere to go to
// the configuration could have been ICT_NONE or ICT_TLS,
// keep the current config
return ic.PreferenceSubscriber
}
return livekit.ICECandidateType_ICT_NONE
}
var preferNext livekit.ICECandidateType
@@ -672,7 +851,11 @@ func (t *TransportManager) handleConnectionFailed(isShortLived bool) {
}, false)
}
func (t *TransportManager) SetMigrateInfo(previousOffer, previousAnswer *webrtc.SessionDescription, dataChannels []*livekit.DataChannelInfo) {
func (t *TransportManager) SetMigrateInfo(
previousOffer *webrtc.SessionDescription,
previousAnswer *webrtc.SessionDescription,
dataChannels []*livekit.DataChannelInfo,
) {
t.lock.Lock()
t.pendingDataChannelsPublisher = make([]*livekit.DataChannelInfo, 0, len(dataChannels))
pendingDataChannelsSubscriber := make([]*livekit.DataChannelInfo, 0, len(dataChannels))
@@ -691,7 +874,11 @@ func (t *TransportManager) SetMigrateInfo(previousOffer, previousAnswer *webrtc.
}
}
t.subscriber.SetPreviousSdp(previousOffer, previousAnswer)
if t.params.UseSinglePeerConnection {
t.publisher.SetPreviousSdp(previousAnswer, previousOffer)
} else {
t.subscriber.SetPreviousSdp(previousOffer, previousAnswer)
}
}
func (t *TransportManager) ProcessPendingPublisherDataChannels() {
@@ -710,7 +897,16 @@ func (t *TransportManager) ProcessPendingPublisherDataChannels() {
dcExisting bool
err error
)
if ci.Label == LossyDataChannel {
switch ci.Label {
case ReliableDataChannel:
id := uint16(ci.GetId())
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(ReliableDataChannel, &webrtc.DataChannelInit{
Ordered: &ordered,
Negotiated: &negotiated,
ID: &id,
})
case LossyDataChannel:
ordered = false
retransmits := uint16(0)
id := uint16(ci.GetId())
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(LossyDataChannel, &webrtc.DataChannelInit{
@@ -719,12 +915,15 @@ func (t *TransportManager) ProcessPendingPublisherDataChannels() {
Negotiated: &negotiated,
ID: &id,
})
} else if ci.Label == ReliableDataChannel {
case DataTrackDataChannel:
ordered = false
retransmits := uint16(0)
id := uint16(ci.GetId())
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(ReliableDataChannel, &webrtc.DataChannelInit{
Ordered: &ordered,
Negotiated: &negotiated,
ID: &id,
dcLabel, dcID, dcExisting, err = t.publisher.CreateDataChannelIfEmpty(DataTrackDataChannel, &webrtc.DataChannelInit{
Ordered: &ordered,
MaxRetransmits: &retransmits,
Negotiated: &negotiated,
ID: &id,
})
}
if err != nil {
@@ -755,7 +954,11 @@ func (t *TransportManager) onMediaLossUpdate(loss uint8) {
t.lock.Unlock()
t.params.Logger.Infow("udp connection unstable, switch to tcp", "signalingRTT", t.signalingRTT)
t.params.SubscriberHandler.OnFailed(true, t.subscriber.GetICEConnectionInfo())
if t.params.UseSinglePeerConnection {
t.params.PublisherHandler.OnFailed(true, t.publisher.GetICEConnectionInfo())
} else {
t.params.SubscriberHandler.OnFailed(true, t.subscriber.GetICEConnectionInfo())
}
return
}
}
@@ -767,8 +970,12 @@ func (t *TransportManager) UpdateSignalingRTT(rtt uint32) {
t.lock.Lock()
t.signalingRTT = rtt
t.lock.Unlock()
t.publisher.SetSignalingRTT(rtt)
t.subscriber.SetSignalingRTT(rtt)
if t.publisher != nil {
t.publisher.SetSignalingRTT(rtt)
}
if t.subscriber != nil {
t.subscriber.SetSignalingRTT(rtt)
}
// TODO: considering using tcp rtt to calculate ice connection cost, if ice connection can't be established
// within 5 * tcp rtt(at least 5s), means udp traffic might be block/dropped, switch to tcp.
@@ -813,13 +1020,25 @@ func (t *TransportManager) SetSignalSourceValid(valid bool) {
}
func (t *TransportManager) SetSubscriberAllowPause(allowPause bool) {
t.subscriber.SetAllowPauseOfStreamAllocator(allowPause)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
t.publisher.SetAllowPauseOfStreamAllocator(allowPause)
} else {
t.subscriber.SetAllowPauseOfStreamAllocator(allowPause)
}
}
func (t *TransportManager) SetSubscriberChannelCapacity(channelCapacity int64) {
t.subscriber.SetChannelCapacityOfStreamAllocator(channelCapacity)
if t.params.UseOneShotSignallingMode || t.params.UseSinglePeerConnection {
t.publisher.SetChannelCapacityOfStreamAllocator(channelCapacity)
} else {
t.subscriber.SetChannelCapacityOfStreamAllocator(channelCapacity)
}
}
func (t *TransportManager) hasRecentSignalLocked() bool {
return time.Since(t.lastSignalAt) < PingTimeoutSeconds*time.Second
}
func (t *TransportManager) RTPStreamPublished(ssrc uint32, mid, rid string) {
t.publisher.RTPStreamPublished(ssrc, mid, rid)
}
+149 -103
View File
@@ -18,30 +18,63 @@ package types
import (
"fmt"
"slices"
"strings"
"sync"
"github.com/pion/ice/v4"
"github.com/pion/webrtc/v4"
"golang.org/x/exp/slices"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
)
type ICEConnectionType string
type ICEConnectionType int
const (
ICEConnectionTypeUDP ICEConnectionType = "udp"
ICEConnectionTypeTCP ICEConnectionType = "tcp"
ICEConnectionTypeTURN ICEConnectionType = "turn"
ICEConnectionTypeUnknown ICEConnectionType = "unknown"
// this is in ICE priority highest -> lowest ordering
// WARNING: Keep this ordering as it is used to find lowest priority connection type.
ICEConnectionTypeUnknown ICEConnectionType = iota
ICEConnectionTypeUDP
ICEConnectionTypeTCP
ICEConnectionTypeTURN
)
func (i ICEConnectionType) String() string {
switch i {
case ICEConnectionTypeUnknown:
return "unknown"
case ICEConnectionTypeUDP:
return "udp"
case ICEConnectionTypeTCP:
return "tcp"
case ICEConnectionTypeTURN:
return "turn"
default:
return "unknown"
}
}
func (i ICEConnectionType) ReporterType() roomobs.ConnectionType {
switch i {
case ICEConnectionTypeUnknown:
return roomobs.ConnectionTypeUndefined
case ICEConnectionTypeUDP:
return roomobs.ConnectionTypeUDP
case ICEConnectionTypeTCP:
return roomobs.ConnectionTypeTCP
case ICEConnectionTypeTURN:
return roomobs.ConnectionTypeTurn
default:
return roomobs.ConnectionTypeUndefined
}
}
// --------------------------------------------
type ICECandidateExtended struct {
// only one of local or remote is set. This is due to type foo in Pion
Local *webrtc.ICECandidate
Remote ice.Candidate
Candidate *webrtc.ICECandidate
SelectedOrder int
Filtered bool
Trickle bool
@@ -60,6 +93,15 @@ func (i *ICEConnectionInfo) HasCandidates() bool {
return len(i.Local) > 0 || len(i.Remote) > 0
}
func ICEConnectionInfosType(infos []*ICEConnectionInfo) ICEConnectionType {
for _, info := range infos {
if info.Type != ICEConnectionTypeUnknown {
return info.Type
}
}
return ICEConnectionTypeUnknown
}
// --------------------------------------------
type ICEConnectionDetails struct {
@@ -91,7 +133,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
}
for _, c := range d.Local {
info.Local = append(info.Local, &ICECandidateExtended{
Local: c.Local,
Candidate: c.Candidate,
Filtered: c.Filtered,
SelectedOrder: c.SelectedOrder,
Trickle: c.Trickle,
@@ -99,7 +141,7 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
}
for _, c := range d.Remote {
info.Remote = append(info.Remote, &ICECandidateExtended{
Remote: c.Remote,
Candidate: c.Candidate,
Filtered: c.Filtered,
SelectedOrder: c.SelectedOrder,
Trickle: c.Trickle,
@@ -108,19 +150,26 @@ func (d *ICEConnectionDetails) GetInfo() *ICEConnectionInfo {
return info
}
func (d *ICEConnectionDetails) GetConnectionType() ICEConnectionType {
d.lock.Lock()
defer d.lock.Unlock()
return d.Type
}
func (d *ICEConnectionDetails) AddLocalCandidate(c *webrtc.ICECandidate, filtered, trickle bool) {
d.lock.Lock()
defer d.lock.Unlock()
compFn := func(e *ICECandidateExtended) bool {
return isCandidateEqualTo(e.Local, c)
return isCandidateEqualTo(e.Candidate, c)
}
if slices.ContainsFunc(d.Local, compFn) {
return
}
d.Local = append(d.Local, &ICECandidateExtended{
Local: c,
Filtered: filtered,
Trickle: trickle,
Candidate: c,
Filtered: filtered,
Trickle: trickle,
})
}
@@ -135,24 +184,30 @@ func (d *ICEConnectionDetails) AddLocalICECandidate(c ice.Candidate, filtered, t
}
func (d *ICEConnectionDetails) AddRemoteCandidate(c webrtc.ICECandidateInit, filtered, trickle, canUpdate bool) {
candidate, err := unmarshalICECandidate(c)
iceCandidate, err := unmarshalICECandidate(c)
if err != nil {
d.logger.Errorw("could not unmarshal candidate", err, "candidate", c)
return
}
d.AddRemoteICECandidate(candidate, filtered, trickle, canUpdate)
d.AddRemoteICECandidate(iceCandidate, filtered, trickle, canUpdate)
}
func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, filtered, trickle, canUpdate bool) {
if candidate == nil {
func (d *ICEConnectionDetails) AddRemoteICECandidate(iceCandidate ice.Candidate, filtered, trickle, canUpdate bool) {
if iceCandidate == nil {
// end-of-candidates candidate
return
}
candidate, err := unmarshalCandidate(iceCandidate)
if err != nil {
d.logger.Errorw("could not unmarshal ice candidate", err, "candidate", iceCandidate)
return
}
d.lock.Lock()
defer d.lock.Unlock()
indexFn := func(e *ICECandidateExtended) bool {
return isICECandidateEqualTo(e.Remote, candidate)
return isCandidateEqualTo(e.Candidate, candidate)
}
if idx := slices.IndexFunc(d.Remote, indexFn); idx != -1 {
if canUpdate {
@@ -162,10 +217,11 @@ func (d *ICEConnectionDetails) AddRemoteICECandidate(candidate ice.Candidate, fi
return
}
d.Remote = append(d.Remote, &ICECandidateExtended{
Remote: candidate,
Filtered: filtered,
Trickle: trickle,
Candidate: candidate,
Filtered: filtered,
Trickle: trickle,
})
d.updateConnectionTypeLocked()
}
func (d *ICEConnectionDetails) Clear() {
@@ -183,56 +239,78 @@ func (d *ICEConnectionDetails) SetSelectedPair(pair *webrtc.ICECandidatePair) {
d.selectedCount++
remoteIdx := slices.IndexFunc(d.Remote, func(e *ICECandidateExtended) bool {
return isICECandidateEqualToCandidate(e.Remote, pair.Remote)
return isCandidateEqualTo(e.Candidate, pair.Remote)
})
if remoteIdx < 0 {
// it's possible for prflx candidates to be generated by Pion, we'll add them
candidate, err := unmarshalICECandidate(pair.Remote.ToJSON())
if err != nil {
d.logger.Errorw("could not unmarshal remote candidate", err, "candidate", pair.Remote)
return
}
if candidate == nil {
return
}
d.Remote = append(d.Remote, &ICECandidateExtended{
Remote: candidate,
Filtered: false,
Trickle: true,
Candidate: pair.Remote,
Filtered: false,
Trickle: false,
})
remoteIdx = len(d.Remote) - 1
}
remote := d.Remote[remoteIdx]
remote.SelectedOrder = d.selectedCount
d.Remote[remoteIdx].SelectedOrder = d.selectedCount
d.updateConnectionTypeLocked()
localIdx := slices.IndexFunc(d.Local, func(e *ICECandidateExtended) bool {
return isCandidateEqualTo(e.Local, pair.Local)
return isCandidateEqualTo(e.Candidate, pair.Local)
})
if localIdx < 0 {
d.logger.Errorw("could not match local candidate", nil, "local", pair.Local)
// should not happen
return
}
local := d.Local[localIdx]
local.SelectedOrder = d.selectedCount
d.Local[localIdx].SelectedOrder = d.selectedCount
}
d.Type = ICEConnectionTypeUDP
if pair.Remote.Protocol == webrtc.ICEProtocolTCP {
func (d *ICEConnectionDetails) updateConnectionTypeLocked() {
highestSelectedOrder := -1
var selectedRemoteCandidate *ICECandidateExtended
for _, remote := range d.Remote {
if remote.SelectedOrder == 0 {
continue
}
if remote.SelectedOrder > highestSelectedOrder {
highestSelectedOrder = remote.SelectedOrder
selectedRemoteCandidate = remote
}
}
if selectedRemoteCandidate == nil {
return
}
remoteCandidate := selectedRemoteCandidate.Candidate
switch remoteCandidate.Protocol {
case webrtc.ICEProtocolUDP:
d.Type = ICEConnectionTypeUDP
case webrtc.ICEProtocolTCP:
d.Type = ICEConnectionTypeTCP
}
if pair.Remote.Typ == webrtc.ICECandidateTypeRelay {
switch remoteCandidate.Typ {
case webrtc.ICECandidateTypeRelay:
d.Type = ICEConnectionTypeTURN
} else if pair.Remote.Typ == webrtc.ICECandidateTypePrflx {
case webrtc.ICECandidateTypePrflx:
// if the remote relay candidate pings us *before* we get a relay candidate,
// Pion would have created a prflx candidate with the same address as the relay candidate.
// to report an accurate connection type, we'll compare to see if existing relay candidates match
for _, other := range d.Remote {
or := other.Remote
if or.Type() == ice.CandidateTypeRelay &&
pair.Remote.Address == or.Address() &&
pair.Remote.Port == uint16(or.Port()) &&
pair.Remote.Protocol.String() == or.NetworkType().NetworkShort() {
or := other.Candidate
if or.Typ == webrtc.ICECandidateTypeRelay &&
remoteCandidate.Address == or.Address &&
// NOTE: port is not compared as relayed address reported by TURN ALLOCATE from
// pion/turn server -> client and later sent from client -> server via ICE Trickle does not
// match port of `prflx` candidate learnt via TURN path. TODO-INVESTIGATE: how and why doesn't
// port match?
// remoteCandidate.Port == or.Port &&
remoteCandidate.Protocol == or.Protocol {
d.Type = ICEConnectionTypeTURN
break
}
}
}
@@ -258,39 +336,6 @@ func isCandidateEqualTo(c1, c2 *webrtc.ICECandidate) bool {
c1.TCPType == c2.TCPType
}
func isICECandidateEqualTo(c1, c2 ice.Candidate) bool {
if c1 == nil && c2 == nil {
return true
}
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
return false
}
return c1.Type() == c2.Type() &&
c1.NetworkType() == c2.NetworkType() &&
c1.Address() == c2.Address() &&
c1.Port() == c2.Port() &&
c1.Foundation() == c2.Foundation() &&
c1.Priority() == c2.Priority() &&
c1.RelatedAddress().Equal(c2.RelatedAddress()) &&
c1.TCPType() == c2.TCPType()
}
func isICECandidateEqualToCandidate(c1 ice.Candidate, c2 *webrtc.ICECandidate) bool {
if c1 == nil && c2 == nil {
return true
}
if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) {
return false
}
return c1.Type().String() == c2.Typ.String() &&
c1.NetworkType().NetworkShort() == c2.Protocol.String() &&
c1.Address() == c2.Address &&
c1.Port() == int(c2.Port) &&
c1.Foundation() == c2.Foundation &&
c1.Priority() == c2.Priority &&
c1.TCPType().String() == c2.TCPType
}
func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
candidateValue := strings.TrimPrefix(c.Candidate, "candidate:")
if candidateValue == "" {
@@ -306,28 +351,14 @@ func unmarshalICECandidate(c webrtc.ICECandidateInit) (ice.Candidate, error) {
}
func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
var typ webrtc.ICECandidateType
switch i.Type() {
case ice.CandidateTypeHost:
typ = webrtc.ICECandidateTypeHost
case ice.CandidateTypeServerReflexive:
typ = webrtc.ICECandidateTypeSrflx
case ice.CandidateTypePeerReflexive:
typ = webrtc.ICECandidateTypePrflx
case ice.CandidateTypeRelay:
typ = webrtc.ICECandidateTypeRelay
default:
return nil, fmt.Errorf("unknown candidate type: %s", i.Type())
typ, err := convertTypeFromICE(i.Type())
if err != nil {
return nil, err
}
var protocol webrtc.ICEProtocol
switch strings.ToLower(i.NetworkType().NetworkShort()) {
case "udp":
protocol = webrtc.ICEProtocolUDP
case "tcp":
protocol = webrtc.ICEProtocolTCP
default:
return nil, fmt.Errorf("unknown network type: %s", i.NetworkType())
protocol, err := webrtc.NewICEProtocol(i.NetworkType().NetworkShort())
if err != nil {
return nil, err
}
c := webrtc.ICECandidate{
@@ -349,6 +380,21 @@ func unmarshalCandidate(i ice.Candidate) (*webrtc.ICECandidate, error) {
return &c, nil
}
func convertTypeFromICE(t ice.CandidateType) (webrtc.ICECandidateType, error) {
switch t {
case ice.CandidateTypeHost:
return webrtc.ICECandidateTypeHost, nil
case ice.CandidateTypeServerReflexive:
return webrtc.ICECandidateTypeSrflx, nil
case ice.CandidateTypePeerReflexive:
return webrtc.ICECandidateTypePrflx, nil
case ice.CandidateTypeRelay:
return webrtc.ICECandidateTypeRelay, nil
default:
return webrtc.ICECandidateType(t), fmt.Errorf("unknown ice candidate type: %s", t)
}
}
func IsCandidateMDNS(candidate webrtc.ICECandidateInit) bool {
c, err := unmarshalICECandidate(candidate)
if err != nil {
@@ -364,5 +410,5 @@ func IsICECandidateMDNS(candidate ice.Candidate) bool {
return false
}
return strings.HasSuffix(candidate.Address(), ".local")
return strings.HasSuffix(candidate.Address(), ".local") || strings.HasSuffix(candidate.Address(), ".invalid")
}
+361 -48
View File
@@ -22,15 +22,20 @@ import (
"github.com/pion/webrtc/v4"
"github.com/livekit/protocol/auth"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
"github.com/livekit/protocol/utils"
"github.com/livekit/livekit-server/pkg/routing"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/sfu/pacer"
"github.com/livekit/livekit-server/pkg/telemetry"
"google.golang.org/protobuf/proto"
)
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
@@ -111,6 +116,8 @@ const (
ParticipantCloseReasonRoomClosed
ParticipantCloseReasonUserUnavailable
ParticipantCloseReasonUserRejected
ParticipantCloseReasonMoveFailed
ParticipantCloseReasonAgentError
)
func (p ParticipantCloseReason) String() string {
@@ -169,6 +176,10 @@ func (p ParticipantCloseReason) String() string {
return "USER_UNAVAILABLE"
case ParticipantCloseReasonUserRejected:
return "USER_REJECTED"
case ParticipantCloseReasonMoveFailed:
return "MOVE_FAILED"
case ParticipantCloseReasonAgentError:
return "AGENT_ERROR"
default:
return fmt.Sprintf("%d", int(p))
}
@@ -184,7 +195,7 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
// expected to be connected but is not
return livekit.DisconnectReason_JOIN_FAILURE
case ParticipantCloseReasonPeerConnectionDisconnected:
return livekit.DisconnectReason_STATE_MISMATCH
return livekit.DisconnectReason_CONNECTION_TIMEOUT
case ParticipantCloseReasonDuplicateIdentity, ParticipantCloseReasonStale:
return livekit.DisconnectReason_DUPLICATE_IDENTITY
case ParticipantCloseReasonMigrationRequested, ParticipantCloseReasonMigrationComplete, ParticipantCloseReasonSimulateMigration:
@@ -195,7 +206,8 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
return livekit.DisconnectReason_ROOM_DELETED
case ParticipantCloseReasonSimulateNodeFailure, ParticipantCloseReasonSimulateServerLeave:
return livekit.DisconnectReason_SERVER_SHUTDOWN
case ParticipantCloseReasonNegotiateFailed, ParticipantCloseReasonPublicationError, ParticipantCloseReasonSubscriptionError, ParticipantCloseReasonDataChannelError, ParticipantCloseReasonMigrateCodecMismatch:
case ParticipantCloseReasonNegotiateFailed, ParticipantCloseReasonPublicationError, ParticipantCloseReasonSubscriptionError,
ParticipantCloseReasonDataChannelError, ParticipantCloseReasonMigrateCodecMismatch, ParticipantCloseReasonMoveFailed:
return livekit.DisconnectReason_STATE_MISMATCH
case ParticipantCloseReasonSignalSourceClose:
return livekit.DisconnectReason_SIGNAL_CLOSE
@@ -205,12 +217,31 @@ func (p ParticipantCloseReason) ToDisconnectReason() livekit.DisconnectReason {
return livekit.DisconnectReason_USER_UNAVAILABLE
case ParticipantCloseReasonUserRejected:
return livekit.DisconnectReason_USER_REJECTED
case ParticipantCloseReasonAgentError:
return livekit.DisconnectReason_AGENT_ERROR
default:
// the other types will map to unknown reason
return livekit.DisconnectReason_UNKNOWN_REASON
}
}
// IsIntentionalDisconnect reports whether a disconnect reason represents an
// intentional/expected closure (client leaving, admin action, room teardown,
// migration, etc.) as opposed to a connection failure.
func IsIntentionalDisconnect(reason livekit.DisconnectReason) bool {
switch reason {
case livekit.DisconnectReason_CLIENT_INITIATED,
livekit.DisconnectReason_SERVER_SHUTDOWN,
livekit.DisconnectReason_DUPLICATE_IDENTITY,
livekit.DisconnectReason_MIGRATION,
livekit.DisconnectReason_PARTICIPANT_REMOVED,
livekit.DisconnectReason_ROOM_DELETED,
livekit.DisconnectReason_ROOM_CLOSED:
return true
}
return false
}
// ---------------------------------------------
type SignallingCloseReason int
@@ -258,6 +289,12 @@ func (s SignallingCloseReason) String() string {
}
}
// ---------------------------------------------
const (
ParticipantCloseKeyNormal = "normal"
ParticipantCloseKeyWHIP = "whip"
)
// ---------------------------------------------
//counterfeiter:generate . Participant
@@ -268,18 +305,26 @@ type Participant interface {
ConnectedAt() time.Time
CloseReason() ParticipantCloseReason
Kind() livekit.ParticipantInfo_Kind
KindDetails() []livekit.ParticipantInfo_KindDetail
IsRecorder() bool
IsDependent() bool
IsAgent() bool
GetLogger() logger.Logger
CanSkipBroadcast() bool
Version() utils.TimedVersion
ToProto() *livekit.ParticipantInfo
ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion)
IsPublisher() bool
GetPublishedTrack(trackID livekit.TrackID) MediaTrack
GetPublishedTracks() []MediaTrack
RemovePublishedTrack(track MediaTrack, isExpectedToResume bool, shouldClose bool)
RemovePublishedTrack(track MediaTrack, isExpectedToResume bool)
GetPublishedDataTracks() []DataTrack
GetPublishedDataTrack(handle uint16) DataTrack
RemovePublishedDataTrack(track DataTrack)
GetAudioLevel() (smoothedLevel float64, active bool)
@@ -290,7 +335,11 @@ type Participant interface {
// permissions
Hidden() bool
MigrateState() MigrateState
Close(sendLeave bool, reason ParticipantCloseReason, isExpectedToResume bool) error
IsClosed() bool
IsDisconnected() bool
SubscriptionPermission() (*livekit.SubscriptionPermission, utils.TimedVersion)
@@ -301,9 +350,11 @@ type Participant interface {
resolverBySid func(participantID livekit.ParticipantID) LocalParticipant,
) error
DebugInfo() map[string]interface{}
DebugInfo() map[string]any
OnMetrics(callback func(Participant, *livekit.DataPacket))
HandleReceivedDataTrackMessage([]byte, *datatrack.Packet, int64)
GetParticipantListener() ParticipantListener
}
// -------------------------------------------------------
@@ -313,22 +364,51 @@ type AddTrackParams struct {
Red bool
}
type MoveToRoomParams struct {
RoomName livekit.RoomName
ParticipantID livekit.ParticipantID
Listener LocalParticipantListener
Helper LocalParticipantHelper
}
type DataMessageCache struct {
Data []byte
SenderID livekit.ParticipantID
Seq uint32
DestIdentities []livekit.ParticipantIdentity
}
//counterfeiter:generate . LocalParticipantHelper
type LocalParticipantHelper interface {
ResolveMediaTrack(LocalParticipant, livekit.TrackID) MediaResolverResult
ResolveDataTrack(LocalParticipant, livekit.TrackID) DataResolverResult
GetParticipantInfo(pID livekit.ParticipantID) *livekit.ParticipantInfo
GetRegionSettings(ip string) *livekit.RegionSettings
GetSubscriberForwarderState(p LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)
ShouldRegressCodec() bool
GetCachedReliableDataMessage(seqs map[livekit.ParticipantID]uint32) []*DataMessageCache
}
//counterfeiter:generate . LocalParticipant
type LocalParticipant interface {
Participant
ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion)
TelemetryGuard() *telemetry.ReferenceGuard
GetTelemetryListener() ParticipantTelemetryListener
// getters
GetCountry() string
GetTrailer() []byte
GetLogger() logger.Logger
GetLoggerResolver() logger.DeferredFieldResolver
GetReporter() roomobs.ParticipantSessionReporter
GetReporterResolver() roomobs.ParticipantReporterResolver
GetAdaptiveStream() bool
ProtocolVersion() ProtocolVersion
SupportsSyncStreamID() bool
SupportsTransceiverReuse() bool
IsClosed() bool
SupportsTransceiverReuse(mt MediaTrack) bool
IsUsingSinglePeerConnection() bool
IsReady() bool
IsDisconnected() bool
ActiveAt() time.Time
Disconnected() <-chan struct{}
IsIdle() bool
SubscriberAsPrimary() bool
@@ -340,15 +420,19 @@ type LocalParticipant interface {
GetICEConnectionInfo() []*ICEConnectionInfo
HasConnected() bool
GetEnabledPublishCodecs() []*livekit.Codec
GetPublisherICESessionUfrag() (string, error)
SupportsMoving() error
GetLastReliableSequence(migrateOut bool) uint32
SetResponseSink(sink routing.MessageSink)
SwapResponseSink(sink routing.MessageSink, reason SignallingCloseReason)
GetResponseSink() routing.MessageSink
CloseSignalConnection(reason SignallingCloseReason)
UpdateLastSeenSignal()
SetSignalSourceValid(valid bool)
HandleSignalSourceClose()
// updates
CheckMetadataLimits(name string, metadata string, attributes map[string]string) error
UpdateMetadata(update *livekit.UpdateParticipantMetadata, fromAdmin bool) error
SetName(name string)
SetMetadata(metadata string)
SetAttributes(attributes map[string]string)
@@ -364,13 +448,15 @@ type LocalParticipant interface {
CanPublishData() bool
// PeerConnection
AddICECandidate(candidate webrtc.ICECandidateInit, target livekit.SignalTarget)
HandleOffer(sdp webrtc.SessionDescription) error
GetAnswer() (webrtc.SessionDescription, error)
HandleICETrickle(trickleRequest *livekit.TrickleRequest)
HandleOffer(sd *livekit.SessionDescription) error
GetAnswer() (webrtc.SessionDescription, uint32, error)
HandleICETrickleSDPFragment(sdpFragment string) error
HandleICERestartSDPFragment(sdpFragment string) (string, error)
AddTrack(req *livekit.AddTrackRequest)
SetTrackMuted(trackID livekit.TrackID, muted bool, fromAdmin bool) *livekit.TrackInfo
SetTrackMuted(mute *livekit.MuteTrackRequest, fromAdmin bool) *livekit.TrackInfo
HandleAnswer(sdp webrtc.SessionDescription)
HandleAnswer(sd *livekit.SessionDescription)
Negotiate(force bool)
ICERestart(iceConfig *livekit.ICEConfig)
AddTrackLocal(trackLocal webrtc.TrackLocal, params AddTrackParams) (*webrtc.RTPSender, *webrtc.RTPTransceiver, error)
@@ -380,11 +466,14 @@ type LocalParticipant interface {
WriteSubscriberRTCP(pkts []rtcp.Packet) error
// subscriptions
SubscribeToTrack(trackID livekit.TrackID)
SubscribeToTrack(trackID livekit.TrackID, isSync bool)
UnsubscribeFromTrack(trackID livekit.TrackID)
UpdateSubscribedTrackSettings(trackID livekit.TrackID, settings *livekit.UpdateTrackSettings)
GetSubscribedTracks() []SubscribedTrack
IsTrackNameSubscribed(publisherIdentity livekit.ParticipantIdentity, trackName string) bool
SubscribeToDataTrack(trackID livekit.TrackID)
UnsubscribeFromDataTrack(trackID livekit.TrackID)
UpdateDataTrackSubscriptionOptions(trackID livekit.TrackID, subscriptionOptions *livekit.DataTrackSubscriptionOptions)
Verify() bool
VerifySubscribeParticipantInfo(pID livekit.ParticipantID, version uint32)
// WaitUntilSubscribed waits until all subscriptions have been settled, or if the timeout
@@ -403,29 +492,18 @@ type LocalParticipant interface {
SendJoinResponse(joinResponse *livekit.JoinResponse) error
SendParticipantUpdate(participants []*livekit.ParticipantInfo) error
SendSpeakerUpdate(speakers []*livekit.SpeakerInfo, force bool) error
SendDataPacket(kind livekit.DataPacket_Kind, encoded []byte) error
SendDataMessage(kind livekit.DataPacket_Kind, data []byte, senderID livekit.ParticipantID, seq uint32) error
SendDataMessageUnlabeled(data []byte, useRaw bool, sender livekit.ParticipantIdentity) error
SendRoomUpdate(room *livekit.Room) error
SendConnectionQualityUpdate(update *livekit.ConnectionQualityUpdate) error
SubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool)
SendSubscriptionPermissionUpdate(publisherID livekit.ParticipantID, trackID livekit.TrackID, allowed bool) error
SendRefreshToken(token string) error
SendRequestResponse(requestResponse *livekit.RequestResponse) error
HandleReconnectAndSendResponse(reconnectReason livekit.ReconnectReason, reconnectResponse *livekit.ReconnectResponse) error
IssueFullReconnect(reason ParticipantCloseReason)
SendRoomMovedResponse(moved *livekit.RoomMovedResponse) error
SendDataTrackSubscriberHandles(handles map[uint32]*livekit.DataTrackSubscriberHandles_PublishedDataTrack) error
// callbacks
OnStateChange(func(p LocalParticipant, state livekit.ParticipantInfo_State))
OnMigrateStateChange(func(p LocalParticipant, migrateState MigrateState))
// OnTrackPublished - remote added a track
OnTrackPublished(func(LocalParticipant, MediaTrack))
// OnTrackUpdated - one of its publishedTracks changed in status
OnTrackUpdated(callback func(LocalParticipant, MediaTrack))
// OnTrackUnpublished - a track was unpublished
OnTrackUnpublished(callback func(LocalParticipant, MediaTrack))
// OnParticipantUpdate - metadata or permission is updated
OnParticipantUpdate(callback func(LocalParticipant))
OnDataPacket(callback func(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket))
OnSubscribeStatusChanged(fn func(publisherID livekit.ParticipantID, subscribed bool))
OnClose(callback func(LocalParticipant))
AddOnClose(key string, callback func(LocalParticipant))
OnClaimsChanged(callback func(LocalParticipant))
HandleReceiverReport(dt *sfu.DownTrack, report *rtcp.ReceiverReport)
@@ -434,13 +512,16 @@ type LocalParticipant interface {
MaybeStartMigration(force bool, onStart func()) bool
NotifyMigration()
SetMigrateState(s MigrateState)
MigrateState() MigrateState
SetMigrateInfo(
previousOffer, previousAnswer *webrtc.SessionDescription,
previousOffer *webrtc.SessionDescription,
previousAnswer *webrtc.SessionDescription,
mediaTracks []*livekit.TrackPublishedResponse,
dataChannels []*livekit.DataChannelInfo,
dataChannelReceiveState []*livekit.DataChannelReceiveState,
dataTracks []*livekit.PublishDataTrackResponse,
)
IsReconnect() bool
MoveToRoom(params MoveToRoomParams)
UpdateMediaRTT(rtt uint32)
UpdateSignalingRTT(rtt uint32)
@@ -454,6 +535,7 @@ type LocalParticipant interface {
OnICEConfigChanged(callback func(participant LocalParticipant, iceConfig *livekit.ICEConfig))
UpdateSubscribedQuality(nodeID livekit.NodeID, trackID livekit.TrackID, maxQualities []SubscribedCodecQuality) error
UpdateSubscribedAudioCodecs(nodeID livekit.NodeID, trackID livekit.TrackID, codecs []*livekit.SubscribedAudioCodec) error
UpdateMediaLoss(nodeID livekit.NodeID, trackID livekit.TrackID, fractionalLoss uint32) error
// down stream bandwidth management
@@ -465,8 +547,177 @@ type LocalParticipant interface {
GetDisableSenderReportPassThrough() bool
HandleMetrics(senderParticipantID livekit.ParticipantID, batch *livekit.MetricsBatch) error
HandleUpdateSubscriptions(
[]livekit.TrackID,
[]*livekit.ParticipantTracks,
bool,
)
HandleUpdateSubscriptionPermission(*livekit.SubscriptionPermission) error
HandleSyncState(*livekit.SyncState) error
HandleSimulateScenario(*livekit.SimulateScenario) error
HandleLeaveRequest(reason ParticipantCloseReason)
HandlePublishDataTrackRequest(*livekit.PublishDataTrackRequest)
HandleUnpublishDataTrackRequest(*livekit.UnpublishDataTrackRequest)
HandleUpdateDataSubscription(*livekit.UpdateDataSubscription)
HandleSignalMessage(msg proto.Message) error
PerformRpc(req *livekit.PerformRpcRequest, resultCh chan string, errorCh chan error)
GetDataTrackTransport() DataTrackTransport
ClearParticipantListener()
GetNextSubscribedDataTrackHandle() uint16
}
// ---------------------------------------------
//counterfeiter:generate . ParticipantListener
type ParticipantListener interface {
OnParticipantUpdate(Participant)
OnTrackPublished(Participant, MediaTrack)
OnTrackUpdated(Participant, MediaTrack)
OnTrackUnpublished(Participant, MediaTrack)
OnDataTrackPublished(Participant, DataTrack)
OnDataTrackUnpublished(Participant, DataTrack)
OnDataTrackMessage(Participant, []byte, *datatrack.Packet)
OnMetrics(Participant, *livekit.DataPacket)
}
var _ ParticipantListener = (*NullParticipantListener)(nil)
type NullParticipantListener struct{}
func (*NullParticipantListener) OnParticipantUpdate(Participant) {}
func (*NullParticipantListener) OnTrackPublished(Participant, MediaTrack) {}
func (*NullParticipantListener) OnTrackUpdated(Participant, MediaTrack) {}
func (*NullParticipantListener) OnTrackUnpublished(Participant, MediaTrack) {}
func (*NullParticipantListener) OnDataTrackPublished(Participant, DataTrack) {}
func (*NullParticipantListener) OnDataTrackUnpublished(Participant, DataTrack) {}
func (*NullParticipantListener) OnDataTrackMessage(Participant, []byte, *datatrack.Packet) {}
func (*NullParticipantListener) OnMetrics(Participant, *livekit.DataPacket) {}
// ---------------------------------------------
//counterfeiter:generate . LocalParticipantListener
type LocalParticipantListener interface {
ParticipantListener
OnStateChange(LocalParticipant)
OnSubscriberReady(LocalParticipant)
OnMigrateStateChange(LocalParticipant, MigrateState)
OnDataMessage(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)
OnDataMessageUnlabeled(LocalParticipant, []byte)
OnSubscribeStatusChanged(LocalParticipant, livekit.ParticipantID, bool)
OnUpdateSubscriptions(
LocalParticipant,
[]livekit.TrackID,
[]*livekit.ParticipantTracks,
bool,
)
OnUpdateSubscriptionPermission(LocalParticipant, *livekit.SubscriptionPermission) error
OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription)
OnSyncState(LocalParticipant, *livekit.SyncState) error
OnSimulateScenario(LocalParticipant, *livekit.SimulateScenario) error
OnLeave(LocalParticipant, ParticipantCloseReason)
}
var _ LocalParticipantListener = (*NullLocalParticipantListener)(nil)
type NullLocalParticipantListener struct {
NullParticipantListener
}
func (*NullLocalParticipantListener) OnStateChange(LocalParticipant) {}
func (*NullLocalParticipantListener) OnSubscriberReady(LocalParticipant) {}
func (*NullLocalParticipantListener) OnMigrateStateChange(LocalParticipant, MigrateState) {}
func (*NullLocalParticipantListener) OnDataMessage(LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket) {
}
func (*NullLocalParticipantListener) OnDataMessageUnlabeled(LocalParticipant, []byte) {}
func (*NullLocalParticipantListener) OnSubscribeStatusChanged(LocalParticipant, livekit.ParticipantID, bool) {
}
func (*NullLocalParticipantListener) OnUpdateSubscriptions(
LocalParticipant,
[]livekit.TrackID,
[]*livekit.ParticipantTracks,
bool,
) {
}
func (*NullLocalParticipantListener) OnUpdateSubscriptionPermission(LocalParticipant, *livekit.SubscriptionPermission) error {
return nil
}
func (*NullLocalParticipantListener) OnUpdateDataSubscriptions(LocalParticipant, *livekit.UpdateDataSubscription) {
}
func (*NullLocalParticipantListener) OnSyncState(LocalParticipant, *livekit.SyncState) error {
return nil
}
func (*NullLocalParticipantListener) OnSimulateScenario(LocalParticipant, *livekit.SimulateScenario) error {
return nil
}
func (*NullLocalParticipantListener) OnLeave(LocalParticipant, ParticipantCloseReason) {}
// ---------------------------------------------
//counterfeiter:generate . ParticipantTelemetryListener
type ParticipantTelemetryListener interface {
OnTrackPublishRequested(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo)
OnTrackPublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool)
OnTrackUnpublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool)
OnTrackSubscribeRequested(pID livekit.ParticipantID, ti *livekit.TrackInfo)
OnTrackSubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, publisherInfo *livekit.ParticipantInfo, shouldSendEvent bool)
OnTrackUnsubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, shouldSendEvent bool)
OnTrackSubscribeFailed(pID livekit.ParticipantID, trackID livekit.TrackID, err error, isUserError bool)
OnTrackSubscribeStreamStarted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
OnTrackMuted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
OnTrackUnmuted(pID livekit.ParticipantID, ti *livekit.TrackInfo)
OnTrackPublishedUpdate(pID livekit.ParticipantID, ti *livekit.TrackInfo)
OnTrackMaxSubscribedVideoQuality(pID livekit.ParticipantID, ti *livekit.TrackInfo, mime mime.MimeType, maxQuality livekit.VideoQuality)
OnTrackPublishRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, layer int, stats *livekit.RTPStats)
OnTrackSubscribeRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, stats *livekit.RTPStats)
OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat)
}
var _ ParticipantTelemetryListener = (*NullParticipantTelemetryListener)(nil)
type NullParticipantTelemetryListener struct{}
func (NullParticipantTelemetryListener) OnTrackPublishRequested(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackPublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool) {
}
func (NullParticipantTelemetryListener) OnTrackUnpublished(pID livekit.ParticipantID, identity livekit.ParticipantIdentity, ti *livekit.TrackInfo, shouldSendEvent bool) {
}
func (NullParticipantTelemetryListener) OnTrackSubscribeRequested(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackSubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, publisherInfo *livekit.ParticipantInfo, shouldSendEvent bool) {
}
func (NullParticipantTelemetryListener) OnTrackUnsubscribed(pID livekit.ParticipantID, ti *livekit.TrackInfo, shouldSendEvent bool) {
}
func (NullParticipantTelemetryListener) OnTrackSubscribeFailed(pID livekit.ParticipantID, trackID livekit.TrackID, err error, isUserError bool) {
}
func (NullParticipantTelemetryListener) OnTrackSubscribeStreamStarted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackMuted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackUnmuted(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackPublishedUpdate(pID livekit.ParticipantID, ti *livekit.TrackInfo) {
}
func (NullParticipantTelemetryListener) OnTrackMaxSubscribedVideoQuality(pID livekit.ParticipantID, ti *livekit.TrackInfo, mime mime.MimeType, maxQuality livekit.VideoQuality) {
}
func (NullParticipantTelemetryListener) OnTrackPublishRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, layer int, stats *livekit.RTPStats) {
}
func (NullParticipantTelemetryListener) OnTrackSubscribeRTPStats(pID livekit.ParticipantID, trackID livekit.TrackID, mimeType mime.MimeType, stats *livekit.RTPStats) {
}
func (NullParticipantTelemetryListener) OnTrackStats(key telemetry.StatsKey, stat *livekit.AnalyticsStat) {
}
// ---------------------------------------------
// Room is a container of participants, and can provide room-level actions
//
//counterfeiter:generate . Room
@@ -474,11 +725,14 @@ type Room interface {
Name() livekit.RoomName
ID() livekit.RoomID
RemoveParticipant(identity livekit.ParticipantIdentity, pID livekit.ParticipantID, reason ParticipantCloseReason)
UpdateSubscriptions(participant LocalParticipant, trackIDs []livekit.TrackID, participantTracks []*livekit.ParticipantTracks, subscribe bool)
UpdateSubscriptionPermission(participant LocalParticipant, permissions *livekit.SubscriptionPermission) error
SyncState(participant LocalParticipant, state *livekit.SyncState) error
SimulateScenario(participant LocalParticipant, scenario *livekit.SimulateScenario) error
UpdateSubscriptions(
participant LocalParticipant,
trackIDs []livekit.TrackID,
participantTracks []*livekit.ParticipantTracks,
subscribe bool,
)
ResolveMediaTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) MediaResolverResult
ResolveDataTrackForSubscriber(sub LocalParticipant, trackID livekit.TrackID) DataResolverResult
GetLocalParticipants() []LocalParticipant
IsDataMessageUserPacketDuplicate(ip *livekit.UserPacket) bool
}
@@ -501,12 +755,11 @@ type MediaTrack interface {
PublisherID() livekit.ParticipantID
PublisherIdentity() livekit.ParticipantIdentity
PublisherVersion() uint32
Logger() logger.Logger
IsMuted() bool
SetMuted(muted bool)
IsSimulcast() bool
GetAudioLevel() (level float64, active bool)
Close(isExpectedToResume bool)
@@ -525,15 +778,16 @@ type MediaTrack interface {
OnTrackSubscribed()
// returns quality information that's appropriate for width & height
GetQualityForDimension(width, height uint32) livekit.VideoQuality
GetQualityForDimension(mimeType mime.MimeType, width, height uint32) livekit.VideoQuality
// returns temporal layer that's appropriate for fps
GetTemporalLayerForSpatialFps(spatial int32, fps uint32, mime mime.MimeType) int32
GetTemporalLayerForSpatialFps(mimeType mime.MimeType, spatial int32, fps uint32) int32
Receivers() []sfu.TrackReceiver
ClearAllReceivers(isExpectedToResume bool)
IsEncrypted() bool
HasPacketTrailer() bool
}
//counterfeiter:generate . LocalMediaTrack
@@ -542,7 +796,7 @@ type LocalMediaTrack interface {
Restart()
SignalCid() string
HasSignalCid(cid string) bool
HasSdpCid(cid string) bool
GetConnectionScoreAndQuality() (float32, livekit.ConnectionQuality)
@@ -551,9 +805,57 @@ type LocalMediaTrack interface {
SetRTT(rtt uint32)
NotifySubscriberNodeMaxQuality(nodeID livekit.NodeID, qualities []SubscribedCodecQuality)
NotifySubscriptionNode(nodeID livekit.NodeID, codecs []*livekit.SubscribedAudioCodec)
ClearSubscriberNodes()
NotifySubscriberNodeMediaLoss(nodeID livekit.NodeID, fractionalLoss uint8)
}
// DataTrack represents a data track
//
//counterfeiter:generate . DataTrack
type DataTrack interface {
ID() livekit.TrackID
PubHandle() uint16
Name() string
ToProto() *livekit.DataTrackInfo
PublisherID() livekit.ParticipantID
PublisherIdentity() livekit.ParticipantIdentity
AddSubscriber(sub LocalParticipant) (DataDownTrack, error)
RemoveSubscriber(participantID livekit.ParticipantID)
IsSubscriber(subID livekit.ParticipantID) bool
AddDataDownTrack(sender DataTrackSender) error
DeleteDataDownTrack(subscriberID livekit.ParticipantID)
HandlePacket(data []byte, packet *datatrack.Packet, arrivalTime int64)
Close()
}
//counterfeiter:generate . DataDownTrack
type DataDownTrack interface {
Close()
Handle() uint16
PublishDataTrack() DataTrack
UpdateSubscriptionOptions(subscriptionOptions *livekit.DataTrackSubscriptionOptions)
}
//counterfeiter:generate . DataTrackSender
type DataTrackSender interface {
SubscriberID() livekit.ParticipantID
WritePacket(data []byte, packet *datatrack.Packet, arrivalTime int64)
}
//counterfeiter:generate . DataTrackTransport
type DataTrackTransport interface {
SendDataTrackMessage(data []byte) error
}
//counterfeiter:generate . SubscribedTrack
type SubscribedTrack interface {
AddOnBind(f func(error))
@@ -595,9 +897,20 @@ type MediaResolverResult struct {
PublisherIdentity livekit.ParticipantIdentity
}
type DataResolverResult struct {
TrackChangedNotifier ChangeNotifier
TrackRemovedNotifier ChangeNotifier
DataTrack DataTrack
PublisherID livekit.ParticipantID
PublisherIdentity livekit.ParticipantIdentity
}
// MediaTrackResolver locates a specific media track for a subscriber
type MediaTrackResolver func(LocalParticipant, livekit.TrackID) MediaResolverResult
// DataTrackResolver locates a specific data track for a subscriber
type DataTrackResolver func(LocalParticipant, livekit.TrackID) DataResolverResult
// Supervisor/operation monitor related definitions
type OperationMonitorEvent int
@@ -626,7 +939,7 @@ func (o OperationMonitorEvent) String() string {
}
}
type OperationMonitorData interface{}
type OperationMonitorData any
type OperationMonitor interface {
PostEvent(ome OperationMonitorEvent, omd OperationMonitorData)
+11 -3
View File
@@ -16,7 +16,7 @@ package types
type ProtocolVersion int
const CurrentProtocol = 15
const CurrentProtocol = 17
func (v ProtocolVersion) SupportsPackedStreamId() bool {
return v > 0
@@ -68,11 +68,11 @@ func (v ProtocolVersion) SupportFastStart() bool {
return v > 7
}
func (v ProtocolVersion) SupportHandlesDisconnectedUpdate() bool {
func (v ProtocolVersion) SupportsDisconnectedUpdate() bool {
return v > 8
}
func (v ProtocolVersion) SupportSyncStreamID() bool {
func (v ProtocolVersion) SupportsSyncStreamID() bool {
return v > 9
}
@@ -95,3 +95,11 @@ func (v ProtocolVersion) SupportsRegionsInLeaveRequest() bool {
func (v ProtocolVersion) SupportsNonErrorSignalResponse() bool {
return v > 14
}
func (v ProtocolVersion) SupportsMoving() bool {
return v > 15
}
func (v ProtocolVersion) SupportsPacketTrailer() bool {
return v > 16
}
@@ -0,0 +1,229 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeDataDownTrack struct {
CloseStub func()
closeMutex sync.RWMutex
closeArgsForCall []struct {
}
HandleStub func() uint16
handleMutex sync.RWMutex
handleArgsForCall []struct {
}
handleReturns struct {
result1 uint16
}
handleReturnsOnCall map[int]struct {
result1 uint16
}
PublishDataTrackStub func() types.DataTrack
publishDataTrackMutex sync.RWMutex
publishDataTrackArgsForCall []struct {
}
publishDataTrackReturns struct {
result1 types.DataTrack
}
publishDataTrackReturnsOnCall map[int]struct {
result1 types.DataTrack
}
UpdateSubscriptionOptionsStub func(*livekit.DataTrackSubscriptionOptions)
updateSubscriptionOptionsMutex sync.RWMutex
updateSubscriptionOptionsArgsForCall []struct {
arg1 *livekit.DataTrackSubscriptionOptions
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeDataDownTrack) Close() {
fake.closeMutex.Lock()
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
}{})
stub := fake.CloseStub
fake.recordInvocation("Close", []interface{}{})
fake.closeMutex.Unlock()
if stub != nil {
fake.CloseStub()
}
}
func (fake *FakeDataDownTrack) CloseCallCount() int {
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
return len(fake.closeArgsForCall)
}
func (fake *FakeDataDownTrack) CloseCalls(stub func()) {
fake.closeMutex.Lock()
defer fake.closeMutex.Unlock()
fake.CloseStub = stub
}
func (fake *FakeDataDownTrack) Handle() uint16 {
fake.handleMutex.Lock()
ret, specificReturn := fake.handleReturnsOnCall[len(fake.handleArgsForCall)]
fake.handleArgsForCall = append(fake.handleArgsForCall, struct {
}{})
stub := fake.HandleStub
fakeReturns := fake.handleReturns
fake.recordInvocation("Handle", []interface{}{})
fake.handleMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataDownTrack) HandleCallCount() int {
fake.handleMutex.RLock()
defer fake.handleMutex.RUnlock()
return len(fake.handleArgsForCall)
}
func (fake *FakeDataDownTrack) HandleCalls(stub func() uint16) {
fake.handleMutex.Lock()
defer fake.handleMutex.Unlock()
fake.HandleStub = stub
}
func (fake *FakeDataDownTrack) HandleReturns(result1 uint16) {
fake.handleMutex.Lock()
defer fake.handleMutex.Unlock()
fake.HandleStub = nil
fake.handleReturns = struct {
result1 uint16
}{result1}
}
func (fake *FakeDataDownTrack) HandleReturnsOnCall(i int, result1 uint16) {
fake.handleMutex.Lock()
defer fake.handleMutex.Unlock()
fake.HandleStub = nil
if fake.handleReturnsOnCall == nil {
fake.handleReturnsOnCall = make(map[int]struct {
result1 uint16
})
}
fake.handleReturnsOnCall[i] = struct {
result1 uint16
}{result1}
}
func (fake *FakeDataDownTrack) PublishDataTrack() types.DataTrack {
fake.publishDataTrackMutex.Lock()
ret, specificReturn := fake.publishDataTrackReturnsOnCall[len(fake.publishDataTrackArgsForCall)]
fake.publishDataTrackArgsForCall = append(fake.publishDataTrackArgsForCall, struct {
}{})
stub := fake.PublishDataTrackStub
fakeReturns := fake.publishDataTrackReturns
fake.recordInvocation("PublishDataTrack", []interface{}{})
fake.publishDataTrackMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataDownTrack) PublishDataTrackCallCount() int {
fake.publishDataTrackMutex.RLock()
defer fake.publishDataTrackMutex.RUnlock()
return len(fake.publishDataTrackArgsForCall)
}
func (fake *FakeDataDownTrack) PublishDataTrackCalls(stub func() types.DataTrack) {
fake.publishDataTrackMutex.Lock()
defer fake.publishDataTrackMutex.Unlock()
fake.PublishDataTrackStub = stub
}
func (fake *FakeDataDownTrack) PublishDataTrackReturns(result1 types.DataTrack) {
fake.publishDataTrackMutex.Lock()
defer fake.publishDataTrackMutex.Unlock()
fake.PublishDataTrackStub = nil
fake.publishDataTrackReturns = struct {
result1 types.DataTrack
}{result1}
}
func (fake *FakeDataDownTrack) PublishDataTrackReturnsOnCall(i int, result1 types.DataTrack) {
fake.publishDataTrackMutex.Lock()
defer fake.publishDataTrackMutex.Unlock()
fake.PublishDataTrackStub = nil
if fake.publishDataTrackReturnsOnCall == nil {
fake.publishDataTrackReturnsOnCall = make(map[int]struct {
result1 types.DataTrack
})
}
fake.publishDataTrackReturnsOnCall[i] = struct {
result1 types.DataTrack
}{result1}
}
func (fake *FakeDataDownTrack) UpdateSubscriptionOptions(arg1 *livekit.DataTrackSubscriptionOptions) {
fake.updateSubscriptionOptionsMutex.Lock()
fake.updateSubscriptionOptionsArgsForCall = append(fake.updateSubscriptionOptionsArgsForCall, struct {
arg1 *livekit.DataTrackSubscriptionOptions
}{arg1})
stub := fake.UpdateSubscriptionOptionsStub
fake.recordInvocation("UpdateSubscriptionOptions", []interface{}{arg1})
fake.updateSubscriptionOptionsMutex.Unlock()
if stub != nil {
fake.UpdateSubscriptionOptionsStub(arg1)
}
}
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCallCount() int {
fake.updateSubscriptionOptionsMutex.RLock()
defer fake.updateSubscriptionOptionsMutex.RUnlock()
return len(fake.updateSubscriptionOptionsArgsForCall)
}
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsCalls(stub func(*livekit.DataTrackSubscriptionOptions)) {
fake.updateSubscriptionOptionsMutex.Lock()
defer fake.updateSubscriptionOptionsMutex.Unlock()
fake.UpdateSubscriptionOptionsStub = stub
}
func (fake *FakeDataDownTrack) UpdateSubscriptionOptionsArgsForCall(i int) *livekit.DataTrackSubscriptionOptions {
fake.updateSubscriptionOptionsMutex.RLock()
defer fake.updateSubscriptionOptionsMutex.RUnlock()
argsForCall := fake.updateSubscriptionOptionsArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataDownTrack) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeDataDownTrack) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.DataDownTrack = new(FakeDataDownTrack)
+786
View File
@@ -0,0 +1,786 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeDataTrack struct {
AddDataDownTrackStub func(types.DataTrackSender) error
addDataDownTrackMutex sync.RWMutex
addDataDownTrackArgsForCall []struct {
arg1 types.DataTrackSender
}
addDataDownTrackReturns struct {
result1 error
}
addDataDownTrackReturnsOnCall map[int]struct {
result1 error
}
AddSubscriberStub func(types.LocalParticipant) (types.DataDownTrack, error)
addSubscriberMutex sync.RWMutex
addSubscriberArgsForCall []struct {
arg1 types.LocalParticipant
}
addSubscriberReturns struct {
result1 types.DataDownTrack
result2 error
}
addSubscriberReturnsOnCall map[int]struct {
result1 types.DataDownTrack
result2 error
}
CloseStub func()
closeMutex sync.RWMutex
closeArgsForCall []struct {
}
DeleteDataDownTrackStub func(livekit.ParticipantID)
deleteDataDownTrackMutex sync.RWMutex
deleteDataDownTrackArgsForCall []struct {
arg1 livekit.ParticipantID
}
HandlePacketStub func([]byte, *datatrack.Packet, int64)
handlePacketMutex sync.RWMutex
handlePacketArgsForCall []struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}
IDStub func() livekit.TrackID
iDMutex sync.RWMutex
iDArgsForCall []struct {
}
iDReturns struct {
result1 livekit.TrackID
}
iDReturnsOnCall map[int]struct {
result1 livekit.TrackID
}
IsSubscriberStub func(livekit.ParticipantID) bool
isSubscriberMutex sync.RWMutex
isSubscriberArgsForCall []struct {
arg1 livekit.ParticipantID
}
isSubscriberReturns struct {
result1 bool
}
isSubscriberReturnsOnCall map[int]struct {
result1 bool
}
NameStub func() string
nameMutex sync.RWMutex
nameArgsForCall []struct {
}
nameReturns struct {
result1 string
}
nameReturnsOnCall map[int]struct {
result1 string
}
PubHandleStub func() uint16
pubHandleMutex sync.RWMutex
pubHandleArgsForCall []struct {
}
pubHandleReturns struct {
result1 uint16
}
pubHandleReturnsOnCall map[int]struct {
result1 uint16
}
PublisherIDStub func() livekit.ParticipantID
publisherIDMutex sync.RWMutex
publisherIDArgsForCall []struct {
}
publisherIDReturns struct {
result1 livekit.ParticipantID
}
publisherIDReturnsOnCall map[int]struct {
result1 livekit.ParticipantID
}
PublisherIdentityStub func() livekit.ParticipantIdentity
publisherIdentityMutex sync.RWMutex
publisherIdentityArgsForCall []struct {
}
publisherIdentityReturns struct {
result1 livekit.ParticipantIdentity
}
publisherIdentityReturnsOnCall map[int]struct {
result1 livekit.ParticipantIdentity
}
RemoveSubscriberStub func(livekit.ParticipantID)
removeSubscriberMutex sync.RWMutex
removeSubscriberArgsForCall []struct {
arg1 livekit.ParticipantID
}
ToProtoStub func() *livekit.DataTrackInfo
toProtoMutex sync.RWMutex
toProtoArgsForCall []struct {
}
toProtoReturns struct {
result1 *livekit.DataTrackInfo
}
toProtoReturnsOnCall map[int]struct {
result1 *livekit.DataTrackInfo
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeDataTrack) AddDataDownTrack(arg1 types.DataTrackSender) error {
fake.addDataDownTrackMutex.Lock()
ret, specificReturn := fake.addDataDownTrackReturnsOnCall[len(fake.addDataDownTrackArgsForCall)]
fake.addDataDownTrackArgsForCall = append(fake.addDataDownTrackArgsForCall, struct {
arg1 types.DataTrackSender
}{arg1})
stub := fake.AddDataDownTrackStub
fakeReturns := fake.addDataDownTrackReturns
fake.recordInvocation("AddDataDownTrack", []interface{}{arg1})
fake.addDataDownTrackMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) AddDataDownTrackCallCount() int {
fake.addDataDownTrackMutex.RLock()
defer fake.addDataDownTrackMutex.RUnlock()
return len(fake.addDataDownTrackArgsForCall)
}
func (fake *FakeDataTrack) AddDataDownTrackCalls(stub func(types.DataTrackSender) error) {
fake.addDataDownTrackMutex.Lock()
defer fake.addDataDownTrackMutex.Unlock()
fake.AddDataDownTrackStub = stub
}
func (fake *FakeDataTrack) AddDataDownTrackArgsForCall(i int) types.DataTrackSender {
fake.addDataDownTrackMutex.RLock()
defer fake.addDataDownTrackMutex.RUnlock()
argsForCall := fake.addDataDownTrackArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) AddDataDownTrackReturns(result1 error) {
fake.addDataDownTrackMutex.Lock()
defer fake.addDataDownTrackMutex.Unlock()
fake.AddDataDownTrackStub = nil
fake.addDataDownTrackReturns = struct {
result1 error
}{result1}
}
func (fake *FakeDataTrack) AddDataDownTrackReturnsOnCall(i int, result1 error) {
fake.addDataDownTrackMutex.Lock()
defer fake.addDataDownTrackMutex.Unlock()
fake.AddDataDownTrackStub = nil
if fake.addDataDownTrackReturnsOnCall == nil {
fake.addDataDownTrackReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.addDataDownTrackReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeDataTrack) AddSubscriber(arg1 types.LocalParticipant) (types.DataDownTrack, error) {
fake.addSubscriberMutex.Lock()
ret, specificReturn := fake.addSubscriberReturnsOnCall[len(fake.addSubscriberArgsForCall)]
fake.addSubscriberArgsForCall = append(fake.addSubscriberArgsForCall, struct {
arg1 types.LocalParticipant
}{arg1})
stub := fake.AddSubscriberStub
fakeReturns := fake.addSubscriberReturns
fake.recordInvocation("AddSubscriber", []interface{}{arg1})
fake.addSubscriberMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakeDataTrack) AddSubscriberCallCount() int {
fake.addSubscriberMutex.RLock()
defer fake.addSubscriberMutex.RUnlock()
return len(fake.addSubscriberArgsForCall)
}
func (fake *FakeDataTrack) AddSubscriberCalls(stub func(types.LocalParticipant) (types.DataDownTrack, error)) {
fake.addSubscriberMutex.Lock()
defer fake.addSubscriberMutex.Unlock()
fake.AddSubscriberStub = stub
}
func (fake *FakeDataTrack) AddSubscriberArgsForCall(i int) types.LocalParticipant {
fake.addSubscriberMutex.RLock()
defer fake.addSubscriberMutex.RUnlock()
argsForCall := fake.addSubscriberArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) AddSubscriberReturns(result1 types.DataDownTrack, result2 error) {
fake.addSubscriberMutex.Lock()
defer fake.addSubscriberMutex.Unlock()
fake.AddSubscriberStub = nil
fake.addSubscriberReturns = struct {
result1 types.DataDownTrack
result2 error
}{result1, result2}
}
func (fake *FakeDataTrack) AddSubscriberReturnsOnCall(i int, result1 types.DataDownTrack, result2 error) {
fake.addSubscriberMutex.Lock()
defer fake.addSubscriberMutex.Unlock()
fake.AddSubscriberStub = nil
if fake.addSubscriberReturnsOnCall == nil {
fake.addSubscriberReturnsOnCall = make(map[int]struct {
result1 types.DataDownTrack
result2 error
})
}
fake.addSubscriberReturnsOnCall[i] = struct {
result1 types.DataDownTrack
result2 error
}{result1, result2}
}
func (fake *FakeDataTrack) Close() {
fake.closeMutex.Lock()
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
}{})
stub := fake.CloseStub
fake.recordInvocation("Close", []interface{}{})
fake.closeMutex.Unlock()
if stub != nil {
fake.CloseStub()
}
}
func (fake *FakeDataTrack) CloseCallCount() int {
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
return len(fake.closeArgsForCall)
}
func (fake *FakeDataTrack) CloseCalls(stub func()) {
fake.closeMutex.Lock()
defer fake.closeMutex.Unlock()
fake.CloseStub = stub
}
func (fake *FakeDataTrack) DeleteDataDownTrack(arg1 livekit.ParticipantID) {
fake.deleteDataDownTrackMutex.Lock()
fake.deleteDataDownTrackArgsForCall = append(fake.deleteDataDownTrackArgsForCall, struct {
arg1 livekit.ParticipantID
}{arg1})
stub := fake.DeleteDataDownTrackStub
fake.recordInvocation("DeleteDataDownTrack", []interface{}{arg1})
fake.deleteDataDownTrackMutex.Unlock()
if stub != nil {
fake.DeleteDataDownTrackStub(arg1)
}
}
func (fake *FakeDataTrack) DeleteDataDownTrackCallCount() int {
fake.deleteDataDownTrackMutex.RLock()
defer fake.deleteDataDownTrackMutex.RUnlock()
return len(fake.deleteDataDownTrackArgsForCall)
}
func (fake *FakeDataTrack) DeleteDataDownTrackCalls(stub func(livekit.ParticipantID)) {
fake.deleteDataDownTrackMutex.Lock()
defer fake.deleteDataDownTrackMutex.Unlock()
fake.DeleteDataDownTrackStub = stub
}
func (fake *FakeDataTrack) DeleteDataDownTrackArgsForCall(i int) livekit.ParticipantID {
fake.deleteDataDownTrackMutex.RLock()
defer fake.deleteDataDownTrackMutex.RUnlock()
argsForCall := fake.deleteDataDownTrackArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) HandlePacket(arg1 []byte, arg2 *datatrack.Packet, arg3 int64) {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.handlePacketMutex.Lock()
fake.handlePacketArgsForCall = append(fake.handlePacketArgsForCall, struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}{arg1Copy, arg2, arg3})
stub := fake.HandlePacketStub
fake.recordInvocation("HandlePacket", []interface{}{arg1Copy, arg2, arg3})
fake.handlePacketMutex.Unlock()
if stub != nil {
fake.HandlePacketStub(arg1, arg2, arg3)
}
}
func (fake *FakeDataTrack) HandlePacketCallCount() int {
fake.handlePacketMutex.RLock()
defer fake.handlePacketMutex.RUnlock()
return len(fake.handlePacketArgsForCall)
}
func (fake *FakeDataTrack) HandlePacketCalls(stub func([]byte, *datatrack.Packet, int64)) {
fake.handlePacketMutex.Lock()
defer fake.handlePacketMutex.Unlock()
fake.HandlePacketStub = stub
}
func (fake *FakeDataTrack) HandlePacketArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
fake.handlePacketMutex.RLock()
defer fake.handlePacketMutex.RUnlock()
argsForCall := fake.handlePacketArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeDataTrack) ID() livekit.TrackID {
fake.iDMutex.Lock()
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
fake.iDArgsForCall = append(fake.iDArgsForCall, struct {
}{})
stub := fake.IDStub
fakeReturns := fake.iDReturns
fake.recordInvocation("ID", []interface{}{})
fake.iDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) IDCallCount() int {
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
return len(fake.iDArgsForCall)
}
func (fake *FakeDataTrack) IDCalls(stub func() livekit.TrackID) {
fake.iDMutex.Lock()
defer fake.iDMutex.Unlock()
fake.IDStub = stub
}
func (fake *FakeDataTrack) IDReturns(result1 livekit.TrackID) {
fake.iDMutex.Lock()
defer fake.iDMutex.Unlock()
fake.IDStub = nil
fake.iDReturns = struct {
result1 livekit.TrackID
}{result1}
}
func (fake *FakeDataTrack) IDReturnsOnCall(i int, result1 livekit.TrackID) {
fake.iDMutex.Lock()
defer fake.iDMutex.Unlock()
fake.IDStub = nil
if fake.iDReturnsOnCall == nil {
fake.iDReturnsOnCall = make(map[int]struct {
result1 livekit.TrackID
})
}
fake.iDReturnsOnCall[i] = struct {
result1 livekit.TrackID
}{result1}
}
func (fake *FakeDataTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
fake.isSubscriberMutex.Lock()
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
fake.isSubscriberArgsForCall = append(fake.isSubscriberArgsForCall, struct {
arg1 livekit.ParticipantID
}{arg1})
stub := fake.IsSubscriberStub
fakeReturns := fake.isSubscriberReturns
fake.recordInvocation("IsSubscriber", []interface{}{arg1})
fake.isSubscriberMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) IsSubscriberCallCount() int {
fake.isSubscriberMutex.RLock()
defer fake.isSubscriberMutex.RUnlock()
return len(fake.isSubscriberArgsForCall)
}
func (fake *FakeDataTrack) IsSubscriberCalls(stub func(livekit.ParticipantID) bool) {
fake.isSubscriberMutex.Lock()
defer fake.isSubscriberMutex.Unlock()
fake.IsSubscriberStub = stub
}
func (fake *FakeDataTrack) IsSubscriberArgsForCall(i int) livekit.ParticipantID {
fake.isSubscriberMutex.RLock()
defer fake.isSubscriberMutex.RUnlock()
argsForCall := fake.isSubscriberArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) IsSubscriberReturns(result1 bool) {
fake.isSubscriberMutex.Lock()
defer fake.isSubscriberMutex.Unlock()
fake.IsSubscriberStub = nil
fake.isSubscriberReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeDataTrack) IsSubscriberReturnsOnCall(i int, result1 bool) {
fake.isSubscriberMutex.Lock()
defer fake.isSubscriberMutex.Unlock()
fake.IsSubscriberStub = nil
if fake.isSubscriberReturnsOnCall == nil {
fake.isSubscriberReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isSubscriberReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeDataTrack) Name() string {
fake.nameMutex.Lock()
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
fake.nameArgsForCall = append(fake.nameArgsForCall, struct {
}{})
stub := fake.NameStub
fakeReturns := fake.nameReturns
fake.recordInvocation("Name", []interface{}{})
fake.nameMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) NameCallCount() int {
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
return len(fake.nameArgsForCall)
}
func (fake *FakeDataTrack) NameCalls(stub func() string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = stub
}
func (fake *FakeDataTrack) NameReturns(result1 string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = nil
fake.nameReturns = struct {
result1 string
}{result1}
}
func (fake *FakeDataTrack) NameReturnsOnCall(i int, result1 string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = nil
if fake.nameReturnsOnCall == nil {
fake.nameReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.nameReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *FakeDataTrack) PubHandle() uint16 {
fake.pubHandleMutex.Lock()
ret, specificReturn := fake.pubHandleReturnsOnCall[len(fake.pubHandleArgsForCall)]
fake.pubHandleArgsForCall = append(fake.pubHandleArgsForCall, struct {
}{})
stub := fake.PubHandleStub
fakeReturns := fake.pubHandleReturns
fake.recordInvocation("PubHandle", []interface{}{})
fake.pubHandleMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) PubHandleCallCount() int {
fake.pubHandleMutex.RLock()
defer fake.pubHandleMutex.RUnlock()
return len(fake.pubHandleArgsForCall)
}
func (fake *FakeDataTrack) PubHandleCalls(stub func() uint16) {
fake.pubHandleMutex.Lock()
defer fake.pubHandleMutex.Unlock()
fake.PubHandleStub = stub
}
func (fake *FakeDataTrack) PubHandleReturns(result1 uint16) {
fake.pubHandleMutex.Lock()
defer fake.pubHandleMutex.Unlock()
fake.PubHandleStub = nil
fake.pubHandleReturns = struct {
result1 uint16
}{result1}
}
func (fake *FakeDataTrack) PubHandleReturnsOnCall(i int, result1 uint16) {
fake.pubHandleMutex.Lock()
defer fake.pubHandleMutex.Unlock()
fake.PubHandleStub = nil
if fake.pubHandleReturnsOnCall == nil {
fake.pubHandleReturnsOnCall = make(map[int]struct {
result1 uint16
})
}
fake.pubHandleReturnsOnCall[i] = struct {
result1 uint16
}{result1}
}
func (fake *FakeDataTrack) PublisherID() livekit.ParticipantID {
fake.publisherIDMutex.Lock()
ret, specificReturn := fake.publisherIDReturnsOnCall[len(fake.publisherIDArgsForCall)]
fake.publisherIDArgsForCall = append(fake.publisherIDArgsForCall, struct {
}{})
stub := fake.PublisherIDStub
fakeReturns := fake.publisherIDReturns
fake.recordInvocation("PublisherID", []interface{}{})
fake.publisherIDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) PublisherIDCallCount() int {
fake.publisherIDMutex.RLock()
defer fake.publisherIDMutex.RUnlock()
return len(fake.publisherIDArgsForCall)
}
func (fake *FakeDataTrack) PublisherIDCalls(stub func() livekit.ParticipantID) {
fake.publisherIDMutex.Lock()
defer fake.publisherIDMutex.Unlock()
fake.PublisherIDStub = stub
}
func (fake *FakeDataTrack) PublisherIDReturns(result1 livekit.ParticipantID) {
fake.publisherIDMutex.Lock()
defer fake.publisherIDMutex.Unlock()
fake.PublisherIDStub = nil
fake.publisherIDReturns = struct {
result1 livekit.ParticipantID
}{result1}
}
func (fake *FakeDataTrack) PublisherIDReturnsOnCall(i int, result1 livekit.ParticipantID) {
fake.publisherIDMutex.Lock()
defer fake.publisherIDMutex.Unlock()
fake.PublisherIDStub = nil
if fake.publisherIDReturnsOnCall == nil {
fake.publisherIDReturnsOnCall = make(map[int]struct {
result1 livekit.ParticipantID
})
}
fake.publisherIDReturnsOnCall[i] = struct {
result1 livekit.ParticipantID
}{result1}
}
func (fake *FakeDataTrack) PublisherIdentity() livekit.ParticipantIdentity {
fake.publisherIdentityMutex.Lock()
ret, specificReturn := fake.publisherIdentityReturnsOnCall[len(fake.publisherIdentityArgsForCall)]
fake.publisherIdentityArgsForCall = append(fake.publisherIdentityArgsForCall, struct {
}{})
stub := fake.PublisherIdentityStub
fakeReturns := fake.publisherIdentityReturns
fake.recordInvocation("PublisherIdentity", []interface{}{})
fake.publisherIdentityMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) PublisherIdentityCallCount() int {
fake.publisherIdentityMutex.RLock()
defer fake.publisherIdentityMutex.RUnlock()
return len(fake.publisherIdentityArgsForCall)
}
func (fake *FakeDataTrack) PublisherIdentityCalls(stub func() livekit.ParticipantIdentity) {
fake.publisherIdentityMutex.Lock()
defer fake.publisherIdentityMutex.Unlock()
fake.PublisherIdentityStub = stub
}
func (fake *FakeDataTrack) PublisherIdentityReturns(result1 livekit.ParticipantIdentity) {
fake.publisherIdentityMutex.Lock()
defer fake.publisherIdentityMutex.Unlock()
fake.PublisherIdentityStub = nil
fake.publisherIdentityReturns = struct {
result1 livekit.ParticipantIdentity
}{result1}
}
func (fake *FakeDataTrack) PublisherIdentityReturnsOnCall(i int, result1 livekit.ParticipantIdentity) {
fake.publisherIdentityMutex.Lock()
defer fake.publisherIdentityMutex.Unlock()
fake.PublisherIdentityStub = nil
if fake.publisherIdentityReturnsOnCall == nil {
fake.publisherIdentityReturnsOnCall = make(map[int]struct {
result1 livekit.ParticipantIdentity
})
}
fake.publisherIdentityReturnsOnCall[i] = struct {
result1 livekit.ParticipantIdentity
}{result1}
}
func (fake *FakeDataTrack) RemoveSubscriber(arg1 livekit.ParticipantID) {
fake.removeSubscriberMutex.Lock()
fake.removeSubscriberArgsForCall = append(fake.removeSubscriberArgsForCall, struct {
arg1 livekit.ParticipantID
}{arg1})
stub := fake.RemoveSubscriberStub
fake.recordInvocation("RemoveSubscriber", []interface{}{arg1})
fake.removeSubscriberMutex.Unlock()
if stub != nil {
fake.RemoveSubscriberStub(arg1)
}
}
func (fake *FakeDataTrack) RemoveSubscriberCallCount() int {
fake.removeSubscriberMutex.RLock()
defer fake.removeSubscriberMutex.RUnlock()
return len(fake.removeSubscriberArgsForCall)
}
func (fake *FakeDataTrack) RemoveSubscriberCalls(stub func(livekit.ParticipantID)) {
fake.removeSubscriberMutex.Lock()
defer fake.removeSubscriberMutex.Unlock()
fake.RemoveSubscriberStub = stub
}
func (fake *FakeDataTrack) RemoveSubscriberArgsForCall(i int) livekit.ParticipantID {
fake.removeSubscriberMutex.RLock()
defer fake.removeSubscriberMutex.RUnlock()
argsForCall := fake.removeSubscriberArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) ToProto() *livekit.DataTrackInfo {
fake.toProtoMutex.Lock()
ret, specificReturn := fake.toProtoReturnsOnCall[len(fake.toProtoArgsForCall)]
fake.toProtoArgsForCall = append(fake.toProtoArgsForCall, struct {
}{})
stub := fake.ToProtoStub
fakeReturns := fake.toProtoReturns
fake.recordInvocation("ToProto", []interface{}{})
fake.toProtoMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) ToProtoCallCount() int {
fake.toProtoMutex.RLock()
defer fake.toProtoMutex.RUnlock()
return len(fake.toProtoArgsForCall)
}
func (fake *FakeDataTrack) ToProtoCalls(stub func() *livekit.DataTrackInfo) {
fake.toProtoMutex.Lock()
defer fake.toProtoMutex.Unlock()
fake.ToProtoStub = stub
}
func (fake *FakeDataTrack) ToProtoReturns(result1 *livekit.DataTrackInfo) {
fake.toProtoMutex.Lock()
defer fake.toProtoMutex.Unlock()
fake.ToProtoStub = nil
fake.toProtoReturns = struct {
result1 *livekit.DataTrackInfo
}{result1}
}
func (fake *FakeDataTrack) ToProtoReturnsOnCall(i int, result1 *livekit.DataTrackInfo) {
fake.toProtoMutex.Lock()
defer fake.toProtoMutex.Unlock()
fake.ToProtoStub = nil
if fake.toProtoReturnsOnCall == nil {
fake.toProtoReturnsOnCall = make(map[int]struct {
result1 *livekit.DataTrackInfo
})
}
fake.toProtoReturnsOnCall[i] = struct {
result1 *livekit.DataTrackInfo
}{result1}
}
func (fake *FakeDataTrack) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeDataTrack) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.DataTrack = new(FakeDataTrack)
@@ -0,0 +1,148 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeDataTrackSender struct {
SubscriberIDStub func() livekit.ParticipantID
subscriberIDMutex sync.RWMutex
subscriberIDArgsForCall []struct {
}
subscriberIDReturns struct {
result1 livekit.ParticipantID
}
subscriberIDReturnsOnCall map[int]struct {
result1 livekit.ParticipantID
}
WritePacketStub func([]byte, *datatrack.Packet, int64)
writePacketMutex sync.RWMutex
writePacketArgsForCall []struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeDataTrackSender) SubscriberID() livekit.ParticipantID {
fake.subscriberIDMutex.Lock()
ret, specificReturn := fake.subscriberIDReturnsOnCall[len(fake.subscriberIDArgsForCall)]
fake.subscriberIDArgsForCall = append(fake.subscriberIDArgsForCall, struct {
}{})
stub := fake.SubscriberIDStub
fakeReturns := fake.subscriberIDReturns
fake.recordInvocation("SubscriberID", []interface{}{})
fake.subscriberIDMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrackSender) SubscriberIDCallCount() int {
fake.subscriberIDMutex.RLock()
defer fake.subscriberIDMutex.RUnlock()
return len(fake.subscriberIDArgsForCall)
}
func (fake *FakeDataTrackSender) SubscriberIDCalls(stub func() livekit.ParticipantID) {
fake.subscriberIDMutex.Lock()
defer fake.subscriberIDMutex.Unlock()
fake.SubscriberIDStub = stub
}
func (fake *FakeDataTrackSender) SubscriberIDReturns(result1 livekit.ParticipantID) {
fake.subscriberIDMutex.Lock()
defer fake.subscriberIDMutex.Unlock()
fake.SubscriberIDStub = nil
fake.subscriberIDReturns = struct {
result1 livekit.ParticipantID
}{result1}
}
func (fake *FakeDataTrackSender) SubscriberIDReturnsOnCall(i int, result1 livekit.ParticipantID) {
fake.subscriberIDMutex.Lock()
defer fake.subscriberIDMutex.Unlock()
fake.SubscriberIDStub = nil
if fake.subscriberIDReturnsOnCall == nil {
fake.subscriberIDReturnsOnCall = make(map[int]struct {
result1 livekit.ParticipantID
})
}
fake.subscriberIDReturnsOnCall[i] = struct {
result1 livekit.ParticipantID
}{result1}
}
func (fake *FakeDataTrackSender) WritePacket(arg1 []byte, arg2 *datatrack.Packet, arg3 int64) {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.writePacketMutex.Lock()
fake.writePacketArgsForCall = append(fake.writePacketArgsForCall, struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}{arg1Copy, arg2, arg3})
stub := fake.WritePacketStub
fake.recordInvocation("WritePacket", []interface{}{arg1Copy, arg2, arg3})
fake.writePacketMutex.Unlock()
if stub != nil {
fake.WritePacketStub(arg1, arg2, arg3)
}
}
func (fake *FakeDataTrackSender) WritePacketCallCount() int {
fake.writePacketMutex.RLock()
defer fake.writePacketMutex.RUnlock()
return len(fake.writePacketArgsForCall)
}
func (fake *FakeDataTrackSender) WritePacketCalls(stub func([]byte, *datatrack.Packet, int64)) {
fake.writePacketMutex.Lock()
defer fake.writePacketMutex.Unlock()
fake.WritePacketStub = stub
}
func (fake *FakeDataTrackSender) WritePacketArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
fake.writePacketMutex.RLock()
defer fake.writePacketMutex.RUnlock()
argsForCall := fake.writePacketArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeDataTrackSender) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeDataTrackSender) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.DataTrackSender = new(FakeDataTrackSender)
@@ -0,0 +1,114 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/types"
)
type FakeDataTrackTransport struct {
SendDataTrackMessageStub func([]byte) error
sendDataTrackMessageMutex sync.RWMutex
sendDataTrackMessageArgsForCall []struct {
arg1 []byte
}
sendDataTrackMessageReturns struct {
result1 error
}
sendDataTrackMessageReturnsOnCall map[int]struct {
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeDataTrackTransport) SendDataTrackMessage(arg1 []byte) error {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.sendDataTrackMessageMutex.Lock()
ret, specificReturn := fake.sendDataTrackMessageReturnsOnCall[len(fake.sendDataTrackMessageArgsForCall)]
fake.sendDataTrackMessageArgsForCall = append(fake.sendDataTrackMessageArgsForCall, struct {
arg1 []byte
}{arg1Copy})
stub := fake.SendDataTrackMessageStub
fakeReturns := fake.sendDataTrackMessageReturns
fake.recordInvocation("SendDataTrackMessage", []interface{}{arg1Copy})
fake.sendDataTrackMessageMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrackTransport) SendDataTrackMessageCallCount() int {
fake.sendDataTrackMessageMutex.RLock()
defer fake.sendDataTrackMessageMutex.RUnlock()
return len(fake.sendDataTrackMessageArgsForCall)
}
func (fake *FakeDataTrackTransport) SendDataTrackMessageCalls(stub func([]byte) error) {
fake.sendDataTrackMessageMutex.Lock()
defer fake.sendDataTrackMessageMutex.Unlock()
fake.SendDataTrackMessageStub = stub
}
func (fake *FakeDataTrackTransport) SendDataTrackMessageArgsForCall(i int) []byte {
fake.sendDataTrackMessageMutex.RLock()
defer fake.sendDataTrackMessageMutex.RUnlock()
argsForCall := fake.sendDataTrackMessageArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrackTransport) SendDataTrackMessageReturns(result1 error) {
fake.sendDataTrackMessageMutex.Lock()
defer fake.sendDataTrackMessageMutex.Unlock()
fake.SendDataTrackMessageStub = nil
fake.sendDataTrackMessageReturns = struct {
result1 error
}{result1}
}
func (fake *FakeDataTrackTransport) SendDataTrackMessageReturnsOnCall(i int, result1 error) {
fake.sendDataTrackMessageMutex.Lock()
defer fake.sendDataTrackMessageMutex.Unlock()
fake.SendDataTrackMessageStub = nil
if fake.sendDataTrackMessageReturnsOnCall == nil {
fake.sendDataTrackMessageReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.sendDataTrackMessageReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeDataTrackTransport) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeDataTrackTransport) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.DataTrackTransport = new(FakeDataTrackTransport)
+294 -225
View File
@@ -6,8 +6,9 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
type FakeLocalMediaTrack struct {
@@ -34,6 +35,10 @@ type FakeLocalMediaTrack struct {
clearAllReceiversArgsForCall []struct {
arg1 bool
}
ClearSubscriberNodesStub func()
clearSubscriberNodesMutex sync.RWMutex
clearSubscriberNodesArgsForCall []struct {
}
CloseStub func(bool)
closeMutex sync.RWMutex
closeArgsForCall []struct {
@@ -83,11 +88,12 @@ type FakeLocalMediaTrack struct {
getNumSubscribersReturnsOnCall map[int]struct {
result1 int
}
GetQualityForDimensionStub func(uint32, uint32) livekit.VideoQuality
GetQualityForDimensionStub func(mime.MimeType, uint32, uint32) livekit.VideoQuality
getQualityForDimensionMutex sync.RWMutex
getQualityForDimensionArgsForCall []struct {
arg1 uint32
arg1 mime.MimeType
arg2 uint32
arg3 uint32
}
getQualityForDimensionReturns struct {
result1 livekit.VideoQuality
@@ -95,12 +101,12 @@ type FakeLocalMediaTrack struct {
getQualityForDimensionReturnsOnCall map[int]struct {
result1 livekit.VideoQuality
}
GetTemporalLayerForSpatialFpsStub func(int32, uint32, mime.MimeType) int32
GetTemporalLayerForSpatialFpsStub func(mime.MimeType, int32, uint32) int32
getTemporalLayerForSpatialFpsMutex sync.RWMutex
getTemporalLayerForSpatialFpsArgsForCall []struct {
arg1 int32
arg2 uint32
arg3 mime.MimeType
arg1 mime.MimeType
arg2 int32
arg3 uint32
}
getTemporalLayerForSpatialFpsReturns struct {
result1 int32
@@ -118,6 +124,16 @@ type FakeLocalMediaTrack struct {
getTrackStatsReturnsOnCall map[int]struct {
result1 *livekit.RTPStats
}
HasPacketTrailerStub func() bool
hasPacketTrailerMutex sync.RWMutex
hasPacketTrailerArgsForCall []struct {
}
hasPacketTrailerReturns struct {
result1 bool
}
hasPacketTrailerReturnsOnCall map[int]struct {
result1 bool
}
HasSdpCidStub func(string) bool
hasSdpCidMutex sync.RWMutex
hasSdpCidArgsForCall []struct {
@@ -129,6 +145,17 @@ type FakeLocalMediaTrack struct {
hasSdpCidReturnsOnCall map[int]struct {
result1 bool
}
HasSignalCidStub func(string) bool
hasSignalCidMutex sync.RWMutex
hasSignalCidArgsForCall []struct {
arg1 string
}
hasSignalCidReturns struct {
result1 bool
}
hasSignalCidReturnsOnCall map[int]struct {
result1 bool
}
IDStub func() livekit.TrackID
iDMutex sync.RWMutex
iDArgsForCall []struct {
@@ -169,16 +196,6 @@ type FakeLocalMediaTrack struct {
isOpenReturnsOnCall map[int]struct {
result1 bool
}
IsSimulcastStub func() bool
isSimulcastMutex sync.RWMutex
isSimulcastArgsForCall []struct {
}
isSimulcastReturns struct {
result1 bool
}
isSimulcastReturnsOnCall map[int]struct {
result1 bool
}
IsSubscriberStub func(livekit.ParticipantID) bool
isSubscriberMutex sync.RWMutex
isSubscriberArgsForCall []struct {
@@ -200,6 +217,16 @@ type FakeLocalMediaTrack struct {
kindReturnsOnCall map[int]struct {
result1 livekit.TrackType
}
LoggerStub func() logger.Logger
loggerMutex sync.RWMutex
loggerArgsForCall []struct {
}
loggerReturns struct {
result1 logger.Logger
}
loggerReturnsOnCall map[int]struct {
result1 logger.Logger
}
NameStub func() string
nameMutex sync.RWMutex
nameArgsForCall []struct {
@@ -222,6 +249,12 @@ type FakeLocalMediaTrack struct {
arg1 livekit.NodeID
arg2 uint8
}
NotifySubscriptionNodeStub func(livekit.NodeID, []*livekit.SubscribedAudioCodec)
notifySubscriptionNodeMutex sync.RWMutex
notifySubscriptionNodeArgsForCall []struct {
arg1 livekit.NodeID
arg2 []*livekit.SubscribedAudioCodec
}
OnTrackSubscribedStub func()
onTrackSubscribedMutex sync.RWMutex
onTrackSubscribedArgsForCall []struct {
@@ -297,16 +330,6 @@ type FakeLocalMediaTrack struct {
setRTTArgsForCall []struct {
arg1 uint32
}
SignalCidStub func() string
signalCidMutex sync.RWMutex
signalCidArgsForCall []struct {
}
signalCidReturns struct {
result1 string
}
signalCidReturnsOnCall map[int]struct {
result1 string
}
SourceStub func() livekit.TrackSource
sourceMutex sync.RWMutex
sourceArgsForCall []struct {
@@ -484,6 +507,30 @@ func (fake *FakeLocalMediaTrack) ClearAllReceiversArgsForCall(i int) bool {
return argsForCall.arg1
}
func (fake *FakeLocalMediaTrack) ClearSubscriberNodes() {
fake.clearSubscriberNodesMutex.Lock()
fake.clearSubscriberNodesArgsForCall = append(fake.clearSubscriberNodesArgsForCall, struct {
}{})
stub := fake.ClearSubscriberNodesStub
fake.recordInvocation("ClearSubscriberNodes", []interface{}{})
fake.clearSubscriberNodesMutex.Unlock()
if stub != nil {
fake.ClearSubscriberNodesStub()
}
}
func (fake *FakeLocalMediaTrack) ClearSubscriberNodesCallCount() int {
fake.clearSubscriberNodesMutex.RLock()
defer fake.clearSubscriberNodesMutex.RUnlock()
return len(fake.clearSubscriberNodesArgsForCall)
}
func (fake *FakeLocalMediaTrack) ClearSubscriberNodesCalls(stub func()) {
fake.clearSubscriberNodesMutex.Lock()
defer fake.clearSubscriberNodesMutex.Unlock()
fake.ClearSubscriberNodesStub = stub
}
func (fake *FakeLocalMediaTrack) Close(arg1 bool) {
fake.closeMutex.Lock()
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
@@ -734,19 +781,20 @@ func (fake *FakeLocalMediaTrack) GetNumSubscribersReturnsOnCall(i int, result1 i
}{result1}
}
func (fake *FakeLocalMediaTrack) GetQualityForDimension(arg1 uint32, arg2 uint32) livekit.VideoQuality {
func (fake *FakeLocalMediaTrack) GetQualityForDimension(arg1 mime.MimeType, arg2 uint32, arg3 uint32) livekit.VideoQuality {
fake.getQualityForDimensionMutex.Lock()
ret, specificReturn := fake.getQualityForDimensionReturnsOnCall[len(fake.getQualityForDimensionArgsForCall)]
fake.getQualityForDimensionArgsForCall = append(fake.getQualityForDimensionArgsForCall, struct {
arg1 uint32
arg1 mime.MimeType
arg2 uint32
}{arg1, arg2})
arg3 uint32
}{arg1, arg2, arg3})
stub := fake.GetQualityForDimensionStub
fakeReturns := fake.getQualityForDimensionReturns
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2})
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2, arg3})
fake.getQualityForDimensionMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
return stub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1
@@ -760,17 +808,17 @@ func (fake *FakeLocalMediaTrack) GetQualityForDimensionCallCount() int {
return len(fake.getQualityForDimensionArgsForCall)
}
func (fake *FakeLocalMediaTrack) GetQualityForDimensionCalls(stub func(uint32, uint32) livekit.VideoQuality) {
func (fake *FakeLocalMediaTrack) GetQualityForDimensionCalls(stub func(mime.MimeType, uint32, uint32) livekit.VideoQuality) {
fake.getQualityForDimensionMutex.Lock()
defer fake.getQualityForDimensionMutex.Unlock()
fake.GetQualityForDimensionStub = stub
}
func (fake *FakeLocalMediaTrack) GetQualityForDimensionArgsForCall(i int) (uint32, uint32) {
func (fake *FakeLocalMediaTrack) GetQualityForDimensionArgsForCall(i int) (mime.MimeType, uint32, uint32) {
fake.getQualityForDimensionMutex.RLock()
defer fake.getQualityForDimensionMutex.RUnlock()
argsForCall := fake.getQualityForDimensionArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeLocalMediaTrack) GetQualityForDimensionReturns(result1 livekit.VideoQuality) {
@@ -796,13 +844,13 @@ func (fake *FakeLocalMediaTrack) GetQualityForDimensionReturnsOnCall(i int, resu
}{result1}
}
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFps(arg1 int32, arg2 uint32, arg3 mime.MimeType) int32 {
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFps(arg1 mime.MimeType, arg2 int32, arg3 uint32) int32 {
fake.getTemporalLayerForSpatialFpsMutex.Lock()
ret, specificReturn := fake.getTemporalLayerForSpatialFpsReturnsOnCall[len(fake.getTemporalLayerForSpatialFpsArgsForCall)]
fake.getTemporalLayerForSpatialFpsArgsForCall = append(fake.getTemporalLayerForSpatialFpsArgsForCall, struct {
arg1 int32
arg2 uint32
arg3 mime.MimeType
arg1 mime.MimeType
arg2 int32
arg3 uint32
}{arg1, arg2, arg3})
stub := fake.GetTemporalLayerForSpatialFpsStub
fakeReturns := fake.getTemporalLayerForSpatialFpsReturns
@@ -823,13 +871,13 @@ func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCallCount() int {
return len(fake.getTemporalLayerForSpatialFpsArgsForCall)
}
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(int32, uint32, mime.MimeType) int32) {
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(mime.MimeType, int32, uint32) int32) {
fake.getTemporalLayerForSpatialFpsMutex.Lock()
defer fake.getTemporalLayerForSpatialFpsMutex.Unlock()
fake.GetTemporalLayerForSpatialFpsStub = stub
}
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (int32, uint32, mime.MimeType) {
func (fake *FakeLocalMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (mime.MimeType, int32, uint32) {
fake.getTemporalLayerForSpatialFpsMutex.RLock()
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
argsForCall := fake.getTemporalLayerForSpatialFpsArgsForCall[i]
@@ -912,6 +960,59 @@ func (fake *FakeLocalMediaTrack) GetTrackStatsReturnsOnCall(i int, result1 *live
}{result1}
}
func (fake *FakeLocalMediaTrack) HasPacketTrailer() bool {
fake.hasPacketTrailerMutex.Lock()
ret, specificReturn := fake.hasPacketTrailerReturnsOnCall[len(fake.hasPacketTrailerArgsForCall)]
fake.hasPacketTrailerArgsForCall = append(fake.hasPacketTrailerArgsForCall, struct {
}{})
stub := fake.HasPacketTrailerStub
fakeReturns := fake.hasPacketTrailerReturns
fake.recordInvocation("HasPacketTrailer", []interface{}{})
fake.hasPacketTrailerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalMediaTrack) HasPacketTrailerCallCount() int {
fake.hasPacketTrailerMutex.RLock()
defer fake.hasPacketTrailerMutex.RUnlock()
return len(fake.hasPacketTrailerArgsForCall)
}
func (fake *FakeLocalMediaTrack) HasPacketTrailerCalls(stub func() bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = stub
}
func (fake *FakeLocalMediaTrack) HasPacketTrailerReturns(result1 bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = nil
fake.hasPacketTrailerReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) HasPacketTrailerReturnsOnCall(i int, result1 bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = nil
if fake.hasPacketTrailerReturnsOnCall == nil {
fake.hasPacketTrailerReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.hasPacketTrailerReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) HasSdpCid(arg1 string) bool {
fake.hasSdpCidMutex.Lock()
ret, specificReturn := fake.hasSdpCidReturnsOnCall[len(fake.hasSdpCidArgsForCall)]
@@ -973,6 +1074,67 @@ func (fake *FakeLocalMediaTrack) HasSdpCidReturnsOnCall(i int, result1 bool) {
}{result1}
}
func (fake *FakeLocalMediaTrack) HasSignalCid(arg1 string) bool {
fake.hasSignalCidMutex.Lock()
ret, specificReturn := fake.hasSignalCidReturnsOnCall[len(fake.hasSignalCidArgsForCall)]
fake.hasSignalCidArgsForCall = append(fake.hasSignalCidArgsForCall, struct {
arg1 string
}{arg1})
stub := fake.HasSignalCidStub
fakeReturns := fake.hasSignalCidReturns
fake.recordInvocation("HasSignalCid", []interface{}{arg1})
fake.hasSignalCidMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalMediaTrack) HasSignalCidCallCount() int {
fake.hasSignalCidMutex.RLock()
defer fake.hasSignalCidMutex.RUnlock()
return len(fake.hasSignalCidArgsForCall)
}
func (fake *FakeLocalMediaTrack) HasSignalCidCalls(stub func(string) bool) {
fake.hasSignalCidMutex.Lock()
defer fake.hasSignalCidMutex.Unlock()
fake.HasSignalCidStub = stub
}
func (fake *FakeLocalMediaTrack) HasSignalCidArgsForCall(i int) string {
fake.hasSignalCidMutex.RLock()
defer fake.hasSignalCidMutex.RUnlock()
argsForCall := fake.hasSignalCidArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalMediaTrack) HasSignalCidReturns(result1 bool) {
fake.hasSignalCidMutex.Lock()
defer fake.hasSignalCidMutex.Unlock()
fake.HasSignalCidStub = nil
fake.hasSignalCidReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) HasSignalCidReturnsOnCall(i int, result1 bool) {
fake.hasSignalCidMutex.Lock()
defer fake.hasSignalCidMutex.Unlock()
fake.HasSignalCidStub = nil
if fake.hasSignalCidReturnsOnCall == nil {
fake.hasSignalCidReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.hasSignalCidReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) ID() livekit.TrackID {
fake.iDMutex.Lock()
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
@@ -1185,59 +1347,6 @@ func (fake *FakeLocalMediaTrack) IsOpenReturnsOnCall(i int, result1 bool) {
}{result1}
}
func (fake *FakeLocalMediaTrack) IsSimulcast() bool {
fake.isSimulcastMutex.Lock()
ret, specificReturn := fake.isSimulcastReturnsOnCall[len(fake.isSimulcastArgsForCall)]
fake.isSimulcastArgsForCall = append(fake.isSimulcastArgsForCall, struct {
}{})
stub := fake.IsSimulcastStub
fakeReturns := fake.isSimulcastReturns
fake.recordInvocation("IsSimulcast", []interface{}{})
fake.isSimulcastMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalMediaTrack) IsSimulcastCallCount() int {
fake.isSimulcastMutex.RLock()
defer fake.isSimulcastMutex.RUnlock()
return len(fake.isSimulcastArgsForCall)
}
func (fake *FakeLocalMediaTrack) IsSimulcastCalls(stub func() bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = stub
}
func (fake *FakeLocalMediaTrack) IsSimulcastReturns(result1 bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = nil
fake.isSimulcastReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) IsSimulcastReturnsOnCall(i int, result1 bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = nil
if fake.isSimulcastReturnsOnCall == nil {
fake.isSimulcastReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isSimulcastReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalMediaTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
fake.isSubscriberMutex.Lock()
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
@@ -1352,6 +1461,59 @@ func (fake *FakeLocalMediaTrack) KindReturnsOnCall(i int, result1 livekit.TrackT
}{result1}
}
func (fake *FakeLocalMediaTrack) Logger() logger.Logger {
fake.loggerMutex.Lock()
ret, specificReturn := fake.loggerReturnsOnCall[len(fake.loggerArgsForCall)]
fake.loggerArgsForCall = append(fake.loggerArgsForCall, struct {
}{})
stub := fake.LoggerStub
fakeReturns := fake.loggerReturns
fake.recordInvocation("Logger", []interface{}{})
fake.loggerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalMediaTrack) LoggerCallCount() int {
fake.loggerMutex.RLock()
defer fake.loggerMutex.RUnlock()
return len(fake.loggerArgsForCall)
}
func (fake *FakeLocalMediaTrack) LoggerCalls(stub func() logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = stub
}
func (fake *FakeLocalMediaTrack) LoggerReturns(result1 logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = nil
fake.loggerReturns = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeLocalMediaTrack) LoggerReturnsOnCall(i int, result1 logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = nil
if fake.loggerReturnsOnCall == nil {
fake.loggerReturnsOnCall = make(map[int]struct {
result1 logger.Logger
})
}
fake.loggerReturnsOnCall[i] = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeLocalMediaTrack) Name() string {
fake.nameMutex.Lock()
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
@@ -1476,6 +1638,44 @@ func (fake *FakeLocalMediaTrack) NotifySubscriberNodeMediaLossArgsForCall(i int)
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalMediaTrack) NotifySubscriptionNode(arg1 livekit.NodeID, arg2 []*livekit.SubscribedAudioCodec) {
var arg2Copy []*livekit.SubscribedAudioCodec
if arg2 != nil {
arg2Copy = make([]*livekit.SubscribedAudioCodec, len(arg2))
copy(arg2Copy, arg2)
}
fake.notifySubscriptionNodeMutex.Lock()
fake.notifySubscriptionNodeArgsForCall = append(fake.notifySubscriptionNodeArgsForCall, struct {
arg1 livekit.NodeID
arg2 []*livekit.SubscribedAudioCodec
}{arg1, arg2Copy})
stub := fake.NotifySubscriptionNodeStub
fake.recordInvocation("NotifySubscriptionNode", []interface{}{arg1, arg2Copy})
fake.notifySubscriptionNodeMutex.Unlock()
if stub != nil {
fake.NotifySubscriptionNodeStub(arg1, arg2)
}
}
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeCallCount() int {
fake.notifySubscriptionNodeMutex.RLock()
defer fake.notifySubscriptionNodeMutex.RUnlock()
return len(fake.notifySubscriptionNodeArgsForCall)
}
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeCalls(stub func(livekit.NodeID, []*livekit.SubscribedAudioCodec)) {
fake.notifySubscriptionNodeMutex.Lock()
defer fake.notifySubscriptionNodeMutex.Unlock()
fake.NotifySubscriptionNodeStub = stub
}
func (fake *FakeLocalMediaTrack) NotifySubscriptionNodeArgsForCall(i int) (livekit.NodeID, []*livekit.SubscribedAudioCodec) {
fake.notifySubscriptionNodeMutex.RLock()
defer fake.notifySubscriptionNodeMutex.RUnlock()
argsForCall := fake.notifySubscriptionNodeArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalMediaTrack) OnTrackSubscribed() {
fake.onTrackSubscribedMutex.Lock()
fake.onTrackSubscribedArgsForCall = append(fake.onTrackSubscribedArgsForCall, struct {
@@ -1899,59 +2099,6 @@ func (fake *FakeLocalMediaTrack) SetRTTArgsForCall(i int) uint32 {
return argsForCall.arg1
}
func (fake *FakeLocalMediaTrack) SignalCid() string {
fake.signalCidMutex.Lock()
ret, specificReturn := fake.signalCidReturnsOnCall[len(fake.signalCidArgsForCall)]
fake.signalCidArgsForCall = append(fake.signalCidArgsForCall, struct {
}{})
stub := fake.SignalCidStub
fakeReturns := fake.signalCidReturns
fake.recordInvocation("SignalCid", []interface{}{})
fake.signalCidMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalMediaTrack) SignalCidCallCount() int {
fake.signalCidMutex.RLock()
defer fake.signalCidMutex.RUnlock()
return len(fake.signalCidArgsForCall)
}
func (fake *FakeLocalMediaTrack) SignalCidCalls(stub func() string) {
fake.signalCidMutex.Lock()
defer fake.signalCidMutex.Unlock()
fake.SignalCidStub = stub
}
func (fake *FakeLocalMediaTrack) SignalCidReturns(result1 string) {
fake.signalCidMutex.Lock()
defer fake.signalCidMutex.Unlock()
fake.SignalCidStub = nil
fake.signalCidReturns = struct {
result1 string
}{result1}
}
func (fake *FakeLocalMediaTrack) SignalCidReturnsOnCall(i int, result1 string) {
fake.signalCidMutex.Lock()
defer fake.signalCidMutex.Unlock()
fake.SignalCidStub = nil
if fake.signalCidReturnsOnCall == nil {
fake.signalCidReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.signalCidReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *FakeLocalMediaTrack) Source() livekit.TrackSource {
fake.sourceMutex.Lock()
ret, specificReturn := fake.sourceReturnsOnCall[len(fake.sourceArgsForCall)]
@@ -2210,84 +2357,6 @@ func (fake *FakeLocalMediaTrack) UpdateVideoTrackArgsForCall(i int) *livekit.Upd
func (fake *FakeLocalMediaTrack) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.addOnCloseMutex.RLock()
defer fake.addOnCloseMutex.RUnlock()
fake.addSubscriberMutex.RLock()
defer fake.addSubscriberMutex.RUnlock()
fake.clearAllReceiversMutex.RLock()
defer fake.clearAllReceiversMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.getAllSubscribersMutex.RLock()
defer fake.getAllSubscribersMutex.RUnlock()
fake.getAudioLevelMutex.RLock()
defer fake.getAudioLevelMutex.RUnlock()
fake.getConnectionScoreAndQualityMutex.RLock()
defer fake.getConnectionScoreAndQualityMutex.RUnlock()
fake.getNumSubscribersMutex.RLock()
defer fake.getNumSubscribersMutex.RUnlock()
fake.getQualityForDimensionMutex.RLock()
defer fake.getQualityForDimensionMutex.RUnlock()
fake.getTemporalLayerForSpatialFpsMutex.RLock()
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
fake.getTrackStatsMutex.RLock()
defer fake.getTrackStatsMutex.RUnlock()
fake.hasSdpCidMutex.RLock()
defer fake.hasSdpCidMutex.RUnlock()
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
fake.isEncryptedMutex.RLock()
defer fake.isEncryptedMutex.RUnlock()
fake.isMutedMutex.RLock()
defer fake.isMutedMutex.RUnlock()
fake.isOpenMutex.RLock()
defer fake.isOpenMutex.RUnlock()
fake.isSimulcastMutex.RLock()
defer fake.isSimulcastMutex.RUnlock()
fake.isSubscriberMutex.RLock()
defer fake.isSubscriberMutex.RUnlock()
fake.kindMutex.RLock()
defer fake.kindMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.notifySubscriberNodeMaxQualityMutex.RLock()
defer fake.notifySubscriberNodeMaxQualityMutex.RUnlock()
fake.notifySubscriberNodeMediaLossMutex.RLock()
defer fake.notifySubscriberNodeMediaLossMutex.RUnlock()
fake.onTrackSubscribedMutex.RLock()
defer fake.onTrackSubscribedMutex.RUnlock()
fake.publisherIDMutex.RLock()
defer fake.publisherIDMutex.RUnlock()
fake.publisherIdentityMutex.RLock()
defer fake.publisherIdentityMutex.RUnlock()
fake.publisherVersionMutex.RLock()
defer fake.publisherVersionMutex.RUnlock()
fake.receiversMutex.RLock()
defer fake.receiversMutex.RUnlock()
fake.removeSubscriberMutex.RLock()
defer fake.removeSubscriberMutex.RUnlock()
fake.restartMutex.RLock()
defer fake.restartMutex.RUnlock()
fake.revokeDisallowedSubscribersMutex.RLock()
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
fake.setMutedMutex.RLock()
defer fake.setMutedMutex.RUnlock()
fake.setRTTMutex.RLock()
defer fake.setRTTMutex.RUnlock()
fake.signalCidMutex.RLock()
defer fake.signalCidMutex.RUnlock()
fake.sourceMutex.RLock()
defer fake.sourceMutex.RUnlock()
fake.streamMutex.RLock()
defer fake.streamMutex.RUnlock()
fake.toProtoMutex.RLock()
defer fake.toProtoMutex.RUnlock()
fake.updateAudioTrackMutex.RLock()
defer fake.updateAudioTrackMutex.RUnlock()
fake.updateTrackInfoMutex.RLock()
defer fake.updateTrackInfoMutex.RUnlock()
fake.updateVideoTrackMutex.RLock()
defer fake.updateVideoTrackMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,542 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeLocalParticipantHelper struct {
GetCachedReliableDataMessageStub func(map[livekit.ParticipantID]uint32) []*types.DataMessageCache
getCachedReliableDataMessageMutex sync.RWMutex
getCachedReliableDataMessageArgsForCall []struct {
arg1 map[livekit.ParticipantID]uint32
}
getCachedReliableDataMessageReturns struct {
result1 []*types.DataMessageCache
}
getCachedReliableDataMessageReturnsOnCall map[int]struct {
result1 []*types.DataMessageCache
}
GetParticipantInfoStub func(livekit.ParticipantID) *livekit.ParticipantInfo
getParticipantInfoMutex sync.RWMutex
getParticipantInfoArgsForCall []struct {
arg1 livekit.ParticipantID
}
getParticipantInfoReturns struct {
result1 *livekit.ParticipantInfo
}
getParticipantInfoReturnsOnCall map[int]struct {
result1 *livekit.ParticipantInfo
}
GetRegionSettingsStub func(string) *livekit.RegionSettings
getRegionSettingsMutex sync.RWMutex
getRegionSettingsArgsForCall []struct {
arg1 string
}
getRegionSettingsReturns struct {
result1 *livekit.RegionSettings
}
getRegionSettingsReturnsOnCall map[int]struct {
result1 *livekit.RegionSettings
}
GetSubscriberForwarderStateStub func(types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)
getSubscriberForwarderStateMutex sync.RWMutex
getSubscriberForwarderStateArgsForCall []struct {
arg1 types.LocalParticipant
}
getSubscriberForwarderStateReturns struct {
result1 map[livekit.TrackID]*livekit.RTPForwarderState
result2 error
}
getSubscriberForwarderStateReturnsOnCall map[int]struct {
result1 map[livekit.TrackID]*livekit.RTPForwarderState
result2 error
}
ResolveDataTrackStub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult
resolveDataTrackMutex sync.RWMutex
resolveDataTrackArgsForCall []struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}
resolveDataTrackReturns struct {
result1 types.DataResolverResult
}
resolveDataTrackReturnsOnCall map[int]struct {
result1 types.DataResolverResult
}
ResolveMediaTrackStub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult
resolveMediaTrackMutex sync.RWMutex
resolveMediaTrackArgsForCall []struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}
resolveMediaTrackReturns struct {
result1 types.MediaResolverResult
}
resolveMediaTrackReturnsOnCall map[int]struct {
result1 types.MediaResolverResult
}
ShouldRegressCodecStub func() bool
shouldRegressCodecMutex sync.RWMutex
shouldRegressCodecArgsForCall []struct {
}
shouldRegressCodecReturns struct {
result1 bool
}
shouldRegressCodecReturnsOnCall map[int]struct {
result1 bool
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessage(arg1 map[livekit.ParticipantID]uint32) []*types.DataMessageCache {
fake.getCachedReliableDataMessageMutex.Lock()
ret, specificReturn := fake.getCachedReliableDataMessageReturnsOnCall[len(fake.getCachedReliableDataMessageArgsForCall)]
fake.getCachedReliableDataMessageArgsForCall = append(fake.getCachedReliableDataMessageArgsForCall, struct {
arg1 map[livekit.ParticipantID]uint32
}{arg1})
stub := fake.GetCachedReliableDataMessageStub
fakeReturns := fake.getCachedReliableDataMessageReturns
fake.recordInvocation("GetCachedReliableDataMessage", []interface{}{arg1})
fake.getCachedReliableDataMessageMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageCallCount() int {
fake.getCachedReliableDataMessageMutex.RLock()
defer fake.getCachedReliableDataMessageMutex.RUnlock()
return len(fake.getCachedReliableDataMessageArgsForCall)
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageCalls(stub func(map[livekit.ParticipantID]uint32) []*types.DataMessageCache) {
fake.getCachedReliableDataMessageMutex.Lock()
defer fake.getCachedReliableDataMessageMutex.Unlock()
fake.GetCachedReliableDataMessageStub = stub
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageArgsForCall(i int) map[livekit.ParticipantID]uint32 {
fake.getCachedReliableDataMessageMutex.RLock()
defer fake.getCachedReliableDataMessageMutex.RUnlock()
argsForCall := fake.getCachedReliableDataMessageArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageReturns(result1 []*types.DataMessageCache) {
fake.getCachedReliableDataMessageMutex.Lock()
defer fake.getCachedReliableDataMessageMutex.Unlock()
fake.GetCachedReliableDataMessageStub = nil
fake.getCachedReliableDataMessageReturns = struct {
result1 []*types.DataMessageCache
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetCachedReliableDataMessageReturnsOnCall(i int, result1 []*types.DataMessageCache) {
fake.getCachedReliableDataMessageMutex.Lock()
defer fake.getCachedReliableDataMessageMutex.Unlock()
fake.GetCachedReliableDataMessageStub = nil
if fake.getCachedReliableDataMessageReturnsOnCall == nil {
fake.getCachedReliableDataMessageReturnsOnCall = make(map[int]struct {
result1 []*types.DataMessageCache
})
}
fake.getCachedReliableDataMessageReturnsOnCall[i] = struct {
result1 []*types.DataMessageCache
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfo(arg1 livekit.ParticipantID) *livekit.ParticipantInfo {
fake.getParticipantInfoMutex.Lock()
ret, specificReturn := fake.getParticipantInfoReturnsOnCall[len(fake.getParticipantInfoArgsForCall)]
fake.getParticipantInfoArgsForCall = append(fake.getParticipantInfoArgsForCall, struct {
arg1 livekit.ParticipantID
}{arg1})
stub := fake.GetParticipantInfoStub
fakeReturns := fake.getParticipantInfoReturns
fake.recordInvocation("GetParticipantInfo", []interface{}{arg1})
fake.getParticipantInfoMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfoCallCount() int {
fake.getParticipantInfoMutex.RLock()
defer fake.getParticipantInfoMutex.RUnlock()
return len(fake.getParticipantInfoArgsForCall)
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfoCalls(stub func(livekit.ParticipantID) *livekit.ParticipantInfo) {
fake.getParticipantInfoMutex.Lock()
defer fake.getParticipantInfoMutex.Unlock()
fake.GetParticipantInfoStub = stub
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfoArgsForCall(i int) livekit.ParticipantID {
fake.getParticipantInfoMutex.RLock()
defer fake.getParticipantInfoMutex.RUnlock()
argsForCall := fake.getParticipantInfoArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfoReturns(result1 *livekit.ParticipantInfo) {
fake.getParticipantInfoMutex.Lock()
defer fake.getParticipantInfoMutex.Unlock()
fake.GetParticipantInfoStub = nil
fake.getParticipantInfoReturns = struct {
result1 *livekit.ParticipantInfo
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetParticipantInfoReturnsOnCall(i int, result1 *livekit.ParticipantInfo) {
fake.getParticipantInfoMutex.Lock()
defer fake.getParticipantInfoMutex.Unlock()
fake.GetParticipantInfoStub = nil
if fake.getParticipantInfoReturnsOnCall == nil {
fake.getParticipantInfoReturnsOnCall = make(map[int]struct {
result1 *livekit.ParticipantInfo
})
}
fake.getParticipantInfoReturnsOnCall[i] = struct {
result1 *livekit.ParticipantInfo
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetRegionSettings(arg1 string) *livekit.RegionSettings {
fake.getRegionSettingsMutex.Lock()
ret, specificReturn := fake.getRegionSettingsReturnsOnCall[len(fake.getRegionSettingsArgsForCall)]
fake.getRegionSettingsArgsForCall = append(fake.getRegionSettingsArgsForCall, struct {
arg1 string
}{arg1})
stub := fake.GetRegionSettingsStub
fakeReturns := fake.getRegionSettingsReturns
fake.recordInvocation("GetRegionSettings", []interface{}{arg1})
fake.getRegionSettingsMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) GetRegionSettingsCallCount() int {
fake.getRegionSettingsMutex.RLock()
defer fake.getRegionSettingsMutex.RUnlock()
return len(fake.getRegionSettingsArgsForCall)
}
func (fake *FakeLocalParticipantHelper) GetRegionSettingsCalls(stub func(string) *livekit.RegionSettings) {
fake.getRegionSettingsMutex.Lock()
defer fake.getRegionSettingsMutex.Unlock()
fake.GetRegionSettingsStub = stub
}
func (fake *FakeLocalParticipantHelper) GetRegionSettingsArgsForCall(i int) string {
fake.getRegionSettingsMutex.RLock()
defer fake.getRegionSettingsMutex.RUnlock()
argsForCall := fake.getRegionSettingsArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantHelper) GetRegionSettingsReturns(result1 *livekit.RegionSettings) {
fake.getRegionSettingsMutex.Lock()
defer fake.getRegionSettingsMutex.Unlock()
fake.GetRegionSettingsStub = nil
fake.getRegionSettingsReturns = struct {
result1 *livekit.RegionSettings
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetRegionSettingsReturnsOnCall(i int, result1 *livekit.RegionSettings) {
fake.getRegionSettingsMutex.Lock()
defer fake.getRegionSettingsMutex.Unlock()
fake.GetRegionSettingsStub = nil
if fake.getRegionSettingsReturnsOnCall == nil {
fake.getRegionSettingsReturnsOnCall = make(map[int]struct {
result1 *livekit.RegionSettings
})
}
fake.getRegionSettingsReturnsOnCall[i] = struct {
result1 *livekit.RegionSettings
}{result1}
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderState(arg1 types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error) {
fake.getSubscriberForwarderStateMutex.Lock()
ret, specificReturn := fake.getSubscriberForwarderStateReturnsOnCall[len(fake.getSubscriberForwarderStateArgsForCall)]
fake.getSubscriberForwarderStateArgsForCall = append(fake.getSubscriberForwarderStateArgsForCall, struct {
arg1 types.LocalParticipant
}{arg1})
stub := fake.GetSubscriberForwarderStateStub
fakeReturns := fake.getSubscriberForwarderStateReturns
fake.recordInvocation("GetSubscriberForwarderState", []interface{}{arg1})
fake.getSubscriberForwarderStateMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateCallCount() int {
fake.getSubscriberForwarderStateMutex.RLock()
defer fake.getSubscriberForwarderStateMutex.RUnlock()
return len(fake.getSubscriberForwarderStateArgsForCall)
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateCalls(stub func(types.LocalParticipant) (map[livekit.TrackID]*livekit.RTPForwarderState, error)) {
fake.getSubscriberForwarderStateMutex.Lock()
defer fake.getSubscriberForwarderStateMutex.Unlock()
fake.GetSubscriberForwarderStateStub = stub
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateArgsForCall(i int) types.LocalParticipant {
fake.getSubscriberForwarderStateMutex.RLock()
defer fake.getSubscriberForwarderStateMutex.RUnlock()
argsForCall := fake.getSubscriberForwarderStateArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateReturns(result1 map[livekit.TrackID]*livekit.RTPForwarderState, result2 error) {
fake.getSubscriberForwarderStateMutex.Lock()
defer fake.getSubscriberForwarderStateMutex.Unlock()
fake.GetSubscriberForwarderStateStub = nil
fake.getSubscriberForwarderStateReturns = struct {
result1 map[livekit.TrackID]*livekit.RTPForwarderState
result2 error
}{result1, result2}
}
func (fake *FakeLocalParticipantHelper) GetSubscriberForwarderStateReturnsOnCall(i int, result1 map[livekit.TrackID]*livekit.RTPForwarderState, result2 error) {
fake.getSubscriberForwarderStateMutex.Lock()
defer fake.getSubscriberForwarderStateMutex.Unlock()
fake.GetSubscriberForwarderStateStub = nil
if fake.getSubscriberForwarderStateReturnsOnCall == nil {
fake.getSubscriberForwarderStateReturnsOnCall = make(map[int]struct {
result1 map[livekit.TrackID]*livekit.RTPForwarderState
result2 error
})
}
fake.getSubscriberForwarderStateReturnsOnCall[i] = struct {
result1 map[livekit.TrackID]*livekit.RTPForwarderState
result2 error
}{result1, result2}
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrack(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.DataResolverResult {
fake.resolveDataTrackMutex.Lock()
ret, specificReturn := fake.resolveDataTrackReturnsOnCall[len(fake.resolveDataTrackArgsForCall)]
fake.resolveDataTrackArgsForCall = append(fake.resolveDataTrackArgsForCall, struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}{arg1, arg2})
stub := fake.ResolveDataTrackStub
fakeReturns := fake.resolveDataTrackReturns
fake.recordInvocation("ResolveDataTrack", []interface{}{arg1, arg2})
fake.resolveDataTrackMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrackCallCount() int {
fake.resolveDataTrackMutex.RLock()
defer fake.resolveDataTrackMutex.RUnlock()
return len(fake.resolveDataTrackArgsForCall)
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrackCalls(stub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult) {
fake.resolveDataTrackMutex.Lock()
defer fake.resolveDataTrackMutex.Unlock()
fake.ResolveDataTrackStub = stub
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrackArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
fake.resolveDataTrackMutex.RLock()
defer fake.resolveDataTrackMutex.RUnlock()
argsForCall := fake.resolveDataTrackArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrackReturns(result1 types.DataResolverResult) {
fake.resolveDataTrackMutex.Lock()
defer fake.resolveDataTrackMutex.Unlock()
fake.ResolveDataTrackStub = nil
fake.resolveDataTrackReturns = struct {
result1 types.DataResolverResult
}{result1}
}
func (fake *FakeLocalParticipantHelper) ResolveDataTrackReturnsOnCall(i int, result1 types.DataResolverResult) {
fake.resolveDataTrackMutex.Lock()
defer fake.resolveDataTrackMutex.Unlock()
fake.ResolveDataTrackStub = nil
if fake.resolveDataTrackReturnsOnCall == nil {
fake.resolveDataTrackReturnsOnCall = make(map[int]struct {
result1 types.DataResolverResult
})
}
fake.resolveDataTrackReturnsOnCall[i] = struct {
result1 types.DataResolverResult
}{result1}
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrack(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.MediaResolverResult {
fake.resolveMediaTrackMutex.Lock()
ret, specificReturn := fake.resolveMediaTrackReturnsOnCall[len(fake.resolveMediaTrackArgsForCall)]
fake.resolveMediaTrackArgsForCall = append(fake.resolveMediaTrackArgsForCall, struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}{arg1, arg2})
stub := fake.ResolveMediaTrackStub
fakeReturns := fake.resolveMediaTrackReturns
fake.recordInvocation("ResolveMediaTrack", []interface{}{arg1, arg2})
fake.resolveMediaTrackMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackCallCount() int {
fake.resolveMediaTrackMutex.RLock()
defer fake.resolveMediaTrackMutex.RUnlock()
return len(fake.resolveMediaTrackArgsForCall)
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackCalls(stub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult) {
fake.resolveMediaTrackMutex.Lock()
defer fake.resolveMediaTrackMutex.Unlock()
fake.ResolveMediaTrackStub = stub
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
fake.resolveMediaTrackMutex.RLock()
defer fake.resolveMediaTrackMutex.RUnlock()
argsForCall := fake.resolveMediaTrackArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackReturns(result1 types.MediaResolverResult) {
fake.resolveMediaTrackMutex.Lock()
defer fake.resolveMediaTrackMutex.Unlock()
fake.ResolveMediaTrackStub = nil
fake.resolveMediaTrackReturns = struct {
result1 types.MediaResolverResult
}{result1}
}
func (fake *FakeLocalParticipantHelper) ResolveMediaTrackReturnsOnCall(i int, result1 types.MediaResolverResult) {
fake.resolveMediaTrackMutex.Lock()
defer fake.resolveMediaTrackMutex.Unlock()
fake.ResolveMediaTrackStub = nil
if fake.resolveMediaTrackReturnsOnCall == nil {
fake.resolveMediaTrackReturnsOnCall = make(map[int]struct {
result1 types.MediaResolverResult
})
}
fake.resolveMediaTrackReturnsOnCall[i] = struct {
result1 types.MediaResolverResult
}{result1}
}
func (fake *FakeLocalParticipantHelper) ShouldRegressCodec() bool {
fake.shouldRegressCodecMutex.Lock()
ret, specificReturn := fake.shouldRegressCodecReturnsOnCall[len(fake.shouldRegressCodecArgsForCall)]
fake.shouldRegressCodecArgsForCall = append(fake.shouldRegressCodecArgsForCall, struct {
}{})
stub := fake.ShouldRegressCodecStub
fakeReturns := fake.shouldRegressCodecReturns
fake.recordInvocation("ShouldRegressCodec", []interface{}{})
fake.shouldRegressCodecMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecCallCount() int {
fake.shouldRegressCodecMutex.RLock()
defer fake.shouldRegressCodecMutex.RUnlock()
return len(fake.shouldRegressCodecArgsForCall)
}
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecCalls(stub func() bool) {
fake.shouldRegressCodecMutex.Lock()
defer fake.shouldRegressCodecMutex.Unlock()
fake.ShouldRegressCodecStub = stub
}
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecReturns(result1 bool) {
fake.shouldRegressCodecMutex.Lock()
defer fake.shouldRegressCodecMutex.Unlock()
fake.ShouldRegressCodecStub = nil
fake.shouldRegressCodecReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalParticipantHelper) ShouldRegressCodecReturnsOnCall(i int, result1 bool) {
fake.shouldRegressCodecMutex.Lock()
defer fake.shouldRegressCodecMutex.Unlock()
fake.ShouldRegressCodecStub = nil
if fake.shouldRegressCodecReturnsOnCall == nil {
fake.shouldRegressCodecReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.shouldRegressCodecReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeLocalParticipantHelper) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeLocalParticipantHelper) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.LocalParticipantHelper = new(FakeLocalParticipantHelper)
@@ -0,0 +1,948 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeLocalParticipantListener struct {
OnDataMessageStub func(types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)
onDataMessageMutex sync.RWMutex
onDataMessageArgsForCall []struct {
arg1 types.LocalParticipant
arg2 livekit.DataPacket_Kind
arg3 *livekit.DataPacket
}
OnDataMessageUnlabeledStub func(types.LocalParticipant, []byte)
onDataMessageUnlabeledMutex sync.RWMutex
onDataMessageUnlabeledArgsForCall []struct {
arg1 types.LocalParticipant
arg2 []byte
}
OnDataTrackMessageStub func(types.Participant, []byte, *datatrack.Packet)
onDataTrackMessageMutex sync.RWMutex
onDataTrackMessageArgsForCall []struct {
arg1 types.Participant
arg2 []byte
arg3 *datatrack.Packet
}
OnDataTrackPublishedStub func(types.Participant, types.DataTrack)
onDataTrackPublishedMutex sync.RWMutex
onDataTrackPublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.DataTrack
}
OnDataTrackUnpublishedStub func(types.Participant, types.DataTrack)
onDataTrackUnpublishedMutex sync.RWMutex
onDataTrackUnpublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.DataTrack
}
OnLeaveStub func(types.LocalParticipant, types.ParticipantCloseReason)
onLeaveMutex sync.RWMutex
onLeaveArgsForCall []struct {
arg1 types.LocalParticipant
arg2 types.ParticipantCloseReason
}
OnMetricsStub func(types.Participant, *livekit.DataPacket)
onMetricsMutex sync.RWMutex
onMetricsArgsForCall []struct {
arg1 types.Participant
arg2 *livekit.DataPacket
}
OnMigrateStateChangeStub func(types.LocalParticipant, types.MigrateState)
onMigrateStateChangeMutex sync.RWMutex
onMigrateStateChangeArgsForCall []struct {
arg1 types.LocalParticipant
arg2 types.MigrateState
}
OnParticipantUpdateStub func(types.Participant)
onParticipantUpdateMutex sync.RWMutex
onParticipantUpdateArgsForCall []struct {
arg1 types.Participant
}
OnSimulateScenarioStub func(types.LocalParticipant, *livekit.SimulateScenario) error
onSimulateScenarioMutex sync.RWMutex
onSimulateScenarioArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SimulateScenario
}
onSimulateScenarioReturns struct {
result1 error
}
onSimulateScenarioReturnsOnCall map[int]struct {
result1 error
}
OnStateChangeStub func(types.LocalParticipant)
onStateChangeMutex sync.RWMutex
onStateChangeArgsForCall []struct {
arg1 types.LocalParticipant
}
OnSubscribeStatusChangedStub func(types.LocalParticipant, livekit.ParticipantID, bool)
onSubscribeStatusChangedMutex sync.RWMutex
onSubscribeStatusChangedArgsForCall []struct {
arg1 types.LocalParticipant
arg2 livekit.ParticipantID
arg3 bool
}
OnSubscriberReadyStub func(types.LocalParticipant)
onSubscriberReadyMutex sync.RWMutex
onSubscriberReadyArgsForCall []struct {
arg1 types.LocalParticipant
}
OnSyncStateStub func(types.LocalParticipant, *livekit.SyncState) error
onSyncStateMutex sync.RWMutex
onSyncStateArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SyncState
}
onSyncStateReturns struct {
result1 error
}
onSyncStateReturnsOnCall map[int]struct {
result1 error
}
OnTrackPublishedStub func(types.Participant, types.MediaTrack)
onTrackPublishedMutex sync.RWMutex
onTrackPublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
OnTrackUnpublishedStub func(types.Participant, types.MediaTrack)
onTrackUnpublishedMutex sync.RWMutex
onTrackUnpublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
OnTrackUpdatedStub func(types.Participant, types.MediaTrack)
onTrackUpdatedMutex sync.RWMutex
onTrackUpdatedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
OnUpdateDataSubscriptionsStub func(types.LocalParticipant, *livekit.UpdateDataSubscription)
onUpdateDataSubscriptionsMutex sync.RWMutex
onUpdateDataSubscriptionsArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.UpdateDataSubscription
}
OnUpdateSubscriptionPermissionStub func(types.LocalParticipant, *livekit.SubscriptionPermission) error
onUpdateSubscriptionPermissionMutex sync.RWMutex
onUpdateSubscriptionPermissionArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SubscriptionPermission
}
onUpdateSubscriptionPermissionReturns struct {
result1 error
}
onUpdateSubscriptionPermissionReturnsOnCall map[int]struct {
result1 error
}
OnUpdateSubscriptionsStub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)
onUpdateSubscriptionsMutex sync.RWMutex
onUpdateSubscriptionsArgsForCall []struct {
arg1 types.LocalParticipant
arg2 []livekit.TrackID
arg3 []*livekit.ParticipantTracks
arg4 bool
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeLocalParticipantListener) OnDataMessage(arg1 types.LocalParticipant, arg2 livekit.DataPacket_Kind, arg3 *livekit.DataPacket) {
fake.onDataMessageMutex.Lock()
fake.onDataMessageArgsForCall = append(fake.onDataMessageArgsForCall, struct {
arg1 types.LocalParticipant
arg2 livekit.DataPacket_Kind
arg3 *livekit.DataPacket
}{arg1, arg2, arg3})
stub := fake.OnDataMessageStub
fake.recordInvocation("OnDataMessage", []interface{}{arg1, arg2, arg3})
fake.onDataMessageMutex.Unlock()
if stub != nil {
fake.OnDataMessageStub(arg1, arg2, arg3)
}
}
func (fake *FakeLocalParticipantListener) OnDataMessageCallCount() int {
fake.onDataMessageMutex.RLock()
defer fake.onDataMessageMutex.RUnlock()
return len(fake.onDataMessageArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnDataMessageCalls(stub func(types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket)) {
fake.onDataMessageMutex.Lock()
defer fake.onDataMessageMutex.Unlock()
fake.OnDataMessageStub = stub
}
func (fake *FakeLocalParticipantListener) OnDataMessageArgsForCall(i int) (types.LocalParticipant, livekit.DataPacket_Kind, *livekit.DataPacket) {
fake.onDataMessageMutex.RLock()
defer fake.onDataMessageMutex.RUnlock()
argsForCall := fake.onDataMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeled(arg1 types.LocalParticipant, arg2 []byte) {
var arg2Copy []byte
if arg2 != nil {
arg2Copy = make([]byte, len(arg2))
copy(arg2Copy, arg2)
}
fake.onDataMessageUnlabeledMutex.Lock()
fake.onDataMessageUnlabeledArgsForCall = append(fake.onDataMessageUnlabeledArgsForCall, struct {
arg1 types.LocalParticipant
arg2 []byte
}{arg1, arg2Copy})
stub := fake.OnDataMessageUnlabeledStub
fake.recordInvocation("OnDataMessageUnlabeled", []interface{}{arg1, arg2Copy})
fake.onDataMessageUnlabeledMutex.Unlock()
if stub != nil {
fake.OnDataMessageUnlabeledStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledCallCount() int {
fake.onDataMessageUnlabeledMutex.RLock()
defer fake.onDataMessageUnlabeledMutex.RUnlock()
return len(fake.onDataMessageUnlabeledArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledCalls(stub func(types.LocalParticipant, []byte)) {
fake.onDataMessageUnlabeledMutex.Lock()
defer fake.onDataMessageUnlabeledMutex.Unlock()
fake.OnDataMessageUnlabeledStub = stub
}
func (fake *FakeLocalParticipantListener) OnDataMessageUnlabeledArgsForCall(i int) (types.LocalParticipant, []byte) {
fake.onDataMessageUnlabeledMutex.RLock()
defer fake.onDataMessageUnlabeledMutex.RUnlock()
argsForCall := fake.onDataMessageUnlabeledArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnDataTrackMessage(arg1 types.Participant, arg2 []byte, arg3 *datatrack.Packet) {
var arg2Copy []byte
if arg2 != nil {
arg2Copy = make([]byte, len(arg2))
copy(arg2Copy, arg2)
}
fake.onDataTrackMessageMutex.Lock()
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
arg1 types.Participant
arg2 []byte
arg3 *datatrack.Packet
}{arg1, arg2Copy, arg3})
stub := fake.OnDataTrackMessageStub
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1, arg2Copy, arg3})
fake.onDataTrackMessageMutex.Unlock()
if stub != nil {
fake.OnDataTrackMessageStub(arg1, arg2, arg3)
}
}
func (fake *FakeLocalParticipantListener) OnDataTrackMessageCallCount() int {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
return len(fake.onDataTrackMessageArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnDataTrackMessageCalls(stub func(types.Participant, []byte, *datatrack.Packet)) {
fake.onDataTrackMessageMutex.Lock()
defer fake.onDataTrackMessageMutex.Unlock()
fake.OnDataTrackMessageStub = stub
}
func (fake *FakeLocalParticipantListener) OnDataTrackMessageArgsForCall(i int) (types.Participant, []byte, *datatrack.Packet) {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
argsForCall := fake.onDataTrackMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeLocalParticipantListener) OnDataTrackPublished(arg1 types.Participant, arg2 types.DataTrack) {
fake.onDataTrackPublishedMutex.Lock()
fake.onDataTrackPublishedArgsForCall = append(fake.onDataTrackPublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.DataTrack
}{arg1, arg2})
stub := fake.OnDataTrackPublishedStub
fake.recordInvocation("OnDataTrackPublished", []interface{}{arg1, arg2})
fake.onDataTrackPublishedMutex.Unlock()
if stub != nil {
fake.OnDataTrackPublishedStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedCallCount() int {
fake.onDataTrackPublishedMutex.RLock()
defer fake.onDataTrackPublishedMutex.RUnlock()
return len(fake.onDataTrackPublishedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedCalls(stub func(types.Participant, types.DataTrack)) {
fake.onDataTrackPublishedMutex.Lock()
defer fake.onDataTrackPublishedMutex.Unlock()
fake.OnDataTrackPublishedStub = stub
}
func (fake *FakeLocalParticipantListener) OnDataTrackPublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
fake.onDataTrackPublishedMutex.RLock()
defer fake.onDataTrackPublishedMutex.RUnlock()
argsForCall := fake.onDataTrackPublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublished(arg1 types.Participant, arg2 types.DataTrack) {
fake.onDataTrackUnpublishedMutex.Lock()
fake.onDataTrackUnpublishedArgsForCall = append(fake.onDataTrackUnpublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.DataTrack
}{arg1, arg2})
stub := fake.OnDataTrackUnpublishedStub
fake.recordInvocation("OnDataTrackUnpublished", []interface{}{arg1, arg2})
fake.onDataTrackUnpublishedMutex.Unlock()
if stub != nil {
fake.OnDataTrackUnpublishedStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedCallCount() int {
fake.onDataTrackUnpublishedMutex.RLock()
defer fake.onDataTrackUnpublishedMutex.RUnlock()
return len(fake.onDataTrackUnpublishedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedCalls(stub func(types.Participant, types.DataTrack)) {
fake.onDataTrackUnpublishedMutex.Lock()
defer fake.onDataTrackUnpublishedMutex.Unlock()
fake.OnDataTrackUnpublishedStub = stub
}
func (fake *FakeLocalParticipantListener) OnDataTrackUnpublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
fake.onDataTrackUnpublishedMutex.RLock()
defer fake.onDataTrackUnpublishedMutex.RUnlock()
argsForCall := fake.onDataTrackUnpublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnLeave(arg1 types.LocalParticipant, arg2 types.ParticipantCloseReason) {
fake.onLeaveMutex.Lock()
fake.onLeaveArgsForCall = append(fake.onLeaveArgsForCall, struct {
arg1 types.LocalParticipant
arg2 types.ParticipantCloseReason
}{arg1, arg2})
stub := fake.OnLeaveStub
fake.recordInvocation("OnLeave", []interface{}{arg1, arg2})
fake.onLeaveMutex.Unlock()
if stub != nil {
fake.OnLeaveStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnLeaveCallCount() int {
fake.onLeaveMutex.RLock()
defer fake.onLeaveMutex.RUnlock()
return len(fake.onLeaveArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnLeaveCalls(stub func(types.LocalParticipant, types.ParticipantCloseReason)) {
fake.onLeaveMutex.Lock()
defer fake.onLeaveMutex.Unlock()
fake.OnLeaveStub = stub
}
func (fake *FakeLocalParticipantListener) OnLeaveArgsForCall(i int) (types.LocalParticipant, types.ParticipantCloseReason) {
fake.onLeaveMutex.RLock()
defer fake.onLeaveMutex.RUnlock()
argsForCall := fake.onLeaveArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnMetrics(arg1 types.Participant, arg2 *livekit.DataPacket) {
fake.onMetricsMutex.Lock()
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
arg1 types.Participant
arg2 *livekit.DataPacket
}{arg1, arg2})
stub := fake.OnMetricsStub
fake.recordInvocation("OnMetrics", []interface{}{arg1, arg2})
fake.onMetricsMutex.Unlock()
if stub != nil {
fake.OnMetricsStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnMetricsCallCount() int {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
return len(fake.onMetricsArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnMetricsCalls(stub func(types.Participant, *livekit.DataPacket)) {
fake.onMetricsMutex.Lock()
defer fake.onMetricsMutex.Unlock()
fake.OnMetricsStub = stub
}
func (fake *FakeLocalParticipantListener) OnMetricsArgsForCall(i int) (types.Participant, *livekit.DataPacket) {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
argsForCall := fake.onMetricsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnMigrateStateChange(arg1 types.LocalParticipant, arg2 types.MigrateState) {
fake.onMigrateStateChangeMutex.Lock()
fake.onMigrateStateChangeArgsForCall = append(fake.onMigrateStateChangeArgsForCall, struct {
arg1 types.LocalParticipant
arg2 types.MigrateState
}{arg1, arg2})
stub := fake.OnMigrateStateChangeStub
fake.recordInvocation("OnMigrateStateChange", []interface{}{arg1, arg2})
fake.onMigrateStateChangeMutex.Unlock()
if stub != nil {
fake.OnMigrateStateChangeStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeCallCount() int {
fake.onMigrateStateChangeMutex.RLock()
defer fake.onMigrateStateChangeMutex.RUnlock()
return len(fake.onMigrateStateChangeArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeCalls(stub func(types.LocalParticipant, types.MigrateState)) {
fake.onMigrateStateChangeMutex.Lock()
defer fake.onMigrateStateChangeMutex.Unlock()
fake.OnMigrateStateChangeStub = stub
}
func (fake *FakeLocalParticipantListener) OnMigrateStateChangeArgsForCall(i int) (types.LocalParticipant, types.MigrateState) {
fake.onMigrateStateChangeMutex.RLock()
defer fake.onMigrateStateChangeMutex.RUnlock()
argsForCall := fake.onMigrateStateChangeArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnParticipantUpdate(arg1 types.Participant) {
fake.onParticipantUpdateMutex.Lock()
fake.onParticipantUpdateArgsForCall = append(fake.onParticipantUpdateArgsForCall, struct {
arg1 types.Participant
}{arg1})
stub := fake.OnParticipantUpdateStub
fake.recordInvocation("OnParticipantUpdate", []interface{}{arg1})
fake.onParticipantUpdateMutex.Unlock()
if stub != nil {
fake.OnParticipantUpdateStub(arg1)
}
}
func (fake *FakeLocalParticipantListener) OnParticipantUpdateCallCount() int {
fake.onParticipantUpdateMutex.RLock()
defer fake.onParticipantUpdateMutex.RUnlock()
return len(fake.onParticipantUpdateArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnParticipantUpdateCalls(stub func(types.Participant)) {
fake.onParticipantUpdateMutex.Lock()
defer fake.onParticipantUpdateMutex.Unlock()
fake.OnParticipantUpdateStub = stub
}
func (fake *FakeLocalParticipantListener) OnParticipantUpdateArgsForCall(i int) types.Participant {
fake.onParticipantUpdateMutex.RLock()
defer fake.onParticipantUpdateMutex.RUnlock()
argsForCall := fake.onParticipantUpdateArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantListener) OnSimulateScenario(arg1 types.LocalParticipant, arg2 *livekit.SimulateScenario) error {
fake.onSimulateScenarioMutex.Lock()
ret, specificReturn := fake.onSimulateScenarioReturnsOnCall[len(fake.onSimulateScenarioArgsForCall)]
fake.onSimulateScenarioArgsForCall = append(fake.onSimulateScenarioArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SimulateScenario
}{arg1, arg2})
stub := fake.OnSimulateScenarioStub
fakeReturns := fake.onSimulateScenarioReturns
fake.recordInvocation("OnSimulateScenario", []interface{}{arg1, arg2})
fake.onSimulateScenarioMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantListener) OnSimulateScenarioCallCount() int {
fake.onSimulateScenarioMutex.RLock()
defer fake.onSimulateScenarioMutex.RUnlock()
return len(fake.onSimulateScenarioArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnSimulateScenarioCalls(stub func(types.LocalParticipant, *livekit.SimulateScenario) error) {
fake.onSimulateScenarioMutex.Lock()
defer fake.onSimulateScenarioMutex.Unlock()
fake.OnSimulateScenarioStub = stub
}
func (fake *FakeLocalParticipantListener) OnSimulateScenarioArgsForCall(i int) (types.LocalParticipant, *livekit.SimulateScenario) {
fake.onSimulateScenarioMutex.RLock()
defer fake.onSimulateScenarioMutex.RUnlock()
argsForCall := fake.onSimulateScenarioArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnSimulateScenarioReturns(result1 error) {
fake.onSimulateScenarioMutex.Lock()
defer fake.onSimulateScenarioMutex.Unlock()
fake.OnSimulateScenarioStub = nil
fake.onSimulateScenarioReturns = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnSimulateScenarioReturnsOnCall(i int, result1 error) {
fake.onSimulateScenarioMutex.Lock()
defer fake.onSimulateScenarioMutex.Unlock()
fake.OnSimulateScenarioStub = nil
if fake.onSimulateScenarioReturnsOnCall == nil {
fake.onSimulateScenarioReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.onSimulateScenarioReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnStateChange(arg1 types.LocalParticipant) {
fake.onStateChangeMutex.Lock()
fake.onStateChangeArgsForCall = append(fake.onStateChangeArgsForCall, struct {
arg1 types.LocalParticipant
}{arg1})
stub := fake.OnStateChangeStub
fake.recordInvocation("OnStateChange", []interface{}{arg1})
fake.onStateChangeMutex.Unlock()
if stub != nil {
fake.OnStateChangeStub(arg1)
}
}
func (fake *FakeLocalParticipantListener) OnStateChangeCallCount() int {
fake.onStateChangeMutex.RLock()
defer fake.onStateChangeMutex.RUnlock()
return len(fake.onStateChangeArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnStateChangeCalls(stub func(types.LocalParticipant)) {
fake.onStateChangeMutex.Lock()
defer fake.onStateChangeMutex.Unlock()
fake.OnStateChangeStub = stub
}
func (fake *FakeLocalParticipantListener) OnStateChangeArgsForCall(i int) types.LocalParticipant {
fake.onStateChangeMutex.RLock()
defer fake.onStateChangeMutex.RUnlock()
argsForCall := fake.onStateChangeArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChanged(arg1 types.LocalParticipant, arg2 livekit.ParticipantID, arg3 bool) {
fake.onSubscribeStatusChangedMutex.Lock()
fake.onSubscribeStatusChangedArgsForCall = append(fake.onSubscribeStatusChangedArgsForCall, struct {
arg1 types.LocalParticipant
arg2 livekit.ParticipantID
arg3 bool
}{arg1, arg2, arg3})
stub := fake.OnSubscribeStatusChangedStub
fake.recordInvocation("OnSubscribeStatusChanged", []interface{}{arg1, arg2, arg3})
fake.onSubscribeStatusChangedMutex.Unlock()
if stub != nil {
fake.OnSubscribeStatusChangedStub(arg1, arg2, arg3)
}
}
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedCallCount() int {
fake.onSubscribeStatusChangedMutex.RLock()
defer fake.onSubscribeStatusChangedMutex.RUnlock()
return len(fake.onSubscribeStatusChangedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedCalls(stub func(types.LocalParticipant, livekit.ParticipantID, bool)) {
fake.onSubscribeStatusChangedMutex.Lock()
defer fake.onSubscribeStatusChangedMutex.Unlock()
fake.OnSubscribeStatusChangedStub = stub
}
func (fake *FakeLocalParticipantListener) OnSubscribeStatusChangedArgsForCall(i int) (types.LocalParticipant, livekit.ParticipantID, bool) {
fake.onSubscribeStatusChangedMutex.RLock()
defer fake.onSubscribeStatusChangedMutex.RUnlock()
argsForCall := fake.onSubscribeStatusChangedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeLocalParticipantListener) OnSubscriberReady(arg1 types.LocalParticipant) {
fake.onSubscriberReadyMutex.Lock()
fake.onSubscriberReadyArgsForCall = append(fake.onSubscriberReadyArgsForCall, struct {
arg1 types.LocalParticipant
}{arg1})
stub := fake.OnSubscriberReadyStub
fake.recordInvocation("OnSubscriberReady", []interface{}{arg1})
fake.onSubscriberReadyMutex.Unlock()
if stub != nil {
fake.OnSubscriberReadyStub(arg1)
}
}
func (fake *FakeLocalParticipantListener) OnSubscriberReadyCallCount() int {
fake.onSubscriberReadyMutex.RLock()
defer fake.onSubscriberReadyMutex.RUnlock()
return len(fake.onSubscriberReadyArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnSubscriberReadyCalls(stub func(types.LocalParticipant)) {
fake.onSubscriberReadyMutex.Lock()
defer fake.onSubscriberReadyMutex.Unlock()
fake.OnSubscriberReadyStub = stub
}
func (fake *FakeLocalParticipantListener) OnSubscriberReadyArgsForCall(i int) types.LocalParticipant {
fake.onSubscriberReadyMutex.RLock()
defer fake.onSubscriberReadyMutex.RUnlock()
argsForCall := fake.onSubscriberReadyArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeLocalParticipantListener) OnSyncState(arg1 types.LocalParticipant, arg2 *livekit.SyncState) error {
fake.onSyncStateMutex.Lock()
ret, specificReturn := fake.onSyncStateReturnsOnCall[len(fake.onSyncStateArgsForCall)]
fake.onSyncStateArgsForCall = append(fake.onSyncStateArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SyncState
}{arg1, arg2})
stub := fake.OnSyncStateStub
fakeReturns := fake.onSyncStateReturns
fake.recordInvocation("OnSyncState", []interface{}{arg1, arg2})
fake.onSyncStateMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantListener) OnSyncStateCallCount() int {
fake.onSyncStateMutex.RLock()
defer fake.onSyncStateMutex.RUnlock()
return len(fake.onSyncStateArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnSyncStateCalls(stub func(types.LocalParticipant, *livekit.SyncState) error) {
fake.onSyncStateMutex.Lock()
defer fake.onSyncStateMutex.Unlock()
fake.OnSyncStateStub = stub
}
func (fake *FakeLocalParticipantListener) OnSyncStateArgsForCall(i int) (types.LocalParticipant, *livekit.SyncState) {
fake.onSyncStateMutex.RLock()
defer fake.onSyncStateMutex.RUnlock()
argsForCall := fake.onSyncStateArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnSyncStateReturns(result1 error) {
fake.onSyncStateMutex.Lock()
defer fake.onSyncStateMutex.Unlock()
fake.OnSyncStateStub = nil
fake.onSyncStateReturns = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnSyncStateReturnsOnCall(i int, result1 error) {
fake.onSyncStateMutex.Lock()
defer fake.onSyncStateMutex.Unlock()
fake.OnSyncStateStub = nil
if fake.onSyncStateReturnsOnCall == nil {
fake.onSyncStateReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.onSyncStateReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnTrackPublished(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackPublishedMutex.Lock()
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackPublishedStub
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2})
fake.onTrackPublishedMutex.Unlock()
if stub != nil {
fake.OnTrackPublishedStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnTrackPublishedCallCount() int {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
return len(fake.onTrackPublishedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnTrackPublishedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackPublishedMutex.Lock()
defer fake.onTrackPublishedMutex.Unlock()
fake.OnTrackPublishedStub = stub
}
func (fake *FakeLocalParticipantListener) OnTrackPublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
argsForCall := fake.onTrackPublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnTrackUnpublished(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackUnpublishedMutex.Lock()
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackUnpublishedStub
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2})
fake.onTrackUnpublishedMutex.Unlock()
if stub != nil {
fake.OnTrackUnpublishedStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedCallCount() int {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
return len(fake.onTrackUnpublishedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackUnpublishedMutex.Lock()
defer fake.onTrackUnpublishedMutex.Unlock()
fake.OnTrackUnpublishedStub = stub
}
func (fake *FakeLocalParticipantListener) OnTrackUnpublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnTrackUpdated(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackUpdatedMutex.Lock()
fake.onTrackUpdatedArgsForCall = append(fake.onTrackUpdatedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackUpdatedStub
fake.recordInvocation("OnTrackUpdated", []interface{}{arg1, arg2})
fake.onTrackUpdatedMutex.Unlock()
if stub != nil {
fake.OnTrackUpdatedStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnTrackUpdatedCallCount() int {
fake.onTrackUpdatedMutex.RLock()
defer fake.onTrackUpdatedMutex.RUnlock()
return len(fake.onTrackUpdatedArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnTrackUpdatedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackUpdatedMutex.Lock()
defer fake.onTrackUpdatedMutex.Unlock()
fake.OnTrackUpdatedStub = stub
}
func (fake *FakeLocalParticipantListener) OnTrackUpdatedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackUpdatedMutex.RLock()
defer fake.onTrackUpdatedMutex.RUnlock()
argsForCall := fake.onTrackUpdatedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptions(arg1 types.LocalParticipant, arg2 *livekit.UpdateDataSubscription) {
fake.onUpdateDataSubscriptionsMutex.Lock()
fake.onUpdateDataSubscriptionsArgsForCall = append(fake.onUpdateDataSubscriptionsArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.UpdateDataSubscription
}{arg1, arg2})
stub := fake.OnUpdateDataSubscriptionsStub
fake.recordInvocation("OnUpdateDataSubscriptions", []interface{}{arg1, arg2})
fake.onUpdateDataSubscriptionsMutex.Unlock()
if stub != nil {
fake.OnUpdateDataSubscriptionsStub(arg1, arg2)
}
}
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsCallCount() int {
fake.onUpdateDataSubscriptionsMutex.RLock()
defer fake.onUpdateDataSubscriptionsMutex.RUnlock()
return len(fake.onUpdateDataSubscriptionsArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsCalls(stub func(types.LocalParticipant, *livekit.UpdateDataSubscription)) {
fake.onUpdateDataSubscriptionsMutex.Lock()
defer fake.onUpdateDataSubscriptionsMutex.Unlock()
fake.OnUpdateDataSubscriptionsStub = stub
}
func (fake *FakeLocalParticipantListener) OnUpdateDataSubscriptionsArgsForCall(i int) (types.LocalParticipant, *livekit.UpdateDataSubscription) {
fake.onUpdateDataSubscriptionsMutex.RLock()
defer fake.onUpdateDataSubscriptionsMutex.RUnlock()
argsForCall := fake.onUpdateDataSubscriptionsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermission(arg1 types.LocalParticipant, arg2 *livekit.SubscriptionPermission) error {
fake.onUpdateSubscriptionPermissionMutex.Lock()
ret, specificReturn := fake.onUpdateSubscriptionPermissionReturnsOnCall[len(fake.onUpdateSubscriptionPermissionArgsForCall)]
fake.onUpdateSubscriptionPermissionArgsForCall = append(fake.onUpdateSubscriptionPermissionArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SubscriptionPermission
}{arg1, arg2})
stub := fake.OnUpdateSubscriptionPermissionStub
fakeReturns := fake.onUpdateSubscriptionPermissionReturns
fake.recordInvocation("OnUpdateSubscriptionPermission", []interface{}{arg1, arg2})
fake.onUpdateSubscriptionPermissionMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionCallCount() int {
fake.onUpdateSubscriptionPermissionMutex.RLock()
defer fake.onUpdateSubscriptionPermissionMutex.RUnlock()
return len(fake.onUpdateSubscriptionPermissionArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionCalls(stub func(types.LocalParticipant, *livekit.SubscriptionPermission) error) {
fake.onUpdateSubscriptionPermissionMutex.Lock()
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
fake.OnUpdateSubscriptionPermissionStub = stub
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionArgsForCall(i int) (types.LocalParticipant, *livekit.SubscriptionPermission) {
fake.onUpdateSubscriptionPermissionMutex.RLock()
defer fake.onUpdateSubscriptionPermissionMutex.RUnlock()
argsForCall := fake.onUpdateSubscriptionPermissionArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionReturns(result1 error) {
fake.onUpdateSubscriptionPermissionMutex.Lock()
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
fake.OnUpdateSubscriptionPermissionStub = nil
fake.onUpdateSubscriptionPermissionReturns = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionPermissionReturnsOnCall(i int, result1 error) {
fake.onUpdateSubscriptionPermissionMutex.Lock()
defer fake.onUpdateSubscriptionPermissionMutex.Unlock()
fake.OnUpdateSubscriptionPermissionStub = nil
if fake.onUpdateSubscriptionPermissionReturnsOnCall == nil {
fake.onUpdateSubscriptionPermissionReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.onUpdateSubscriptionPermissionReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptions(arg1 types.LocalParticipant, arg2 []livekit.TrackID, arg3 []*livekit.ParticipantTracks, arg4 bool) {
var arg2Copy []livekit.TrackID
if arg2 != nil {
arg2Copy = make([]livekit.TrackID, len(arg2))
copy(arg2Copy, arg2)
}
var arg3Copy []*livekit.ParticipantTracks
if arg3 != nil {
arg3Copy = make([]*livekit.ParticipantTracks, len(arg3))
copy(arg3Copy, arg3)
}
fake.onUpdateSubscriptionsMutex.Lock()
fake.onUpdateSubscriptionsArgsForCall = append(fake.onUpdateSubscriptionsArgsForCall, struct {
arg1 types.LocalParticipant
arg2 []livekit.TrackID
arg3 []*livekit.ParticipantTracks
arg4 bool
}{arg1, arg2Copy, arg3Copy, arg4})
stub := fake.OnUpdateSubscriptionsStub
fake.recordInvocation("OnUpdateSubscriptions", []interface{}{arg1, arg2Copy, arg3Copy, arg4})
fake.onUpdateSubscriptionsMutex.Unlock()
if stub != nil {
fake.OnUpdateSubscriptionsStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsCallCount() int {
fake.onUpdateSubscriptionsMutex.RLock()
defer fake.onUpdateSubscriptionsMutex.RUnlock()
return len(fake.onUpdateSubscriptionsArgsForCall)
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsCalls(stub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)) {
fake.onUpdateSubscriptionsMutex.Lock()
defer fake.onUpdateSubscriptionsMutex.Unlock()
fake.OnUpdateSubscriptionsStub = stub
}
func (fake *FakeLocalParticipantListener) OnUpdateSubscriptionsArgsForCall(i int) (types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool) {
fake.onUpdateSubscriptionsMutex.RLock()
defer fake.onUpdateSubscriptionsMutex.RUnlock()
argsForCall := fake.onUpdateSubscriptionsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeLocalParticipantListener) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeLocalParticipantListener) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.LocalParticipantListener = new(FakeLocalParticipantListener)
+150 -146
View File
@@ -6,8 +6,9 @@ import (
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
type FakeMediaTrack struct {
@@ -71,11 +72,12 @@ type FakeMediaTrack struct {
getNumSubscribersReturnsOnCall map[int]struct {
result1 int
}
GetQualityForDimensionStub func(uint32, uint32) livekit.VideoQuality
GetQualityForDimensionStub func(mime.MimeType, uint32, uint32) livekit.VideoQuality
getQualityForDimensionMutex sync.RWMutex
getQualityForDimensionArgsForCall []struct {
arg1 uint32
arg1 mime.MimeType
arg2 uint32
arg3 uint32
}
getQualityForDimensionReturns struct {
result1 livekit.VideoQuality
@@ -83,12 +85,12 @@ type FakeMediaTrack struct {
getQualityForDimensionReturnsOnCall map[int]struct {
result1 livekit.VideoQuality
}
GetTemporalLayerForSpatialFpsStub func(int32, uint32, mime.MimeType) int32
GetTemporalLayerForSpatialFpsStub func(mime.MimeType, int32, uint32) int32
getTemporalLayerForSpatialFpsMutex sync.RWMutex
getTemporalLayerForSpatialFpsArgsForCall []struct {
arg1 int32
arg2 uint32
arg3 mime.MimeType
arg1 mime.MimeType
arg2 int32
arg3 uint32
}
getTemporalLayerForSpatialFpsReturns struct {
result1 int32
@@ -96,6 +98,16 @@ type FakeMediaTrack struct {
getTemporalLayerForSpatialFpsReturnsOnCall map[int]struct {
result1 int32
}
HasPacketTrailerStub func() bool
hasPacketTrailerMutex sync.RWMutex
hasPacketTrailerArgsForCall []struct {
}
hasPacketTrailerReturns struct {
result1 bool
}
hasPacketTrailerReturnsOnCall map[int]struct {
result1 bool
}
IDStub func() livekit.TrackID
iDMutex sync.RWMutex
iDArgsForCall []struct {
@@ -136,16 +148,6 @@ type FakeMediaTrack struct {
isOpenReturnsOnCall map[int]struct {
result1 bool
}
IsSimulcastStub func() bool
isSimulcastMutex sync.RWMutex
isSimulcastArgsForCall []struct {
}
isSimulcastReturns struct {
result1 bool
}
isSimulcastReturnsOnCall map[int]struct {
result1 bool
}
IsSubscriberStub func(livekit.ParticipantID) bool
isSubscriberMutex sync.RWMutex
isSubscriberArgsForCall []struct {
@@ -167,6 +169,16 @@ type FakeMediaTrack struct {
kindReturnsOnCall map[int]struct {
result1 livekit.TrackType
}
LoggerStub func() logger.Logger
loggerMutex sync.RWMutex
loggerArgsForCall []struct {
}
loggerReturns struct {
result1 logger.Logger
}
loggerReturnsOnCall map[int]struct {
result1 logger.Logger
}
NameStub func() string
nameMutex sync.RWMutex
nameArgsForCall []struct {
@@ -614,19 +626,20 @@ func (fake *FakeMediaTrack) GetNumSubscribersReturnsOnCall(i int, result1 int) {
}{result1}
}
func (fake *FakeMediaTrack) GetQualityForDimension(arg1 uint32, arg2 uint32) livekit.VideoQuality {
func (fake *FakeMediaTrack) GetQualityForDimension(arg1 mime.MimeType, arg2 uint32, arg3 uint32) livekit.VideoQuality {
fake.getQualityForDimensionMutex.Lock()
ret, specificReturn := fake.getQualityForDimensionReturnsOnCall[len(fake.getQualityForDimensionArgsForCall)]
fake.getQualityForDimensionArgsForCall = append(fake.getQualityForDimensionArgsForCall, struct {
arg1 uint32
arg1 mime.MimeType
arg2 uint32
}{arg1, arg2})
arg3 uint32
}{arg1, arg2, arg3})
stub := fake.GetQualityForDimensionStub
fakeReturns := fake.getQualityForDimensionReturns
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2})
fake.recordInvocation("GetQualityForDimension", []interface{}{arg1, arg2, arg3})
fake.getQualityForDimensionMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
return stub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1
@@ -640,17 +653,17 @@ func (fake *FakeMediaTrack) GetQualityForDimensionCallCount() int {
return len(fake.getQualityForDimensionArgsForCall)
}
func (fake *FakeMediaTrack) GetQualityForDimensionCalls(stub func(uint32, uint32) livekit.VideoQuality) {
func (fake *FakeMediaTrack) GetQualityForDimensionCalls(stub func(mime.MimeType, uint32, uint32) livekit.VideoQuality) {
fake.getQualityForDimensionMutex.Lock()
defer fake.getQualityForDimensionMutex.Unlock()
fake.GetQualityForDimensionStub = stub
}
func (fake *FakeMediaTrack) GetQualityForDimensionArgsForCall(i int) (uint32, uint32) {
func (fake *FakeMediaTrack) GetQualityForDimensionArgsForCall(i int) (mime.MimeType, uint32, uint32) {
fake.getQualityForDimensionMutex.RLock()
defer fake.getQualityForDimensionMutex.RUnlock()
argsForCall := fake.getQualityForDimensionArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeMediaTrack) GetQualityForDimensionReturns(result1 livekit.VideoQuality) {
@@ -676,13 +689,13 @@ func (fake *FakeMediaTrack) GetQualityForDimensionReturnsOnCall(i int, result1 l
}{result1}
}
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFps(arg1 int32, arg2 uint32, arg3 mime.MimeType) int32 {
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFps(arg1 mime.MimeType, arg2 int32, arg3 uint32) int32 {
fake.getTemporalLayerForSpatialFpsMutex.Lock()
ret, specificReturn := fake.getTemporalLayerForSpatialFpsReturnsOnCall[len(fake.getTemporalLayerForSpatialFpsArgsForCall)]
fake.getTemporalLayerForSpatialFpsArgsForCall = append(fake.getTemporalLayerForSpatialFpsArgsForCall, struct {
arg1 int32
arg2 uint32
arg3 mime.MimeType
arg1 mime.MimeType
arg2 int32
arg3 uint32
}{arg1, arg2, arg3})
stub := fake.GetTemporalLayerForSpatialFpsStub
fakeReturns := fake.getTemporalLayerForSpatialFpsReturns
@@ -703,13 +716,13 @@ func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCallCount() int {
return len(fake.getTemporalLayerForSpatialFpsArgsForCall)
}
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(int32, uint32, mime.MimeType) int32) {
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsCalls(stub func(mime.MimeType, int32, uint32) int32) {
fake.getTemporalLayerForSpatialFpsMutex.Lock()
defer fake.getTemporalLayerForSpatialFpsMutex.Unlock()
fake.GetTemporalLayerForSpatialFpsStub = stub
}
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (int32, uint32, mime.MimeType) {
func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsArgsForCall(i int) (mime.MimeType, int32, uint32) {
fake.getTemporalLayerForSpatialFpsMutex.RLock()
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
argsForCall := fake.getTemporalLayerForSpatialFpsArgsForCall[i]
@@ -739,6 +752,59 @@ func (fake *FakeMediaTrack) GetTemporalLayerForSpatialFpsReturnsOnCall(i int, re
}{result1}
}
func (fake *FakeMediaTrack) HasPacketTrailer() bool {
fake.hasPacketTrailerMutex.Lock()
ret, specificReturn := fake.hasPacketTrailerReturnsOnCall[len(fake.hasPacketTrailerArgsForCall)]
fake.hasPacketTrailerArgsForCall = append(fake.hasPacketTrailerArgsForCall, struct {
}{})
stub := fake.HasPacketTrailerStub
fakeReturns := fake.hasPacketTrailerReturns
fake.recordInvocation("HasPacketTrailer", []interface{}{})
fake.hasPacketTrailerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeMediaTrack) HasPacketTrailerCallCount() int {
fake.hasPacketTrailerMutex.RLock()
defer fake.hasPacketTrailerMutex.RUnlock()
return len(fake.hasPacketTrailerArgsForCall)
}
func (fake *FakeMediaTrack) HasPacketTrailerCalls(stub func() bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = stub
}
func (fake *FakeMediaTrack) HasPacketTrailerReturns(result1 bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = nil
fake.hasPacketTrailerReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeMediaTrack) HasPacketTrailerReturnsOnCall(i int, result1 bool) {
fake.hasPacketTrailerMutex.Lock()
defer fake.hasPacketTrailerMutex.Unlock()
fake.HasPacketTrailerStub = nil
if fake.hasPacketTrailerReturnsOnCall == nil {
fake.hasPacketTrailerReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.hasPacketTrailerReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeMediaTrack) ID() livekit.TrackID {
fake.iDMutex.Lock()
ret, specificReturn := fake.iDReturnsOnCall[len(fake.iDArgsForCall)]
@@ -951,59 +1017,6 @@ func (fake *FakeMediaTrack) IsOpenReturnsOnCall(i int, result1 bool) {
}{result1}
}
func (fake *FakeMediaTrack) IsSimulcast() bool {
fake.isSimulcastMutex.Lock()
ret, specificReturn := fake.isSimulcastReturnsOnCall[len(fake.isSimulcastArgsForCall)]
fake.isSimulcastArgsForCall = append(fake.isSimulcastArgsForCall, struct {
}{})
stub := fake.IsSimulcastStub
fakeReturns := fake.isSimulcastReturns
fake.recordInvocation("IsSimulcast", []interface{}{})
fake.isSimulcastMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeMediaTrack) IsSimulcastCallCount() int {
fake.isSimulcastMutex.RLock()
defer fake.isSimulcastMutex.RUnlock()
return len(fake.isSimulcastArgsForCall)
}
func (fake *FakeMediaTrack) IsSimulcastCalls(stub func() bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = stub
}
func (fake *FakeMediaTrack) IsSimulcastReturns(result1 bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = nil
fake.isSimulcastReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeMediaTrack) IsSimulcastReturnsOnCall(i int, result1 bool) {
fake.isSimulcastMutex.Lock()
defer fake.isSimulcastMutex.Unlock()
fake.IsSimulcastStub = nil
if fake.isSimulcastReturnsOnCall == nil {
fake.isSimulcastReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isSimulcastReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeMediaTrack) IsSubscriber(arg1 livekit.ParticipantID) bool {
fake.isSubscriberMutex.Lock()
ret, specificReturn := fake.isSubscriberReturnsOnCall[len(fake.isSubscriberArgsForCall)]
@@ -1118,6 +1131,59 @@ func (fake *FakeMediaTrack) KindReturnsOnCall(i int, result1 livekit.TrackType)
}{result1}
}
func (fake *FakeMediaTrack) Logger() logger.Logger {
fake.loggerMutex.Lock()
ret, specificReturn := fake.loggerReturnsOnCall[len(fake.loggerArgsForCall)]
fake.loggerArgsForCall = append(fake.loggerArgsForCall, struct {
}{})
stub := fake.LoggerStub
fakeReturns := fake.loggerReturns
fake.recordInvocation("Logger", []interface{}{})
fake.loggerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeMediaTrack) LoggerCallCount() int {
fake.loggerMutex.RLock()
defer fake.loggerMutex.RUnlock()
return len(fake.loggerArgsForCall)
}
func (fake *FakeMediaTrack) LoggerCalls(stub func() logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = stub
}
func (fake *FakeMediaTrack) LoggerReturns(result1 logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = nil
fake.loggerReturns = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeMediaTrack) LoggerReturnsOnCall(i int, result1 logger.Logger) {
fake.loggerMutex.Lock()
defer fake.loggerMutex.Unlock()
fake.LoggerStub = nil
if fake.loggerReturnsOnCall == nil {
fake.loggerReturnsOnCall = make(map[int]struct {
result1 logger.Logger
})
}
fake.loggerReturnsOnCall[i] = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeMediaTrack) Name() string {
fake.nameMutex.Lock()
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
@@ -1796,68 +1862,6 @@ func (fake *FakeMediaTrack) UpdateVideoTrackArgsForCall(i int) *livekit.UpdateLo
func (fake *FakeMediaTrack) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.addOnCloseMutex.RLock()
defer fake.addOnCloseMutex.RUnlock()
fake.addSubscriberMutex.RLock()
defer fake.addSubscriberMutex.RUnlock()
fake.clearAllReceiversMutex.RLock()
defer fake.clearAllReceiversMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.getAllSubscribersMutex.RLock()
defer fake.getAllSubscribersMutex.RUnlock()
fake.getAudioLevelMutex.RLock()
defer fake.getAudioLevelMutex.RUnlock()
fake.getNumSubscribersMutex.RLock()
defer fake.getNumSubscribersMutex.RUnlock()
fake.getQualityForDimensionMutex.RLock()
defer fake.getQualityForDimensionMutex.RUnlock()
fake.getTemporalLayerForSpatialFpsMutex.RLock()
defer fake.getTemporalLayerForSpatialFpsMutex.RUnlock()
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
fake.isEncryptedMutex.RLock()
defer fake.isEncryptedMutex.RUnlock()
fake.isMutedMutex.RLock()
defer fake.isMutedMutex.RUnlock()
fake.isOpenMutex.RLock()
defer fake.isOpenMutex.RUnlock()
fake.isSimulcastMutex.RLock()
defer fake.isSimulcastMutex.RUnlock()
fake.isSubscriberMutex.RLock()
defer fake.isSubscriberMutex.RUnlock()
fake.kindMutex.RLock()
defer fake.kindMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.onTrackSubscribedMutex.RLock()
defer fake.onTrackSubscribedMutex.RUnlock()
fake.publisherIDMutex.RLock()
defer fake.publisherIDMutex.RUnlock()
fake.publisherIdentityMutex.RLock()
defer fake.publisherIdentityMutex.RUnlock()
fake.publisherVersionMutex.RLock()
defer fake.publisherVersionMutex.RUnlock()
fake.receiversMutex.RLock()
defer fake.receiversMutex.RUnlock()
fake.removeSubscriberMutex.RLock()
defer fake.removeSubscriberMutex.RUnlock()
fake.revokeDisallowedSubscribersMutex.RLock()
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
fake.setMutedMutex.RLock()
defer fake.setMutedMutex.RUnlock()
fake.sourceMutex.RLock()
defer fake.sourceMutex.RUnlock()
fake.streamMutex.RLock()
defer fake.streamMutex.RUnlock()
fake.toProtoMutex.RLock()
defer fake.toProtoMutex.RUnlock()
fake.updateAudioTrackMutex.RLock()
defer fake.updateAudioTrackMutex.RUnlock()
fake.updateTrackInfoMutex.RLock()
defer fake.updateTrackInfoMutex.RUnlock()
fake.updateVideoTrackMutex.RLock()
defer fake.updateVideoTrackMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
+672 -93
View File
@@ -5,8 +5,10 @@ import (
"sync"
"time"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
)
@@ -54,15 +56,15 @@ type FakeParticipant struct {
connectedAtReturnsOnCall map[int]struct {
result1 time.Time
}
DebugInfoStub func() map[string]interface{}
DebugInfoStub func() map[string]any
debugInfoMutex sync.RWMutex
debugInfoArgsForCall []struct {
}
debugInfoReturns struct {
result1 map[string]interface{}
result1 map[string]any
}
debugInfoReturnsOnCall map[int]struct {
result1 map[string]interface{}
result1 map[string]any
}
GetAudioLevelStub func() (float64, bool)
getAudioLevelMutex sync.RWMutex
@@ -76,6 +78,47 @@ type FakeParticipant struct {
result1 float64
result2 bool
}
GetLoggerStub func() logger.Logger
getLoggerMutex sync.RWMutex
getLoggerArgsForCall []struct {
}
getLoggerReturns struct {
result1 logger.Logger
}
getLoggerReturnsOnCall map[int]struct {
result1 logger.Logger
}
GetParticipantListenerStub func() types.ParticipantListener
getParticipantListenerMutex sync.RWMutex
getParticipantListenerArgsForCall []struct {
}
getParticipantListenerReturns struct {
result1 types.ParticipantListener
}
getParticipantListenerReturnsOnCall map[int]struct {
result1 types.ParticipantListener
}
GetPublishedDataTrackStub func(uint16) types.DataTrack
getPublishedDataTrackMutex sync.RWMutex
getPublishedDataTrackArgsForCall []struct {
arg1 uint16
}
getPublishedDataTrackReturns struct {
result1 types.DataTrack
}
getPublishedDataTrackReturnsOnCall map[int]struct {
result1 types.DataTrack
}
GetPublishedDataTracksStub func() []types.DataTrack
getPublishedDataTracksMutex sync.RWMutex
getPublishedDataTracksArgsForCall []struct {
}
getPublishedDataTracksReturns struct {
result1 []types.DataTrack
}
getPublishedDataTracksReturnsOnCall map[int]struct {
result1 []types.DataTrack
}
GetPublishedTrackStub func(livekit.TrackID) types.MediaTrack
getPublishedTrackMutex sync.RWMutex
getPublishedTrackArgsForCall []struct {
@@ -97,6 +140,13 @@ type FakeParticipant struct {
getPublishedTracksReturnsOnCall map[int]struct {
result1 []types.MediaTrack
}
HandleReceivedDataTrackMessageStub func([]byte, *datatrack.Packet, int64)
handleReceivedDataTrackMessageMutex sync.RWMutex
handleReceivedDataTrackMessageArgsForCall []struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}
HasPermissionStub func(livekit.TrackID, livekit.ParticipantIdentity) bool
hasPermissionMutex sync.RWMutex
hasPermissionArgsForCall []struct {
@@ -149,6 +199,16 @@ type FakeParticipant struct {
isAgentReturnsOnCall map[int]struct {
result1 bool
}
IsClosedStub func() bool
isClosedMutex sync.RWMutex
isClosedArgsForCall []struct {
}
isClosedReturns struct {
result1 bool
}
isClosedReturnsOnCall map[int]struct {
result1 bool
}
IsDependentStub func() bool
isDependentMutex sync.RWMutex
isDependentArgsForCall []struct {
@@ -159,6 +219,16 @@ type FakeParticipant struct {
isDependentReturnsOnCall map[int]struct {
result1 bool
}
IsDisconnectedStub func() bool
isDisconnectedMutex sync.RWMutex
isDisconnectedArgsForCall []struct {
}
isDisconnectedReturns struct {
result1 bool
}
isDisconnectedReturnsOnCall map[int]struct {
result1 bool
}
IsPublisherStub func() bool
isPublisherMutex sync.RWMutex
isPublisherArgsForCall []struct {
@@ -189,17 +259,36 @@ type FakeParticipant struct {
kindReturnsOnCall map[int]struct {
result1 livekit.ParticipantInfo_Kind
}
OnMetricsStub func(func(types.Participant, *livekit.DataPacket))
onMetricsMutex sync.RWMutex
onMetricsArgsForCall []struct {
arg1 func(types.Participant, *livekit.DataPacket)
KindDetailsStub func() []livekit.ParticipantInfo_KindDetail
kindDetailsMutex sync.RWMutex
kindDetailsArgsForCall []struct {
}
RemovePublishedTrackStub func(types.MediaTrack, bool, bool)
kindDetailsReturns struct {
result1 []livekit.ParticipantInfo_KindDetail
}
kindDetailsReturnsOnCall map[int]struct {
result1 []livekit.ParticipantInfo_KindDetail
}
MigrateStateStub func() types.MigrateState
migrateStateMutex sync.RWMutex
migrateStateArgsForCall []struct {
}
migrateStateReturns struct {
result1 types.MigrateState
}
migrateStateReturnsOnCall map[int]struct {
result1 types.MigrateState
}
RemovePublishedDataTrackStub func(types.DataTrack)
removePublishedDataTrackMutex sync.RWMutex
removePublishedDataTrackArgsForCall []struct {
arg1 types.DataTrack
}
RemovePublishedTrackStub func(types.MediaTrack, bool)
removePublishedTrackMutex sync.RWMutex
removePublishedTrackArgsForCall []struct {
arg1 types.MediaTrack
arg2 bool
arg3 bool
}
StateStub func() livekit.ParticipantInfo_State
stateMutex sync.RWMutex
@@ -233,6 +322,18 @@ type FakeParticipant struct {
toProtoReturnsOnCall map[int]struct {
result1 *livekit.ParticipantInfo
}
ToProtoWithVersionStub func() (*livekit.ParticipantInfo, utils.TimedVersion)
toProtoWithVersionMutex sync.RWMutex
toProtoWithVersionArgsForCall []struct {
}
toProtoWithVersionReturns struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
}
toProtoWithVersionReturnsOnCall map[int]struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
}
UpdateSubscriptionPermissionStub func(*livekit.SubscriptionPermission, utils.TimedVersion, func(participantID livekit.ParticipantID) types.LocalParticipant) error
updateSubscriptionPermissionMutex sync.RWMutex
updateSubscriptionPermissionArgsForCall []struct {
@@ -482,7 +583,7 @@ func (fake *FakeParticipant) ConnectedAtReturnsOnCall(i int, result1 time.Time)
}{result1}
}
func (fake *FakeParticipant) DebugInfo() map[string]interface{} {
func (fake *FakeParticipant) DebugInfo() map[string]any {
fake.debugInfoMutex.Lock()
ret, specificReturn := fake.debugInfoReturnsOnCall[len(fake.debugInfoArgsForCall)]
fake.debugInfoArgsForCall = append(fake.debugInfoArgsForCall, struct {
@@ -506,32 +607,32 @@ func (fake *FakeParticipant) DebugInfoCallCount() int {
return len(fake.debugInfoArgsForCall)
}
func (fake *FakeParticipant) DebugInfoCalls(stub func() map[string]interface{}) {
func (fake *FakeParticipant) DebugInfoCalls(stub func() map[string]any) {
fake.debugInfoMutex.Lock()
defer fake.debugInfoMutex.Unlock()
fake.DebugInfoStub = stub
}
func (fake *FakeParticipant) DebugInfoReturns(result1 map[string]interface{}) {
func (fake *FakeParticipant) DebugInfoReturns(result1 map[string]any) {
fake.debugInfoMutex.Lock()
defer fake.debugInfoMutex.Unlock()
fake.DebugInfoStub = nil
fake.debugInfoReturns = struct {
result1 map[string]interface{}
result1 map[string]any
}{result1}
}
func (fake *FakeParticipant) DebugInfoReturnsOnCall(i int, result1 map[string]interface{}) {
func (fake *FakeParticipant) DebugInfoReturnsOnCall(i int, result1 map[string]any) {
fake.debugInfoMutex.Lock()
defer fake.debugInfoMutex.Unlock()
fake.DebugInfoStub = nil
if fake.debugInfoReturnsOnCall == nil {
fake.debugInfoReturnsOnCall = make(map[int]struct {
result1 map[string]interface{}
result1 map[string]any
})
}
fake.debugInfoReturnsOnCall[i] = struct {
result1 map[string]interface{}
result1 map[string]any
}{result1}
}
@@ -591,6 +692,226 @@ func (fake *FakeParticipant) GetAudioLevelReturnsOnCall(i int, result1 float64,
}{result1, result2}
}
func (fake *FakeParticipant) GetLogger() logger.Logger {
fake.getLoggerMutex.Lock()
ret, specificReturn := fake.getLoggerReturnsOnCall[len(fake.getLoggerArgsForCall)]
fake.getLoggerArgsForCall = append(fake.getLoggerArgsForCall, struct {
}{})
stub := fake.GetLoggerStub
fakeReturns := fake.getLoggerReturns
fake.recordInvocation("GetLogger", []interface{}{})
fake.getLoggerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) GetLoggerCallCount() int {
fake.getLoggerMutex.RLock()
defer fake.getLoggerMutex.RUnlock()
return len(fake.getLoggerArgsForCall)
}
func (fake *FakeParticipant) GetLoggerCalls(stub func() logger.Logger) {
fake.getLoggerMutex.Lock()
defer fake.getLoggerMutex.Unlock()
fake.GetLoggerStub = stub
}
func (fake *FakeParticipant) GetLoggerReturns(result1 logger.Logger) {
fake.getLoggerMutex.Lock()
defer fake.getLoggerMutex.Unlock()
fake.GetLoggerStub = nil
fake.getLoggerReturns = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeParticipant) GetLoggerReturnsOnCall(i int, result1 logger.Logger) {
fake.getLoggerMutex.Lock()
defer fake.getLoggerMutex.Unlock()
fake.GetLoggerStub = nil
if fake.getLoggerReturnsOnCall == nil {
fake.getLoggerReturnsOnCall = make(map[int]struct {
result1 logger.Logger
})
}
fake.getLoggerReturnsOnCall[i] = struct {
result1 logger.Logger
}{result1}
}
func (fake *FakeParticipant) GetParticipantListener() types.ParticipantListener {
fake.getParticipantListenerMutex.Lock()
ret, specificReturn := fake.getParticipantListenerReturnsOnCall[len(fake.getParticipantListenerArgsForCall)]
fake.getParticipantListenerArgsForCall = append(fake.getParticipantListenerArgsForCall, struct {
}{})
stub := fake.GetParticipantListenerStub
fakeReturns := fake.getParticipantListenerReturns
fake.recordInvocation("GetParticipantListener", []interface{}{})
fake.getParticipantListenerMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) GetParticipantListenerCallCount() int {
fake.getParticipantListenerMutex.RLock()
defer fake.getParticipantListenerMutex.RUnlock()
return len(fake.getParticipantListenerArgsForCall)
}
func (fake *FakeParticipant) GetParticipantListenerCalls(stub func() types.ParticipantListener) {
fake.getParticipantListenerMutex.Lock()
defer fake.getParticipantListenerMutex.Unlock()
fake.GetParticipantListenerStub = stub
}
func (fake *FakeParticipant) GetParticipantListenerReturns(result1 types.ParticipantListener) {
fake.getParticipantListenerMutex.Lock()
defer fake.getParticipantListenerMutex.Unlock()
fake.GetParticipantListenerStub = nil
fake.getParticipantListenerReturns = struct {
result1 types.ParticipantListener
}{result1}
}
func (fake *FakeParticipant) GetParticipantListenerReturnsOnCall(i int, result1 types.ParticipantListener) {
fake.getParticipantListenerMutex.Lock()
defer fake.getParticipantListenerMutex.Unlock()
fake.GetParticipantListenerStub = nil
if fake.getParticipantListenerReturnsOnCall == nil {
fake.getParticipantListenerReturnsOnCall = make(map[int]struct {
result1 types.ParticipantListener
})
}
fake.getParticipantListenerReturnsOnCall[i] = struct {
result1 types.ParticipantListener
}{result1}
}
func (fake *FakeParticipant) GetPublishedDataTrack(arg1 uint16) types.DataTrack {
fake.getPublishedDataTrackMutex.Lock()
ret, specificReturn := fake.getPublishedDataTrackReturnsOnCall[len(fake.getPublishedDataTrackArgsForCall)]
fake.getPublishedDataTrackArgsForCall = append(fake.getPublishedDataTrackArgsForCall, struct {
arg1 uint16
}{arg1})
stub := fake.GetPublishedDataTrackStub
fakeReturns := fake.getPublishedDataTrackReturns
fake.recordInvocation("GetPublishedDataTrack", []interface{}{arg1})
fake.getPublishedDataTrackMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) GetPublishedDataTrackCallCount() int {
fake.getPublishedDataTrackMutex.RLock()
defer fake.getPublishedDataTrackMutex.RUnlock()
return len(fake.getPublishedDataTrackArgsForCall)
}
func (fake *FakeParticipant) GetPublishedDataTrackCalls(stub func(uint16) types.DataTrack) {
fake.getPublishedDataTrackMutex.Lock()
defer fake.getPublishedDataTrackMutex.Unlock()
fake.GetPublishedDataTrackStub = stub
}
func (fake *FakeParticipant) GetPublishedDataTrackArgsForCall(i int) uint16 {
fake.getPublishedDataTrackMutex.RLock()
defer fake.getPublishedDataTrackMutex.RUnlock()
argsForCall := fake.getPublishedDataTrackArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeParticipant) GetPublishedDataTrackReturns(result1 types.DataTrack) {
fake.getPublishedDataTrackMutex.Lock()
defer fake.getPublishedDataTrackMutex.Unlock()
fake.GetPublishedDataTrackStub = nil
fake.getPublishedDataTrackReturns = struct {
result1 types.DataTrack
}{result1}
}
func (fake *FakeParticipant) GetPublishedDataTrackReturnsOnCall(i int, result1 types.DataTrack) {
fake.getPublishedDataTrackMutex.Lock()
defer fake.getPublishedDataTrackMutex.Unlock()
fake.GetPublishedDataTrackStub = nil
if fake.getPublishedDataTrackReturnsOnCall == nil {
fake.getPublishedDataTrackReturnsOnCall = make(map[int]struct {
result1 types.DataTrack
})
}
fake.getPublishedDataTrackReturnsOnCall[i] = struct {
result1 types.DataTrack
}{result1}
}
func (fake *FakeParticipant) GetPublishedDataTracks() []types.DataTrack {
fake.getPublishedDataTracksMutex.Lock()
ret, specificReturn := fake.getPublishedDataTracksReturnsOnCall[len(fake.getPublishedDataTracksArgsForCall)]
fake.getPublishedDataTracksArgsForCall = append(fake.getPublishedDataTracksArgsForCall, struct {
}{})
stub := fake.GetPublishedDataTracksStub
fakeReturns := fake.getPublishedDataTracksReturns
fake.recordInvocation("GetPublishedDataTracks", []interface{}{})
fake.getPublishedDataTracksMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) GetPublishedDataTracksCallCount() int {
fake.getPublishedDataTracksMutex.RLock()
defer fake.getPublishedDataTracksMutex.RUnlock()
return len(fake.getPublishedDataTracksArgsForCall)
}
func (fake *FakeParticipant) GetPublishedDataTracksCalls(stub func() []types.DataTrack) {
fake.getPublishedDataTracksMutex.Lock()
defer fake.getPublishedDataTracksMutex.Unlock()
fake.GetPublishedDataTracksStub = stub
}
func (fake *FakeParticipant) GetPublishedDataTracksReturns(result1 []types.DataTrack) {
fake.getPublishedDataTracksMutex.Lock()
defer fake.getPublishedDataTracksMutex.Unlock()
fake.GetPublishedDataTracksStub = nil
fake.getPublishedDataTracksReturns = struct {
result1 []types.DataTrack
}{result1}
}
func (fake *FakeParticipant) GetPublishedDataTracksReturnsOnCall(i int, result1 []types.DataTrack) {
fake.getPublishedDataTracksMutex.Lock()
defer fake.getPublishedDataTracksMutex.Unlock()
fake.GetPublishedDataTracksStub = nil
if fake.getPublishedDataTracksReturnsOnCall == nil {
fake.getPublishedDataTracksReturnsOnCall = make(map[int]struct {
result1 []types.DataTrack
})
}
fake.getPublishedDataTracksReturnsOnCall[i] = struct {
result1 []types.DataTrack
}{result1}
}
func (fake *FakeParticipant) GetPublishedTrack(arg1 livekit.TrackID) types.MediaTrack {
fake.getPublishedTrackMutex.Lock()
ret, specificReturn := fake.getPublishedTrackReturnsOnCall[len(fake.getPublishedTrackArgsForCall)]
@@ -705,6 +1026,45 @@ func (fake *FakeParticipant) GetPublishedTracksReturnsOnCall(i int, result1 []ty
}{result1}
}
func (fake *FakeParticipant) HandleReceivedDataTrackMessage(arg1 []byte, arg2 *datatrack.Packet, arg3 int64) {
var arg1Copy []byte
if arg1 != nil {
arg1Copy = make([]byte, len(arg1))
copy(arg1Copy, arg1)
}
fake.handleReceivedDataTrackMessageMutex.Lock()
fake.handleReceivedDataTrackMessageArgsForCall = append(fake.handleReceivedDataTrackMessageArgsForCall, struct {
arg1 []byte
arg2 *datatrack.Packet
arg3 int64
}{arg1Copy, arg2, arg3})
stub := fake.HandleReceivedDataTrackMessageStub
fake.recordInvocation("HandleReceivedDataTrackMessage", []interface{}{arg1Copy, arg2, arg3})
fake.handleReceivedDataTrackMessageMutex.Unlock()
if stub != nil {
fake.HandleReceivedDataTrackMessageStub(arg1, arg2, arg3)
}
}
func (fake *FakeParticipant) HandleReceivedDataTrackMessageCallCount() int {
fake.handleReceivedDataTrackMessageMutex.RLock()
defer fake.handleReceivedDataTrackMessageMutex.RUnlock()
return len(fake.handleReceivedDataTrackMessageArgsForCall)
}
func (fake *FakeParticipant) HandleReceivedDataTrackMessageCalls(stub func([]byte, *datatrack.Packet, int64)) {
fake.handleReceivedDataTrackMessageMutex.Lock()
defer fake.handleReceivedDataTrackMessageMutex.Unlock()
fake.HandleReceivedDataTrackMessageStub = stub
}
func (fake *FakeParticipant) HandleReceivedDataTrackMessageArgsForCall(i int) ([]byte, *datatrack.Packet, int64) {
fake.handleReceivedDataTrackMessageMutex.RLock()
defer fake.handleReceivedDataTrackMessageMutex.RUnlock()
argsForCall := fake.handleReceivedDataTrackMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeParticipant) HasPermission(arg1 livekit.TrackID, arg2 livekit.ParticipantIdentity) bool {
fake.hasPermissionMutex.Lock()
ret, specificReturn := fake.hasPermissionReturnsOnCall[len(fake.hasPermissionArgsForCall)]
@@ -979,6 +1339,59 @@ func (fake *FakeParticipant) IsAgentReturnsOnCall(i int, result1 bool) {
}{result1}
}
func (fake *FakeParticipant) IsClosed() bool {
fake.isClosedMutex.Lock()
ret, specificReturn := fake.isClosedReturnsOnCall[len(fake.isClosedArgsForCall)]
fake.isClosedArgsForCall = append(fake.isClosedArgsForCall, struct {
}{})
stub := fake.IsClosedStub
fakeReturns := fake.isClosedReturns
fake.recordInvocation("IsClosed", []interface{}{})
fake.isClosedMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) IsClosedCallCount() int {
fake.isClosedMutex.RLock()
defer fake.isClosedMutex.RUnlock()
return len(fake.isClosedArgsForCall)
}
func (fake *FakeParticipant) IsClosedCalls(stub func() bool) {
fake.isClosedMutex.Lock()
defer fake.isClosedMutex.Unlock()
fake.IsClosedStub = stub
}
func (fake *FakeParticipant) IsClosedReturns(result1 bool) {
fake.isClosedMutex.Lock()
defer fake.isClosedMutex.Unlock()
fake.IsClosedStub = nil
fake.isClosedReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeParticipant) IsClosedReturnsOnCall(i int, result1 bool) {
fake.isClosedMutex.Lock()
defer fake.isClosedMutex.Unlock()
fake.IsClosedStub = nil
if fake.isClosedReturnsOnCall == nil {
fake.isClosedReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isClosedReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeParticipant) IsDependent() bool {
fake.isDependentMutex.Lock()
ret, specificReturn := fake.isDependentReturnsOnCall[len(fake.isDependentArgsForCall)]
@@ -1032,6 +1445,59 @@ func (fake *FakeParticipant) IsDependentReturnsOnCall(i int, result1 bool) {
}{result1}
}
func (fake *FakeParticipant) IsDisconnected() bool {
fake.isDisconnectedMutex.Lock()
ret, specificReturn := fake.isDisconnectedReturnsOnCall[len(fake.isDisconnectedArgsForCall)]
fake.isDisconnectedArgsForCall = append(fake.isDisconnectedArgsForCall, struct {
}{})
stub := fake.IsDisconnectedStub
fakeReturns := fake.isDisconnectedReturns
fake.recordInvocation("IsDisconnected", []interface{}{})
fake.isDisconnectedMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) IsDisconnectedCallCount() int {
fake.isDisconnectedMutex.RLock()
defer fake.isDisconnectedMutex.RUnlock()
return len(fake.isDisconnectedArgsForCall)
}
func (fake *FakeParticipant) IsDisconnectedCalls(stub func() bool) {
fake.isDisconnectedMutex.Lock()
defer fake.isDisconnectedMutex.Unlock()
fake.IsDisconnectedStub = stub
}
func (fake *FakeParticipant) IsDisconnectedReturns(result1 bool) {
fake.isDisconnectedMutex.Lock()
defer fake.isDisconnectedMutex.Unlock()
fake.IsDisconnectedStub = nil
fake.isDisconnectedReturns = struct {
result1 bool
}{result1}
}
func (fake *FakeParticipant) IsDisconnectedReturnsOnCall(i int, result1 bool) {
fake.isDisconnectedMutex.Lock()
defer fake.isDisconnectedMutex.Unlock()
fake.IsDisconnectedStub = nil
if fake.isDisconnectedReturnsOnCall == nil {
fake.isDisconnectedReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isDisconnectedReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *FakeParticipant) IsPublisher() bool {
fake.isPublisherMutex.Lock()
ret, specificReturn := fake.isPublisherReturnsOnCall[len(fake.isPublisherArgsForCall)]
@@ -1191,50 +1657,155 @@ func (fake *FakeParticipant) KindReturnsOnCall(i int, result1 livekit.Participan
}{result1}
}
func (fake *FakeParticipant) OnMetrics(arg1 func(types.Participant, *livekit.DataPacket)) {
fake.onMetricsMutex.Lock()
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
arg1 func(types.Participant, *livekit.DataPacket)
}{arg1})
stub := fake.OnMetricsStub
fake.recordInvocation("OnMetrics", []interface{}{arg1})
fake.onMetricsMutex.Unlock()
func (fake *FakeParticipant) KindDetails() []livekit.ParticipantInfo_KindDetail {
fake.kindDetailsMutex.Lock()
ret, specificReturn := fake.kindDetailsReturnsOnCall[len(fake.kindDetailsArgsForCall)]
fake.kindDetailsArgsForCall = append(fake.kindDetailsArgsForCall, struct {
}{})
stub := fake.KindDetailsStub
fakeReturns := fake.kindDetailsReturns
fake.recordInvocation("KindDetails", []interface{}{})
fake.kindDetailsMutex.Unlock()
if stub != nil {
fake.OnMetricsStub(arg1)
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) KindDetailsCallCount() int {
fake.kindDetailsMutex.RLock()
defer fake.kindDetailsMutex.RUnlock()
return len(fake.kindDetailsArgsForCall)
}
func (fake *FakeParticipant) KindDetailsCalls(stub func() []livekit.ParticipantInfo_KindDetail) {
fake.kindDetailsMutex.Lock()
defer fake.kindDetailsMutex.Unlock()
fake.KindDetailsStub = stub
}
func (fake *FakeParticipant) KindDetailsReturns(result1 []livekit.ParticipantInfo_KindDetail) {
fake.kindDetailsMutex.Lock()
defer fake.kindDetailsMutex.Unlock()
fake.KindDetailsStub = nil
fake.kindDetailsReturns = struct {
result1 []livekit.ParticipantInfo_KindDetail
}{result1}
}
func (fake *FakeParticipant) KindDetailsReturnsOnCall(i int, result1 []livekit.ParticipantInfo_KindDetail) {
fake.kindDetailsMutex.Lock()
defer fake.kindDetailsMutex.Unlock()
fake.KindDetailsStub = nil
if fake.kindDetailsReturnsOnCall == nil {
fake.kindDetailsReturnsOnCall = make(map[int]struct {
result1 []livekit.ParticipantInfo_KindDetail
})
}
fake.kindDetailsReturnsOnCall[i] = struct {
result1 []livekit.ParticipantInfo_KindDetail
}{result1}
}
func (fake *FakeParticipant) MigrateState() types.MigrateState {
fake.migrateStateMutex.Lock()
ret, specificReturn := fake.migrateStateReturnsOnCall[len(fake.migrateStateArgsForCall)]
fake.migrateStateArgsForCall = append(fake.migrateStateArgsForCall, struct {
}{})
stub := fake.MigrateStateStub
fakeReturns := fake.migrateStateReturns
fake.recordInvocation("MigrateState", []interface{}{})
fake.migrateStateMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeParticipant) MigrateStateCallCount() int {
fake.migrateStateMutex.RLock()
defer fake.migrateStateMutex.RUnlock()
return len(fake.migrateStateArgsForCall)
}
func (fake *FakeParticipant) MigrateStateCalls(stub func() types.MigrateState) {
fake.migrateStateMutex.Lock()
defer fake.migrateStateMutex.Unlock()
fake.MigrateStateStub = stub
}
func (fake *FakeParticipant) MigrateStateReturns(result1 types.MigrateState) {
fake.migrateStateMutex.Lock()
defer fake.migrateStateMutex.Unlock()
fake.MigrateStateStub = nil
fake.migrateStateReturns = struct {
result1 types.MigrateState
}{result1}
}
func (fake *FakeParticipant) MigrateStateReturnsOnCall(i int, result1 types.MigrateState) {
fake.migrateStateMutex.Lock()
defer fake.migrateStateMutex.Unlock()
fake.MigrateStateStub = nil
if fake.migrateStateReturnsOnCall == nil {
fake.migrateStateReturnsOnCall = make(map[int]struct {
result1 types.MigrateState
})
}
fake.migrateStateReturnsOnCall[i] = struct {
result1 types.MigrateState
}{result1}
}
func (fake *FakeParticipant) RemovePublishedDataTrack(arg1 types.DataTrack) {
fake.removePublishedDataTrackMutex.Lock()
fake.removePublishedDataTrackArgsForCall = append(fake.removePublishedDataTrackArgsForCall, struct {
arg1 types.DataTrack
}{arg1})
stub := fake.RemovePublishedDataTrackStub
fake.recordInvocation("RemovePublishedDataTrack", []interface{}{arg1})
fake.removePublishedDataTrackMutex.Unlock()
if stub != nil {
fake.RemovePublishedDataTrackStub(arg1)
}
}
func (fake *FakeParticipant) OnMetricsCallCount() int {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
return len(fake.onMetricsArgsForCall)
func (fake *FakeParticipant) RemovePublishedDataTrackCallCount() int {
fake.removePublishedDataTrackMutex.RLock()
defer fake.removePublishedDataTrackMutex.RUnlock()
return len(fake.removePublishedDataTrackArgsForCall)
}
func (fake *FakeParticipant) OnMetricsCalls(stub func(func(types.Participant, *livekit.DataPacket))) {
fake.onMetricsMutex.Lock()
defer fake.onMetricsMutex.Unlock()
fake.OnMetricsStub = stub
func (fake *FakeParticipant) RemovePublishedDataTrackCalls(stub func(types.DataTrack)) {
fake.removePublishedDataTrackMutex.Lock()
defer fake.removePublishedDataTrackMutex.Unlock()
fake.RemovePublishedDataTrackStub = stub
}
func (fake *FakeParticipant) OnMetricsArgsForCall(i int) func(types.Participant, *livekit.DataPacket) {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
argsForCall := fake.onMetricsArgsForCall[i]
func (fake *FakeParticipant) RemovePublishedDataTrackArgsForCall(i int) types.DataTrack {
fake.removePublishedDataTrackMutex.RLock()
defer fake.removePublishedDataTrackMutex.RUnlock()
argsForCall := fake.removePublishedDataTrackArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeParticipant) RemovePublishedTrack(arg1 types.MediaTrack, arg2 bool, arg3 bool) {
func (fake *FakeParticipant) RemovePublishedTrack(arg1 types.MediaTrack, arg2 bool) {
fake.removePublishedTrackMutex.Lock()
fake.removePublishedTrackArgsForCall = append(fake.removePublishedTrackArgsForCall, struct {
arg1 types.MediaTrack
arg2 bool
arg3 bool
}{arg1, arg2, arg3})
}{arg1, arg2})
stub := fake.RemovePublishedTrackStub
fake.recordInvocation("RemovePublishedTrack", []interface{}{arg1, arg2, arg3})
fake.recordInvocation("RemovePublishedTrack", []interface{}{arg1, arg2})
fake.removePublishedTrackMutex.Unlock()
if stub != nil {
fake.RemovePublishedTrackStub(arg1, arg2, arg3)
fake.RemovePublishedTrackStub(arg1, arg2)
}
}
@@ -1244,17 +1815,17 @@ func (fake *FakeParticipant) RemovePublishedTrackCallCount() int {
return len(fake.removePublishedTrackArgsForCall)
}
func (fake *FakeParticipant) RemovePublishedTrackCalls(stub func(types.MediaTrack, bool, bool)) {
func (fake *FakeParticipant) RemovePublishedTrackCalls(stub func(types.MediaTrack, bool)) {
fake.removePublishedTrackMutex.Lock()
defer fake.removePublishedTrackMutex.Unlock()
fake.RemovePublishedTrackStub = stub
}
func (fake *FakeParticipant) RemovePublishedTrackArgsForCall(i int) (types.MediaTrack, bool, bool) {
func (fake *FakeParticipant) RemovePublishedTrackArgsForCall(i int) (types.MediaTrack, bool) {
fake.removePublishedTrackMutex.RLock()
defer fake.removePublishedTrackMutex.RUnlock()
argsForCall := fake.removePublishedTrackArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipant) State() livekit.ParticipantInfo_State {
@@ -1419,6 +1990,62 @@ func (fake *FakeParticipant) ToProtoReturnsOnCall(i int, result1 *livekit.Partic
}{result1}
}
func (fake *FakeParticipant) ToProtoWithVersion() (*livekit.ParticipantInfo, utils.TimedVersion) {
fake.toProtoWithVersionMutex.Lock()
ret, specificReturn := fake.toProtoWithVersionReturnsOnCall[len(fake.toProtoWithVersionArgsForCall)]
fake.toProtoWithVersionArgsForCall = append(fake.toProtoWithVersionArgsForCall, struct {
}{})
stub := fake.ToProtoWithVersionStub
fakeReturns := fake.toProtoWithVersionReturns
fake.recordInvocation("ToProtoWithVersion", []interface{}{})
fake.toProtoWithVersionMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakeParticipant) ToProtoWithVersionCallCount() int {
fake.toProtoWithVersionMutex.RLock()
defer fake.toProtoWithVersionMutex.RUnlock()
return len(fake.toProtoWithVersionArgsForCall)
}
func (fake *FakeParticipant) ToProtoWithVersionCalls(stub func() (*livekit.ParticipantInfo, utils.TimedVersion)) {
fake.toProtoWithVersionMutex.Lock()
defer fake.toProtoWithVersionMutex.Unlock()
fake.ToProtoWithVersionStub = stub
}
func (fake *FakeParticipant) ToProtoWithVersionReturns(result1 *livekit.ParticipantInfo, result2 utils.TimedVersion) {
fake.toProtoWithVersionMutex.Lock()
defer fake.toProtoWithVersionMutex.Unlock()
fake.ToProtoWithVersionStub = nil
fake.toProtoWithVersionReturns = struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
}{result1, result2}
}
func (fake *FakeParticipant) ToProtoWithVersionReturnsOnCall(i int, result1 *livekit.ParticipantInfo, result2 utils.TimedVersion) {
fake.toProtoWithVersionMutex.Lock()
defer fake.toProtoWithVersionMutex.Unlock()
fake.ToProtoWithVersionStub = nil
if fake.toProtoWithVersionReturnsOnCall == nil {
fake.toProtoWithVersionReturnsOnCall = make(map[int]struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
})
}
fake.toProtoWithVersionReturnsOnCall[i] = struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
}{result1, result2}
}
func (fake *FakeParticipant) UpdateSubscriptionPermission(arg1 *livekit.SubscriptionPermission, arg2 utils.TimedVersion, arg3 func(participantID livekit.ParticipantID) types.LocalParticipant) error {
fake.updateSubscriptionPermissionMutex.Lock()
ret, specificReturn := fake.updateSubscriptionPermissionReturnsOnCall[len(fake.updateSubscriptionPermissionArgsForCall)]
@@ -1538,54 +2165,6 @@ func (fake *FakeParticipant) VersionReturnsOnCall(i int, result1 utils.TimedVers
func (fake *FakeParticipant) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.canSkipBroadcastMutex.RLock()
defer fake.canSkipBroadcastMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.closeReasonMutex.RLock()
defer fake.closeReasonMutex.RUnlock()
fake.connectedAtMutex.RLock()
defer fake.connectedAtMutex.RUnlock()
fake.debugInfoMutex.RLock()
defer fake.debugInfoMutex.RUnlock()
fake.getAudioLevelMutex.RLock()
defer fake.getAudioLevelMutex.RUnlock()
fake.getPublishedTrackMutex.RLock()
defer fake.getPublishedTrackMutex.RUnlock()
fake.getPublishedTracksMutex.RLock()
defer fake.getPublishedTracksMutex.RUnlock()
fake.hasPermissionMutex.RLock()
defer fake.hasPermissionMutex.RUnlock()
fake.hiddenMutex.RLock()
defer fake.hiddenMutex.RUnlock()
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
fake.identityMutex.RLock()
defer fake.identityMutex.RUnlock()
fake.isAgentMutex.RLock()
defer fake.isAgentMutex.RUnlock()
fake.isDependentMutex.RLock()
defer fake.isDependentMutex.RUnlock()
fake.isPublisherMutex.RLock()
defer fake.isPublisherMutex.RUnlock()
fake.isRecorderMutex.RLock()
defer fake.isRecorderMutex.RUnlock()
fake.kindMutex.RLock()
defer fake.kindMutex.RUnlock()
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
fake.removePublishedTrackMutex.RLock()
defer fake.removePublishedTrackMutex.RUnlock()
fake.stateMutex.RLock()
defer fake.stateMutex.RUnlock()
fake.subscriptionPermissionMutex.RLock()
defer fake.subscriptionPermissionMutex.RUnlock()
fake.toProtoMutex.RLock()
defer fake.toProtoMutex.RUnlock()
fake.updateSubscriptionPermissionMutex.RLock()
defer fake.updateSubscriptionPermissionMutex.RUnlock()
fake.versionMutex.RLock()
defer fake.versionMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
@@ -0,0 +1,356 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
)
type FakeParticipantListener struct {
OnDataTrackMessageStub func(types.Participant, []byte, *datatrack.Packet)
onDataTrackMessageMutex sync.RWMutex
onDataTrackMessageArgsForCall []struct {
arg1 types.Participant
arg2 []byte
arg3 *datatrack.Packet
}
OnDataTrackPublishedStub func(types.Participant, types.DataTrack)
onDataTrackPublishedMutex sync.RWMutex
onDataTrackPublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.DataTrack
}
OnDataTrackUnpublishedStub func(types.Participant, types.DataTrack)
onDataTrackUnpublishedMutex sync.RWMutex
onDataTrackUnpublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.DataTrack
}
OnMetricsStub func(types.Participant, *livekit.DataPacket)
onMetricsMutex sync.RWMutex
onMetricsArgsForCall []struct {
arg1 types.Participant
arg2 *livekit.DataPacket
}
OnParticipantUpdateStub func(types.Participant)
onParticipantUpdateMutex sync.RWMutex
onParticipantUpdateArgsForCall []struct {
arg1 types.Participant
}
OnTrackPublishedStub func(types.Participant, types.MediaTrack)
onTrackPublishedMutex sync.RWMutex
onTrackPublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
OnTrackUnpublishedStub func(types.Participant, types.MediaTrack)
onTrackUnpublishedMutex sync.RWMutex
onTrackUnpublishedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
OnTrackUpdatedStub func(types.Participant, types.MediaTrack)
onTrackUpdatedMutex sync.RWMutex
onTrackUpdatedArgsForCall []struct {
arg1 types.Participant
arg2 types.MediaTrack
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeParticipantListener) OnDataTrackMessage(arg1 types.Participant, arg2 []byte, arg3 *datatrack.Packet) {
var arg2Copy []byte
if arg2 != nil {
arg2Copy = make([]byte, len(arg2))
copy(arg2Copy, arg2)
}
fake.onDataTrackMessageMutex.Lock()
fake.onDataTrackMessageArgsForCall = append(fake.onDataTrackMessageArgsForCall, struct {
arg1 types.Participant
arg2 []byte
arg3 *datatrack.Packet
}{arg1, arg2Copy, arg3})
stub := fake.OnDataTrackMessageStub
fake.recordInvocation("OnDataTrackMessage", []interface{}{arg1, arg2Copy, arg3})
fake.onDataTrackMessageMutex.Unlock()
if stub != nil {
fake.OnDataTrackMessageStub(arg1, arg2, arg3)
}
}
func (fake *FakeParticipantListener) OnDataTrackMessageCallCount() int {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
return len(fake.onDataTrackMessageArgsForCall)
}
func (fake *FakeParticipantListener) OnDataTrackMessageCalls(stub func(types.Participant, []byte, *datatrack.Packet)) {
fake.onDataTrackMessageMutex.Lock()
defer fake.onDataTrackMessageMutex.Unlock()
fake.OnDataTrackMessageStub = stub
}
func (fake *FakeParticipantListener) OnDataTrackMessageArgsForCall(i int) (types.Participant, []byte, *datatrack.Packet) {
fake.onDataTrackMessageMutex.RLock()
defer fake.onDataTrackMessageMutex.RUnlock()
argsForCall := fake.onDataTrackMessageArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeParticipantListener) OnDataTrackPublished(arg1 types.Participant, arg2 types.DataTrack) {
fake.onDataTrackPublishedMutex.Lock()
fake.onDataTrackPublishedArgsForCall = append(fake.onDataTrackPublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.DataTrack
}{arg1, arg2})
stub := fake.OnDataTrackPublishedStub
fake.recordInvocation("OnDataTrackPublished", []interface{}{arg1, arg2})
fake.onDataTrackPublishedMutex.Unlock()
if stub != nil {
fake.OnDataTrackPublishedStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnDataTrackPublishedCallCount() int {
fake.onDataTrackPublishedMutex.RLock()
defer fake.onDataTrackPublishedMutex.RUnlock()
return len(fake.onDataTrackPublishedArgsForCall)
}
func (fake *FakeParticipantListener) OnDataTrackPublishedCalls(stub func(types.Participant, types.DataTrack)) {
fake.onDataTrackPublishedMutex.Lock()
defer fake.onDataTrackPublishedMutex.Unlock()
fake.OnDataTrackPublishedStub = stub
}
func (fake *FakeParticipantListener) OnDataTrackPublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
fake.onDataTrackPublishedMutex.RLock()
defer fake.onDataTrackPublishedMutex.RUnlock()
argsForCall := fake.onDataTrackPublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) OnDataTrackUnpublished(arg1 types.Participant, arg2 types.DataTrack) {
fake.onDataTrackUnpublishedMutex.Lock()
fake.onDataTrackUnpublishedArgsForCall = append(fake.onDataTrackUnpublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.DataTrack
}{arg1, arg2})
stub := fake.OnDataTrackUnpublishedStub
fake.recordInvocation("OnDataTrackUnpublished", []interface{}{arg1, arg2})
fake.onDataTrackUnpublishedMutex.Unlock()
if stub != nil {
fake.OnDataTrackUnpublishedStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnDataTrackUnpublishedCallCount() int {
fake.onDataTrackUnpublishedMutex.RLock()
defer fake.onDataTrackUnpublishedMutex.RUnlock()
return len(fake.onDataTrackUnpublishedArgsForCall)
}
func (fake *FakeParticipantListener) OnDataTrackUnpublishedCalls(stub func(types.Participant, types.DataTrack)) {
fake.onDataTrackUnpublishedMutex.Lock()
defer fake.onDataTrackUnpublishedMutex.Unlock()
fake.OnDataTrackUnpublishedStub = stub
}
func (fake *FakeParticipantListener) OnDataTrackUnpublishedArgsForCall(i int) (types.Participant, types.DataTrack) {
fake.onDataTrackUnpublishedMutex.RLock()
defer fake.onDataTrackUnpublishedMutex.RUnlock()
argsForCall := fake.onDataTrackUnpublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) OnMetrics(arg1 types.Participant, arg2 *livekit.DataPacket) {
fake.onMetricsMutex.Lock()
fake.onMetricsArgsForCall = append(fake.onMetricsArgsForCall, struct {
arg1 types.Participant
arg2 *livekit.DataPacket
}{arg1, arg2})
stub := fake.OnMetricsStub
fake.recordInvocation("OnMetrics", []interface{}{arg1, arg2})
fake.onMetricsMutex.Unlock()
if stub != nil {
fake.OnMetricsStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnMetricsCallCount() int {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
return len(fake.onMetricsArgsForCall)
}
func (fake *FakeParticipantListener) OnMetricsCalls(stub func(types.Participant, *livekit.DataPacket)) {
fake.onMetricsMutex.Lock()
defer fake.onMetricsMutex.Unlock()
fake.OnMetricsStub = stub
}
func (fake *FakeParticipantListener) OnMetricsArgsForCall(i int) (types.Participant, *livekit.DataPacket) {
fake.onMetricsMutex.RLock()
defer fake.onMetricsMutex.RUnlock()
argsForCall := fake.onMetricsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) OnParticipantUpdate(arg1 types.Participant) {
fake.onParticipantUpdateMutex.Lock()
fake.onParticipantUpdateArgsForCall = append(fake.onParticipantUpdateArgsForCall, struct {
arg1 types.Participant
}{arg1})
stub := fake.OnParticipantUpdateStub
fake.recordInvocation("OnParticipantUpdate", []interface{}{arg1})
fake.onParticipantUpdateMutex.Unlock()
if stub != nil {
fake.OnParticipantUpdateStub(arg1)
}
}
func (fake *FakeParticipantListener) OnParticipantUpdateCallCount() int {
fake.onParticipantUpdateMutex.RLock()
defer fake.onParticipantUpdateMutex.RUnlock()
return len(fake.onParticipantUpdateArgsForCall)
}
func (fake *FakeParticipantListener) OnParticipantUpdateCalls(stub func(types.Participant)) {
fake.onParticipantUpdateMutex.Lock()
defer fake.onParticipantUpdateMutex.Unlock()
fake.OnParticipantUpdateStub = stub
}
func (fake *FakeParticipantListener) OnParticipantUpdateArgsForCall(i int) types.Participant {
fake.onParticipantUpdateMutex.RLock()
defer fake.onParticipantUpdateMutex.RUnlock()
argsForCall := fake.onParticipantUpdateArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeParticipantListener) OnTrackPublished(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackPublishedMutex.Lock()
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackPublishedStub
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2})
fake.onTrackPublishedMutex.Unlock()
if stub != nil {
fake.OnTrackPublishedStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnTrackPublishedCallCount() int {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
return len(fake.onTrackPublishedArgsForCall)
}
func (fake *FakeParticipantListener) OnTrackPublishedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackPublishedMutex.Lock()
defer fake.onTrackPublishedMutex.Unlock()
fake.OnTrackPublishedStub = stub
}
func (fake *FakeParticipantListener) OnTrackPublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
argsForCall := fake.onTrackPublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) OnTrackUnpublished(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackUnpublishedMutex.Lock()
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackUnpublishedStub
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2})
fake.onTrackUnpublishedMutex.Unlock()
if stub != nil {
fake.OnTrackUnpublishedStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnTrackUnpublishedCallCount() int {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
return len(fake.onTrackUnpublishedArgsForCall)
}
func (fake *FakeParticipantListener) OnTrackUnpublishedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackUnpublishedMutex.Lock()
defer fake.onTrackUnpublishedMutex.Unlock()
fake.OnTrackUnpublishedStub = stub
}
func (fake *FakeParticipantListener) OnTrackUnpublishedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) OnTrackUpdated(arg1 types.Participant, arg2 types.MediaTrack) {
fake.onTrackUpdatedMutex.Lock()
fake.onTrackUpdatedArgsForCall = append(fake.onTrackUpdatedArgsForCall, struct {
arg1 types.Participant
arg2 types.MediaTrack
}{arg1, arg2})
stub := fake.OnTrackUpdatedStub
fake.recordInvocation("OnTrackUpdated", []interface{}{arg1, arg2})
fake.onTrackUpdatedMutex.Unlock()
if stub != nil {
fake.OnTrackUpdatedStub(arg1, arg2)
}
}
func (fake *FakeParticipantListener) OnTrackUpdatedCallCount() int {
fake.onTrackUpdatedMutex.RLock()
defer fake.onTrackUpdatedMutex.RUnlock()
return len(fake.onTrackUpdatedArgsForCall)
}
func (fake *FakeParticipantListener) OnTrackUpdatedCalls(stub func(types.Participant, types.MediaTrack)) {
fake.onTrackUpdatedMutex.Lock()
defer fake.onTrackUpdatedMutex.Unlock()
fake.OnTrackUpdatedStub = stub
}
func (fake *FakeParticipantListener) OnTrackUpdatedArgsForCall(i int) (types.Participant, types.MediaTrack) {
fake.onTrackUpdatedMutex.RLock()
defer fake.onTrackUpdatedMutex.RUnlock()
argsForCall := fake.onTrackUpdatedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantListener) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeParticipantListener) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.ParticipantListener = new(FakeParticipantListener)
@@ -0,0 +1,659 @@
// Code generated by counterfeiter. DO NOT EDIT.
package typesfakes
import (
"sync"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/livekit-server/pkg/telemetry"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
)
type FakeParticipantTelemetryListener struct {
OnTrackMaxSubscribedVideoQualityStub func(livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality)
onTrackMaxSubscribedVideoQualityMutex sync.RWMutex
onTrackMaxSubscribedVideoQualityArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 mime.MimeType
arg4 livekit.VideoQuality
}
OnTrackMutedStub func(livekit.ParticipantID, *livekit.TrackInfo)
onTrackMutedMutex sync.RWMutex
onTrackMutedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}
OnTrackPublishRTPStatsStub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats)
onTrackPublishRTPStatsMutex sync.RWMutex
onTrackPublishRTPStatsArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 mime.MimeType
arg4 int
arg5 *livekit.RTPStats
}
OnTrackPublishRequestedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo)
onTrackPublishRequestedMutex sync.RWMutex
onTrackPublishRequestedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
}
OnTrackPublishedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)
onTrackPublishedMutex sync.RWMutex
onTrackPublishedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
arg4 bool
}
OnTrackPublishedUpdateStub func(livekit.ParticipantID, *livekit.TrackInfo)
onTrackPublishedUpdateMutex sync.RWMutex
onTrackPublishedUpdateArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}
OnTrackStatsStub func(telemetry.StatsKey, *livekit.AnalyticsStat)
onTrackStatsMutex sync.RWMutex
onTrackStatsArgsForCall []struct {
arg1 telemetry.StatsKey
arg2 *livekit.AnalyticsStat
}
OnTrackSubscribeFailedStub func(livekit.ParticipantID, livekit.TrackID, error, bool)
onTrackSubscribeFailedMutex sync.RWMutex
onTrackSubscribeFailedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 error
arg4 bool
}
OnTrackSubscribeRTPStatsStub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats)
onTrackSubscribeRTPStatsMutex sync.RWMutex
onTrackSubscribeRTPStatsArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 mime.MimeType
arg4 *livekit.RTPStats
}
OnTrackSubscribeRequestedStub func(livekit.ParticipantID, *livekit.TrackInfo)
onTrackSubscribeRequestedMutex sync.RWMutex
onTrackSubscribeRequestedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}
OnTrackSubscribeStreamStartedStub func(livekit.ParticipantID, *livekit.TrackInfo)
onTrackSubscribeStreamStartedMutex sync.RWMutex
onTrackSubscribeStreamStartedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}
OnTrackSubscribedStub func(livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool)
onTrackSubscribedMutex sync.RWMutex
onTrackSubscribedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 *livekit.ParticipantInfo
arg4 bool
}
OnTrackUnmutedStub func(livekit.ParticipantID, *livekit.TrackInfo)
onTrackUnmutedMutex sync.RWMutex
onTrackUnmutedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}
OnTrackUnpublishedStub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)
onTrackUnpublishedMutex sync.RWMutex
onTrackUnpublishedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
arg4 bool
}
OnTrackUnsubscribedStub func(livekit.ParticipantID, *livekit.TrackInfo, bool)
onTrackUnsubscribedMutex sync.RWMutex
onTrackUnsubscribedArgsForCall []struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 bool
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQuality(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 mime.MimeType, arg4 livekit.VideoQuality) {
fake.onTrackMaxSubscribedVideoQualityMutex.Lock()
fake.onTrackMaxSubscribedVideoQualityArgsForCall = append(fake.onTrackMaxSubscribedVideoQualityArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 mime.MimeType
arg4 livekit.VideoQuality
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackMaxSubscribedVideoQualityStub
fake.recordInvocation("OnTrackMaxSubscribedVideoQuality", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackMaxSubscribedVideoQualityMutex.Unlock()
if stub != nil {
fake.OnTrackMaxSubscribedVideoQualityStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityCallCount() int {
fake.onTrackMaxSubscribedVideoQualityMutex.RLock()
defer fake.onTrackMaxSubscribedVideoQualityMutex.RUnlock()
return len(fake.onTrackMaxSubscribedVideoQualityArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality)) {
fake.onTrackMaxSubscribedVideoQualityMutex.Lock()
defer fake.onTrackMaxSubscribedVideoQualityMutex.Unlock()
fake.OnTrackMaxSubscribedVideoQualityStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackMaxSubscribedVideoQualityArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, mime.MimeType, livekit.VideoQuality) {
fake.onTrackMaxSubscribedVideoQualityMutex.RLock()
defer fake.onTrackMaxSubscribedVideoQualityMutex.RUnlock()
argsForCall := fake.onTrackMaxSubscribedVideoQualityArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackMuted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
fake.onTrackMutedMutex.Lock()
fake.onTrackMutedArgsForCall = append(fake.onTrackMutedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}{arg1, arg2})
stub := fake.OnTrackMutedStub
fake.recordInvocation("OnTrackMuted", []interface{}{arg1, arg2})
fake.onTrackMutedMutex.Unlock()
if stub != nil {
fake.OnTrackMutedStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackMutedCallCount() int {
fake.onTrackMutedMutex.RLock()
defer fake.onTrackMutedMutex.RUnlock()
return len(fake.onTrackMutedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackMutedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
fake.onTrackMutedMutex.Lock()
defer fake.onTrackMutedMutex.Unlock()
fake.OnTrackMutedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackMutedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
fake.onTrackMutedMutex.RLock()
defer fake.onTrackMutedMutex.RUnlock()
argsForCall := fake.onTrackMutedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStats(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 mime.MimeType, arg4 int, arg5 *livekit.RTPStats) {
fake.onTrackPublishRTPStatsMutex.Lock()
fake.onTrackPublishRTPStatsArgsForCall = append(fake.onTrackPublishRTPStatsArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 mime.MimeType
arg4 int
arg5 *livekit.RTPStats
}{arg1, arg2, arg3, arg4, arg5})
stub := fake.OnTrackPublishRTPStatsStub
fake.recordInvocation("OnTrackPublishRTPStats", []interface{}{arg1, arg2, arg3, arg4, arg5})
fake.onTrackPublishRTPStatsMutex.Unlock()
if stub != nil {
fake.OnTrackPublishRTPStatsStub(arg1, arg2, arg3, arg4, arg5)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsCallCount() int {
fake.onTrackPublishRTPStatsMutex.RLock()
defer fake.onTrackPublishRTPStatsMutex.RUnlock()
return len(fake.onTrackPublishRTPStatsArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsCalls(stub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats)) {
fake.onTrackPublishRTPStatsMutex.Lock()
defer fake.onTrackPublishRTPStatsMutex.Unlock()
fake.OnTrackPublishRTPStatsStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRTPStatsArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, mime.MimeType, int, *livekit.RTPStats) {
fake.onTrackPublishRTPStatsMutex.RLock()
defer fake.onTrackPublishRTPStatsMutex.RUnlock()
argsForCall := fake.onTrackPublishRTPStatsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequested(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo) {
fake.onTrackPublishRequestedMutex.Lock()
fake.onTrackPublishRequestedArgsForCall = append(fake.onTrackPublishRequestedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
}{arg1, arg2, arg3})
stub := fake.OnTrackPublishRequestedStub
fake.recordInvocation("OnTrackPublishRequested", []interface{}{arg1, arg2, arg3})
fake.onTrackPublishRequestedMutex.Unlock()
if stub != nil {
fake.OnTrackPublishRequestedStub(arg1, arg2, arg3)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedCallCount() int {
fake.onTrackPublishRequestedMutex.RLock()
defer fake.onTrackPublishRequestedMutex.RUnlock()
return len(fake.onTrackPublishRequestedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo)) {
fake.onTrackPublishRequestedMutex.Lock()
defer fake.onTrackPublishRequestedMutex.Unlock()
fake.OnTrackPublishRequestedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishRequestedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo) {
fake.onTrackPublishRequestedMutex.RLock()
defer fake.onTrackPublishRequestedMutex.RUnlock()
argsForCall := fake.onTrackPublishRequestedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublished(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo, arg4 bool) {
fake.onTrackPublishedMutex.Lock()
fake.onTrackPublishedArgsForCall = append(fake.onTrackPublishedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
arg4 bool
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackPublishedStub
fake.recordInvocation("OnTrackPublished", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackPublishedMutex.Unlock()
if stub != nil {
fake.OnTrackPublishedStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedCallCount() int {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
return len(fake.onTrackPublishedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)) {
fake.onTrackPublishedMutex.Lock()
defer fake.onTrackPublishedMutex.Unlock()
fake.OnTrackPublishedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool) {
fake.onTrackPublishedMutex.RLock()
defer fake.onTrackPublishedMutex.RUnlock()
argsForCall := fake.onTrackPublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdate(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
fake.onTrackPublishedUpdateMutex.Lock()
fake.onTrackPublishedUpdateArgsForCall = append(fake.onTrackPublishedUpdateArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}{arg1, arg2})
stub := fake.OnTrackPublishedUpdateStub
fake.recordInvocation("OnTrackPublishedUpdate", []interface{}{arg1, arg2})
fake.onTrackPublishedUpdateMutex.Unlock()
if stub != nil {
fake.OnTrackPublishedUpdateStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateCallCount() int {
fake.onTrackPublishedUpdateMutex.RLock()
defer fake.onTrackPublishedUpdateMutex.RUnlock()
return len(fake.onTrackPublishedUpdateArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
fake.onTrackPublishedUpdateMutex.Lock()
defer fake.onTrackPublishedUpdateMutex.Unlock()
fake.OnTrackPublishedUpdateStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackPublishedUpdateArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
fake.onTrackPublishedUpdateMutex.RLock()
defer fake.onTrackPublishedUpdateMutex.RUnlock()
argsForCall := fake.onTrackPublishedUpdateArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackStats(arg1 telemetry.StatsKey, arg2 *livekit.AnalyticsStat) {
fake.onTrackStatsMutex.Lock()
fake.onTrackStatsArgsForCall = append(fake.onTrackStatsArgsForCall, struct {
arg1 telemetry.StatsKey
arg2 *livekit.AnalyticsStat
}{arg1, arg2})
stub := fake.OnTrackStatsStub
fake.recordInvocation("OnTrackStats", []interface{}{arg1, arg2})
fake.onTrackStatsMutex.Unlock()
if stub != nil {
fake.OnTrackStatsStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackStatsCallCount() int {
fake.onTrackStatsMutex.RLock()
defer fake.onTrackStatsMutex.RUnlock()
return len(fake.onTrackStatsArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackStatsCalls(stub func(telemetry.StatsKey, *livekit.AnalyticsStat)) {
fake.onTrackStatsMutex.Lock()
defer fake.onTrackStatsMutex.Unlock()
fake.OnTrackStatsStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackStatsArgsForCall(i int) (telemetry.StatsKey, *livekit.AnalyticsStat) {
fake.onTrackStatsMutex.RLock()
defer fake.onTrackStatsMutex.RUnlock()
argsForCall := fake.onTrackStatsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailed(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 error, arg4 bool) {
fake.onTrackSubscribeFailedMutex.Lock()
fake.onTrackSubscribeFailedArgsForCall = append(fake.onTrackSubscribeFailedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 error
arg4 bool
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackSubscribeFailedStub
fake.recordInvocation("OnTrackSubscribeFailed", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackSubscribeFailedMutex.Unlock()
if stub != nil {
fake.OnTrackSubscribeFailedStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedCallCount() int {
fake.onTrackSubscribeFailedMutex.RLock()
defer fake.onTrackSubscribeFailedMutex.RUnlock()
return len(fake.onTrackSubscribeFailedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedCalls(stub func(livekit.ParticipantID, livekit.TrackID, error, bool)) {
fake.onTrackSubscribeFailedMutex.Lock()
defer fake.onTrackSubscribeFailedMutex.Unlock()
fake.OnTrackSubscribeFailedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeFailedArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, error, bool) {
fake.onTrackSubscribeFailedMutex.RLock()
defer fake.onTrackSubscribeFailedMutex.RUnlock()
argsForCall := fake.onTrackSubscribeFailedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStats(arg1 livekit.ParticipantID, arg2 livekit.TrackID, arg3 mime.MimeType, arg4 *livekit.RTPStats) {
fake.onTrackSubscribeRTPStatsMutex.Lock()
fake.onTrackSubscribeRTPStatsArgsForCall = append(fake.onTrackSubscribeRTPStatsArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.TrackID
arg3 mime.MimeType
arg4 *livekit.RTPStats
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackSubscribeRTPStatsStub
fake.recordInvocation("OnTrackSubscribeRTPStats", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackSubscribeRTPStatsMutex.Unlock()
if stub != nil {
fake.OnTrackSubscribeRTPStatsStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsCallCount() int {
fake.onTrackSubscribeRTPStatsMutex.RLock()
defer fake.onTrackSubscribeRTPStatsMutex.RUnlock()
return len(fake.onTrackSubscribeRTPStatsArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsCalls(stub func(livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats)) {
fake.onTrackSubscribeRTPStatsMutex.Lock()
defer fake.onTrackSubscribeRTPStatsMutex.Unlock()
fake.OnTrackSubscribeRTPStatsStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRTPStatsArgsForCall(i int) (livekit.ParticipantID, livekit.TrackID, mime.MimeType, *livekit.RTPStats) {
fake.onTrackSubscribeRTPStatsMutex.RLock()
defer fake.onTrackSubscribeRTPStatsMutex.RUnlock()
argsForCall := fake.onTrackSubscribeRTPStatsArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequested(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
fake.onTrackSubscribeRequestedMutex.Lock()
fake.onTrackSubscribeRequestedArgsForCall = append(fake.onTrackSubscribeRequestedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}{arg1, arg2})
stub := fake.OnTrackSubscribeRequestedStub
fake.recordInvocation("OnTrackSubscribeRequested", []interface{}{arg1, arg2})
fake.onTrackSubscribeRequestedMutex.Unlock()
if stub != nil {
fake.OnTrackSubscribeRequestedStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedCallCount() int {
fake.onTrackSubscribeRequestedMutex.RLock()
defer fake.onTrackSubscribeRequestedMutex.RUnlock()
return len(fake.onTrackSubscribeRequestedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
fake.onTrackSubscribeRequestedMutex.Lock()
defer fake.onTrackSubscribeRequestedMutex.Unlock()
fake.OnTrackSubscribeRequestedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeRequestedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
fake.onTrackSubscribeRequestedMutex.RLock()
defer fake.onTrackSubscribeRequestedMutex.RUnlock()
argsForCall := fake.onTrackSubscribeRequestedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStarted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
fake.onTrackSubscribeStreamStartedMutex.Lock()
fake.onTrackSubscribeStreamStartedArgsForCall = append(fake.onTrackSubscribeStreamStartedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}{arg1, arg2})
stub := fake.OnTrackSubscribeStreamStartedStub
fake.recordInvocation("OnTrackSubscribeStreamStarted", []interface{}{arg1, arg2})
fake.onTrackSubscribeStreamStartedMutex.Unlock()
if stub != nil {
fake.OnTrackSubscribeStreamStartedStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedCallCount() int {
fake.onTrackSubscribeStreamStartedMutex.RLock()
defer fake.onTrackSubscribeStreamStartedMutex.RUnlock()
return len(fake.onTrackSubscribeStreamStartedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
fake.onTrackSubscribeStreamStartedMutex.Lock()
defer fake.onTrackSubscribeStreamStartedMutex.Unlock()
fake.OnTrackSubscribeStreamStartedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribeStreamStartedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
fake.onTrackSubscribeStreamStartedMutex.RLock()
defer fake.onTrackSubscribeStreamStartedMutex.RUnlock()
argsForCall := fake.onTrackSubscribeStreamStartedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribed(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 *livekit.ParticipantInfo, arg4 bool) {
fake.onTrackSubscribedMutex.Lock()
fake.onTrackSubscribedArgsForCall = append(fake.onTrackSubscribedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 *livekit.ParticipantInfo
arg4 bool
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackSubscribedStub
fake.recordInvocation("OnTrackSubscribed", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackSubscribedMutex.Unlock()
if stub != nil {
fake.OnTrackSubscribedStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedCallCount() int {
fake.onTrackSubscribedMutex.RLock()
defer fake.onTrackSubscribedMutex.RUnlock()
return len(fake.onTrackSubscribedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool)) {
fake.onTrackSubscribedMutex.Lock()
defer fake.onTrackSubscribedMutex.Unlock()
fake.OnTrackSubscribedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackSubscribedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, *livekit.ParticipantInfo, bool) {
fake.onTrackSubscribedMutex.RLock()
defer fake.onTrackSubscribedMutex.RUnlock()
argsForCall := fake.onTrackSubscribedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnmuted(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo) {
fake.onTrackUnmutedMutex.Lock()
fake.onTrackUnmutedArgsForCall = append(fake.onTrackUnmutedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
}{arg1, arg2})
stub := fake.OnTrackUnmutedStub
fake.recordInvocation("OnTrackUnmuted", []interface{}{arg1, arg2})
fake.onTrackUnmutedMutex.Unlock()
if stub != nil {
fake.OnTrackUnmutedStub(arg1, arg2)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedCallCount() int {
fake.onTrackUnmutedMutex.RLock()
defer fake.onTrackUnmutedMutex.RUnlock()
return len(fake.onTrackUnmutedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo)) {
fake.onTrackUnmutedMutex.Lock()
defer fake.onTrackUnmutedMutex.Unlock()
fake.OnTrackUnmutedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnmutedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo) {
fake.onTrackUnmutedMutex.RLock()
defer fake.onTrackUnmutedMutex.RUnlock()
argsForCall := fake.onTrackUnmutedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublished(arg1 livekit.ParticipantID, arg2 livekit.ParticipantIdentity, arg3 *livekit.TrackInfo, arg4 bool) {
fake.onTrackUnpublishedMutex.Lock()
fake.onTrackUnpublishedArgsForCall = append(fake.onTrackUnpublishedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 livekit.ParticipantIdentity
arg3 *livekit.TrackInfo
arg4 bool
}{arg1, arg2, arg3, arg4})
stub := fake.OnTrackUnpublishedStub
fake.recordInvocation("OnTrackUnpublished", []interface{}{arg1, arg2, arg3, arg4})
fake.onTrackUnpublishedMutex.Unlock()
if stub != nil {
fake.OnTrackUnpublishedStub(arg1, arg2, arg3, arg4)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedCallCount() int {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
return len(fake.onTrackUnpublishedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedCalls(stub func(livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool)) {
fake.onTrackUnpublishedMutex.Lock()
defer fake.onTrackUnpublishedMutex.Unlock()
fake.OnTrackUnpublishedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnpublishedArgsForCall(i int) (livekit.ParticipantID, livekit.ParticipantIdentity, *livekit.TrackInfo, bool) {
fake.onTrackUnpublishedMutex.RLock()
defer fake.onTrackUnpublishedMutex.RUnlock()
argsForCall := fake.onTrackUnpublishedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribed(arg1 livekit.ParticipantID, arg2 *livekit.TrackInfo, arg3 bool) {
fake.onTrackUnsubscribedMutex.Lock()
fake.onTrackUnsubscribedArgsForCall = append(fake.onTrackUnsubscribedArgsForCall, struct {
arg1 livekit.ParticipantID
arg2 *livekit.TrackInfo
arg3 bool
}{arg1, arg2, arg3})
stub := fake.OnTrackUnsubscribedStub
fake.recordInvocation("OnTrackUnsubscribed", []interface{}{arg1, arg2, arg3})
fake.onTrackUnsubscribedMutex.Unlock()
if stub != nil {
fake.OnTrackUnsubscribedStub(arg1, arg2, arg3)
}
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedCallCount() int {
fake.onTrackUnsubscribedMutex.RLock()
defer fake.onTrackUnsubscribedMutex.RUnlock()
return len(fake.onTrackUnsubscribedArgsForCall)
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedCalls(stub func(livekit.ParticipantID, *livekit.TrackInfo, bool)) {
fake.onTrackUnsubscribedMutex.Lock()
defer fake.onTrackUnsubscribedMutex.Unlock()
fake.OnTrackUnsubscribedStub = stub
}
func (fake *FakeParticipantTelemetryListener) OnTrackUnsubscribedArgsForCall(i int) (livekit.ParticipantID, *livekit.TrackInfo, bool) {
fake.onTrackUnsubscribedMutex.RLock()
defer fake.onTrackUnsubscribedMutex.RUnlock()
argsForCall := fake.onTrackUnsubscribedArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeParticipantTelemetryListener) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeParticipantTelemetryListener) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ types.ParticipantTelemetryListener = new(FakeParticipantTelemetryListener)
+74 -242
View File
@@ -57,6 +57,18 @@ type FakeRoom struct {
arg2 livekit.ParticipantID
arg3 types.ParticipantCloseReason
}
ResolveDataTrackForSubscriberStub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult
resolveDataTrackForSubscriberMutex sync.RWMutex
resolveDataTrackForSubscriberArgsForCall []struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}
resolveDataTrackForSubscriberReturns struct {
result1 types.DataResolverResult
}
resolveDataTrackForSubscriberReturnsOnCall map[int]struct {
result1 types.DataResolverResult
}
ResolveMediaTrackForSubscriberStub func(types.LocalParticipant, livekit.TrackID) types.MediaResolverResult
resolveMediaTrackForSubscriberMutex sync.RWMutex
resolveMediaTrackForSubscriberArgsForCall []struct {
@@ -69,42 +81,6 @@ type FakeRoom struct {
resolveMediaTrackForSubscriberReturnsOnCall map[int]struct {
result1 types.MediaResolverResult
}
SimulateScenarioStub func(types.LocalParticipant, *livekit.SimulateScenario) error
simulateScenarioMutex sync.RWMutex
simulateScenarioArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SimulateScenario
}
simulateScenarioReturns struct {
result1 error
}
simulateScenarioReturnsOnCall map[int]struct {
result1 error
}
SyncStateStub func(types.LocalParticipant, *livekit.SyncState) error
syncStateMutex sync.RWMutex
syncStateArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SyncState
}
syncStateReturns struct {
result1 error
}
syncStateReturnsOnCall map[int]struct {
result1 error
}
UpdateSubscriptionPermissionStub func(types.LocalParticipant, *livekit.SubscriptionPermission) error
updateSubscriptionPermissionMutex sync.RWMutex
updateSubscriptionPermissionArgsForCall []struct {
arg1 types.LocalParticipant
arg2 *livekit.SubscriptionPermission
}
updateSubscriptionPermissionReturns struct {
result1 error
}
updateSubscriptionPermissionReturnsOnCall map[int]struct {
result1 error
}
UpdateSubscriptionsStub func(types.LocalParticipant, []livekit.TrackID, []*livekit.ParticipantTracks, bool)
updateSubscriptionsMutex sync.RWMutex
updateSubscriptionsArgsForCall []struct {
@@ -371,6 +347,68 @@ func (fake *FakeRoom) RemoveParticipantArgsForCall(i int) (livekit.ParticipantId
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakeRoom) ResolveDataTrackForSubscriber(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.DataResolverResult {
fake.resolveDataTrackForSubscriberMutex.Lock()
ret, specificReturn := fake.resolveDataTrackForSubscriberReturnsOnCall[len(fake.resolveDataTrackForSubscriberArgsForCall)]
fake.resolveDataTrackForSubscriberArgsForCall = append(fake.resolveDataTrackForSubscriberArgsForCall, struct {
arg1 types.LocalParticipant
arg2 livekit.TrackID
}{arg1, arg2})
stub := fake.ResolveDataTrackForSubscriberStub
fakeReturns := fake.resolveDataTrackForSubscriberReturns
fake.recordInvocation("ResolveDataTrackForSubscriber", []interface{}{arg1, arg2})
fake.resolveDataTrackForSubscriberMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeRoom) ResolveDataTrackForSubscriberCallCount() int {
fake.resolveDataTrackForSubscriberMutex.RLock()
defer fake.resolveDataTrackForSubscriberMutex.RUnlock()
return len(fake.resolveDataTrackForSubscriberArgsForCall)
}
func (fake *FakeRoom) ResolveDataTrackForSubscriberCalls(stub func(types.LocalParticipant, livekit.TrackID) types.DataResolverResult) {
fake.resolveDataTrackForSubscriberMutex.Lock()
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
fake.ResolveDataTrackForSubscriberStub = stub
}
func (fake *FakeRoom) ResolveDataTrackForSubscriberArgsForCall(i int) (types.LocalParticipant, livekit.TrackID) {
fake.resolveDataTrackForSubscriberMutex.RLock()
defer fake.resolveDataTrackForSubscriberMutex.RUnlock()
argsForCall := fake.resolveDataTrackForSubscriberArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeRoom) ResolveDataTrackForSubscriberReturns(result1 types.DataResolverResult) {
fake.resolveDataTrackForSubscriberMutex.Lock()
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
fake.ResolveDataTrackForSubscriberStub = nil
fake.resolveDataTrackForSubscriberReturns = struct {
result1 types.DataResolverResult
}{result1}
}
func (fake *FakeRoom) ResolveDataTrackForSubscriberReturnsOnCall(i int, result1 types.DataResolverResult) {
fake.resolveDataTrackForSubscriberMutex.Lock()
defer fake.resolveDataTrackForSubscriberMutex.Unlock()
fake.ResolveDataTrackForSubscriberStub = nil
if fake.resolveDataTrackForSubscriberReturnsOnCall == nil {
fake.resolveDataTrackForSubscriberReturnsOnCall = make(map[int]struct {
result1 types.DataResolverResult
})
}
fake.resolveDataTrackForSubscriberReturnsOnCall[i] = struct {
result1 types.DataResolverResult
}{result1}
}
func (fake *FakeRoom) ResolveMediaTrackForSubscriber(arg1 types.LocalParticipant, arg2 livekit.TrackID) types.MediaResolverResult {
fake.resolveMediaTrackForSubscriberMutex.Lock()
ret, specificReturn := fake.resolveMediaTrackForSubscriberReturnsOnCall[len(fake.resolveMediaTrackForSubscriberArgsForCall)]
@@ -433,192 +471,6 @@ func (fake *FakeRoom) ResolveMediaTrackForSubscriberReturnsOnCall(i int, result1
}{result1}
}
func (fake *FakeRoom) SimulateScenario(arg1 types.LocalParticipant, arg2 *livekit.SimulateScenario) error {
fake.simulateScenarioMutex.Lock()
ret, specificReturn := fake.simulateScenarioReturnsOnCall[len(fake.simulateScenarioArgsForCall)]
fake.simulateScenarioArgsForCall = append(fake.simulateScenarioArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SimulateScenario
}{arg1, arg2})
stub := fake.SimulateScenarioStub
fakeReturns := fake.simulateScenarioReturns
fake.recordInvocation("SimulateScenario", []interface{}{arg1, arg2})
fake.simulateScenarioMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeRoom) SimulateScenarioCallCount() int {
fake.simulateScenarioMutex.RLock()
defer fake.simulateScenarioMutex.RUnlock()
return len(fake.simulateScenarioArgsForCall)
}
func (fake *FakeRoom) SimulateScenarioCalls(stub func(types.LocalParticipant, *livekit.SimulateScenario) error) {
fake.simulateScenarioMutex.Lock()
defer fake.simulateScenarioMutex.Unlock()
fake.SimulateScenarioStub = stub
}
func (fake *FakeRoom) SimulateScenarioArgsForCall(i int) (types.LocalParticipant, *livekit.SimulateScenario) {
fake.simulateScenarioMutex.RLock()
defer fake.simulateScenarioMutex.RUnlock()
argsForCall := fake.simulateScenarioArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeRoom) SimulateScenarioReturns(result1 error) {
fake.simulateScenarioMutex.Lock()
defer fake.simulateScenarioMutex.Unlock()
fake.SimulateScenarioStub = nil
fake.simulateScenarioReturns = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) SimulateScenarioReturnsOnCall(i int, result1 error) {
fake.simulateScenarioMutex.Lock()
defer fake.simulateScenarioMutex.Unlock()
fake.SimulateScenarioStub = nil
if fake.simulateScenarioReturnsOnCall == nil {
fake.simulateScenarioReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.simulateScenarioReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) SyncState(arg1 types.LocalParticipant, arg2 *livekit.SyncState) error {
fake.syncStateMutex.Lock()
ret, specificReturn := fake.syncStateReturnsOnCall[len(fake.syncStateArgsForCall)]
fake.syncStateArgsForCall = append(fake.syncStateArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SyncState
}{arg1, arg2})
stub := fake.SyncStateStub
fakeReturns := fake.syncStateReturns
fake.recordInvocation("SyncState", []interface{}{arg1, arg2})
fake.syncStateMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeRoom) SyncStateCallCount() int {
fake.syncStateMutex.RLock()
defer fake.syncStateMutex.RUnlock()
return len(fake.syncStateArgsForCall)
}
func (fake *FakeRoom) SyncStateCalls(stub func(types.LocalParticipant, *livekit.SyncState) error) {
fake.syncStateMutex.Lock()
defer fake.syncStateMutex.Unlock()
fake.SyncStateStub = stub
}
func (fake *FakeRoom) SyncStateArgsForCall(i int) (types.LocalParticipant, *livekit.SyncState) {
fake.syncStateMutex.RLock()
defer fake.syncStateMutex.RUnlock()
argsForCall := fake.syncStateArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeRoom) SyncStateReturns(result1 error) {
fake.syncStateMutex.Lock()
defer fake.syncStateMutex.Unlock()
fake.SyncStateStub = nil
fake.syncStateReturns = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) SyncStateReturnsOnCall(i int, result1 error) {
fake.syncStateMutex.Lock()
defer fake.syncStateMutex.Unlock()
fake.SyncStateStub = nil
if fake.syncStateReturnsOnCall == nil {
fake.syncStateReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.syncStateReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) UpdateSubscriptionPermission(arg1 types.LocalParticipant, arg2 *livekit.SubscriptionPermission) error {
fake.updateSubscriptionPermissionMutex.Lock()
ret, specificReturn := fake.updateSubscriptionPermissionReturnsOnCall[len(fake.updateSubscriptionPermissionArgsForCall)]
fake.updateSubscriptionPermissionArgsForCall = append(fake.updateSubscriptionPermissionArgsForCall, struct {
arg1 types.LocalParticipant
arg2 *livekit.SubscriptionPermission
}{arg1, arg2})
stub := fake.UpdateSubscriptionPermissionStub
fakeReturns := fake.updateSubscriptionPermissionReturns
fake.recordInvocation("UpdateSubscriptionPermission", []interface{}{arg1, arg2})
fake.updateSubscriptionPermissionMutex.Unlock()
if stub != nil {
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeRoom) UpdateSubscriptionPermissionCallCount() int {
fake.updateSubscriptionPermissionMutex.RLock()
defer fake.updateSubscriptionPermissionMutex.RUnlock()
return len(fake.updateSubscriptionPermissionArgsForCall)
}
func (fake *FakeRoom) UpdateSubscriptionPermissionCalls(stub func(types.LocalParticipant, *livekit.SubscriptionPermission) error) {
fake.updateSubscriptionPermissionMutex.Lock()
defer fake.updateSubscriptionPermissionMutex.Unlock()
fake.UpdateSubscriptionPermissionStub = stub
}
func (fake *FakeRoom) UpdateSubscriptionPermissionArgsForCall(i int) (types.LocalParticipant, *livekit.SubscriptionPermission) {
fake.updateSubscriptionPermissionMutex.RLock()
defer fake.updateSubscriptionPermissionMutex.RUnlock()
argsForCall := fake.updateSubscriptionPermissionArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeRoom) UpdateSubscriptionPermissionReturns(result1 error) {
fake.updateSubscriptionPermissionMutex.Lock()
defer fake.updateSubscriptionPermissionMutex.Unlock()
fake.UpdateSubscriptionPermissionStub = nil
fake.updateSubscriptionPermissionReturns = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) UpdateSubscriptionPermissionReturnsOnCall(i int, result1 error) {
fake.updateSubscriptionPermissionMutex.Lock()
defer fake.updateSubscriptionPermissionMutex.Unlock()
fake.UpdateSubscriptionPermissionStub = nil
if fake.updateSubscriptionPermissionReturnsOnCall == nil {
fake.updateSubscriptionPermissionReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.updateSubscriptionPermissionReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeRoom) UpdateSubscriptions(arg1 types.LocalParticipant, arg2 []livekit.TrackID, arg3 []*livekit.ParticipantTracks, arg4 bool) {
var arg2Copy []livekit.TrackID
if arg2 != nil {
@@ -667,26 +519,6 @@ func (fake *FakeRoom) UpdateSubscriptionsArgsForCall(i int) (types.LocalParticip
func (fake *FakeRoom) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.getLocalParticipantsMutex.RLock()
defer fake.getLocalParticipantsMutex.RUnlock()
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
fake.isDataMessageUserPacketDuplicateMutex.RLock()
defer fake.isDataMessageUserPacketDuplicateMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.removeParticipantMutex.RLock()
defer fake.removeParticipantMutex.RUnlock()
fake.resolveMediaTrackForSubscriberMutex.RLock()
defer fake.resolveMediaTrackForSubscriberMutex.RUnlock()
fake.simulateScenarioMutex.RLock()
defer fake.simulateScenarioMutex.RUnlock()
fake.syncStateMutex.RLock()
defer fake.syncStateMutex.RUnlock()
fake.updateSubscriptionPermissionMutex.RLock()
defer fake.updateSubscriptionPermissionMutex.RUnlock()
fake.updateSubscriptionsMutex.RLock()
defer fake.updateSubscriptionsMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
@@ -1052,44 +1052,6 @@ func (fake *FakeSubscribedTrack) UpdateVideoLayerCalls(stub func()) {
func (fake *FakeSubscribedTrack) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.addOnBindMutex.RLock()
defer fake.addOnBindMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.downTrackMutex.RLock()
defer fake.downTrackMutex.RUnlock()
fake.iDMutex.RLock()
defer fake.iDMutex.RUnlock()
fake.isBoundMutex.RLock()
defer fake.isBoundMutex.RUnlock()
fake.isMutedMutex.RLock()
defer fake.isMutedMutex.RUnlock()
fake.mediaTrackMutex.RLock()
defer fake.mediaTrackMutex.RUnlock()
fake.needsNegotiationMutex.RLock()
defer fake.needsNegotiationMutex.RUnlock()
fake.onCloseMutex.RLock()
defer fake.onCloseMutex.RUnlock()
fake.publisherIDMutex.RLock()
defer fake.publisherIDMutex.RUnlock()
fake.publisherIdentityMutex.RLock()
defer fake.publisherIdentityMutex.RUnlock()
fake.publisherVersionMutex.RLock()
defer fake.publisherVersionMutex.RUnlock()
fake.rTPSenderMutex.RLock()
defer fake.rTPSenderMutex.RUnlock()
fake.setPublisherMutedMutex.RLock()
defer fake.setPublisherMutedMutex.RUnlock()
fake.subscriberMutex.RLock()
defer fake.subscriberMutex.RUnlock()
fake.subscriberIDMutex.RLock()
defer fake.subscriberIDMutex.RUnlock()
fake.subscriberIdentityMutex.RLock()
defer fake.subscriberIdentityMutex.RUnlock()
fake.updateSubscriberSettingsMutex.RLock()
defer fake.updateSubscriberSettingsMutex.RUnlock()
fake.updateVideoLayerMutex.RLock()
defer fake.updateVideoLayerMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
@@ -384,16 +384,6 @@ func (fake *FakeWebsocketClient) WriteMessageReturnsOnCall(i int, result1 error)
func (fake *FakeWebsocketClient) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.closeMutex.RLock()
defer fake.closeMutex.RUnlock()
fake.readMessageMutex.RLock()
defer fake.readMessageMutex.RUnlock()
fake.setReadDeadlineMutex.RLock()
defer fake.setReadDeadlineMutex.RUnlock()
fake.writeControlMutex.RLock()
defer fake.writeControlMutex.RUnlock()
fake.writeMessageMutex.RLock()
defer fake.writeMessageMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
-132
View File
@@ -1,132 +0,0 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"github.com/pion/interceptor"
"github.com/pion/rtp"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v4"
"github.com/livekit/livekit-server/pkg/sfu/utils"
)
const (
simulcastProbeCount = 10
)
type UnhandleSimulcastOption func(r *UnhandleSimulcastInterceptor) error
func UnhandleSimulcastTracks(tracks map[uint32]SimulcastTrackInfo) UnhandleSimulcastOption {
return func(r *UnhandleSimulcastInterceptor) error {
r.simTracks = tracks
return nil
}
}
type UnhandleSimulcastInterceptorFactory struct {
opts []UnhandleSimulcastOption
}
func (f *UnhandleSimulcastInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &UnhandleSimulcastInterceptor{simTracks: map[uint32]SimulcastTrackInfo{}}
for _, o := range f.opts {
if err := o(i); err != nil {
return nil, err
}
}
return i, nil
}
func NewUnhandleSimulcastInterceptorFactory(opts ...UnhandleSimulcastOption) (*UnhandleSimulcastInterceptorFactory, error) {
return &UnhandleSimulcastInterceptorFactory{opts: opts}, nil
}
type unhandleSimulcastRTPReader struct {
SimulcastTrackInfo
tryTimes int
reader interceptor.RTPReader
midExtensionID uint8
streamIDExtensionID uint8
}
func (r *unhandleSimulcastRTPReader) Read(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
n, a, err := r.reader.Read(b, a)
if r.tryTimes < 0 || err != nil {
return n, a, err
}
header := rtp.Header{}
hsize, err := header.Unmarshal(b[:n])
if err != nil {
return n, a, nil
}
var mid, rid string
if payload := header.GetExtension(r.midExtensionID); payload != nil {
mid = string(payload)
}
if payload := header.GetExtension(r.streamIDExtensionID); payload != nil {
rid = string(payload)
}
if mid != "" && rid != "" {
r.tryTimes = -1
return n, a, nil
}
r.tryTimes--
if mid == "" {
header.SetExtension(r.midExtensionID, []byte(r.Mid))
}
if rid == "" {
header.SetExtension(r.streamIDExtensionID, []byte(r.Rid))
}
hsize2 := header.MarshalSize()
if hsize2-hsize+n > len(b) { // no enough buf to set extension
return n, a, nil
}
copy(b[hsize2:], b[hsize:n])
header.MarshalTo(b)
return hsize2 - hsize + n, a, nil
}
type UnhandleSimulcastInterceptor struct {
interceptor.NoOp
simTracks map[uint32]SimulcastTrackInfo
}
func (u *UnhandleSimulcastInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
if t, ok := u.simTracks[info.SSRC]; ok {
// if we support fec for simulcast streams at future, should get rsid extensions
midExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESMidURI})
streamIDExtensionID := utils.GetHeaderExtensionID(info.RTPHeaderExtensions, webrtc.RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI})
if midExtensionID == 0 || streamIDExtensionID == 0 {
return reader
}
return &unhandleSimulcastRTPReader{
SimulcastTrackInfo: t,
reader: reader,
tryTimes: simulcastProbeCount,
midExtensionID: uint8(midExtensionID),
streamIDExtensionID: uint8(streamIDExtensionID),
}
}
return reader
}
+126
View File
@@ -0,0 +1,126 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"maps"
"slices"
"sync"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
type UpDataTrackManagerParams struct {
Logger logger.Logger
Participant types.Participant
}
type UpDataTrackManager struct {
params UpDataTrackManagerParams
lock sync.RWMutex
dataTracks map[uint16]types.DataTrack
closed bool
}
func NewUpDataTrackManager(params UpDataTrackManagerParams) *UpDataTrackManager {
return &UpDataTrackManager{
params: params,
dataTracks: make(map[uint16]types.DataTrack),
}
}
func (u *UpDataTrackManager) Close() {
u.lock.Lock()
if u.closed {
u.lock.Unlock()
return
}
u.closed = true
dataTracks := u.dataTracks
u.dataTracks = make(map[uint16]types.DataTrack)
u.lock.Unlock()
for _, t := range dataTracks {
t.Close()
}
}
func (u *UpDataTrackManager) AddPublishedDataTrack(dt types.DataTrack) {
u.lock.Lock()
u.dataTracks[dt.PubHandle()] = dt
u.lock.Unlock()
u.params.Participant.GetParticipantListener().OnDataTrackPublished(u.params.Participant, dt)
}
func (u *UpDataTrackManager) RemovePublishedDataTrack(dt types.DataTrack) {
var found bool
pubHandle := dt.PubHandle()
u.lock.Lock()
if u.dataTracks[pubHandle] == dt {
delete(u.dataTracks, pubHandle)
found = true
}
u.lock.Unlock()
if found {
dt.Close()
u.params.Participant.GetParticipantListener().OnDataTrackUnpublished(u.params.Participant, dt)
}
}
func (u *UpDataTrackManager) GetPublishedDataTracks() []types.DataTrack {
u.lock.RLock()
defer u.lock.RUnlock()
return slices.Collect(maps.Values(u.dataTracks))
}
func (u *UpDataTrackManager) GetPublishedDataTrack(handle uint16) types.DataTrack {
u.lock.RLock()
defer u.lock.RUnlock()
return u.dataTracks[handle]
}
func (u *UpDataTrackManager) HandleReceivedDataTrackMessage(data []byte, packet *datatrack.Packet, arrivalTime int64) {
u.lock.RLock()
dt := u.dataTracks[packet.Handle]
u.lock.RUnlock()
if dt == nil {
return
}
dt.HandlePacket(data, packet, arrivalTime)
}
func (u *UpDataTrackManager) ToProto() []*livekit.DataTrackInfo {
u.lock.RLock()
defer u.lock.RUnlock()
var dataTrackInfos []*livekit.DataTrackInfo
for _, dt := range u.dataTracks {
dataTrackInfos = append(dataTrackInfos, dt.ToProto())
}
return dataTrackInfos
}
+30 -17
View File
@@ -16,11 +16,12 @@ package rtc
import (
"errors"
"maps"
"slices"
"sync"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"golang.org/x/exp/maps"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/utils"
@@ -116,13 +117,15 @@ func (u *UpTrackManager) OnPublishedTrackUpdated(f func(track types.MediaTrack))
u.onTrackUpdated = f
}
func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted bool) types.MediaTrack {
func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted bool) (types.MediaTrack, bool) {
changed := false
track := u.GetPublishedTrack(trackID)
if track != nil {
currentMuted := track.IsMuted()
track.SetMuted(muted)
if currentMuted != track.IsMuted() {
changed = true
u.params.Logger.Debugw("publisher mute status changed", "trackID", trackID, "muted", track.IsMuted())
if u.onTrackUpdated != nil {
u.onTrackUpdated(track)
@@ -130,7 +133,7 @@ func (u *UpTrackManager) SetPublishedTrackMuted(trackID livekit.TrackID, muted b
}
}
return track
return track, changed
}
func (u *UpTrackManager) GetPublishedTrack(trackID livekit.TrackID) types.MediaTrack {
@@ -144,7 +147,7 @@ func (u *UpTrackManager) GetPublishedTracks() []types.MediaTrack {
u.lock.RLock()
defer u.lock.RUnlock()
return maps.Values(u.publishedTracks)
return slices.Collect(maps.Values(u.publishedTracks))
}
func (u *UpTrackManager) UpdateSubscriptionPermission(
@@ -272,12 +275,9 @@ func (u *UpTrackManager) AddPublishedTrack(track types.MediaTrack) {
})
}
func (u *UpTrackManager) RemovePublishedTrack(track types.MediaTrack, isExpectedToResume bool, shouldClose bool) {
if shouldClose {
track.Close(isExpectedToResume)
} else {
track.ClearAllReceivers(isExpectedToResume)
}
func (u *UpTrackManager) RemovePublishedTrack(track types.MediaTrack, isExpectedToResume bool) {
track.Close(isExpectedToResume)
u.lock.Lock()
delete(u.publishedTracks, track.ID())
u.lock.Unlock()
@@ -311,7 +311,11 @@ func (u *UpTrackManager) parseSubscriptionPermissionsLocked(
sub := resolver(livekit.ParticipantID(trackPerms.ParticipantSid))
if sub == nil {
u.params.Logger.Warnw("could not find subscriber for permissions update", nil, "subscriberID", trackPerms.ParticipantSid)
u.params.Logger.Warnw(
"could not find subscriber for permissions update", nil,
"subscriberID", trackPerms.ParticipantSid,
"subscriptionPermission", logger.Proto(subscriptionPermission),
)
continue
}
@@ -320,10 +324,19 @@ func (u *UpTrackManager) parseSubscriptionPermissionsLocked(
if trackPerms.ParticipantSid != "" {
sub := resolver(livekit.ParticipantID(trackPerms.ParticipantSid))
if sub != nil && sub.Identity() != subscriberIdentity {
u.params.Logger.Errorw("participant identity mismatch", nil, "expected", subscriberIdentity, "got", sub.Identity())
u.params.Logger.Errorw(
"participant identity mismatch", nil,
"expected", subscriberIdentity,
"got", sub.Identity(),
"subscriptionPermission", logger.Proto(subscriptionPermission),
)
}
if sub == nil {
u.params.Logger.Warnw("could not find subscriber for permissions update", nil, "subscriberID", trackPerms.ParticipantSid)
u.params.Logger.Warnw(
"could not find subscriber for permissions update", nil,
"subscriberID", trackPerms.ParticipantSid,
"subscriptionPermission", logger.Proto(subscriptionPermission),
)
}
}
}
@@ -399,16 +412,16 @@ func (u *UpTrackManager) maybeRevokeSubscriptions() {
}
}
func (u *UpTrackManager) DebugInfo() map[string]interface{} {
info := map[string]interface{}{}
publishedTrackInfo := make(map[livekit.TrackID]interface{})
func (u *UpTrackManager) DebugInfo() map[string]any {
info := map[string]any{}
publishedTrackInfo := make(map[livekit.TrackID]any)
u.lock.RLock()
for trackID, track := range u.publishedTracks {
if mt, ok := track.(*MediaTrack); ok {
publishedTrackInfo[trackID] = mt.DebugInfo()
} else {
publishedTrackInfo[trackID] = map[string]interface{}{
publishedTrackInfo[trackID] = map[string]any{
"ID": track.ID(),
"Kind": track.Kind().String(),
"PubMuted": track.IsMuted(),
+33 -48
View File
@@ -15,15 +15,15 @@
package rtc
import (
"encoding/json"
"errors"
"io"
"net"
"strings"
"github.com/pion/webrtc/v4"
"google.golang.org/protobuf/proto"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
)
@@ -80,49 +80,6 @@ func UnpackDataTrackLabel(packed string) (participantID livekit.ParticipantID, t
return
}
func ToProtoSessionDescription(sd webrtc.SessionDescription) *livekit.SessionDescription {
return &livekit.SessionDescription{
Type: sd.Type.String(),
Sdp: sd.SDP,
}
}
func FromProtoSessionDescription(sd *livekit.SessionDescription) webrtc.SessionDescription {
var sdType webrtc.SDPType
switch sd.Type {
case webrtc.SDPTypeOffer.String():
sdType = webrtc.SDPTypeOffer
case webrtc.SDPTypeAnswer.String():
sdType = webrtc.SDPTypeAnswer
case webrtc.SDPTypePranswer.String():
sdType = webrtc.SDPTypePranswer
case webrtc.SDPTypeRollback.String():
sdType = webrtc.SDPTypeRollback
}
return webrtc.SessionDescription{
Type: sdType,
SDP: sd.Sdp,
}
}
func ToProtoTrickle(candidateInit webrtc.ICECandidateInit, target livekit.SignalTarget, final bool) *livekit.TrickleRequest {
data, _ := json.Marshal(candidateInit)
return &livekit.TrickleRequest{
CandidateInit: string(data),
Target: target,
Final: final,
}
}
func FromProtoTrickle(trickle *livekit.TrickleRequest) (webrtc.ICECandidateInit, error) {
ci := webrtc.ICECandidateInit{}
err := json.Unmarshal([]byte(trickle.CandidateInit), &ci)
if err != nil {
return webrtc.ICECandidateInit{}, err
}
return ci, nil
}
func ToProtoTrackKind(kind webrtc.RTPCodecType) livekit.TrackType {
switch kind {
case webrtc.RTPCodecTypeVideo:
@@ -160,12 +117,12 @@ func Recover(l logger.Logger) any {
// logger helpers
func LoggerWithParticipant(l logger.Logger, identity livekit.ParticipantIdentity, sid livekit.ParticipantID, isRemote bool) logger.Logger {
values := make([]interface{}, 0, 4)
values := make([]any, 0, 4)
if identity != "" {
values = append(values, "participant", identity)
}
if sid != "" {
values = append(values, "pID", sid)
values = append(values, "participantID", sid)
}
values = append(values, "remote", isRemote)
// enable sampling per participant
@@ -173,7 +130,7 @@ func LoggerWithParticipant(l logger.Logger, identity livekit.ParticipantIdentity
}
func LoggerWithRoom(l logger.Logger, name livekit.RoomName, roomID livekit.RoomID) logger.Logger {
values := make([]interface{}, 0, 2)
values := make([]any, 0, 2)
if name != "" {
values = append(values, "room", name)
}
@@ -215,3 +172,31 @@ func MaybeTruncateIP(addr string) string {
return addr[:len(addr)-3] + "..."
}
func ChunkProtoBatch[T proto.Message](batch []T, target int) [][]T {
var chunks [][]T
var start, size int
for i, m := range batch {
s := proto.Size(m)
if size+s > target {
if start < i {
chunks = append(chunks, batch[start:i])
}
start = i
size = 0
}
size += s
}
if start < len(batch) {
chunks = append(chunks, batch[start:])
}
return chunks
}
func IsRedEnabled(ti *livekit.TrackInfo) bool {
if len(ti.Codecs) != 0 && ti.Codecs[0].MimeType != "" {
return mime.IsMimeTypeStringRED(ti.Codecs[0].MimeType)
}
return !ti.GetDisableRed()
}
+30
View File
@@ -15,11 +15,16 @@
package rtc
import (
"math/rand/v2"
"strings"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/utils/guid"
)
func TestPackStreamId(t *testing.T) {
@@ -43,3 +48,28 @@ func TestPackDataTrackLabel(t *testing.T) {
require.Equal(t, trackID, tr)
require.Equal(t, label, l)
}
func TestChunkProtoBatch(t *testing.T) {
rng := rand.New(rand.NewPCG(1, 2))
var updates []*livekit.ParticipantInfo
for range 32 {
updates = append(updates, &livekit.ParticipantInfo{
Sid: guid.New(guid.ParticipantPrefix),
Identity: uuid.NewString(),
Metadata: strings.Repeat("x", rng.IntN(128*1024)),
})
}
target := 64 * 1024
batches := ChunkProtoBatch(updates, target)
var count int
for _, b := range batches {
var sum int
for _, m := range b {
sum += proto.Size(m)
count++
}
require.True(t, sum < target || len(b) == 1, "batch size exceeds target")
}
require.Equal(t, len(updates), count)
}
+137 -114
View File
@@ -16,18 +16,21 @@ package rtc
import (
"errors"
"maps"
"slices"
"sync"
"github.com/pion/webrtc/v4"
"go.uber.org/atomic"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"github.com/livekit/mediatransportutil/pkg/codec"
protoCodecs "github.com/livekit/protocol/codecs"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/livekit-server/pkg/sfu"
"github.com/livekit/livekit-server/pkg/sfu/mime"
"github.com/livekit/livekit-server/pkg/sfu/buffer"
)
// wrapper around WebRTC receiver, overriding its ID
@@ -39,6 +42,7 @@ type WrappedReceiverParams struct {
UpstreamCodecs []webrtc.RTPCodecParameters
Logger logger.Logger
DisableRed bool
IsEncrypted bool
}
type WrappedReceiver struct {
@@ -62,16 +66,10 @@ func NewWrappedReceiver(params WrappedReceiverParams) *WrappedReceiver {
normalizedMimeType := mime.NormalizeMimeType(codecs[0].MimeType)
if normalizedMimeType == mime.MimeTypeRED {
// if upstream is opus/red, then add opus to match clients that don't support red
codecs = append(codecs, webrtc.RTPCodecParameters{
RTPCodecCapability: OpusCodecCapability,
PayloadType: 111,
})
codecs = append(codecs, protoCodecs.OpusCodecParameters)
} else if !params.DisableRed && normalizedMimeType == mime.MimeTypeOpus {
// if upstream is opus only and red enabled, add red to match clients that support red
codecs = append(codecs, webrtc.RTPCodecParameters{
RTPCodecCapability: RedCodecCapability,
PayloadType: 63,
})
codecs = append(codecs, protoCodecs.RedCodecParameters)
// prefer red codec
codecs[0], codecs[1] = codecs[1], codecs[0]
}
@@ -92,54 +90,67 @@ func (r *WrappedReceiver) StreamID() string {
return r.params.StreamId
}
// DetermineReceiver determines the receiver of negotiated codec and return ready state of the receiver
func (r *WrappedReceiver) DetermineReceiver(codec webrtc.RTPCodecCapability) bool {
// DetermineReceiver determines the receiver of negotiated codec and returns
//
// isAvailable: returns true if given codec is a potential codec from publisher or if an existing published codec can be translated
// needsPublish: indicates if the codec is needed from publisher, some combinations can be achieved via codec translation internally,
//
// example: unencrypted opus -> RED translation and vice-versa can be done without the need for publisher to send the other codec.
func (r *WrappedReceiver) DetermineReceiver(codec webrtc.RTPCodecCapability) (isAvailable bool, needsPublish bool) {
r.lock.Lock()
reason := "no matching receiver"
codecMimeType := mime.NormalizeMimeType(codec.MimeType)
var trackReceiver sfu.TrackReceiver
for _, receiver := range r.receivers {
receiverMimeType := receiver.Mime()
if receiverMimeType == codecMimeType {
trackReceiver = receiver
break
} else if receiverMimeType == mime.MimeTypeRED && codecMimeType == mime.MimeTypeOpus {
// audio opus/red can match opus only
trackReceiver = receiver.GetPrimaryReceiverForRed()
break
} else if receiverMimeType == mime.MimeTypeOpus && codecMimeType == mime.MimeTypeRED {
trackReceiver = receiver.GetRedReceiver()
isAvailable = true
needsPublish = true
break
}
if receiverMimeType == mime.MimeTypeRED && codecMimeType == mime.MimeTypeOpus {
// audio opus/red can match opus only
if !r.params.IsEncrypted { // cannot match encrypted source
trackReceiver = receiver.GetPrimaryReceiverForRed()
isAvailable = true
break
} else {
reason = "encrypted source"
}
} else if receiverMimeType == mime.MimeTypeOpus && codecMimeType == mime.MimeTypeRED {
if !r.params.IsEncrypted { // cannot match encrypted source
trackReceiver = receiver.GetRedReceiver()
isAvailable = true
break
} else {
reason = "encrypted source"
}
}
}
if trackReceiver == nil {
r.params.Logger.Errorw("can't determine receiver for codec", nil, "codec", codec.MimeType)
if len(r.receivers) > 0 {
trackReceiver = r.receivers[0]
}
r.lock.Unlock()
r.params.Logger.Warnw(
"can't determine receiver for codec", nil,
"codec", codec.MimeType,
"reason", reason,
)
return
}
r.TrackReceiver = trackReceiver
var onReadyCallbacks []func()
if trackReceiver != nil {
onReadyCallbacks = r.onReadyCallbacks
r.onReadyCallbacks = nil
}
onReadyCallbacks := r.onReadyCallbacks
r.onReadyCallbacks = nil
r.lock.Unlock()
if trackReceiver != nil {
for _, f := range onReadyCallbacks {
trackReceiver.AddOnReady(f)
}
if s, ok := trackReceiver.(*simulcastReceiver); ok {
if d, ok := s.TrackReceiver.(*DummyReceiver); ok {
return d.IsReady()
}
}
return true
for _, f := range onReadyCallbacks {
trackReceiver.AddOnReady(f)
}
return false
return
}
func (r *WrappedReceiver) Codecs() []webrtc.RTPCodecParameters {
@@ -186,8 +197,6 @@ type DummyReceiver struct {
settingsLock sync.Mutex
maxExpectedLayerValid bool
maxExpectedLayer int32
pausedValid bool
paused bool
redReceiver, primaryReceiver *DummyRedReceiver
}
@@ -217,8 +226,10 @@ func (d *DummyReceiver) Upgrade(receiver sfu.TrackReceiver) {
receiver.AddDownTrack(t)
}
d.downTracks = make(map[livekit.ParticipantID]sfu.TrackSender)
onReadyCallbacks := d.onReadyCallbacks
d.onReadyCallbacks = nil
codecChange := d.onCodecStateChange
d.onCodecStateChange = nil
d.downTrackLock.Unlock()
@@ -232,16 +243,15 @@ func (d *DummyReceiver) Upgrade(receiver sfu.TrackReceiver) {
}
d.settingsLock.Lock()
if d.maxExpectedLayerValid {
maxExpectedLayerValid := d.maxExpectedLayerValid
d.maxExpectedLayerValid = false
d.settingsLock.Unlock()
if maxExpectedLayerValid {
receiver.SetMaxExpectedSpatialLayer(d.maxExpectedLayer)
}
d.maxExpectedLayerValid = false
if d.pausedValid {
receiver.SetUpTrackPaused(d.paused)
}
d.pausedValid = false
d.settingsLock.Lock()
if d.primaryReceiver != nil {
d.primaryReceiver.upgrade(receiver)
}
@@ -260,83 +270,82 @@ func (d *DummyReceiver) StreamID() string {
}
func (d *DummyReceiver) Codec() webrtc.RTPCodecParameters {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.Codec()
if receiver := d.getReceiver(); receiver != nil {
return receiver.Codec()
}
return d.codec
}
func (d *DummyReceiver) Mime() mime.MimeType {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.Mime()
if receiver := d.getReceiver(); receiver != nil {
return receiver.Mime()
}
return mime.NormalizeMimeType(d.codec.MimeType)
}
func (d *DummyReceiver) VideoLayerMode() livekit.VideoLayer_Mode {
if receiver := d.getReceiver(); receiver != nil {
return receiver.VideoLayerMode()
}
return buffer.GetVideoLayerModeForMimeType(d.Mime(), d.TrackInfo())
}
func (d *DummyReceiver) HeaderExtensions() []webrtc.RTPHeaderExtensionParameter {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.HeaderExtensions()
if receiver := d.getReceiver(); receiver != nil {
return receiver.HeaderExtensions()
}
return d.headerExtensions
}
func (d *DummyReceiver) ReadRTP(buf []byte, layer uint8, esn uint64) (int, error) {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.ReadRTP(buf, layer, esn)
if receiver := d.getReceiver(); receiver != nil {
return receiver.ReadRTP(buf, layer, esn)
}
return 0, errors.New("no receiver")
}
func (d *DummyReceiver) GetLayeredBitrate() ([]int32, sfu.Bitrates) {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.GetLayeredBitrate()
if receiver := d.getReceiver(); receiver != nil {
return receiver.GetLayeredBitrate()
}
return nil, sfu.Bitrates{}
}
func (d *DummyReceiver) GetAudioLevel() (float64, bool) {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.GetAudioLevel()
if receiver := d.getReceiver(); receiver != nil {
return receiver.GetAudioLevel()
}
return 0, false
}
func (d *DummyReceiver) SendPLI(layer int32, force bool) {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
r.SendPLI(layer, force)
}
}
func (d *DummyReceiver) SetUpTrackPaused(paused bool) {
d.settingsLock.Lock()
defer d.settingsLock.Unlock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
d.pausedValid = false
r.SetUpTrackPaused(paused)
} else {
d.pausedValid = true
d.paused = paused
if receiver := d.getReceiver(); receiver != nil {
receiver.SendPLI(layer, force)
}
}
func (d *DummyReceiver) SetMaxExpectedSpatialLayer(layer int32) {
d.settingsLock.Lock()
defer d.settingsLock.Unlock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
receiver := d.getReceiver()
if receiver != nil {
d.maxExpectedLayerValid = false
r.SetMaxExpectedSpatialLayer(layer)
} else {
d.maxExpectedLayerValid = true
d.maxExpectedLayer = layer
}
d.settingsLock.Unlock()
if receiver != nil {
receiver.SetMaxExpectedSpatialLayer(layer)
}
}
func (d *DummyReceiver) AddDownTrack(track sfu.TrackSender) error {
d.downTrackLock.Lock()
defer d.downTrackLock.Unlock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
r.AddDownTrack(track)
if receiver := d.getReceiver(); receiver != nil {
receiver.AddDownTrack(track)
} else {
d.downTracks[track.SubscriberID()] = track
}
@@ -347,8 +356,8 @@ func (d *DummyReceiver) DeleteDownTrack(subscriberID livekit.ParticipantID) {
d.downTrackLock.Lock()
defer d.downTrackLock.Unlock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
r.DeleteDownTrack(subscriberID)
if receiver := d.getReceiver(); receiver != nil {
receiver.DeleteDownTrack(subscriberID)
} else {
delete(d.downTracks, subscriberID)
}
@@ -358,42 +367,42 @@ func (d *DummyReceiver) GetDownTracks() []sfu.TrackSender {
d.downTrackLock.Lock()
defer d.downTrackLock.Unlock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.GetDownTracks()
if receiver := d.getReceiver(); receiver != nil {
return receiver.GetDownTracks()
}
return maps.Values(d.downTracks)
return slices.Collect(maps.Values(d.downTracks))
}
func (d *DummyReceiver) DebugInfo() map[string]interface{} {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.DebugInfo()
func (d *DummyReceiver) DebugInfo() map[string]any {
if receiver := d.getReceiver(); receiver != nil {
return receiver.DebugInfo()
}
return nil
}
func (d *DummyReceiver) GetTemporalLayerFpsForSpatial(spatial int32) []float32 {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.GetTemporalLayerFpsForSpatial(spatial)
if receiver := d.getReceiver(); receiver != nil {
return receiver.GetTemporalLayerFpsForSpatial(spatial)
}
return nil
}
func (d *DummyReceiver) TrackInfo() *livekit.TrackInfo {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.TrackInfo()
if receiver := d.getReceiver(); receiver != nil {
return receiver.TrackInfo()
}
return nil
}
func (d *DummyReceiver) UpdateTrackInfo(ti *livekit.TrackInfo) {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
r.UpdateTrackInfo(ti)
if receiver := d.getReceiver(); receiver != nil {
receiver.UpdateTrackInfo(ti)
}
}
func (d *DummyReceiver) IsClosed() bool {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.IsClosed()
if receiver := d.getReceiver(); receiver != nil {
return receiver.IsClosed()
}
return false
}
@@ -424,18 +433,16 @@ func (d *DummyReceiver) GetRedReceiver() sfu.TrackReceiver {
}
func (d *DummyReceiver) GetTrackStats() *livekit.RTPStats {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.GetTrackStats()
if receiver := d.getReceiver(); receiver != nil {
return receiver.GetTrackStats()
}
return nil
}
func (d *DummyReceiver) AddOnReady(f func()) {
var receiver sfu.TrackReceiver
d.downTrackLock.Lock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
receiver = r
} else {
receiver := d.getReceiver()
if receiver == nil {
d.onReadyCallbacks = append(d.onReadyCallbacks, f)
}
d.downTrackLock.Unlock()
@@ -444,16 +451,10 @@ func (d *DummyReceiver) AddOnReady(f func()) {
}
}
func (d *DummyReceiver) IsReady() bool {
return d.receiver.Load() != nil
}
func (d *DummyReceiver) AddOnCodecStateChange(f func(codec webrtc.RTPCodecParameters, state sfu.ReceiverCodecState)) {
var receiver sfu.TrackReceiver
d.downTrackLock.Lock()
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
receiver = r
} else {
receiver := d.getReceiver()
if receiver == nil {
d.onCodecStateChange = append(d.onCodecStateChange, f)
}
d.downTrackLock.Unlock()
@@ -463,12 +464,34 @@ func (d *DummyReceiver) AddOnCodecStateChange(f func(codec webrtc.RTPCodecParame
}
func (d *DummyReceiver) CodecState() sfu.ReceiverCodecState {
if r, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return r.CodecState()
if receiver := d.getReceiver(); receiver != nil {
return receiver.CodecState()
}
return sfu.ReceiverCodecStateNormal
}
func (d *DummyReceiver) VideoSizes() []codec.VideoSize {
if receiver := d.getReceiver(); receiver != nil {
return receiver.VideoSizes()
}
return nil
}
func (d *DummyReceiver) Restart(reason string) {
if receiver := d.getReceiver(); receiver != nil {
receiver.Restart(reason)
}
}
func (d *DummyReceiver) getReceiver() sfu.TrackReceiver {
if receiver, ok := d.receiver.Load().(sfu.TrackReceiver); ok {
return receiver
}
return nil
}
// --------------------------------------------
type DummyRedReceiver struct {
@@ -520,7 +543,7 @@ func (d *DummyRedReceiver) GetDownTracks() []sfu.TrackSender {
if r, ok := d.redReceiver.Load().(sfu.TrackReceiver); ok {
return r.GetDownTracks()
}
return maps.Values(d.downTracks)
return slices.Collect(maps.Values(d.downTracks))
}
func (d *DummyRedReceiver) ReadRTP(buf []byte, layer uint8, esn uint64) (int, error) {