parent
021a572145
commit
f69d746a42
@ -1,3 +1,8 @@
|
||||
## unreleased
|
||||
|
||||
* improve interop with Ubiquiti cameras by ignoring `fmtp` attributes in
|
||||
SDP which have nothing (no required SP, no data) after the payload type.
|
||||
|
||||
## `v0.4.3` (2022-09-28)
|
||||
|
||||
* upgrade version of `h264-reader` crate. Compatibility note: Retina may now
|
||||
|
@ -288,16 +288,20 @@ fn parse_media(base_url: &Url, media_description: &Media) -> Result<Stream, Stri
|
||||
}
|
||||
}
|
||||
"fmtp" => {
|
||||
// Similarly starts with payload-type SP.
|
||||
// Similarly should start with payload-type SP.
|
||||
let v = a
|
||||
.value
|
||||
.as_ref()
|
||||
.ok_or_else(|| "fmtp attribute with no value".to_string())?;
|
||||
let (fmtp_payload_type, v) = v
|
||||
.split_once(' ')
|
||||
.ok_or_else(|| "invalid fmtp attribute".to_string())?;
|
||||
if fmtp_payload_type == rtp_payload_type_str {
|
||||
fmtp = Some(v);
|
||||
|
||||
if let Some((fmtp_payload_type, v)) = v.split_once(' ') {
|
||||
if fmtp_payload_type == rtp_payload_type_str {
|
||||
fmtp = Some(v);
|
||||
}
|
||||
} else {
|
||||
// Ubiquiti cameras sometimes have e.g. "a=fmtp:96": payload
|
||||
// type only, no actual attributes. Don't fail on this.
|
||||
warn!("ignoring invalid fmtp attribute value {:?}", v);
|
||||
}
|
||||
}
|
||||
"control" => {
|
||||
@ -722,7 +726,7 @@ mod tests {
|
||||
|
||||
use super::super::StreamState;
|
||||
use super::SessionHeader;
|
||||
use crate::testutil::response;
|
||||
use crate::testutil::{init_logging, response};
|
||||
|
||||
fn parse_describe(
|
||||
raw_url: &str,
|
||||
@ -746,6 +750,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn anvpiz_sdp() {
|
||||
init_logging();
|
||||
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)
|
||||
@ -758,6 +763,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn geovision_sdp() {
|
||||
init_logging();
|
||||
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)
|
||||
@ -768,12 +774,27 @@ mod tests {
|
||||
super::parse_describe(url, &response).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ubiquiti_sdp() {
|
||||
init_logging();
|
||||
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/ubiquiti_sdp.txt"
|
||||
)));
|
||||
let d = super::parse_describe(url, &response).unwrap();
|
||||
assert_eq!(d.streams.len(), 3);
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
init_logging();
|
||||
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)
|
||||
@ -787,6 +808,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dahua_h264_aac_onvif() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let prefix =
|
||||
"rtsp://192.168.5.111:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif";
|
||||
@ -879,6 +901,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn dahua_h265_pcma() {
|
||||
init_logging();
|
||||
let p = parse_describe(
|
||||
"rtsp://192.168.5.111:554/cam/realmonitor?channel=1&subtype=2",
|
||||
include_bytes!("testdata/dahua_describe_h265_pcma.txt"),
|
||||
@ -902,6 +925,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hikvision() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let prefix = "rtsp://192.168.5.106:554/Streaming/Channels/101";
|
||||
let mut p = parse_describe(
|
||||
@ -981,6 +1005,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn reolink() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let mut p = parse_describe(
|
||||
"rtsp://192.168.5.206:554/h264Preview_01_main",
|
||||
@ -1068,6 +1093,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn bunny() {
|
||||
init_logging();
|
||||
// This is a public test server for Wowza Streaming Engine.
|
||||
// https://www.wowza.com/html/mobile.html
|
||||
|
||||
@ -1147,6 +1173,7 @@ mod tests {
|
||||
/// This is currently treated as if the `rtptime` parameter were absent.
|
||||
#[test]
|
||||
fn bad_rtptime() {
|
||||
init_logging();
|
||||
let prefix = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov";
|
||||
let mut p = parse_describe(prefix, include_bytes!("testdata/bunny_describe.txt")).unwrap();
|
||||
p.streams[0].state = dummy_stream_state_init(None);
|
||||
@ -1167,6 +1194,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn foscam() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let prefix = "rtsp://192.168.5.107:65534/videoMain";
|
||||
let p = parse_describe(prefix, include_bytes!("testdata/foscam_describe.txt")).unwrap();
|
||||
@ -1215,6 +1243,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn vstarcam() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let prefix = "rtsp://192.168.1.198:10554/tcp/av0_0";
|
||||
let p = parse_describe(prefix, include_bytes!("testdata/vstarcam_describe.txt")).unwrap();
|
||||
@ -1264,6 +1293,7 @@ mod tests {
|
||||
/// main stream (high-res, audio).
|
||||
#[test]
|
||||
fn gw_main() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let base = "rtsp://192.168.1.110:5050/H264?channel=1&subtype=0&unicast=true&proto=Onvif";
|
||||
let mut p = parse_describe(base, include_bytes!("testdata/gw_main_describe.txt")).unwrap();
|
||||
@ -1359,6 +1389,7 @@ mod tests {
|
||||
/// sub stream (low-res, no audio).
|
||||
#[test]
|
||||
fn gw_sub() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let base = "rtsp://192.168.1.110:5049/H264?channel=1&subtype=1&unicast=true&proto=Onvif";
|
||||
let mut p = parse_describe(base, include_bytes!("testdata/gw_sub_describe.txt")).unwrap();
|
||||
@ -1419,6 +1450,7 @@ mod tests {
|
||||
/// Tests parsing SDP from `the macro-video rtsp server`, with missing origin line.
|
||||
#[test]
|
||||
fn macrovideo() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let p = parse_describe(
|
||||
"rtsp://camera",
|
||||
@ -1434,6 +1466,7 @@ mod tests {
|
||||
/// This camera notably sends a trailing space in its `rtpmap` attribute.
|
||||
#[test]
|
||||
fn ipcam() {
|
||||
init_logging();
|
||||
// DESCRIBE.
|
||||
let p = parse_describe(
|
||||
"rtsp://camera",
|
||||
|
25
src/client/testdata/ubiquiti_sdp.txt
vendored
Normal file
25
src/client/testdata/ubiquiti_sdp.txt
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
v=0
|
||||
o=- 221 0 IN IP4 192.168.0.1
|
||||
s=68D79AE46148_0
|
||||
u=www.evostream.com
|
||||
e=contact@evostream.com
|
||||
c=IN IP4 192.168.0.1
|
||||
t=0 0
|
||||
a=recvonly
|
||||
a=control:*
|
||||
a=range:npt=now-
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=recvonly
|
||||
a=rtpmap:96 mpeg4-generic/48000/1
|
||||
a=control:trackID=0
|
||||
a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=1188; SizeLength=13; IndexLength=3; IndexDeltaLength=3;
|
||||
m=audio 0 RTP/AVP 96
|
||||
a=recvonly
|
||||
a=rtpmap:96 opus/48000/2
|
||||
a=control:trackID=1
|
||||
a=fmtp:96
|
||||
m=video 0 RTP/AVP 97
|
||||
a=recvonly
|
||||
a=control:trackID=2
|
||||
a=rtpmap:97 H264/90000
|
||||
a=fmtp:97 profile-level-id=4d402a; packetization-mode=1; sprop-parameter-sets=Z01AKo2NQDwBE/LgLcBAQFAAAD6AABX5CdoIhGo=,aO44gA==
|
Loading…
Reference in New Issue
Block a user