mirror of
https://github.com/aler9/gortsplib
synced 2025-10-10 01:20:14 +08:00

This is useful for clients that need to override the control field, which was
possible prior to 6d5bf0c1bb
via the Media field.
190 lines
3.9 KiB
Go
190 lines
3.9 KiB
Go
package gortsplib
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
psdp "github.com/pion/sdp/v3"
|
|
|
|
"github.com/aler9/gortsplib/pkg/base"
|
|
)
|
|
|
|
func trackGenericGetClockRate(formats []string, rtpmap string) (int, error) {
|
|
if len(formats) < 1 {
|
|
return 0, fmt.Errorf("no formats provided")
|
|
}
|
|
|
|
// 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":
|
|
return 8000, nil
|
|
|
|
case "6":
|
|
return 16000, nil
|
|
|
|
case "10", "11":
|
|
return 44100, nil
|
|
|
|
case "14", "25", "26", "28", "31", "32", "33", "34":
|
|
return 90000, nil
|
|
|
|
case "16":
|
|
return 11025, nil
|
|
|
|
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 == "" {
|
|
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], "/")
|
|
if len(tmp) != 2 && len(tmp) != 3 {
|
|
return 0, fmt.Errorf("invalid rtpmap (%v)", rtpmap)
|
|
}
|
|
|
|
v, err := strconv.ParseInt(tmp[1], 10, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return int(v), nil
|
|
}
|
|
|
|
// TrackGeneric is a generic track.
|
|
type TrackGeneric struct {
|
|
control string
|
|
clockRate int
|
|
media string
|
|
formats []string
|
|
rtpmap string
|
|
fmtp string
|
|
}
|
|
|
|
// NewTrackGeneric allocates a generic track.
|
|
func NewTrackGeneric(media string, formats []string, rtpmap string, fmtp string) (*TrackGeneric, error) {
|
|
clockRate, err := trackGenericGetClockRate(formats, rtpmap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get clock rate: %s", err)
|
|
}
|
|
|
|
return &TrackGeneric{
|
|
clockRate: clockRate,
|
|
media: media,
|
|
formats: formats,
|
|
rtpmap: rtpmap,
|
|
fmtp: fmtp,
|
|
}, nil
|
|
}
|
|
|
|
func newTrackGenericFromMediaDescription(md *psdp.MediaDescription) (*TrackGeneric, error) {
|
|
control := trackFindControl(md)
|
|
|
|
rtpmap := func() string {
|
|
for _, attr := range md.Attributes {
|
|
if attr.Key == "rtpmap" {
|
|
return attr.Value
|
|
}
|
|
}
|
|
return ""
|
|
}()
|
|
|
|
clockRate, 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{
|
|
control: control,
|
|
clockRate: clockRate,
|
|
media: md.MediaName.Media,
|
|
formats: md.MediaName.Formats,
|
|
rtpmap: rtpmap,
|
|
fmtp: fmtp,
|
|
}, nil
|
|
}
|
|
|
|
// ClockRate returns the track clock rate.
|
|
func (t *TrackGeneric) ClockRate() int {
|
|
return t.clockRate
|
|
}
|
|
|
|
func (t *TrackGeneric) clone() Track {
|
|
return &TrackGeneric{
|
|
control: t.control,
|
|
clockRate: t.clockRate,
|
|
media: t.media,
|
|
formats: t.formats,
|
|
rtpmap: t.rtpmap,
|
|
fmtp: t.fmtp,
|
|
}
|
|
}
|
|
|
|
// GetControl returns the track control.
|
|
func (t *TrackGeneric) GetControl() string {
|
|
return t.control
|
|
}
|
|
|
|
// SetControl set the track control.
|
|
func (t *TrackGeneric) SetControl(c string) {
|
|
t.control = c
|
|
}
|
|
|
|
func (t *TrackGeneric) url(contentBase *base.URL) (*base.URL, error) {
|
|
return trackURL(t, contentBase)
|
|
}
|
|
|
|
// MediaDescription returns the structured media information from the SDP
|
|
func (t *TrackGeneric) MediaDescription() *psdp.MediaDescription {
|
|
return &psdp.MediaDescription{
|
|
MediaName: psdp.MediaName{
|
|
Media: t.media,
|
|
Protos: []string{"RTP", "AVP"},
|
|
Formats: t.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
|
|
}(),
|
|
}
|
|
}
|