documentation improvements
This commit is contained in:
parent
a63497eb09
commit
4b9a726a84
@ -3,6 +3,7 @@
|
||||
* [#42](https://github.com/scottlamb/retina/issues/42): support servers that
|
||||
don't send out-of-band H.264 parameters or send invalid parameters; wait for
|
||||
in-band parameters in this case. The in-band parameters must be valid.
|
||||
* documentation improvements.
|
||||
|
||||
## `v0.3.4` (2021-10-26)
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (C) 2021 Scott Lamb <slamb@slamb.org>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
//! RTSP client: connect to a server via [`Session`].
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
@ -31,6 +33,8 @@ pub mod rtp;
|
||||
mod teardown;
|
||||
mod timeline;
|
||||
|
||||
// TODO: this is pub but can't be fed back into the crate in any way; strange.
|
||||
#[doc(hidden)]
|
||||
/// Duration between keepalive RTSP requests during [Playing] state.
|
||||
pub const KEEPALIVE_DURATION: std::time::Duration = std::time::Duration::from_secs(30);
|
||||
|
||||
@ -50,7 +54,7 @@ struct StaleSession {
|
||||
expires: tokio::time::Instant,
|
||||
}
|
||||
|
||||
/// A grouping of sessions, currently used only to track stale sessions.
|
||||
/// A group of sessions, currently used only to track stale sessions.
|
||||
///
|
||||
/// Sessions are associated with a group via [`SessionOptions::session_group`].
|
||||
///
|
||||
@ -80,7 +84,7 @@ struct StaleSession {
|
||||
/// no bound on when the server could see the request and extend the session.
|
||||
/// Retina ignores this possibility.
|
||||
///
|
||||
/// A SessionGroup can be of any granularity, but a typical use is to ensure
|
||||
/// A `SessionGroup` can be of any granularity, but a typical use is to ensure
|
||||
/// there are no stale sessions before starting a fresh session. Groups should
|
||||
/// be sized to match that idea. If connecting to a live555 server affected by
|
||||
/// the stale TCP session bug, it might be wise to have one group per server, so
|
||||
@ -344,11 +348,15 @@ pub struct SessionOptions {
|
||||
teardown: TeardownPolicy,
|
||||
}
|
||||
|
||||
/// The RTP packet transport to request.
|
||||
///
|
||||
/// Defaults to `Transport::Tcp`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Transport {
|
||||
/// Sends RTP packets over the RTSP TCP connection via interleaved data.
|
||||
Tcp,
|
||||
|
||||
/// UDP (experimental).
|
||||
/// Sends RTP packets over UDP (experimental).
|
||||
///
|
||||
/// This support is currently only suitable for a LAN for a couple reasons:
|
||||
/// * There's no reorder buffer, so out-of-order packets are all dropped.
|
||||
@ -435,6 +443,7 @@ pub struct PlayOptions {
|
||||
}
|
||||
|
||||
impl PlayOptions {
|
||||
/// Sets the policy for handling the `rtptime` parameter normally seem in the `RTP-Info` header.
|
||||
pub fn initial_timestamp(self, initial_timestamp: InitialTimestampPolicy) -> Self {
|
||||
Self {
|
||||
initial_timestamp,
|
||||
@ -442,9 +451,11 @@ impl PlayOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/// If the `RTP-Time` specifies `seq=0`, ignore it. Some cameras set this value then start
|
||||
/// the stream with something dramatically different. (Eg the Hikvision DS-2CD2032-I on its
|
||||
/// metadata stream; the other streams are fine.)
|
||||
/// If the `RTP-Time` specifies `seq=0`, ignore it.
|
||||
///
|
||||
/// Some cameras set this value then start the stream with something
|
||||
/// dramatically different. (Eg the Hikvision DS-2CD2032-I on its metadata
|
||||
/// stream; the other streams are fine.)
|
||||
pub fn ignore_zero_seq(self, ignore_zero_seq: bool) -> Self {
|
||||
Self {
|
||||
ignore_zero_seq,
|
||||
@ -466,6 +477,9 @@ impl PlayOptions {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this is `pub` yet not actually accessible from outside the crate, a combination which
|
||||
// makes little sense.
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct Presentation {
|
||||
pub streams: Vec<Stream>,
|
||||
@ -478,6 +492,7 @@ pub struct Presentation {
|
||||
}
|
||||
|
||||
/// Information about a stream offered within a presentation.
|
||||
///
|
||||
/// Currently if multiple formats are offered, this only describes the first.
|
||||
#[derive(Debug)]
|
||||
pub struct Stream {
|
||||
@ -499,6 +514,7 @@ pub struct Stream {
|
||||
pub encoding_name: String,
|
||||
|
||||
/// RTP payload type.
|
||||
///
|
||||
/// See the [registry](https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-1).
|
||||
/// It's common to use one of the dynamically assigned values, 96–127.
|
||||
pub rtp_payload_type: u8,
|
||||
@ -512,6 +528,7 @@ pub struct Stream {
|
||||
depacketizer: Result<crate::codec::Depacketizer, String>,
|
||||
|
||||
/// The specified control URL.
|
||||
///
|
||||
/// This is needed with multiple streams to send `SETUP` requests and
|
||||
/// interpret the `PLAY` response's `RTP-Info` header.
|
||||
/// [RFC 2326 section C.3](https://datatracker.ietf.org/doc/html/rfc2326#appendix-C.3)
|
||||
@ -579,7 +596,8 @@ struct StreamStateInit {
|
||||
initial_rtptime: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Username and password authentication credentials.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Credentials {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
@ -639,10 +657,18 @@ enum ResponseMode {
|
||||
|
||||
/// An RTSP session.
|
||||
///
|
||||
/// When a `Session` is dropped, Retina may issue a `TEARDOWN` in the
|
||||
/// background, depending on the [`SessionOptions::teardown`] parameter. Clients
|
||||
/// may need to wait for the `TEARDOWN` to complete; see
|
||||
/// [`SessionOptions::session_group`] and [`SessionGroup::await_teardown`].
|
||||
/// The expected lifecycle is as follows:
|
||||
///
|
||||
/// 1. Create a session via [`Session::describe`].
|
||||
/// 2. Examine the session's available streams via [`Session::streams`] and set
|
||||
/// up one or more via [`Session::setup`].
|
||||
/// 3. Start playing via [`Session::play`].
|
||||
/// 4. Get packets via the [`futures::stream::Stream`] impl on `Session<Playing>`,
|
||||
/// or frames via the [`futures::stream::Stream`] impl returned by [`Session<Playing>::demuxed`].
|
||||
/// 5. Drop the session. Retina may issue a `TEARDOWN` in the background, depending on the
|
||||
/// `[SessionOptions::teardown`] parameter.
|
||||
/// 6. Possibly wait for `TEARDOWN` to complete; see
|
||||
/// [`SessionOptions::session_group`] and [`SessionGroup::await_teardown`].
|
||||
pub struct Session<S: State>(Pin<Box<SessionInner>>, S);
|
||||
|
||||
#[pin_project(PinnedDrop)]
|
||||
@ -900,6 +926,13 @@ impl RtspConnection {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> Session<S> {
|
||||
/// Returns the available streams as described the server.
|
||||
pub fn streams(&self) -> &[Stream] {
|
||||
&self.0.presentation.streams
|
||||
}
|
||||
}
|
||||
|
||||
impl Session<Described> {
|
||||
/// Creates a new session from a `DESCRIBE` request on the given URL.
|
||||
///
|
||||
@ -969,10 +1002,6 @@ impl Session<Described> {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn streams(&self) -> &[Stream] {
|
||||
&self.0.presentation.streams
|
||||
}
|
||||
|
||||
/// Sends a `SETUP` request for a stream.
|
||||
///
|
||||
/// Note these can't reasonably be pipelined because subsequent requests
|
||||
@ -1344,6 +1373,7 @@ async fn punch_firewall_hole(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An item yielded by [`Session<Playing>`]'s [`futures::stream::Stream`] impl.
|
||||
#[derive(Debug)]
|
||||
pub enum PacketItem {
|
||||
RtpPacket(rtp::Packet),
|
||||
@ -1381,7 +1411,7 @@ impl Session<Playing> {
|
||||
}
|
||||
|
||||
/// Sends a `TEARDOWN`, ending the session.
|
||||
#[deprecated(since = "0.3.1", note = "Use SessionGroup::await_teardown instead")]
|
||||
#[deprecated(since = "0.3.1", note = "Use `SessionGroup::await_teardown` instead")]
|
||||
pub async fn teardown(mut self) -> Result<(), Error> {
|
||||
let inner = self.0.as_mut().project();
|
||||
let mut req = rtsp_types::Request::builder(Method::Teardown, rtsp_types::Version::V1_0)
|
||||
@ -1407,10 +1437,6 @@ impl Session<Playing> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn streams(&self) -> &[Stream] {
|
||||
&self.0.presentation.streams
|
||||
}
|
||||
|
||||
fn handle_keepalive_timer(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
@ -1847,7 +1873,7 @@ pub struct Demuxed {
|
||||
}
|
||||
|
||||
impl Demuxed {
|
||||
#[deprecated(since = "0.3.1", note = "Use SessionGroup::await_teardown instead")]
|
||||
#[deprecated(since = "0.3.1", note = "Use `SessionGroup::await_teardown` instead")]
|
||||
pub async fn teardown(self) -> Result<(), Error> {
|
||||
#[allow(deprecated)]
|
||||
self.session.teardown().await
|
||||
|
@ -25,7 +25,7 @@ pub struct Packet {
|
||||
/// In the case of the first packet on the stream, this may also report loss
|
||||
/// packets since the `RTP-Info` header's `seq` value. However, currently
|
||||
/// that header is not required to be present and may be ignored (see
|
||||
/// [`retina::client::PlayPolicy::ignore_zero_seq()`].)
|
||||
/// [`super::PlayOptions::ignore_zero_seq()`].)
|
||||
pub loss: u16,
|
||||
|
||||
pub mark: bool,
|
||||
|
@ -24,6 +24,7 @@ pub mod h264;
|
||||
pub(crate) mod onvif;
|
||||
pub(crate) mod simple_audio;
|
||||
|
||||
/// An item yielded from [`crate::client::Demuxed`]'s [`futures::stream::Stream`] impl.
|
||||
#[derive(Debug)]
|
||||
pub enum CodecItem {
|
||||
VideoFrame(VideoFrame),
|
||||
@ -32,6 +33,17 @@ pub enum CodecItem {
|
||||
SenderReport(crate::client::rtp::SenderReport),
|
||||
}
|
||||
|
||||
/// Parameters which describe a stream.
|
||||
///
|
||||
/// Parameters are often, but not always, available immediately
|
||||
/// after `DESCRIBE` via [`crate::client::Stream::parameters`]. They should
|
||||
/// always be available after the first frame.
|
||||
///
|
||||
/// Video streams' parameters may change mid-stream; if so, the frame which
|
||||
/// changed them will have `VideoFrame::new_parameters` set, and subsequent
|
||||
/// calls to [`crate::client::Stream::parameters`] will return the new value.
|
||||
///
|
||||
/// Currently audio and message streams' parameters never change mid-stream.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Parameters {
|
||||
Video(VideoParameters),
|
||||
@ -39,6 +51,15 @@ pub enum Parameters {
|
||||
Message(MessageParameters),
|
||||
}
|
||||
|
||||
/// Parameters which describe a video stream.
|
||||
///
|
||||
/// A video stream's parameters are often, but not always, available immediately
|
||||
/// after `DESCRIBE` via [`crate::client::Stream::parameters`]. They should
|
||||
/// always be available after the first frame. They may change mid-stream.
|
||||
///
|
||||
/// Video streams' parameters may change mid-stream; if so, the frame which
|
||||
/// changed them will have `VideoFrame::new_parameters` set, and subsequent
|
||||
/// calls to [`crate::client::Stream::parameters`] will return the new value.
|
||||
#[derive(Clone)]
|
||||
pub struct VideoParameters {
|
||||
pixel_dimensions: (u32, u32),
|
||||
@ -107,6 +128,7 @@ impl std::fmt::Debug for VideoParameters {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters which describe an audio stream.
|
||||
#[derive(Clone)]
|
||||
pub struct AudioParameters {
|
||||
rfc6381_codec: Option<String>,
|
||||
@ -198,9 +220,11 @@ impl Buf for AudioFrame {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters which describe a message stream, for `application` media types.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageParameters(onvif::CompressionType);
|
||||
|
||||
/// A single message, for `application` media types.
|
||||
pub struct MessageFrame {
|
||||
pub ctx: crate::PacketContext,
|
||||
pub timestamp: crate::Timestamp,
|
||||
|
31
src/lib.rs
31
src/lib.rs
@ -1,6 +1,11 @@
|
||||
// Copyright (C) 2021 Scott Lamb <slamb@slamb.org>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
//! High-level RTSP library.
|
||||
//!
|
||||
//! Currently this is useful for clients; it will be extended to support
|
||||
//! servers and proxies.
|
||||
|
||||
use bytes::Bytes;
|
||||
use log::trace;
|
||||
//use failure::{bail, format_err, Error};
|
||||
@ -40,9 +45,13 @@ mod tokio;
|
||||
|
||||
use error::ErrorInt;
|
||||
|
||||
// TODO: these are `pub`, yet the crate doesn't expose a direct way to set or get
|
||||
// headers, a combination which makes little sense.
|
||||
#[doc(hidden)]
|
||||
pub static X_ACCEPT_DYNAMIC_RATE: Lazy<rtsp_types::HeaderName> = Lazy::new(|| {
|
||||
rtsp_types::HeaderName::from_static_str("x-Accept-Dynamic-Rate").expect("is ascii")
|
||||
});
|
||||
#[doc(hidden)]
|
||||
pub static X_DYNAMIC_RATE: Lazy<rtsp_types::HeaderName> =
|
||||
Lazy::new(|| rtsp_types::HeaderName::from_static_str("x-Dynamic-Rate").expect("is ascii"));
|
||||
|
||||
@ -54,6 +63,7 @@ struct ReceivedMessage {
|
||||
}
|
||||
|
||||
/// A monotonically increasing timestamp within an RTP stream.
|
||||
///
|
||||
/// The [Display] and [Debug] implementations display:
|
||||
/// * the bottom 32 bits, as seen in RTP packet headers. This advances at a
|
||||
/// codec-specified clock rate.
|
||||
@ -148,11 +158,26 @@ impl Debug for Timestamp {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Unix epoch as an [`NtpTimestamp`].
|
||||
pub const UNIX_EPOCH: NtpTimestamp = NtpTimestamp((2_208_988_800) << 32);
|
||||
|
||||
/// A wallclock time represented using the format of the Network Time Protocol.
|
||||
/// This isn't necessarily gathered from a real NTP server. Reported NTP
|
||||
/// timestamps are allowed to jump backwards and/or be complete nonsense.
|
||||
///
|
||||
/// NTP timestamps are in a fixed-point representation of seconds since
|
||||
/// 0h UTC on 1 January 1900. The top 32 bits represent the integer part
|
||||
/// (wrapping around every 68 years) and the bottom 32 bits represent the
|
||||
/// fractional part.
|
||||
///
|
||||
/// This is a simple wrapper around a `u64` in that format, with a `Display`
|
||||
/// impl that writes the timestamp as a human-readable string. Currently this
|
||||
/// assumes the time is within 68 years of 1970; the string will be incorrect
|
||||
/// after `2038-01-19T03:14:07Z`.
|
||||
///
|
||||
/// An `NtpTimestamp` isn't necessarily gathered from a real NTP server.
|
||||
/// Reported NTP timestamps are allowed to jump backwards and/or be complete
|
||||
/// nonsense.
|
||||
///
|
||||
/// The NTP timestamp of the Unix epoch is available via the constant [`UNIX_EPOCH`].
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct NtpTimestamp(pub u64);
|
||||
|
||||
@ -288,7 +313,7 @@ impl Display for RtspMessageContext {
|
||||
|
||||
/// Context for an RTP or RTCP packet, received either via RTSP interleaved data or UDP.
|
||||
///
|
||||
/// Should be paired with an [`RtspConnectionContext`] of the RTSP connection that started
|
||||
/// Should be paired with an [`ConnectionContext`] of the RTSP connection that started
|
||||
/// the session. In the interleaved data case, it's assumed the packet was received over
|
||||
/// that same connection.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
Loading…
Reference in New Issue
Block a user