#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod affine;
pub mod align_report;
pub mod audio_align;
pub mod beat_align;
pub mod color;
pub mod distortion;
pub mod drift_correct;
pub mod drift_correction;
pub mod elastic_align;
pub mod features;
pub mod frame_matcher;
pub mod frequency_align;
pub mod gradient_flow;
pub mod icp;
pub mod lip_sync;
pub mod markers;
pub mod motion_compensate;
pub mod multi_stream;
pub mod multicam_sync;
pub mod multitrack_align;
pub mod optical_flow;
pub mod phase_correlate;
pub mod rolling_shutter;
pub mod spatial;
pub mod stereo_rectify;
pub mod subframe_interp;
pub mod sync_score;
pub mod tempo_align;
pub mod temporal;
pub mod temporal_align;
pub mod transform;
pub mod warp;
use thiserror::Error;
pub type AlignResult<T> = Result<T, AlignError>;
#[derive(Debug, Error)]
pub enum AlignError {
#[error("Insufficient data: {0}")]
InsufficientData(String),
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("No solution found: {0}")]
NoSolution(String),
#[error("Numerical instability: {0}")]
NumericalError(String),
#[error("Feature detection failed: {0}")]
FeatureError(String),
#[error("Matching failed: {0}")]
MatchingError(String),
#[error("Estimation failed: {0}")]
EstimationError(String),
#[error("Synchronization failed: {0}")]
SyncError(String),
#[error("Color correction failed: {0}")]
ColorError(String),
#[error("Distortion correction failed: {0}")]
DistortionError(String),
#[error("Rolling shutter correction failed: {0}")]
RollingShutterError(String),
#[error("Core error: {0}")]
Core(#[from] oximedia_core::error::OxiError),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point2D {
pub x: f64,
pub y: f64,
}
impl Point2D {
#[must_use]
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
#[must_use]
pub fn distance(&self, other: &Self) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
#[must_use]
pub fn distance_squared(&self, other: &Self) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
dx * dx + dy * dy
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TimeOffset {
pub samples: i64,
pub confidence: f64,
pub correlation: f64,
}
impl TimeOffset {
#[must_use]
pub fn new(samples: i64, confidence: f64, correlation: f64) -> Self {
Self {
samples,
confidence,
correlation,
}
}
#[must_use]
pub fn to_seconds(&self, sample_rate: u32) -> f64 {
self.samples as f64 / f64::from(sample_rate)
}
#[must_use]
pub fn to_milliseconds(&self, sample_rate: u32) -> f64 {
self.to_seconds(sample_rate) * 1000.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point2d_distance() {
let p1 = Point2D::new(0.0, 0.0);
let p2 = Point2D::new(3.0, 4.0);
assert!((p1.distance(&p2) - 5.0).abs() < f64::EPSILON);
assert!((p1.distance_squared(&p2) - 25.0).abs() < f64::EPSILON);
}
#[test]
fn test_time_offset_conversion() {
let offset = TimeOffset::new(48000, 0.95, 0.85);
assert!((offset.to_seconds(48000) - 1.0).abs() < f64::EPSILON);
assert!((offset.to_milliseconds(48000) - 1000.0).abs() < f64::EPSILON);
}
}