mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-11-10 05:59:44 -05:00
read sample files from dedicated threads
Reading from the mmap()ed region in the tokio threads could cause
them to stall:
* That could affect UI serving when there were concurrent
UI requests (i.e., not just requests that needed the reads in
question anyway).
* If there's a faulty disk, it could cause the UI to totally hang.
Better to not mix disks between threads.
* Soon, I want to handle RTSP from the tokio threads (#37). Similarly,
we don't want RTSP streaming to block on operations from unrelated
disks.
I went with just one thread per disk which I think is sufficient.
But it'd be possible to do a fixed-size pool instead which might improve
latency when some pages are already cached.
I also dropped the memmap dependency. I had to compute the page
alignment anyway to get mremap to work, and Moonfire NVR already is
Unix-specific, so there wasn't much value from the memmap or memmap2
crates.
Fixes #88
This commit is contained in:
@@ -1,8 +1,18 @@
|
||||
// This file is part of Moonfire NVR, a security camera network video recorder.
|
||||
// Copyright (C) 2018 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
// Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
//! Tools for implementing a `http_serve::Entity` body composed from many "slices".
|
||||
//! HTTP body implementation using `ARefss<'static, [u8]>` chunks.
|
||||
//!
|
||||
//! Moonfire NVR uses this custom chunk type rather than [bytes::Bytes]. This
|
||||
//! is mostly for historical reasons: we used to use `mmap`-backed chunks.
|
||||
//! The custom chunk type also helps minimize reference-counting in `mp4::File`
|
||||
//! as described [here](https://github.com/tokio-rs/bytes/issues/359#issuecomment-640812016),
|
||||
//! although this is a pretty small optimization.
|
||||
//!
|
||||
//! Some day I expect [bytes::Bytes] will expose its vtable (see link above),
|
||||
//! allowing us to minimize reference-counting while using the standard
|
||||
//! [hyper::Body].
|
||||
|
||||
use base::Error;
|
||||
use futures::{stream, Stream};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 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.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
|
||||
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
|
||||
|
||||
//! `.mp4` virtual file serving.
|
||||
//!
|
||||
@@ -61,7 +61,7 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||
use bytes::BytesMut;
|
||||
use db::dir;
|
||||
use db::recording::{self, rescale, TIME_UNITS_PER_SEC};
|
||||
use futures::stream;
|
||||
use futures::stream::{self, TryStreamExt};
|
||||
use futures::Stream;
|
||||
use http::header::HeaderValue;
|
||||
use hyper::body::Buf;
|
||||
@@ -801,7 +801,7 @@ impl slices::Slice for Slice {
|
||||
SliceType::Stsz => self.wrap_index(f, range.clone(), len, &Segment::stsz),
|
||||
SliceType::Stss => self.wrap_index(f, range.clone(), len, &Segment::stss),
|
||||
SliceType::Co64 => f.0.get_co64(range.clone(), len),
|
||||
SliceType::VideoSampleData => f.0.get_video_sample_data(p, range.clone()),
|
||||
SliceType::VideoSampleData => return f.0.get_video_sample_data(p, range.clone()),
|
||||
SliceType::SubtitleSampleData => f.0.get_subtitle_sample_data(p, range.clone(), len),
|
||||
SliceType::Truns => self.wrap_truns(f, range.clone(), len as usize),
|
||||
};
|
||||
@@ -1799,24 +1799,19 @@ impl FileInner {
|
||||
///
|
||||
/// * If the backing file is truncated, the program will crash with `SIGBUS`. This shouldn't
|
||||
/// happen because nothing should be touching Moonfire NVR's files but itself.
|
||||
fn get_video_sample_data(&self, i: usize, r: Range<u64>) -> Result<Chunk, Error> {
|
||||
fn get_video_sample_data(&self, i: usize, r: Range<u64>) -> Box<dyn Stream<Item = Result<Chunk, BoxedError>> + Send + Sync> {
|
||||
let s = &self.segments[i];
|
||||
let f = self
|
||||
.dirs_by_stream_id
|
||||
.get(&s.s.id.stream())
|
||||
.ok_or_else(|| format_err_t!(NotFound, "{}: stream not found", s.s.id))?
|
||||
.open_file(s.s.id)
|
||||
.err_kind(ErrorKind::Unknown)?;
|
||||
let start = s.s.sample_file_range().start + r.start;
|
||||
let mmap = Box::new(unsafe {
|
||||
memmap::MmapOptions::new()
|
||||
.offset(start)
|
||||
.len((r.end - r.start) as usize)
|
||||
.map(&f)
|
||||
.err_kind(ErrorKind::Internal)?
|
||||
});
|
||||
use core::ops::Deref;
|
||||
Ok(ARefss::new(mmap).map(|m| m.deref()).into())
|
||||
let sr = s.s.sample_file_range();
|
||||
let f = match self.dirs_by_stream_id.get(&s.s.id.stream()) {
|
||||
None => return Box::new(stream::iter(std::iter::once(Err(
|
||||
wrap_error(format_err_t!(NotFound, "{}: stream not found", s.s.id))
|
||||
)))),
|
||||
Some(d) => d.open_file(s.s.id, (r.start + sr.start)..(r.end + sr.start - r.start)),
|
||||
};
|
||||
Box::new(f
|
||||
.map_ok(Chunk::from)
|
||||
.map_err(wrap_error)
|
||||
)
|
||||
}
|
||||
|
||||
fn get_subtitle_sample_data(&self, i: usize, r: Range<u64>, len: u64) -> Result<Chunk, Error> {
|
||||
|
||||
Reference in New Issue
Block a user