1use std::collections::VecDeque;
13
14use super::frame_rate::FrameRate;
15use super::software::{convert_to_yuv_mt, convert_to_yuv10_mt};
16use super::{QP_HYSTERESIS_LIMIT, vbv_bits};
17use crate::RustCaptureSettings;
18
19pub struct Planes {
22 pub width: usize,
23 pub height: usize,
24 pub i444: bool,
25 pub bit_depth: u32,
26 pub y: Vec<u8>,
27 pub u: Vec<u8>,
28 pub v: Vec<u8>,
29 pub y16: Vec<u16>,
30 pub u16: Vec<u16>,
31 pub v16: Vec<u16>,
32}
33
34impl Planes {
35 pub fn new(width: usize, height: usize, i444: bool, bit_depth: u32) -> Self {
36 let (cw, ch) = if i444 {
37 (width, height)
38 } else {
39 (width.div_ceil(2), height.div_ceil(2))
40 };
41 let (narrow, wide) = if bit_depth > 8 { (0, 1) } else { (1, 0) };
42 Self {
43 width,
44 height,
45 i444,
46 bit_depth,
47 y: vec![0; width * height * narrow],
48 u: vec![0; cw * ch * narrow],
49 v: vec![0; cw * ch * narrow],
50 y16: vec![0; width * height * wide],
51 u16: vec![0; cw * ch * wide],
52 v16: vec![0; cw * ch * wide],
53 }
54 }
55
56 pub fn chroma_width(&self) -> usize {
57 if self.i444 {
58 self.width
59 } else {
60 self.width.div_ceil(2)
61 }
62 }
63
64 pub fn pointers(&mut self) -> [*mut u8; 3] {
66 if self.bit_depth > 8 {
67 [
68 self.y16.as_mut_ptr().cast(),
69 self.u16.as_mut_ptr().cast(),
70 self.v16.as_mut_ptr().cast(),
71 ]
72 } else {
73 [
74 self.y.as_mut_ptr(),
75 self.u.as_mut_ptr(),
76 self.v.as_mut_ptr(),
77 ]
78 }
79 }
80
81 pub fn sample_bytes(&self) -> usize {
83 if self.bit_depth > 8 { 2 } else { 1 }
84 }
85
86 pub fn byte_len(&self) -> usize {
88 self.y.len()
89 + self.u.len()
90 + self.v.len()
91 + 2 * (self.y16.len() + self.u16.len() + self.v16.len())
92 }
93
94 pub fn convert(
98 &mut self,
99 pixels: &[u8],
100 stride: usize,
101 rgba: bool,
102 full_range: bool,
103 bt601: bool,
104 threads: usize,
105 ) -> Result<(), String> {
106 let cw = self.chroma_width();
107 if self.bit_depth > 8 {
108 check_host_frame(pixels, stride, self.width, self.height)?;
109 convert_to_yuv10_mt(
110 pixels,
111 stride,
112 self.width,
113 self.height,
114 rgba,
115 self.i444,
116 full_range,
117 &mut self.y16,
118 &mut self.u16,
119 &mut self.v16,
120 (self.width, cw),
121 threads,
122 );
123 return Ok(());
124 }
125 convert_into(
126 pixels,
127 stride,
128 self.width,
129 self.height,
130 rgba,
131 self.i444,
132 full_range,
133 bt601,
134 threads,
135 &mut self.y,
136 &mut self.u,
137 &mut self.v,
138 (self.width, cw),
139 )
140 }
141}
142
143#[allow(clippy::too_many_arguments)]
146pub fn convert_into(
147 pixels: &[u8],
148 stride: usize,
149 width: usize,
150 height: usize,
151 rgba: bool,
152 i444: bool,
153 full_range: bool,
154 bt601: bool,
155 threads: usize,
156 y: &mut [u8],
157 u: &mut [u8],
158 v: &mut [u8],
159 strides: (usize, usize),
160) -> Result<(), String> {
161 check_host_frame(pixels, stride, width, height)?;
162 convert_to_yuv_mt(
163 pixels,
164 stride as u32,
165 width,
166 height,
167 rgba,
168 i444,
169 full_range,
170 bt601,
171 y,
172 u,
173 v,
174 strides,
175 threads,
176 )
177 .map_err(|e| format!("rgb-to-yuv conversion failed: {e:?}"))
178}
179
180pub fn check_host_frame(
182 pixels: &[u8],
183 stride: usize,
184 width: usize,
185 height: usize,
186) -> Result<(), String> {
187 let row_bytes = width * 4;
188 let needed = if height == 0 {
189 0
190 } else {
191 stride.checked_mul(height - 1).ok_or("stride overflow")? + row_bytes
192 };
193 if stride < row_bytes || pixels.len() < needed {
194 return Err("Input buffer too small".into());
195 }
196 Ok(())
197}
198
199pub struct Quality {
204 pub current: u32,
205 counter: u32,
206}
207
208impl Quality {
209 pub fn new(quantizer: u32) -> Self {
210 Self {
211 current: quantizer,
212 counter: 0,
213 }
214 }
215
216 pub fn update(&mut self, target: u32) -> Option<u32> {
218 if target == self.current {
219 self.counter = 0;
220 return None;
221 }
222 if target > self.current {
223 self.counter += 1;
224 if self.counter <= QP_HYSTERESIS_LIMIT {
225 return None;
226 }
227 }
228 self.counter = 0;
229 self.current = target;
230 Some(target)
231 }
232}
233
234#[derive(Clone, Copy, Debug, PartialEq)]
237pub struct RateSettings {
238 pub cbr: bool,
239 pub bitrate_kbps: i32,
240 pub vbv_multiplier: f64,
241 pub keyframe_interval_s: f64,
242 pub fps: FrameRate,
243 pub min_qp: i32,
244 pub max_qp: i32,
245}
246
247impl RateSettings {
248 pub fn new(settings: &RustCaptureSettings) -> Self {
249 Self {
250 cbr: settings.video_cbr_mode,
251 bitrate_kbps: settings.video_bitrate_kbps,
252 vbv_multiplier: settings.video_vbv_multiplier,
253 keyframe_interval_s: settings.keyframe_interval_s,
254 fps: FrameRate::of(settings.target_fps),
255 min_qp: settings.video_min_qp,
256 max_qp: settings.video_max_qp,
257 }
258 }
259
260 pub fn changed(&self, settings: &RustCaptureSettings) -> Option<Self> {
263 let next = Self {
264 cbr: self.cbr,
265 min_qp: self.min_qp,
266 max_qp: self.max_qp,
267 ..Self::new(settings)
268 };
269 let rate_moved = self.cbr
270 && (next.bitrate_kbps != self.bitrate_kbps
271 || next.vbv_multiplier != self.vbv_multiplier);
272 (rate_moved || next.fps != self.fps).then_some(next)
273 }
274
275 pub fn bps(&self) -> u64 {
277 (self.bitrate_kbps.max(0) as u64) * 1000
278 }
279
280 pub fn vbv(&self) -> u32 {
282 vbv_bits(
283 self.bps().min(u32::MAX as u64) as u32,
284 self.fps.fps(),
285 self.keyframe_interval_s,
286 self.vbv_multiplier,
287 )
288 }
289}
290
291#[derive(Default)]
296pub struct Pending(VecDeque<(u64, u16)>);
297
298impl Pending {
299 pub fn push(&mut self, pts: u64, frame_id: u16) {
301 self.0.push_back((pts, frame_id));
302 }
303
304 pub fn undo(&mut self) {
306 self.0.pop_back();
307 }
308
309 pub fn is_empty(&self) -> bool {
310 self.0.is_empty()
311 }
312
313 pub fn take(&mut self, pts: u64) -> Option<u16> {
316 let at = self.0.iter().position(|&(p, _)| p == pts)?;
317 let id = self.0[at].1;
318 self.0.drain(..=at);
319 Some(id)
320 }
321}
322
323pub fn encode_threads() -> i32 {
326 std::thread::available_parallelism()
327 .map(|n| n.get())
328 .unwrap_or(1)
329 .saturating_sub(1)
330 .clamp(1, 8) as i32
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn a_quality_increase_applies_at_once_and_a_drop_waits() {
339 let mut q = Quality::new(30);
340 assert_eq!(q.update(30), None);
341 assert_eq!(q.update(20), Some(20), "a lower quantizer applies at once");
342 for _ in 0..QP_HYSTERESIS_LIMIT {
343 assert_eq!(q.update(40), None);
344 }
345 assert_eq!(
346 q.update(40),
347 Some(40),
348 "a higher one waits out the hysteresis"
349 );
350 assert_eq!(q.update(45), None);
351 assert_eq!(
352 q.update(40),
353 None,
354 "a request back at the current value resets the count"
355 );
356 assert_eq!(q.current, 40);
357 }
358
359 #[test]
360 fn a_rate_change_is_noticed_only_where_the_encoder_reads_it() {
361 let mut settings = RustCaptureSettings {
362 target_fps: 30.0,
363 video_bitrate_kbps: 4000,
364 ..Default::default()
365 };
366 let rate = RateSettings::new(&settings);
367 assert_eq!(rate.changed(&settings), None);
368 settings.video_bitrate_kbps = 8000;
369 assert_eq!(
370 rate.changed(&settings),
371 None,
372 "a bitrate moves nothing in constant-quality mode"
373 );
374 settings.target_fps = 60.0;
375 assert_eq!(
376 rate.changed(&settings).map(|r| r.fps),
377 Some(FrameRate { num: 60, den: 1 })
378 );
379 let ntsc = RateSettings::new(&RustCaptureSettings {
380 target_fps: 60000.0 / 1001.0,
381 ..settings.clone()
382 });
383 assert_eq!(
384 ntsc.fps,
385 FrameRate {
386 num: 60000,
387 den: 1001
388 }
389 );
390 assert!(
391 ntsc.changed(&settings).is_some(),
392 "60 fps moves a session at 59.94"
393 );
394 settings.video_cbr_mode = true;
395 let cbr = RateSettings::new(&settings);
396 settings.video_bitrate_kbps = 9000;
397 assert_eq!(cbr.changed(&settings).map(|r| r.bitrate_kbps), Some(9000));
398 assert_eq!(cbr.bps(), 8_000_000);
399 }
400
401 #[test]
404 fn a_packet_names_the_frame_it_encodes() {
405 let mut pending = Pending::default();
406 assert!(pending.is_empty());
407 for (pts, id) in [(0u64, 7u16), (1, 8), (2, 9)] {
408 pending.push(pts, id);
409 }
410 assert_eq!(pending.take(99), None, "a timestamp never submitted");
411 assert_eq!(
412 pending.take(1),
413 Some(8),
414 "the encoder answered two frames in"
415 );
416 assert_eq!(
417 pending.take(0),
418 None,
419 "the frame it passed over went with it"
420 );
421 assert_eq!(pending.take(2), Some(9));
422 assert!(pending.is_empty());
423 pending.push(3, 10);
424 pending.undo();
425 assert_eq!(pending.take(3), None, "a refused frame was never submitted");
426 }
427
428 #[test]
429 fn a_frame_shorter_than_its_stride_says_so() {
430 assert!(check_host_frame(&[0; 16 * 4 * 4], 64, 16, 4).is_ok());
431 assert!(check_host_frame(&[0; 16 * 4 * 4 - 1], 64, 16, 4).is_err());
432 assert!(
433 check_host_frame(&[0; 16 * 4 * 4], 60, 16, 4).is_err(),
434 "a stride shorter than a row"
435 );
436 let mut planes = Planes::new(4, 2, false, 8);
437 assert_eq!((planes.u.len(), planes.chroma_width()), (2, 2));
438 assert!(
439 planes
440 .convert(&[0; 4 * 4 * 2], 16, false, false, false, 1)
441 .is_ok()
442 );
443 assert!(
444 Planes::new(4, 2, true, 8)
445 .convert(&[0; 4 * 4], 16, false, true, false, 1)
446 .is_err()
447 );
448 }
449}