use crate::Interceptor;
use crate::{Attribute, Packet, StreamInfo, TaggedPacket};
use sansio::Protocol;
use shared::error::Error;
use std::collections::VecDeque;
use std::time::Instant;
#[derive(Default)]
pub struct NoopInterceptor {
read_queue: VecDeque<TaggedPacket>,
write_queue: VecDeque<TaggedPacket>,
}
impl NoopInterceptor {
pub fn new() -> Self {
Self::default()
}
}
impl Protocol<TaggedPacket, TaggedPacket, ()> for NoopInterceptor {
type Rout = TaggedPacket;
type Wout = TaggedPacket;
type Eout = ();
type Error = Error;
type Time = Instant;
fn handle_read(&mut self, mut msg: TaggedPacket) -> Result<(), Self::Error> {
if matches!(msg.message.packet, Packet::Rtp(_)) {
self.read_queue.push_back(msg);
return Ok(());
}
if msg.message.has(&Attribute::DeliverToApplication) {
self.read_queue.push_back(msg);
} else if !msg.message.attributes.is_empty() {
msg.message.packet = Packet::Rtcp(Vec::new());
self.read_queue.push_back(msg);
}
Ok(())
}
fn poll_read(&mut self) -> Option<Self::Rout> {
self.read_queue.pop_front()
}
fn handle_write(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
self.write_queue.push_back(msg);
Ok(())
}
fn poll_write(&mut self) -> Option<Self::Wout> {
self.write_queue.pop_front()
}
}
impl Interceptor for NoopInterceptor {
fn bind_local_stream(&mut self, _info: &StreamInfo) {}
fn unbind_local_stream(&mut self, _info: &StreamInfo) {}
fn bind_remote_stream(&mut self, _info: &StreamInfo) {}
fn unbind_remote_stream(&mut self, _info: &StreamInfo) {}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AttributedPacket, Registry, Slot, StreamInfo};
use sansio::Protocol;
use shared::TransportContext;
use shared::error::Error;
use std::collections::VecDeque;
use std::time::Instant;
fn packet(message: Packet) -> TaggedPacket {
TaggedPacket {
now: Instant::now(),
transport: TransportContext::default(),
message: AttributedPacket::new(message),
}
}
#[test]
fn inbound_rtcp_does_not_reach_the_application() {
let mut chain = Registry::new().build();
chain.handle_read(packet(Packet::Rtcp(vec![]))).unwrap();
assert!(chain.poll_read().is_none());
}
#[test]
fn inbound_rtp_passes_through() {
let mut chain = Registry::new().build();
chain
.handle_read(packet(Packet::Rtp(rtp::Packet::default())))
.unwrap();
assert!(chain.poll_read().is_some());
}
#[test]
fn outbound_rtcp_is_not_affected() {
let mut chain = Registry::new().build();
chain.handle_write(packet(Packet::Rtcp(vec![]))).unwrap();
assert!(chain.poll_write().is_some());
}
#[test]
fn stages_before_it_still_see_inbound_rtcp() {
#[derive(Default)]
struct Counter {
seen: std::sync::Arc<std::sync::atomic::AtomicUsize>,
read_queue: VecDeque<TaggedPacket>,
write_queue: VecDeque<TaggedPacket>,
}
impl Protocol<TaggedPacket, TaggedPacket, ()> for Counter {
type Rout = TaggedPacket;
type Wout = TaggedPacket;
type Eout = ();
type Error = Error;
type Time = Instant;
fn handle_read(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
if matches!(msg.message.packet, Packet::Rtcp(_)) {
self.seen.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
self.read_queue.push_back(msg);
Ok(())
}
fn poll_read(&mut self) -> Option<Self::Rout> {
self.read_queue.pop_front()
}
fn handle_write(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
self.write_queue.push_back(msg);
Ok(())
}
fn poll_write(&mut self) -> Option<Self::Wout> {
self.write_queue.pop_front()
}
fn handle_timeout(&mut self, _now: Instant) -> Result<(), Self::Error> {
Ok(())
}
fn poll_timeout(&mut self) -> Option<Self::Time> {
None
}
}
impl Interceptor for Counter {
fn bind_local_stream(&mut self, _info: &StreamInfo) {}
fn unbind_local_stream(&mut self, _info: &StreamInfo) {}
fn bind_remote_stream(&mut self, _info: &StreamInfo) {}
fn unbind_remote_stream(&mut self, _info: &StreamInfo) {}
}
let counter = Counter::default();
let seen = counter.seen.clone();
let mut chain = Registry::new().with(Slot::NackGenerator, counter).build();
chain.handle_read(packet(Packet::Rtcp(vec![]))).unwrap();
assert_eq!(1, seen.load(std::sync::atomic::Ordering::Relaxed));
assert!(chain.poll_read().is_none(), "but it stops at the terminus");
}
}
#[cfg(test)]
mod carrier_tests {
use super::*;
use crate::{Attribute, AttributedPacket, Registry};
use sansio::Protocol;
use shared::TransportContext;
use std::time::Instant;
fn annotated(attribute: Option<Attribute>) -> TaggedPacket {
let mut message = AttributedPacket::new(Packet::Rtcp(vec![Box::new(
rtcp::receiver_report::ReceiverReport::default(),
)]));
if let Some(attribute) = attribute {
message.add(attribute);
}
TaggedPacket {
now: Instant::now(),
transport: TransportContext::default(),
message,
}
}
#[test]
fn an_annotated_report_passes_on_as_an_empty_carrier() {
let mut chain = Registry::new().build();
chain
.handle_read(annotated(Some(Attribute::TargetBitrateChanged {
bits_per_second: 750_000.0,
})))
.unwrap();
let carrier = chain.poll_read().expect("the attributes must get through");
assert!(
matches!(&carrier.message.packet, Packet::Rtcp(packets) if packets.is_empty()),
"the payload must be stripped: the application did not ask for RTCP"
);
assert!(
carrier.message.has(&Attribute::TargetBitrateChanged {
bits_per_second: 0.0
}),
"but the attribute must survive"
);
}
#[test]
fn an_unannotated_report_still_stops_here() {
let mut chain = Registry::new().build();
chain.handle_read(annotated(None)).unwrap();
assert!(chain.poll_read().is_none());
}
#[test]
fn deliver_to_application_keeps_the_payload() {
let mut chain = Registry::new().build();
chain
.handle_read(annotated(Some(Attribute::DeliverToApplication)))
.unwrap();
let delivered = chain.poll_read().expect("forwarded");
assert!(
matches!(&delivered.message.packet, Packet::Rtcp(packets) if !packets.is_empty()),
"this packet was judged worth delivering, payload and all"
);
}
}