Skip to main content

ff_decode/
lib.rs

1//! # ff-decode
2//!
3//! Video and audio decoding - the Rust way.
4//!
5//! This crate provides frame-by-frame video/audio decoding, efficient seeking,
6//! and thumbnail generation. It completely hides `FFmpeg` internals and provides
7//! a safe, ergonomic Rust API.
8//!
9//! ## Features
10//!
11//! - **Video Decoding**: Frame-by-frame decoding with Iterator pattern
12//! - **Audio Decoding**: Sample-level audio extraction
13//! - **Seeking**: Fast keyframe and exact seeking without file re-open
14//! - **Thumbnails**: Efficient thumbnail generation for timelines
15//! - **Hardware Acceleration**: Optional NVDEC, QSV, AMF, `VideoToolbox`, VAAPI support
16//! - **Frame Pooling**: Memory reuse for reduced allocation overhead
17//!
18//! ## Usage
19//!
20//! ### Video Decoding
21//!
22//! ```ignore
23//! use ff_decode::{VideoDecoder, SeekMode};
24//! use ff_format::PixelFormat;
25//! use std::time::Duration;
26//!
27//! // Open a video file and create decoder
28//! let mut decoder = VideoDecoder::open("video.mp4")?
29//!     .output_format(PixelFormat::Rgba)
30//!     .build()?;
31//!
32//! // Get basic info
33//! println!("Duration: {:?}", decoder.duration());
34//! println!("Resolution: {}x{}", decoder.width(), decoder.height());
35//!
36//! // Decode frames sequentially
37//! for result in &mut decoder {
38//!     let frame = result?;
39//!     println!("Frame at {:?}", frame.timestamp().as_duration());
40//! }
41//!
42//! // Seek to specific position
43//! decoder.seek(Duration::from_secs(30), SeekMode::Keyframe)?;
44//! ```
45//!
46//! ### Audio Decoding
47//!
48//! ```ignore
49//! use ff_decode::AudioDecoder;
50//! use ff_format::SampleFormat;
51//!
52//! let mut decoder = AudioDecoder::open("audio.mp3")?
53//!     .output_format(SampleFormat::F32)
54//!     .output_sample_rate(48000)
55//!     .build()?;
56//!
57//! // Decode all audio samples
58//! for result in &mut decoder {
59//!     let frame = result?;
60//!     println!("Audio frame with {} samples", frame.samples());
61//! }
62//! ```
63//!
64//! ### Hardware Acceleration (Video)
65//!
66//! ```ignore
67//! use ff_decode::{VideoDecoder, HardwareAccel};
68//!
69//! let decoder = VideoDecoder::open("video.mp4")?
70//!     .hardware_accel(HardwareAccel::Auto)  // Auto-detect GPU
71//!     .build()?;
72//! ```
73//!
74//! ### Frame Pooling (Video)
75//!
76//! ```ignore
77//! use ff_decode::{VideoDecoder, FramePool};
78//! use std::sync::Arc;
79//!
80//! // Use a frame pool for memory reuse
81//! let pool: Arc<dyn FramePool> = create_frame_pool(32);
82//! let decoder = VideoDecoder::open("video.mp4")?
83//!     .frame_pool(pool)
84//!     .build()?;
85//! ```
86//!
87//! ## Module Structure
88//!
89//! - [`audio`] - Audio decoder for extracting audio frames
90//! - [`video`] - Video decoder for extracting video frames
91//! - [`error`] - Error types for decoding operations
92//! - Frame pool types (`FramePool`, `PooledBuffer`, `VecPool`) are provided by `ff-common`
93//!
94//! ## Re-exports
95//!
96//! This crate re-exports commonly used types from `ff-format` for convenience.
97
98#![warn(missing_docs)]
99#![warn(clippy::all)]
100#![warn(clippy::pedantic)]
101
102// Module declarations
103pub mod analysis;
104#[cfg(feature = "tokio")]
105pub(crate) mod async_decoder;
106pub mod audio;
107pub mod error;
108pub mod extract;
109pub mod image;
110pub mod scope;
111mod shared;
112pub mod video;
113
114// Preserve crate::network path used throughout decoder_inner modules.
115pub(crate) use shared::network;
116
117// Re-exports for convenience
118pub use analysis::{
119    BlackFrameDetector, FrameHistogram, HistogramExtractor, KeyframeEnumerator, SceneDetector,
120    SilenceDetector, SilenceRange, WaveformAnalyzer, WaveformSample,
121};
122pub use audio::{AudioDecoder, AudioDecoderBuilder};
123pub use error::DecodeError;
124pub use extract::{FrameExtractor, ThumbnailSelector};
125pub use ff_common::{FramePool, PooledBuffer};
126pub use ff_format::ContainerInfo;
127pub use image::{ImageDecoder, ImageDecoderBuilder};
128pub use scope::{Histogram, RgbParade, ScopeAnalyzer};
129pub use shared::{HardwareAccel, SeekMode};
130pub use video::{VideoDecoder, VideoDecoderBuilder};
131
132#[cfg(feature = "tokio")]
133pub use audio::{AsyncAudioDecoder, AsyncAudioDecoderBuilder};
134#[cfg(feature = "tokio")]
135pub use image::AsyncImageDecoder;
136#[cfg(feature = "tokio")]
137pub use video::{AsyncVideoDecoder, AsyncVideoDecoderBuilder};
138
139/// Prelude module for convenient imports.
140///
141/// This module re-exports all commonly used types:
142///
143/// ```ignore
144/// use ff_decode::prelude::*;
145/// ```
146pub mod prelude {
147    #[cfg(feature = "tokio")]
148    pub use crate::{AsyncAudioDecoder, AsyncImageDecoder, AsyncVideoDecoder};
149    pub use crate::{
150        AudioDecoder, AudioDecoderBuilder, DecodeError, FramePool, HardwareAccel, ImageDecoder,
151        ImageDecoderBuilder, PooledBuffer, SeekMode, VideoDecoder, VideoDecoderBuilder,
152    };
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_decode_error_display() {
161        use std::path::PathBuf;
162
163        let error = DecodeError::FileNotFound {
164            path: PathBuf::from("/path/to/video.mp4"),
165        };
166        assert!(error.to_string().contains("File not found"));
167
168        let error = DecodeError::NoVideoStream {
169            path: PathBuf::from("/path/to/audio.mp3"),
170        };
171        assert!(error.to_string().contains("No video stream"));
172
173        let error = DecodeError::UnsupportedCodec {
174            codec: "unknown_codec".to_string(),
175        };
176        assert!(error.to_string().contains("Codec not supported"));
177    }
178
179    #[test]
180    fn test_prelude_imports() {
181        // Verify prelude exports all expected types
182        use crate::prelude::*;
183
184        let _mode: SeekMode = SeekMode::default();
185        let _accel: HardwareAccel = HardwareAccel::default();
186
187        // Video builder can be created
188        let _video_builder: VideoDecoderBuilder = VideoDecoder::open("test.mp4");
189
190        // Audio builder can be created
191        let _audio_builder: AudioDecoderBuilder = AudioDecoder::open("test.mp3");
192    }
193}