package description

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/bluenviron/gortsplib/v5/pkg/format"
	"github.com/bluenviron/gortsplib/v5/pkg/headers"
	"github.com/bluenviron/gortsplib/v5/pkg/mikey"
	"github.com/bluenviron/gortsplib/v5/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,
							ChannelCount: 1,
						},
						&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,
					}},
				},
			},
		},
	},
	{
		"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,
					}},
				},
			},
		},
	},
	{
		"key-mgmt in session",
		"v=0\n" +
			"o=actionmovie 2891092738 2891092738 IN IP4 movie.example.com\n" +
			"s=Action Movie\n" +
			"t=0 0\n" +
			"c=IN IP4 movie.example.com\n" +
			"a=key-mgmt:mikey AQAFAHojKV4BAACVjCMnAAAAAAsA6/mdTLBeokwKEGwcAuPrxj6/enyb+" +
			"A2+rNcBAAAAFQABAQEBEAIBAQMBCgcBAQgBAQoBAQAAACIAIAAeX8XvOCzIMh0JTOWivWLxEflTUSp1fjj2i8xG7D9DAA==\r\n" +
			"m=video 0 RTP/SAVP 96\n" +
			"a=rtpmap:96 H264/90000\n" +
			"a=control:trackID=0\n",
		"v=0\r\n" +
			"o=- 0 0 IN IP4 127.0.0.1\r\n" +
			"s=Action Movie\r\n" +
			"c=IN IP4 0.0.0.0\r\n" +
			"t=0 0\r\n" +
			"a=key-mgmt:mikey AQAFAHojKV4BAACVjCMnAAAAAAsA6/mdTLBeokwKEGwcAuPrxj6/enyb+" +
			"A2+rNcBAAAAFQABAQEBEAIBAQMBCgcBAQgBAQoBAQAAACIAIAAeX8XvOCzIMh0JTOWivWLxEflTUSp1fjj2i8xG7D9DAA==\r\n" +
			"m=video 0 RTP/SAVP 96\r\n" +
			"a=control:trackID=0\r\n" +
			"a=rtpmap:96 H264/90000\r\n",
		Session{
			Title: "Action Movie",
			KeyMgmtMikey: &mikey.Message{ //nolint:dupl
				Header: mikey.Header{
					Version: 1,
					CSBID:   2049124702,
					CSIDMapInfo: []mikey.SRTPIDEntry{{
						SSRC: 2508989223,
					}},
				},
				Payloads: []mikey.Payload{
					&mikey.PayloadT{
						TSValue: 17003794820816085580,
					},
					&mikey.PayloadRAND{
						Data: []byte{
							0x6c, 0x1c, 0x02, 0xe3, 0xeb, 0xc6, 0x3e, 0xbf,
							0x7a, 0x7c, 0x9b, 0xf8, 0x0d, 0xbe, 0xac, 0xd7,
						},
					},
					&mikey.PayloadSP{
						PolicyParams: []mikey.PayloadSPPolicyParam{
							{
								Type: 0, Value: []byte{1},
							},
							{
								Type: 1, Value: []byte{0x10},
							},
							{
								Type: 2, Value: []byte{1},
							},
							{
								Type: 3, Value: []byte{0x0a},
							},
							{
								Type: 7, Value: []byte{1},
							},
							{
								Type: 8, Value: []byte{1},
							},
							{
								Type: 10, Value: []byte{1},
							},
						},
					},
					&mikey.PayloadKEMAC{
						SubPayloads: []*mikey.SubPayloadKeyData{
							{
								Type: 2,
								KeyData: []byte{
									0x5f, 0xc5, 0xef, 0x38, 0x2c, 0xc8, 0x32, 0x1d,
									0x09, 0x4c, 0xe5, 0xa2, 0xbd, 0x62, 0xf1, 0x11,
									0xf9, 0x53, 0x51, 0x2a, 0x75, 0x7e, 0x38, 0xf6,
									0x8b, 0xcc, 0x46, 0xec, 0x3f, 0x43,
								},
							},
						},
					},
				},
			},
			Medias: []*Media{
				{
					Type:    "video",
					Control: "trackID=0",
					Profile: headers.TransportProfileSAVP,
					Formats: []format.Format{&format.H264{
						PayloadTyp: 96,
					}},
				},
			},
		},
	},
	{
		"key-mgmt in media",
		"v=0\n" +
			"o=actionmovie 2891092738 2891092738 IN IP4 movie.example.com\n" +
			"s=Action Movie\n" +
			"t=0 0\n" +
			"c=IN IP4 movie.example.com\n" +
			"m=video 0 RTP/SAVP 96\n" +
			"a=key-mgmt:mikey AQAFAHojKV4BAACVjCMnAAAAAAsA6/mdTLBeokwKEGwcAuPrxj6/enyb+" +
			"A2+rNcBAAAAFQABAQEBEAIBAQMBCgcBAQgBAQoBAQAAACIAIAAeX8XvOCzIMh0JTOWivWLxEflTUSp1fjj2i8xG7D9DAA==\r\n" +
			"a=rtpmap:96 H264/90000\n" +
			"a=control:trackID=0\n",
		"v=0\r\n" +
			"o=- 0 0 IN IP4 127.0.0.1\r\n" +
			"s=Action Movie\r\n" +
			"c=IN IP4 0.0.0.0\r\n" +
			"t=0 0\r\n" +
			"m=video 0 RTP/SAVP 96\r\n" +
			"a=key-mgmt:mikey AQAFAHojKV4BAACVjCMnAAAAAAsA6/mdTLBeokwKEGwcAuPrxj6/enyb+" +
			"A2+rNcBAAAAFQABAQEBEAIBAQMBCgcBAQgBAQoBAQAAACIAIAAeX8XvOCzIMh0JTOWivWLxEflTUSp1fjj2i8xG7D9DAA==\r\n" +
			"a=control:trackID=0\r\n" +
			"a=rtpmap:96 H264/90000\r\n",
		Session{
			Title: "Action Movie",
			Medias: []*Media{
				{
					Type:    "video",
					Control: "trackID=0",
					Profile: headers.TransportProfileSAVP,
					KeyMgmtMikey: &mikey.Message{ //nolint:dupl
						Header: mikey.Header{
							Version: 1,
							CSBID:   2049124702,
							CSIDMapInfo: []mikey.SRTPIDEntry{{
								SSRC: 2508989223,
							}},
						},
						Payloads: []mikey.Payload{
							&mikey.PayloadT{
								TSValue: 17003794820816085580,
							},
							&mikey.PayloadRAND{
								Data: []byte{
									0x6c, 0x1c, 0x02, 0xe3, 0xeb, 0xc6, 0x3e, 0xbf,
									0x7a, 0x7c, 0x9b, 0xf8, 0x0d, 0xbe, 0xac, 0xd7,
								},
							},
							&mikey.PayloadSP{
								PolicyParams: []mikey.PayloadSPPolicyParam{
									{
										Type: 0, Value: []byte{1},
									},
									{
										Type: 1, Value: []byte{0x10},
									},
									{
										Type: 2, Value: []byte{1},
									},
									{
										Type: 3, Value: []byte{0x0a},
									},
									{
										Type: 7, Value: []byte{1},
									},
									{
										Type: 8, Value: []byte{1},
									},
									{
										Type: 10, Value: []byte{1},
									},
								},
							},
							&mikey.PayloadKEMAC{
								SubPayloads: []*mikey.SubPayloadKeyData{
									{
										Type: 2,
										KeyData: []byte{
											0x5f, 0xc5, 0xef, 0x38, 0x2c, 0xc8, 0x32, 0x1d,
											0x09, 0x4c, 0xe5, 0xa2, 0xbd, 0x62, 0xf1, 0x11,
											0xf9, 0x53, 0x51, 0x2a, 0x75, 0x7e, 0x38, 0xf6,
											0x8b, 0xcc, 0x46, 0xec, 0x3f, 0x43,
										},
									},
								},
							},
						},
					},
					Formats: []format.Format{&format.H264{
						PayloadTyp: 96,
					}},
				},
			},
		},
	},
}

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()
			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,
						ChannelCount: 2,
					},
				},
			},
			md,
		},
	}

	var forma *format.Generic
	me := desc.FindFormat(&forma)
	require.Equal(t, md, me)
	require.Equal(t, tr, forma)
}

func FuzzSessionUnmarshal(f *testing.F) {
	for _, ca := range casesSession {
		f.Add(ca.in)
	}

	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
		err = desc.Unmarshal(&sd)
		if err != nil {
			return
		}

		require.NotZero(t, len(desc.Medias))

		_, err = desc.Marshal()
		require.NoError(t, err)
	})
}
