pub mod packets;
use crate::{Result, RtpSsrc};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FeedbackPacketType {
GenericNack = 205,
PayloadSpecificFeedback = 206,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PayloadFeedbackFormat {
PictureLossIndication = 1,
SliceLossIndication = 2,
ReferencePictureSelectionIndication = 3,
FullIntraRequest = 4,
TemporalSpatialTradeoff = 5,
TemporalSpatialTradeoffNotification = 6,
VideoBackChannelMessage = 7,
ApplicationLayerFeedback = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TransportCcFormat {
TransportCcFeedback = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum FeedbackPriority {
Low = 0,
Normal = 1,
High = 2,
Critical = 3,
}
#[derive(Debug, Clone)]
pub struct FeedbackContext {
pub local_ssrc: RtpSsrc,
pub media_ssrc: RtpSsrc,
pub last_feedback: Option<Instant>,
pub feedback_count: u32,
pub congestion_state: CongestionState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CongestionState {
None,
Light,
Moderate,
Severe,
Critical,
}
#[derive(Debug, Clone)]
pub struct FeedbackConfig {
pub enable_pli: bool,
pub enable_fir: bool,
pub enable_sli: bool,
pub enable_remb: bool,
pub enable_transport_cc: bool,
pub pli_interval_ms: u32,
pub fir_interval_ms: u32,
pub max_feedback_rate: u32,
pub congestion_sensitivity: f32,
}
impl Default for FeedbackConfig {
fn default() -> Self {
Self {
enable_pli: true,
enable_fir: true,
enable_sli: false, enable_remb: true,
enable_transport_cc: true,
pli_interval_ms: 500, fir_interval_ms: 2000, max_feedback_rate: 10, congestion_sensitivity: 0.7, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QualityDegradation {
PacketLoss {
rate: u8,
consecutive: u16,
},
HighJitter {
jitter: u32,
threshold_exceeded: bool,
},
BandwidthLimited {
available_bps: u32,
required_bps: u32,
},
FrameCorruption {
count: u16,
corruption_type: u8,
},
}
#[derive(Debug, Clone)]
pub enum FeedbackDecision {
None,
Pli {
priority: FeedbackPriority,
reason: QualityDegradation,
},
Fir {
priority: FeedbackPriority,
sequence_number: u8,
},
Remb { bitrate_bps: u32, confidence: f32 },
Multiple(Vec<FeedbackDecision>),
}
impl FeedbackContext {
pub fn new(local_ssrc: RtpSsrc, media_ssrc: RtpSsrc) -> Self {
Self {
local_ssrc,
media_ssrc,
last_feedback: None,
feedback_count: 0,
congestion_state: CongestionState::None,
}
}
pub fn update_congestion_state(&mut self, loss_rate: f32, rtt_ms: u32, jitter: u32) {
let congestion_score = self.calculate_congestion_score(loss_rate, rtt_ms, jitter);
self.congestion_state = match congestion_score {
score if score < 0.1 => CongestionState::None,
score if score < 0.3 => CongestionState::Light,
score if score < 0.6 => CongestionState::Moderate,
score if score < 0.8 => CongestionState::Severe,
_ => CongestionState::Critical,
};
}
fn calculate_congestion_score(&self, loss_rate: f32, rtt_ms: u32, jitter: u32) -> f32 {
let loss_weight = 0.5;
let rtt_weight = 0.3;
let jitter_weight = 0.2;
let normalized_loss = (loss_rate * 100.0).min(20.0) / 20.0; let normalized_rtt = (rtt_ms as f32).min(1000.0) / 1000.0; let normalized_jitter = (jitter as f32).min(100.0) / 100.0;
(normalized_loss * loss_weight
+ normalized_rtt * rtt_weight
+ normalized_jitter * jitter_weight)
.min(1.0)
}
pub fn record_feedback(&mut self) {
self.last_feedback = Some(Instant::now());
self.feedback_count += 1;
}
pub fn can_send_feedback(&self, interval_ms: u32) -> bool {
match self.last_feedback {
None => true,
Some(last) => last.elapsed().as_millis() >= interval_ms as u128,
}
}
}
pub trait FeedbackGenerator {
fn generate_feedback(
&self,
context: &FeedbackContext,
config: &FeedbackConfig,
) -> Result<FeedbackDecision>;
fn update_statistics(&mut self, stats: &crate::api::common::stats::StreamStats);
fn name(&self) -> &'static str;
}
pub struct FeedbackGeneratorFactory;
impl FeedbackGeneratorFactory {
pub fn create_loss_generator() -> Box<dyn FeedbackGenerator> {
panic!("Feedback generators moved to media-core. Use media_core::rtp_processing::rtcp::LossFeedbackGenerator")
}
pub fn create_congestion_generator() -> Box<dyn FeedbackGenerator> {
panic!("Feedback generators moved to media-core. Use media_core::rtp_processing::rtcp::CongestionFeedbackGenerator")
}
pub fn create_quality_generator() -> Box<dyn FeedbackGenerator> {
panic!("Feedback generators moved to media-core. Use media_core::rtp_processing::rtcp::QualityFeedbackGenerator")
}
pub fn create_comprehensive_generator() -> Box<dyn FeedbackGenerator> {
panic!("Feedback generators moved to media-core. Use media_core::rtp_processing::rtcp::ComprehensiveFeedbackGenerator")
}
}