Merge pull request #49 from flussonic/dev
Add verbose output to stream info and info cmd to example client
This commit is contained in:
commit
8ab5808054
15
README.md
15
README.md
@ -12,6 +12,7 @@ brokenness in cheap closed-source cameras.
|
||||
Progress:
|
||||
|
||||
* [x] client support
|
||||
* [x] basic authentication.
|
||||
* [x] digest authentication.
|
||||
* [x] RTP over TCP via RTSP interleaved channels.
|
||||
* [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
|
||||
you hit ctrl-C.
|
||||
|
||||
```
|
||||
```shell
|
||||
$ cargo run --example client mp4 --url rtsp://ip.address.goes.here/ --username admin --password test out.mp4
|
||||
...
|
||||
^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
|
||||
|
||||
This builds on the whole Rust ecosystem. A couple folks have been especially
|
||||
|
@ -28,8 +28,12 @@ struct Source {
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum Cmd {
|
||||
/// Write available audio and video streams to mp4 file
|
||||
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),
|
||||
}
|
||||
|
||||
fn init_logging() -> mylog::Handle {
|
||||
@ -78,6 +82,7 @@ async fn main_inner() -> Result<(), Error> {
|
||||
let cmd = Cmd::from_args();
|
||||
match cmd {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,20 @@ pub struct Opts {
|
||||
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 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 {
|
||||
error!("TEARDOWN failed: {}", e);
|
||||
}
|
||||
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 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),
|
||||
)
|
||||
.await?;
|
||||
if is_info {
|
||||
for stream in session.streams() {
|
||||
println!("{:#?}", stream);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let onvif_stream_i = session
|
||||
.streams()
|
||||
.iter()
|
||||
|
@ -546,7 +546,6 @@ 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 {
|
||||
/// Media type, as specified in the [IANA SDP parameters media
|
||||
/// registry](https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml#sdp-parameters-1).
|
||||
@ -593,7 +592,22 @@ pub struct Stream {
|
||||
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 {
|
||||
local_ip: IpAddr,
|
||||
local_rtp_port: u16,
|
||||
@ -604,6 +618,20 @@ struct UdpSockets {
|
||||
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 {
|
||||
/// Returns the parameters for this stream.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user