Merge pull request #49 from flussonic/dev

Add verbose output to stream info and info cmd to example client
This commit is contained in:
Scott Lamb 2022-01-12 08:22:46 -08:00 committed by GitHub
commit 8ab5808054
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 7 deletions

View File

@ -12,6 +12,7 @@ brokenness in cheap closed-source cameras.
Progress: Progress:
* [x] client support * [x] client support
* [x] basic authentication.
* [x] digest authentication. * [x] digest authentication.
* [x] RTP over TCP via RTSP interleaved channels. * [x] RTP over TCP via RTSP interleaved channels.
* [x] RTP over UDP (experimental). * [x] RTP over UDP (experimental).
@ -63,12 +64,24 @@ Help welcome!
Try the `mp4` example. It streams from an RTSP server to a `.mp4` file until Try the `mp4` example. It streams from an RTSP server to a `.mp4` file until
you hit ctrl-C. you hit ctrl-C.
``` ```shell
$ cargo run --example client mp4 --url rtsp://ip.address.goes.here/ --username admin --password test out.mp4 $ cargo run --example client mp4 --url rtsp://ip.address.goes.here/ --username admin --password test out.mp4
... ...
^C ^C
``` ```
## Example client
```shell
$ 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
## Acknowledgements ## Acknowledgements
This builds on the whole Rust ecosystem. A couple folks have been especially This builds on the whole Rust ecosystem. A couple folks have been especially

View File

@ -28,8 +28,12 @@ struct Source {
#[derive(StructOpt)] #[derive(StructOpt)]
enum Cmd { enum Cmd {
/// Write available audio and video streams to mp4 file
Mp4(mp4::Opts), Mp4(mp4::Opts),
/// Get realtime metadata of onvif stream, use Ctrl+C to stop
Metadata(metadata::Opts), Metadata(metadata::Opts),
/// Get info about available streams and exit
Info(metadata::Opts),
} }
fn init_logging() -> mylog::Handle { fn init_logging() -> mylog::Handle {
@ -78,6 +82,7 @@ async fn main_inner() -> Result<(), Error> {
let cmd = Cmd::from_args(); let cmd = Cmd::from_args();
match cmd { match cmd {
Cmd::Mp4(opts) => mp4::run(opts).await, Cmd::Mp4(opts) => mp4::run(opts).await,
Cmd::Metadata(opts) => metadata::run(opts).await, Cmd::Metadata(opts) => metadata::run(opts, false).await,
Cmd::Info(opts) => metadata::run(opts, true).await,
} }
} }

View File

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

View File

@ -546,7 +546,6 @@ pub struct Presentation {
/// Information about a stream offered within a presentation. /// Information about a stream offered within a presentation.
/// ///
/// Currently if multiple formats are offered, this only describes the first. /// Currently if multiple formats are offered, this only describes the first.
#[derive(Debug)]
pub struct Stream { pub struct Stream {
/// Media type, as specified in the [IANA SDP parameters media /// Media type, as specified in the [IANA SDP parameters media
/// registry](https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml#sdp-parameters-1). /// registry](https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml#sdp-parameters-1).
@ -593,7 +592,22 @@ pub struct Stream {
state: StreamState, state: StreamState,
} }
#[derive(Debug)] impl std::fmt::Debug for Stream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("Stream")
.field("media", &self.media)
.field("control", &self.control.as_ref().map(Url::as_str))
.field("encoding_name", &self.encoding_name)
.field("rtp_payload_type", &self.rtp_payload_type)
.field("clock_rate", &self.clock_rate)
.field("channels", &self.channels)
.field("depacketizer", &self.depacketizer)
.field("UDP", &self.sockets)
.field("state", &self.state)
.finish()
}
}
struct UdpSockets { struct UdpSockets {
local_ip: IpAddr, local_ip: IpAddr,
local_rtp_port: u16, local_rtp_port: u16,
@ -604,6 +618,20 @@ struct UdpSockets {
rtcp_socket: UdpSocket, rtcp_socket: UdpSocket,
} }
impl std::fmt::Debug for UdpSockets {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("UDP")
.field("local_ip", &self.local_ip)
.field("local_rtp_port", &self.local_rtp_port)
.field("remote_ip", &self.remote_ip)
.field("remote_rtp_port", &self.remote_rtp_port)
.field("rtp_socket", &self.rtp_socket)
.field("remote_rtcp_port", &self.remote_rtcp_port)
.field("rtcp_socket", &self.rtcp_socket)
.finish()
}
}
impl Stream { impl Stream {
/// Returns the parameters for this stream. /// Returns the parameters for this stream.
/// ///