diff --git a/fmtp.go b/fmtp.go index f82dcb820a..9d4be5da64 100644 --- a/fmtp.go +++ b/fmtp.go @@ -21,7 +21,7 @@ func parseFmtp(line string) fmtp { return f } -// fmtpConsist checks that two FTMP parameters are not inconsistent. +// fmtpConsist checks that two FMTP parameters are not inconsistent. func fmtpConsist(a, b fmtp) bool { for k, v := range a { if vb, ok := b[k]; ok && !strings.EqualFold(vb, v) { diff --git a/peerconnection.go b/peerconnection.go index 1b7a684542..8f1648d1dc 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -31,6 +31,8 @@ type PeerConnection struct { statsID string mu sync.RWMutex + sdpOrigin sdp.Origin + // ops is an operations queue which will ensure the enqueued actions are // executed in order. It is used for asynchronously, but serially processing // remote and local descriptions @@ -672,6 +674,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription return SessionDescription{}, err } + updateSDPOrigin(&pc.sdpOrigin, d) sdpBytes, err := d.Marshal() if err != nil { return SessionDescription{}, err @@ -809,6 +812,7 @@ func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescripti return SessionDescription{}, err } + updateSDPOrigin(&pc.sdpOrigin, d) sdpBytes, err := d.Marshal() if err != nil { return SessionDescription{}, err diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index 75d73bda2a..1dd2d77026 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -1350,3 +1350,50 @@ func TestPeerConnection_TransceiverDirection(t *testing.T) { }) } } + +func TestPeerConnection_SessionID(t *testing.T) { + defer test.TimeOut(time.Second * 10).Stop() + defer test.CheckRoutines(t)() + + pcOffer, pcAnswer, err := newPair() + assert.NoError(t, err) + var offerSessionID uint64 + var offerSessionVersion uint64 + var answerSessionID uint64 + var answerSessionVersion uint64 + for i := 0; i < 10; i++ { + assert.NoError(t, signalPair(pcOffer, pcAnswer)) + offer := pcOffer.LocalDescription().parsed + sessionID := offer.Origin.SessionID + sessionVersion := offer.Origin.SessionVersion + if offerSessionID == 0 { + offerSessionID = sessionID + offerSessionVersion = sessionVersion + } else { + if offerSessionID != sessionID { + t.Errorf("offer[%v] session id mismatch: expected=%v, got=%v", i, offerSessionID, sessionID) + } + if offerSessionVersion+1 != sessionVersion { + t.Errorf("offer[%v] session version mismatch: expected=%v, got=%v", i, offerSessionVersion+1, sessionVersion) + } + offerSessionVersion++ + } + + answer := pcAnswer.LocalDescription().parsed + sessionID = answer.Origin.SessionID + sessionVersion = answer.Origin.SessionVersion + if answerSessionID == 0 { + answerSessionID = sessionID + answerSessionVersion = sessionVersion + } else { + if answerSessionID != sessionID { + t.Errorf("answer[%v] session id mismatch: expected=%v, got=%v", i, answerSessionID, sessionID) + } + if answerSessionVersion+1 != sessionVersion { + t.Errorf("answer[%v] session version mismatch: expected=%v, got=%v", i, answerSessionVersion+1, sessionVersion) + } + answerSessionVersion++ + } + } + closePairNow(t, pcOffer, pcAnswer) +} diff --git a/sdp.go b/sdp.go index 0345a488c3..af4c36461a 100644 --- a/sdp.go +++ b/sdp.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "sync/atomic" "github.com/pion/ice/v2" "github.com/pion/logging" @@ -271,8 +272,9 @@ func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGathe } return &SessionDescription{ - SDP: string(sdp), - Type: sessionDescription.Type, + SDP: string(sdp), + Type: sessionDescription.Type, + parsed: parsed, } } @@ -641,3 +643,22 @@ func rtpExtensionsFromMediaDescription(m *sdp.MediaDescription) (map[string]int, return out, nil } + +// updateSDPOrigin saves sdp.Origin in PeerConnection when creating 1st local SDP; +// for subsequent calling, it updates Origin for SessionDescription from saved one +// and increments session version by one. +// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-25#section-5.2.2 +// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-25#section-5.3.2 +func updateSDPOrigin(origin *sdp.Origin, d *sdp.SessionDescription) { + if atomic.CompareAndSwapUint64(&origin.SessionVersion, 0, d.Origin.SessionVersion) { // store + atomic.StoreUint64(&origin.SessionID, d.Origin.SessionID) + } else { // load + for { // awaiting for saving session id + d.Origin.SessionID = atomic.LoadUint64(&origin.SessionID) + if d.Origin.SessionID != 0 { + break + } + } + d.Origin.SessionVersion = atomic.AddUint64(&origin.SessionVersion, 1) + } +}