API and example for printing a session's SDP

This is useful for debugging.
This commit is contained in:
Scott Lamb 2022-04-04 15:08:52 -07:00
parent 76d5d883fa
commit d49e77a668
5 changed files with 74 additions and 27 deletions

View File

@ -78,9 +78,9 @@ $ cargo run --example client <CMD>
Where CMD:
* **info** - Get info about available streams and exit
* **mp4** - Write RTSP streams to mp4 file, exit with Ctrl+C
* **metadata** - Get realtime onvif metadata if available, exit with Ctrl+C
* **info** - Gets info about available streams and exits.
* **mp4** - Writes RTSP streams to mp4 file; exit with Ctrl+C.
* **metadata** - Gets realtime onvif metadata if available; exit with Ctrl+C.
## Acknowledgements

43
examples/client/info.rs Normal file
View File

@ -0,0 +1,43 @@
// Copyright (C) 2022 Scott Lamb <slamb@slamb.org>
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Gets info about available streams and exits.
use anyhow::Error;
#[derive(structopt::StructOpt)]
pub struct Opts {
#[structopt(flatten)]
src: super::Source,
/// Prints the SDP (Session Description Protocol) session description.
#[structopt(long)]
print_sdp: bool,
/// Prints debug output for each decoded stream.
#[structopt(long)]
print_streams: bool,
}
pub async fn run(opts: Opts) -> Result<(), Error> {
let creds = super::creds(opts.src.username.clone(), opts.src.password.clone());
let session = retina::client::Session::describe(
opts.src.url.clone(),
retina::client::SessionOptions::default()
.creds(creds)
.user_agent("Retina sdp example".to_owned()),
)
.await?;
if opts.print_sdp {
println!("SDP:\n{}\n\n", std::str::from_utf8(session.sdp())?);
}
if opts.print_streams {
for (i, stream) in session.streams().iter().enumerate() {
println!("stream {}:\n{:#?}\n\n", i, stream);
}
}
if !opts.print_sdp && !opts.print_streams {
eprintln!("You probably wanted at least one of --print-sdp or --print-streams?");
}
Ok(())
}

View File

@ -3,8 +3,9 @@
//! RTSP client examples.
mod metadata;
mod info;
mod mp4;
mod onvif;
use anyhow::Error;
use log::{error, info};
@ -28,12 +29,12 @@ struct Source {
#[derive(StructOpt)]
enum Cmd {
/// Write available audio and video streams to mp4 file
/// Gets info about available streams and exits.
Info(info::Opts),
/// Writes available audio and video streams to mp4 file; use Ctrl+C to stop.
Mp4(mp4::Opts),
/// Get realtime metadata of onvif stream, use Ctrl+C to stop
Metadata(metadata::Opts),
/// Get info about available streams and exit
Info(metadata::Opts),
/// Follows ONVIF metadata stream; use Ctrl+C to stop.
Onvif(onvif::Opts),
}
fn init_logging() -> mylog::Handle {
@ -81,8 +82,8 @@ fn creds(
async fn main_inner() -> Result<(), Error> {
let cmd = Cmd::from_args();
match cmd {
Cmd::Info(opts) => info::run(opts).await,
Cmd::Mp4(opts) => mp4::run(opts).await,
Cmd::Metadata(opts) => metadata::run(opts, false).await,
Cmd::Info(opts) => metadata::run(opts, true).await,
Cmd::Onvif(opts) => onvif::run(opts).await,
}
}

View File

@ -14,20 +14,16 @@ pub struct Opts {
src: super::Source,
}
pub async fn run(opts: Opts, is_info: bool) -> Result<(), Error> {
pub async fn run(opts: Opts) -> Result<(), Error> {
let session_group = Arc::new(SessionGroup::default());
let r = run_inner(opts, session_group.clone(), is_info).await;
let r = run_inner(opts, session_group.clone()).await;
if let Err(e) = session_group.await_teardown().await {
error!("TEARDOWN failed: {}", e);
}
r
}
async fn run_inner(
opts: Opts,
session_group: Arc<SessionGroup>,
is_info: bool,
) -> Result<(), Error> {
async fn run_inner(opts: Opts, session_group: Arc<SessionGroup>) -> Result<(), Error> {
let stop = tokio::signal::ctrl_c();
let creds = super::creds(opts.src.username, opts.src.password);
@ -39,12 +35,6 @@ async fn run_inner(
.session_group(session_group),
)
.await?;
if is_info {
for stream in session.streams() {
println!("{:#?}", stream);
}
return Ok(());
}
let onvif_stream_i = session
.streams()
.iter()

View File

@ -758,7 +758,9 @@ pub trait State {}
/// Initial state after a `DESCRIBE`; use via `Session<Described>`.
#[doc(hidden)]
pub struct Described(());
pub struct Described {
sdp: Bytes,
}
impl State for Described {}
enum KeepaliveState {
@ -1127,6 +1129,8 @@ impl Session<Described> {
description,
})
})?;
let describe_status = response.status();
let sdp = response.into_body();
Ok(Session(
Box::pin(SessionInner {
conn: Some(conn),
@ -1137,16 +1141,25 @@ impl Session<Described> {
session: None,
describe_ctx: msg_ctx,
describe_cseq: cseq,
describe_status: response.status(),
describe_status,
keepalive_state: KeepaliveState::Idle,
keepalive_timer: None,
maybe_playing: false,
udp_next_poll_i: 0,
}),
Described(()),
Described { sdp },
))
}
/// Returns the raw SDP (Session Description Protocol) session description of this URL.
///
/// Retina interprets the SDP automatically, but the raw bytes may be useful for debugging.
/// They're accessibled in the `Session<Described>` state. Currently, they're discarded on
/// `play` to reduce memory usage.
pub fn sdp(&self) -> &[u8] {
&self.1.sdp
}
/// Sends a `SETUP` request for a stream.
///
/// Note these can't reasonably be pipelined because subsequent requests