use crate::media_stream::MediaStreamId;
use crate::peer_connection::RTCPeerConnection;
use crate::rtp_transceiver::rtp_sender::RTCRtpCodecParameters;
use crate::rtp_transceiver::rtp_sender::rtp_encoding_parameters::RTCRtpEncodingParameters;
pub use direction::RTCRtpTransceiverDirection;
use interceptor::{Interceptor, NoopInterceptor};
use log::trace;
use shared::error::Result;
pub(crate) mod direction;
pub(crate) mod fmtp;
pub(crate) mod internal;
pub mod rtp_receiver;
pub mod rtp_sender;
#[allow(clippy::upper_case_acronyms)]
pub type SSRC = u32;
pub type PayloadType = u8;
pub type RtpStreamId = String;
pub type RepairedStreamId = String;
pub type RTCRtpTransceiverId = usize;
impl From<RTCRtpSenderId> for RTCRtpTransceiverId {
fn from(id: RTCRtpSenderId) -> Self {
id.0
}
}
impl From<RTCRtpReceiverId> for RTCRtpTransceiverId {
fn from(id: RTCRtpReceiverId) -> Self {
id.0
}
}
#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct RTCRtpSenderId(pub(crate) RTCRtpTransceiverId);
impl From<RTCRtpTransceiverId> for RTCRtpSenderId {
fn from(id: RTCRtpTransceiverId) -> Self {
Self(id)
}
}
#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct RTCRtpReceiverId(pub(crate) RTCRtpTransceiverId);
impl From<RTCRtpTransceiverId> for RTCRtpReceiverId {
fn from(id: RTCRtpTransceiverId) -> Self {
Self(id)
}
}
#[derive(Default, Clone)]
pub struct RTCRtpTransceiverInit {
pub direction: RTCRtpTransceiverDirection,
pub streams: Vec<MediaStreamId>,
pub send_encodings: Vec<RTCRtpEncodingParameters>,
}
pub struct RTCRtpTransceiver<'a, I = NoopInterceptor>
where
I: Interceptor,
{
pub(crate) id: RTCRtpTransceiverId,
pub(crate) peer_connection: &'a mut RTCPeerConnection<I>,
}
impl<I> RTCRtpTransceiver<'_, I>
where
I: Interceptor,
{
pub fn mid(&self) -> &Option<String> {
self.peer_connection.rtp_transceivers[self.id].mid()
}
pub fn sender(&self) -> Option<RTCRtpSenderId> {
if self.peer_connection.rtp_transceivers[self.id]
.sender()
.is_some()
{
Some(RTCRtpSenderId::from(self.id))
} else {
None
}
}
pub fn receiver(&self) -> Option<RTCRtpReceiverId> {
if self.peer_connection.rtp_transceivers[self.id]
.receiver()
.is_some()
{
Some(RTCRtpReceiverId::from(self.id))
} else {
None
}
}
pub fn direction(&self) -> RTCRtpTransceiverDirection {
self.peer_connection.rtp_transceivers[self.id].direction()
}
pub fn set_direction(&mut self, direction: RTCRtpTransceiverDirection) {
let previous_direction = self.peer_connection.rtp_transceivers[self.id].direction();
self.peer_connection.rtp_transceivers[self.id].set_direction(direction);
let current_direction = self.peer_connection.rtp_transceivers[self.id].direction();
if current_direction != previous_direction {
trace!(
"Changing direction of transceiver from {previous_direction} to {current_direction}"
);
self.peer_connection.on_transceiver_direction_changed();
}
}
pub fn current_direction(&self) -> RTCRtpTransceiverDirection {
self.peer_connection.rtp_transceivers[self.id].current_direction()
}
pub fn stop(&mut self) -> Result<()> {
self.peer_connection.rtp_transceivers[self.id].stop(
&self.peer_connection.media_engine,
&mut self.peer_connection.interceptor,
)
}
pub fn set_codec_preferences(&mut self, codecs: Vec<RTCRtpCodecParameters>) -> Result<()> {
self.peer_connection.rtp_transceivers[self.id]
.set_codec_preferences(codecs, &self.peer_connection.media_engine)
}
}
#[cfg(test)]
mod tests {
use sansio::Protocol;
use crate::peer_connection::configuration::media_engine::MediaEngine;
use crate::peer_connection::event::RTCPeerConnectionEvent;
use crate::peer_connection::{RTCPeerConnection, RTCPeerConnectionBuilder};
use crate::rtp_transceiver::RTCRtpTransceiverDirection;
use crate::rtp_transceiver::rtp_sender::RtpCodecKind;
fn build_pc() -> RTCPeerConnection {
let mut media_engine = MediaEngine::default();
media_engine
.register_default_codecs()
.expect("register default codecs");
RTCPeerConnectionBuilder::new()
.with_media_engine(media_engine)
.build()
.expect("build peer connection")
}
fn count_negotiation_needed(pc: &mut RTCPeerConnection) -> usize {
let mut count = 0;
while let Some(event) = pc.poll_event() {
if matches!(event, RTCPeerConnectionEvent::OnNegotiationNeededEvent) {
count += 1;
}
}
count
}
fn negotiate(offerer: &mut RTCPeerConnection, answerer: &mut RTCPeerConnection) {
let offer = offerer.create_offer(None).expect("create offer");
offerer
.set_local_description(offer.clone())
.expect("offerer set local");
answerer
.set_remote_description(offer)
.expect("answerer set remote");
let answer = answerer.create_answer(None).expect("create answer");
answerer
.set_local_description(answer.clone())
.expect("answerer set local");
offerer
.set_remote_description(answer)
.expect("offerer set remote");
}
#[test]
fn test_set_direction_triggers_negotiation_on_change() {
let mut offerer = build_pc();
let mut answerer = build_pc();
let tid = offerer
.add_transceiver_from_kind(RtpCodecKind::Video, None)
.expect("add offerer transceiver");
answerer
.add_transceiver_from_kind(RtpCodecKind::Video, None)
.expect("add answerer transceiver");
negotiate(&mut offerer, &mut answerer);
let _ = count_negotiation_needed(&mut offerer);
assert_eq!(
offerer.rtp_transceivers[tid].direction(),
RTCRtpTransceiverDirection::Recvonly,
);
offerer
.rtp_transceiver(tid)
.expect("transceiver exists")
.set_direction(RTCRtpTransceiverDirection::Inactive);
assert_eq!(
count_negotiation_needed(&mut offerer),
1,
"changing the transceiver direction must trigger negotiation-needed",
);
}
#[test]
fn test_set_direction_noop_when_unchanged() {
let mut offerer = build_pc();
let mut answerer = build_pc();
let tid = offerer
.add_transceiver_from_kind(RtpCodecKind::Video, None)
.expect("add offerer transceiver");
answerer
.add_transceiver_from_kind(RtpCodecKind::Video, None)
.expect("add answerer transceiver");
negotiate(&mut offerer, &mut answerer);
let _ = count_negotiation_needed(&mut offerer);
let current = offerer.rtp_transceivers[tid].direction();
offerer
.rtp_transceiver(tid)
.expect("transceiver exists")
.set_direction(current);
assert_eq!(
count_negotiation_needed(&mut offerer),
0,
"setting the same direction must not trigger negotiation-needed",
);
}
}