|
31 | 31 | /// 1. First line must be v=0 |
32 | 32 | /// 2. The second SDP line MUST be an "o=" line The sess-id MUST be representable by a 64-bit signed |
33 | 33 | /// integer, and the initial value MUST be less than (2**62)-1 |
34 | | -/// 3. Third line a single dash SHOULD be used as the session name, e.g. "s=-" |
| 34 | +/// 3. Third line is the session name. The "s=" field MUST NOT be empty and SHOULD contain |
| 35 | +/// ISO 10646 characters (RFC 8866 section 5.3). |
35 | 36 | /// |
36 | 37 | /// Session is over when we find a "t=" line MUST be added, both <start-time> and <stop-time> |
37 | 38 | /// SHOULD be set to zero, e.g. "t=0 0". |
|
43 | 44 | ( |
44 | 45 | typed_line('v', token('0')), // v=0 |
45 | 46 | originator_line(), // o=- 6564425948916445306 2 IN IP4 127.0.0.1 |
46 | | - typed_line('s', token('-')), // s=- |
| 47 | + // The "s=" field MUST NOT be empty (RFC 8866 section 5.3) |
| 48 | + typed_line('s', any_value()), |
47 | 49 | many::<Vec<_>, _, _>(ignored_session_line()), |
48 | 50 | optional(bandwidth_line()), // b=CT:1234 |
49 | 51 | typed_line('t', string("0 0")), // t=0 0 |
@@ -1034,6 +1036,58 @@ mod test { |
1034 | 1036 | ); |
1035 | 1037 | } |
1036 | 1038 |
|
| 1039 | + #[test] |
| 1040 | + fn session_parser_named_session() { |
| 1041 | + // A session name other than "-". |
| 1042 | + let sdp = "v=0\r\n\ |
| 1043 | + o=publisher 4489045141692799359 2 IN IP4 127.0.0.1\r\n\ |
| 1044 | + s=NamedPublishSession\r\n\ |
| 1045 | + t=0 0\r\n\ |
| 1046 | + a=group:BUNDLE 0 1\r\n\ |
| 1047 | + a=extmap-allow-mixed\r\n\ |
| 1048 | + a=msid-semantic: WMS\r\n"; |
| 1049 | + assert_eq!( |
| 1050 | + session_parser().parse(sdp), |
| 1051 | + Ok(( |
| 1052 | + Session { |
| 1053 | + id: 4_489_045_141_692_799_359.into(), |
| 1054 | + bw: None, |
| 1055 | + attrs: vec![ |
| 1056 | + SessionAttribute::Group { |
| 1057 | + typ: "BUNDLE".into(), |
| 1058 | + mids: vec!["0".into(), "1".into()], |
| 1059 | + }, |
| 1060 | + SessionAttribute::Unused("extmap-allow-mixed".into()), |
| 1061 | + SessionAttribute::Unused("msid-semantic: WMS".into()), |
| 1062 | + ], |
| 1063 | + }, |
| 1064 | + "" |
| 1065 | + )) |
| 1066 | + ); |
| 1067 | + } |
| 1068 | + |
| 1069 | + #[test] |
| 1070 | + fn session_parser_space_session_name() { |
| 1071 | + // "If a session has no meaningful name, the value "s= " SHOULD be used |
| 1072 | + // (i.e., a single space as the session name)." (RFC 8866 section 5.3) |
| 1073 | + let sdp = "v=0\n\ |
| 1074 | + o=- 6564425948916445306 2 IN IP4 127.0.0.1\n\ |
| 1075 | + s= \n\ |
| 1076 | + t=0 0\n\ |
| 1077 | + "; |
| 1078 | + assert_eq!( |
| 1079 | + session_parser().parse(sdp), |
| 1080 | + Ok(( |
| 1081 | + Session { |
| 1082 | + id: 6_564_425_948_916_445_306.into(), |
| 1083 | + bw: None, |
| 1084 | + attrs: vec![], |
| 1085 | + }, |
| 1086 | + "" |
| 1087 | + )) |
| 1088 | + ); |
| 1089 | + } |
| 1090 | + |
1037 | 1091 | #[test] |
1038 | 1092 | fn parse_sdp_firefox() { |
1039 | 1093 | let sdp = "v=0\r\n\ |
@@ -1163,6 +1217,64 @@ mod test { |
1163 | 1217 | assert!(parsed.is_ok()); |
1164 | 1218 | } |
1165 | 1219 |
|
| 1220 | + #[test] |
| 1221 | + fn parse_offer_sdp_ffmpeg_whip() { |
| 1222 | + // ffmpeg -re \ |
| 1223 | + // -f lavfi -i "testsrc=size=640x480:rate=30" \ |
| 1224 | + // -f lavfi -i "sine=frequency=440:sample_rate=48000" \ |
| 1225 | + // -c:v libx264 -profile:v baseline -pix_fmt yuv420p -g 60 -tune zerolatency \ |
| 1226 | + // -c:a libopus -ar 48000 -ac 2 \ |
| 1227 | + // -f whip "http://127.0.0.1:8080/whip" |
| 1228 | + let sdp = "v=0\r\n\ |
| 1229 | + o=FFmpeg 4489045141692799359 2 IN IP4 127.0.0.1\r\n\ |
| 1230 | + s=FFmpegPublishSession\r\n\ |
| 1231 | + t=0 0\r\n\ |
| 1232 | + a=group:BUNDLE 0 1\r\n\ |
| 1233 | + a=extmap-allow-mixed\r\n\ |
| 1234 | + a=msid-semantic: WMS\r\n\ |
| 1235 | + m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n\ |
| 1236 | + c=IN IP4 0.0.0.0\r\n\ |
| 1237 | + a=ice-ufrag:08a9c3f4\r\n\ |
| 1238 | + a=ice-pwd:a202d23be47d1decaa6f67d04d4e6c16\r\n\ |
| 1239 | + a=fingerprint:sha-256 21:69:7C:46:D3:DC:4D:D3:66:61:5C:C5:6A:0B:F7:36\ |
| 1240 | + :BD:91:A9:2A:FF:BC:1A:59:D9:38:38:C8:E2:A4:8D:AF\r\n\ |
| 1241 | + a=setup:passive\r\n\ |
| 1242 | + a=mid:0\r\n\ |
| 1243 | + a=sendonly\r\n\ |
| 1244 | + a=msid:FFmpeg audio\r\n\ |
| 1245 | + a=rtcp-mux\r\n\ |
| 1246 | + a=rtpmap:111 opus/48000/2\r\n\ |
| 1247 | + a=ssrc:3166090378 cname:FFmpeg\r\n\ |
| 1248 | + a=ssrc:3166090378 msid:FFmpeg audio\r\n\ |
| 1249 | + m=video 9 UDP/TLS/RTP/SAVPF 106 105\r\n\ |
| 1250 | + c=IN IP4 0.0.0.0\r\n\ |
| 1251 | + a=ice-ufrag:08a9c3f4\r\n\ |
| 1252 | + a=ice-pwd:a202d23be47d1decaa6f67d04d4e6c16\r\n\ |
| 1253 | + a=fingerprint:sha-256 21:69:7C:46:D3:DC:4D:D3:66:61:5C:C5:6A:0B:F7:36\ |
| 1254 | + :BD:91:A9:2A:FF:BC:1A:59:D9:38:38:C8:E2:A4:8D:AF\r\n\ |
| 1255 | + a=setup:passive\r\n\ |
| 1256 | + a=mid:1\r\n\ |
| 1257 | + a=sendonly\r\n\ |
| 1258 | + a=msid:FFmpeg video\r\n\ |
| 1259 | + a=rtcp-mux\r\n\ |
| 1260 | + a=rtcp-rsize\r\n\ |
| 1261 | + a=rtpmap:106 H264/90000\r\n\ |
| 1262 | + a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001e\r\n\ |
| 1263 | + a=rtcp-fb:106 nack\r\n\ |
| 1264 | + a=rtpmap:105 rtx/90000\r\n\ |
| 1265 | + a=fmtp:105 apt=106\r\n\ |
| 1266 | + a=ssrc-group:FID 3166090379 3166090380\r\n\ |
| 1267 | + a=ssrc:3166090379 cname:FFmpeg\r\n\ |
| 1268 | + a=ssrc:3166090379 msid:FFmpeg video\r\n\ |
| 1269 | + "; |
| 1270 | + |
| 1271 | + let (sdp, _) = sdp_parser().parse(sdp).unwrap(); |
| 1272 | + |
| 1273 | + assert_eq!(sdp.media_lines.len(), 2); |
| 1274 | + assert_eq!(sdp.media_lines[0].mid().to_string(), "0"); |
| 1275 | + assert_eq!(sdp.media_lines[1].mid().to_string(), "1"); |
| 1276 | + } |
| 1277 | + |
1166 | 1278 | #[test] |
1167 | 1279 | fn parse_safari_data_channel() { |
1168 | 1280 | let sdp = "v=0\r\n\ |
|
0 commit comments