1use crate::{Application, OpusEncoder};
18
19#[derive(Clone, Copy)]
21pub struct ParallelConfig {
22 pub sample_rate: i32,
23 pub channels: usize,
24 pub application: Application,
25 pub bitrate_bps: i32,
26 pub complexity: i32,
27 pub use_cbr: bool,
28 pub warmup: usize,
33 pub threads: usize,
35}
36
37impl ParallelConfig {
38 pub fn new(sample_rate: i32, channels: usize, application: Application) -> Self {
39 ParallelConfig {
40 sample_rate,
41 channels,
42 application,
43 bitrate_bps: 64_000,
44 complexity: 9,
45 use_cbr: false,
46 warmup: 8,
47 threads: 0,
48 }
49 }
50}
51
52pub fn encode_parallel(cfg: &ParallelConfig, pcm: &[f32], frame_size: usize) -> Vec<Vec<u8>> {
60 let step = frame_size * cfg.channels;
61 if step == 0 {
62 return Vec::new();
63 }
64 let total_frames = pcm.len() / step;
65 if total_frames == 0 {
66 return Vec::new();
67 }
68
69 let threads = if cfg.threads == 0 {
70 std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
71 } else {
72 cfg.threads
73 };
74
75 let min_chunk = (cfg.warmup * 4).max(1);
78 let n_workers = threads.max(1).min((total_frames / min_chunk).max(1));
79 if n_workers <= 1 {
80 return encode_serial(cfg, pcm, frame_size);
81 }
82
83 let base = total_frames / n_workers;
85 let rem = total_frames % n_workers;
86 let mut ranges = Vec::with_capacity(n_workers);
87 let mut start = 0usize;
88 for w in 0..n_workers {
89 let len = base + if w < rem { 1 } else { 0 };
90 ranges.push((start, start + len));
91 start += len;
92 }
93
94 let mut chunks: Vec<Vec<Vec<u8>>> = Vec::new();
95 std::thread::scope(|scope| {
96 let handles: Vec<_> = ranges
97 .iter()
98 .map(|&(cstart, cend)| {
99 let cfg = cfg;
100 scope.spawn(move || encode_chunk(cfg, pcm, frame_size, cstart, cend))
101 })
102 .collect();
103 for h in handles {
104 chunks.push(h.join().expect("opus parallel worker panicked"));
105 }
106 });
107
108 let mut out = Vec::with_capacity(total_frames);
110 for c in chunks {
111 out.extend(c);
112 }
113 out
114}
115
116fn encode_chunk(
119 cfg: &ParallelConfig,
120 pcm: &[f32],
121 frame_size: usize,
122 cstart: usize,
123 cend: usize,
124) -> Vec<Vec<u8>> {
125 let step = frame_size * cfg.channels;
126 let mut enc = new_encoder(cfg);
127 let warm_start = cstart.saturating_sub(cfg.warmup);
128 let mut buf = vec![0u8; 4000];
129 let mut packets = Vec::with_capacity(cend - cstart);
130 for f in warm_start..cend {
131 let frame = &pcm[f * step..(f + 1) * step];
132 let n = enc.encode(frame, frame_size, &mut buf).expect("opus encode");
133 if f >= cstart {
134 packets.push(buf[..n].to_vec());
135 }
136 }
137 packets
138}
139
140pub fn encode_serial(cfg: &ParallelConfig, pcm: &[f32], frame_size: usize) -> Vec<Vec<u8>> {
143 let step = frame_size * cfg.channels;
144 if step == 0 {
145 return Vec::new();
146 }
147 let total_frames = pcm.len() / step;
148 let mut enc = new_encoder(cfg);
149 let mut buf = vec![0u8; 4000];
150 let mut packets = Vec::with_capacity(total_frames);
151 for f in 0..total_frames {
152 let frame = &pcm[f * step..(f + 1) * step];
153 let n = enc.encode(frame, frame_size, &mut buf).expect("opus encode");
154 packets.push(buf[..n].to_vec());
155 }
156 packets
157}
158
159pub fn encode_streams(
169 streams: &[(ParallelConfig, &[f32], usize)],
170 threads: usize,
171) -> Vec<Vec<Vec<u8>>> {
172 let n = streams.len();
173 let mut out: Vec<Vec<Vec<u8>>> = (0..n).map(|_| Vec::new()).collect();
174 if n == 0 {
175 return out;
176 }
177 let workers = if threads == 0 {
178 std::thread::available_parallelism().map(|p| p.get()).unwrap_or(1)
179 } else {
180 threads
181 }
182 .max(1)
183 .min(n);
184
185 let next = std::sync::atomic::AtomicUsize::new(0);
186 let out_slots: Vec<std::sync::Mutex<Option<Vec<Vec<u8>>>>> =
187 (0..n).map(|_| std::sync::Mutex::new(None)).collect();
188 std::thread::scope(|scope| {
189 for _ in 0..workers {
190 let next = &next;
191 let out_slots = &out_slots;
192 scope.spawn(move || loop {
193 let idx = next.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
194 if idx >= n {
195 break;
196 }
197 let (cfg, pcm, frame_size) = &streams[idx];
198 let pkts = encode_serial(cfg, pcm, *frame_size);
199 *out_slots[idx].lock().unwrap() = Some(pkts);
200 });
201 }
202 });
203 for (slot, dst) in out_slots.into_iter().zip(out.iter_mut()) {
204 *dst = slot.into_inner().unwrap().unwrap_or_default();
205 }
206 out
207}
208
209fn new_encoder(cfg: &ParallelConfig) -> OpusEncoder {
210 let mut enc = OpusEncoder::new(cfg.sample_rate, cfg.channels, cfg.application)
211 .expect("opus encoder init");
212 enc.bitrate_bps = cfg.bitrate_bps;
213 enc.complexity = cfg.complexity.clamp(0, 10);
214 enc.use_cbr = cfg.use_cbr;
215 enc
216}