Skip to main content

ff_format/
lib.rs

1//! # ff-format
2//!
3//! Common types for video/audio processing - the Rust way.
4//!
5//! This crate provides shared type definitions used across the ff-* crate family.
6//! It completely hides `FFmpeg` internals and provides Rust-idiomatic type safety.
7//!
8//! ## Module Structure
9//!
10//! - `pixel` - Pixel format definitions ([`PixelFormat`])
11//! - `sample` - Audio sample format definitions ([`SampleFormat`])
12//! - `time` - Time primitives ([`Timestamp`], [`Rational`])
13//! - `frame` - Frame types ([`VideoFrame`], [`AudioFrame`])
14//! - `stream` - Stream info ([`VideoStreamInfo`], [`AudioStreamInfo`])
15//! - `media` - Media container info ([`MediaInfo`])
16//! - `color` - Color space definitions ([`ColorSpace`], [`ColorRange`], [`ColorPrimaries`])
17//! - `codec` - Codec definitions ([`VideoCodec`], [`AudioCodec`])
18//! - `channel` - Channel layout definitions ([`ChannelLayout`])
19//! - `chapter` - Chapter information ([`ChapterInfo`])
20//! - `error` - Error types ([`FormatError`])
21//!
22//! ## Usage
23//!
24//! ```
25//! use ff_format::prelude::*;
26//!
27//! // Access pixel formats
28//! let format = PixelFormat::Yuv420p;
29//! assert!(format.is_planar());
30//!
31//! // Access sample formats
32//! let audio = SampleFormat::F32;
33//! assert!(audio.is_float());
34//! assert_eq!(audio.bytes_per_sample(), 4);
35//!
36//! // Work with timestamps
37//! let time_base = Rational::new(1, 90000);
38//! let ts = Timestamp::new(90000, time_base);
39//! assert!((ts.as_secs_f64() - 1.0).abs() < 0.001);
40//!
41//! // Access color and codec types
42//! use ff_format::color::ColorSpace;
43//! use ff_format::codec::VideoCodec;
44//! let space = ColorSpace::Bt709;
45//! let codec = VideoCodec::H264;
46//! ```
47
48#![warn(missing_docs)]
49#![warn(clippy::all)]
50#![warn(clippy::pedantic)]
51
52// Module declarations
53pub mod channel;
54pub mod chapter;
55pub mod codec;
56pub mod color;
57pub mod error;
58pub mod frame;
59pub mod media;
60pub mod pixel;
61pub mod sample;
62pub mod stream;
63pub mod time;
64
65pub use channel::ChannelLayout;
66pub use chapter::{ChapterInfo, ChapterInfoBuilder};
67pub use codec::{AudioCodec, SubtitleCodec, VideoCodec};
68pub use color::{ColorPrimaries, ColorRange, ColorSpace};
69pub use error::{FormatError, FrameError};
70pub use ff_common::PooledBuffer;
71pub use frame::{AudioFrame, VideoFrame};
72pub use media::{MediaInfo, MediaInfoBuilder};
73pub use pixel::PixelFormat;
74pub use sample::SampleFormat;
75pub use stream::{
76    AudioStreamInfo, AudioStreamInfoBuilder, SubtitleStreamInfo, SubtitleStreamInfoBuilder,
77    VideoStreamInfo, VideoStreamInfoBuilder,
78};
79pub use time::{Rational, Timestamp};
80
81/// Prelude module for convenient imports.
82///
83/// This module re-exports all commonly used types for easy access:
84///
85/// ```ignore
86/// use ff_format::prelude::*;
87/// ```
88pub mod prelude {
89    pub use crate::{
90        AudioCodec, AudioFrame, AudioStreamInfo, ChannelLayout, ChapterInfo, ColorPrimaries,
91        ColorRange, ColorSpace, FormatError, FrameError, MediaInfo, PixelFormat, PooledBuffer,
92        Rational, SampleFormat, Timestamp, VideoCodec, VideoFrame, VideoStreamInfo,
93    };
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_prelude_exports() {
102        // Verify prelude exports all expected types
103        let _pixel: PixelFormat = PixelFormat::default();
104        let _sample: SampleFormat = SampleFormat::default();
105        let _rational: Rational = Rational::default();
106        let _timestamp: Timestamp = Timestamp::default();
107        let _video_frame: VideoFrame = VideoFrame::default();
108        let _audio_frame: AudioFrame = AudioFrame::default();
109
110        // New types
111        let _color_space: ColorSpace = ColorSpace::default();
112        let _color_range: ColorRange = ColorRange::default();
113        let _color_primaries: ColorPrimaries = ColorPrimaries::default();
114        let _video_codec: VideoCodec = VideoCodec::default();
115        let _audio_codec: AudioCodec = AudioCodec::default();
116        let _channel_layout: ChannelLayout = ChannelLayout::default();
117        let _video_stream: VideoStreamInfo = VideoStreamInfo::default();
118        let _audio_stream: AudioStreamInfo = AudioStreamInfo::default();
119        let _media_info: MediaInfo = MediaInfo::default();
120    }
121
122    #[test]
123    fn test_stream_info_builder() {
124        // Test VideoStreamInfo builder
125        let video = VideoStreamInfo::builder()
126            .index(0)
127            .codec(VideoCodec::H264)
128            .width(1920)
129            .height(1080)
130            .frame_rate(Rational::new(30, 1))
131            .pixel_format(PixelFormat::Yuv420p)
132            .color_space(ColorSpace::Bt709)
133            .build();
134
135        assert_eq!(video.width(), 1920);
136        assert_eq!(video.height(), 1080);
137        assert_eq!(video.codec(), VideoCodec::H264);
138        assert_eq!(video.color_space(), ColorSpace::Bt709);
139
140        // Test AudioStreamInfo builder
141        let audio = AudioStreamInfo::builder()
142            .index(1)
143            .codec(AudioCodec::Aac)
144            .sample_rate(48000)
145            .channels(2)
146            .sample_format(SampleFormat::F32)
147            .build();
148
149        assert_eq!(audio.sample_rate(), 48000);
150        assert_eq!(audio.channels(), 2);
151        assert_eq!(audio.codec(), AudioCodec::Aac);
152        assert_eq!(audio.channel_layout(), ChannelLayout::Stereo);
153    }
154
155    #[test]
156    fn test_media_info_builder() {
157        use std::time::Duration;
158
159        // Create streams
160        let video = VideoStreamInfo::builder()
161            .index(0)
162            .codec(VideoCodec::H264)
163            .width(1920)
164            .height(1080)
165            .frame_rate(Rational::new(30, 1))
166            .build();
167
168        let audio = AudioStreamInfo::builder()
169            .index(1)
170            .codec(AudioCodec::Aac)
171            .sample_rate(48000)
172            .channels(2)
173            .build();
174
175        // Create media info
176        let media = MediaInfo::builder()
177            .path("/path/to/video.mp4")
178            .format("mp4")
179            .format_long_name("QuickTime / MOV")
180            .duration(Duration::from_secs(120))
181            .file_size(100_000_000)
182            .bitrate(8_000_000)
183            .video_stream(video)
184            .audio_stream(audio)
185            .metadata("title", "Test Video")
186            .build();
187
188        assert!(media.has_video());
189        assert!(media.has_audio());
190        assert_eq!(media.resolution(), Some((1920, 1080)));
191        assert!((media.frame_rate().unwrap() - 30.0).abs() < 0.001);
192        assert_eq!(media.sample_rate(), Some(48000));
193        assert_eq!(media.channels(), Some(2));
194        assert_eq!(media.format(), "mp4");
195        assert_eq!(media.format_long_name(), Some("QuickTime / MOV"));
196        assert_eq!(media.metadata_value("title"), Some("Test Video"));
197    }
198}