use serde::{Deserialize, Serialize}; use super::SyncOffset; /// Transformação temporal completa de uma faixa: deslocamento constante + fator de escala. /// /// - `offset_ms`: deslocamento em milissegundos (mesmo semântico de `SyncOffset`). /// - `scale`: fator de escala temporal. `1.0` = identidade. `0.99983` ≈ correção 25 fps → 24 fps. /// /// Quando `scale == 1.0`, o pipeline padrão FFmpeg é utilizado. /// Quando `scale != 1.0`, o projeto precisa passar pelo pipeline mkvmerge. #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct SyncTransform { pub offset_ms: i64, pub scale: f64, } impl SyncTransform { /// Cria uma transformação com deslocamento e escala explícitos. pub fn new(offset_ms: i64, scale: f64) -> Self { SyncTransform { offset_ms, scale } } /// Cria uma transformação a partir de um `SyncOffset` sem drift (escala = 1.0). pub fn from_offset(offset: SyncOffset) -> Self { SyncTransform { offset_ms: offset.as_ms(), scale: 1.0, } } /// Retorna `true` se o fator de escala difere de 1.0 por mais de 1e-9. pub fn has_drift(&self) -> bool { (self.scale - 1.0).abs() > 1e-9 } /// Retorna `true` se a transformação é identidade (offset == 0 e sem drift). pub fn is_identity(&self) -> bool { self.offset_ms == 0 && !self.has_drift() } /// Converte para `SyncOffset` descartando a escala (compatibilidade com FFmpeg path). pub fn to_sync_offset(&self) -> SyncOffset { SyncOffset::from_ms(self.offset_ms) } } impl Default for SyncTransform { /// Identidade: sem deslocamento, sem drift. fn default() -> Self { SyncTransform { offset_ms: 0, scale: 1.0, } } } #[cfg(test)] mod tests { use super::*; #[test] fn default_e_identidade() { let t = SyncTransform::default(); assert!(t.is_identity()); assert!(!t.has_drift()); assert_eq!(t.offset_ms, 0); assert_eq!(t.scale, 1.0); } #[test] fn has_drift_false_scale_1() { let t = SyncTransform::new(500, 1.0); assert!(!t.has_drift()); } #[test] fn has_drift_true_scale_0_99983() { let t = SyncTransform::new(0, 0.99983); assert!(t.has_drift()); } #[test] fn from_offset_sem_drift() { let offset = SyncOffset::from_ms(1200); let t = SyncTransform::from_offset(offset); assert_eq!(t.offset_ms, 1200); assert!(!t.has_drift()); } #[test] fn to_sync_offset_descarta_escala() { let t = SyncTransform::new(-500, 0.99983); assert_eq!(t.to_sync_offset().as_ms(), -500); } }