Add RTPTransceiver.Mid()

Refactor existing code for Unified Plan to ensure the same transceiver
will always be associated with the media description by mid.
This commit is contained in:
Jerko Steiner
2020-04-20 09:16:08 +02:00
parent 97c8f5290c
commit 8013159fff
4 changed files with 85 additions and 40 deletions

View File

@@ -1692,7 +1692,12 @@ func (pc *PeerConnection) generateUnmatchedSDP(useIdentity bool) (*sdp.SessionDe
mediaSections = append(mediaSections, mediaSection{id: "data", data: true})
} else {
for _, t := range pc.GetTransceivers() {
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), transceivers: []*RTPTransceiver{t}})
mid := strconv.Itoa(len(mediaSections))
err := t.setMid(mid)
if err != nil {
return nil, err
}
mediaSections = append(mediaSections, mediaSection{id: mid, transceivers: []*RTPTransceiver{t}})
}
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true})
@@ -1741,43 +1746,58 @@ func (pc *PeerConnection) generateMatchedSDP(useIdentity bool, includeUnmatched
continue
}
t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
mediaTransceivers := []*RTPTransceiver{t}
switch pc.configuration.SDPSemantics {
case SDPSemanticsUnifiedPlanWithFallback:
// If no match, process as unified-plan
if !detectedPlanB {
break
}
// If there was a match, fall through to plan-b
fallthrough
case SDPSemanticsPlanB:
sdpSemantics := pc.configuration.SDPSemantics
switch {
case sdpSemantics == SDPSemanticsPlanB || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback && detectedPlanB:
if !detectedPlanB {
return nil, &rtcerr.TypeError{Err: ErrIncorrectSDPSemantics}
}
// If we're responding to a plan-b offer, then we should try to fill up this
// media entry with all matching local transceivers
mediaTransceivers := []*RTPTransceiver{}
for {
// keep going until we can't get any more
t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
if t.Direction() == RTPTransceiverDirectionInactive {
if t == nil {
if len(mediaTransceivers) == 0 {
t = &RTPTransceiver{kind: kind}
t.setDirection(RTPTransceiverDirectionInactive)
mediaTransceivers = append(mediaTransceivers, t)
}
break
}
mediaTransceivers = append(mediaTransceivers, t)
}
case SDPSemanticsUnifiedPlan:
mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers})
case sdpSemantics == SDPSemanticsUnifiedPlan || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback:
if detectedPlanB {
return nil, &rtcerr.TypeError{Err: ErrIncorrectSDPSemantics}
}
t, localTransceivers = findByMid(midValue, localTransceivers)
if t == nil {
t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
}
if t == nil {
t = pc.newRTPTransceiver(nil, nil, RTPTransceiverDirectionInactive, kind)
}
if t.Mid() == "" {
_ = t.setMid(midValue)
}
mediaTransceivers := []*RTPTransceiver{t}
mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers})
}
mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers})
}
// If we are offering also include unmatched local transceivers
if !detectedPlanB && includeUnmatched {
for _, t := range localTransceivers {
mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), transceivers: []*RTPTransceiver{t}})
mid := strconv.Itoa(len(mediaSections))
err := t.setMid(mid)
if err != nil {
return nil, err
}
mediaSections = append(mediaSections, mediaSection{id: mid, transceivers: []*RTPTransceiver{t}})
}
}

View File

@@ -403,20 +403,20 @@ func TestPeerConnection_satisfyTypeAndDirection(t *testing.T) {
[]RTPCodecType{RTPCodecTypeVideo},
[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv},
[]*RTPTransceiver{createTransceiver(RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv)},
[]*RTPTransceiver{createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionInactive)},
[]*RTPTransceiver{nil},
},
{
"No local Transceivers, every remote should get an inactive",
"No local Transceivers, every remote should get nil",
[]RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeAudio, RTPCodecTypeVideo, RTPCodecTypeVideo},
[]RTPTransceiverDirection{RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly, RTPTransceiverDirectionInactive},
[]*RTPTransceiver{},
[]*RTPTransceiver{
createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionInactive),
createTransceiver(RTPCodecTypeAudio, RTPTransceiverDirectionInactive),
createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionInactive),
createTransceiver(RTPCodecTypeVideo, RTPTransceiverDirectionInactive),
nil,
nil,
nil,
nil,
},
},
{

View File

@@ -228,12 +228,12 @@ func TestPeerConnection_Media_Shutdown(t *testing.T) {
t.Fatal(err)
}
_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeAudio, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
_, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
if err != nil {
t.Fatal(err)
}
_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
_, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeAudio, RtpTransceiverInit{Direction: RTPTransceiverDirectionRecvonly})
if err != nil {
t.Fatal(err)
}
@@ -288,13 +288,13 @@ func TestPeerConnection_Media_Shutdown(t *testing.T) {
}
receivers := pc.GetReceivers()
if len(receivers) != 1 {
t.Errorf("Each PeerConnection should have one RTPReceivers, we have %d", len(receivers))
if len(receivers) != 2 {
t.Errorf("Each PeerConnection should have two RTPReceivers, we have %d", len(receivers))
}
transceivers := pc.GetTransceivers()
if len(transceivers) != 1 {
t.Errorf("Each PeerConnection should have one RTPTransceivers, we have %d", len(transceivers))
if len(transceivers) != 2 {
t.Errorf("Each PeerConnection should have two RTPTransceivers, we have %d", len(transceivers))
}
}

View File

@@ -9,6 +9,7 @@ import (
// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
type RTPTransceiver struct {
mid atomic.Value // string
sender atomic.Value // *RTPSender
receiver atomic.Value // *RTPReceiver
direction atomic.Value // RTPTransceiverDirection
@@ -39,6 +40,28 @@ func (t *RTPTransceiver) Receiver() *RTPReceiver {
return nil
}
// setMid sets the RTPTransceiver's mid. If it was already set, will return an error.
func (t *RTPTransceiver) setMid(mid string) error {
if currentMid := t.Mid(); currentMid != "" {
return fmt.Errorf("cannot change transceiver mid from: %s to %s", currentMid, mid)
}
t.mid.Store(mid)
return nil
}
// Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer.
func (t *RTPTransceiver) Mid() string {
if v := t.mid.Load(); v != nil {
return v.(string)
}
return ""
}
// Kind returns RTPTransceiver's kind.
func (t *RTPTransceiver) Kind() RTPCodecType {
return t.kind
}
// Direction returns the RTPTransceiver's current direction
func (t *RTPTransceiver) Direction() RTPTransceiverDirection {
return t.direction.Load().(RTPTransceiverDirection)
@@ -90,6 +113,16 @@ func (t *RTPTransceiver) setSendingTrack(track *Track) error {
return nil
}
func findByMid(mid string, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
for i, t := range localTransceivers {
if t.Mid() == mid {
return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
}
}
return nil, localTransceivers
}
// Given a direction+type pluck a transceiver from the passed list
// if no entry satisfies the requested type+direction return a inactive Transceiver
func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransceiverDirection, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
@@ -109,19 +142,11 @@ func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransce
for _, possibleDirection := range getPreferredDirections() {
for i := range localTransceivers {
t := localTransceivers[i]
if t.kind != remoteKind || possibleDirection != t.Direction() {
continue
if t.Mid() == "" && t.kind == remoteKind && possibleDirection == t.Direction() {
return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
}
return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
}
}
d := atomic.Value{}
d.Store(RTPTransceiverDirectionInactive)
return &RTPTransceiver{
kind: remoteKind,
direction: d,
}, localTransceivers
return nil, localTransceivers
}