use super::stream_supports_pli;
use crate::stream_info::StreamInfo;
use crate::{Interceptor, Packet, TaggedPacket, interceptor};
use shared::TransportContext;
use shared::error::Error;
use std::collections::{BTreeSet, VecDeque};
use std::marker::PhantomData;
use std::time::{Duration, Instant};
pub const DEFAULT_INTERVAL: Duration = Duration::from_secs(3);
pub struct IntervalPliBuilder<P> {
interval: Duration,
_phantom: PhantomData<P>,
}
impl<P> Default for IntervalPliBuilder<P> {
fn default() -> Self {
Self {
interval: DEFAULT_INTERVAL,
_phantom: PhantomData,
}
}
}
impl<P> IntervalPliBuilder<P> {
pub fn new() -> Self {
Self::default()
}
pub fn with_interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
pub fn build(self) -> impl FnOnce(P) -> IntervalPliInterceptor<P> {
move |inner| IntervalPliInterceptor::new(inner, self.interval)
}
}
#[derive(Interceptor)]
pub struct IntervalPliInterceptor<P> {
#[next]
inner: P,
interval: Duration,
streams: BTreeSet<u32>,
pending_immediate: BTreeSet<u32>,
next_timeout: Option<Instant>,
write_queue: VecDeque<TaggedPacket>,
}
impl<P> IntervalPliInterceptor<P> {
fn new(inner: P, interval: Duration) -> Self {
Self {
inner,
interval,
streams: BTreeSet::new(),
pending_immediate: BTreeSet::new(),
next_timeout: None,
write_queue: VecDeque::new(),
}
}
pub fn force_pli(&mut self, now: Instant) {
let ssrcs: Vec<u32> = self.streams.iter().copied().collect();
self.queue_plis(now, &ssrcs);
}
pub fn force_pli_for(&mut self, now: Instant, ssrcs: &[u32]) {
let bound: Vec<u32> = ssrcs
.iter()
.copied()
.filter(|ssrc| self.streams.contains(ssrc))
.collect();
self.queue_plis(now, &bound);
}
pub fn bound_streams(&self) -> impl Iterator<Item = u32> + '_ {
self.streams.iter().copied()
}
fn queue_plis(&mut self, now: Instant, ssrcs: &[u32]) {
if ssrcs.is_empty() {
return;
}
let plis: Vec<Box<dyn rtcp::Packet>> = ssrcs
.iter()
.map(|&ssrc| {
Box::new(
rtcp::payload_feedbacks::picture_loss_indication::PictureLossIndication {
sender_ssrc: 0,
media_ssrc: ssrc,
},
) as Box<dyn rtcp::Packet>
})
.collect();
self.write_queue.push_back(TaggedPacket {
now,
transport: TransportContext::default(),
message: Packet::Rtcp(plis),
});
}
fn observe(&mut self, now: Instant) {
if !self.pending_immediate.is_empty() {
let ssrcs: Vec<u32> = self.pending_immediate.iter().copied().collect();
self.pending_immediate.clear();
self.queue_plis(now, &ssrcs);
}
if self.next_timeout.is_none() && self.is_periodic() && !self.streams.is_empty() {
self.next_timeout = Some(now + self.interval);
}
}
fn is_periodic(&self) -> bool {
!self.interval.is_zero()
}
}
#[interceptor]
impl<P: Interceptor> IntervalPliInterceptor<P> {
#[overrides]
fn bind_remote_stream(&mut self, info: &StreamInfo) {
if stream_supports_pli(info) {
self.streams.insert(info.ssrc);
self.pending_immediate.insert(info.ssrc);
}
self.inner.bind_remote_stream(info);
}
#[overrides]
fn unbind_remote_stream(&mut self, info: &StreamInfo) {
self.streams.remove(&info.ssrc);
self.pending_immediate.remove(&info.ssrc);
if self.streams.is_empty() {
self.next_timeout = None;
}
self.inner.unbind_remote_stream(info);
}
#[overrides]
fn handle_read(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
self.observe(msg.now);
self.inner.handle_read(msg)
}
#[overrides]
fn handle_timeout(&mut self, now: Self::Time) -> Result<(), Self::Error> {
self.observe(now);
if let Some(next_timeout) = self.next_timeout
&& now >= next_timeout
{
self.next_timeout = Some(now + self.interval);
let ssrcs: Vec<u32> = self.streams.iter().copied().collect();
self.queue_plis(now, &ssrcs);
}
self.inner.handle_timeout(now)
}
#[overrides]
fn poll_timeout(&mut self) -> Option<Self::Time> {
match (self.next_timeout, self.inner.poll_timeout()) {
(Some(mine), Some(theirs)) => Some(mine.min(theirs)),
(mine, theirs) => mine.or(theirs),
}
}
#[overrides]
fn poll_write(&mut self) -> Option<Self::Wout> {
if let Some(packet) = self.write_queue.pop_front() {
return Some(packet);
}
self.inner.poll_write()
}
}