mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 11:32:08 +08:00
support routing ULPFEC group definitions
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
@@ -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))
|
||||
|
2
pkg/description/testdata/fuzz/FuzzSessionUnmarshalErrors/fb7e5db5b68fa760
vendored
Normal file
2
pkg/description/testdata/fuzz/FuzzSessionUnmarshalErrors/fb7e5db5b68fa760
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
string("v=0\ns=0\na=group:FEC 0")
|
@@ -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
|
||||
|
Reference in New Issue
Block a user