mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-11-28 13:09:10 -05:00
support --rtsp-library=retina (#37)
This isn't well-tested and doesn't yet support an initial connection timeout. But in a quick test, it successfully returns video! I'd like to do some more aggressive code restructuring for zero-copy and to have only one writer thread per sample file directory (rather than the syncer thread + one writer thread per RTSP stream). But I'll likely wait until I drop support for ffmpeg entirely.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
|
||||
use crate::stream::{self, Opener, Stream};
|
||||
use crate::stream::{self, Opener};
|
||||
use base::strutil::{decode_size, encode_size};
|
||||
use cursive::traits::{Boxable, Finder, Identifiable};
|
||||
use cursive::views;
|
||||
@@ -35,24 +35,30 @@ fn get_change(siv: &mut Cursive) -> db::CameraChange {
|
||||
.get_content()
|
||||
.as_str()
|
||||
.into();
|
||||
let u = siv
|
||||
let username = match siv
|
||||
.find_name::<views::EditView>("username")
|
||||
.unwrap()
|
||||
.get_content()
|
||||
.as_str()
|
||||
.into();
|
||||
let p = siv
|
||||
{
|
||||
"" => None,
|
||||
u => Some(u.to_owned()),
|
||||
};
|
||||
let password = match siv
|
||||
.find_name::<views::EditView>("password")
|
||||
.unwrap()
|
||||
.get_content()
|
||||
.as_str()
|
||||
.into();
|
||||
{
|
||||
"" => None,
|
||||
p => Some(p.to_owned()),
|
||||
};
|
||||
let mut c = db::CameraChange {
|
||||
short_name: sn,
|
||||
description: d,
|
||||
onvif_host: h,
|
||||
username: u,
|
||||
password: p,
|
||||
username,
|
||||
password,
|
||||
streams: Default::default(),
|
||||
};
|
||||
for &t in &db::ALL_STREAM_TYPES {
|
||||
@@ -114,12 +120,16 @@ fn press_edit(siv: &mut Cursive, db: &Arc<db::Database>, id: Option<i32>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn press_test_inner(url: &Url) -> Result<String, Error> {
|
||||
let stream = stream::FFMPEG.open(stream::Source::Rtsp {
|
||||
url: url.as_str(),
|
||||
redacted_url: url.as_str(), // don't need redaction in config UI.
|
||||
fn press_test_inner(
|
||||
url: Url,
|
||||
username: Option<String>,
|
||||
password: Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
let (extra_data, _stream) = stream::FFMPEG.open(stream::Source::Rtsp {
|
||||
url,
|
||||
username,
|
||||
password,
|
||||
})?;
|
||||
let extra_data = stream.get_extra_data()?;
|
||||
Ok(format!(
|
||||
"{}x{} video stream",
|
||||
extra_data.entry.width, extra_data.entry.height
|
||||
@@ -128,7 +138,7 @@ fn press_test_inner(url: &Url) -> Result<String, Error> {
|
||||
|
||||
fn press_test(siv: &mut Cursive, t: db::StreamType) {
|
||||
let c = get_change(siv);
|
||||
let mut url = match Url::parse(&c.streams[t.index()].rtsp_url) {
|
||||
let url = match Url::parse(&c.streams[t.index()].rtsp_url) {
|
||||
Ok(u) => u,
|
||||
Err(e) => {
|
||||
siv.add_layer(
|
||||
@@ -139,11 +149,9 @@ fn press_test(siv: &mut Cursive, t: db::StreamType) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let username = c.username;
|
||||
let password = c.password;
|
||||
|
||||
if !c.username.is_empty() {
|
||||
let _ = url.set_username(&c.username);
|
||||
let _ = url.set_password(Some(&c.password));
|
||||
}
|
||||
siv.add_layer(
|
||||
views::Dialog::text(format!(
|
||||
"Testing {} stream at {}. This may take a while \
|
||||
@@ -159,7 +167,7 @@ fn press_test(siv: &mut Cursive, t: db::StreamType) {
|
||||
siv.set_fps(5);
|
||||
let sink = siv.cb_sink().clone();
|
||||
::std::thread::spawn(move || {
|
||||
let r = press_test_inner(&url);
|
||||
let r = press_test_inner(url.clone(), username, password);
|
||||
sink.send(Box::new(move |siv: &mut Cursive| {
|
||||
// Polling is no longer necessary.
|
||||
siv.set_fps(0);
|
||||
@@ -442,8 +450,8 @@ fn edit_camera_dialog(db: &Arc<db::Database>, siv: &mut Cursive, item: &Option<i
|
||||
for &(view_id, content) in &[
|
||||
("short_name", &*camera.short_name),
|
||||
("onvif_host", &*camera.onvif_host),
|
||||
("username", &*camera.username),
|
||||
("password", &*camera.password),
|
||||
("username", camera.username.as_deref().unwrap_or("")),
|
||||
("password", camera.password.as_deref().unwrap_or("")),
|
||||
] {
|
||||
dialog
|
||||
.call_on_name(view_id, |v: &mut views::EditView| {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
|
||||
use crate::stream;
|
||||
use crate::streamer;
|
||||
use crate::web;
|
||||
use base::clock;
|
||||
@@ -65,6 +64,14 @@ pub struct Args {
|
||||
/// --http-addr=127.0.0.1:8080.
|
||||
#[structopt(long)]
|
||||
trust_forward_hdrs: bool,
|
||||
|
||||
/// RTSP library to use for fetching the cameras' video stream.
|
||||
/// Moonfire NVR is in the process of switching from `ffmpeg` (the current
|
||||
/// default, used since the beginning of the project) to `retina` (a
|
||||
/// pure-Rust RTSP library developed by Moonfire NVR's author). `retina`
|
||||
/// is still experimental.
|
||||
#[structopt(long, default_value = "ffmpeg", parse(try_from_str))]
|
||||
rtsp_library: crate::stream::RtspLibrary,
|
||||
}
|
||||
|
||||
// These are used in a hack to get the name of the current time zone (e.g. America/Los_Angeles).
|
||||
@@ -203,7 +210,7 @@ pub async fn run(args: &Args) -> Result<i32, Error> {
|
||||
let streams = l.streams_by_id().len();
|
||||
let env = streamer::Environment {
|
||||
db: &db,
|
||||
opener: &*stream::FFMPEG,
|
||||
opener: args.rtsp_library.opener(),
|
||||
shutdown: &shutdown_streamers,
|
||||
};
|
||||
|
||||
@@ -227,6 +234,7 @@ pub async fn run(args: &Args) -> Result<i32, Error> {
|
||||
}
|
||||
|
||||
// Then start up streams.
|
||||
let handle = tokio::runtime::Handle::current();
|
||||
let l = db.lock();
|
||||
for (i, (id, stream)) in l.streams_by_id().iter().enumerate() {
|
||||
if !stream.record {
|
||||
@@ -259,10 +267,12 @@ pub async fn run(args: &Args) -> Result<i32, Error> {
|
||||
)?;
|
||||
info!("Starting streamer for {}", streamer.short_name());
|
||||
let name = format!("s-{}", streamer.short_name());
|
||||
let handle = handle.clone();
|
||||
streamers.push(
|
||||
thread::Builder::new()
|
||||
.name(name)
|
||||
.spawn(move || {
|
||||
let _enter = handle.enter();
|
||||
streamer.run();
|
||||
})
|
||||
.expect("can't create thread"),
|
||||
|
||||
Reference in New Issue
Block a user