package description import ( "testing" "github.com/stretchr/testify/require" "github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/sdp" ) var casesSession = []struct { name string in string out string desc Session }{ { "one format for each media, absolute", "v=0\r\n" + "o=- 0 0 IN IP4 10.0.0.131\r\n" + "s=Media Presentation\r\n" + "i=samsung\r\n" + "c=IN IP4 0.0.0.0\r\n" + "b=AS:2632\r\n" + "t=0 0\r\n" + "a=control:rtsp://10.0.100.50/profile5/media.smp\r\n" + "a=range:npt=now-\r\n" + "m=video 42504 RTP/AVP 97\r\n" + "b=AS:2560\r\n" + "a=rtpmap:97 H264/90000\r\n" + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=v\r\n" + "a=cliprect:0,0,1080,1920\r\n" + "a=framesize:97 1920-1080\r\n" + "a=framerate:30.0\r\n" + "a=fmtp:97 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + "m=audio 42506 RTP/AVP 0\r\n" + "b=AS:64\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=a\r\n" + "a=recvonly\r\n" + "m=application 42508 RTP/AVP 107\r\n" + "b=AS:8\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=Media Presentation\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 97\r\n" + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=v\r\n" + "a=rtpmap:97 H264/90000\r\n" + "a=fmtp:97 packetization-mode=1; profile-level-id=640028; sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=a\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=application 0 RTP/AVP 107\r\n" + "a=control\r\n", Session{ Title: `Media Presentation`, Medias: []*Media{ { Type: MediaTypeVideo, Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", Formats: []format.Format{&format.H264{ PayloadTyp: 97, PacketizationMode: 1, SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, }}, }, { Type: MediaTypeAudio, Control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", Formats: []format.Format{&format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }}, }, { Type: MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 107, }}, }, }, }, }, { "one format for each media, relative", "v=0\r\n" + "o=- 0 0 IN IP4 10.0.0.131\r\n" + "s=Media Presentation\r\n" + "i=samsung\r\n" + "c=IN IP4 0.0.0.0\r\n" + "b=AS:2632\r\n" + "t=0 0\r\n" + "a=range:npt=now-\r\n" + "m=video 42504 RTP/AVP 97\r\n" + "b=AS:2560\r\n" + "a=rtpmap:97 H264/90000\r\n" + "a=control:trackID=1\r\n" + "a=cliprect:0,0,1080,1920\r\n" + "a=framesize:97 1920-1080\r\n" + "a=framerate:30.0\r\n" + "a=fmtp:97 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + "m=audio 42506 RTP/AVP 0\r\n" + "b=AS:64\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=control:trackID=2\r\n" + "a=recvonly\r\n" + "m=application 42508 RTP/AVP 107\r\n" + "b=AS:8\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=Media Presentation\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 97\r\n" + "a=control:trackID=1\r\n" + "a=rtpmap:97 H264/90000\r\n" + "a=fmtp:97 packetization-mode=1; profile-level-id=640028; sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=control:trackID=2\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=application 0 RTP/AVP 107\r\n" + "a=control\r\n", Session{ Title: `Media Presentation`, Medias: []*Media{ { Type: MediaTypeVideo, Control: "trackID=1", Formats: []format.Format{&format.H264{ PayloadTyp: 97, PacketizationMode: 1, SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, }}, }, { Type: MediaTypeAudio, Control: "trackID=2", Formats: []format.Format{&format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }}, }, { Type: MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 107, }}, }, }, }, }, { "multiple formats for each media", "v=0\r\n" + "o=- 4158123474391860926 2 IN IP4 127.0.0.1\r\n" + "s= \r\n" + "t=0 0\r\n" + "a=group:BUNDLE audio video\r\n" + "a=msid-semantic: WMS mediaSessionLocal\r\n" + "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:0D6Y\r\n" + "a=ice-pwd:V3YEqLGAJJhUDUa13C/pKbWe\r\n" + "a=ice-options:trickle renomination\r\n" + "a=fingerprint:sha-256" + " 5E:B5:97:8B:B4:D8:AE:2B:89:F6:82:44:47:69:77:83:05:29:C5:C8:EE:67:50:C3:77:6B:A7:BA:10:E3:08:B8\r\n" + "a=setup:actpass\r\n" + "a=mid:audio\r\n" + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" + "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" + "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" + "a=sendonly\r\n" + "a=rtcp-mux\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=rtcp-fb:111 transport-cc\r\n" + "a=fmtp:111 minptime=10;useinbandfec=1\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:104 ISAC/32000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:102 ILBC/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:106 CN/32000\r\n" + "a=rtpmap:105 CN/16000\r\n" + "a=rtpmap:13 CN/8000\r\n" + "a=rtpmap:110 telephone-event/48000\r\n" + "a=rtpmap:112 telephone-event/32000\r\n" + "a=rtpmap:113 telephone-event/16000\r\n" + "a=rtpmap:126 telephone-event/8000\r\n" + "a=ssrc:3754810229 cname:CvU1TYqkVsjj5XOt\r\n" + "a=ssrc:3754810229 msid:mediaSessionLocal 101\r\n" + "a=ssrc:3754810229 mslabel:mediaSessionLocal\r\n" + "a=ssrc:3754810229 label:101\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 124 125\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:0D6Y\r\n" + "a=ice-pwd:V3YEqLGAJJhUDUa13C/pKbWe\r\n" + "a=ice-options:trickle renomination\r\n" + "a=fingerprint:sha-256" + " 5E:B5:97:8B:B4:D8:AE:2B:89:F6:82:44:47:69:77:83:05:29:C5:C8:EE:67:50:C3:77:6B:A7:BA:10:E3:08:B8\r\n" + "a=setup:actpass\r\n" + "a=mid:video\r\n" + "a=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\n" + "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" + "a=extmap:13 urn:3gpp:video-orientation\r\n" + "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" + "a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" + "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\n" + "a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\n" + "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\n" + "a=sendonly\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=rtcp-fb:96 goog-remb\r\n" + "a=rtcp-fb:96 transport-cc\r\n" + "a=rtcp-fb:96 ccm fir\r\n" + "a=rtcp-fb:96 nack\r\n" + "a=rtcp-fb:96 nack pli\r\n" + "a=rtpmap:97 rtx/90000\r\n" + "a=fmtp:97 apt=96\r\n" + "a=rtpmap:98 VP9/90000\r\n" + "a=rtcp-fb:98 goog-remb\r\n" + "a=rtcp-fb:98 transport-cc\r\n" + "a=rtcp-fb:98 ccm fir\r\n" + "a=rtcp-fb:98 nack\r\n" + "a=rtcp-fb:98 nack pli\r\n" + "a=rtpmap:99 rtx/90000\r\n" + "a=fmtp:99 apt=98\r\n" + "a=rtpmap:100 H264/90000\r\n" + "a=rtcp-fb:100 goog-remb\r\n" + "a=rtcp-fb:100 transport-cc\r\n" + "a=rtcp-fb:100 ccm fir\r\n" + "a=rtcp-fb:100 nack\r\n" + "a=rtcp-fb:100 nack pli\r\n" + "a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n" + "a=rtpmap:101 rtx/90000\r\n" + "a=fmtp:101 apt=100\r\n" + "a=rtpmap:127 red/90000\r\n" + "a=rtpmap:124 rtx/90000\r\n" + "a=fmtp:124 apt=127\r\n" + "a=rtpmap:125 ulpfec/90000\r\n" + "a=ssrc-group:FID 2712436124 1733091158\r\n" + "a=ssrc:2712436124 cname:CvU1TYqkVsjj5XOt\r\n" + "a=ssrc:2712436124 msid:mediaSessionLocal 100\r\n" + "a=ssrc:2712436124 mslabel:mediaSessionLocal\r\n" + "a=ssrc:2712436124 label:100\r\n" + "a=ssrc:1733091158 cname:CvU1TYqkVsjj5XOt\r\n" + "a=ssrc:1733091158 msid:mediaSessionLocal 100\r\n" + "a=ssrc:1733091158 mslabel:mediaSessionLocal\r\n" + "a=ssrc:1733091158 label:100\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s= \r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=audio 0 RTP/AVP 111 103 104 9 102 0 8 106 105 13 110 112 113 126\r\n" + "a=mid:audio\r\n" + "a=sendonly\r\n" + "a=control\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=fmtp:111 sprop-stereo=0\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:104 ISAC/32000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:102 ILBC/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:106 CN/32000\r\n" + "a=rtpmap:105 CN/16000\r\n" + "a=rtpmap:13 CN/8000\r\n" + "a=rtpmap:110 telephone-event/48000\r\n" + "a=rtpmap:112 telephone-event/32000\r\n" + "a=rtpmap:113 telephone-event/16000\r\n" + "a=rtpmap:126 telephone-event/8000\r\n" + "m=video 0 RTP/AVP 96 97 98 99 100 101 127 124 125\r\n" + "a=mid:video\r\n" + "a=sendonly\r\n" + "a=control\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=rtpmap:97 rtx/90000\r\n" + "a=fmtp:97 apt=96\r\n" + "a=rtpmap:98 VP9/90000\r\n" + "a=rtpmap:99 rtx/90000\r\n" + "a=fmtp:99 apt=98\r\n" + "a=rtpmap:100 H264/90000\r\n" + "a=fmtp:100 packetization-mode=1\r\n" + "a=rtpmap:101 rtx/90000\r\n" + "a=fmtp:101 apt=100\r\n" + "a=rtpmap:127 red/90000\r\n" + "a=rtpmap:124 rtx/90000\r\n" + "a=fmtp:124 apt=127\r\na=rtpmap:125 ulpfec/90000\r\n", Session{ Title: ``, Medias: []*Media{ { ID: "audio", Type: MediaTypeAudio, IsBackChannel: true, Formats: []format.Format{ &format.Opus{ PayloadTyp: 111, IsStereo: false, }, &format.Generic{ PayloadTyp: 103, RTPMa: "ISAC/16000", ClockRat: 16000, }, &format.Generic{ PayloadTyp: 104, RTPMa: "ISAC/32000", ClockRat: 32000, }, &format.G722{}, &format.Generic{ PayloadTyp: 102, RTPMa: "ILBC/8000", ClockRat: 8000, }, &format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }, &format.G711{ PayloadTyp: 8, MULaw: false, SampleRate: 8000, ChannelCount: 1, }, &format.Generic{ PayloadTyp: 106, RTPMa: "CN/32000", ClockRat: 32000, }, &format.Generic{ PayloadTyp: 105, RTPMa: "CN/16000", ClockRat: 16000, }, &format.Generic{ PayloadTyp: 13, RTPMa: "CN/8000", ClockRat: 8000, }, &format.Generic{ PayloadTyp: 110, RTPMa: "telephone-event/48000", ClockRat: 48000, }, &format.Generic{ PayloadTyp: 112, RTPMa: "telephone-event/32000", ClockRat: 32000, }, &format.Generic{ PayloadTyp: 113, RTPMa: "telephone-event/16000", ClockRat: 16000, }, &format.Generic{ PayloadTyp: 126, RTPMa: "telephone-event/8000", ClockRat: 8000, }, }, }, { ID: "video", Type: MediaTypeVideo, IsBackChannel: true, Formats: []format.Format{ &format.VP8{ PayloadTyp: 96, }, &format.Generic{ PayloadTyp: 97, RTPMa: "rtx/90000", FMT: map[string]string{ "apt": "96", }, ClockRat: 90000, }, &format.VP9{ PayloadTyp: 98, }, &format.Generic{ PayloadTyp: 99, RTPMa: "rtx/90000", FMT: map[string]string{ "apt": "98", }, ClockRat: 90000, }, &format.H264{ PayloadTyp: 100, PacketizationMode: 1, }, &format.Generic{ PayloadTyp: 101, RTPMa: "rtx/90000", FMT: map[string]string{ "apt": "100", }, ClockRat: 90000, }, &format.Generic{ PayloadTyp: 127, RTPMa: "red/90000", ClockRat: 90000, }, &format.Generic{ PayloadTyp: 124, RTPMa: "rtx/90000", FMT: map[string]string{ "apt": "127", }, ClockRat: 90000, }, &format.Generic{ PayloadTyp: 125, RTPMa: "ulpfec/90000", ClockRat: 90000, }, }, }, }, }, }, { "multiple formats for each media 2", "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 98\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=rtpmap:98 MetaData\r\n" + "a=rtcp-mux\r\n" + "a=fmtp:96 packetization-mode=1;profile-level-id=4d002a;" + "sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAgQA==,aO48gA==\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 96 98\r\n" + "a=control\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=fmtp:96 packetization-mode=1; profile-level-id=4D002A; " + "sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAgQA==,aO48gA==\r\n" + "a=rtpmap:98 MetaData\r\n", Session{ Title: `-`, Medias: []*Media{ { Type: MediaTypeVideo, Formats: []format.Format{ &format.H264{ PayloadTyp: 96, SPS: []byte{ 0x67, 0x4d, 0x00, 0x2a, 0x9d, 0xa8, 0x1e, 0x00, 0x89, 0xf9, 0x66, 0xe0, 0x20, 0x20, 0x20, 0x40, }, PPS: []byte{0x68, 0xee, 0x3c, 0x80}, PacketizationMode: 1, }, &format.Generic{ PayloadTyp: 98, RTPMa: "MetaData", }, }, }, }, }, }, { "onvif back channel", "v=0\r\n" + "o= 2890842807 IN IP4 192.168.0.1\r\n" + "s=RTSP Session with audiobackchannel\r\n" + "m=video 0 RTP/AVP 26\r\n" + "a=control:rtsp://192.168.0.1/video\r\n" + "a=recvonly\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=control:rtsp://192.168.0.1/audio\r\n" + "a=recvonly\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=control:rtsp://192.168.0.1/audioback\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendonly\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=RTSP Session with audiobackchannel\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 26\r\n" + "a=control:rtsp://192.168.0.1/video\r\n" + "a=rtpmap:26 JPEG/90000\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=control:rtsp://192.168.0.1/audio\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=sendonly\r\n" + "a=control:rtsp://192.168.0.1/audioback\r\n" + "a=rtpmap:0 PCMU/8000\r\n", Session{ Title: `RTSP Session with audiobackchannel`, Medias: []*Media{ { Type: MediaTypeVideo, Control: "rtsp://192.168.0.1/video", Formats: []format.Format{&format.MJPEG{}}, }, { Type: MediaTypeAudio, Control: "rtsp://192.168.0.1/audio", Formats: []format.Format{&format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }}, }, { Type: MediaTypeAudio, IsBackChannel: true, Control: "rtsp://192.168.0.1/audioback", Formats: []format.Format{&format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }}, }, }, }, }, { "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/TP-LINK 0 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=-\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=application/TP-LINK 0 RTP/AVP 95\r\n" + "a=control\r\n" + "a=rtpmap:95 TP-LINK/90000\r\n", Session{ Title: `-`, Medias: []*Media{ { Type: "application/TP-LINK", Formats: []format.Format{&format.Generic{ PayloadTyp: 95, RTPMa: "TP-LINK/90000", ClockRat: 90000, }}, }, }, }, }, { "mercury", "v=0\n" + "o=- 14665860 31787219 1 IN IP4 192.168.0.60\n" + "s=Session streamed by \"MERCURY RTSP Server\"\n" + "t=0 0\n" + "a=smart_encoder:virtualIFrame=1\n" + "m=application/MERCURY 0 RTP/AVP smart/1/90000\n" + "a=rtpmap:95 MERCURY/90000\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=Session streamed by \"MERCURY RTSP Server\"\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "m=application/MERCURY 0 RTP/AVP 95\r\n" + "a=control\r\n" + "a=rtpmap:95 MERCURY/90000\r\n", Session{ Title: `Session streamed by "MERCURY RTSP Server"`, Medias: []*Media{ { Type: "application/MERCURY", Formats: []format.Format{&format.Generic{ PayloadTyp: 95, RTPMa: "MERCURY/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=-\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", Session{ Title: "-", Medias: []*Media{ { Type: MediaTypeVideo, Formats: []format.Format{ &format.H264{ PayloadTyp: 96, PacketizationMode: 1, }, }, }, }, }, }, { "ulpfec rfc5109", "v=0\r\n" + "o=adam 289083124 289083124 IN IP4 host.example.com\r\n" + "s=ULP FEC Seminar\r\n" + "t=0 0\r\n" + "c=IN IP4 224.2.17.12/127\r\n" + "a=group:FEC 1 2\r\n" + "a=group:FEC 3 4\r\n" + "m=audio 30000 RTP/AVP 0\r\n" + "a=mid:1\r\n" + "m=application 30002 RTP/AVP 100\r\n" + "a=rtpmap:100 ulpfec/8000\r\n" + "a=mid:2\r\n" + "m=video 30004 RTP/AVP 31\r\n" + "a=mid:3\r\n" + "m=application 30004 RTP/AVP 101\r\n" + "c=IN IP4 224.2.17.13/127\r\n" + "a=rtpmap:101 ulpfec/8000\r\n" + "a=mid:4\r\n", "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=ULP FEC Seminar\r\n" + "c=IN IP4 0.0.0.0\r\n" + "t=0 0\r\n" + "a=group:FEC 1 2\r\n" + "a=group:FEC 3 4\r\n" + "m=audio 0 RTP/AVP 0\r\n" + "a=mid:1\r\n" + "a=control\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "m=application 0 RTP/AVP 100\r\n" + "a=mid:2\r\n" + "a=control\r\n" + "a=rtpmap:100 ulpfec/8000\r\n" + "m=video 0 RTP/AVP 31\r\n" + "a=mid:3\r\n" + "a=control\r\n" + "m=application 0 RTP/AVP 101\r\n" + "a=mid:4\r\n" + "a=control\r\n" + "a=rtpmap:101 ulpfec/8000\r\n", Session{ Title: "ULP FEC Seminar", FECGroups: []SessionFECGroup{ {"1", "2"}, {"3", "4"}, }, Medias: []*Media{ { ID: "1", Type: MediaTypeAudio, Formats: []format.Format{&format.G711{ PayloadTyp: 0, MULaw: true, SampleRate: 8000, ChannelCount: 1, }}, }, { ID: "2", Type: MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 100, RTPMa: "ulpfec/8000", ClockRat: 8000, }}, }, { ID: "3", Type: MediaTypeVideo, Formats: []format.Format{&format.Generic{ PayloadTyp: 31, ClockRat: 90000, }}, }, { ID: "4", Type: MediaTypeApplication, Formats: []format.Format{&format.Generic{ PayloadTyp: 101, RTPMa: "ulpfec/8000", ClockRat: 8000, }}, }, }, }, }, } func TestSessionUnmarshal(t *testing.T) { for _, ca := range casesSession { t.Run(ca.name, func(t *testing.T) { var sdp sdp.SessionDescription err := sdp.Unmarshal([]byte(ca.in)) require.NoError(t, err) var desc Session err = desc.Unmarshal(&sdp) require.NoError(t, err) require.Equal(t, ca.desc, desc) }) } } func TestSessionMarshal(t *testing.T) { for _, ca := range casesSession { t.Run(ca.name, func(t *testing.T) { byts, err := ca.desc.Marshal(false) require.NoError(t, err) require.Equal(t, ca.out, string(byts)) }) } } func TestSessionFindFormat(t *testing.T) { tr := &format.Generic{ PayloadTyp: 97, RTPMa: "rtx/90000", FMT: map[string]string{ "apt": "96", }, ClockRat: 90000, } md := &Media{ Type: MediaTypeVideo, Formats: []format.Format{ &format.VP8{ PayloadTyp: 96, }, tr, &format.VP9{ PayloadTyp: 98, }, }, } desc := &Session{ Medias: []*Media{ { Type: MediaTypeAudio, Formats: []format.Format{ &format.Opus{ PayloadTyp: 111, IsStereo: true, }, }, }, md, }, } var forma *format.Generic me := desc.FindFormat(&forma) require.Equal(t, md, me) require.Equal(t, tr, forma) } func FuzzSessionUnmarshalErrors(f *testing.F) { f.Add("v=0\r\n" + "o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n" + "s=SDP Seminar\r\n" + "m=video 0 RTP/AVP/TCP 96\r\n" + "a=rtpmap:96 H265/90000\r\n" + "a=fmtp:96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwB4FwJA; " + "sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB4oAKggC8c1YgXuRZFL/y5/E/qbgQEBAE=; sprop-pps=RAHAcvBTJA==;\r\n" + "a=control:streamid=0\r\n" + "m=audio 0 RTP/AVP/TCP 97\r\n" + "a=rtpmap:97 mpeg4-generic/44100/2\r\n" + "a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=zzz1210\r\n" + "a=control:streamid=1\r\n") f.Add("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" + "m=audio 0 RTP/AVP/TCP 0\r\n" + "a=mid:2\r\n") f.Add("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=mid:2\r\n" + "a=rtpmap:96 H264/90000\r\n" + "a=fmtp:96 packetization-mode=1\r\n" + "m=audio 0 RTP/AVP/TCP 0\r\n" + "a=mid:2\r\n") f.Add("v=0\r\n" + "o=adam 289083124 289083124 IN IP4 host.example.com\r\n" + "s=ULP FEC Seminar\r\n" + "t=0 0\r\n" + "c=IN IP4 224.2.17.12/127\r\n" + "a=group:FEC 1 2\r\n" + "a=group:FEC 3 4\r\n" + "m=audio 30000 RTP/AVP 0\r\n" + "a=mid:1\r\n" + "m=application 30002 RTP/AVP 100\r\n" + "a=rtpmap:100 ulpfec/8000\r\n" + "a=mid:2\r\n" + "m=video 30004 RTP/AVP 31\r\n" + "a=mid:3\r\n" + "m=application 30004 RTP/AVP 101\r\n" + "c=IN IP4 224.2.17.13/127\r\n" + "a=rtpmap:101 ulpfec/8000\r\n" + "a=mid:4\r\n") f.Fuzz(func(t *testing.T, enc string) { var sd sdp.SessionDescription err := sd.Unmarshal([]byte(enc)) if err != nil { return } var desc Session desc.Unmarshal(&sd) //nolint:errcheck }) }