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