mp4 --duration=secs (fixes #28)

also briefly document commandline options
This commit is contained in:
Scott Lamb 2021-08-26 14:31:26 -07:00
parent 2911edec70
commit c9a1ec1912
2 changed files with 36 additions and 9 deletions

View File

@ -13,12 +13,15 @@ use structopt::StructOpt;
#[derive(StructOpt)]
struct Source {
/// `rtsp://` URL to connect to.
#[structopt(long, parse(try_from_str))]
url: url::Url,
#[structopt(long, requires = "password")]
/// Username to send if the server requires authentication.
#[structopt(long)]
username: Option<String>,
/// Password; requires username.
#[structopt(long, requires = "username")]
password: Option<String>,
}
@ -62,11 +65,12 @@ fn creds(
password: Option<String>,
) -> Option<retina::client::Credentials> {
match (username, password) {
(Some(username), Some(password)) => {
Some(retina::client::Credentials { username, password })
}
(Some(username), password) => Some(retina::client::Credentials {
username,
password: password.unwrap_or_default(),
}),
(None, None) => None,
_ => unreachable!(), // structopt/clap enforce username and password's mutual "requires".
_ => unreachable!(), // structopt/clap enforce that password requires username.
}
}

View File

@ -34,21 +34,33 @@ pub struct Opts {
#[structopt(flatten)]
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)]
initial_timestamp: retina::client::InitialTimestampPolicy,
/// Don't attempt to include video streams.
#[structopt(long)]
no_video: bool,
/// Don't attempt to include audio streams.
#[structopt(long)]
no_audio: bool,
/// Allow lost packets mid-stream without aborting.
#[structopt(long)]
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)]
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))]
out: PathBuf,
}
@ -553,7 +565,7 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
pub async fn run(opts: Opts) -> Result<(), Error> {
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(
opts.src.url,
retina::client::SessionOptions::default()
@ -611,8 +623,15 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
)
.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!(stop);
tokio::pin!(stop_signal);
tokio::pin!(sleep);
loop {
tokio::select! {
pkt = session.next() => {
@ -625,12 +644,16 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
_ => continue,
};
},
_ = &mut stop => {
_ = &mut stop_signal => {
info!("Stopping due to signal");
break;
},
_ = &mut sleep => {
info!("Stopping after {} seconds", opts.duration.unwrap());
break;
},
}
}
info!("Stopping");
mp4.finish().await?;
Ok(())
}