mp4 --duration=secs (fixes #28)
also briefly document commandline options
This commit is contained in:
parent
2911edec70
commit
c9a1ec1912
@ -13,12 +13,15 @@ use structopt::StructOpt;
|
|||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Source {
|
struct Source {
|
||||||
|
/// `rtsp://` URL to connect to.
|
||||||
#[structopt(long, parse(try_from_str))]
|
#[structopt(long, parse(try_from_str))]
|
||||||
url: url::Url,
|
url: url::Url,
|
||||||
|
|
||||||
#[structopt(long, requires = "password")]
|
/// Username to send if the server requires authentication.
|
||||||
|
#[structopt(long)]
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
|
|
||||||
|
/// Password; requires username.
|
||||||
#[structopt(long, requires = "username")]
|
#[structopt(long, requires = "username")]
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
}
|
}
|
||||||
@ -62,11 +65,12 @@ fn creds(
|
|||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
) -> Option<retina::client::Credentials> {
|
) -> Option<retina::client::Credentials> {
|
||||||
match (username, password) {
|
match (username, password) {
|
||||||
(Some(username), Some(password)) => {
|
(Some(username), password) => Some(retina::client::Credentials {
|
||||||
Some(retina::client::Credentials { username, password })
|
username,
|
||||||
}
|
password: password.unwrap_or_default(),
|
||||||
|
}),
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
_ => unreachable!(), // structopt/clap enforce username and password's mutual "requires".
|
_ => unreachable!(), // structopt/clap enforce that password requires username.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,21 +34,33 @@ pub struct Opts {
|
|||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
src: super::Source,
|
src: super::Source,
|
||||||
|
|
||||||
|
/// Policy for handling the `rtptime` parameter normally seem in the `RTP-Info` header.
|
||||||
|
/// One of `default`, `require`, `ignore`, `permissive`.
|
||||||
#[structopt(default_value, long)]
|
#[structopt(default_value, long)]
|
||||||
initial_timestamp: retina::client::InitialTimestampPolicy,
|
initial_timestamp: retina::client::InitialTimestampPolicy,
|
||||||
|
|
||||||
|
/// Don't attempt to include video streams.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
no_video: bool,
|
no_video: bool,
|
||||||
|
|
||||||
|
/// Don't attempt to include audio streams.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
no_audio: bool,
|
no_audio: bool,
|
||||||
|
|
||||||
|
/// Allow lost packets mid-stream without aborting.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
allow_loss: bool,
|
allow_loss: bool,
|
||||||
|
|
||||||
|
/// Works around an old live555 server bug which sends data packets meant
|
||||||
|
/// for a closed RTP connection to one opened afterward.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
ignore_spurious_data: bool,
|
ignore_spurious_data: bool,
|
||||||
|
|
||||||
|
/// Duration after which to exit automatically, in seconds.
|
||||||
|
#[structopt(long, name = "secs")]
|
||||||
|
duration: Option<u64>,
|
||||||
|
|
||||||
|
/// Path to `.mp4` file to write.
|
||||||
#[structopt(parse(try_from_str))]
|
#[structopt(parse(try_from_str))]
|
||||||
out: PathBuf,
|
out: PathBuf,
|
||||||
}
|
}
|
||||||
@ -553,7 +565,7 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
|
|||||||
|
|
||||||
pub async fn run(opts: Opts) -> Result<(), Error> {
|
pub async fn run(opts: Opts) -> Result<(), Error> {
|
||||||
let creds = super::creds(opts.src.username, opts.src.password);
|
let creds = super::creds(opts.src.username, opts.src.password);
|
||||||
let stop = tokio::signal::ctrl_c();
|
let stop_signal = tokio::signal::ctrl_c();
|
||||||
let mut session = retina::client::Session::describe(
|
let mut session = retina::client::Session::describe(
|
||||||
opts.src.url,
|
opts.src.url,
|
||||||
retina::client::SessionOptions::default()
|
retina::client::SessionOptions::default()
|
||||||
@ -611,8 +623,15 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let sleep = match opts.duration {
|
||||||
|
Some(secs) => {
|
||||||
|
futures::future::Either::Left(tokio::time::sleep(std::time::Duration::from_secs(secs)))
|
||||||
|
}
|
||||||
|
None => futures::future::Either::Right(futures::future::pending()),
|
||||||
|
};
|
||||||
tokio::pin!(session);
|
tokio::pin!(session);
|
||||||
tokio::pin!(stop);
|
tokio::pin!(stop_signal);
|
||||||
|
tokio::pin!(sleep);
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
pkt = session.next() => {
|
pkt = session.next() => {
|
||||||
@ -625,12 +644,16 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
_ = &mut stop => {
|
_ = &mut stop_signal => {
|
||||||
|
info!("Stopping due to signal");
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
_ = &mut sleep => {
|
||||||
|
info!("Stopping after {} seconds", opts.duration.unwrap());
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Stopping");
|
|
||||||
mp4.finish().await?;
|
mp4.finish().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user