Skip to main content

ff_encode/
lib.rs

1//! # ff-encode
2//!
3//! Video and audio encoding - the Rust way.
4
5// FFmpeg's C API is called through `unsafe`; the FFI is isolated in the
6// `*_inner` modules, which carry their own scoped clippy allows for the
7// FFmpeg-boundary lints (casts, pointer idioms).
8#![allow(unsafe_code)]
9//!
10//! This crate provides video and audio encoding functionality for timeline export.
11//! It supports automatic codec selection with LGPL compliance, hardware acceleration,
12//! and provides a clean Builder pattern API.
13//!
14//! ## Features
15//!
16//! - **Video Encoding**: H.264, H.265, VP9, AV1, `ProRes`, `DNxHD`
17//! - **Audio Encoding**: AAC, Opus, MP3, FLAC, PCM, Vorbis
18//! - **Hardware Acceleration**: NVENC, QSV, AMF, `VideoToolbox`, VA-API
19//! - **LGPL Compliance**: Automatic codec fallback when GPL features disabled
20//! - **Progress Callbacks**: Real-time encoding progress updates
21//! - **Builder Pattern**: Ergonomic encoder configuration
22//!
23//! ## Usage
24//!
25//! ### Basic Encoding
26//!
27//! ```ignore
28//! use ff_encode::{VideoEncoder, VideoCodec, AudioCodec, BitrateMode, Preset};
29//! use ff_format::VideoFrame;
30//!
31//! // Create encoder with Builder pattern
32//! let mut encoder = VideoEncoder::create("output.mp4")?
33//!     .video(1920, 1080, 30.0)          // resolution, FPS
34//!     .video_codec(VideoCodec::H264)     // codec
35//!     .bitrate_mode(BitrateMode::Cbr(8_000_000))  // 8 Mbps
36//!     .preset(Preset::Medium)            // speed/quality balance
37//!     .audio(48000, 2)                   // sample rate, channels
38//!     .audio_codec(AudioCodec::Aac)
39//!     .audio_bitrate(192_000)            // 192 kbps
40//!     .build()?;
41//!
42//! // Check actual codec used
43//! println!("Video codec: {}", encoder.actual_video_codec());
44//! println!("Audio codec: {}", encoder.actual_audio_codec());
45//!
46//! // Push frames
47//! for frame in frames {
48//!     encoder.push_video(&frame)?;
49//! }
50//!
51//! // Push audio
52//! encoder.push_audio(&audio_samples)?;
53//!
54//! // Finish encoding
55//! encoder.finish()?;
56//! ```
57//!
58//! ### Progress Callbacks
59//!
60//! ```ignore
61//! use ff_encode::{VideoEncoder, EncodeProgress};
62//!
63//! // Simple closure-based callback
64//! let mut encoder = VideoEncoder::create("output.mp4")?
65//!     .video(1920, 1080, 30.0)
66//!     .on_progress(|progress| {
67//!         println!("Encoded {} frames ({:.1}%) at {:.1} fps",
68//!             progress.frames_encoded,
69//!             progress.percent(),
70//!             progress.current_fps
71//!         );
72//!     })
73//!     .build()?;
74//! ```
75//!
76//! ### Progress Callbacks with Cancellation
77//!
78//! ```ignore
79//! use ff_encode::{VideoEncoder, EncodeProgressCallback, EncodeProgress};
80//! use std::sync::Arc;
81//! use std::sync::atomic::{AtomicBool, Ordering};
82//!
83//! struct CancellableProgress {
84//!     cancelled: Arc<AtomicBool>,
85//! }
86//!
87//! impl EncodeProgressCallback for CancellableProgress {
88//!     fn on_progress(&mut self, progress: &EncodeProgress) {
89//!         println!("Progress: {:.1}%", progress.percent());
90//!     }
91//!
92//!     fn should_cancel(&self) -> bool {
93//!         self.cancelled.load(Ordering::Relaxed)
94//!     }
95//! }
96//!
97//! let cancelled = Arc::new(AtomicBool::new(false));
98//! let mut encoder = VideoEncoder::create("output.mp4")?
99//!     .video(1920, 1080, 30.0)
100//!     .progress_callback(CancellableProgress {
101//!         cancelled: cancelled.clone()
102//!     })
103//!     .build()?;
104//!
105//! // Later, to cancel encoding:
106//! cancelled.store(true, Ordering::Relaxed);
107//! ```
108//!
109//! ### Hardware Encoding
110//!
111//! ```ignore
112//! use ff_encode::{VideoEncoder, HardwareEncoder};
113//!
114//! // Check available hardware encoders
115//! for hw in HardwareEncoder::available() {
116//!     println!("Available: {:?}", hw);
117//! }
118//!
119//! // Create encoder with auto hardware detection
120//! let mut encoder = VideoEncoder::create("output.mp4")?
121//!     .video(1920, 1080, 60.0)
122//!     .hardware_encoder(HardwareEncoder::Auto)  // auto-detect
123//!     .build()?;
124//!
125//! // Check what encoder was actually used
126//! println!("Using: {}", encoder.actual_video_codec());
127//! println!("Hardware encoder: {:?}", encoder.hardware_encoder());
128//! println!("Is hardware encoding: {}", encoder.is_hardware_encoding());
129//! ```
130//!
131//! ## LGPL Compliance & Commercial Use
132//!
133//! **By default, this crate is LGPL-compliant and safe for commercial use without licensing fees.**
134//!
135//! ### Default Behavior (LGPL-Compatible)
136//!
137//! When H.264/H.265 encoding is requested, the encoder automatically selects codecs in this priority:
138//!
139//! 1. **Hardware encoders** (LGPL-compatible, no licensing fees):
140//!    - NVIDIA NVENC (`h264_nvenc`, `hevc_nvenc`)
141//!    - Intel Quick Sync Video (`h264_qsv`, `hevc_qsv`)
142//!    - AMD AMF/VCE (`h264_amf`, `hevc_amf`)
143//!    - Apple `VideoToolbox` (`h264_videotoolbox`, `hevc_videotoolbox`)
144//!    - VA-API (`h264_vaapi`, `hevc_vaapi`) - Linux
145//!
146//! 2. **Fallback to royalty-free codecs**:
147//!    - For H.264 request → VP9 (libvpx-vp9)
148//!    - For H.265 request → AV1 (libaom-av1)
149//!
150//! ### GPL Feature (Commercial Licensing Required)
151//!
152//! Enable GPL codecs (libx264, libx265) only if:
153//! - You have appropriate licenses from MPEG LA, or
154//! - Your software is GPL-licensed (open source), or
155//! - For non-commercial/educational use only
156//!
157//! ```toml
158//! # WARNING: Requires GPL compliance and licensing fees for commercial use
159//! ff-encode = { version = "0.1", features = ["gpl"] }
160//! ```
161//!
162//! ### Checking Compliance at Runtime
163//!
164//! You can verify which encoder was selected:
165//!
166//! ```ignore
167//! let encoder = VideoEncoder::create("output.mp4")?
168//!     .video(1920, 1080, 30.0)
169//!     .video_codec(VideoCodec::H264)
170//!     .build()?;
171//!
172//! println!("Using: {}", encoder.actual_video_codec());
173//! println!("LGPL compliant: {}", encoder.is_lgpl_compliant());
174//! ```
175
176#[cfg(feature = "tokio")]
177mod async_encoder;
178mod audio;
179mod error;
180mod image;
181mod preset;
182#[cfg(feature = "preview-image")]
183mod preview;
184mod shared;
185mod video;
186
187pub use audio::{
188    AacOptions, AacProfile, AudioCodecOptions, AudioEncoder, AudioEncoderBuilder, FlacOptions,
189    Mp3Options, Mp3Quality, OpusApplication, OpusOptions,
190};
191pub use error::EncodeError;
192// The bound `VideoEncoderBuilder::output_sink` takes. Callers passing a `Cursor`
193// or a `File` never name it; one storing a boxed sink does.
194pub use ff_format::{ErrorSeverity, MediaError};
195pub use ff_sys::IoSink;
196pub use image::{ImageEncoder, ImageEncoderBuilder};
197pub use preset::{AudioEncoderConfig, ExportPreset, VideoEncoderConfig};
198#[cfg(feature = "preview-image")]
199pub use preview::{GifPreview, PreviewImageError, SpriteSheet};
200pub use shared::{
201    AudioCodec, BitrateMode, CRF_MAX, EncodeProgress, EncodeProgressCallback, HardwareEncoder,
202    OutputContainer, Preset, VideoCodec, VideoCodecEncodeExt,
203};
204pub use video::{
205    Av1Options, Av1Usage, DnxhdOptions, DnxhdVariant, H264Options, H264Preset, H264Profile,
206    H264Tune, H265Options, H265Profile, H265Tier, ProResOptions, ProResProfile, SvtAv1Options,
207    VideoCodecOptions, VideoEncoder, VideoEncoderBuilder, Vp9Options,
208};
209
210#[cfg(feature = "tokio")]
211pub use audio::AsyncAudioEncoder;
212#[cfg(feature = "tokio")]
213pub use video::AsyncVideoEncoder;