diff --git a/pkg/formats/format.go b/pkg/formats/format.go index 5691bbc1..84e790b5 100644 --- a/pkg/formats/format.go +++ b/pkg/formats/format.go @@ -2,59 +2,18 @@ package formats import ( - "strconv" "strings" "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) { parts2 := strings.SplitN(rtpMap, "/", 2) if len(parts2) != 2 { return "", "" } - return 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 + return strings.ToLower(parts2[0]), parts2[1] } // Format is a RTP format of a media. @@ -79,31 +38,12 @@ type Format interface { } // Unmarshal decodes a format from a media description. -func Unmarshal(md *psdp.MediaDescription, payloadTypeStr 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") +func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[string]string) (Format, error) { codec, clock := getCodecAndClock(rtpMap) - codec = strings.ToLower(codec) - fmtp := decodeFMTP(getFormatAttribute(md.Attributes, payloadType, "fmtp")) format := func() Format { switch { - case md.MediaName.Media == "video": + case mediaType == "video": switch { case payloadType == 26: return &MJPEG{} @@ -127,7 +67,7 @@ func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error) return &VP9{} } - case md.MediaName.Media == "audio": + case mediaType == "audio": switch { case payloadType == 0, payloadType == 8: return &G711{} @@ -158,7 +98,7 @@ func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error) return &Generic{} }() - err = format.unmarshal(payloadType, clock, codec, rtpMap, fmtp) + err := format.unmarshal(payloadType, clock, codec, rtpMap, fmtp) if err != nil { return nil, err } diff --git a/pkg/formats/format_test.go b/pkg/formats/format_test.go index 379b8fa9..2c89c7c9 100644 --- a/pkg/formats/format_test.go +++ b/pkg/formats/format_test.go @@ -3,7 +3,6 @@ package formats import ( "testing" - psdp "github.com/pion/sdp/v3" "github.com/stretchr/testify/require" "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" @@ -17,1084 +16,716 @@ func boolPtr(v bool) *bool { return &v } -func TestUnmarshal(t *testing.T) { - for _, ca := range []struct { - name string - md *psdp.MediaDescription - format Format - }{ - { - "audio g711 pcma", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"8"}, - }, - }, - &G711{}, +var casesFormat = []struct { + name string + mediaType string + payloadType uint8 + rtpMap string + fmtp map[string]string + dec Format + encRtpMap string + encFmtp map[string]string +}{ + { + "audio g711 pcma", + "audio", + 8, + "", + nil, + &G711{}, + "PCMA/8000", + nil, + }, + { + "audio g711 pcmu", + "audio", + 0, + "", + nil, + &G711{ + MULaw: true, }, - { - "audio g711 pcmu", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"0"}, - }, - }, - &G711{ - MULaw: true, - }, + "PCMU/8000", + nil, + }, + { + "audio g722", + "audio", + 9, + "", + nil, + &G722{}, + "G722/8000", + nil, + }, + { + "audio lpcm 8", + "audio", + 97, + "L8/48000/2", + nil, + &LPCM{ + PayloadTyp: 97, + BitDepth: 8, + SampleRate: 48000, + ChannelCount: 2, }, - { - "audio g722", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"9"}, - }, - }, - &G722{}, + "L8/48000/2", + nil, + }, + { + "audio lpcm 16", + "audio", + 97, + "L16/96000/2", + nil, + &LPCM{ + PayloadTyp: 97, + BitDepth: 16, + SampleRate: 96000, + ChannelCount: 2, }, - { - "audio lpcm 8", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"97"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 L8/48000/2", - }, - }, - }, - &LPCM{ - PayloadTyp: 97, - BitDepth: 8, + "L16/96000/2", + nil, + }, + { + "audio lpcm 16 with no explicit channel", + "audio", + 97, + "L16/16000", + nil, + &LPCM{ + PayloadTyp: 97, + BitDepth: 16, + SampleRate: 16000, + ChannelCount: 1, + }, + "L16/16000/1", + nil, + }, + { + "audio lpcm 24", + "audio", + 98, + "L24/44100/4", + nil, + &LPCM{ + PayloadTyp: 98, + BitDepth: 24, + SampleRate: 44100, + ChannelCount: 4, + }, + "L24/44100/4", + nil, + }, + { + "audio mpeg2 audio", + "audio", + 14, + "", + nil, + &MPEG2Audio{}, + "", + nil, + }, + { + "audio aac", + "audio", + 96, + "mpeg4-generic/48000/2", + map[string]string{ + "profile-level-id": "1", + "mode": "AAC-hbr", + "sizelength": "13", + "indexlength": "3", + "indexdeltalength": "3", + "config": "11900810", + }, + &MPEG4Audio{ + PayloadTyp: 96, + Config: &mpeg4audio.Config{ + Type: mpeg4audio.ObjectTypeAACLC, SampleRate: 48000, ChannelCount: 2, }, + SizeLength: 13, + IndexLength: 3, + IndexDeltaLength: 3, }, - { - "audio lpcm 16", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"97"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 L16/96000/2", - }, - }, - }, - &LPCM{ - PayloadTyp: 97, - BitDepth: 16, - SampleRate: 96000, + "mpeg4-generic/48000/2", + map[string]string{ + "profile-level-id": "1", + "mode": "AAC-hbr", + "sizelength": "13", + "indexlength": "3", + "indexdeltalength": "3", + "config": "1190", + }, + }, + { + "audio aac vlc rtsp server", + "audio", + 96, + "mpeg4-generic/48000/2", + map[string]string{ + "profile-level-id": "1", + "mode": "AAC-hbr", + "sizelength": "13", + "indexlength": "3", + "indexdeltalength": "3", + "config": "1190", + }, + &MPEG4Audio{ + PayloadTyp: 96, + Config: &mpeg4audio.Config{ + Type: mpeg4audio.ObjectTypeAACLC, + SampleRate: 48000, ChannelCount: 2, }, + SizeLength: 13, + IndexLength: 3, + IndexDeltaLength: 3, }, - { - "audio lpcm 16 with no explicit channel", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"97"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 L16/16000", - }, - }, + "mpeg4-generic/48000/2", + map[string]string{ + "profile-level-id": "1", + "mode": "AAC-hbr", + "sizelength": "13", + "indexlength": "3", + "indexdeltalength": "3", + "config": "1190", + }, + }, + { + "audio aac without indexlength", + "audio", + 96, + "mpeg4-generic/48000/2", + map[string]string{ + "streamtype": "3", + "profile-level-id": "14", + "mode": "AAC-hbr", + "config": "1190", + "sizelength": "13", + }, + &MPEG4Audio{ + PayloadTyp: 96, + Config: &mpeg4audio.Config{ + Type: mpeg4audio.ObjectTypeAACLC, + SampleRate: 48000, + ChannelCount: 2, }, - &LPCM{ - PayloadTyp: 97, - BitDepth: 16, - SampleRate: 16000, - ChannelCount: 1, + SizeLength: 13, + }, + "mpeg4-generic/48000/2", + map[string]string{ + "profile-level-id": "1", + "mode": "AAC-hbr", + "config": "1190", + "sizelength": "13", + }, + }, + { + "audio aac lc latm", + "audio", + 96, + "MP4A-LATM/24000/2", + map[string]string{ + "profile-level-id": "1", + "bitrate": "64000", + "cpresent": "0", + "object": "2", + "config": "400026203fc0", + }, + &MPEG4AudioLATM{ + PayloadTyp: 96, + SampleRate: 24000, + Channels: 2, + ProfileLevelID: 1, + Bitrate: intPtr(64000), + CPresent: boolPtr(false), + Object: 2, + Config: []byte{0x40, 0x00, 0x26, 0x20, 0x3f, 0xc0}, + }, + "MP4A-LATM/24000/2", + map[string]string{ + "profile-level-id": "1", + "bitrate": "64000", + "cpresent": "0", + "object": "2", + "config": "400026203fc0", + }, + }, + { + "audio aac v2 latm", + "audio", + 110, + "MP4A-LATM/24000/1", + map[string]string{ + "profile-level-id": "15", + "object": "2", + "cpresent": "0", + "config": "400026103fc0", + "sbr-enabled": "1", + }, + &MPEG4AudioLATM{ + PayloadTyp: 110, + SampleRate: 24000, + Channels: 1, + ProfileLevelID: 15, + CPresent: boolPtr(false), + Object: 2, + SBREnabled: boolPtr(true), + Config: []byte{0x40, 0x00, 0x26, 0x10, 0x3f, 0xc0}, + }, + "MP4A-LATM/24000/1", + map[string]string{ + "profile-level-id": "15", + "object": "2", + "cpresent": "0", + "config": "400026103fc0", + "SBR-enabled": "1", + }, + }, + { + "audio vorbis", + "audio", + 96, + "VORBIS/44100/2", + map[string]string{ + "configuration": "AQIDBA==", + }, + &Vorbis{ + PayloadTyp: 96, + SampleRate: 44100, + ChannelCount: 2, + Configuration: []byte{0x01, 0x02, 0x03, 0x04}, + }, + "VORBIS/44100/2", + map[string]string{ + "configuration": "AQIDBA==", + }, + }, + { + "audio opus", + "audio", + 96, + "opus/48000/2", + map[string]string{ + "sprop-stereo": "1", + }, + &Opus{ + PayloadTyp: 96, + IsStereo: true, + }, + "opus/48000/2", + map[string]string{ + "sprop-stereo": "1", + }, + }, + { + "video jpeg", + "video", + 26, + "", + nil, + &MJPEG{}, + "JPEG/90000", + nil, + }, + { + "video mpeg2 video", + "video", + 32, + "", + nil, + &MPEG2Video{}, + "", + nil, + }, + { + "video mpeg4 video", + "video", + 96, + "MP4V-ES/90000", + map[string]string{ + "profile-level-id": "1", + "config": "000001B001000001B58913000001000000012000C48" + + "D8AEE053C04641443000001B24C61766335382E3133342E313030", + }, + &MPEG4Video{ + PayloadTyp: 96, + ProfileLevelID: 1, + Config: []byte{ + 0x00, 0x00, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x01, + 0xb5, 0x89, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x20, 0x00, 0xc4, 0x8d, 0x8a, 0xee, + 0x05, 0x3c, 0x04, 0x64, 0x14, 0x43, 0x00, 0x00, + 0x01, 0xb2, 0x4c, 0x61, 0x76, 0x63, 0x35, 0x38, + 0x2e, 0x31, 0x33, 0x34, 0x2e, 0x31, 0x30, 0x30, }, }, - { - "audio lpcm 24", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"98"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "98 L24/44100/4", - }, - }, - }, - &LPCM{ - PayloadTyp: 98, - BitDepth: 24, - SampleRate: 44100, - ChannelCount: 4, - }, + "MP4V-ES/90000", + map[string]string{ + "profile-level-id": "1", + "config": "000001B001000001B58913000001000000012000C48" + + "D8AEE053C04641443000001B24C61766335382E3133342E313030", }, - { - "audio mpeg2 audio", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"14"}, - }, - }, - &MPEG2Audio{}, + }, + { + "video h264", + "video", + 96, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "sprop-parameter-sets": "Z2QADKw7ULBLQgAAAwACAAADAD0I,aO48gA==", + "profile-level-id": "64000C", }, - { - "audio aac", - &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; mode=AAC-hbr; sizelength=13; indexlength=3; indexdeltalength=3; config=11900810", - }, - { - Key: "control", - Value: "", - }, - }, + &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, }, - &MPEG4Audio{ - PayloadTyp: 96, - Config: &mpeg4audio.Config{ - Type: mpeg4audio.ObjectTypeAACLC, - SampleRate: 48000, - ChannelCount: 2, - }, - SizeLength: 13, - IndexLength: 3, - IndexDeltaLength: 3, + PPS: []byte{ + 0x68, 0xee, 0x3c, 0x80, }, + PacketizationMode: 1, }, - { - "audio aac vlc rtsp server", - &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; mode=AAC-hbr; sizelength=13; indexlength=3; indexdeltalength=3; config=1190;", - }, - }, - }, - &MPEG4Audio{ - PayloadTyp: 96, - Config: &mpeg4audio.Config{ - Type: mpeg4audio.ObjectTypeAACLC, - SampleRate: 48000, - ChannelCount: 2, - }, - SizeLength: 13, - IndexLength: 3, - IndexDeltaLength: 3, - }, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "sprop-parameter-sets": "Z2QADKw7ULBLQgAAAwACAAADAD0I,aO48gA==", + "profile-level-id": "64000C", }, - { - "audio aac without indexlength", - &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 streamtype=3;profile-level-id=14;mode=AAC-hbr;config=1190;sizeLength=13", - }, - { - Key: "control", - Value: "", - }, - }, - }, - &MPEG4Audio{ - PayloadTyp: 96, - Config: &mpeg4audio.Config{ - Type: mpeg4audio.ObjectTypeAACLC, - SampleRate: 48000, - ChannelCount: 2, - }, - SizeLength: 13, - IndexLength: 0, - IndexDeltaLength: 0, - }, + }, + { + "video h264 vlc rtsp server", + "video", + 96, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "profile-level-id": "64001f", + "sprop-parameter-sets": "Z2QAH6zZQFAFuwFsgAAAAwCAAAAeB4wYyw==,aOvjyyLA", }, - { - "audio aac lc latm", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 MP4A-LATM/24000/2", - }, - { - Key: "fmtp", - Value: "96 110 profile-level-id=1; bitrate=64000; cpresent=0; " + - "object=2; config=400026203fc0", - }, - }, + &H264{ + PayloadTyp: 96, + SPS: []byte{ + 0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50, + 0x05, 0xbb, 0x01, 0x6c, 0x80, 0x00, 0x00, 0x03, + 0x00, 0x80, 0x00, 0x00, 0x1e, 0x07, 0x8c, 0x18, + 0xcb, }, - &MPEG4AudioLATM{ - PayloadTyp: 96, - SampleRate: 24000, - Channels: 2, - ProfileLevelID: 30, - Bitrate: intPtr(64000), - CPresent: boolPtr(false), - Object: 2, - Config: []byte{0x40, 0x00, 0x26, 0x20, 0x3f, 0xc0}, + PPS: []byte{ + 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0, }, + PacketizationMode: 1, }, - { - "audio aac v2 latm", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"110"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "110 MP4A-LATM/24000/1", - }, - { - Key: "fmtp", - Value: "110 profile-level-id=15; object=2; cpresent=0; " + - "config=400026103fc0; SBR-enabled=1", - }, - }, - }, - &MPEG4AudioLATM{ - PayloadTyp: 110, - SampleRate: 24000, - Channels: 1, - ProfileLevelID: 15, - CPresent: boolPtr(false), - Object: 2, - SBREnabled: boolPtr(true), - Config: []byte{0x40, 0x00, 0x26, 0x10, 0x3f, 0xc0}, - }, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "profile-level-id": "64001F", + "sprop-parameter-sets": "Z2QAH6zZQFAFuwFsgAAAAwCAAAAeB4wYyw==,aOvjyyLA", }, - { - "audio vorbis", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 VORBIS/44100/2", - }, - { - Key: "fmtp", - Value: "96 configuration=AQIDBA==", - }, - }, - }, - &Vorbis{ - PayloadTyp: 96, - SampleRate: 44100, - ChannelCount: 2, - Configuration: []byte{0x01, 0x02, 0x03, 0x04}, - }, + }, + { + "video h264 sprop-parameter-sets with extra data", + "video", + 96, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "sprop-parameter-sets": "Z2QAKawTMUB4BEfeA+oCAgPgAAADACAAAAZSgA==,aPqPLA==,aF6jzAMA", + "profile-level-id": "640029", }, - { - "audio opus", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 opus/48000/2", - }, - { - Key: "fmtp", - Value: "96 sprop-stereo=1", - }, - }, + &H264{ + PayloadTyp: 96, + SPS: []byte{ + 0x67, 0x64, 0x00, 0x29, 0xac, 0x13, 0x31, 0x40, + 0x78, 0x04, 0x47, 0xde, 0x03, 0xea, 0x02, 0x02, + 0x03, 0xe0, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, + 0x00, 0x06, 0x52, 0x80, }, - &Opus{ - PayloadTyp: 96, - IsStereo: true, + PPS: []byte{ + 0x68, 0xfa, 0x8f, 0x2c, }, + PacketizationMode: 1, }, - { - "video jpeg", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"26"}, - }, - }, - &MJPEG{}, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "sprop-parameter-sets": "Z2QAKawTMUB4BEfeA+oCAgPgAAADACAAAAZSgA==,aPqPLA==", + "profile-level-id": "640029", }, - { - "video mpeg2 video", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"32"}, - }, - }, - &MPEG2Video{}, + }, + { + "video h264 empty sprop-parameter-sets", + "video", + 96, + "H264/90000", + map[string]string{ + "packetization-mode": "1", + "sprop-parameter-sets": "", }, - { - "video mpeg4 video", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 MP4V-ES/90000", - }, - { - Key: "fmtp", - Value: "96 profile-level-id=1; " + - "config=000001B001000001B58913000001000000012000C48D8AEE053C04641443000001B24C61766335382E3133342E313030", - }, - }, - }, - &MPEG4Video{ - PayloadTyp: 96, - ProfileLevelID: 1, - Config: []byte{ - 0x00, 0x00, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x01, - 0xb5, 0x89, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x01, 0x20, 0x00, 0xc4, 0x8d, 0x8a, 0xee, - 0x05, 0x3c, 0x04, 0x64, 0x14, 0x43, 0x00, 0x00, - 0x01, 0xb2, 0x4c, 0x61, 0x76, 0x63, 0x35, 0x38, - 0x2e, 0x31, 0x33, 0x34, 0x2e, 0x31, 0x30, 0x30, - }, - }, + &H264{ + PayloadTyp: 96, + PacketizationMode: 1, }, - { - "video h264", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 packetization-mode=1; " + - "sprop-parameter-sets=Z2QADKw7ULBLQgAAAwACAAADAD0I,aO48gA==; profile-level-id=64000C", - }, - }, - }, - &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, - }, + "H264/90000", + map[string]string{ + "packetization-mode": "1", }, - { - "video h264 with a space at the end of rtpmap", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000 ", - }, - }, - }, - &H264{ - PayloadTyp: 96, - }, + }, + { + "video h265", + "video", + 96, + "H265/90000", + map[string]string{ + "sprop-vps": "QAEMAf//AWAAAAMAkAAAAwAAAwB4mZgJ", + "sprop-sps": "QgEBAWAAAAMAkAAAAwAAAwB4oAPAgBDllmZpJMrgEAAAAwAQAAADAeCA", + "sprop-pps": "RAHBcrRiQA==", + "sprop-max-don-diff": "2", }, - { - "video h264 vlc rtsp server", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 packetization-mode=1;profile-level-id=64001f;" + - "sprop-parameter-sets=Z2QAH6zZQFAFuwFsgAAAAwCAAAAeB4wYyw==,aOvjyyLA;", - }, - }, + &H265{ + PayloadTyp: 96, + VPS: []byte{ + 0x40, 0x1, 0xc, 0x1, 0xff, 0xff, 0x1, 0x60, + 0x0, 0x0, 0x3, 0x0, 0x90, 0x0, 0x0, 0x3, + 0x0, 0x0, 0x3, 0x0, 0x78, 0x99, 0x98, 0x9, }, - &H264{ - PayloadTyp: 96, - SPS: []byte{ - 0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50, - 0x05, 0xbb, 0x01, 0x6c, 0x80, 0x00, 0x00, 0x03, - 0x00, 0x80, 0x00, 0x00, 0x1e, 0x07, 0x8c, 0x18, - 0xcb, - }, - PPS: []byte{ - 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0, - }, - PacketizationMode: 1, + SPS: []byte{ + 0x42, 0x1, 0x1, 0x1, 0x60, 0x0, 0x0, 0x3, + 0x0, 0x90, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, + 0x0, 0x78, 0xa0, 0x3, 0xc0, 0x80, 0x10, 0xe5, + 0x96, 0x66, 0x69, 0x24, 0xca, 0xe0, 0x10, 0x0, + 0x0, 0x3, 0x0, 0x10, 0x0, 0x0, 0x3, 0x1, + 0xe0, 0x80, }, + PPS: []byte{ + 0x44, 0x1, 0xc1, 0x72, 0xb4, 0x62, 0x40, + }, + MaxDONDiff: 2, }, - { - "video h264 sprop-parameter-sets with extra data", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 packetization-mode=1; " + - "sprop-parameter-sets=Z2QAKawTMUB4BEfeA+oCAgPgAAADACAAAAZSgA==,aPqPLA==,aF6jzAMA; profile-level-id=640029", - }, - }, - }, - &H264{ - PayloadTyp: 96, - SPS: []byte{ - 0x67, 0x64, 0x00, 0x29, 0xac, 0x13, 0x31, 0x40, - 0x78, 0x04, 0x47, 0xde, 0x03, 0xea, 0x02, 0x02, - 0x03, 0xe0, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, - 0x00, 0x06, 0x52, 0x80, - }, - PPS: []byte{ - 0x68, 0xfa, 0x8f, 0x2c, - }, - PacketizationMode: 1, - }, + "H265/90000", + map[string]string{ + "sprop-vps": "QAEMAf//AWAAAAMAkAAAAwAAAwB4mZgJ", + "sprop-sps": "QgEBAWAAAAMAkAAAAwAAAwB4oAPAgBDllmZpJMrgEAAAAwAQAAADAeCA", + "sprop-pps": "RAHBcrRiQA==", + "sprop-max-don-diff": "2", }, - { - "video h264 empty sprop-parameter-sets", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 packetization-mode=1; sprop-parameter-sets=", - }, - }, - }, - &H264{ - PayloadTyp: 96, - PacketizationMode: 1, - }, + }, + { + "video vp8", + "video", + 96, + "VP8/90000", + map[string]string{ + "max-fr": "123", + "max-fs": "456", }, - { - "video h265", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H265/90000", - }, - { - Key: "fmtp", - Value: "96 sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwB4mZgJ; " + - "sprop-sps=QgEBAWAAAAMAkAAAAwAAAwB4oAPAgBDllmZpJMrgEAAAAwAQAAADAeCA; " + - "sprop-pps=RAHBcrRiQA==; sprop-max-don-diff=2", - }, - }, - }, - &H265{ - PayloadTyp: 96, - VPS: []byte{ - 0x40, 0x1, 0xc, 0x1, 0xff, 0xff, 0x1, 0x60, - 0x0, 0x0, 0x3, 0x0, 0x90, 0x0, 0x0, 0x3, - 0x0, 0x0, 0x3, 0x0, 0x78, 0x99, 0x98, 0x9, - }, - SPS: []byte{ - 0x42, 0x1, 0x1, 0x1, 0x60, 0x0, 0x0, 0x3, - 0x0, 0x90, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, - 0x0, 0x78, 0xa0, 0x3, 0xc0, 0x80, 0x10, 0xe5, - 0x96, 0x66, 0x69, 0x24, 0xca, 0xe0, 0x10, 0x0, - 0x0, 0x3, 0x0, 0x10, 0x0, 0x0, 0x3, 0x1, - 0xe0, 0x80, - }, - PPS: []byte{ - 0x44, 0x1, 0xc1, 0x72, 0xb4, 0x62, 0x40, - }, - MaxDONDiff: 2, - }, + &VP8{ + PayloadTyp: 96, + MaxFR: intPtr(123), + MaxFS: intPtr(456), }, - { - "video vp8", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 VP8/90000", - }, - { - Key: "fmtp", - Value: "96 max-fr=123;max-fs=456", - }, - }, - }, - &VP8{ - PayloadTyp: 96, - MaxFR: intPtr(123), - MaxFS: intPtr(456), - }, + "VP8/90000", + map[string]string{ + "max-fr": "123", + "max-fs": "456", }, - { - "video vp9", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 VP9/90000", - }, - { - Key: "fmtp", - Value: "96 max-fr=123;max-fs=456;profile-id=789", - }, - }, - }, - &VP9{ - PayloadTyp: 96, - MaxFR: intPtr(123), - MaxFS: intPtr(456), - ProfileID: intPtr(789), - }, + }, + { + "video vp9", + "video", + 96, + "VP9/90000", + map[string]string{ + "max-fr": "123", + "max-fs": "456", + "profile-id": "789", }, - { - "application", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "application", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"98"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "98 MetaData/80000", - }, - { - Key: "rtcp-mux", - }, - }, - }, - &Generic{ - PayloadTyp: 98, - RTPMap: "MetaData/80000", - ClockRat: 80000, - }, + &VP9{ + PayloadTyp: 96, + MaxFR: intPtr(123), + MaxFS: intPtr(456), + ProfileID: intPtr(789), }, - { - "application without clock rate", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "application", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"107"}, - }, - }, - &Generic{ - PayloadTyp: 107, - }, + "VP9/90000", + map[string]string{ + "max-fr": "123", + "max-fs": "456", + "profile-id": "789", }, - { - "application invalid rtpmap", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "application", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"98"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "98 custom", - }, - }, - }, - &Generic{ - PayloadTyp: 98, - RTPMap: "custom", - }, + }, + { + "application", + "application", + 98, + "MetaData/80000", + nil, + &Generic{ + PayloadTyp: 98, + RTPMap: "MetaData/80000", + ClockRat: 80000, }, - { - "application invalid rtpmap 2", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "application", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"98"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "98 custom/aaa", - }, - }, - }, - &Generic{ - PayloadTyp: 98, - RTPMap: "custom/aaa", - }, + "MetaData/80000", + nil, + }, + { + "application without clock rate", + "application", + 107, + "", + nil, + &Generic{ + PayloadTyp: 107, }, - { - "application tp-link", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "application", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"smart/1/90000"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "95 TP-LINK/90000", - }, - }, - }, - &Generic{ - PayloadTyp: 95, - RTPMap: "TP-LINK/90000", - ClockRat: 90000, - }, + "", + nil, + }, + { + "application invalid rtpmap", + "application", + 98, + "custom", + nil, + &Generic{ + PayloadTyp: 98, + RTPMap: "custom", }, - } { + "custom", + nil, + }, + { + "application invalid rtpmap 2", + "application", + 98, + "custom/aaa", + nil, + &Generic{ + PayloadTyp: 98, + RTPMap: "custom/aaa", + }, + "custom/aaa", + nil, + }, +} + +func TestUnmarshal(t *testing.T) { + for _, ca := range casesFormat { t.Run(ca.name, func(t *testing.T) { - format, err := Unmarshal(ca.md, ca.md.MediaName.Formats[0]) + dec, err := Unmarshal(ca.mediaType, ca.payloadType, ca.rtpMap, ca.fmtp) require.NoError(t, err) - require.Equal(t, ca.format, format) + require.Equal(t, ca.dec, dec) }) } } -func TestUnmarshalErrors(t *testing.T) { - for _, ca := range []struct { - name string - md *psdp.MediaDescription - err string - }{ - { - "audio lpcm invalid clock", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"97"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 L8/", - }, - }, - }, - "strconv.ParseInt: parsing \"\": invalid syntax", - }, - { - "audio lpcm invalid channels", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"97"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 L8/48000/", - }, - }, - }, - "strconv.ParseInt: parsing \"\": invalid syntax", - }, - { - "audio aac missing config", - &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 is missing", - }, - { - "audio aac invalid config 1", - &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=zz", - }, - }, - }, - "invalid AAC config: zz", - }, - { - "audio 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", - }, - { - "audio aac missing sizelength", - &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=1190", - }, - }, - }, - "sizelength is missing", - }, - { - "audio aac invalid sizelength", - &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; sizelength=aaa", - }, - }, - }, - "invalid AAC SizeLength: aaa", - }, - { - "audio aac invalid indexlength", - &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; indexlength=aaa", - }, - }, - }, - "invalid AAC IndexLength: aaa", - }, - { - "audio aac invalid indexdeltalength", - &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; indexdeltalength=aaa", - }, - }, - }, - "invalid AAC IndexDeltaLength: aaa", - }, - { - "audio vorbis missing configuration", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "audio", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 VORBIS/44100/2", - }, - { - Key: "fmtp", - Value: "96 aa=bb", - }, - }, - }, - "config is missing", - }, - { - "audio 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 clock (48000)", - }, - { - "audio 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", - }, - { - "audio 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", - }, - { - "video h264 invalid sps", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 sprop-parameter-sets=kkk,vvv", - }, - }, - }, - "invalid sprop-parameter-sets (kkk,vvv)", - }, - { - "video h264 invalid pps", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 sprop-parameter-sets=Z2QADKw7ULBLQgAAAwACAAADAD0I,vvv", - }, - }, - }, - "invalid sprop-parameter-sets (Z2QADKw7ULBLQgAAAwACAAADAD0I,vvv)", - }, - { - "video h264 invalid packetization-mode", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 H264/90000", - }, - { - Key: "fmtp", - Value: "96 packetization-mode=aaa", - }, - }, - }, - "invalid packetization-mode (aaa)", - }, - } { +func TestMarshal(t *testing.T) { + for _, ca := range casesFormat { t.Run(ca.name, func(t *testing.T) { - _, err := Unmarshal(ca.md, ca.md.MediaName.Formats[0]) - require.EqualError(t, err, ca.err) + rtpMap, fmtp := ca.dec.Marshal() + require.Equal(t, ca.payloadType, ca.dec.PayloadType()) + require.Equal(t, ca.encRtpMap, rtpMap) + require.Equal(t, ca.encFmtp, fmtp) }) } } + +func FuzzUnmarshalH264(f *testing.F) { + f.Fuzz(func(t *testing.T, sps string, pktMode string) { + Unmarshal("video", 96, "H264/90000", map[string]string{ + "sprop-parameter-sets": sps, + "packetization-mode": pktMode, + }) + }) +} + +func FuzzUnmarshalH265(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b, c, d string) { + Unmarshal("video", 96, "H265/90000", map[string]string{ + "sprop-vps": a, + "sprop-sps": b, + "sprop-pps": c, + "sprop-max-don-diff": d, + }) + }) +} + +func FuzzUnmarshalLPCM(f *testing.F) { + f.Fuzz(func(t *testing.T, a string) { + Unmarshal("audio", 96, "L16/"+a, nil) + }) +} + +func FuzzUnmarshalMPEG4AudioGeneric(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b, c, d string) { + Unmarshal("audio", 96, "MPEG4-generic/48000/2", map[string]string{ + "config": a, + "sizelength": b, + "indexlength": c, + "indexdeltalength": d, + }) + }) +} + +func FuzzUnmarshalMPEG4AudioLATM(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b, c, d, e, f string) { + Unmarshal("audio", 96, "MP4A-LATM/48000/2", map[string]string{ + "profile-level-id": a, + "bitrate": b, + "object": c, + "cpresent": d, + "config": e, + "sbr-enabled": f, + }) + }) +} + +func FuzzUnmarshalMPEG4VideoES(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b string) { + Unmarshal("video", 96, "MP4V-ES/90000", map[string]string{ + "profile-level-id": a, + "config": b, + }) + }) +} + +func FuzzUnmarshalOpus(f *testing.F) { + f.Add("48000/a") + + f.Fuzz(func(t *testing.T, a string) { + Unmarshal("audio", 96, "Opus/"+a, nil) + }) +} + +func FuzzUnmarshalVorbis(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b string) { + Unmarshal("audio", 96, "Vorbis/"+a, map[string]string{ + "configuration": b, + }) + }) +} + +func FuzzUnmarshalVP8(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b string) { + Unmarshal("video", 96, "VP8/90000", map[string]string{ + "max-fr": a, + "max-fs": b, + }) + }) +} + +func FuzzUnmarshalVP9(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b, c string) { + Unmarshal("video", 96, "VP9/90000", map[string]string{ + "max-fr": a, + "max-fs": b, + "profile-id": c, + }) + }) +} diff --git a/pkg/formats/g711_test.go b/pkg/formats/g711_test.go index fa07c9e1..63fd51cc 100644 --- a/pkg/formats/g711_test.go +++ b/pkg/formats/g711_test.go @@ -22,26 +22,6 @@ func TestG711Attributes(t *testing.T) { 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) { format := &G711{} diff --git a/pkg/formats/g722_test.go b/pkg/formats/g722_test.go index 18b7b635..c51f40b8 100644 --- a/pkg/formats/g722_test.go +++ b/pkg/formats/g722_test.go @@ -15,14 +15,6 @@ func TestG722Attributes(t *testing.T) { 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) { format := &G722{} diff --git a/pkg/formats/generic_test.go b/pkg/formats/generic_test.go index 07d0aeb9..7ccec9b6 100644 --- a/pkg/formats/generic_test.go +++ b/pkg/formats/generic_test.go @@ -26,27 +26,3 @@ func TestGenericAttributes(t *testing.T) { require.Equal(t, uint8(98), format.PayloadType()) 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) -} diff --git a/pkg/formats/h264_test.go b/pkg/formats/h264_test.go index c174d28d..4bd32c7d 100644 --- a/pkg/formats/h264_test.go +++ b/pkg/formats/h264_test.go @@ -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) { format := &H264{} diff --git a/pkg/formats/h265_test.go b/pkg/formats/h265_test.go index fcc2117b..8f4e5747 100644 --- a/pkg/formats/h265_test.go +++ b/pkg/formats/h265_test.go @@ -32,23 +32,6 @@ func TestH265Attributes(t *testing.T) { 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) { format := &H265{} diff --git a/pkg/formats/lpcm_test.go b/pkg/formats/lpcm_test.go index dc31a636..246d77a1 100644 --- a/pkg/formats/lpcm_test.go +++ b/pkg/formats/lpcm_test.go @@ -1,8 +1,6 @@ package formats import ( - "fmt" - "strconv" "testing" "github.com/pion/rtp" @@ -22,23 +20,6 @@ func TestLPCMAttributes(t *testing.T) { 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) { format := &LPCM{ PayloadTyp: 96, diff --git a/pkg/formats/mjpeg_test.go b/pkg/formats/mjpeg_test.go index b0242f93..b733192b 100644 --- a/pkg/formats/mjpeg_test.go +++ b/pkg/formats/mjpeg_test.go @@ -15,14 +15,6 @@ func TestMJPEGAttributes(t *testing.T) { 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) { format := &MJPEG{} diff --git a/pkg/formats/mpeg2_audio_test.go b/pkg/formats/mpeg2_audio_test.go index cc6bccb5..5b8fc986 100644 --- a/pkg/formats/mpeg2_audio_test.go +++ b/pkg/formats/mpeg2_audio_test.go @@ -14,11 +14,3 @@ func TestMPEG2AudioAttributes(t *testing.T) { require.Equal(t, uint8(14), format.PayloadType()) 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) -} diff --git a/pkg/formats/mpeg2_video_test.go b/pkg/formats/mpeg2_video_test.go index 3fa83833..ce054d8c 100644 --- a/pkg/formats/mpeg2_video_test.go +++ b/pkg/formats/mpeg2_video_test.go @@ -14,11 +14,3 @@ func TestMPEG2VideoAttributes(t *testing.T) { require.Equal(t, uint8(32), format.PayloadType()) 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) -} diff --git a/pkg/formats/mpeg4_audio_generic_test.go b/pkg/formats/mpeg4_audio_generic_test.go index 0feabb6d..3759be26 100644 --- a/pkg/formats/mpeg4_audio_generic_test.go +++ b/pkg/formats/mpeg4_audio_generic_test.go @@ -27,31 +27,6 @@ func TestMPEG4AudioGenericAttributes(t *testing.T) { 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) { format := &MPEG4AudioGeneric{ PayloadTyp: 96, diff --git a/pkg/formats/mpeg4_audio_latm.go b/pkg/formats/mpeg4_audio_latm.go index 1e118320..f34d4434 100644 --- a/pkg/formats/mpeg4_audio_latm.go +++ b/pkg/formats/mpeg4_audio_latm.go @@ -147,7 +147,7 @@ func (f *MPEG4AudioLATM) Marshal() (string, map[string]string) { } if f.SBREnabled != nil { - if *f.CPresent { + if *f.SBREnabled { fmtp["SBR-enabled"] = "1" } else { fmtp["SBR-enabled"] = "0" diff --git a/pkg/formats/mpeg4_audio_latm_test.go b/pkg/formats/mpeg4_audio_latm_test.go index d59ba352..45ddd32e 100644 --- a/pkg/formats/mpeg4_audio_latm_test.go +++ b/pkg/formats/mpeg4_audio_latm_test.go @@ -21,22 +21,3 @@ func TestMPEG4AudioLATMAttributes(t *testing.T) { require.Equal(t, uint8(96), format.PayloadType()) 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) -} diff --git a/pkg/formats/mpeg4_video_es_test.go b/pkg/formats/mpeg4_video_es_test.go index 9899e7c3..d3c0104f 100644 --- a/pkg/formats/mpeg4_video_es_test.go +++ b/pkg/formats/mpeg4_video_es_test.go @@ -18,18 +18,3 @@ func TestMPEG4VideoESAttributes(t *testing.T) { require.Equal(t, uint8(96), format.PayloadType()) 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) -} diff --git a/pkg/formats/opus.go b/pkg/formats/opus.go index 1d0dcbf4..45226a5b 100644 --- a/pkg/formats/opus.go +++ b/pkg/formats/opus.go @@ -43,18 +43,12 @@ func (f *Opus) unmarshal(payloadType uint8, clock string, codec string, rtpmap s } sampleRate, err := strconv.ParseInt(tmp[0], 10, 64) - if err != nil { - return err - } - if sampleRate != 48000 { + if err != nil || sampleRate != 48000 { return fmt.Errorf("invalid sample rate: %d", sampleRate) } channelCount, err := strconv.ParseInt(tmp[1], 10, 64) - if err != nil { - return err - } - if channelCount != 2 { + if err != nil || channelCount != 2 { return fmt.Errorf("invalid channel count: %d", channelCount) } diff --git a/pkg/formats/opus_test.go b/pkg/formats/opus_test.go index 556ed63e..964a49a2 100644 --- a/pkg/formats/opus_test.go +++ b/pkg/formats/opus_test.go @@ -18,19 +18,6 @@ func TestOpusAttributes(t *testing.T) { 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) { format := &Opus{} diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/708789f132a57d65 b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/708789f132a57d65 new file mode 100644 index 00000000..5850a161 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/708789f132a57d65 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0,") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/85649d45641911d0 b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/85649d45641911d0 new file mode 100644 index 00000000..befd7df0 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/85649d45641911d0 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0") +string("") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/d0410f4fd49cc1b8 b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/d0410f4fd49cc1b8 new file mode 100644 index 00000000..ddcaaafb --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH264/d0410f4fd49cc1b8 @@ -0,0 +1,3 @@ +go test fuzz v1 +string(",0") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/09376da4826c9233 b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/09376da4826c9233 new file mode 100644 index 00000000..00d8cdcc --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/09376da4826c9233 @@ -0,0 +1,5 @@ +go test fuzz v1 +string("") +string("") +string("") +string("A") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/63e3b0a8cb682bdf b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/63e3b0a8cb682bdf new file mode 100644 index 00000000..92483d23 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/63e3b0a8cb682bdf @@ -0,0 +1,5 @@ +go test fuzz v1 +string("0") +string("0") +string("") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/fae5e048802aad6c b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/fae5e048802aad6c new file mode 100644 index 00000000..22e048dc --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalH265/fae5e048802aad6c @@ -0,0 +1,5 @@ +go test fuzz v1 +string("") +string("") +string("0") +string("") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/4e36b64da83067c2 b/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/4e36b64da83067c2 new file mode 100644 index 00000000..da98342e --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/4e36b64da83067c2 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0/") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/e9e3ffbe3b3a072c b/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/e9e3ffbe3b3a072c new file mode 100644 index 00000000..9d842673 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalLPCM/e9e3ffbe3b3a072c @@ -0,0 +1,2 @@ +go test fuzz v1 +string("A") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/2898e764696b0420 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/2898e764696b0420 new file mode 100644 index 00000000..9c70a86d --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/2898e764696b0420 @@ -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") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/47164857eaf9f343 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/47164857eaf9f343 new file mode 100644 index 00000000..e5ce1e0f --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/47164857eaf9f343 @@ -0,0 +1,5 @@ +go test fuzz v1 +string("0") +string("") +string("") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/6775f71697eec2db b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/6775f71697eec2db new file mode 100644 index 00000000..ceca43c3 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/6775f71697eec2db @@ -0,0 +1,5 @@ +go test fuzz v1 +string("") +string("") +string("") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/76b36b8b3c310afb b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/76b36b8b3c310afb new file mode 100644 index 00000000..8d6f0d54 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioGeneric/76b36b8b3c310afb @@ -0,0 +1,5 @@ +go test fuzz v1 +string("") +string("\f3r\x05\x00\xe7\xe8") +string("2\xf5\xe13") +string("7") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/3570da660901e702 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/3570da660901e702 new file mode 100644 index 00000000..2587b1e7 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/3570da660901e702 @@ -0,0 +1,7 @@ +go test fuzz v1 +string("%20") +string("") +string("oc\x97\xf3V\x8e\xe0") +string("") +string("\x0f\x11") +string("Z") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/400020fb8be5431f b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/400020fb8be5431f new file mode 100644 index 00000000..ef04c2ae --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/400020fb8be5431f @@ -0,0 +1,7 @@ +go test fuzz v1 +string("") +string("") +string("") +string("") +string("") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/b95227f0e71f8fa1 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/b95227f0e71f8fa1 new file mode 100644 index 00000000..321e624d --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/b95227f0e71f8fa1 @@ -0,0 +1,7 @@ +go test fuzz v1 +string("") +string("") +string("oc\x97\xf3V\x8e\xe0") +string("") +string("9A") +string("X") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/c440c66d0e8a2f04 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/c440c66d0e8a2f04 new file mode 100644 index 00000000..e61f676a --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4AudioLATM/c440c66d0e8a2f04 @@ -0,0 +1,7 @@ +go test fuzz v1 +string("") +string("") +string("oc\x97\xf3V\x8e\xe0") +string("") +string("\x0f\x11") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/00e15d22123489fd b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/00e15d22123489fd new file mode 100644 index 00000000..b0e98fbe --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/00e15d22123489fd @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/aa424148ecba23f1 b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/aa424148ecba23f1 new file mode 100644 index 00000000..b2fff3ef --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalMPEG4VideoES/aa424148ecba23f1 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("A") +string("") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/771e938e4458e983 b/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/771e938e4458e983 new file mode 100644 index 00000000..ee3f3399 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/771e938e4458e983 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/9cd9b70960b4a733 b/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/9cd9b70960b4a733 new file mode 100644 index 00000000..e4946606 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalOpus/9cd9b70960b4a733 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("/") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/85649d45641911d0 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/85649d45641911d0 new file mode 100644 index 00000000..befd7df0 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/85649d45641911d0 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0") +string("") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/aa424148ecba23f1 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/aa424148ecba23f1 new file mode 100644 index 00000000..b2fff3ef --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP8/aa424148ecba23f1 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("A") +string("") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/4c51a06c202d48d0 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/4c51a06c202d48d0 new file mode 100644 index 00000000..2a773e70 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/4c51a06c202d48d0 @@ -0,0 +1,4 @@ +go test fuzz v1 +string("1") +string("") +string("#") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/54e9dd922dda87eb b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/54e9dd922dda87eb new file mode 100644 index 00000000..bc602bfa --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/54e9dd922dda87eb @@ -0,0 +1,4 @@ +go test fuzz v1 +string("") +string("") +string("1") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/671f05b72e69d643 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/671f05b72e69d643 new file mode 100644 index 00000000..5e5e5bfb --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVP9/671f05b72e69d643 @@ -0,0 +1,4 @@ +go test fuzz v1 +string("") +string("") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/199c2c84b007b8d1 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/199c2c84b007b8d1 new file mode 100644 index 00000000..8798f740 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/199c2c84b007b8d1 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("/") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/1e2e9f272753060b b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/1e2e9f272753060b new file mode 100644 index 00000000..d1de66a1 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/1e2e9f272753060b @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0/0") +string("0") diff --git a/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/70149aff1d6b6142 b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/70149aff1d6b6142 new file mode 100644 index 00000000..a7213182 --- /dev/null +++ b/pkg/formats/testdata/fuzz/FuzzUnmarshalVorbis/70149aff1d6b6142 @@ -0,0 +1,3 @@ +go test fuzz v1 +string("0/") +string("0") diff --git a/pkg/formats/vorbis.go b/pkg/formats/vorbis.go index d27b89f2..6e5cd08f 100644 --- a/pkg/formats/vorbis.go +++ b/pkg/formats/vorbis.go @@ -57,7 +57,7 @@ func (f *Vorbis) unmarshal(payloadType uint8, clock string, codec string, rtpmap if key == "configuration" { conf, err := base64.StdEncoding.DecodeString(val) if err != nil { - return fmt.Errorf("invalid AAC config (%v)", val) + return fmt.Errorf("invalid config: %v", val) } f.Configuration = conf diff --git a/pkg/formats/vorbis_test.go b/pkg/formats/vorbis_test.go index ed08aec0..d98c5fba 100644 --- a/pkg/formats/vorbis_test.go +++ b/pkg/formats/vorbis_test.go @@ -19,18 +19,3 @@ func TestVorbisAttributes(t *testing.T) { require.Equal(t, uint8(96), format.PayloadType()) 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) -} diff --git a/pkg/formats/vp8.go b/pkg/formats/vp8.go index 685283c5..f02e9297 100644 --- a/pkg/formats/vp8.go +++ b/pkg/formats/vp8.go @@ -40,16 +40,18 @@ func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap st case "max-fr": n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("invalid max-fr (%v)", val) + return fmt.Errorf("invalid max-fr: %v", val) } + v2 := int(n) f.MaxFR = &v2 case "max-fs": n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("invalid max-fs (%v)", val) + return fmt.Errorf("invalid max-fs: %v", val) } + v2 := int(n) f.MaxFS = &v2 } @@ -61,9 +63,11 @@ func (f *VP8) unmarshal(payloadType uint8, clock string, codec string, rtpmap st // Marshal implements Format. func (f *VP8) Marshal() (string, map[string]string) { fmtp := make(map[string]string) + if f.MaxFR != nil { fmtp["max-fr"] = strconv.FormatInt(int64(*f.MaxFR), 10) } + if f.MaxFS != nil { fmtp["max-fs"] = strconv.FormatInt(int64(*f.MaxFS), 10) } diff --git a/pkg/formats/vp8_test.go b/pkg/formats/vp8_test.go index ade49020..f54182cb 100644 --- a/pkg/formats/vp8_test.go +++ b/pkg/formats/vp8_test.go @@ -1,4 +1,4 @@ -package formats +package formats //nolint:dupl import ( "testing" @@ -17,23 +17,6 @@ func TestVP8ttributes(t *testing.T) { 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) { format := &VP8{} diff --git a/pkg/formats/vp9.go b/pkg/formats/vp9.go index 7f9e047f..4ed8cb97 100644 --- a/pkg/formats/vp9.go +++ b/pkg/formats/vp9.go @@ -41,24 +41,27 @@ func (f *VP9) unmarshal(payloadType uint8, clock string, codec string, rtpmap st case "max-fr": n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("invalid max-fr (%v)", val) + return fmt.Errorf("invalid max-fr: %v", val) } + v2 := int(n) f.MaxFR = &v2 case "max-fs": n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("invalid max-fs (%v)", val) + return fmt.Errorf("invalid max-fs: %v", val) } + v2 := int(n) f.MaxFS = &v2 case "profile-id": n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("invalid profile-id (%v)", val) + return fmt.Errorf("invalid profile-id: %v", val) } + v2 := int(n) f.ProfileID = &v2 } diff --git a/pkg/formats/vp9_test.go b/pkg/formats/vp9_test.go index 0b80bc90..202f52c7 100644 --- a/pkg/formats/vp9_test.go +++ b/pkg/formats/vp9_test.go @@ -1,4 +1,4 @@ -package formats +package formats //nolint:dupl import ( "testing" @@ -17,26 +17,6 @@ func TestVP9Attributes(t *testing.T) { 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) { format := &VP9{} diff --git a/pkg/media/media.go b/pkg/media/media.go index f39e078f..22fc86e5 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -39,6 +39,45 @@ func getDirection(attributes []psdp.Attribute) Direction { 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. type Direction string @@ -82,7 +121,28 @@ func (m *Media) unmarshal(md *psdp.MediaDescription) error { m.Formats = nil 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 { return err } diff --git a/pkg/media/medias_test.go b/pkg/media/medias_test.go index ff61c0ec..8fb0580f 100644 --- a/pkg/media/medias_test.go +++ b/pkg/media/medias_test.go @@ -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) {