support generic tracks with multiple formats (https://github.com/aler9/rtsp-simple-server/issues/1103)

This commit is contained in:
aler9
2022-10-28 19:10:54 +02:00
parent 394c2f0696
commit 6e6be34636
8 changed files with 243 additions and 157 deletions

View File

@@ -8,152 +8,166 @@ import (
psdp "github.com/pion/sdp/v3"
)
func trackGenericGetClockRate(formats []string, rtpmap string) (int, error) {
if len(formats) < 1 {
return 0, fmt.Errorf("no formats provided")
}
func findClockRate(track *TrackGeneric) (int, error) {
// RFC 4566
// When a list of
// payload type numbers is given, this implies that all of these
// payload formats MAY be used in the session, but the first of these
// formats SHOULD be used as the default format for the session
payload := track.Payloads[0]
// get clock rate from payload type
// https://en.wikipedia.org/wiki/RTP_payload_formats
switch formats[0] {
case "0", "1", "2", "3", "4", "5", "7", "8", "9", "12", "13", "15", "18":
switch payload.Type {
case 0, 1, 2, 3, 4, 5, 7, 8, 9, 12, 13, 15, 18:
return 8000, nil
case "6":
case 6:
return 16000, nil
case "10", "11":
case 10, 11:
return 44100, nil
case "14", "25", "26", "28", "31", "32", "33", "34":
case 14, 25, 26, 28, 31, 32, 33, 34:
return 90000, nil
case "16":
case 16:
return 11025, nil
case "17":
case 17:
return 22050, nil
}
// get clock rate from rtpmap
// https://tools.ietf.org/html/rfc4566
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
if rtpmap == "" {
if payload.RTPMap == "" {
return 0, fmt.Errorf("attribute 'rtpmap' not found")
}
tmp := strings.Split(rtpmap, " ")
if len(tmp) < 2 {
return 0, fmt.Errorf("invalid rtpmap (%v)", rtpmap)
}
tmp = strings.Split(tmp[1], "/")
tmp := strings.Split(payload.RTPMap, "/")
if len(tmp) != 2 && len(tmp) != 3 {
return 0, fmt.Errorf("invalid rtpmap (%v)", rtpmap)
return 0, fmt.Errorf("invalid rtpmap (%v)", payload.RTPMap)
}
v, err := strconv.ParseInt(tmp[1], 10, 64)
if err != nil {
return 0, err
}
return int(v), nil
}
// TrackGenericPayload is a payload of a TrackGeneric.
type TrackGenericPayload struct {
Type uint8
RTPMap string
FMTP string
}
// TrackGeneric is a generic track.
type TrackGeneric struct {
Media string
Formats []string
RTPMap string
FMTP string
Media string
Payloads []TrackGenericPayload
trackBase
clockRate int
}
func newTrackGenericFromMediaDescription(
control string,
md *psdp.MediaDescription,
) (*TrackGeneric, error) {
rtpmap := func() string {
for _, attr := range md.Attributes {
if attr.Key == "rtpmap" {
return attr.Value
}
}
return ""
}()
_, err := trackGenericGetClockRate(md.MediaName.Formats, rtpmap)
if err != nil {
return nil, fmt.Errorf("unable to get clock rate: %s", err)
}
fmtp := func() string {
for _, attr := range md.Attributes {
if attr.Key == "fmtp" {
return attr.Value
}
}
return ""
}()
return &TrackGeneric{
Media: md.MediaName.Media,
Formats: md.MediaName.Formats,
RTPMap: rtpmap,
FMTP: fmtp,
t := &TrackGeneric{
Media: md.MediaName.Media,
trackBase: trackBase{
control: control,
},
}, nil
}
for _, format := range md.MediaName.Formats {
tmp, err := strconv.ParseInt(format, 10, 8)
if err != nil {
return nil, err
}
payloadType := uint8(tmp)
t.Payloads = append(t.Payloads, TrackGenericPayload{
Type: payloadType,
RTPMap: getRtpmapAttribute(md.Attributes, payloadType),
FMTP: getFmtpAttribute(md.Attributes, payloadType),
})
}
err := t.Init()
if err != nil {
return nil, err
}
return t, nil
}
// Init initializes a TrackGeneric
func (t *TrackGeneric) Init() error {
var err error
t.clockRate, err = findClockRate(t)
if err != nil {
return fmt.Errorf("unable to get clock rate: %s", err)
}
return nil
}
// ClockRate returns the track clock rate.
func (t *TrackGeneric) ClockRate() int {
clockRate, _ := trackGenericGetClockRate(t.Formats, t.RTPMap)
return clockRate
return t.clockRate
}
func (t *TrackGeneric) clone() Track {
return &TrackGeneric{
Media: t.Media,
Formats: t.Formats,
RTPMap: t.RTPMap,
FMTP: t.FMTP,
Payloads: append([]TrackGenericPayload(nil), t.Payloads...),
trackBase: t.trackBase,
clockRate: t.clockRate,
}
}
// MediaDescription returns the track media description in SDP format.
func (t *TrackGeneric) MediaDescription() *psdp.MediaDescription {
formats := make([]string, len(t.Payloads))
for i, pl := range t.Payloads {
formats[i] = strconv.FormatInt(int64(pl.Type), 10)
}
var attributes []psdp.Attribute
for _, pl := range t.Payloads {
if pl.RTPMap != "" {
attributes = append(attributes, psdp.Attribute{
Key: "rtpmap",
Value: strconv.FormatInt(int64(pl.Type), 10) + " " + pl.RTPMap,
})
}
if pl.FMTP != "" {
attributes = append(attributes, psdp.Attribute{
Key: "fmtp",
Value: strconv.FormatInt(int64(pl.Type), 10) + " " + pl.FMTP,
})
}
}
attributes = append(attributes, psdp.Attribute{
Key: "control",
Value: t.control,
})
return &psdp.MediaDescription{
MediaName: psdp.MediaName{
Media: t.Media,
Protos: []string{"RTP", "AVP"},
Formats: t.Formats,
Formats: formats,
},
Attributes: func() []psdp.Attribute {
var ret []psdp.Attribute
if t.RTPMap != "" {
ret = append(ret, psdp.Attribute{
Key: "rtpmap",
Value: t.RTPMap,
})
}
if t.FMTP != "" {
ret = append(ret, psdp.Attribute{
Key: "fmtp",
Value: t.FMTP,
})
}
ret = append(ret, psdp.Attribute{
Key: "control",
Value: t.control,
})
return ret
}(),
Attributes: attributes,
}
}