#[cfg(any(test, feature = "internal-testing", feature = "native-capture"))]
use std::num::NonZeroU32;
#[cfg(any(test, feature = "internal-testing", feature = "native-capture"))]
pub fn initialize_monotonic_timestamp_domain() {
let _ = crate::timing::monotonic_timestamp_ns();
}
pub fn monotonic_timestamp_ns() -> u64 {
crate::timing::monotonic_timestamp_ns()
}
#[derive(Debug)]
#[cfg(any(test, feature = "internal-testing", feature = "native-capture"))]
pub struct CaptureSampleTimeline {
sample_rate_hz: NonZeroU32,
origin_timestamp_ns: Option<u64>,
elapsed_sample_frames: u64,
source_origin_sample_frame: Option<u64>,
next_source_sample_frame: Option<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg(any(test, feature = "internal-testing", feature = "native-capture"))]
pub enum CaptureSampleTimelineError {
MixedAdvanceModes,
SourcePositionOverflow,
SourcePositionMovedBackward {
expected_at_least: u64,
observed: u64,
},
}
#[cfg(any(test, feature = "internal-testing", feature = "native-capture"))]
impl CaptureSampleTimeline {
pub fn new(sample_rate_hz: NonZeroU32) -> Self {
Self {
sample_rate_hz,
origin_timestamp_ns: None,
elapsed_sample_frames: 0,
source_origin_sample_frame: None,
next_source_sample_frame: None,
}
}
#[cfg(any(test, target_os = "linux"))]
pub fn advance(&mut self, sample_frames: u64) -> u64 {
let origin_timestamp_ns = *self
.origin_timestamp_ns
.get_or_insert_with(monotonic_timestamp_ns);
let elapsed_ns = u128::from(self.elapsed_sample_frames)
.saturating_mul(1_000_000_000)
.checked_div(u128::from(self.sample_rate_hz.get()))
.unwrap_or(0)
.min(u128::from(u64::MAX)) as u64;
self.elapsed_sample_frames = self.elapsed_sample_frames.saturating_add(sample_frames);
origin_timestamp_ns.saturating_add(elapsed_ns)
}
pub fn advance_from_source_position(
&mut self,
source_sample_frame: u64,
sample_frames: u64,
) -> Result<u64, CaptureSampleTimelineError> {
if self.elapsed_sample_frames != 0 {
return Err(CaptureSampleTimelineError::MixedAdvanceModes);
}
let next_source_sample_frame = source_sample_frame
.checked_add(sample_frames)
.ok_or(CaptureSampleTimelineError::SourcePositionOverflow)?;
if let Some(expected_at_least) = self.next_source_sample_frame {
if source_sample_frame < expected_at_least {
return Err(CaptureSampleTimelineError::SourcePositionMovedBackward {
expected_at_least,
observed: source_sample_frame,
});
}
}
let source_origin_sample_frame = *self
.source_origin_sample_frame
.get_or_insert(source_sample_frame);
let elapsed_sample_frames = source_sample_frame
.checked_sub(source_origin_sample_frame)
.ok_or(CaptureSampleTimelineError::SourcePositionMovedBackward {
expected_at_least: source_origin_sample_frame,
observed: source_sample_frame,
})?;
let origin_timestamp_ns = *self
.origin_timestamp_ns
.get_or_insert_with(monotonic_timestamp_ns);
let elapsed_ns = u128::from(elapsed_sample_frames)
.saturating_mul(1_000_000_000)
.checked_div(u128::from(self.sample_rate_hz.get()))
.unwrap_or(0)
.min(u128::from(u64::MAX)) as u64;
self.next_source_sample_frame = Some(next_source_sample_frame);
Ok(origin_timestamp_ns.saturating_add(elapsed_ns))
}
}