Skip to main content

oximedia_proxy/generate/
encoder.rs

1//! Proxy encoder implementation.
2
3use super::settings::ProxyGenerationSettings;
4use crate::{ProxyError, Result};
5use std::path::Path;
6
7/// Proxy encoder with support for various codecs and settings.
8pub struct ProxyEncoder {
9    settings: ProxyGenerationSettings,
10}
11
12impl ProxyEncoder {
13    /// Create a new proxy encoder with the given settings.
14    pub fn new(settings: ProxyGenerationSettings) -> Result<Self> {
15        settings.validate()?;
16        Ok(Self { settings })
17    }
18
19    /// Encode a proxy from the input file.
20    ///
21    /// # Errors
22    ///
23    /// Returns an error if:
24    /// - Input file does not exist or cannot be read
25    /// - Output path is invalid
26    /// - Encoding fails
27    pub async fn encode(&self, input: &Path, output: &Path) -> Result<ProxyEncodeResult> {
28        // Validate input
29        if !input.exists() {
30            return Err(ProxyError::FileNotFound(input.display().to_string()));
31        }
32
33        // Create output directory if needed
34        if let Some(parent) = output.parent() {
35            std::fs::create_dir_all(parent)?;
36        }
37
38        // For now, this is a placeholder implementation
39        // In a real implementation, this would use oximedia-transcode
40        // to perform the actual encoding with the specified settings
41
42        tracing::info!(
43            "Encoding proxy: {} -> {} (scale: {}, codec: {})",
44            input.display(),
45            output.display(),
46            self.settings.scale_factor,
47            self.settings.codec
48        );
49
50        Ok(ProxyEncodeResult {
51            output_path: output.to_path_buf(),
52            file_size: 0,
53            duration: 0.0,
54            bitrate: self.settings.bitrate,
55            codec: self.settings.codec.clone(),
56            resolution: (0, 0),
57            encoding_time: 0.0,
58        })
59    }
60
61    /// Get the current settings.
62    #[must_use]
63    pub fn settings(&self) -> &ProxyGenerationSettings {
64        &self.settings
65    }
66
67    /// Update the settings.
68    pub fn set_settings(&mut self, settings: ProxyGenerationSettings) -> Result<()> {
69        settings.validate()?;
70        self.settings = settings;
71        Ok(())
72    }
73
74    /// Calculate the target resolution for the given input resolution.
75    #[must_use]
76    pub fn calculate_target_resolution(&self, input_width: u32, input_height: u32) -> (u32, u32) {
77        let target_width = ((input_width as f32) * self.settings.scale_factor) as u32;
78        let target_height = ((input_height as f32) * self.settings.scale_factor) as u32;
79
80        // Ensure dimensions are even (required for most video codecs)
81        let target_width = target_width & !1;
82        let target_height = target_height & !1;
83
84        (target_width, target_height)
85    }
86}
87
88/// Result of proxy encoding operation.
89#[derive(Debug, Clone)]
90pub struct ProxyEncodeResult {
91    /// Output file path.
92    pub output_path: std::path::PathBuf,
93
94    /// File size in bytes.
95    pub file_size: u64,
96
97    /// Duration in seconds.
98    pub duration: f64,
99
100    /// Actual bitrate in bits per second.
101    pub bitrate: u64,
102
103    /// Codec used.
104    pub codec: String,
105
106    /// Output resolution (width, height).
107    pub resolution: (u32, u32),
108
109    /// Encoding time in seconds.
110    pub encoding_time: f64,
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_encoder_creation() {
119        let settings = ProxyGenerationSettings::quarter_res_h264();
120        let encoder = ProxyEncoder::new(settings);
121        assert!(encoder.is_ok());
122    }
123
124    #[test]
125    fn test_invalid_settings() {
126        let mut settings = ProxyGenerationSettings::default();
127        settings.scale_factor = 0.0;
128        let encoder = ProxyEncoder::new(settings);
129        assert!(encoder.is_err());
130    }
131
132    #[test]
133    fn test_resolution_calculation() {
134        let settings = ProxyGenerationSettings::quarter_res_h264();
135        let encoder = ProxyEncoder::new(settings).expect("should succeed in test");
136
137        let (width, height) = encoder.calculate_target_resolution(1920, 1080);
138        assert_eq!(width, 480);
139        assert_eq!(height, 270);
140
141        let (width, height) = encoder.calculate_target_resolution(3840, 2160);
142        assert_eq!(width, 960);
143        assert_eq!(height, 540);
144    }
145
146    #[test]
147    fn test_resolution_even_alignment() {
148        let settings = ProxyGenerationSettings::default().with_scale_factor(0.3);
149        let encoder = ProxyEncoder::new(settings).expect("should succeed in test");
150
151        let (width, height) = encoder.calculate_target_resolution(1920, 1080);
152        // Should be even numbers
153        assert_eq!(width % 2, 0);
154        assert_eq!(height % 2, 0);
155    }
156}