mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-27 15:45:55 -05:00
gracefully handle bad video_indexes during upgrade
Dolf reported hitting this problem: $ sudo -u moonfire-nvr RUST_LOG=info RUST_BACKTRACE=1 release/moonfire-nvr --upgrade Jan 06 17:10:57.148 INFO Upgrading database from version 0 to version 1... Jan 06 17:10:57.149 INFO ...database now in journal_mode delete (requested delete). Jan 06 17:10:57.149 INFO ...from version 0 to version 1 thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { description: "zero duration only allowed at end; have 3123 bytes left", cause: None }', /buildslave/rust-buildbot/slave/stable-dist-rustc-cross-host-linux/build/src/libcore/result.rs:837 The indexes were being scanned on upgrade to set the trailing zero flag which is some sanity checking for /view.mp4 URLs. It's not a big problem to skip it for some funny recordings to let the update proceed. Separately, I'll add validation of the pts when writing a recording; it will report error and end the recording (retrying a second later) rather than write an unplayable database enty. Probably also a good time to add a --check to spot database problems such as this and recording rows without a matching sample file or vice versa.
This commit is contained in:
parent
a7e1c9473a
commit
21b8e0b6df
@ -35,6 +35,7 @@ use error::Error;
|
|||||||
use recording;
|
use recording;
|
||||||
use rusqlite;
|
use rusqlite;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use strutil;
|
||||||
|
|
||||||
pub fn run(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
pub fn run(tx: &rusqlite::Transaction) -> Result<(), Error> {
|
||||||
// These create statements match the schema.sql when version 1 was the latest.
|
// These create statements match the schema.sql when version 1 was the latest.
|
||||||
@ -101,6 +102,12 @@ struct CameraState {
|
|||||||
next_recording_id: i32,
|
next_recording_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_trailing_zero(video_index: &[u8]) -> Result<bool, Error> {
|
||||||
|
let mut it = recording::SampleIndexIterator::new();
|
||||||
|
while it.next(video_index)? {}
|
||||||
|
Ok(it.duration_90k == 0)
|
||||||
|
}
|
||||||
|
|
||||||
/// Fills the `recording` and `recording_playback` tables from `old_recording`, returning
|
/// Fills the `recording` and `recording_playback` tables from `old_recording`, returning
|
||||||
/// the `camera_state` map for use by a following call to `fill_cameras`.
|
/// the `camera_state` map for use by a following call to `fill_cameras`.
|
||||||
fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState>, Error> {
|
fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState>, Error> {
|
||||||
@ -116,7 +123,8 @@ fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState
|
|||||||
video_sample_entry_id,
|
video_sample_entry_id,
|
||||||
sample_file_uuid,
|
sample_file_uuid,
|
||||||
sample_file_sha1,
|
sample_file_sha1,
|
||||||
video_index
|
video_index,
|
||||||
|
id
|
||||||
from
|
from
|
||||||
old_recording
|
old_recording
|
||||||
"#)?;
|
"#)?;
|
||||||
@ -153,11 +161,13 @@ fn fill_recording(tx: &rusqlite::Transaction) -> Result<HashMap<i32, CameraState
|
|||||||
let sample_file_uuid: Vec<u8> = row.get_checked(8)?;
|
let sample_file_uuid: Vec<u8> = row.get_checked(8)?;
|
||||||
let sample_file_sha1: Vec<u8> = row.get_checked(9)?;
|
let sample_file_sha1: Vec<u8> = row.get_checked(9)?;
|
||||||
let video_index: Vec<u8> = row.get_checked(10)?;
|
let video_index: Vec<u8> = row.get_checked(10)?;
|
||||||
let trailing_zero = {
|
let old_id: i32 = row.get_checked(11)?;
|
||||||
let mut it = recording::SampleIndexIterator::new();
|
let trailing_zero = has_trailing_zero(&video_index).unwrap_or_else(|e| {
|
||||||
while it.next(&video_index)? {}
|
warn!("recording {}/{} (sample file {}, formerly recording {}) has corrupt \
|
||||||
it.duration_90k == 0
|
video_index: {}",
|
||||||
};
|
camera_id, composite_id & 0xFFFF, strutil::hex(&sample_file_uuid), old_id, e);
|
||||||
|
false
|
||||||
|
});
|
||||||
let run_id = match camera_state.current_run {
|
let run_id = match camera_state.current_run {
|
||||||
Some((run_id, expected_start)) if expected_start == start_time_90k => run_id,
|
Some((run_id, expected_start)) if expected_start == start_time_90k => run_id,
|
||||||
_ => composite_id,
|
_ => composite_id,
|
||||||
|
Loading…
Reference in New Issue
Block a user