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