introduce /etc/moonfire-nvr.json (#133)
This commit is contained in:
parent
1a51b53b54
commit
ceaef46ea9
|
@ -8,6 +8,8 @@ Each release is tagged in Git and on the Docker repository
|
|||
|
||||
## unreleased
|
||||
|
||||
* introduce a configuration file `/etc/moonfire-nvr.json`; you will need
|
||||
to create one when upgrading.
|
||||
* bump minimum Rust version from 1.53 to 1.56.
|
||||
* fix [#187](https://github.com/scottlamb/moonfire-nvr/issues/187):
|
||||
incompatibility with cameras that (incorrectly) omit the SDP origin line.
|
||||
|
|
|
@ -277,23 +277,19 @@ some of the shell script's subcommands that wrap Docker (`start`, `stop`, and
|
|||
If you want to deploy a non-Docker build on Linux, you may want to use
|
||||
`systemd`. Create `/etc/systemd/system/moonfire-nvr.service`:
|
||||
|
||||
```
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Moonfire NVR
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/moonfire-nvr run \
|
||||
--db-dir=/var/lib/moonfire-nvr/db \
|
||||
--http-addr=0.0.0.0:8080 \
|
||||
--allow-unauthenticated-permissions='view_video: true'
|
||||
ExecStart=/usr/local/bin/moonfire-nvr run
|
||||
Environment=TZ=:/etc/localtime
|
||||
Environment=MOONFIRE_FORMAT=google-systemd
|
||||
Environment=MOONFIRE_LOG=info
|
||||
Environment=RUST_BACKTRACE=1
|
||||
Type=simple
|
||||
User=moonfire-nvr
|
||||
Nice=-20
|
||||
Restart=on-failure
|
||||
CPUAccounting=true
|
||||
MemoryAccounting=true
|
||||
|
@ -303,10 +299,24 @@ BlockIOAccounting=true
|
|||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Note that the arguments used here are insecure. You can change that via
|
||||
replacing the `--allow-unauthenticated-permissions` argument here as
|
||||
described in [Securing Moonfire NVR and exposing it to the
|
||||
Internet](secure.md).
|
||||
You'll also need a `/etc/moonfire-nvr.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"binds": [
|
||||
{
|
||||
"ipv4": "0.0.0.0:8080",
|
||||
"allowUnauthenticatedPermissions": {
|
||||
"viewVideo": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Note this configuration is insecure. You can change that via replacing the
|
||||
`allowUnauthenticatedPermissions` here as described in [Securing Moonfire NVR
|
||||
and exposing it to the Internet](secure.md).
|
||||
|
||||
Some handy commands:
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ image_name="scottlamb/moonfire-nvr:latest"
|
|||
container_name="moonfire-nvr"
|
||||
common_docker_run_args=(
|
||||
--mount=type=bind,source=/var/lib/moonfire-nvr,destination=/var/lib/moonfire-nvr
|
||||
--mount=type=bind,source=/etc/moonfire-nvr.json,destination=/etc/moonfire-nvr.json
|
||||
|
||||
# Add additional mount lines here for each sample file directory
|
||||
# outside of /var/lib/moonfire-nvr, eg:
|
||||
|
@ -107,12 +108,6 @@ run)
|
|||
--name="${container_name}" \
|
||||
"${image_name}" \
|
||||
run \
|
||||
|
||||
# Add any additional `moonfire-nvr run` arguments here, eg
|
||||
# "--rtsp-library=ffmpeg" if the default "--rtsp-library=retina"
|
||||
# isn't working.
|
||||
--allow-unauthenticated-permissions='view_video: true' \
|
||||
|
||||
"$@"
|
||||
;;
|
||||
start|stop|logs|rm)
|
||||
|
@ -263,6 +258,21 @@ In the user interface,
|
|||
|
||||
### Starting it up
|
||||
|
||||
You'll need to create the runtime configuration file, `/etc/moonfire-nvr.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"binds": [
|
||||
{
|
||||
"ipv4": "0.0.0.0:8080",
|
||||
"allowUnauthenticatedPermissions": {
|
||||
"viewVideo": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Note that at this stage, Moonfire NVR's web interface is **insecure**: it
|
||||
doesn't use `https` and doesn't require you to authenticate
|
||||
to it. You might be comfortable starting it in this configuration to try it
|
||||
|
|
|
@ -161,31 +161,33 @@ your browser. See [How to secure Nginx with Let's Encrypt on Ubuntu
|
|||
|
||||
## 6. Reconfigure Moonfire NVR
|
||||
|
||||
If you follow the recommended Docker setup, your `/usr/local/bin/nvr` script
|
||||
will contain this line:
|
||||
If you follow the recommended Docker setup, your `/etc/moonfire-nvr.json`
|
||||
will contain these lines:
|
||||
|
||||
```
|
||||
--allow-unauthenticated-permissions='view_video: true'
|
||||
```json
|
||||
"allowUnauthenticatedPermissions": {
|
||||
"viewVideo": true
|
||||
}
|
||||
```
|
||||
|
||||
Replace it with the following:
|
||||
Replace them with the following:
|
||||
|
||||
```
|
||||
--trust-forward-hdrs
|
||||
```json
|
||||
"trustForwardHdrs": true
|
||||
```
|
||||
|
||||
This change has two effects:
|
||||
|
||||
* No `--allow-unauthenticated-permissions` means that web users must
|
||||
authenticate.
|
||||
* `--trust-forward-hdrs` means that Moonfire NVR will look for `X-Real-IP`
|
||||
* No `allowUnauthenticatePermissions` means that web users must authenticate.
|
||||
* `trustForwardHdrs` means that Moonfire NVR will look for `X-Real-IP`
|
||||
and `X-Forwarded-Proto` headers as added by the webserver configuration
|
||||
in the next section.
|
||||
|
||||
If the webserver is running on the same machine as Moonfire NVR, you might
|
||||
also change `--publish=8080:8080` to `--publish=127.0.0.1:8080:8080`, which
|
||||
prevents other machines on the network from impersonating the proxy,
|
||||
effectively allowing them to lie about the client's IP and protocol.
|
||||
also change `--publish=8080:8080` to `--publish=127.0.0.1:8080:8080` in your
|
||||
`/usr/local/bin/nvr` script, preventing other machines on the network from
|
||||
impersonating the proxy, effectively allowing them to lie about the client's IP
|
||||
and protocol.
|
||||
|
||||
To make this take effect, you'll need to stop the running Docker container,
|
||||
delete it, and create/run a new one:
|
||||
|
|
|
@ -27,7 +27,7 @@ mod proto {
|
|||
}
|
||||
mod raw;
|
||||
pub mod recording;
|
||||
use proto::schema;
|
||||
pub use proto::schema;
|
||||
pub mod signal;
|
||||
pub mod upgrade;
|
||||
pub mod writer;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
// This file is part of Moonfire NVR, a security camera network video recorder.
|
||||
// Copyright (C) 2022 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
|
||||
//! Runtime configuration file (`/etc/moonfire-nvr.conf`).
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
fn default_db_dir() -> PathBuf {
|
||||
"/var/lib/moonfire-nvr/db".into()
|
||||
}
|
||||
|
||||
fn default_ui_dir() -> PathBuf {
|
||||
"/usr/local/lib/moonfire-nvr/ui".into()
|
||||
}
|
||||
|
||||
/// Top-level configuration file object.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConfigFile {
|
||||
pub binds: Vec<BindConfig>,
|
||||
|
||||
/// Directory holding the SQLite3 index database.
|
||||
#[serde(default = "default_db_dir")]
|
||||
pub db_dir: PathBuf,
|
||||
|
||||
/// Directory holding user interface files (`.html`, `.js`, etc).
|
||||
#[serde(default = "default_ui_dir")]
|
||||
pub ui_dir: PathBuf,
|
||||
|
||||
/// The number of worker threads used by the asynchronous runtime.
|
||||
///
|
||||
/// Defaults to the number of cores on the system.
|
||||
#[serde(default)]
|
||||
pub worker_threads: Option<usize>,
|
||||
|
||||
/// RTSP library to use for fetching the cameras' video stream.
|
||||
/// Moonfire NVR is in the process of switching from `ffmpeg` (used since
|
||||
/// the beginning of the project) to `retina` (a pure-Rust RTSP library
|
||||
/// developed by Moonfire NVR's author).
|
||||
#[serde(default)]
|
||||
pub rtsp_library: crate::stream::RtspLibrary,
|
||||
}
|
||||
|
||||
/// Per-bind configuration.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BindConfig {
|
||||
/// The address to bind to.
|
||||
#[serde(flatten)]
|
||||
pub address: AddressConfig,
|
||||
|
||||
/// Allow unauthenticated API access on this bind, with the given
|
||||
/// permissions (defaults to empty).
|
||||
///
|
||||
/// Note that even an empty string allows some basic access that would be rejected if the
|
||||
/// argument were omitted.
|
||||
#[serde(default)]
|
||||
pub allow_unauthenticated_permissions: Option<Permissions>,
|
||||
|
||||
/// Trusts `X-Real-IP:` and `X-Forwarded-Proto:` headers on the incoming request.
|
||||
///
|
||||
/// Set this only after ensuring your proxy server is configured to set them
|
||||
/// and that no untrusted requests bypass the proxy server. You may want to
|
||||
/// specify a localhost bind address.
|
||||
#[serde(default)]
|
||||
pub trust_forward_hdrs: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AddressConfig {
|
||||
/// IPv4 address such as `0.0.0.0:8080` or `127.0.0.1:8080`.
|
||||
Ipv4(std::net::SocketAddrV4),
|
||||
|
||||
/// IPv6 address such as `[::]:8080` or `[::1]:8080`.
|
||||
Ipv6(std::net::SocketAddrV6),
|
||||
// TODO: /// Unix socket path such as `/var/lib/moonfire-nvr/sock`.
|
||||
// Unix(PathBuf),
|
||||
|
||||
// TODO: SystemdFileDescriptorName(String), see
|
||||
// https://www.freedesktop.org/software/systemd/man/systemd.socket.html
|
||||
}
|
||||
|
||||
/// JSON analog of `Permissions` defined in `db/proto/schema.proto`.
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Permissions {
|
||||
view_video: bool,
|
||||
read_camera_configs: bool,
|
||||
update_signals: bool,
|
||||
}
|
||||
|
||||
impl Permissions {
|
||||
pub fn as_proto(&self) -> db::schema::Permissions {
|
||||
db::schema::Permissions {
|
||||
view_video: self.view_video,
|
||||
read_camera_configs: self.read_camera_configs,
|
||||
update_signals: self.update_signals,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
// This file is part of Moonfire NVR, a security camera network video recorder.
|
||||
// Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// Copyright (C) 2022 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::cmds::run::config::Permissions;
|
||||
use crate::streamer;
|
||||
use crate::web;
|
||||
use base::clock;
|
||||
|
@ -11,75 +12,28 @@ use fnv::FnvHashMap;
|
|||
use hyper::service::{make_service_fn, service_fn};
|
||||
use log::error;
|
||||
use log::{info, warn};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use structopt::StructOpt;
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
|
||||
use self::config::ConfigFile;
|
||||
|
||||
mod config;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct Args {
|
||||
/// Directory holding the SQLite3 index database.
|
||||
#[structopt(
|
||||
long,
|
||||
default_value = "/var/lib/moonfire-nvr/db",
|
||||
value_name = "path",
|
||||
parse(from_os_str)
|
||||
)]
|
||||
db_dir: PathBuf,
|
||||
|
||||
/// The number of worker threads used by the asynchronous runtime.
|
||||
/// Defaults to the number of cores on the system.
|
||||
#[structopt(long, value_name = "worker_threads")]
|
||||
worker_threads: Option<usize>,
|
||||
|
||||
/// Directory holding user interface files (.html, .js, etc).
|
||||
#[structopt(
|
||||
long,
|
||||
default_value = "/usr/local/lib/moonfire-nvr/ui",
|
||||
value_name = "path",
|
||||
parse(from_os_str)
|
||||
)]
|
||||
ui_dir: std::path::PathBuf,
|
||||
|
||||
/// Bind address for unencrypted HTTP server.
|
||||
#[structopt(long, default_value = "0.0.0.0:8080", parse(try_from_str))]
|
||||
http_addr: std::net::SocketAddr,
|
||||
#[structopt(short, long, default_value = "/etc/moonfire-nvr.json")]
|
||||
config: PathBuf,
|
||||
|
||||
/// Open the database in read-only mode and disables recording.
|
||||
///
|
||||
/// Note this is incompatible with authentication, so you'll likely want to specify
|
||||
/// --allow_unauthenticated_permissions.
|
||||
/// Note this is incompatible with session authentication; consider adding
|
||||
/// a bind with `allowUnauthenticatedPermissions` your config.
|
||||
#[structopt(long)]
|
||||
read_only: bool,
|
||||
|
||||
/// Allow unauthenticated access to the web interface, with the given permissions (may be
|
||||
/// empty). Should be a text Permissions protobuf such as "view_videos: true".
|
||||
///
|
||||
/// Note that even an empty string allows some basic access that would be rejected if the
|
||||
/// argument were omitted.
|
||||
#[structopt(long, parse(try_from_str = protobuf::text_format::parse_from_str))]
|
||||
allow_unauthenticated_permissions: Option<db::Permissions>,
|
||||
|
||||
/// Trust X-Real-IP: and X-Forwarded-Proto: headers on the incoming request.
|
||||
///
|
||||
/// Set this only after ensuring your proxy server is configured to set them and that no
|
||||
/// untrusted requests bypass the proxy server. You may want to specify
|
||||
/// --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` (used since
|
||||
/// the beginning of the project) to `retina` (a pure-Rust RTSP library
|
||||
/// developed by Moonfire NVR's author).
|
||||
#[structopt(long, default_value = "retina", parse(try_from_str))]
|
||||
rtsp_library: crate::stream::RtspLibrary,
|
||||
|
||||
/// The RTSP transport (`tcp` or `udp`) to use when none is specified in the
|
||||
/// per-stream configuration.
|
||||
#[structopt(long, default_value)]
|
||||
rtsp_transport: retina::client::Transport,
|
||||
}
|
||||
|
||||
// These are used in a hack to get the name of the current time zone (e.g. America/Los_Angeles).
|
||||
|
@ -171,14 +125,23 @@ struct Syncer {
|
|||
join: thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
fn read_config(path: &Path) -> Result<ConfigFile, Error> {
|
||||
let config = std::fs::read(path)?;
|
||||
let config = serde_json::from_slice(&config)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<i32, Error> {
|
||||
let config = read_config(&args.config)
|
||||
.with_context(|_| format!("unable to read {}", &args.config.display()))?;
|
||||
|
||||
let mut builder = tokio::runtime::Builder::new_multi_thread();
|
||||
builder.enable_all();
|
||||
if let Some(worker_threads) = args.worker_threads {
|
||||
if let Some(worker_threads) = config.worker_threads {
|
||||
builder.worker_threads(worker_threads);
|
||||
}
|
||||
let rt = builder.build()?;
|
||||
let r = rt.block_on(async_run(args));
|
||||
let r = rt.block_on(async_run(args.read_only, &config));
|
||||
|
||||
// tokio normally waits for all spawned tasks to complete, but:
|
||||
// * in the graceful shutdown path, we wait for specific tasks with logging.
|
||||
|
@ -188,14 +151,14 @@ pub fn run(args: Args) -> Result<i32, Error> {
|
|||
r
|
||||
}
|
||||
|
||||
async fn async_run(args: Args) -> Result<i32, Error> {
|
||||
async fn async_run(read_only: bool, config: &ConfigFile) -> Result<i32, Error> {
|
||||
let (shutdown_tx, shutdown_rx) = base::shutdown::channel();
|
||||
let mut shutdown_tx = Some(shutdown_tx);
|
||||
|
||||
tokio::pin! {
|
||||
let int = signal(SignalKind::interrupt())?;
|
||||
let term = signal(SignalKind::terminate())?;
|
||||
let inner = inner(args, shutdown_rx);
|
||||
let inner = inner(read_only, config, shutdown_rx);
|
||||
}
|
||||
|
||||
tokio::select! {
|
||||
|
@ -219,17 +182,21 @@ async fn async_run(args: Args) -> Result<i32, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn inner(args: Args, shutdown_rx: base::shutdown::Receiver) -> Result<i32, Error> {
|
||||
async fn inner(
|
||||
read_only: bool,
|
||||
config: &ConfigFile,
|
||||
shutdown_rx: base::shutdown::Receiver,
|
||||
) -> Result<i32, Error> {
|
||||
let clocks = clock::RealClocks {};
|
||||
let (_db_dir, conn) = super::open_conn(
|
||||
&args.db_dir,
|
||||
if args.read_only {
|
||||
&config.db_dir,
|
||||
if read_only {
|
||||
super::OpenMode::ReadOnly
|
||||
} else {
|
||||
super::OpenMode::ReadWrite
|
||||
},
|
||||
)?;
|
||||
let db = Arc::new(db::Database::new(clocks, conn, !args.read_only)?);
|
||||
let db = Arc::new(db::Database::new(clocks, conn, !read_only)?);
|
||||
info!("Database is loaded.");
|
||||
|
||||
{
|
||||
|
@ -245,19 +212,12 @@ async fn inner(args: Args, shutdown_rx: base::shutdown::Receiver) -> Result<i32,
|
|||
|
||||
let time_zone_name = resolve_zone()?;
|
||||
info!("Resolved timezone: {}", &time_zone_name);
|
||||
let svc = Arc::new(web::Service::new(web::Config {
|
||||
db: db.clone(),
|
||||
ui_dir: Some(&args.ui_dir),
|
||||
allow_unauthenticated_permissions: args.allow_unauthenticated_permissions.clone(),
|
||||
trust_forward_hdrs: args.trust_forward_hdrs,
|
||||
time_zone_name,
|
||||
})?);
|
||||
|
||||
// Start a streamer for each stream.
|
||||
let mut streamers = Vec::new();
|
||||
let mut session_groups_by_camera: FnvHashMap<i32, Arc<retina::client::SessionGroup>> =
|
||||
FnvHashMap::default();
|
||||
let syncers = if !args.read_only {
|
||||
let syncers = if !read_only {
|
||||
let l = db.lock();
|
||||
let mut dirs = FnvHashMap::with_capacity_and_hasher(
|
||||
l.sample_file_dirs_by_id().len(),
|
||||
|
@ -266,8 +226,7 @@ async fn inner(args: Args, shutdown_rx: base::shutdown::Receiver) -> Result<i32,
|
|||
let streams = l.streams_by_id().len();
|
||||
let env = streamer::Environment {
|
||||
db: &db,
|
||||
opener: args.rtsp_library.opener(),
|
||||
default_transport: args.rtsp_transport,
|
||||
opener: config.rtsp_library.opener(),
|
||||
shutdown_rx: &shutdown_rx,
|
||||
};
|
||||
|
||||
|
@ -354,19 +313,40 @@ async fn inner(args: Args, shutdown_rx: base::shutdown::Receiver) -> Result<i32,
|
|||
None
|
||||
};
|
||||
|
||||
// Start the web interface.
|
||||
let make_svc = make_service_fn(move |_conn| {
|
||||
futures::future::ok::<_, std::convert::Infallible>(service_fn({
|
||||
let svc = Arc::clone(&svc);
|
||||
move |req| Arc::clone(&svc).serve(req)
|
||||
}))
|
||||
});
|
||||
let server = ::hyper::Server::try_bind(&args.http_addr)
|
||||
.with_context(|_| format!("unable to bind --http-addr={}", &args.http_addr))?
|
||||
.tcp_nodelay(true)
|
||||
.serve(make_svc);
|
||||
let server = server.with_graceful_shutdown(shutdown_rx.future());
|
||||
let server_handle = tokio::spawn(server);
|
||||
// Start the web interface(s).
|
||||
let web_handles: Result<Vec<_>, Error> = config
|
||||
.binds
|
||||
.iter()
|
||||
.map(|b| {
|
||||
let svc = Arc::new(web::Service::new(web::Config {
|
||||
db: db.clone(),
|
||||
ui_dir: Some(&config.ui_dir),
|
||||
allow_unauthenticated_permissions: b
|
||||
.allow_unauthenticated_permissions
|
||||
.as_ref()
|
||||
.map(Permissions::as_proto),
|
||||
trust_forward_hdrs: b.trust_forward_hdrs,
|
||||
time_zone_name: time_zone_name.clone(),
|
||||
})?);
|
||||
let make_svc = make_service_fn(move |_conn| {
|
||||
futures::future::ok::<_, std::convert::Infallible>(service_fn({
|
||||
let svc = Arc::clone(&svc);
|
||||
move |req| Arc::clone(&svc).serve(req)
|
||||
}))
|
||||
});
|
||||
let socket_addr = match b.address {
|
||||
config::AddressConfig::Ipv4(a) => a.into(),
|
||||
config::AddressConfig::Ipv6(a) => a.into(),
|
||||
};
|
||||
let server = ::hyper::Server::try_bind(&socket_addr)
|
||||
.with_context(|_| format!("unable to bind to {}", &socket_addr))?
|
||||
.tcp_nodelay(true)
|
||||
.serve(make_svc);
|
||||
let server = server.with_graceful_shutdown(shutdown_rx.future());
|
||||
Ok(tokio::spawn(server))
|
||||
})
|
||||
.collect();
|
||||
let web_handles = web_handles?;
|
||||
|
||||
info!("Ready to serve HTTP requests");
|
||||
let _ = shutdown_rx.as_future().await;
|
||||
|
@ -394,7 +374,9 @@ async fn inner(args: Args, shutdown_rx: base::shutdown::Receiver) -> Result<i32,
|
|||
db.lock().clear_watches();
|
||||
|
||||
info!("Waiting for HTTP requests to finish.");
|
||||
server_handle.await??;
|
||||
for h in web_handles {
|
||||
h.await??;
|
||||
}
|
||||
|
||||
info!("Waiting for TEARDOWN requests to complete.");
|
||||
for g in session_groups_by_camera.values() {
|
|
@ -11,6 +11,7 @@ use lazy_static::lazy_static;
|
|||
use log::warn;
|
||||
use retina::client::{Credentials, Transport};
|
||||
use retina::codec::{CodecItem, VideoParameters};
|
||||
use serde::Deserialize;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::CString;
|
||||
use std::pin::Pin;
|
||||
|
@ -26,11 +27,18 @@ lazy_static! {
|
|||
pub static ref FFMPEG: Ffmpeg = Ffmpeg::new();
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
pub enum RtspLibrary {
|
||||
Ffmpeg,
|
||||
Retina,
|
||||
}
|
||||
|
||||
impl Default for RtspLibrary {
|
||||
fn default() -> Self {
|
||||
RtspLibrary::Retina
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for RtspLibrary {
|
||||
type Err = Error;
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ where
|
|||
C: Clocks + Clone,
|
||||
{
|
||||
pub opener: &'a dyn stream::Opener,
|
||||
pub default_transport: retina::client::Transport,
|
||||
pub db: &'tmp Arc<Database<C>>,
|
||||
pub shutdown_rx: &'tmp base::shutdown::Receiver,
|
||||
}
|
||||
|
@ -96,7 +95,7 @@ where
|
|||
dir,
|
||||
syncer_channel,
|
||||
opener: env.opener,
|
||||
transport: stream_transport.unwrap_or(env.default_transport),
|
||||
transport: stream_transport.unwrap_or_default(),
|
||||
stream_id,
|
||||
session_group,
|
||||
short_name: format!("{}-{}", c.short_name, s.type_.as_str()),
|
||||
|
@ -434,7 +433,6 @@ mod tests {
|
|||
opener: &opener,
|
||||
db: &db.db,
|
||||
shutdown_rx: &shutdown_rx,
|
||||
default_transport: retina::client::Transport::Tcp,
|
||||
};
|
||||
let mut stream;
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue