1#![allow(clippy::cast_lossless)]
24#![allow(clippy::cast_precision_loss)]
25#![allow(clippy::cast_possible_truncation)]
26#![allow(clippy::cast_sign_loss)]
27#![allow(clippy::similar_names)]
28#![allow(clippy::too_many_arguments)]
29#![allow(clippy::struct_excessive_bools)]
30#![forbid(unsafe_code)]
31
32use crate::frame::FrameType;
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum QpStrategy {
37 Constant,
39 FrameTypeBased,
41 ComplexityAdaptive,
43 Psychovisual,
45 RateDistortionOptimized,
47 Hierarchical,
49}
50
51impl Default for QpStrategy {
52 fn default() -> Self {
53 Self::ComplexityAdaptive
54 }
55}
56
57#[derive(Clone, Debug)]
59pub struct QpSelector {
60 strategy: QpStrategy,
62 base_qp: f32,
64 min_qp: u8,
66 max_qp: u8,
68 i_qp_offset: i8,
70 p_qp_offset: i8,
72 b_qp_offset: i8,
74 enable_aq: bool,
76 aq_strength: f32,
78 enable_psy: bool,
80 psy_strength: f32,
82 adaptation_speed: f32,
84 historical_qp: Vec<f32>,
86 max_history: usize,
88 frame_count: u64,
90}
91
92impl QpSelector {
93 #[must_use]
95 pub fn new(base_qp: f32, min_qp: u8, max_qp: u8) -> Self {
96 Self {
97 strategy: QpStrategy::default(),
98 base_qp: base_qp.clamp(min_qp as f32, max_qp as f32),
99 min_qp,
100 max_qp,
101 i_qp_offset: -2,
102 p_qp_offset: 0,
103 b_qp_offset: 2,
104 enable_aq: true,
105 aq_strength: 1.0,
106 enable_psy: true,
107 psy_strength: 1.0,
108 adaptation_speed: 0.5,
109 historical_qp: Vec::new(),
110 max_history: 100,
111 frame_count: 0,
112 }
113 }
114
115 pub fn set_strategy(&mut self, strategy: QpStrategy) {
117 self.strategy = strategy;
118 }
119
120 pub fn set_base_qp(&mut self, qp: f32) {
122 self.base_qp = qp.clamp(self.min_qp as f32, self.max_qp as f32);
123 }
124
125 pub fn set_i_qp_offset(&mut self, offset: i8) {
127 self.i_qp_offset = offset;
128 }
129
130 pub fn set_p_qp_offset(&mut self, offset: i8) {
132 self.p_qp_offset = offset;
133 }
134
135 pub fn set_b_qp_offset(&mut self, offset: i8) {
137 self.b_qp_offset = offset;
138 }
139
140 pub fn set_aq_enabled(&mut self, enabled: bool) {
142 self.enable_aq = enabled;
143 }
144
145 pub fn set_aq_strength(&mut self, strength: f32) {
147 self.aq_strength = strength.clamp(0.0, 2.0);
148 }
149
150 pub fn set_psy_enabled(&mut self, enabled: bool) {
152 self.enable_psy = enabled;
153 }
154
155 pub fn set_psy_strength(&mut self, strength: f32) {
157 self.psy_strength = strength.clamp(0.0, 2.0);
158 }
159
160 pub fn set_adaptation_speed(&mut self, speed: f32) {
162 self.adaptation_speed = speed.clamp(0.0, 1.0);
163 }
164
165 #[must_use]
167 pub fn select_frame_qp(
168 &mut self,
169 frame_type: FrameType,
170 complexity: f32,
171 target_bits: u64,
172 frame_in_gop: u32,
173 ) -> QpResult {
174 let base_qp = match self.strategy {
175 QpStrategy::Constant => self.select_constant_qp(frame_type),
176 QpStrategy::FrameTypeBased => self.select_frame_type_qp(frame_type),
177 QpStrategy::ComplexityAdaptive => {
178 self.select_complexity_adaptive_qp(frame_type, complexity)
179 }
180 QpStrategy::Psychovisual => {
181 self.select_psychovisual_qp(frame_type, complexity, target_bits)
182 }
183 QpStrategy::RateDistortionOptimized => {
184 self.select_rd_optimized_qp(frame_type, complexity, target_bits)
185 }
186 QpStrategy::Hierarchical => self.select_hierarchical_qp(frame_type, frame_in_gop),
187 };
188
189 let lambda = Self::qp_to_lambda(base_qp);
191 let lambda_me = lambda.sqrt();
192
193 let block_qp_offsets = if self.enable_aq {
195 Some(self.generate_aq_offsets(base_qp, complexity))
196 } else {
197 None
198 };
199
200 self.historical_qp.push(base_qp);
202 if self.historical_qp.len() > self.max_history {
203 self.historical_qp.remove(0);
204 }
205
206 self.frame_count += 1;
207
208 QpResult {
209 qp: base_qp.round() as u8,
210 qp_f: base_qp,
211 lambda,
212 lambda_me,
213 block_qp_offsets,
214 frame_type,
215 complexity_factor: complexity / self.average_complexity(),
216 }
217 }
218
219 fn select_constant_qp(&self, frame_type: FrameType) -> f32 {
221 let offset = self.get_frame_type_offset(frame_type);
222 (self.base_qp + offset as f32).clamp(self.min_qp as f32, self.max_qp as f32)
223 }
224
225 fn select_frame_type_qp(&self, frame_type: FrameType) -> f32 {
227 let offset = self.get_frame_type_offset(frame_type);
228 (self.base_qp + offset as f32).clamp(self.min_qp as f32, self.max_qp as f32)
229 }
230
231 fn select_complexity_adaptive_qp(&self, frame_type: FrameType, complexity: f32) -> f32 {
233 let base = self.select_frame_type_qp(frame_type);
234 let avg_complexity = self.average_complexity();
235
236 if avg_complexity <= 0.0 {
237 return base;
238 }
239
240 let complexity_ratio = complexity / avg_complexity;
242 let adjustment = (complexity_ratio - 1.0) * 3.0 * self.adaptation_speed;
243
244 (base + adjustment).clamp(self.min_qp as f32, self.max_qp as f32)
245 }
246
247 fn select_psychovisual_qp(
249 &self,
250 frame_type: FrameType,
251 complexity: f32,
252 _target_bits: u64,
253 ) -> f32 {
254 let base = self.select_complexity_adaptive_qp(frame_type, complexity);
255
256 if !self.enable_psy {
257 return base;
258 }
259
260 let psy_adjustment = if complexity > 3.0 {
263 self.psy_strength * 0.5
265 } else if complexity < 0.5 {
266 -self.psy_strength * 0.5
268 } else {
269 0.0
270 };
271
272 (base + psy_adjustment).clamp(self.min_qp as f32, self.max_qp as f32)
273 }
274
275 fn select_rd_optimized_qp(
277 &self,
278 frame_type: FrameType,
279 complexity: f32,
280 target_bits: u64,
281 ) -> f32 {
282 let base = self.select_complexity_adaptive_qp(frame_type, complexity);
283
284 let estimated_bits = self.estimate_bits_at_qp(base, complexity, frame_type);
286
287 if estimated_bits == 0 {
288 return base;
289 }
290
291 let bits_ratio = estimated_bits as f32 / target_bits as f32;
293
294 let qp_adjustment = if bits_ratio > 1.0 {
297 (bits_ratio.ln() * 6.0).min(5.0)
298 } else {
299 (bits_ratio.ln() * 6.0).max(-5.0)
300 };
301
302 (base + qp_adjustment).clamp(self.min_qp as f32, self.max_qp as f32)
303 }
304
305 fn select_hierarchical_qp(&self, frame_type: FrameType, frame_in_gop: u32) -> f32 {
307 let base = self.select_frame_type_qp(frame_type);
308
309 if frame_type != FrameType::BiDir {
310 return base;
311 }
312
313 let pyramid_level = self.calculate_pyramid_level(frame_in_gop);
315 let level_offset = pyramid_level as f32 * 1.0;
316
317 (base + level_offset).clamp(self.min_qp as f32, self.max_qp as f32)
318 }
319
320 fn calculate_pyramid_level(&self, frame_in_gop: u32) -> u32 {
322 let mut level = 0;
323 let mut pos = frame_in_gop;
324
325 while pos % 2 == 0 && pos > 0 {
326 level += 1;
327 pos /= 2;
328 }
329
330 level
331 }
332
333 fn get_frame_type_offset(&self, frame_type: FrameType) -> i8 {
335 match frame_type {
336 FrameType::Key => self.i_qp_offset,
337 FrameType::Inter => self.p_qp_offset,
338 FrameType::BiDir => self.b_qp_offset,
339 FrameType::Switch => (self.i_qp_offset + self.p_qp_offset) / 2,
340 }
341 }
342
343 fn generate_aq_offsets(&self, base_qp: f32, _complexity: f32) -> Vec<f32> {
345 Vec::new()
348 }
349
350 fn estimate_bits_at_qp(&self, qp: f32, complexity: f32, frame_type: FrameType) -> u64 {
352 let base_complexity_bits = complexity * 50_000.0;
356 let qp_ref = 28.0;
357 let qp_factor = 2.0_f32.powf((qp_ref - qp) / 6.0);
358
359 let frame_type_multiplier = match frame_type {
360 FrameType::Key => 3.0,
361 FrameType::Inter => 1.0,
362 FrameType::BiDir => 0.5,
363 FrameType::Switch => 1.5,
364 };
365
366 (base_complexity_bits * qp_factor * frame_type_multiplier) as u64
367 }
368
369 fn qp_to_lambda(qp: f32) -> f64 {
371 0.85 * 2.0_f64.powf((f64::from(qp) - 12.0) / 3.0)
373 }
374
375 fn average_qp(&self) -> f32 {
377 if self.historical_qp.is_empty() {
378 return self.base_qp;
379 }
380
381 let sum: f32 = self.historical_qp.iter().sum();
382 sum / self.historical_qp.len() as f32
383 }
384
385 fn average_complexity(&self) -> f32 {
387 1.0
390 }
391
392 pub fn update(&mut self, actual_qp: f32, actual_bits: u64, target_bits: u64) {
394 if target_bits == 0 {
396 return;
397 }
398
399 let bits_ratio = actual_bits as f32 / target_bits as f32;
400
401 let adjustment = if bits_ratio > 1.1 {
403 0.5 * self.adaptation_speed
404 } else if bits_ratio < 0.8 {
405 -0.5 * self.adaptation_speed
406 } else {
407 0.0
408 };
409
410 self.base_qp = (self.base_qp + adjustment).clamp(self.min_qp as f32, self.max_qp as f32);
411 }
412
413 pub fn reset(&mut self) {
415 self.historical_qp.clear();
416 self.frame_count = 0;
417 }
418}
419
420#[derive(Clone, Debug)]
422pub struct QpResult {
423 pub qp: u8,
425 pub qp_f: f32,
427 pub lambda: f64,
429 pub lambda_me: f64,
431 pub block_qp_offsets: Option<Vec<f32>>,
433 pub frame_type: FrameType,
435 pub complexity_factor: f32,
437}
438
439impl QpResult {
440 #[must_use]
442 pub fn get_block_qp(&self, block_index: usize) -> u8 {
443 if let Some(ref offsets) = self.block_qp_offsets {
444 if let Some(&offset) = offsets.get(block_index) {
445 return ((self.qp_f + offset).round() as i32).clamp(1, 63) as u8;
446 }
447 }
448 self.qp
449 }
450
451 #[must_use]
453 pub fn is_valid(&self, min_qp: u8, max_qp: u8) -> bool {
454 self.qp >= min_qp && self.qp <= max_qp
455 }
456}
457
458#[derive(Clone, Debug)]
460pub struct BlockQpMap {
461 width: usize,
463 height: usize,
465 qp_values: Vec<u8>,
467 base_qp: u8,
469}
470
471impl BlockQpMap {
472 #[must_use]
474 pub fn new(width: usize, height: usize, base_qp: u8) -> Self {
475 let qp_values = vec![base_qp; width * height];
476 Self {
477 width,
478 height,
479 qp_values,
480 base_qp,
481 }
482 }
483
484 pub fn set_block_qp(&mut self, x: usize, y: usize, qp: u8) {
486 if x < self.width && y < self.height {
487 self.qp_values[y * self.width + x] = qp;
488 }
489 }
490
491 #[must_use]
493 pub fn get_block_qp(&self, x: usize, y: usize) -> u8 {
494 if x < self.width && y < self.height {
495 self.qp_values[y * self.width + x]
496 } else {
497 self.base_qp
498 }
499 }
500
501 pub fn apply_offsets(&mut self, offsets: &[f32]) {
503 for (i, offset) in offsets.iter().enumerate() {
504 if i < self.qp_values.len() {
505 let new_qp = ((self.base_qp as f32 + offset).round() as i32).clamp(1, 63) as u8;
506 self.qp_values[i] = new_qp;
507 }
508 }
509 }
510
511 #[must_use]
513 pub fn average_qp(&self) -> f32 {
514 if self.qp_values.is_empty() {
515 return self.base_qp as f32;
516 }
517
518 let sum: u32 = self.qp_values.iter().map(|&qp| u32::from(qp)).sum();
519 sum as f32 / self.qp_values.len() as f32
520 }
521
522 #[must_use]
524 pub fn qp_variance(&self) -> f32 {
525 if self.qp_values.is_empty() {
526 return 0.0;
527 }
528
529 let avg = self.average_qp();
530 let variance: f32 = self
531 .qp_values
532 .iter()
533 .map(|&qp| {
534 let diff = qp as f32 - avg;
535 diff * diff
536 })
537 .sum::<f32>()
538 / self.qp_values.len() as f32;
539
540 variance
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547
548 #[test]
549 fn test_qp_selector_creation() {
550 let selector = QpSelector::new(28.0, 1, 63);
551 assert_eq!(selector.base_qp, 28.0);
552 assert_eq!(selector.min_qp, 1);
553 assert_eq!(selector.max_qp, 63);
554 }
555
556 #[test]
557 fn test_constant_qp() {
558 let mut selector = QpSelector::new(28.0, 1, 63);
559 selector.set_strategy(QpStrategy::Constant);
560
561 let result = selector.select_frame_qp(FrameType::Inter, 1.0, 100_000, 0);
562 assert_eq!(result.qp, 28);
563
564 let i_result = selector.select_frame_qp(FrameType::Key, 1.0, 300_000, 0);
565 assert_eq!(i_result.qp, 26); }
567
568 #[test]
569 fn test_frame_type_based_qp() {
570 let mut selector = QpSelector::new(28.0, 1, 63);
571 selector.set_strategy(QpStrategy::FrameTypeBased);
572
573 let i_result = selector.select_frame_qp(FrameType::Key, 1.0, 300_000, 0);
574 let p_result = selector.select_frame_qp(FrameType::Inter, 1.0, 100_000, 1);
575 let b_result = selector.select_frame_qp(FrameType::BiDir, 1.0, 50_000, 2);
576
577 assert!(i_result.qp < p_result.qp); assert!(p_result.qp < b_result.qp); }
580
581 #[test]
582 fn test_complexity_adaptive_qp() {
583 let mut selector = QpSelector::new(28.0, 1, 63);
584 selector.set_strategy(QpStrategy::ComplexityAdaptive);
585
586 let low_complexity = selector.select_frame_qp(FrameType::Inter, 0.5, 100_000, 0);
587 let high_complexity = selector.select_frame_qp(FrameType::Inter, 2.0, 100_000, 1);
588
589 assert!(high_complexity.qp > low_complexity.qp);
591 }
592
593 #[test]
594 fn test_hierarchical_qp() {
595 let mut selector = QpSelector::new(28.0, 1, 63);
596 selector.set_strategy(QpStrategy::Hierarchical);
597
598 let level0_b = selector.select_frame_qp(FrameType::BiDir, 1.0, 50_000, 2);
599 let level1_b = selector.select_frame_qp(FrameType::BiDir, 1.0, 50_000, 4);
600
601 assert!(level1_b.qp >= level0_b.qp);
603 }
604
605 #[test]
606 fn test_qp_clamping() {
607 let mut selector = QpSelector::new(28.0, 10, 40);
608
609 selector.set_base_qp(5.0);
611 assert_eq!(selector.base_qp, 10.0); selector.set_base_qp(50.0);
615 assert_eq!(selector.base_qp, 40.0); }
617
618 #[test]
619 fn test_lambda_calculation() {
620 let lambda1 = QpSelector::qp_to_lambda(28.0);
621 let lambda2 = QpSelector::qp_to_lambda(34.0);
622
623 assert!(lambda1 > 0.0);
624 assert!(lambda2 > lambda1); }
626
627 #[test]
628 fn test_qp_adaptation() {
629 let mut selector = QpSelector::new(28.0, 1, 63);
630
631 for _ in 0..10 {
633 selector.update(28.0, 120_000, 100_000);
634 }
635
636 assert!(selector.base_qp > 28.0);
638 }
639
640 #[test]
641 fn test_block_qp_map() {
642 let mut map = BlockQpMap::new(10, 10, 28);
643
644 assert_eq!(map.get_block_qp(5, 5), 28);
645
646 map.set_block_qp(5, 5, 30);
647 assert_eq!(map.get_block_qp(5, 5), 30);
648
649 let avg = map.average_qp();
650 assert!(avg > 28.0); }
652
653 #[test]
654 fn test_block_qp_offsets() {
655 let mut map = BlockQpMap::new(4, 4, 28);
656 let offsets = vec![
657 0.0, 1.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
658 ];
659
660 map.apply_offsets(&offsets);
661
662 assert_eq!(map.get_block_qp(0, 0), 28);
663 assert_eq!(map.get_block_qp(1, 0), 29);
664 assert_eq!(map.get_block_qp(2, 0), 27);
665 assert_eq!(map.get_block_qp(3, 0), 30);
666 }
667
668 #[test]
669 fn test_qp_variance() {
670 let mut map = BlockQpMap::new(3, 3, 28);
671
672 let variance1 = map.qp_variance();
674 assert!(variance1 < 0.01);
675
676 map.set_block_qp(0, 0, 20);
678 map.set_block_qp(1, 1, 36);
679 let variance2 = map.qp_variance();
680 assert!(variance2 > 1.0);
681 }
682
683 #[test]
684 fn test_qp_result() {
685 let result = QpResult {
686 qp: 28,
687 qp_f: 28.0,
688 lambda: 10.0,
689 lambda_me: 3.16,
690 block_qp_offsets: Some(vec![0.0, 1.0, -1.0]),
691 frame_type: FrameType::Inter,
692 complexity_factor: 1.2,
693 };
694
695 assert_eq!(result.get_block_qp(0), 28);
696 assert_eq!(result.get_block_qp(1), 29);
697 assert_eq!(result.get_block_qp(2), 27);
698 assert!(result.is_valid(1, 63));
699 }
700
701 #[test]
702 fn test_reset() {
703 let mut selector = QpSelector::new(28.0, 1, 63);
704
705 let _ = selector.select_frame_qp(FrameType::Inter, 1.0, 100_000, 0);
706 let _ = selector.select_frame_qp(FrameType::Inter, 1.0, 100_000, 1);
707
708 assert!(!selector.historical_qp.is_empty());
709 assert!(selector.frame_count > 0);
710
711 selector.reset();
712
713 assert!(selector.historical_qp.is_empty());
714 assert_eq!(selector.frame_count, 0);
715 }
716}