Skip to main content

ff_decode/video/builder/
hw.rs

1//! Hardware acceleration and threading options for [`VideoDecoderBuilder`].
2
3use std::sync::Arc;
4
5use ff_common::FramePool;
6
7use crate::HardwareAccel;
8
9use super::VideoDecoderBuilder;
10
11impl VideoDecoderBuilder {
12    /// Sets the hardware acceleration mode.
13    ///
14    /// Hardware acceleration can significantly improve decoding performance,
15    /// especially for high-resolution video (4K and above).
16    ///
17    /// # Available Modes
18    ///
19    /// - [`HardwareAccel::Auto`] - Automatically detect and use available hardware (default)
20    /// - [`HardwareAccel::None`] - Disable hardware acceleration (CPU only)
21    /// - [`HardwareAccel::Nvdec`] - NVIDIA NVDEC (requires NVIDIA GPU)
22    /// - [`HardwareAccel::Qsv`] - Intel Quick Sync Video
23    /// - [`HardwareAccel::Amf`] - AMD Advanced Media Framework
24    /// - [`HardwareAccel::VideoToolbox`] - Apple `VideoToolbox` (macOS/iOS)
25    /// - [`HardwareAccel::Vaapi`] - VA-API (Linux)
26    ///
27    /// # Fallback Behavior
28    ///
29    /// If the requested hardware accelerator is unavailable, the decoder
30    /// will fall back to software decoding unless
31    /// `DecodeError::HwAccelUnavailable` is explicitly requested.
32    ///
33    /// # Examples
34    ///
35    /// ```ignore
36    /// use ff_decode::{VideoDecoder, HardwareAccel};
37    ///
38    /// // Use NVIDIA NVDEC if available
39    /// let decoder = VideoDecoder::open("video.mp4")?
40    ///     .hardware_accel(HardwareAccel::Nvdec)
41    ///     .build()?;
42    ///
43    /// // Force CPU decoding
44    /// let cpu_decoder = Decoder::open("video.mp4")?
45    ///     .hardware_accel(HardwareAccel::None)
46    ///     .build()?;
47    /// ```
48    #[must_use]
49    pub fn hardware_accel(mut self, accel: HardwareAccel) -> Self {
50        self.hardware_accel = accel;
51        self
52    }
53
54    /// Sets the number of decoding threads.
55    ///
56    /// More threads can improve decoding throughput, especially for
57    /// high-resolution videos or codecs that support parallel decoding.
58    ///
59    /// # Thread Count Values
60    ///
61    /// - `0` - Auto-detect based on CPU cores (default)
62    /// - `1` - Single-threaded decoding
63    /// - `N` - Use N threads for decoding
64    ///
65    /// # Performance Notes
66    ///
67    /// - H.264/H.265: Benefit significantly from multi-threading
68    /// - VP9: Good parallel decoding support
69    /// - `ProRes`: Limited threading benefit
70    ///
71    /// Setting too many threads may increase memory usage without
72    /// proportional performance gains.
73    ///
74    /// # Examples
75    ///
76    /// ```ignore
77    /// use ff_decode::VideoDecoder;
78    ///
79    /// // Use 4 threads for decoding
80    /// let decoder = VideoDecoder::open("video.mp4")?
81    ///     .thread_count(4)
82    ///     .build()?;
83    ///
84    /// // Single-threaded for minimal memory
85    /// let decoder = VideoDecoder::open("video.mp4")?
86    ///     .thread_count(1)
87    ///     .build()?;
88    /// ```
89    #[must_use]
90    pub fn thread_count(mut self, count: usize) -> Self {
91        self.thread_count = count;
92        self
93    }
94
95    /// Sets a frame pool for memory reuse.
96    ///
97    /// Using a frame pool can significantly reduce allocation overhead
98    /// during continuous video playback by reusing frame buffers.
99    ///
100    /// # Memory Management
101    ///
102    /// When a frame pool is set:
103    /// - Decoded frames attempt to acquire buffers from the pool
104    /// - When frames are dropped, their buffers are returned to the pool
105    /// - If the pool is exhausted, new buffers are allocated normally
106    ///
107    /// # Examples
108    ///
109    /// ```ignore
110    /// use ff_decode::{VideoDecoder, FramePool, PooledBuffer};
111    /// use std::sync::{Arc, Mutex};
112    ///
113    /// // Create a simple frame pool
114    /// struct SimplePool {
115    ///     buffers: Mutex<Vec<Vec<u8>>>,
116    /// }
117    ///
118    /// impl FramePool for SimplePool {
119    ///     fn acquire(&self, size: usize) -> Option<PooledBuffer> {
120    ///         // Implementation...
121    ///         None
122    ///     }
123    /// }
124    ///
125    /// let pool = Arc::new(SimplePool {
126    ///     buffers: Mutex::new(vec![]),
127    /// });
128    ///
129    /// let decoder = VideoDecoder::open("video.mp4")?
130    ///     .frame_pool(pool)
131    ///     .build()?;
132    /// ```
133    #[must_use]
134    pub fn frame_pool(mut self, pool: Arc<dyn FramePool>) -> Self {
135        self.frame_pool = Some(pool);
136        self
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use std::path::PathBuf;
144
145    #[test]
146    fn builder_hardware_accel_should_override_default() {
147        let builder = VideoDecoderBuilder::new(PathBuf::from("test.mp4"))
148            .hardware_accel(HardwareAccel::Nvdec);
149
150        assert_eq!(builder.get_hardware_accel(), HardwareAccel::Nvdec);
151    }
152
153    #[test]
154    fn builder_hardware_accel_none_should_disable_hw() {
155        let builder =
156            VideoDecoderBuilder::new(PathBuf::from("test.mp4")).hardware_accel(HardwareAccel::None);
157
158        assert_eq!(builder.get_hardware_accel(), HardwareAccel::None);
159    }
160
161    #[test]
162    fn builder_thread_count_should_set_thread_count() {
163        let builder = VideoDecoderBuilder::new(PathBuf::from("test.mp4")).thread_count(8);
164
165        assert_eq!(builder.get_thread_count(), 8);
166    }
167
168    #[test]
169    fn builder_thread_count_single_thread_should_be_accepted() {
170        let builder = VideoDecoderBuilder::new(PathBuf::from("test.mp4")).thread_count(1);
171
172        assert_eq!(builder.get_thread_count(), 1);
173    }
174
175    #[test]
176    fn builder_thread_count_zero_means_auto() {
177        let builder = VideoDecoderBuilder::new(PathBuf::from("test.mp4")).thread_count(0);
178
179        assert_eq!(builder.get_thread_count(), 0);
180    }
181}