Skip to content

Commit 37b5b4c

Browse files
committed
Add opt control transceiver re-use in recvonly
SetDisableTransceiverReuseInRecvonly controls if a transceiver is re-used when its current direction is `recvonly`. This is useful for the following scenario - Remote side sends `offer` with `sendonly` media section. - Local side creates transceiver in `SetRemoteDescription` and sets direction to `recvonly. - Local side calls `AddTrack`. - As the current direction is `recvonly`, the transceiver added above will be re-used. That will set the direction to `sendrecv` and the generated `answer` will have `sendrecv` for that media section. - That answer becomes incompatible as the offerer is using `sendonly`. Note that local transceiver will be in `recvonly` for both `sendrecv` and `sendonly` directions in the media section. If the `offer` did use `sendrecv`, it is possible to re-use that transceiver for sending. So, disabling re-use will prohibit re-use in the `sendrecv` case also and hence is slightly wasteful.
1 parent 8efd17e commit 37b5b4c

File tree

7 files changed

+356
-27
lines changed

7 files changed

+356
-27
lines changed

peerconnection.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,16 +1160,25 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
11601160
// if transceiver is create by remote sdp, set prefer codec same as remote peer
11611161
if codecs, err := codecsFromMediaDescription(media); err == nil {
11621162
filteredCodecs := []RTPCodecParameters{}
1163+
filteredCodecsPartial := []RTPCodecParameters{}
11631164
for _, codec := range codecs {
1164-
if c, matchType := codecParametersFuzzySearch(
1165+
c, matchType := codecParametersFuzzySearch(
11651166
codec,
11661167
pc.api.mediaEngine.getCodecsByKind(kind),
1167-
); matchType == codecMatchExact {
1168+
)
1169+
switch matchType {
1170+
case codecMatchExact:
11681171
// if codec match exact, use payloadtype register to mediaengine
11691172
codec.PayloadType = c.PayloadType
11701173
filteredCodecs = append(filteredCodecs, codec)
1174+
case codecMatchPartial:
1175+
codec.PayloadType = c.PayloadType
1176+
filteredCodecsPartial = append(filteredCodecsPartial, codec)
1177+
1178+
default:
11711179
}
11721180
}
1181+
filteredCodecs = append(filteredCodecs, filteredCodecsPartial...)
11731182
_ = transceiver.SetCodecPreferences(filteredCodecs)
11741183
}
11751184

@@ -2096,7 +2105,8 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) {
20962105
// But that will cause sdp inflate. So we only check currentDirection's current value,
20972106
// that's worked for all browsers.
20982107
if transceiver.kind == track.Kind() && transceiver.Sender() == nil &&
2099-
currentDirection != RTPTransceiverDirectionSendrecv && currentDirection != RTPTransceiverDirectionSendonly {
2108+
currentDirection != RTPTransceiverDirectionSendrecv && currentDirection != RTPTransceiverDirectionSendonly &&
2109+
(!pc.api.settingEngine.disableTransceiverReuseInRecvonly || currentDirection != RTPTransceiverDirectionRecvonly) {
21002110
sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
21012111
if err == nil {
21022112
err = transceiver.SetSender(sender, track)

peerconnection_go_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,10 @@ a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01
14221422
_, matchType = codecParametersFuzzySearch(codecOfTr2, codecs)
14231423
assert.Equal(t, codecMatchExact, matchType)
14241424
assert.EqualValues(t, 94, codecOfTr2.PayloadType)
1425+
codecPartialMatchOfTr2 := pc.GetTransceivers()[1].getCodecs()[2]
1426+
_, matchType = codecParametersFuzzySearch(codecPartialMatchOfTr2, codecs)
1427+
assert.Equal(t, codecMatchPartial, matchType)
1428+
assert.EqualValues(t, 98, codecPartialMatchOfTr2.PayloadType)
14251429
assert.NoError(t, pc.Close())
14261430
})
14271431

peerconnection_media_test.go

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -707,41 +707,95 @@ func TestAddTransceiverFromTrackSendRecv(t *testing.T) {
707707
}
708708

709709
func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
710-
pc, err := NewPeerConnection(Configuration{})
711-
assert.NoError(t, err)
710+
t.Run("reuse test", func(t *testing.T) {
711+
pc, err := NewPeerConnection(Configuration{})
712+
assert.NoError(t, err)
712713

713-
tr, err := pc.AddTransceiverFromKind(
714-
RTPCodecTypeVideo,
715-
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
716-
)
717-
assert.NoError(t, err)
714+
tr, err := pc.AddTransceiverFromKind(
715+
RTPCodecTypeVideo,
716+
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
717+
)
718+
assert.NoError(t, err)
718719

719-
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
720+
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
720721

721-
addTrack := func() (TrackLocal, *RTPSender) {
722-
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
722+
addTrack := func() (TrackLocal, *RTPSender) {
723+
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
724+
assert.NoError(t, err)
725+
726+
sender, err := pc.AddTrack(track)
727+
assert.NoError(t, err)
728+
729+
return track, sender
730+
}
731+
732+
track1, sender1 := addTrack()
733+
assert.Equal(t, 1, len(pc.GetTransceivers()))
734+
assert.Equal(t, sender1, tr.Sender())
735+
assert.Equal(t, track1, tr.Sender().Track())
736+
require.NoError(t, pc.RemoveTrack(sender1))
737+
738+
track2, _ := addTrack()
739+
assert.Equal(t, 1, len(pc.GetTransceivers()))
740+
assert.Equal(t, track2, tr.Sender().Track())
741+
742+
addTrack()
743+
assert.Equal(t, 2, len(pc.GetTransceivers()))
744+
745+
assert.NoError(t, pc.Close())
746+
})
747+
748+
t.Run("reuse disable test", func(t *testing.T) {
749+
se := SettingEngine{}
750+
se.SetDisableTransceiverReuseInRecvonly(true)
751+
pc, err := NewAPI(WithSettingEngine(se)).NewPeerConnection(Configuration{})
723752
assert.NoError(t, err)
724753

725-
sender, err := pc.AddTrack(track)
754+
tr, err := pc.AddTransceiverFromKind(
755+
RTPCodecTypeVideo,
756+
RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly},
757+
)
726758
assert.NoError(t, err)
727759

728-
return track, sender
729-
}
760+
assert.Equal(t, []*RTPTransceiver{tr}, pc.GetTransceivers())
730761

731-
track1, sender1 := addTrack()
732-
assert.Equal(t, 1, len(pc.GetTransceivers()))
733-
assert.Equal(t, sender1, tr.Sender())
734-
assert.Equal(t, track1, tr.Sender().Track())
735-
require.NoError(t, pc.RemoveTrack(sender1))
762+
addTrack := func() (TrackLocal, *RTPSender) {
763+
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar")
764+
assert.NoError(t, err)
736765

737-
track2, _ := addTrack()
738-
assert.Equal(t, 1, len(pc.GetTransceivers()))
739-
assert.Equal(t, track2, tr.Sender().Track())
766+
sender, err := pc.AddTrack(track)
767+
assert.NoError(t, err)
740768

741-
addTrack()
742-
assert.Equal(t, 2, len(pc.GetTransceivers()))
769+
return track, sender
770+
}
743771

744-
assert.NoError(t, pc.Close())
772+
// force direction to `recvonly` and ensure SettingEngine setting disables re-use
773+
tr.setCurrentDirection(RTPTransceiverDirectionRecvonly)
774+
addTrack()
775+
assert.Equal(t, 2, len(pc.GetTransceivers()))
776+
777+
// the newly added transceiver above will have a sender, so not re-usable
778+
_, sender := addTrack()
779+
assert.Equal(t, 3, len(pc.GetTransceivers()))
780+
781+
// remove last added track to make that transceiver re-usable
782+
require.NoError(t, pc.RemoveTrack(sender))
783+
784+
track, sender := addTrack()
785+
assert.Equal(t, 3, len(pc.GetTransceivers()))
786+
var matchedTransceiver *RTPTransceiver
787+
for _, tr := range pc.GetTransceivers() {
788+
if tr.Sender() == sender {
789+
matchedTransceiver = tr
790+
791+
break
792+
}
793+
}
794+
assert.NotNil(t, matchedTransceiver)
795+
assert.Equal(t, track, matchedTransceiver.Sender().Track())
796+
797+
assert.NoError(t, pc.Close())
798+
})
745799
}
746800

747801
func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {

rtpcodec.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package webrtc
55

66
import (
77
"fmt"
8+
"strconv"
89
"strings"
910

1011
"github.com/pion/webrtc/v4/internal/fmtp"
@@ -155,6 +156,38 @@ func findRTXPayloadType(needle PayloadType, haystack []RTPCodecParameters) Paylo
155156
return PayloadType(0)
156157
}
157158

159+
// Given needle CodecParameters, returns if needle is RTX and
160+
// if primary codec corresponding to that needle is in the haystack of codecs.
161+
func primaryPayloadTypeForRTXExists(needle RTPCodecParameters, haystack []RTPCodecParameters) (
162+
isRTX bool, primaryExists bool,
163+
) {
164+
if !strings.EqualFold(needle.MimeType, MimeTypeRTX) {
165+
return
166+
}
167+
168+
isRTX = true
169+
parsed := fmtp.Parse(needle.MimeType, needle.ClockRate, needle.Channels, needle.SDPFmtpLine)
170+
aptPayload, ok := parsed.Parameter("apt")
171+
if !ok {
172+
return
173+
}
174+
175+
primaryPayloadType, err := strconv.Atoi(aptPayload)
176+
if err != nil || primaryPayloadType < 0 || primaryPayloadType > 255 {
177+
return
178+
}
179+
180+
for _, c := range haystack {
181+
if c.PayloadType == PayloadType(primaryPayloadType) {
182+
primaryExists = true
183+
184+
return
185+
}
186+
}
187+
188+
return
189+
}
190+
158191
// For now, only FlexFEC is supported.
159192
func findFECPayloadType(haystack []RTPCodecParameters) PayloadType {
160193
for _, c := range haystack {

0 commit comments

Comments
 (0)