ffmpeg_light/
types.rs

1//! Common domain types shared across the crate.
2
3use std::fmt;
4use std::time::Duration;
5
6/// Represents a position in time used for seeking and trimming.
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
8pub struct Time(Duration);
9
10impl Time {
11    /// Zero timestamp.
12    pub const fn zero() -> Self {
13        Self(Duration::from_secs(0))
14    }
15
16    /// Create an instance from whole seconds.
17    pub fn from_seconds(seconds: u64) -> Self {
18        Self(Duration::from_secs(seconds))
19    }
20
21    /// Create from a floating-point second representation.
22    pub fn from_seconds_f64(seconds: f64) -> Self {
23        let nanos = (seconds * 1_000_000_000.0).round();
24        let secs = (nanos / 1_000_000_000.0).trunc() as u64;
25        let sub_nanos = (nanos % 1_000_000_000.0) as u32;
26        Self(Duration::new(secs, sub_nanos))
27    }
28
29    /// Create from an existing `Duration`.
30    pub const fn from_duration(duration: Duration) -> Self {
31        Self(duration)
32    }
33
34    /// Convert to std `Duration`.
35    pub const fn as_duration(self) -> Duration {
36        self.0
37    }
38
39    /// Convert to the timestamp format expected by FFmpeg (HH:MM:SS.mmm).
40    pub fn to_ffmpeg_timestamp(self) -> String {
41        let total_secs = self.0.as_secs();
42        let hours = total_secs / 3600;
43        let minutes = (total_secs % 3600) / 60;
44        let seconds = total_secs % 60;
45        let millis = self.0.subsec_millis();
46        format!("{hours:02}:{minutes:02}:{seconds:02}.{millis:03}")
47    }
48}
49
50impl From<Duration> for Time {
51    fn from(duration: Duration) -> Self {
52        Self(duration)
53    }
54}
55
56impl From<Time> for Duration {
57    fn from(value: Time) -> Self {
58        value.0
59    }
60}
61
62impl fmt::Display for Time {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        write!(f, "{}", self.to_ffmpeg_timestamp())
65    }
66}
67
68/// High-level codec representation.
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub enum CodecType {
71    /// H.264/AVC video.
72    H264,
73    /// H.265/HEVC video.
74    Hevc,
75    /// VP9 video.
76    Vp9,
77    /// AV1 video.
78    Av1,
79    /// AAC audio.
80    Aac,
81    /// MP3 audio.
82    Mp3,
83    /// Opus audio.
84    Opus,
85    /// PCM S16LE audio.
86    PcmS16Le,
87    /// Copy stream without re-encoding.
88    Copy,
89    /// Any other codec string returned by FFmpeg.
90    Other(String),
91}
92
93impl CodecType {
94    /// Create from a codec name string (case insensitive).
95    pub fn from_name(name: &str) -> Self {
96        match name.to_lowercase().as_str() {
97            "h264" | "libx264" => CodecType::H264,
98            "hevc" | "h265" | "libx265" => CodecType::Hevc,
99            "vp9" => CodecType::Vp9,
100            "av1" => CodecType::Av1,
101            "aac" => CodecType::Aac,
102            "mp3" => CodecType::Mp3,
103            "opus" => CodecType::Opus,
104            "pcm_s16le" => CodecType::PcmS16Le,
105            "copy" => CodecType::Copy,
106            other => CodecType::Other(other.to_string()),
107        }
108    }
109
110    /// Convert back into an FFmpeg codec string.
111    pub fn as_str(&self) -> &str {
112        match self {
113            CodecType::H264 => "libx264",
114            CodecType::Hevc => "libx265",
115            CodecType::Vp9 => "libvpx-vp9",
116            CodecType::Av1 => "libaom-av1",
117            CodecType::Aac => "aac",
118            CodecType::Mp3 => "libmp3lame",
119            CodecType::Opus => "libopus",
120            CodecType::PcmS16Le => "pcm_s16le",
121            CodecType::Copy => "copy",
122            CodecType::Other(name) => name,
123        }
124    }
125}
126
127/// Simplified stream classification.
128#[derive(Clone, Debug, PartialEq, Eq)]
129pub enum StreamType {
130    /// Video stream.
131    Video,
132    /// Audio stream.
133    Audio,
134    /// Subtitle stream.
135    Subtitle,
136    /// Auxiliary data.
137    Data,
138}
139
140/// Container-level metadata reported by ffprobe.
141#[derive(Clone, Debug)]
142pub struct FormatInfo {
143    /// Name of the format (e.g. "mov,mp4,m4a,3gp,3g2,mj2").
144    pub format_name: Option<String>,
145    /// Human readable format description.
146    pub format_long_name: Option<String>,
147    /// Optional duration.
148    pub duration: Option<Duration>,
149    /// Optional overall bitrate.
150    pub bit_rate: Option<u64>,
151    /// Optional file size in bytes.
152    pub size: Option<u64>,
153}
154
155impl FormatInfo {
156    /// Create a new instance with defaults.
157    pub fn new(
158        format_name: Option<String>,
159        format_long_name: Option<String>,
160        duration: Option<Duration>,
161        bit_rate: Option<u64>,
162        size: Option<u64>,
163    ) -> Self {
164        Self {
165            format_name,
166            format_long_name,
167            duration,
168            bit_rate,
169            size,
170        }
171    }
172}
173
174/// Stream metadata.
175#[derive(Clone, Debug)]
176pub enum StreamInfo {
177    /// Video stream info.
178    Video(VideoStreamInfo),
179    /// Audio stream info.
180    Audio(AudioStreamInfo),
181    /// Subtitle stream info.
182    Subtitle(SubtitleStreamInfo),
183    /// Auxiliary data stream info.
184    Data(DataStreamInfo),
185}
186
187/// Video stream metadata.
188#[derive(Clone, Debug)]
189pub struct VideoStreamInfo {
190    /// Codec identifier.
191    pub codec: CodecType,
192    /// Width in pixels.
193    pub width: Option<u32>,
194    /// Height in pixels.
195    pub height: Option<u32>,
196    /// Bit rate in bits/sec.
197    pub bit_rate: Option<u64>,
198    /// Average frame rate (frames per second).
199    pub frame_rate: Option<f64>,
200}
201
202/// Audio stream metadata.
203#[derive(Clone, Debug)]
204pub struct AudioStreamInfo {
205    /// Codec identifier.
206    pub codec: CodecType,
207    /// Number of audio channels.
208    pub channels: Option<u32>,
209    /// Sample rate in Hz.
210    pub sample_rate: Option<u32>,
211    /// Bit rate in bits/sec.
212    pub bit_rate: Option<u64>,
213}
214
215/// Subtitle stream metadata.
216#[derive(Clone, Debug)]
217pub struct SubtitleStreamInfo {
218    /// Codec identifier.
219    pub codec: CodecType,
220    /// Optional language tag (e.g. "eng").
221    pub language: Option<String>,
222}
223
224/// Misc data stream metadata.
225#[derive(Clone, Debug)]
226pub struct DataStreamInfo {
227    /// Codec identifier.
228    pub codec: CodecType,
229    /// Optional handler description.
230    pub description: Option<String>,
231}
232
233/// Top-level probe result.
234#[derive(Clone, Debug)]
235pub struct ProbeResult {
236    format: FormatInfo,
237    streams: Vec<StreamInfo>,
238}
239
240impl ProbeResult {
241    /// Create a new result.
242    pub fn new(format: FormatInfo, streams: Vec<StreamInfo>) -> Self {
243        Self { format, streams }
244    }
245
246    /// Format metadata (container-level details).
247    pub fn format(&self) -> &FormatInfo {
248        &self.format
249    }
250
251    /// All streams reported by ffprobe.
252    pub fn streams(&self) -> &[StreamInfo] {
253        &self.streams
254    }
255
256    /// Convenience helper returning first video stream.
257    pub fn first_video(&self) -> Option<&VideoStreamInfo> {
258        self.streams.iter().find_map(|stream| match stream {
259            StreamInfo::Video(info) => Some(info),
260            _ => None,
261        })
262    }
263
264    /// Convenience helper returning first audio stream.
265    pub fn first_audio(&self) -> Option<&AudioStreamInfo> {
266        self.streams.iter().find_map(|stream| match stream {
267            StreamInfo::Audio(info) => Some(info),
268            _ => None,
269        })
270    }
271
272    /// Duration if reported by ffprobe.
273    pub fn duration(&self) -> Option<Duration> {
274        self.format.duration
275    }
276}