mirror of
https://github.com/livekit/livekit.git
synced 2026-10-15 13:20:13 +00:00
rtc: apply simulcast rids from SDP for WHIP (one-shot) publishers (#5004)
* rtc: apply simulcast rids from SDP for WHIP (one-shot) publishers In one-shot signalling mode HandleRemoteDescription set the remote offer directly and never called OnSetRemoteDescriptionOffer, so updateRidsFromSDP did not run for WHIP participants. Every simulcast layer then mapped to spatial layer 0 and all but the first were rejected with 'duplicate layer'. * rtc: test that a one-shot simulcast offer applies rids to the pending track * rtc: use h/q rids in one-shot simulcast test, cover single-layer offer Name the simulcast test layers medium/h and low/q per convention, and add TestOneShotSingleLayerOfferUnchanged to check a one-shot offer without rids still yields a single layer with rid "" at spatial layer 0.
This commit is contained in:
@@ -824,6 +824,89 @@ func TestPreferMediaCodecForPublisher(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOneShotSimulcastOfferAppliesRids(t *testing.T) {
|
||||
participant := newParticipantForTestWithOpts("whip", &participantOpts{
|
||||
publisher: true,
|
||||
oneShot: true,
|
||||
})
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer pc.Close()
|
||||
|
||||
codec := webrtc.RTPCodecCapability{MimeType: mime.MimeTypeVP8.String()}
|
||||
medium, err := webrtc.NewTrackLocalStaticRTP(codec, "video", "stream", webrtc.WithRTPStreamID("h"))
|
||||
require.NoError(t, err)
|
||||
low, err := webrtc.NewTrackLocalStaticRTP(codec, "video", "stream", webrtc.WithRTPStreamID("q"))
|
||||
require.NoError(t, err)
|
||||
sender, err := pc.AddTrack(medium)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, sender.AddEncoding(low))
|
||||
|
||||
offer, err := pc.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, pc.SetLocalDescription(offer))
|
||||
require.Contains(t, offer.SDP, "a=simulcast:send h;q")
|
||||
|
||||
require.NoError(t, participant.HandleOffer(&livekit.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer.String(),
|
||||
Sdp: offer.SDP,
|
||||
}))
|
||||
|
||||
participant.pendingTracksLock.RLock()
|
||||
pti := participant.pendingTracks["video"]
|
||||
participant.pendingTracksLock.RUnlock()
|
||||
require.NotNil(t, pti)
|
||||
|
||||
layers := pti.trackInfos[0].Codecs[0].Layers
|
||||
require.Len(t, layers, 2)
|
||||
require.Equal(t, livekit.VideoQuality_LOW, layers[0].Quality)
|
||||
require.Equal(t, "q", layers[0].Rid)
|
||||
require.EqualValues(t, 0, layers[0].SpatialLayer)
|
||||
require.Equal(t, livekit.VideoQuality_MEDIUM, layers[1].Quality)
|
||||
require.Equal(t, "h", layers[1].Rid)
|
||||
require.EqualValues(t, 1, layers[1].SpatialLayer)
|
||||
}
|
||||
|
||||
func TestOneShotSingleLayerOfferUnchanged(t *testing.T) {
|
||||
participant := newParticipantForTestWithOpts("whip", &participantOpts{
|
||||
publisher: true,
|
||||
oneShot: true,
|
||||
})
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{})
|
||||
require.NoError(t, err)
|
||||
defer pc.Close()
|
||||
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: mime.MimeTypeVP8.String()}, "video", "stream")
|
||||
require.NoError(t, err)
|
||||
_, err = pc.AddTransceiverFromTrack(track, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
|
||||
require.NoError(t, err)
|
||||
|
||||
offer, err := pc.CreateOffer(nil)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, pc.SetLocalDescription(offer))
|
||||
require.NotContains(t, offer.SDP, "a=simulcast")
|
||||
require.NotContains(t, offer.SDP, "a=rid")
|
||||
|
||||
require.NoError(t, participant.HandleOffer(&livekit.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer.String(),
|
||||
Sdp: offer.SDP,
|
||||
}))
|
||||
|
||||
participant.pendingTracksLock.RLock()
|
||||
pti := participant.pendingTracks["video"]
|
||||
participant.pendingTracksLock.RUnlock()
|
||||
require.NotNil(t, pti)
|
||||
|
||||
layers := pti.trackInfos[0].Codecs[0].Layers
|
||||
require.Len(t, layers, 1)
|
||||
require.Equal(t, "", layers[0].Rid)
|
||||
require.EqualValues(t, 0, layers[0].SpatialLayer)
|
||||
}
|
||||
|
||||
func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
participant := newParticipantForTestWithOpts("123", &participantOpts{
|
||||
publisher: true,
|
||||
@@ -969,6 +1052,7 @@ type participantOpts struct {
|
||||
clientConf *livekit.ClientConfiguration
|
||||
clientInfo *livekit.ClientInfo
|
||||
migration bool
|
||||
oneShot bool
|
||||
}
|
||||
|
||||
func newParticipantForTestWithOpts(identity livekit.ParticipantIdentity, opts *participantOpts) *ParticipantImpl {
|
||||
@@ -1005,25 +1089,26 @@ func newParticipantForTestWithOpts(identity livekit.ParticipantIdentity, opts *p
|
||||
}
|
||||
sid := livekit.ParticipantID(guid.New(utils.ParticipantPrefix))
|
||||
p, _ := NewParticipant(ParticipantParams{
|
||||
SID: sid,
|
||||
Identity: identity,
|
||||
Config: rtcConf,
|
||||
Sink: &routingfakes.FakeMessageSink{},
|
||||
ProtocolVersion: opts.protocolVersion,
|
||||
SessionStartTime: time.Now(),
|
||||
PLIThrottleConfig: conf.RTC.PLIThrottle,
|
||||
Grants: grants,
|
||||
PublishEnabledCodecs: enabledCodecs,
|
||||
SubscribeEnabledCodecs: enabledCodecs,
|
||||
ClientConf: opts.clientConf,
|
||||
ClientInfo: ClientInfo{ClientInfo: opts.clientInfo},
|
||||
Logger: LoggerWithParticipant(logger.GetLogger(), identity, sid, false),
|
||||
Reporter: roomobs.NewNoopParticipantSessionReporter(),
|
||||
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
|
||||
VersionGenerator: utils.NewDefaultTimedVersionGenerator(),
|
||||
ParticipantListener: &typesfakes.FakeLocalParticipantListener{},
|
||||
ParticipantHelper: &typesfakes.FakeLocalParticipantHelper{},
|
||||
Migration: opts.migration,
|
||||
SID: sid,
|
||||
Identity: identity,
|
||||
Config: rtcConf,
|
||||
Sink: &routingfakes.FakeMessageSink{},
|
||||
ProtocolVersion: opts.protocolVersion,
|
||||
SessionStartTime: time.Now(),
|
||||
PLIThrottleConfig: conf.RTC.PLIThrottle,
|
||||
Grants: grants,
|
||||
PublishEnabledCodecs: enabledCodecs,
|
||||
SubscribeEnabledCodecs: enabledCodecs,
|
||||
ClientConf: opts.clientConf,
|
||||
ClientInfo: ClientInfo{ClientInfo: opts.clientInfo},
|
||||
Logger: LoggerWithParticipant(logger.GetLogger(), identity, sid, false),
|
||||
Reporter: roomobs.NewNoopParticipantSessionReporter(),
|
||||
TelemetryListener: &typesfakes.FakeParticipantTelemetryListener{},
|
||||
VersionGenerator: utils.NewDefaultTimedVersionGenerator(),
|
||||
ParticipantListener: &typesfakes.FakeLocalParticipantListener{},
|
||||
ParticipantHelper: &typesfakes.FakeLocalParticipantHelper{},
|
||||
Migration: opts.migration,
|
||||
UseOneShotSignallingMode: opts.oneShot,
|
||||
})
|
||||
p.isPublisher.Store(opts.publisher)
|
||||
p.updateState(livekit.ParticipantInfo_ACTIVE)
|
||||
|
||||
@@ -1709,6 +1709,9 @@ func (t *PCTransport) HandleRemoteDescription(sd webrtc.SessionDescription, remo
|
||||
return err
|
||||
}
|
||||
t.setRemoteOfferParsed(sd.Type, parsed)
|
||||
if sd.Type == webrtc.SDPTypeOffer {
|
||||
t.params.Handler.OnSetRemoteDescriptionOffer()
|
||||
}
|
||||
|
||||
rtxRepairs := nonSimulcastRTXRepairsFromSDP(parsed, t.params.Logger)
|
||||
if len(rtxRepairs) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user