Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion jsep.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,34 @@ func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescr
return s
}

// addOrUpdateICEOption adds or updates the ice-options attribute with the given value.
func (s *SessionDescription) addOrUpdateICEOption(value string) *SessionDescription {
for i := range s.Attributes {
if s.Attributes[i].Key == AttrKeyICEOptions {
prefix := " "
if s.Attributes[i].Value == "" {
prefix = ""
}

s.Attributes[i].Value += prefix + value

return s
}
}

return s.WithValueAttribute(AttrKeyICEOptions, value)
}

// WithICETrickleAdvertised advertises ICE trickle support in the session description.
// See https://datatracker.ietf.org/doc/html/rfc9429#section-5.2.1
func (s *SessionDescription) WithICETrickleAdvertised() *SessionDescription {
return s.WithValueAttribute(AttrKeyICEOptions, "trickle")
return s.addOrUpdateICEOption("trickle")
}

// WithICERenomination advertises ICE renomination support in the session description.
// See https://datatracker.ietf.org/doc/html/draft-thatcher-ice-renomination-01#section-3
func (s *SessionDescription) WithICERenomination() *SessionDescription {
return s.addOrUpdateICEOption("renomination")
}

// WithFingerprint adds a fingerprint to the session description.
Expand Down
78 changes: 66 additions & 12 deletions jsep_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,34 +37,88 @@ func TestNewJSEPSessionDescription(t *testing.T) {
}

func TestSessionDescriptionAttributes(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)

t.Run("WithPropertyAttribute", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)
sd = sd.WithPropertyAttribute(AttrKeyRTCPMux)
assert.Len(t, sd.Attributes, 1)
assert.Equal(t, AttrKeyRTCPMux, sd.Attributes[0].Key)
})

t.Run("WithValueAttribute", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)
sd = sd.WithValueAttribute(AttrKeyMID, "video")
assert.Len(t, sd.Attributes, 2)
assert.Equal(t, AttrKeyMID, sd.Attributes[1].Key)
assert.Equal(t, "video", sd.Attributes[1].Value)
assert.Len(t, sd.Attributes, 1)
assert.Equal(t, AttrKeyMID, sd.Attributes[0].Key)
assert.Equal(t, "video", sd.Attributes[0].Value)
})

t.Run("WithICETrickleAdvertised", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)
sd = sd.WithICETrickleAdvertised()
assert.Len(t, sd.Attributes, 3)
assert.Equal(t, AttrKeyICEOptions, sd.Attributes[2].Key)
assert.Equal(t, "trickle", sd.Attributes[2].Value)
assert.Len(t, sd.Attributes, 1)
assert.Equal(t, AttrKeyICEOptions, sd.Attributes[0].Key)
assert.Equal(t, "trickle", sd.Attributes[0].Value)
})

t.Run("WithICERenomination", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)
sd = sd.WithICETrickleAdvertised().WithICERenomination()
assert.Len(t, sd.Attributes, 1)
assert.Equal(t, AttrKeyICEOptions, sd.Attributes[0].Key)
assert.Equal(t, "trickle renomination", sd.Attributes[0].Value)
})

t.Run("WithFingerprint", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)
sd = sd.WithFingerprint("sha-256", "test-fingerprint")
assert.Len(t, sd.Attributes, 4)
assert.Equal(t, "fingerprint", sd.Attributes[3].Key)
assert.Equal(t, "sha-256 test-fingerprint", sd.Attributes[3].Value)
assert.Len(t, sd.Attributes, 1)
assert.Equal(t, "fingerprint", sd.Attributes[0].Key)
assert.Equal(t, "sha-256 test-fingerprint", sd.Attributes[0].Value)
})
}

func TestSessionDescription_ICEOptions_Combined(t *testing.T) {
t.Run("WithICETrickleAdvertised and WithICERenominationAdvertised", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)

sd = sd.WithICETrickleAdvertised().WithICERenomination()

iceOptionsCount := 0
var iceOptionsValue string
for _, attr := range sd.Attributes {
if attr.Key == AttrKeyICEOptions {
iceOptionsCount++
iceOptionsValue = attr.Value
}
}

assert.Equal(t, 1, iceOptionsCount, "Should have exactly one ice-options attribute")
assert.Equal(t, "trickle renomination", iceOptionsValue, "Should combine both values with space")
})

t.Run("WithICERenominationAdvertised and WithICETrickleAdvertised (reverse order)", func(t *testing.T) {
sd, err := NewJSEPSessionDescription(false)
assert.NoError(t, err)

sd = sd.WithICERenomination().WithICETrickleAdvertised()

iceOptionsCount := 0
var iceOptionsValue string
for _, attr := range sd.Attributes {
if attr.Key == AttrKeyICEOptions {
iceOptionsCount++
iceOptionsValue = attr.Value
}
}

assert.Equal(t, 1, iceOptionsCount, "Should have exactly one ice-options attribute")
assert.Equal(t, "renomination trickle", iceOptionsValue, "Should combine both values with space")
})
}

Expand Down
Loading