diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dde23..08e0af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## `v0.4.2` (2022-09-28) + +* ignore unparseable SDP media, improving compatibility with TP-Link cameras, + as described in [scottlamb/moonfire-nvr#238](https://github.com/scottlamb/moonfire-nvr/issues/238). + ## `v0.4.1` (2022-08-09) * Send keepalives at every half-session-timeout rather than a fixed 30-second diff --git a/Cargo.lock b/Cargo.lock index 8b94432..2485396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1780,7 +1780,7 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "retina" -version = "0.4.1" +version = "0.4.2" dependencies = [ "base64", "bitreader", diff --git a/Cargo.toml b/Cargo.toml index a1a9868..bfeb1e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["."] [package] name = "retina" -version = "0.4.1" +version = "0.4.2" authors = ["Scott Lamb "] license = "MIT/Apache-2.0" edition = "2021" diff --git a/src/client/parse.rs b/src/client/parse.rs index 45a22c3..7ec2c1f 100644 --- a/src/client/parse.rs +++ b/src/client/parse.rs @@ -452,19 +452,30 @@ pub(crate) fn parse_describe( } let control = control.unwrap_or(request_url); - let streams = sdp + let streams: Box<[Stream]> = sdp .medias .iter() .enumerate() - .map(|(i, m)| { - parse_media(&base_url, m).map_err(|e| { - format!( - "Unable to parse stream {}: {}\nraw SDP: {:#?}", - i, &e, raw_sdp - ) - }) + .filter_map(|(i, m)| { + parse_media(&base_url, m).map_or_else( + |e| { + warn!( + "Ignoring unparseable stream {}: {}\nraw SDP: {:#?}", + i, &e, raw_sdp + ); + None + }, + Some, + ) }) - .collect::>()?; + .collect(); + + if streams.is_empty() { + return Err(format!( + "No parseable streams (and {} unparseable streams)", + sdp.medias.len() + )); + } Ok(Presentation { streams, @@ -757,6 +768,23 @@ mod tests { super::parse_describe(url, &response).unwrap(); } + /// Parses SDP taken from a TP-LINK TL-IPC44AW-COLOR 4.0 camera running firmware + /// FW: 1.0.10 Build 220330 Rel.35741n, which has a stream with a string where an RTP + /// payload description is expected. + /// https://github.com/scottlamb/moonfire-nvr/issues/238 + #[test] + fn tplink_sdp() { + let url = Url::parse("rtsp://127.0.0.1/").unwrap(); + let response = + rtsp_types::Response::builder(rtsp_types::Version::V1_0, rtsp_types::StatusCode::Ok) + .header(rtsp_types::headers::CONTENT_TYPE, "application/sdp") + .build(Bytes::from_static(include_bytes!( + "testdata/tplink_sdp.txt" + ))); + let p = super::parse_describe(url, &response).unwrap(); + assert_eq!(p.streams.len(), 2); + } + #[test] fn dahua_h264_aac_onvif() { // DESCRIBE. diff --git a/src/client/testdata/tplink_sdp.txt b/src/client/testdata/tplink_sdp.txt new file mode 100644 index 0000000..187ed9e --- /dev/null +++ b/src/client/testdata/tplink_sdp.txt @@ -0,0 +1,17 @@ +v=0 +o=- 14665860 31787219 1 IN IP4 192.168.2.29 +s=Session streamed by "TP-LINK RTSP Server" +t=0 0 +m=video 0 RTP/AVP 96 +c=IN IP4 0.0.0.0 +b=AS:4096 +a=range:npt=0- +a=control:track1 +a=rtpmap:96 H265/90000 +a=fmtp:96 profile-space=0;profile-id=1;tier-flag=0;level-id=150;interop-constraints=000000000000;sprop-vps=QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ;sprop-sps=QgEBAWAAAAMAAAMAAAMAAAMAlqABQCAFof4qtO6JLuaAgA27oADN/mAE;sprop-pps=RAHAcvAbJA== +m=audio 0 RTP/AVP 8 +a=rtpmap:8 PCMA/8000 +a=control:track2 +m=application/TP-LINK 0 RTP/AVP smart/1/90000 +a=rtpmap:95 TP-LINK/90000 +a=control:track3