diff --git a/track.go b/track.go index db233fb7..d2928293 100644 --- a/track.go +++ b/track.go @@ -18,13 +18,22 @@ type Track interface { GetControl() string // SetControl sets the track control. SetControl(string) - // MediaDescription returns the media description in SDP format. + // MediaDescription returns the track media description in SDP format. MediaDescription() *psdp.MediaDescription clone() Track url(*base.URL) (*base.URL, error) } func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) { + control := func() string { + for _, attr := range md.Attributes { + if attr.Key == "control" { + return attr.Value + } + } + return "" + }() + rtpmapPart1, payloadType := func() (string, uint8) { rtpmap, ok := md.Attribute("rtpmap") if !ok { @@ -49,35 +58,40 @@ func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) { switch { case md.MediaName.Media == "video": if rtpmapPart1 == "H264/90000" { - return newTrackH264FromMediaDescription(payloadType, md) + return newTrackH264FromMediaDescription(control, payloadType, md) } case md.MediaName.Media == "audio": switch { case len(md.MediaName.Formats) == 1 && md.MediaName.Formats[0] == "0": - return newTrackPCMUFromMediaDescription(rtpmapPart1, md) + return newTrackPCMUFromMediaDescription(control, rtpmapPart1, md) case strings.HasPrefix(strings.ToLower(rtpmapPart1), "mpeg4-generic/"): - return newTrackAACFromMediaDescription(payloadType, md) + return newTrackAACFromMediaDescription(control, payloadType, md) case strings.HasPrefix(rtpmapPart1, "opus/"): - return newTrackOpusFromMediaDescription(payloadType, rtpmapPart1, md) + return newTrackOpusFromMediaDescription(control, payloadType, rtpmapPart1, md) } } - return newTrackGenericFromMediaDescription(md) + return newTrackGenericFromMediaDescription(control, md) } -func trackFindControl(md *psdp.MediaDescription) string { - for _, attr := range md.Attributes { - if attr.Key == "control" { - return attr.Value - } - } - return "" +type trackBase struct { + control string } -func trackURL(t Track, contentBase *base.URL) (*base.URL, error) { +// GetControl gets the track control. +func (t *trackBase) GetControl() string { + return t.control +} + +// SetControl sets the track control. +func (t *trackBase) SetControl(c string) { + t.control = c +} + +func (t *trackBase) url(contentBase *base.URL) (*base.URL, error) { if contentBase == nil { return nil, fmt.Errorf("Content-Base header not provided") } diff --git a/track_aac.go b/track_aac.go index 9e9acc20..7bbd00c9 100644 --- a/track_aac.go +++ b/track_aac.go @@ -9,12 +9,11 @@ import ( psdp "github.com/pion/sdp/v3" "github.com/aler9/gortsplib/pkg/aac" - "github.com/aler9/gortsplib/pkg/base" ) // TrackAAC is an AAC track. type TrackAAC struct { - control string + trackBase payloadType uint8 typ int sampleRate int @@ -47,10 +46,9 @@ func NewTrackAAC(payloadType uint8, typ int, sampleRate int, } func newTrackAACFromMediaDescription( + control string, payloadType uint8, md *psdp.MediaDescription) (*TrackAAC, error) { - control := trackFindControl(md) - v, ok := md.Attribute("fmtp") if !ok { return nil, fmt.Errorf("fmtp attribute is missing") @@ -89,7 +87,9 @@ func newTrackAACFromMediaDescription( enc, _ = mpegConf.Encode() return &TrackAAC{ - control: control, + trackBase: trackBase{ + control: control, + }, payloadType: payloadType, typ: int(mpegConf.Type), sampleRate: mpegConf.SampleRate, @@ -125,7 +125,7 @@ func (t *TrackAAC) AOTSpecificConfig() []byte { func (t *TrackAAC) clone() Track { return &TrackAAC{ - control: t.control, + trackBase: t.trackBase, payloadType: t.payloadType, typ: t.typ, sampleRate: t.sampleRate, @@ -135,21 +135,7 @@ func (t *TrackAAC) clone() Track { } } -// GetControl gets the track control. -func (t *TrackAAC) GetControl() string { - return t.control -} - -// SetControl sets the track control. -func (t *TrackAAC) SetControl(c string) { - t.control = c -} - -func (t *TrackAAC) url(contentBase *base.URL) (*base.URL, error) { - return trackURL(t, contentBase) -} - -// MediaDescription returns the media description in SDP format. +// MediaDescription returns the track media description in SDP format. func (t *TrackAAC) MediaDescription() *psdp.MediaDescription { typ := strconv.FormatInt(int64(t.payloadType), 10) diff --git a/track_generic.go b/track_generic.go index 39dd9513..ab731b01 100644 --- a/track_generic.go +++ b/track_generic.go @@ -6,8 +6,6 @@ import ( "strings" psdp "github.com/pion/sdp/v3" - - "github.com/aler9/gortsplib/pkg/base" ) func trackGenericGetClockRate(formats []string, rtpmap string) (int, error) { @@ -63,7 +61,7 @@ func trackGenericGetClockRate(formats []string, rtpmap string) (int, error) { // TrackGeneric is a generic track. type TrackGeneric struct { - control string + trackBase clockRate int media string formats []string @@ -87,9 +85,9 @@ func NewTrackGeneric(media string, formats []string, rtpmap string, fmtp string) }, nil } -func newTrackGenericFromMediaDescription(md *psdp.MediaDescription) (*TrackGeneric, error) { - control := trackFindControl(md) - +func newTrackGenericFromMediaDescription( + control string, + md *psdp.MediaDescription) (*TrackGeneric, error) { rtpmap := func() string { for _, attr := range md.Attributes { if attr.Key == "rtpmap" { @@ -114,7 +112,9 @@ func newTrackGenericFromMediaDescription(md *psdp.MediaDescription) (*TrackGener }() return &TrackGeneric{ - control: control, + trackBase: trackBase{ + control: control, + }, clockRate: clockRate, media: md.MediaName.Media, formats: md.MediaName.Formats, @@ -130,7 +130,7 @@ func (t *TrackGeneric) ClockRate() int { func (t *TrackGeneric) clone() Track { return &TrackGeneric{ - control: t.control, + trackBase: t.trackBase, clockRate: t.clockRate, media: t.media, formats: t.formats, @@ -139,21 +139,7 @@ func (t *TrackGeneric) clone() Track { } } -// 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 media description in SDP format. +// MediaDescription returns the track media description in SDP format. func (t *TrackGeneric) MediaDescription() *psdp.MediaDescription { return &psdp.MediaDescription{ MediaName: psdp.MediaName{ diff --git a/track_generic_test.go b/track_generic_test.go index 21107ce8..8e5d0aeb 100644 --- a/track_generic_test.go +++ b/track_generic_test.go @@ -31,26 +31,13 @@ func TestTrackGenericNewErrors(t *testing.T) { } func TestTrackGenericClone(t *testing.T) { - track, err := newTrackGenericFromMediaDescription( - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Port: psdp.RangedPort{Value: 0}, - Protos: []string{"RTP", "AVP"}, - Formats: []string{"98", "96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "98 H265/90000", - }, - { - Key: "fmtp", - Value: "98 profile-id=1; sprop-vps=QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ; " + - "sprop-sps=QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=; sprop-pps=RAHgdrAwxmQ=", - }, - }, - }) + track, err := NewTrackGeneric( + "video", + []string{"100", "101"}, + "98 H265/90000", + "98 profile-id=1; sprop-vps=QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ; "+ + "sprop-sps=QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=; sprop-pps=RAHgdrAwxmQ=", + ) require.NoError(t, err) clone := track.clone() diff --git a/track_h264.go b/track_h264.go index 70e7475a..b5b1cedb 100644 --- a/track_h264.go +++ b/track_h264.go @@ -9,8 +9,6 @@ import ( "sync" psdp "github.com/pion/sdp/v3" - - "github.com/aler9/gortsplib/pkg/base" ) func trackH264GetSPSPPS(md *psdp.MediaDescription) ([]byte, []byte, error) { @@ -61,7 +59,7 @@ func trackH264GetSPSPPS(md *psdp.MediaDescription) ([]byte, []byte, error) { // TrackH264 is a H264 track. type TrackH264 struct { - control string + trackBase payloadType uint8 sps []byte pps []byte @@ -80,12 +78,13 @@ func NewTrackH264(payloadType uint8, sps []byte, pps []byte, extradata []byte) ( } func newTrackH264FromMediaDescription( + control string, payloadType uint8, md *psdp.MediaDescription) (*TrackH264, error) { - control := trackFindControl(md) - t := &TrackH264{ - control: control, + trackBase: trackBase{ + control: control, + }, payloadType: payloadType, } @@ -105,7 +104,7 @@ func (t *TrackH264) ClockRate() int { func (t *TrackH264) clone() Track { return &TrackH264{ - control: t.control, + trackBase: t.trackBase, payloadType: t.payloadType, sps: t.sps, pps: t.pps, @@ -113,20 +112,6 @@ func (t *TrackH264) clone() Track { } } -// GetControl gets the track control. -func (t *TrackH264) GetControl() string { - return t.control -} - -// SetControl sets the track control. -func (t *TrackH264) SetControl(c string) { - t.control = c -} - -func (t *TrackH264) url(contentBase *base.URL) (*base.URL, error) { - return trackURL(t, contentBase) -} - // SPS returns the track SPS. func (t *TrackH264) SPS() []byte { t.mutex.RLock() @@ -160,7 +145,7 @@ func (t *TrackH264) SetPPS(v []byte) { t.pps = v } -// MediaDescription returns the media description in SDP format. +// MediaDescription returns the track media description in SDP format. func (t *TrackH264) MediaDescription() *psdp.MediaDescription { t.mutex.RLock() defer t.mutex.RUnlock() diff --git a/track_opus.go b/track_opus.go index 225f7c53..71b85bfa 100644 --- a/track_opus.go +++ b/track_opus.go @@ -6,13 +6,11 @@ import ( "strings" psdp "github.com/pion/sdp/v3" - - "github.com/aler9/gortsplib/pkg/base" ) // TrackOpus is a Opus track. type TrackOpus struct { - control string + trackBase payloadType uint8 sampleRate int channelCount int @@ -28,11 +26,10 @@ func NewTrackOpus(payloadType uint8, sampleRate int, channelCount int) (*TrackOp } func newTrackOpusFromMediaDescription( + control string, payloadType uint8, rtpmapPart1 string, md *psdp.MediaDescription) (*TrackOpus, error) { - control := trackFindControl(md) - tmp := strings.SplitN(rtpmapPart1, "/", 3) if len(tmp) != 3 { return nil, fmt.Errorf("invalid rtpmap (%v)", rtpmapPart1) @@ -49,7 +46,9 @@ func newTrackOpusFromMediaDescription( } return &TrackOpus{ - control: control, + trackBase: trackBase{ + control: control, + }, payloadType: payloadType, sampleRate: int(sampleRate), channelCount: int(channelCount), @@ -63,33 +62,19 @@ func (t *TrackOpus) ClockRate() int { func (t *TrackOpus) clone() Track { return &TrackOpus{ - control: t.control, + trackBase: t.trackBase, payloadType: t.payloadType, sampleRate: t.sampleRate, channelCount: t.channelCount, } } -// GetControl returns the track control. -func (t *TrackOpus) GetControl() string { - return t.control -} - -// SetControl sets the track control. -func (t *TrackOpus) SetControl(c string) { - t.control = c -} - -func (t *TrackOpus) url(contentBase *base.URL) (*base.URL, error) { - return trackURL(t, contentBase) -} - // ChannelCount returns the channel count. func (t *TrackOpus) ChannelCount() int { return t.channelCount } -// MediaDescription returns the media description in SDP format. +// MediaDescription returns the track media description in SDP format. func (t *TrackOpus) MediaDescription() *psdp.MediaDescription { typ := strconv.FormatInt(int64(t.payloadType), 10) diff --git a/track_pcmu.go b/track_pcmu.go index f90750e2..df104124 100644 --- a/track_pcmu.go +++ b/track_pcmu.go @@ -5,13 +5,11 @@ import ( "strings" psdp "github.com/pion/sdp/v3" - - "github.com/aler9/gortsplib/pkg/base" ) // TrackPCMU is a PCMU track. type TrackPCMU struct { - control string + trackBase } // NewTrackPCMU allocates a TrackPCMU. @@ -19,18 +17,20 @@ func NewTrackPCMU() *TrackPCMU { return &TrackPCMU{} } -func newTrackPCMUFromMediaDescription(rtpmapPart1 string, +func newTrackPCMUFromMediaDescription( + control string, + rtpmapPart1 string, md *psdp.MediaDescription) (*TrackPCMU, error, ) { - control := trackFindControl(md) - tmp := strings.Split(rtpmapPart1, "/") if len(tmp) >= 3 && tmp[2] != "1" { return nil, fmt.Errorf("PCMU tracks must have only one channel") } return &TrackPCMU{ - control: control, + trackBase: trackBase{ + control: control, + }, }, nil } @@ -40,24 +40,12 @@ func (t *TrackPCMU) ClockRate() int { } func (t *TrackPCMU) clone() Track { - return &TrackPCMU{} + return &TrackPCMU{ + trackBase: t.trackBase, + } } -// GetControl returns the track control. -func (t *TrackPCMU) GetControl() string { - return t.control -} - -// SetControl sets the track control. -func (t *TrackPCMU) SetControl(c string) { - t.control = c -} - -func (t *TrackPCMU) url(contentBase *base.URL) (*base.URL, error) { - return trackURL(t, contentBase) -} - -// MediaDescription returns the media description in SDP format. +// MediaDescription returns the track media description in SDP format. func (t *TrackPCMU) MediaDescription() *psdp.MediaDescription { return &psdp.MediaDescription{ MediaName: psdp.MediaName{ diff --git a/tracks_test.go b/tracks_test.go index 6194c440..8c2db03a 100644 --- a/tracks_test.go +++ b/tracks_test.go @@ -71,13 +71,17 @@ func TestTracksReadSkipGenericTracksWithoutClockRate(t *testing.T) { require.NoError(t, err) require.Equal(t, Tracks{ &TrackH264{ - control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", + trackBase: trackBase{ + control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", + }, payloadType: 97, sps: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, pps: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, }, &TrackPCMU{ - control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", + trackBase: trackBase{ + control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", + }, }, }, tracks) }