-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use sdp-types 0.1.3 with more permissive parsing
- Loading branch information
Showing
6 changed files
with
101 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
// Copyright (C) 2021 Scott Lamb <[email protected]> | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
|
||
use bytes::{Buf, Bytes}; | ||
use bytes::Bytes; | ||
use log::debug; | ||
use pretty_hex::PrettyHex; | ||
use sdp::media_description::MediaDescription; | ||
use std::{convert::TryFrom, net::IpAddr, num::NonZeroU16}; | ||
use sdp_types::Media; | ||
use std::{net::IpAddr, num::NonZeroU16}; | ||
use url::Url; | ||
|
||
use super::{Presentation, Stream}; | ||
|
@@ -224,21 +224,16 @@ pub(crate) fn get_cseq(response: &rtsp_types::Response<Bytes>) -> Option<u32> { | |
/// Parses a [MediaDescription] to a [Stream]. | ||
/// On failure, returns an error which is expected to be supplemented with | ||
/// the [MediaDescription] debug string and packed into a `RtspResponseError`. | ||
fn parse_media(base_url: &Url, media_description: &MediaDescription) -> Result<Stream, String> { | ||
let media = media_description.media_name.media.clone(); | ||
fn parse_media(base_url: &Url, media_description: &Media) -> Result<Stream, String> { | ||
let media = media_description.media.clone(); | ||
|
||
// https://tools.ietf.org/html/rfc8866#section-5.14 says "If the <proto> | ||
// sub-field is "RTP/AVP" or "RTP/SAVP" the <fmt> sub-fields contain RTP | ||
// payload type numbers." | ||
// https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml#sdp-parameters-2 | ||
// shows several other variants, such as "TCP/RTP/AVP". Looking a "RTP" component | ||
// shows several other variants, such as "TCP/RTP/AVP". Looking for a "RTP" component | ||
// seems appropriate. | ||
if !media_description | ||
.media_name | ||
.protos | ||
.iter() | ||
.any(|p| p == "RTP") | ||
{ | ||
if !media_description.proto.starts_with("RTP/") && !media_description.proto.contains("/RTP/") { | ||
return Err("Expected RTP-based proto".into()); | ||
} | ||
|
||
|
@@ -248,10 +243,10 @@ fn parse_media(base_url: &Url, media_description: &MediaDescription) -> Result<S | |
// format for the session." Just use the first until we find a stream | ||
// where this isn't the right thing to do. | ||
let rtp_payload_type_str = media_description | ||
.media_name | ||
.formats | ||
.first() | ||
.ok_or_else(|| "missing RTP payload type".to_string())?; | ||
.fmt | ||
.split_ascii_whitespace() | ||
.next() | ||
.unwrap(); | ||
let rtp_payload_type = u8::from_str_radix(rtp_payload_type_str, 10) | ||
.map_err(|_| format!("invalid RTP payload type {:?}", rtp_payload_type_str))?; | ||
if (rtp_payload_type & 0x80) != 0 { | ||
|
@@ -268,7 +263,7 @@ fn parse_media(base_url: &Url, media_description: &MediaDescription) -> Result<S | |
let mut fmtp = None; | ||
let mut control = None; | ||
for a in &media_description.attributes { | ||
if a.key == "rtpmap" { | ||
if a.attribute == "rtpmap" { | ||
let v = a | ||
.value | ||
.as_ref() | ||
|
@@ -287,7 +282,7 @@ fn parse_media(base_url: &Url, media_description: &MediaDescription) -> Result<S | |
if rtpmap_payload_type == rtp_payload_type_str { | ||
rtpmap = Some(v); | ||
} | ||
} else if a.key == "fmtp" { | ||
} else if a.attribute == "fmtp" { | ||
// Similarly starts with payload-type SP. | ||
let v = a | ||
.value | ||
|
@@ -299,7 +294,7 @@ fn parse_media(base_url: &Url, media_description: &MediaDescription) -> Result<S | |
if fmtp_payload_type == rtp_payload_type_str { | ||
fmtp = Some(v); | ||
} | ||
} else if a.key == "control" { | ||
} else if a.attribute == "control" { | ||
control = a | ||
.value | ||
.as_deref() | ||
|
@@ -385,24 +380,13 @@ pub(crate) fn parse_describe( | |
)); | ||
} | ||
|
||
let sdp; | ||
{ | ||
let mut cursor = std::io::Cursor::new(&response.body()[..]); | ||
sdp = | ||
sdp::session_description::SessionDescription::unmarshal(&mut cursor).map_err(|e| { | ||
format!( | ||
"Unable to parse SDP: {}\n\n{:#?}", | ||
e, | ||
response.body().hex_dump() | ||
) | ||
})?; | ||
if cursor.has_remaining() { | ||
return Err(format!( | ||
"garbage after sdp: {:?}", | ||
&response.body()[usize::try_from(cursor.position()).unwrap()..] | ||
)); | ||
} | ||
} | ||
let sdp = sdp_types::Session::parse(&response.body()[..]).map_err(|e| { | ||
format!( | ||
"Unable to parse SDP: {}\n\n{:#?}", | ||
e, | ||
response.body().hex_dump() | ||
) | ||
})?; | ||
|
||
// https://tools.ietf.org/html/rfc2326#appendix-C.1.1 | ||
let base_url = response | ||
|
@@ -419,21 +403,21 @@ pub(crate) fn parse_describe( | |
let mut control = None; | ||
let mut tool = None; | ||
for a in &sdp.attributes { | ||
if a.key == "control" { | ||
if a.attribute == "control" { | ||
control = a | ||
.value | ||
.as_deref() | ||
.map(|c| join_control(&base_url, c)) | ||
.transpose()?; | ||
break; | ||
} else if a.key == "tool" { | ||
} else if a.attribute == "tool" { | ||
tool = a.value.as_deref().map(Into::into); | ||
} | ||
} | ||
let control = control.unwrap_or(request_url); | ||
|
||
let streams = sdp | ||
.media_descriptions | ||
.medias | ||
.iter() | ||
.enumerate() | ||
.map(|(i, m)| { | ||
|
@@ -616,6 +600,7 @@ pub(crate) fn parse_play( | |
mod tests { | ||
use std::num::NonZeroU16; | ||
|
||
use bytes::Bytes; | ||
use url::Url; | ||
|
||
use crate::{client::StreamStateInit, codec::Parameters}; | ||
|
@@ -631,6 +616,30 @@ mod tests { | |
super::parse_describe(url, &response(raw_response)) | ||
} | ||
|
||
#[test] | ||
fn anvpiz_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/anpviz_sdp.txt" | ||
))); | ||
super::parse_describe(url, &response).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn geovision_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/geovision_sdp.txt" | ||
))); | ||
super::parse_describe(url, &response).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn dahua_h264_aac_onvif() { | ||
// DESCRIBE. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
v=0 | ||
o=- 1109162014219182 1109162014219192 IN IP4 x.y.z.w | ||
s=RTSP/RTP stream from anjvision ipcamera | ||
e=NONE | ||
c=IN IP4 0.0.0.0 | ||
a=tool:LIVE555 Streaming Media v2011.05.25 [email protected] | ||
t=0 0 | ||
a=range:npt=0- | ||
a=control:* | ||
m=video 0 RTP/AVP 96 | ||
a=rtpmap:96 H264/90000 | ||
a=control:trackID=1 | ||
a=fmtp:96 profile-level-id=4D401F;packetization-mode=0;sprop-parameter-sets=Z01AH5WgLASabAQ=,aO48gA==;config=00000001674d401f95a02c049a6c040000000168ee3c800000000106f02c0445c6f5000620ebc2f3f7639e48250bfcb561bb2b85dda6fe5f06cc8b887b6a915f5aa3bebfffffffffff7380 | ||
a=x-dimensions: 704, 576 | ||
a=x-framerate: 12 | ||
m=audio 0 RTP/AVP 0 | ||
a=rtpmap:0 MPEG4-GENERIC/16000/2 | ||
a=fmtp:0 config=1408 | ||
a=control:trackID=2 | ||
a=Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000; | ||
a=appversion:1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
v=0 | ||
o=- 1001 1 IN IP4 192.168.5.237 | ||
s=VCP IPC Realtime stream | ||
m=video 0 RTP/AVP 105 | ||
c=IN IP4 192.168.5.237 | ||
a=control:rtsp://192.168.5.237/media/video1/video | ||
a=rtpmap:105 H264/90000 | ||
a=fmtp:105 profile-level-id=4d4032; packetization-mode=1; sprop-parameter-sets=Z01AMpWgCoAwfiZuAgICgAAB9AAAdTBC,aO48gA== | ||
a=recvonly | ||
m=application 0 RTP/AVP 107 | ||
c=IN IP4 192.168.5.237 | ||
a=control:rtsp://192.168.5.237/media/video1/metadata | ||
a=rtpmap:107 vnd.onvif.metadata/90000 | ||
a=fmtp:107 DecoderTag=h3c-v3 RTCP=0 | ||
a=recvonly |