diff --git a/db/upgrade/mod.rs b/db/upgrade/mod.rs index 22cd8b7..f6ca38a 100644 --- a/db/upgrade/mod.rs +++ b/db/upgrade/mod.rs @@ -230,19 +230,31 @@ mod tests { insert into video_sample_entry (id, sha1, width, height, data) values (3, X'0000000000000000000000000000000000000002', 704, 480, ?); "#, params![GOOD_ANAMORPHIC_VIDEO_SAMPLE_ENTRY])?; + upgraded.execute(r#" + insert into video_sample_entry (id, sha1, width, height, data) + values (4, X'0000000000000000000000000000000000000003', 704, 480, ?); + "#, params![GOOD_ANAMORPHIC_VIDEO_SAMPLE_ENTRY])?; upgraded.execute_batch(r#" insert into recording (id, camera_id, sample_file_bytes, start_time_90k, duration_90k, local_time_delta_90k, video_samples, video_sync_samples, video_sample_entry_id, sample_file_uuid, sample_file_sha1, video_index) values (1, 1, 42, 140063580000000, 90000, 0, 1, 1, 1, - X'E69D45E8CBA64DC1BA2ECB1585983A10', zeroblob(20), X'00'); + X'E69D45E8CBA64DC1BA2ECB1585983A10', zeroblob(20), X'00'), + (2, 1, 42, 140063580090000, 90000, 0, 1, 1, 2, + X'94DE8484FF874A5295D488C8038A0312', zeroblob(20), X'00'), + (3, 1, 42, 140063580180000, 90000, 0, 1, 1, 3, + X'C94D4D0B533746059CD40B29039E641E', zeroblob(20), X'00'); insert into reserved_sample_files values (X'51EF700C933E4197AAE4EE8161E94221', 0), (X'E69D45E8CBA64DC1BA2ECB1585983A10', 1); "#)?; let rec1 = tmpdir.path().join("e69d45e8-cba6-4dc1-ba2e-cb1585983a10"); + let rec2 = tmpdir.path().join("94de8484-ff87-4a52-95d4-88c8038a0312"); + let rec3 = tmpdir.path().join("c94d4d0b-5337-4605-9cd4-0b29039e641e"); let garbage = tmpdir.path().join("51ef700c-933e-4197-aae4-ee8161e94221"); std::fs::File::create(&rec1)?; + std::fs::File::create(&rec2)?; + std::fs::File::create(&rec3)?; std::fs::File::create(&garbage)?; for (ver, fresh_sql) in &[(1, Some(include_str!("v1.sql"))), @@ -287,6 +299,9 @@ mod tests { assert_eq!(pasp_by_id.get(&1), Some(&(1, 1))); assert_eq!(pasp_by_id.get(&2), Some(&(4, 3))); assert_eq!(pasp_by_id.get(&3), Some(&(40, 33))); + + // No recording references this video_sample_entry, so it gets dropped on upgrade. + assert_eq!(pasp_by_id.get(&4), None); } } diff --git a/db/upgrade/v5_to_v6.rs b/db/upgrade/v5_to_v6.rs index fcab056..7dfef9a 100644 --- a/db/upgrade/v5_to_v6.rs +++ b/db/upgrade/v5_to_v6.rs @@ -31,7 +31,7 @@ /// Upgrades a version 4 schema to a version 5 schema. use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use failure::{Error, bail, format_err}; +use failure::{Error, ResultExt, bail, format_err}; use h264_reader::avcc::AvcDecoderConfigurationRecord; use rusqlite::{named_params, params}; use std::convert::{TryFrom, TryInto}; @@ -93,6 +93,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> values (:id, :width, :height, :rfc6381_codec, :data, :pasp_h_spacing, :pasp_v_spacing) "#)?; + + // Only insert still-referenced video sample entries. I've had problems with + // no-longer-referenced ones (perhaps from some ancient, buggy version of Moonfire NVR) for + // which avcc.create_context(()) fails. let mut stmt = tx.prepare(r#" select id, @@ -101,7 +105,15 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> rfc6381_codec, data from - old_video_sample_entry + old_video_sample_entry v + where + exists ( + select + 'x' + from + recording r + where + r.video_sample_entry_id = v.id) "#)?; let mut rows = stmt.query(params![])?; while let Some(row) = rows.next()? { @@ -115,9 +127,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> bail!("Multiple SPSs!"); } let ctx = avcc.create_context(()) - .map_err(|e| format_err!("Can't load SPS+PPS: {:?}", e))?; + .map_err(|e| format_err!("Can't load SPS+PPS for video_sample_entry_id {}: {:?}", + id, e))?; let sps = ctx.sps_by_id(h264_reader::nal::pps::ParamSetId::from_u32(0).unwrap()) - .ok_or_else(|| format_err!("No SPS 0"))?; + .ok_or_else(|| format_err!("No SPS 0 for video_sample_entry_id {}", id))?; let pasp = sps.vui_parameters.as_ref() .and_then(|v| v.aspect_ratio_info.as_ref()) .and_then(|a| a.clone().get()) @@ -257,8 +270,8 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> ":video_samples": video_samples, ":video_sync_samples": video_sync_samples, ":video_sample_entry_id": video_sample_entry_id, - })?; - cum_duration_90k += duration_90k; + }).with_context(|_| format!("Unable to insert composite_id {}", composite_id))?; + cum_duration_90k += i64::from(duration_90k); cum_runs += if run_offset == 0 { 1 } else { 0 }; } tx.execute_batch(r#"