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}