support routing ULPFEC group definitions

This commit is contained in:
aler9
2023-08-22 20:01:44 +02:00
committed by Alessandro Ros
parent 1879843195
commit cab426e350
4 changed files with 140 additions and 6 deletions

View File

@@ -2,6 +2,7 @@ package description
import (
"fmt"
"strings"
psdp "github.com/pion/sdp/v3"
@@ -36,15 +37,21 @@ func hasMediaWithID(medias []*Media, id string) bool {
return false
}
// SessionFECGroup is a FEC group.
type SessionFECGroup []string
// Session is the description of a RTSP stream.
type Session struct {
// base URL of the stream (read only).
// Base URL of the stream (read only).
BaseURL *url.URL
// title of the stream (optional).
// Title of the stream (optional).
Title string
// available media streams.
// FEC groups (RFC5109).
FECGroups []SessionFECGroup
// Media streams.
Medias []*Media
}
@@ -87,6 +94,20 @@ func (d *Session) Unmarshal(ssd *sdp.SessionDescription) error {
return fmt.Errorf("media IDs sent partially")
}
for _, attr := range ssd.Attributes {
if attr.Key == "group" && strings.HasPrefix(attr.Value, "FEC ") {
group := SessionFECGroup(strings.Split(attr.Value[len("FEC "):], " "))
for _, id := range group {
if !hasMediaWithID(d.Medias, id) {
return fmt.Errorf("FEC group points to an invalid media ID: %v", id)
}
}
d.FECGroups = append(d.FECGroups, group)
}
}
return nil
}
@@ -132,5 +153,12 @@ func (d Session) Marshal(multicast bool) ([]byte, error) {
sout.MediaDescriptions[i] = media.Marshal()
}
for _, group := range d.FECGroups {
sout.Attributes = append(sout.Attributes, psdp.Attribute{
Key: "group",
Value: "FEC " + strings.Join(group, " "),
})
}
return sout.Marshal()
}

View File

@@ -629,6 +629,89 @@ var casesSession = []struct {
},
},
},
{
"ulpfec rfc5109",
"v=0\r\n" +
"o=adam 289083124 289083124 IN IP4 host.example.com\r\n" +
"s=ULP FEC Seminar\r\n" +
"t=0 0\r\n" +
"c=IN IP4 224.2.17.12/127\r\n" +
"a=group:FEC 1 2\r\n" +
"a=group:FEC 3 4\r\n" +
"m=audio 30000 RTP/AVP 0\r\n" +
"a=mid:1\r\n" +
"m=application 30002 RTP/AVP 100\r\n" +
"a=rtpmap:100 ulpfec/8000\r\n" +
"a=mid:2\r\n" +
"m=video 30004 RTP/AVP 31\r\n" +
"a=mid:3\r\n" +
"m=application 30004 RTP/AVP 101\r\n" +
"c=IN IP4 224.2.17.13/127\r\n" +
"a=rtpmap:101 ulpfec/8000\r\n" +
"a=mid:4\r\n",
"v=0\r\n" +
"o=- 0 0 IN IP4 127.0.0.1\r\n" +
"s=ULP FEC Seminar\r\n" +
"c=IN IP4 0.0.0.0\r\n" +
"t=0 0\r\n" +
"a=group:FEC 1 2\r\n" +
"a=group:FEC 3 4\r\n" +
"m=audio 0 RTP/AVP 0\r\n" +
"a=mid:1\r\n" +
"a=control\r\n" +
"a=rtpmap:0 PCMU/8000\r\n" +
"m=application 0 RTP/AVP 100\r\n" +
"a=mid:2\r\n" +
"a=control\r\n" +
"a=rtpmap:100 ulpfec/8000\r\n" +
"m=video 0 RTP/AVP 31\r\n" +
"a=mid:3\r\n" +
"a=control\r\n" +
"m=application 0 RTP/AVP 101\r\n" +
"a=mid:4\r\n" +
"a=control\r\n" +
"a=rtpmap:101 ulpfec/8000\r\n",
Session{
Title: "ULP FEC Seminar",
FECGroups: []SessionFECGroup{
{"1", "2"},
{"3", "4"},
},
Medias: []*Media{
{
ID: "1",
Type: MediaTypeAudio,
Formats: []format.Format{&format.G711{MULaw: true}},
},
{
ID: "2",
Type: MediaTypeApplication,
Formats: []format.Format{&format.Generic{
PayloadTyp: 100,
RTPMa: "ulpfec/8000",
ClockRat: 8000,
}},
},
{
ID: "3",
Type: MediaTypeVideo,
Formats: []format.Format{&format.Generic{
PayloadTyp: 31,
ClockRat: 90000,
}},
},
{
ID: "4",
Type: MediaTypeApplication,
Formats: []format.Format{&format.Generic{
PayloadTyp: 101,
RTPMa: "ulpfec/8000",
ClockRat: 8000,
}},
},
},
},
},
}
func TestSessionUnmarshal(t *testing.T) {
@@ -735,6 +818,25 @@ func FuzzSessionUnmarshalErrors(f *testing.F) {
"m=audio 0 RTP/AVP/TCP 0\r\n" +
"a=mid:2\r\n")
f.Add("v=0\r\n" +
"o=adam 289083124 289083124 IN IP4 host.example.com\r\n" +
"s=ULP FEC Seminar\r\n" +
"t=0 0\r\n" +
"c=IN IP4 224.2.17.12/127\r\n" +
"a=group:FEC 1 2\r\n" +
"a=group:FEC 3 4\r\n" +
"m=audio 30000 RTP/AVP 0\r\n" +
"a=mid:1\r\n" +
"m=application 30002 RTP/AVP 100\r\n" +
"a=rtpmap:100 ulpfec/8000\r\n" +
"a=mid:2\r\n" +
"m=video 30004 RTP/AVP 31\r\n" +
"a=mid:3\r\n" +
"m=application 30004 RTP/AVP 101\r\n" +
"c=IN IP4 224.2.17.13/127\r\n" +
"a=rtpmap:101 ulpfec/8000\r\n" +
"a=mid:4\r\n")
f.Fuzz(func(t *testing.T, enc string) {
var sd sdp.SessionDescription
err := sd.Unmarshal([]byte(enc))

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("v=0\ns=0\na=group:FEC 0")

View File

@@ -26,18 +26,20 @@ func getSessionID(header base.Header) string {
func serverSideDescription(d *description.Session, contentBase *url.URL) *description.Session {
out := &description.Session{
Title: d.Title,
Medias: make([]*description.Media, len(d.Medias)),
Title: d.Title,
FECGroups: d.FECGroups,
Medias: make([]*description.Media, len(d.Medias)),
}
for i, medi := range d.Medias {
mc := &description.Media{
Type: medi.Type,
ID: medi.ID,
// Direction: skipped for the moment
Formats: medi.Formats,
// we have to use trackID=number in order to support clients
// like the Grandstream GXV3500.
Control: "trackID=" + strconv.FormatInt(int64(i), 10),
Formats: medi.Formats,
}
// always use the absolute URL of the track as control attribute, in order