mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
improve coverage
This commit is contained in:
45
track.go
45
track.go
@@ -25,37 +25,28 @@ type Track interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) {
|
func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) {
|
||||||
switch md.MediaName.Media {
|
if rtpmap, ok := md.Attribute("rtpmap"); ok {
|
||||||
case "video":
|
rtpmap = strings.TrimSpace(rtpmap)
|
||||||
if rtpmap, ok := md.Attribute("rtpmap"); ok {
|
|
||||||
rtpmap = strings.TrimSpace(rtpmap)
|
|
||||||
|
|
||||||
if vals := strings.Split(rtpmap, " "); len(vals) == 2 && vals[1] == "H264/90000" {
|
if rtpmapParts := strings.Split(rtpmap, " "); len(rtpmapParts) == 2 {
|
||||||
tmp, err := strconv.ParseInt(vals[0], 10, 64)
|
tmp, err := strconv.ParseInt(rtpmapParts[0], 10, 64)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return nil, fmt.Errorf("invalid payload type '%s'", vals[0])
|
|
||||||
}
|
|
||||||
payloadType := uint8(tmp)
|
payloadType := uint8(tmp)
|
||||||
|
|
||||||
return newTrackH264FromMediaDescription(payloadType, md)
|
switch {
|
||||||
}
|
case md.MediaName.Media == "video":
|
||||||
}
|
if rtpmapParts[1] == "H264/90000" {
|
||||||
|
return newTrackH264FromMediaDescription(payloadType, md)
|
||||||
|
}
|
||||||
|
|
||||||
case "audio":
|
case md.MediaName.Media == "audio":
|
||||||
if rtpmap, ok := md.Attribute("rtpmap"); ok {
|
switch {
|
||||||
if vals := strings.Split(rtpmap, " "); len(vals) == 2 {
|
case strings.HasPrefix(strings.ToLower(rtpmapParts[1]), "mpeg4-generic/"):
|
||||||
tmp, err := strconv.ParseInt(vals[0], 10, 64)
|
return newTrackAACFromMediaDescription(payloadType, md)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid payload type '%s'", vals[0])
|
|
||||||
}
|
|
||||||
payloadType := uint8(tmp)
|
|
||||||
|
|
||||||
if strings.HasPrefix(strings.ToLower(vals[1]), "mpeg4-generic/") {
|
case strings.HasPrefix(rtpmapParts[1], "opus/"):
|
||||||
return newTrackAACFromMediaDescription(payloadType, md)
|
return newTrackOpusFromMediaDescription(payloadType, rtpmapParts[1], md)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(vals[1], "opus/") {
|
|
||||||
return newTrackOpusFromMediaDescription(payloadType, rtpmap, md)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +66,7 @@ func trackFindControl(md *psdp.MediaDescription) string {
|
|||||||
|
|
||||||
func trackURL(t Track, contentBase *base.URL) (*base.URL, error) {
|
func trackURL(t Track, contentBase *base.URL) (*base.URL, error) {
|
||||||
if contentBase == nil {
|
if contentBase == nil {
|
||||||
return nil, fmt.Errorf("no Content-Base header provided")
|
return nil, fmt.Errorf("Content-Base header not provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
control := t.GetControl()
|
control := t.GetControl()
|
||||||
|
@@ -86,10 +86,7 @@ func newTrackAACFromMediaDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// re-encode the conf to normalize it
|
// re-encode the conf to normalize it
|
||||||
enc, err = mpegConf.Encode()
|
enc, _ = mpegConf.Encode()
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid AAC config (%v)", tmp[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return &TrackAAC{
|
return &TrackAAC{
|
||||||
control: control,
|
control: control,
|
||||||
|
@@ -10,10 +10,10 @@ import (
|
|||||||
func TestTrackAACNew(t *testing.T) {
|
func TestTrackAACNew(t *testing.T) {
|
||||||
track, err := NewTrackAAC(96, 2, 48000, 4, []byte{0x01, 0x02})
|
track, err := NewTrackAAC(96, 2, 48000, 4, []byte{0x01, 0x02})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, track.typ)
|
require.Equal(t, 2, track.Type())
|
||||||
require.Equal(t, 48000, track.sampleRate)
|
require.Equal(t, 48000, track.ClockRate())
|
||||||
require.Equal(t, 4, track.channelCount)
|
require.Equal(t, 4, track.ChannelCount())
|
||||||
require.Equal(t, []byte{0x01, 0x02}, track.aotSpecificConfig)
|
require.Equal(t, []byte{0x01, 0x02}, track.AOTSpecificConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrackAACClone(t *testing.T) {
|
func TestTrackAACClone(t *testing.T) {
|
||||||
|
@@ -7,6 +7,27 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTrackGenericNew(t *testing.T) {
|
||||||
|
track, err := NewTrackGeneric(
|
||||||
|
"video",
|
||||||
|
[]string{"100", "101"},
|
||||||
|
"98 H265/90000",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 90000, track.ClockRate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrackGenericNewErrors(t *testing.T) {
|
||||||
|
_, err := NewTrackGeneric(
|
||||||
|
"video",
|
||||||
|
[]string{"100", "101"},
|
||||||
|
"98 H265/",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
require.EqualError(t, err, "unable to get clock rate: strconv.ParseInt: parsing \"\": invalid syntax")
|
||||||
|
}
|
||||||
|
|
||||||
func TestTrackGenericClone(t *testing.T) {
|
func TestTrackGenericClone(t *testing.T) {
|
||||||
track, err := newTrackGenericFromMediaDescription(
|
track, err := newTrackGenericFromMediaDescription(
|
||||||
&psdp.MediaDescription{
|
&psdp.MediaDescription{
|
||||||
@@ -34,3 +55,30 @@ func TestTrackGenericClone(t *testing.T) {
|
|||||||
require.NotSame(t, track, copy)
|
require.NotSame(t, track, copy)
|
||||||
require.Equal(t, track, copy)
|
require.Equal(t, track, copy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrackGenericMediaDescription(t *testing.T) {
|
||||||
|
track, err := NewTrackGeneric(
|
||||||
|
"video",
|
||||||
|
[]string{"100", "101"},
|
||||||
|
"98 H265/90000",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "video",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"100", "101"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "98 H265/90000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "control",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, track.MediaDescription())
|
||||||
|
}
|
||||||
|
@@ -135,6 +135,11 @@ func (t *TrackH264) PPS() []byte {
|
|||||||
return t.pps
|
return t.pps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtraData returns the track extra data.
|
||||||
|
func (t *TrackH264) ExtraData() []byte {
|
||||||
|
return t.extradata
|
||||||
|
}
|
||||||
|
|
||||||
// SetSPS sets the track SPS.
|
// SetSPS sets the track SPS.
|
||||||
func (t *TrackH264) SetSPS(v []byte) {
|
func (t *TrackH264) SetSPS(v []byte) {
|
||||||
t.sps = v
|
t.sps = v
|
||||||
|
@@ -168,9 +168,9 @@ func TestTrackH264New(t *testing.T) {
|
|||||||
track, err := NewTrackH264(96,
|
track, err := NewTrackH264(96,
|
||||||
[]byte{0x01, 0x02}, []byte{0x03, 0x04}, []byte{0x05, 0x06})
|
[]byte{0x01, 0x02}, []byte{0x03, 0x04}, []byte{0x05, 0x06})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02}, track.sps)
|
require.Equal(t, []byte{0x01, 0x02}, track.SPS())
|
||||||
require.Equal(t, []byte{0x03, 0x04}, track.pps)
|
require.Equal(t, []byte{0x03, 0x04}, track.PPS())
|
||||||
require.Equal(t, []byte{0x05, 0x06}, track.extradata)
|
require.Equal(t, []byte{0x05, 0x06}, track.ExtraData())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrackH264Clone(t *testing.T) {
|
func TestTrackH264Clone(t *testing.T) {
|
||||||
|
@@ -29,12 +29,12 @@ func NewTrackOpus(payloadType uint8, sampleRate int, channelCount int) (*TrackOp
|
|||||||
|
|
||||||
func newTrackOpusFromMediaDescription(
|
func newTrackOpusFromMediaDescription(
|
||||||
payloadType uint8,
|
payloadType uint8,
|
||||||
rtpmap string,
|
rtpmapPart1 string,
|
||||||
md *psdp.MediaDescription) (*TrackOpus, error) {
|
md *psdp.MediaDescription) (*TrackOpus, error) {
|
||||||
control := trackFindControl(md)
|
control := trackFindControl(md)
|
||||||
tmp := strings.SplitN(rtpmap, "/", 3)
|
tmp := strings.SplitN(rtpmapPart1, "/", 3)
|
||||||
if len(tmp) != 3 {
|
if len(tmp) != 3 {
|
||||||
return nil, fmt.Errorf("invalid rtpmap (%v)", rtpmap)
|
return nil, fmt.Errorf("invalid rtpmap (%v)", rtpmapPart1)
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleRate, err := strconv.ParseInt(tmp[1], 10, 64)
|
sampleRate, err := strconv.ParseInt(tmp[1], 10, 64)
|
||||||
@@ -83,6 +83,11 @@ func (t *TrackOpus) url(contentBase *base.URL) (*base.URL, error) {
|
|||||||
return trackURL(t, contentBase)
|
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 media description in SDP format.
|
||||||
func (t *TrackOpus) MediaDescription() *psdp.MediaDescription {
|
func (t *TrackOpus) MediaDescription() *psdp.MediaDescription {
|
||||||
typ := strconv.FormatInt(int64(t.payloadType), 10)
|
typ := strconv.FormatInt(int64(t.payloadType), 10)
|
||||||
|
@@ -10,8 +10,8 @@ import (
|
|||||||
func TestTrackOpusNew(t *testing.T) {
|
func TestTrackOpusNew(t *testing.T) {
|
||||||
track, err := NewTrackOpus(96, 48000, 2)
|
track, err := NewTrackOpus(96, 48000, 2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 48000, track.sampleRate)
|
require.Equal(t, 48000, track.ClockRate())
|
||||||
require.Equal(t, 2, track.channelCount)
|
require.Equal(t, 2, track.ChannelCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTracOpusClone(t *testing.T) {
|
func TestTracOpusClone(t *testing.T) {
|
||||||
|
102
track_test.go
102
track_test.go
@@ -366,7 +366,7 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) {
|
|||||||
"unable to get clock rate: attribute 'rtpmap' not found",
|
"unable to get clock rate: attribute 'rtpmap' not found",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"generic invalid rtpmap 1",
|
"generic invalid clockrate 1",
|
||||||
&psdp.MediaDescription{
|
&psdp.MediaDescription{
|
||||||
MediaName: psdp.MediaName{
|
MediaName: psdp.MediaName{
|
||||||
Media: "video",
|
Media: "video",
|
||||||
@@ -383,7 +383,7 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) {
|
|||||||
"unable to get clock rate: invalid rtpmap (96)",
|
"unable to get clock rate: invalid rtpmap (96)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"generic invalid rtpmap 2",
|
"generic invalid clockrate 2",
|
||||||
&psdp.MediaDescription{
|
&psdp.MediaDescription{
|
||||||
MediaName: psdp.MediaName{
|
MediaName: psdp.MediaName{
|
||||||
Media: "video",
|
Media: "video",
|
||||||
@@ -399,6 +399,23 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"unable to get clock rate: invalid rtpmap (96 mpeg4-generic)",
|
"unable to get clock rate: invalid rtpmap (96 mpeg4-generic)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"generic invalid clockrate 3",
|
||||||
|
&psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "video",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"96"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "96 mpeg4-generic/aa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"unable to get clock rate: strconv.ParseInt: parsing \"aa\": invalid syntax",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"aac missing fmtp",
|
"aac missing fmtp",
|
||||||
&psdp.MediaDescription{
|
&psdp.MediaDescription{
|
||||||
@@ -480,7 +497,7 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) {
|
|||||||
"config is missing (96 profile-level-id=1)",
|
"config is missing (96 profile-level-id=1)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"aac invalid config",
|
"aac invalid config 1",
|
||||||
&psdp.MediaDescription{
|
&psdp.MediaDescription{
|
||||||
MediaName: psdp.MediaName{
|
MediaName: psdp.MediaName{
|
||||||
Media: "audio",
|
Media: "audio",
|
||||||
@@ -500,6 +517,78 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"invalid AAC config (zz)",
|
"invalid AAC config (zz)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"aac invalid config 2",
|
||||||
|
&psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"96"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "96 mpeg4-generic/48000/2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "fmtp",
|
||||||
|
Value: "96 profile-level-id=1; config=aa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"invalid AAC config (aa)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"opus invalid 1",
|
||||||
|
&psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"96"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "96 opus/48000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"invalid rtpmap (opus/48000)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"opus invalid 2",
|
||||||
|
&psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"96"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "96 opus/aa/2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"strconv.ParseInt: parsing \"aa\": invalid syntax",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"opus invalid 3",
|
||||||
|
&psdp.MediaDescription{
|
||||||
|
MediaName: psdp.MediaName{
|
||||||
|
Media: "audio",
|
||||||
|
Protos: []string{"RTP", "AVP"},
|
||||||
|
Formats: []string{"96"},
|
||||||
|
},
|
||||||
|
Attributes: []psdp.Attribute{
|
||||||
|
{
|
||||||
|
Key: "rtpmap",
|
||||||
|
Value: "96 opus/48000/aa",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"strconv.ParseInt: parsing \"aa\": invalid syntax",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
_, err := newTrackFromMediaDescription(ca.md)
|
_, err := newTrackFromMediaDescription(ca.md)
|
||||||
@@ -618,3 +707,10 @@ func TestTrackURL(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrackURLError(t *testing.T) {
|
||||||
|
track, err := NewTrackH264(96, []byte{0x01, 0x02, 0x03, 0x04}, []byte{0x05, 0x06, 0x07, 0x08}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = track.url(nil)
|
||||||
|
require.EqualError(t, err, "Content-Base header not provided")
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user