rewrite unmarshaling interface and tests of formats (#238)

This commit is contained in:
Alessandro Ros
2023-04-10 15:07:59 +02:00
committed by GitHub
parent c0c2dd68d3
commit 9ca5e130fe
53 changed files with 911 additions and 1383 deletions

View File

@@ -2,59 +2,18 @@
package formats package formats
import ( import (
"strconv"
"strings" "strings"
"github.com/pion/rtp" "github.com/pion/rtp"
psdp "github.com/pion/sdp/v3"
) )
func getFormatAttribute(attributes []psdp.Attribute, payloadType uint8, key string) string {
for _, attr := range attributes {
if attr.Key == key {
v := strings.TrimSpace(attr.Value)
if parts := strings.SplitN(v, " ", 2); len(parts) == 2 {
if tmp, err := strconv.ParseInt(parts[0], 10, 8); err == nil && uint8(tmp) == payloadType {
return parts[1]
}
}
}
}
return ""
}
func getCodecAndClock(rtpMap string) (string, string) { func getCodecAndClock(rtpMap string) (string, string) {
parts2 := strings.SplitN(rtpMap, "/", 2) parts2 := strings.SplitN(rtpMap, "/", 2)
if len(parts2) != 2 { if len(parts2) != 2 {
return "", "" return "", ""
} }
return parts2[0], parts2[1] return strings.ToLower(parts2[0]), parts2[1]
}
func decodeFMTP(enc string) map[string]string {
if enc == "" {
return nil
}
ret := make(map[string]string)
for _, kv := range strings.Split(enc, ";") {
kv = strings.Trim(kv, " ")
if len(kv) == 0 {
continue
}
tmp := strings.SplitN(kv, "=", 2)
if len(tmp) != 2 {
continue
}
ret[strings.ToLower(tmp[0])] = tmp[1]
}
return ret
} }
// Format is a RTP format of a media. // Format is a RTP format of a media.
@@ -79,31 +38,12 @@ type Format interface {
} }
// Unmarshal decodes a format from a media description. // Unmarshal decodes a format from a media description.
func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error) { func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[string]string) (Format, error) {
if payloadTypeStr == "smart/1/90000" {
attr, ok := md.Attribute("rtpmap")
if ok {
i := strings.Index(attr, " TP-LINK/90000")
if i >= 0 {
payloadTypeStr = attr[:i]
}
}
}
tmp, err := strconv.ParseInt(payloadTypeStr, 10, 8)
if err != nil {
return nil, err
}
payloadType := uint8(tmp)
rtpMap := getFormatAttribute(md.Attributes, payloadType, "rtpmap")
codec, clock := getCodecAndClock(rtpMap) codec, clock := getCodecAndClock(rtpMap)
codec = strings.ToLower(codec)
fmtp := decodeFMTP(getFormatAttribute(md.Attributes, payloadType, "fmtp"))
format := func() Format { format := func() Format {
switch { switch {
case md.MediaName.Media == "video": case mediaType == "video":
switch { switch {
case payloadType == 26: case payloadType == 26:
return &MJPEG{} return &MJPEG{}
@@ -127,7 +67,7 @@ func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error)
return &VP9{} return &VP9{}
} }
case md.MediaName.Media == "audio": case mediaType == "audio":
switch { switch {
case payloadType == 0, payloadType == 8: case payloadType == 0, payloadType == 8:
return &G711{} return &G711{}
@@ -158,7 +98,7 @@ func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error)
return &Generic{} return &Generic{}
}() }()
err = format.unmarshal(payloadType, clock, codec, rtpMap, fmtp) err := format.unmarshal(payloadType, clock, codec, rtpMap, fmtp)
if err != nil { if err != nil {
return nil, err return nil, err
} }

File diff suppressed because it is too large Load Diff

View File

@@ -22,26 +22,6 @@ func TestG711Attributes(t *testing.T) {
require.Equal(t, uint8(0), format.PayloadType()) require.Equal(t, uint8(0), format.PayloadType())
} }
func TestG711MediaDescription(t *testing.T) {
t.Run("pcma", func(t *testing.T) {
format := &G711{}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "PCMA/8000", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
})
t.Run("pcmu", func(t *testing.T) {
format := &G711{
MULaw: true,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "PCMU/8000", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
})
}
func TestG711DecEncoder(t *testing.T) { func TestG711DecEncoder(t *testing.T) {
format := &G711{} format := &G711{}

View File

@@ -15,14 +15,6 @@ func TestG722Attributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestG722MediaDescription(t *testing.T) {
format := &G722{}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "G722/8000", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
}
func TestG722DecEncoder(t *testing.T) { func TestG722DecEncoder(t *testing.T) {
format := &G722{} format := &G722{}

View File

@@ -26,27 +26,3 @@ func TestGenericAttributes(t *testing.T) {
require.Equal(t, uint8(98), format.PayloadType()) require.Equal(t, uint8(98), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestGenericMediaDescription(t *testing.T) {
format := &Generic{
PayloadTyp: 98,
RTPMap: "H265/90000",
FMTP: map[string]string{
"profile-id": "1",
"sprop-vps": "QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ",
"sprop-sps": "QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=",
"sprop-pps": "RAHgdrAwxmQ=",
},
}
err := format.Init()
require.NoError(t, err)
rtpmap, fmtp := format.Marshal()
require.Equal(t, "H265/90000", rtpmap)
require.Equal(t, map[string]string{
"profile-id": "1",
"sprop-vps": "QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ",
"sprop-sps": "QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=",
"sprop-pps": "RAHgdrAwxmQ=",
}, fmtp)
}

View File

@@ -45,44 +45,6 @@ func TestH264PTSEqualsDTS(t *testing.T) {
})) }))
} }
func TestH264MediaDescription(t *testing.T) {
t.Run("standard", func(t *testing.T) {
format := &H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
PPS: []byte{
0x68, 0xee, 0x3c, 0x80,
},
PacketizationMode: 1,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "H264/90000", rtpmap)
require.Equal(t, map[string]string{
"packetization-mode": "1",
"sprop-parameter-sets": "Z2QADKw7ULBLQgAAAwACAAADAD0I,aO48gA==",
"profile-level-id": "64000C",
}, fmtp)
})
t.Run("no sps/pps", func(t *testing.T) {
format := &H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "H264/90000", rtpmap)
require.Equal(t, map[string]string{
"packetization-mode": "1",
}, fmtp)
})
}
func TestH264DecEncoder(t *testing.T) { func TestH264DecEncoder(t *testing.T) {
format := &H264{} format := &H264{}

View File

@@ -32,23 +32,6 @@ func TestH265Attributes(t *testing.T) {
require.Equal(t, []byte{0x0B, 0x0C}, pps) require.Equal(t, []byte{0x0B, 0x0C}, pps)
} }
func TestH265MediaDescription(t *testing.T) {
format := &H265{
PayloadTyp: 96,
VPS: []byte{0x01, 0x02},
SPS: []byte{0x03, 0x04},
PPS: []byte{0x05, 0x06},
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "H265/90000", rtpmap)
require.Equal(t, map[string]string{
"sprop-vps": "AQI=",
"sprop-sps": "AwQ=",
"sprop-pps": "BQY=",
}, fmtp)
}
func TestH265DecEncoder(t *testing.T) { func TestH265DecEncoder(t *testing.T) {
format := &H265{} format := &H265{}

View File

@@ -1,8 +1,6 @@
package formats package formats
import ( import (
"fmt"
"strconv"
"testing" "testing"
"github.com/pion/rtp" "github.com/pion/rtp"
@@ -22,23 +20,6 @@ func TestLPCMAttributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestLPCMMediaDescription(t *testing.T) {
for _, ca := range []int{8, 16, 24} {
t.Run(strconv.FormatInt(int64(ca), 10), func(t *testing.T) {
format := &LPCM{
PayloadTyp: 96,
BitDepth: ca,
SampleRate: 96000,
ChannelCount: 2,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, fmt.Sprintf("L%d/96000/2", ca), rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
})
}
}
func TestLPCMDecEncoder(t *testing.T) { func TestLPCMDecEncoder(t *testing.T) {
format := &LPCM{ format := &LPCM{
PayloadTyp: 96, PayloadTyp: 96,

View File

@@ -15,14 +15,6 @@ func TestMJPEGAttributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMJPEGMediaDescription(t *testing.T) {
format := &MJPEG{}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "JPEG/90000", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
}
func TestMJPEGDecEncoder(t *testing.T) { func TestMJPEGDecEncoder(t *testing.T) {
format := &MJPEG{} format := &MJPEG{}

View File

@@ -14,11 +14,3 @@ func TestMPEG2AudioAttributes(t *testing.T) {
require.Equal(t, uint8(14), format.PayloadType()) require.Equal(t, uint8(14), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG2AudioMediaDescription(t *testing.T) {
format := &MPEG2Audio{}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
}

View File

@@ -14,11 +14,3 @@ func TestMPEG2VideoAttributes(t *testing.T) {
require.Equal(t, uint8(32), format.PayloadType()) require.Equal(t, uint8(32), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG2VideoMediaDescription(t *testing.T) {
format := &MPEG2Video{}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "", rtpmap)
require.Equal(t, map[string]string(nil), fmtp)
}

View File

@@ -27,31 +27,6 @@ func TestMPEG4AudioGenericAttributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG4AudioGenericMediaDescription(t *testing.T) {
format := &MPEG4AudioGeneric{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "mpeg4-generic/48000/2", rtpmap)
require.Equal(t, map[string]string{
"profile-level-id": "1",
"mode": "AAC-hbr",
"sizelength": "13",
"indexlength": "3",
"indexdeltalength": "3",
"config": "1190",
}, fmtp)
}
func TestMPEG4AudioGenericDecEncoder(t *testing.T) { func TestMPEG4AudioGenericDecEncoder(t *testing.T) {
format := &MPEG4AudioGeneric{ format := &MPEG4AudioGeneric{
PayloadTyp: 96, PayloadTyp: 96,

View File

@@ -147,7 +147,7 @@ func (f *MPEG4AudioLATM) Marshal() (string, map[string]string) {
} }
if f.SBREnabled != nil { if f.SBREnabled != nil {
if *f.CPresent { if *f.SBREnabled {
fmtp["SBR-enabled"] = "1" fmtp["SBR-enabled"] = "1"
} else { } else {
fmtp["SBR-enabled"] = "0" fmtp["SBR-enabled"] = "0"

View File

@@ -21,22 +21,3 @@ func TestMPEG4AudioLATMAttributes(t *testing.T) {
require.Equal(t, uint8(96), format.PayloadType()) require.Equal(t, uint8(96), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG4AudioLATMMediaDescription(t *testing.T) {
format := &MPEG4AudioLATM{
PayloadTyp: 96,
SampleRate: 48000,
Channels: 2,
Object: 2,
ProfileLevelID: 1,
Config: []byte{0x01, 0x02, 0x03},
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "MP4A-LATM/48000/2", rtpmap)
require.Equal(t, map[string]string{
"profile-level-id": "1",
"object": "2",
"config": "010203",
}, fmtp)
}

View File

@@ -18,18 +18,3 @@ func TestMPEG4VideoESAttributes(t *testing.T) {
require.Equal(t, uint8(96), format.PayloadType()) require.Equal(t, uint8(96), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG4VideoESMediaDescription(t *testing.T) {
format := &MPEG4VideoES{
PayloadTyp: 96,
ProfileLevelID: 1,
Config: []byte{0x0a, 0x0b, 0x03},
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "MP4V-ES/90000", rtpmap)
require.Equal(t, map[string]string{
"profile-level-id": "1",
"config": "0A0B03",
}, fmtp)
}

View File

@@ -43,18 +43,12 @@ func (f *Opus) unmarshal(payloadType uint8, clock string, codec string, rtpmap s
} }
sampleRate, err := strconv.ParseInt(tmp[0], 10, 64) sampleRate, err := strconv.ParseInt(tmp[0], 10, 64)
if err != nil { if err != nil || sampleRate != 48000 {
return err
}
if sampleRate != 48000 {
return fmt.Errorf("invalid sample rate: %d", sampleRate) return fmt.Errorf("invalid sample rate: %d", sampleRate)
} }
channelCount, err := strconv.ParseInt(tmp[1], 10, 64) channelCount, err := strconv.ParseInt(tmp[1], 10, 64)
if err != nil { if err != nil || channelCount != 2 {
return err
}
if channelCount != 2 {
return fmt.Errorf("invalid channel count: %d", channelCount) return fmt.Errorf("invalid channel count: %d", channelCount)
} }

View File

@@ -18,19 +18,6 @@ func TestOpusAttributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestOpusMediaDescription(t *testing.T) {
format := &Opus{
PayloadTyp: 96,
IsStereo: true,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "opus/48000/2", rtpmap)
require.Equal(t, map[string]string{
"sprop-stereo": "1",
}, fmtp)
}
func TestOpusDecEncoder(t *testing.T) { func TestOpusDecEncoder(t *testing.T) {
format := &Opus{} format := &Opus{}

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0,")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0")
string("")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string(",0")
string("0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("")
string("")
string("")
string("A")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("0")
string("0")
string("")
string("0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("")
string("")
string("0")
string("")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("0/")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("A")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("0")
string("\xc9u\xb9")
string("\x94\xe3ȿJ\xcai\xd5\xee\xc0\xff\xff\x00\x00O\x96a\xdb\x04\x01y\xc8@")
string("d\x00\x00\x00")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("0")
string("")
string("")
string("0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("")
string("")
string("")
string("0")

View File

@@ -0,0 +1,5 @@
go test fuzz v1
string("")
string("\f3r\x05\x00\xe7\xe8")
string("2\xf5\xe13")
string("7")

View File

@@ -0,0 +1,7 @@
go test fuzz v1
string("%20")
string("")
string("oc\x97\xf3V\x8e\xe0")
string("")
string("\x0f\x11")
string("Z")

View File

@@ -0,0 +1,7 @@
go test fuzz v1
string("")
string("")
string("")
string("")
string("")
string("0")

View File

@@ -0,0 +1,7 @@
go test fuzz v1
string("")
string("")
string("oc\x97\xf3V\x8e\xe0")
string("")
string("9A")
string("X")

View File

@@ -0,0 +1,7 @@
go test fuzz v1
string("")
string("")
string("oc\x97\xf3V\x8e\xe0")
string("")
string("\x0f\x11")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("A")
string("")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("0")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
string("/")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0")
string("")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("A")
string("")

View File

@@ -0,0 +1,4 @@
go test fuzz v1
string("1")
string("")
string("#")

View File

@@ -0,0 +1,4 @@
go test fuzz v1
string("")
string("")
string("1")

View File

@@ -0,0 +1,4 @@
go test fuzz v1
string("")
string("")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("/")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0/0")
string("0")

View File

@@ -0,0 +1,3 @@
go test fuzz v1
string("0/")
string("0")

View File

@@ -57,7 +57,7 @@ func (f *Vorbis) unmarshal(payloadType uint8, clock string, codec string, rtpmap
if key == "configuration" { if key == "configuration" {
conf, err := base64.StdEncoding.DecodeString(val) conf, err := base64.StdEncoding.DecodeString(val)
if err != nil { if err != nil {
return fmt.Errorf("invalid AAC config (%v)", val) return fmt.Errorf("invalid config: %v", val)
} }
f.Configuration = conf f.Configuration = conf

View File

@@ -19,18 +19,3 @@ func TestVorbisAttributes(t *testing.T) {
require.Equal(t, uint8(96), format.PayloadType()) require.Equal(t, uint8(96), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestVorbisMediaDescription(t *testing.T) {
format := &Vorbis{
PayloadTyp: 96,
SampleRate: 48000,
ChannelCount: 2,
Configuration: []byte{0x01, 0x02, 0x03, 0x04},
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "VORBIS/48000/2", rtpmap)
require.Equal(t, map[string]string{
"configuration": "AQIDBA==",
}, fmtp)
}

View File

@@ -40,16 +40,18 @@ func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
case "max-fr": case "max-fr":
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("invalid max-fr (%v)", val) return fmt.Errorf("invalid max-fr: %v", val)
} }
v2 := int(n) v2 := int(n)
f.MaxFR = &v2 f.MaxFR = &v2
case "max-fs": case "max-fs":
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("invalid max-fs (%v)", val) return fmt.Errorf("invalid max-fs: %v", val)
} }
v2 := int(n) v2 := int(n)
f.MaxFS = &v2 f.MaxFS = &v2
} }
@@ -61,9 +63,11 @@ func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
// Marshal implements Format. // Marshal implements Format.
func (f *VP8) Marshal() (string, map[string]string) { func (f *VP8) Marshal() (string, map[string]string) {
fmtp := make(map[string]string) fmtp := make(map[string]string)
if f.MaxFR != nil { if f.MaxFR != nil {
fmtp["max-fr"] = strconv.FormatInt(int64(*f.MaxFR), 10) fmtp["max-fr"] = strconv.FormatInt(int64(*f.MaxFR), 10)
} }
if f.MaxFS != nil { if f.MaxFS != nil {
fmtp["max-fs"] = strconv.FormatInt(int64(*f.MaxFS), 10) fmtp["max-fs"] = strconv.FormatInt(int64(*f.MaxFS), 10)
} }

View File

@@ -1,4 +1,4 @@
package formats package formats //nolint:dupl
import ( import (
"testing" "testing"
@@ -17,23 +17,6 @@ func TestVP8ttributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestVP8MediaDescription(t *testing.T) {
maxFR := 123
maxFS := 456
format := &VP8{
PayloadTyp: 96,
MaxFR: &maxFR,
MaxFS: &maxFS,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "VP8/90000", rtpmap)
require.Equal(t, map[string]string{
"max-fr": "123",
"max-fs": "456",
}, fmtp)
}
func TestVP8DecEncoder(t *testing.T) { func TestVP8DecEncoder(t *testing.T) {
format := &VP8{} format := &VP8{}

View File

@@ -41,24 +41,27 @@ func (f *VP9) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
case "max-fr": case "max-fr":
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("invalid max-fr (%v)", val) return fmt.Errorf("invalid max-fr: %v", val)
} }
v2 := int(n) v2 := int(n)
f.MaxFR = &v2 f.MaxFR = &v2
case "max-fs": case "max-fs":
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("invalid max-fs (%v)", val) return fmt.Errorf("invalid max-fs: %v", val)
} }
v2 := int(n) v2 := int(n)
f.MaxFS = &v2 f.MaxFS = &v2
case "profile-id": case "profile-id":
n, err := strconv.ParseUint(val, 10, 64) n, err := strconv.ParseUint(val, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("invalid profile-id (%v)", val) return fmt.Errorf("invalid profile-id: %v", val)
} }
v2 := int(n) v2 := int(n)
f.ProfileID = &v2 f.ProfileID = &v2
} }

View File

@@ -1,4 +1,4 @@
package formats package formats //nolint:dupl
import ( import (
"testing" "testing"
@@ -17,26 +17,6 @@ func TestVP9Attributes(t *testing.T) {
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestVP9MediaDescription(t *testing.T) {
maxFR := 123
maxFS := 456
profileID := 789
format := &VP9{
PayloadTyp: 96,
MaxFR: &maxFR,
MaxFS: &maxFS,
ProfileID: &profileID,
}
rtpmap, fmtp := format.Marshal()
require.Equal(t, "VP9/90000", rtpmap)
require.Equal(t, map[string]string{
"max-fr": "123",
"max-fs": "456",
"profile-id": "789",
}, fmtp)
}
func TestVP9DecEncoder(t *testing.T) { func TestVP9DecEncoder(t *testing.T) {
format := &VP9{} format := &VP9{}

View File

@@ -39,6 +39,45 @@ func getDirection(attributes []psdp.Attribute) Direction {
return "" return ""
} }
func getFormatAttribute(attributes []psdp.Attribute, payloadType uint8, key string) string {
for _, attr := range attributes {
if attr.Key == key {
v := strings.TrimSpace(attr.Value)
if parts := strings.SplitN(v, " ", 2); len(parts) == 2 {
if tmp, err := strconv.ParseInt(parts[0], 10, 8); err == nil && uint8(tmp) == payloadType {
return parts[1]
}
}
}
}
return ""
}
func decodeFMTP(enc string) map[string]string {
if enc == "" {
return nil
}
ret := make(map[string]string)
for _, kv := range strings.Split(enc, ";") {
kv = strings.Trim(kv, " ")
if len(kv) == 0 {
continue
}
tmp := strings.SplitN(kv, "=", 2)
if len(tmp) != 2 {
continue
}
ret[strings.ToLower(tmp[0])] = tmp[1]
}
return ret
}
// Direction is the direction of a media stream. // Direction is the direction of a media stream.
type Direction string type Direction string
@@ -82,7 +121,28 @@ func (m *Media) unmarshal(md *psdp.MediaDescription) error {
m.Formats = nil m.Formats = nil
for _, payloadType := range md.MediaName.Formats { for _, payloadType := range md.MediaName.Formats {
format, err := formats.Unmarshal(md, payloadType) if payloadType == "smart/1/90000" {
for _, attr := range md.Attributes {
if attr.Key == "rtpmap" {
i := strings.Index(attr.Value, " TP-LINK/90000")
if i >= 0 {
payloadType = attr.Value[:i]
break
}
}
}
}
tmp, err := strconv.ParseInt(payloadType, 10, 8)
if err != nil {
return err
}
payloadTypeInt := uint8(tmp)
rtpMap := getFormatAttribute(md.Attributes, payloadTypeInt, "rtpmap")
fmtp := decodeFMTP(getFormatAttribute(md.Attributes, payloadTypeInt, "fmtp"))
format, err := formats.Unmarshal(string(m.Type), payloadTypeInt, rtpMap, fmtp)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -516,6 +516,63 @@ var casesMedias = []struct {
}, },
}, },
}, },
{
"tp-link",
"v=0\r\n" +
"o=- 4158123474391860926 2 IN IP4 127.0.0.1\r\n" +
"s=-\r\n" +
"t=0 0\r\n" +
"m=application 42504 RTP/AVP smart/1/90000\r\n" +
"a=rtpmap:95 TP-LINK/90000\r\n",
"v=0\r\n" +
"o=- 0 0 IN IP4 127.0.0.1\r\n" +
"s=Stream\r\n" +
"c=IN IP4 0.0.0.0\r\n" +
"t=0 0\r\n" +
"m=application 0 RTP/AVP 95\r\n" +
"a=control\r\n" +
"a=rtpmap:95 TP-LINK/90000\r\n",
Medias{
{
Type: "application",
Formats: []formats.Format{&formats.Generic{
PayloadTyp: 95,
RTPMap: "TP-LINK/90000",
ClockRat: 90000,
}},
},
},
},
{
"h264 with space at end",
"v=0\r\n" +
"o=- 4158123474391860926 2 IN IP4 127.0.0.1\r\n" +
"s=-\r\n" +
"t=0 0\r\n" +
"m=video 42504 RTP/AVP 96\r\n" +
"a=rtpmap:96 H264/90000 \r\n" +
"a=fmtp:96 packetization-mode=1\r\n",
"v=0\r\n" +
"o=- 0 0 IN IP4 127.0.0.1\r\n" +
"s=Stream\r\n" +
"c=IN IP4 0.0.0.0\r\n" +
"t=0 0\r\n" +
"m=video 0 RTP/AVP 96\r\n" +
"a=control\r\n" +
"a=rtpmap:96 H264/90000\r\n" +
"a=fmtp:96 packetization-mode=1\r\n",
Medias{
{
Type: "video",
Formats: []formats.Format{
&formats.H264{
PayloadTyp: 96,
PacketizationMode: 1,
},
},
},
},
},
} }
func TestMediasUnmarshal(t *testing.T) { func TestMediasUnmarshal(t *testing.T) {