diff --git a/src/dir.rs b/src/dir.rs index 7432754..4398069 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -554,6 +554,8 @@ impl<'a> InnerWriter<'a> { sha1_bytes.copy_from_slice(&self.hasher.finish()?[..]); let start_time = self.prev_end.unwrap_or(local_start); let end = start_time + recording::Duration(self.index.total_duration_90k as i64); + let flags = if self.index.has_trailing_zero() { db::RecordingFlags::TrailingZero as i32 } + else { 0 }; let recording = db::RecordingToInsert{ camera_id: self.camera_id, sample_file_bytes: self.index.sample_file_bytes, @@ -566,7 +568,7 @@ impl<'a> InnerWriter<'a> { video_index: self.index.video_index, sample_file_sha1: sha1_bytes, run_offset: self.run_offset, - flags: 0, // TODO + flags: flags, }; self.syncer_channel.async_save_recording(recording, self.f); Ok(PreviousWriter{ diff --git a/src/recording.rs b/src/recording.rs index 0be4a31..06bc27b 100644 --- a/src/recording.rs +++ b/src/recording.rs @@ -335,6 +335,8 @@ impl SampleIndexEncoder { append_varint32((zigzag32(duration_delta) << 1) | (is_key as u32), &mut self.video_index); append_varint32(zigzag32(bytes_delta), &mut self.video_index); } + + pub fn has_trailing_zero(&self) -> bool { self.prev_duration_90k == 0 } } /// A segment represents a view of some or all of a single recording, starting from a key frame. diff --git a/src/streamer.rs b/src/streamer.rs index 8b9745a..078b317 100644 --- a/src/streamer.rs +++ b/src/streamer.rs @@ -192,6 +192,7 @@ mod tests { use ffmpeg::packet::Mut; use h264; use recording; + use std::cmp; use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::atomic::{AtomicBool, Ordering}; use stream::{self, Opener, Stream}; @@ -201,18 +202,22 @@ mod tests { struct ProxyingStream<'a> { clock: &'a clock::SimulatedClock, inner: stream::FfmpegStream, - last_duration: time::Duration, + buffered: time::Duration, + slept: time::Duration, ts_offset: i64, ts_offset_pkts_left: u32, pkts_left: u32, } impl<'a> ProxyingStream<'a> { - fn new(clock: &'a clock::SimulatedClock, inner: stream::FfmpegStream) -> ProxyingStream { + fn new(clock: &'a clock::SimulatedClock, buffered: time::Duration, + inner: stream::FfmpegStream) -> ProxyingStream { + clock.sleep(buffered); ProxyingStream { clock: clock, inner: inner, - last_duration: time::Duration::seconds(0), + buffered: buffered, + slept: time::Duration::seconds(0), ts_offset: 0, ts_offset_pkts_left: 0, pkts_left: 0, @@ -227,13 +232,21 @@ mod tests { } self.pkts_left -= 1; - // Advance clock to when this packet starts. - self.clock.sleep(self.last_duration); - let mut pkt = self.inner.get_next()?; - self.last_duration = time::Duration::nanoseconds( - pkt.duration() * 1_000_000_000 / recording::TIME_UNITS_PER_SEC); + // Advance clock to the end of this frame. + // Avoid accumulating conversion error by tracking the total amount to sleep and how + // much we've already slept, rather than considering each frame in isolation. + { + let goal = pkt.pts().unwrap() + pkt.duration(); + let goal = time::Duration::nanoseconds( + goal * 1_000_000_000 / recording::TIME_UNITS_PER_SEC); + let duration = goal - self.slept; + let buf_part = cmp::min(self.buffered, duration); + self.buffered = self.buffered - buf_part; + self.clock.sleep(duration - buf_part); + self.slept = goal; + } if self.ts_offset_pkts_left > 0 { self.ts_offset_pkts_left -= 1; @@ -309,9 +322,10 @@ mod tests { fn basic() { testutil::init(); let clock = clock::SimulatedClock::new(); + clock.sleep(time::Duration::seconds(1430006400)); // 2015-04-26 00:00:00 UTC let stream = stream::FFMPEG.open(stream::Source::File("src/testdata/clip.mp4")).unwrap(); - let mut stream = ProxyingStream::new(&clock, stream); + let mut stream = ProxyingStream::new(&clock, time::Duration::seconds(2), stream); stream.ts_offset = 180000; // starting pts of the input should be irrelevant stream.ts_offset_pkts_left = u32::max_value(); stream.pkts_left = u32::max_value(); @@ -358,5 +372,17 @@ mod tests { Frame{start_90k: 0, duration_90k: 90011, is_key: true}, Frame{start_90k: 90011, duration_90k: 0, is_key: false}, ]); + let mut recordings = Vec::new(); + db.list_recordings_by_id(testutil::TEST_CAMERA_ID, 1..3, |r| { + recordings.push(r); + Ok(()) + }).unwrap(); + assert_eq!(2, recordings.len()); + assert_eq!(1, recordings[0].id); + assert_eq!(recording::Time(128700575999999), recordings[0].start); + assert_eq!(0, recordings[0].flags); + assert_eq!(2, recordings[1].id); + assert_eq!(recording::Time(128700576719993), recordings[1].start); + assert_eq!(db::RecordingFlags::TrailingZero as i32, recordings[1].flags); } }