mirror of
https://github.com/livekit/livekit.git
synced 2026-05-14 16:15:25 +00:00
Set publisher codec preferences after setting remote description (#3913)
* Set publisher codec preferences after setting remote description Munging SDP prior to setting remote description was becoming problematic in single peer connection mode. In that mode, it is possible that a subscribe track m-section is added which sets the fmtp of H.265 to a value that is different from when that client publishes. That gets locked in as negotiated codecs when pion processes remote description. Later when the client publishes H.265, the H.265 does only partial match. So, if we munge offer and send it to SetRemoteDescription, the H.265 does only a partial match due to different fmtp line and that gets put at the end of the list. So, the answer does not enforce the preferred codec. Changing pion to put partial match up front is more risky given other projects. So, switch codec preferences to after remote description is set and directly operate on transceiver which is a better place to make these changes without munging SDP. This fixes the case of - firefox joins first - Chrome preferring H.265 joining next. This causes a subscribe track m-section (for firefox's tracks) to be created first. So, the preferred codec munging was not working. Works after this change. * clean up * mage generate * test * clean up
This commit is contained in:
+19
-16
@@ -1259,22 +1259,6 @@ func (p *ParticipantImpl) HandleOffer(sd *livekit.SessionDescription) error {
|
||||
}
|
||||
}
|
||||
|
||||
unmatchAudios, unmatchVideos := p.populateSdpCid(parsedOffer)
|
||||
parsedOffer = p.setCodecPreferencesForPublisher(parsedOffer, unmatchAudios, unmatchVideos)
|
||||
p.updateRidsFromSDP(parsedOffer, unmatchVideos)
|
||||
|
||||
// put together munged offer after setting codec preferences
|
||||
bytes, err := parsedOffer.Marshal()
|
||||
if err != nil {
|
||||
lgr.Errorw("failed to marshal offer", err, "parsedOffer", parsedOffer)
|
||||
return err
|
||||
}
|
||||
|
||||
offer = webrtc.SessionDescription{
|
||||
Type: offer.Type,
|
||||
SDP: string(bytes),
|
||||
}
|
||||
|
||||
err = p.TransportManager.HandleOffer(offer, offerId, p.MigrateState() == types.MigrateStateInit)
|
||||
if err != nil {
|
||||
lgr.Warnw("could not handle offer", err, "mungedOffer", offer)
|
||||
@@ -1291,6 +1275,21 @@ func (p *ParticipantImpl) HandleOffer(sd *livekit.SessionDescription) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) onPublisherSetRemoteDescription() {
|
||||
offer := p.TransportManager.LastPublisherOfferPending()
|
||||
parsedOffer, err := offer.Unmarshal()
|
||||
if err != nil {
|
||||
p.pubLogger.Warnw("could not parse offer", err)
|
||||
return
|
||||
}
|
||||
|
||||
// set publish codec preferences after remote description is set
|
||||
// and required transceivers are created
|
||||
unmatchAudios, unmatchVideos := p.populateSdpCid(parsedOffer)
|
||||
p.setCodecPreferencesForPublisher(parsedOffer, unmatchAudios, unmatchVideos)
|
||||
p.updateRidsFromSDP(parsedOffer, unmatchVideos)
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) onPublisherAnswer(answer webrtc.SessionDescription, answerId uint32) error {
|
||||
if p.IsClosed() || p.IsDisconnected() {
|
||||
return nil
|
||||
@@ -1921,6 +1920,10 @@ type PublisherTransportHandler struct {
|
||||
AnyTransportHandler
|
||||
}
|
||||
|
||||
func (h PublisherTransportHandler) OnSetRemoteDescriptionOffer() {
|
||||
h.p.onPublisherSetRemoteDescription()
|
||||
}
|
||||
|
||||
func (h PublisherTransportHandler) OnAnswer(sd webrtc.SessionDescription, answerId uint32) error {
|
||||
return h.p.onPublisherAnswer(sd, answerId)
|
||||
}
|
||||
|
||||
@@ -625,8 +625,12 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
participant.SetMigrateState(types.MigrateStateComplete)
|
||||
|
||||
me := webrtc.MediaEngine{}
|
||||
me.RegisterDefaultCodecs()
|
||||
require.NoError(t, me.RegisterCodec(RedCodecParameters, webrtc.RTPCodecTypeAudio))
|
||||
opusCodecParameters := OpusCodecParameters
|
||||
opusCodecParameters.RTPCodecCapability.RTCPFeedback = []webrtc.RTCPFeedback{{Type: webrtc.TypeRTCPFBNACK}}
|
||||
require.NoError(t, me.RegisterCodec(opusCodecParameters, webrtc.RTPCodecTypeAudio))
|
||||
redCodecParameters := 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{})
|
||||
@@ -641,9 +645,17 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
DisableRed: disableRed,
|
||||
Cid: trackCid,
|
||||
})
|
||||
track, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: "audio/opus"}, trackCid, trackCid)
|
||||
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 {
|
||||
@@ -683,7 +695,14 @@ func TestPreferAudioCodecForRed(t *testing.T) {
|
||||
Id: offerId,
|
||||
})
|
||||
|
||||
require.Eventually(t, func() bool { return answerReceived.Load() && answerIdReceived.Load() == offerId }, 5*time.Second, 10*time.Millisecond)
|
||||
require.Eventually(
|
||||
t,
|
||||
func() bool {
|
||||
return answerReceived.Load() && answerIdReceived.Load() == offerId
|
||||
},
|
||||
5*time.Second,
|
||||
10*time.Millisecond,
|
||||
)
|
||||
|
||||
var redPreferred bool
|
||||
parsed, err := answer.Unmarshal()
|
||||
|
||||
+70
-124
@@ -163,7 +163,7 @@ func (p *ParticipantImpl) populateSdpCid(parsedOffer *sdp.SessionDescription) ([
|
||||
|
||||
unmatchVideos, err := p.TransportManager.GetUnmatchMediaForOffer(parsedOffer, "video")
|
||||
if err != nil {
|
||||
p.pubLogger.Warnw("could not get unmatch audios", err)
|
||||
p.pubLogger.Warnw("could not get unmatch videos", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -176,108 +176,41 @@ func (p *ParticipantImpl) setCodecPreferencesForPublisher(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatchAudios []*sdp.MediaDescription,
|
||||
unmatchVideos []*sdp.MediaDescription,
|
||||
) *sdp.SessionDescription {
|
||||
parsedOffer, unprocessedUnmatchAudios := p.setCodecPreferencesForPublisherMedia(
|
||||
) {
|
||||
unprocessedUnmatchAudios := p.setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer,
|
||||
unmatchAudios,
|
||||
livekit.TrackType_AUDIO,
|
||||
)
|
||||
parsedOffer = p.setCodecPreferencesOpusRedForPublisher(parsedOffer, unprocessedUnmatchAudios)
|
||||
parsedOffer, _ = p.setCodecPreferencesForPublisherMedia(
|
||||
p.setCodecPreferencesOpusRedForPublisher(parsedOffer, unprocessedUnmatchAudios)
|
||||
_ = p.setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer,
|
||||
unmatchVideos,
|
||||
livekit.TrackType_VIDEO,
|
||||
)
|
||||
return parsedOffer
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesOpusRedForPublisher(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatchAudios []*sdp.MediaDescription,
|
||||
) *sdp.SessionDescription {
|
||||
for _, unmatchAudio := range unmatchAudios {
|
||||
streamID, ok := lksdp.ExtractStreamID(unmatchAudio)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p.pendingTracksLock.RLock()
|
||||
_, ti, _, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
p.pendingTracksLock.RUnlock()
|
||||
if ti == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(unmatchAudio)
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw(
|
||||
"extract codecs from media section failed", err,
|
||||
"media", unmatchAudio,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
var opusPayload uint8
|
||||
for _, codec := range codecs {
|
||||
if mime.IsMimeTypeCodecStringOpus(codec.Name) {
|
||||
opusPayload = codec.PayloadType
|
||||
break
|
||||
}
|
||||
}
|
||||
if opusPayload == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// if RED is disabled for this track, don't prefer RED codec in offer
|
||||
var preferredCodecs, leftCodecs []string
|
||||
for _, codec := range codecs {
|
||||
// codec contain opus/red
|
||||
if !ti.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
|
||||
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...)
|
||||
}
|
||||
|
||||
return parsedOffer
|
||||
}
|
||||
|
||||
func (p *ParticipantImpl) setCodecPreferencesForPublisherMedia(
|
||||
parsedOffer *sdp.SessionDescription,
|
||||
unmatches []*sdp.MediaDescription,
|
||||
trackType livekit.TrackType,
|
||||
) (*sdp.SessionDescription, []*sdp.MediaDescription) {
|
||||
) []*sdp.MediaDescription {
|
||||
unprocessed := make([]*sdp.MediaDescription, 0, len(unmatches))
|
||||
// unmatched media is pending for publish, set codec preference
|
||||
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)
|
||||
@@ -313,61 +246,74 @@ func (p *ParticipantImpl) setCodecPreferencesForPublisherMedia(
|
||||
continue
|
||||
}
|
||||
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(unmatch)
|
||||
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()
|
||||
_, ti, _, _, _ := p.getPendingTrack(streamID, livekit.TrackType_AUDIO, false)
|
||||
p.pendingTracksLock.RUnlock()
|
||||
if ti == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codecs, err := lksdp.CodecsFromMediaDescription(unmatchAudio)
|
||||
if err != nil {
|
||||
p.pubLogger.Errorw(
|
||||
"extract codecs from media section failed", err,
|
||||
"media", unmatch,
|
||||
"media", unmatchAudio,
|
||||
"parsedOffer", parsedOffer,
|
||||
)
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
continue
|
||||
}
|
||||
|
||||
var codecIdx int
|
||||
var preferredCodecs, leftCodecs []string
|
||||
for idx, c := range codecs {
|
||||
if mime.GetMimeTypeCodec(mimeType) == mime.NormalizeMimeTypeCodec(c.Name) {
|
||||
preferredCodecs = append(preferredCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
|
||||
codecIdx = idx
|
||||
} else {
|
||||
leftCodecs = append(leftCodecs, strconv.FormatInt(int64(c.PayloadType), 10))
|
||||
var opusPayload uint8
|
||||
for _, codec := range codecs {
|
||||
if mime.IsMimeTypeCodecStringOpus(codec.Name) {
|
||||
opusPayload = codec.PayloadType
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// could not find preferred mime in the offer
|
||||
if len(preferredCodecs) == 0 {
|
||||
unprocessed = append(unprocessed, unmatch)
|
||||
if opusPayload == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
unmatch.MediaName.Formats = append(unmatch.MediaName.Formats[:0], preferredCodecs...)
|
||||
if trackType == livekit.TrackType_VIDEO {
|
||||
// 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() {
|
||||
unmatch.MediaName.Formats = append(unmatch.MediaName.Formats, leftCodecs...)
|
||||
// if RED is disabled for this track, don't prefer RED codec in offer
|
||||
for _, codec := range codecs {
|
||||
// codec contain opus/red
|
||||
if !ti.DisableRed &&
|
||||
mime.IsMimeTypeCodecStringRED(codec.Name) &&
|
||||
strings.Contains(codec.Fmtp, strconv.FormatInt(int64(opusPayload), 10)) {
|
||||
configureReceiverCodecs(transceiver, "audio/red", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// ensure nack enabled for audio in publisher offer
|
||||
var nackFound bool
|
||||
for _, attr := range unmatch.Attributes {
|
||||
if attr.Key == "rtcp-fb" && strings.Contains(attr.Value, fmt.Sprintf("%d nack", codecs[codecIdx].PayloadType)) {
|
||||
nackFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !nackFound {
|
||||
unmatch.Attributes = append(unmatch.Attributes, sdp.Attribute{
|
||||
Key: "rtcp-fb",
|
||||
Value: fmt.Sprintf("%d nack", codecs[codecIdx].PayloadType),
|
||||
})
|
||||
}
|
||||
|
||||
unmatch.MediaName.Formats = append(unmatch.MediaName.Formats, leftCodecs...)
|
||||
}
|
||||
}
|
||||
|
||||
return parsedOffer, unprocessed
|
||||
}
|
||||
|
||||
// configure publisher answer for audio track's dtx and stereo settings
|
||||
@@ -419,7 +365,7 @@ func (p *ParticipantImpl) configurePublisherAnswer(answer webrtc.SessionDescript
|
||||
}
|
||||
}
|
||||
|
||||
if ti == nil || (ti.DisableDtx && !ti.Stereo) {
|
||||
if ti == nil || (ti.DisableDtx && !slices.Contains(ti.AudioFeatures, livekit.AudioTrackFeature_TF_STEREO)) {
|
||||
// no need to configure
|
||||
continue
|
||||
}
|
||||
|
||||
+81
-9
@@ -956,9 +956,9 @@ func (t *PCTransport) AddTrack(
|
||||
return
|
||||
}
|
||||
|
||||
configureTransceiverCodecs(transceiver, enabledCodecs, rtcpFeedbackConfig, !t.params.IsOfferer)
|
||||
configureSenderCodecs(transceiver, enabledCodecs, rtcpFeedbackConfig, !t.params.IsOfferer)
|
||||
if trackLocal.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
configureAudioTransceiver(transceiver, params.Stereo, !params.Red || !t.params.ClientInfo.SupportsAudioRED())
|
||||
configureSenderAudio(transceiver, params.Stereo, !params.Red || !t.params.ClientInfo.SupportsAudioRED())
|
||||
}
|
||||
t.adjustNumOutstandingMedia(transceiver)
|
||||
return
|
||||
@@ -981,9 +981,9 @@ func (t *PCTransport) AddTransceiverFromTrack(
|
||||
return
|
||||
}
|
||||
|
||||
configureTransceiverCodecs(transceiver, enabledCodecs, rtcpFeedbackConfig, !t.params.IsOfferer)
|
||||
configureSenderCodecs(transceiver, enabledCodecs, rtcpFeedbackConfig, !t.params.IsOfferer)
|
||||
if trackLocal.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
configureAudioTransceiver(transceiver, params.Stereo, !params.Red || !t.params.ClientInfo.SupportsAudioRED())
|
||||
configureSenderAudio(transceiver, params.Stereo, !params.Red || !t.params.ClientInfo.SupportsAudioRED())
|
||||
}
|
||||
t.adjustNumOutstandingMedia(transceiver)
|
||||
return
|
||||
@@ -1020,6 +1020,16 @@ func (t *PCTransport) CurrentRemoteDescription() *webrtc.SessionDescription {
|
||||
return &rd
|
||||
}
|
||||
|
||||
func (t *PCTransport) PendingRemoteDescription() *webrtc.SessionDescription {
|
||||
prd := t.pc.PendingRemoteDescription()
|
||||
if prd == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rd := *prd
|
||||
return &rd
|
||||
}
|
||||
|
||||
func (t *PCTransport) GetMid(rtpReceiver *webrtc.RTPReceiver) string {
|
||||
for _, tr := range t.pc.GetTransceivers() {
|
||||
if tr.Receiver() == rtpReceiver {
|
||||
@@ -1030,6 +1040,16 @@ func (t *PCTransport) GetMid(rtpReceiver *webrtc.RTPReceiver) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *PCTransport) GetRTPTransceiver(mid string) *webrtc.RTPTransceiver {
|
||||
for _, tr := range t.pc.GetTransceivers() {
|
||||
if tr.Mid() == mid {
|
||||
return tr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PCTransport) GetRTPReceiver(mid string) *webrtc.RTPReceiver {
|
||||
for _, tr := range t.pc.GetTransceivers() {
|
||||
if tr.Mid() == mid {
|
||||
@@ -2124,6 +2144,7 @@ func (t *PCTransport) handleICEGatheringCompleteAnswerer() error {
|
||||
if err := t.setRemoteDescription(offer); err != nil {
|
||||
return err
|
||||
}
|
||||
t.params.Handler.OnSetRemoteDescriptionOffer()
|
||||
|
||||
return t.createAndSendAnswer()
|
||||
}
|
||||
@@ -2643,6 +2664,8 @@ func (t *PCTransport) handleRemoteOfferReceived(sd *webrtc.SessionDescription, o
|
||||
if err := t.setRemoteDescription(*sd); err != nil {
|
||||
return err
|
||||
}
|
||||
t.params.Handler.OnSetRemoteDescriptionOffer()
|
||||
|
||||
rtxRepairs := nonSimulcastRTXRepairsFromSDP(parsed, t.params.Logger)
|
||||
if len(rtxRepairs) > 0 {
|
||||
t.params.Logger.Debugw("rtx pairs found from sdp", "ssrcs", rtxRepairs)
|
||||
@@ -2805,7 +2828,7 @@ func (t *PCTransport) outputAndClearICEStats() {
|
||||
// configure subscriber transceiver for audio stereo and nack
|
||||
// pion doesn't support per transciver codec configuration, so the nack of this session will be disabled
|
||||
// forever once it is first disabled by a transceiver.
|
||||
func configureAudioTransceiver(tr *webrtc.RTPTransceiver, stereo bool, nack bool) {
|
||||
func configureSenderAudio(tr *webrtc.RTPTransceiver, stereo bool, nack bool) {
|
||||
sender := tr.Sender()
|
||||
if sender == nil {
|
||||
return
|
||||
@@ -2834,13 +2857,14 @@ func configureAudioTransceiver(tr *webrtc.RTPTransceiver, stereo bool, nack bool
|
||||
tr.SetCodecPreferences(configCodecs)
|
||||
}
|
||||
|
||||
// In single peer connection mode, set up enebled codecs,
|
||||
// the config provides config of direction, for publisher peer connection, it is publish enabled codecs
|
||||
// and for subscriber peer connection, it is subscribe enabled codecs.
|
||||
// In single peer connection mode, set up enebled codecs for sender.
|
||||
// The config provides config of direction.
|
||||
// For publisher peer connection those are publish enabled codecs
|
||||
// and for subscriber peer connection those are subscribe enabled codecs.
|
||||
//
|
||||
// But, in single peer connection mode, if setting up a transceiver where the media is
|
||||
// flowing in the other direction, the other direction codec config needs to be set.
|
||||
func configureTransceiverCodecs(
|
||||
func configureSenderCodecs(
|
||||
tr *webrtc.RTPTransceiver,
|
||||
enabledCodecs []*livekit.Codec,
|
||||
rtcpFeedbackConfig RTCPFeedbackConfig,
|
||||
@@ -2864,6 +2888,54 @@ func configureTransceiverCodecs(
|
||||
tr.SetCodecPreferences(filteredCodecs)
|
||||
}
|
||||
|
||||
func configureReceiverCodecs(
|
||||
tr *webrtc.RTPTransceiver,
|
||||
preferredMimeType string,
|
||||
compliesWithCodecOrderInSDPAnswer bool,
|
||||
) {
|
||||
receiver := tr.Receiver()
|
||||
if receiver == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var preferredCodecs, leftCodecs []webrtc.RTPCodecParameters
|
||||
for _, c := range receiver.GetParameters().Codecs {
|
||||
if tr.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
nackFound := false
|
||||
for _, fb := range c.RTCPFeedback {
|
||||
if fb.Type == webrtc.TypeRTCPFBNACK {
|
||||
nackFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !nackFound {
|
||||
c.RTCPFeedback = append(c.RTCPFeedback, webrtc.RTCPFeedback{Type: webrtc.TypeRTCPFBNACK})
|
||||
}
|
||||
}
|
||||
|
||||
if mime.GetMimeTypeCodec(preferredMimeType) == mime.GetMimeTypeCodec(c.RTPCodecCapability.MimeType) {
|
||||
preferredCodecs = append(preferredCodecs, c)
|
||||
} else {
|
||||
leftCodecs = append(leftCodecs, c)
|
||||
}
|
||||
}
|
||||
if len(preferredCodecs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
reorderedCodecs := append([]webrtc.RTPCodecParameters{}, preferredCodecs...)
|
||||
if tr.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
// if the client don't comply with codec order in SDP answer, only keep preferred codecs to force client to use it
|
||||
if compliesWithCodecOrderInSDPAnswer {
|
||||
reorderedCodecs = append(reorderedCodecs, leftCodecs...)
|
||||
}
|
||||
} else {
|
||||
reorderedCodecs = append(reorderedCodecs, leftCodecs...)
|
||||
}
|
||||
tr.SetCodecPreferences(reorderedCodecs)
|
||||
}
|
||||
|
||||
func nonSimulcastRTXRepairsFromSDP(s *sdp.SessionDescription, logger logger.Logger) map[uint32]uint32 {
|
||||
rtxRepairFlows := map[uint32]uint32{}
|
||||
for _, media := range s.MediaDescriptions {
|
||||
|
||||
@@ -43,6 +43,7 @@ type Handler interface {
|
||||
OnDataMessageUnlabeled(data []byte)
|
||||
OnDataSendError(err error)
|
||||
OnOffer(sd webrtc.SessionDescription, offerId uint32) error
|
||||
OnSetRemoteDescriptionOffer()
|
||||
OnAnswer(sd webrtc.SessionDescription, answerId uint32) error
|
||||
OnNegotiationStateChanged(state NegotiationState)
|
||||
OnNegotiationFailed()
|
||||
@@ -65,6 +66,7 @@ func (h UnimplementedHandler) OnDataSendError(err error)
|
||||
func (h UnimplementedHandler) OnOffer(sd webrtc.SessionDescription, offerId uint32) error {
|
||||
return ErrNoOfferHandler
|
||||
}
|
||||
func (h UnimplementedHandler) OnSetRemoteDescriptionOffer() {}
|
||||
func (h UnimplementedHandler) OnAnswer(sd webrtc.SessionDescription, answerId uint32) error {
|
||||
return ErrNoAnswerHandler
|
||||
}
|
||||
|
||||
@@ -87,6 +87,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 {
|
||||
@@ -550,6 +554,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)]
|
||||
|
||||
@@ -617,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) {
|
||||
|
||||
@@ -224,6 +224,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)
|
||||
}
|
||||
@@ -451,6 +455,10 @@ func (t *TransportManager) LastPublisherOffer() *webrtc.SessionDescription {
|
||||
return t.publisher.CurrentRemoteDescription()
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -542,8 +542,8 @@ func (d *DownTrack) Bind(t webrtc.TrackLocalContext) (webrtc.RTPCodecParameters,
|
||||
d.payloadTypeRTX.Store(uint32(utils.FindRTXPayloadType(codec.PayloadType, d.negotiatedCodecParameters)))
|
||||
logFields = append(
|
||||
logFields,
|
||||
"payloadType", d.payloadType,
|
||||
"payloadTypeRTX", d.payloadTypeRTX,
|
||||
"payloadType", d.payloadType.Load(),
|
||||
"payloadTypeRTX", d.payloadTypeRTX.Load(),
|
||||
"codecParameters", d.negotiatedCodecParameters,
|
||||
)
|
||||
d.params.Logger.Debugw("DownTrack.Bind", logFields...)
|
||||
|
||||
Reference in New Issue
Block a user