make jingle more generic over codecs
This adds a very simple Codec struct, which describes each codec supported by a RTP description. It will make it much easier to add newer codecs in the future, like AV1. This commit is best viewed with git log -p -w to ignore indent changes.
This commit is contained in:
parent
5db78b11bf
commit
7fa31bd796
|
@ -41,18 +41,82 @@ const RTP_HDREXT_TRANSPORT_CC: &str =
|
||||||
const DEFAULT_STUN_PORT: u16 = 3478;
|
const DEFAULT_STUN_PORT: u16 = 3478;
|
||||||
const DEFAULT_TURNS_PORT: u16 = 5349;
|
const DEFAULT_TURNS_PORT: u16 = 5349;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
enum CodecName {
|
||||||
|
Opus,
|
||||||
|
H264,
|
||||||
|
Vp8,
|
||||||
|
Vp9,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Codec {
|
||||||
|
name: CodecName,
|
||||||
|
pt: u8,
|
||||||
|
rtx_pt: Option<u8>,
|
||||||
|
rtcp_fbs: Vec<RtcpFb>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Codec {
|
||||||
|
fn is(&self, pt: u8) -> bool {
|
||||||
|
self.pt == pt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_rtx(&self, rtx_pt: u8) -> bool {
|
||||||
|
if let Some(pt) = self.rtx_pt {
|
||||||
|
pt == rtx_pt
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_audio(&self) -> bool {
|
||||||
|
self.name == CodecName::Opus
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_video(&self) -> bool {
|
||||||
|
self.name != CodecName::Opus
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_codec(&self, name: &str) -> bool {
|
||||||
|
match name {
|
||||||
|
"h264" => self.name == CodecName::H264,
|
||||||
|
"vp8" => self.name == CodecName::Vp8,
|
||||||
|
"vp9" => self.name == CodecName::Vp9,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoding_name(&self) -> &'static str {
|
||||||
|
match self.name {
|
||||||
|
CodecName::Opus => "opus",
|
||||||
|
CodecName::H264 => "H264",
|
||||||
|
CodecName::Vp8 => "VP8",
|
||||||
|
CodecName::Vp9 => "VP9",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_depay_name(&self) -> &'static str {
|
||||||
|
match self.name {
|
||||||
|
CodecName::Opus => "rtpopusdepay",
|
||||||
|
CodecName::H264 => "rtph264depay",
|
||||||
|
CodecName::Vp8 => "rtpvp8depay",
|
||||||
|
CodecName::Vp9 => "rtpvp9depay",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_pay_name(&self) -> &'static str {
|
||||||
|
match self.name {
|
||||||
|
CodecName::Opus => "rtpopuspay",
|
||||||
|
CodecName::H264 => "rtph264pay",
|
||||||
|
CodecName::Vp8 => "rtpvp8pay",
|
||||||
|
CodecName::Vp9 => "rtpvp9pay",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ParsedRtpDescription {
|
struct ParsedRtpDescription {
|
||||||
opus_payload_type: Option<u8>,
|
codecs: Vec<Codec>,
|
||||||
opus_rtcp_fbs: Option<Vec<RtcpFb>>,
|
|
||||||
h264_payload_type: Option<u8>,
|
|
||||||
h264_rtx_payload_type: Option<u8>,
|
|
||||||
h264_rtcp_fbs: Option<Vec<RtcpFb>>,
|
|
||||||
vp8_payload_type: Option<u8>,
|
|
||||||
vp8_rtx_payload_type: Option<u8>,
|
|
||||||
vp8_rtcp_fbs: Option<Vec<RtcpFb>>,
|
|
||||||
vp9_payload_type: Option<u8>,
|
|
||||||
vp9_rtx_payload_type: Option<u8>,
|
|
||||||
vp9_rtcp_fbs: Option<Vec<RtcpFb>>,
|
|
||||||
audio_hdrext_ssrc_audio_level: Option<u8>,
|
audio_hdrext_ssrc_audio_level: Option<u8>,
|
||||||
audio_hdrext_transport_cc: Option<u8>,
|
audio_hdrext_transport_cc: Option<u8>,
|
||||||
video_hdrext_abs_send_time: Option<u8>,
|
video_hdrext_abs_send_time: Option<u8>,
|
||||||
|
@ -111,17 +175,10 @@ impl JingleSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_rtp_description(description: &RtpDescription, remote_ssrc_map: &mut HashMap<u32, Source>) -> Result<Option<ParsedRtpDescription>> {
|
fn parse_rtp_description(description: &RtpDescription, remote_ssrc_map: &mut HashMap<u32, Source>) -> Result<Option<ParsedRtpDescription>> {
|
||||||
let mut opus_payload_type = None;
|
let mut opus = None;
|
||||||
let mut opus_rtcp_fbs = None;
|
let mut h264 = None;
|
||||||
let mut h264_payload_type = None;
|
let mut vp8 = None;
|
||||||
let mut h264_rtx_payload_type = None;
|
let mut vp9 = None;
|
||||||
let mut h264_rtcp_fbs = None;
|
|
||||||
let mut vp8_payload_type = None;
|
|
||||||
let mut vp8_rtx_payload_type = None;
|
|
||||||
let mut vp8_rtcp_fbs = None;
|
|
||||||
let mut vp9_payload_type = None;
|
|
||||||
let mut vp9_rtx_payload_type = None;
|
|
||||||
let mut vp9_rtcp_fbs = None;
|
|
||||||
let mut audio_hdrext_ssrc_audio_level = None;
|
let mut audio_hdrext_ssrc_audio_level = None;
|
||||||
let mut audio_hdrext_transport_cc = None;
|
let mut audio_hdrext_transport_cc = None;
|
||||||
let mut video_hdrext_abs_send_time = None;
|
let mut video_hdrext_abs_send_time = None;
|
||||||
|
@ -133,8 +190,12 @@ impl JingleSession {
|
||||||
if let Some(name) = &pt.name {
|
if let Some(name) = &pt.name {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"opus" => {
|
"opus" => {
|
||||||
opus_payload_type = Some(pt.id);
|
opus = Some(Codec {
|
||||||
opus_rtcp_fbs = Some(pt.rtcp_fbs.clone());
|
name: CodecName::Opus,
|
||||||
|
pt: pt.id,
|
||||||
|
rtx_pt: None,
|
||||||
|
rtcp_fbs: pt.rtcp_fbs.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -157,16 +218,28 @@ impl JingleSession {
|
||||||
if let Some(name) = &pt.name {
|
if let Some(name) = &pt.name {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"H264" => {
|
"H264" => {
|
||||||
h264_payload_type = Some(pt.id);
|
h264 = Some(Codec {
|
||||||
h264_rtcp_fbs = Some(pt.rtcp_fbs.clone());
|
name: CodecName::H264,
|
||||||
|
pt: pt.id,
|
||||||
|
rtx_pt: None,
|
||||||
|
rtcp_fbs: pt.rtcp_fbs.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"VP8" => {
|
"VP8" => {
|
||||||
vp8_payload_type = Some(pt.id);
|
vp8 = Some(Codec {
|
||||||
vp8_rtcp_fbs = Some(pt.rtcp_fbs.clone());
|
name: CodecName::Vp8,
|
||||||
|
pt: pt.id,
|
||||||
|
rtx_pt: None,
|
||||||
|
rtcp_fbs: pt.rtcp_fbs.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
"VP9" => {
|
"VP9" => {
|
||||||
vp9_payload_type = Some(pt.id);
|
vp9 = Some(Codec {
|
||||||
vp9_rtcp_fbs = Some(pt.rtcp_fbs.clone());
|
name: CodecName::Vp9,
|
||||||
|
pt: pt.id,
|
||||||
|
rtx_pt: None,
|
||||||
|
rtcp_fbs: pt.rtcp_fbs.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -177,18 +250,21 @@ impl JingleSession {
|
||||||
if name == "rtx" {
|
if name == "rtx" {
|
||||||
for param in pt.parameters.iter() {
|
for param in pt.parameters.iter() {
|
||||||
if param.name == "apt" {
|
if param.name == "apt" {
|
||||||
let pt: u8 = param.value.parse()?;
|
let apt_pt: u8 = param.value.parse()?;
|
||||||
if Some(pt) == h264_payload_type {
|
if let Some(h264) = &mut h264 {
|
||||||
h264_rtx_payload_type = Some(pt);
|
if apt_pt == h264.pt {
|
||||||
|
h264.rtx_pt = Some(pt.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if Some(pt) == h264_payload_type {
|
if let Some(vp8) = &mut vp8 {
|
||||||
h264_rtx_payload_type = Some(pt);
|
if apt_pt == vp8.pt {
|
||||||
|
vp8.rtx_pt = Some(pt.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if Some(pt) == h264_payload_type {
|
if let Some(vp9) = &mut vp9 {
|
||||||
h264_rtx_payload_type = Some(pt);
|
if apt_pt == vp9.pt {
|
||||||
}
|
vp9.rtx_pt = Some(pt.id);
|
||||||
else {
|
}
|
||||||
bail!("unknown rtx apt: {}", pt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,6 +287,8 @@ impl JingleSession {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let codecs = [opus, h264, vp8, vp9].iter().flatten().cloned().collect();
|
||||||
|
|
||||||
for ssrc in &description.ssrcs {
|
for ssrc in &description.ssrcs {
|
||||||
let owner = ssrc
|
let owner = ssrc
|
||||||
.info
|
.info
|
||||||
|
@ -246,17 +324,7 @@ impl JingleSession {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(Some(ParsedRtpDescription {
|
Ok(Some(ParsedRtpDescription {
|
||||||
opus_payload_type,
|
codecs,
|
||||||
opus_rtcp_fbs,
|
|
||||||
h264_payload_type,
|
|
||||||
h264_rtx_payload_type,
|
|
||||||
h264_rtcp_fbs,
|
|
||||||
vp8_payload_type,
|
|
||||||
vp8_rtx_payload_type,
|
|
||||||
vp8_rtcp_fbs,
|
|
||||||
vp9_payload_type,
|
|
||||||
vp9_rtx_payload_type,
|
|
||||||
vp9_rtcp_fbs,
|
|
||||||
audio_hdrext_ssrc_audio_level,
|
audio_hdrext_ssrc_audio_level,
|
||||||
audio_hdrext_transport_cc,
|
audio_hdrext_transport_cc,
|
||||||
video_hdrext_abs_send_time,
|
video_hdrext_abs_send_time,
|
||||||
|
@ -396,17 +464,7 @@ impl JingleSession {
|
||||||
debug!("Received Jingle session-initiate from {}", initiator);
|
debug!("Received Jingle session-initiate from {}", initiator);
|
||||||
|
|
||||||
let mut ice_transport = None;
|
let mut ice_transport = None;
|
||||||
let mut opus_payload_type = None;
|
let mut codecs = vec![];
|
||||||
let mut opus_rtcp_fbs = None;
|
|
||||||
let mut h264_payload_type = None;
|
|
||||||
let mut h264_rtx_payload_type = None;
|
|
||||||
let mut h264_rtcp_fbs = None;
|
|
||||||
let mut vp8_payload_type = None;
|
|
||||||
let mut vp8_rtx_payload_type = None;
|
|
||||||
let mut vp8_rtcp_fbs = None;
|
|
||||||
let mut vp9_payload_type = None;
|
|
||||||
let mut vp9_rtx_payload_type = None;
|
|
||||||
let mut vp9_rtcp_fbs = None;
|
|
||||||
let mut audio_hdrext_ssrc_audio_level = None;
|
let mut audio_hdrext_ssrc_audio_level = None;
|
||||||
let mut audio_hdrext_transport_cc = None;
|
let mut audio_hdrext_transport_cc = None;
|
||||||
let mut video_hdrext_abs_send_time = None;
|
let mut video_hdrext_abs_send_time = None;
|
||||||
|
@ -417,17 +475,7 @@ impl JingleSession {
|
||||||
for content in &jingle.contents {
|
for content in &jingle.contents {
|
||||||
if let Some(Description::Rtp(description)) = &content.description {
|
if let Some(Description::Rtp(description)) = &content.description {
|
||||||
if let Some(description) = JingleSession::parse_rtp_description(description, &mut remote_ssrc_map)? {
|
if let Some(description) = JingleSession::parse_rtp_description(description, &mut remote_ssrc_map)? {
|
||||||
opus_payload_type = opus_payload_type.or(description.opus_payload_type);
|
codecs.extend(description.codecs);
|
||||||
opus_rtcp_fbs = opus_rtcp_fbs.or(description.opus_rtcp_fbs);
|
|
||||||
h264_payload_type = h264_payload_type.or(description.h264_payload_type);
|
|
||||||
h264_rtx_payload_type = h264_rtx_payload_type.or(description.h264_rtx_payload_type);
|
|
||||||
h264_rtcp_fbs = h264_rtcp_fbs.or(description.h264_rtcp_fbs);
|
|
||||||
vp8_payload_type = vp8_payload_type.or(description.vp8_payload_type);
|
|
||||||
vp8_rtx_payload_type = vp8_rtx_payload_type.or(description.vp8_rtx_payload_type);
|
|
||||||
vp8_rtcp_fbs = vp8_rtcp_fbs.or(description.vp8_rtcp_fbs);
|
|
||||||
vp9_payload_type = vp9_payload_type.or(description.vp9_payload_type);
|
|
||||||
vp9_rtx_payload_type = vp9_rtx_payload_type.or(description.vp9_rtx_payload_type);
|
|
||||||
vp9_rtcp_fbs = vp9_rtcp_fbs.or(description.vp9_rtcp_fbs);
|
|
||||||
audio_hdrext_ssrc_audio_level = audio_hdrext_ssrc_audio_level.or(description.audio_hdrext_ssrc_audio_level);
|
audio_hdrext_ssrc_audio_level = audio_hdrext_ssrc_audio_level.or(description.audio_hdrext_ssrc_audio_level);
|
||||||
audio_hdrext_transport_cc = audio_hdrext_transport_cc.or(description.audio_hdrext_transport_cc);
|
audio_hdrext_transport_cc = audio_hdrext_transport_cc.or(description.audio_hdrext_transport_cc);
|
||||||
video_hdrext_abs_send_time = video_hdrext_abs_send_time.or(description.video_hdrext_abs_send_time);
|
video_hdrext_abs_send_time = video_hdrext_abs_send_time.or(description.video_hdrext_abs_send_time);
|
||||||
|
@ -522,96 +570,68 @@ impl JingleSession {
|
||||||
)?;
|
)?;
|
||||||
pipeline.add(&dtlssrtpdec)?;
|
pipeline.add(&dtlssrtpdec)?;
|
||||||
|
|
||||||
rtpbin.connect("request-pt-map", false, move |values| {
|
{
|
||||||
let f = || {
|
let codecs = codecs.clone();
|
||||||
debug!("rtpbin request-pt-map {:?}", values);
|
rtpbin.connect("request-pt-map", false, move |values| {
|
||||||
let pt = values[2].get::<u32>()? as u8;
|
let f = || {
|
||||||
let mut caps = gstreamer::Caps::builder("application/x-rtp");
|
debug!("rtpbin request-pt-map {:?}", values);
|
||||||
if Some(pt) == opus_payload_type {
|
let pt = values[2].get::<u32>()? as u8;
|
||||||
caps = caps
|
let mut caps = gstreamer::Caps::builder("application/x-rtp");
|
||||||
.field("media", "audio")
|
for codec in codecs.iter() {
|
||||||
.field("encoding-name", "OPUS")
|
if codec.is(pt) {
|
||||||
.field("clock-rate", 48000);
|
if codec.is_audio() {
|
||||||
if let Some(hdrext) = audio_hdrext_ssrc_audio_level {
|
caps = caps
|
||||||
caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL);
|
.field("media", "audio")
|
||||||
}
|
.field("encoding-name", "OPUS")
|
||||||
if let Some(hdrext) = audio_hdrext_transport_cc {
|
.field("clock-rate", 48000);
|
||||||
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
if let Some(hdrext) = audio_hdrext_ssrc_audio_level {
|
||||||
}
|
caps = caps.field(&format!("extmap-{}", hdrext), RTP_HDREXT_SSRC_AUDIO_LEVEL);
|
||||||
Ok::<_, anyhow::Error>(Some(caps.build()))
|
}
|
||||||
}
|
if let Some(hdrext) = audio_hdrext_transport_cc {
|
||||||
else if Some(pt) == h264_payload_type
|
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
||||||
|| Some(pt) == vp8_payload_type
|
}
|
||||||
|| Some(pt) == vp9_payload_type
|
|
||||||
{
|
|
||||||
caps = caps
|
|
||||||
.field("media", "video")
|
|
||||||
.field("clock-rate", 90000)
|
|
||||||
.field(
|
|
||||||
"encoding-name",
|
|
||||||
if Some(pt) == h264_payload_type {
|
|
||||||
"H264"
|
|
||||||
}
|
|
||||||
else if Some(pt) == vp8_payload_type {
|
|
||||||
"VP8"
|
|
||||||
}
|
|
||||||
else if Some(pt) == vp9_payload_type {
|
|
||||||
"VP9"
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
// A video codec, as the only audio codec we support is Opus.
|
||||||
},
|
caps = caps
|
||||||
);
|
.field("media", "video")
|
||||||
// if let Some(hdrext) = video_hdrext_abs_send_time {
|
.field("clock-rate", 90000)
|
||||||
// caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
.field("encoding-name", codec.encoding_name());
|
||||||
// }
|
// if let Some(hdrext) = video_hdrext_abs_send_time {
|
||||||
if let Some(hdrext) = video_hdrext_transport_cc {
|
// caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
||||||
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
// }
|
||||||
|
if let Some(hdrext) = video_hdrext_transport_cc {
|
||||||
|
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok::<_, anyhow::Error>(Some(caps.build()));
|
||||||
|
}
|
||||||
|
else if codec.is_rtx(pt) {
|
||||||
|
caps = caps
|
||||||
|
.field("media", "video")
|
||||||
|
.field("clock-rate", 90000)
|
||||||
|
.field("encoding-name", "RTX")
|
||||||
|
.field("apt", codec.pt);
|
||||||
|
return Ok(Some(caps.build()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(caps.build()))
|
|
||||||
}
|
|
||||||
else if Some(pt) == vp8_rtx_payload_type
|
|
||||||
|| Some(pt) == vp9_rtx_payload_type
|
|
||||||
|| Some(pt) == h264_rtx_payload_type
|
|
||||||
{
|
|
||||||
caps = caps
|
|
||||||
.field("media", "video")
|
|
||||||
.field("clock-rate", 90000)
|
|
||||||
.field("encoding-name", "RTX")
|
|
||||||
.field(
|
|
||||||
"apt",
|
|
||||||
if Some(pt) == vp8_rtx_payload_type {
|
|
||||||
vp8_payload_type.context("missing VP8 payload type")?
|
|
||||||
}
|
|
||||||
else if Some(pt) == vp9_rtx_payload_type {
|
|
||||||
vp9_payload_type.context("missing VP9 payload type")?
|
|
||||||
}
|
|
||||||
else if Some(pt) == h264_rtx_payload_type {
|
|
||||||
h264_payload_type.context("missing H264 payload type")?
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Ok(Some(caps.build()))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
warn!("unknown payload type: {}", pt);
|
warn!("unknown payload type: {}", pt);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
};
|
||||||
|
match f() {
|
||||||
|
Ok(Some(caps)) => {
|
||||||
|
debug!("mapped pt to caps: {:?}", caps);
|
||||||
|
Some(caps.to_value())
|
||||||
|
},
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(e) => {
|
||||||
|
error!("handling request-pt-map: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
})?;
|
||||||
match f() {
|
}
|
||||||
Ok(Some(caps)) => {
|
|
||||||
debug!("mapped pt to caps: {:?}", caps);
|
|
||||||
Some(caps.to_value())
|
|
||||||
},
|
|
||||||
Ok(None) => None,
|
|
||||||
Err(e) => {
|
|
||||||
error!("handling request-pt-map: {:?}", e);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let handle = Handle::current();
|
let handle = Handle::current();
|
||||||
let jingle_session = conference.jingle_session.clone();
|
let jingle_session = conference.jingle_session.clone();
|
||||||
|
@ -653,63 +673,64 @@ impl JingleSession {
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
rtpbin.connect("request-aux-sender", false, move |values| {
|
let pts: Vec<(String, u32)> = codecs.iter()
|
||||||
let f = move || {
|
.filter(|codec| codec.is_video())
|
||||||
let session: u32 = values[1].get()?;
|
.flat_map(|codec| {
|
||||||
debug!("creating RTX sender for session {}", session);
|
if let Some(rtx_pt) = codec.rtx_pt {
|
||||||
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
|
Some((codec.pt.to_string(), rtx_pt as u32))
|
||||||
let mut ssrc_map = gstreamer::Structure::builder("application/x-rtp-ssrc-map");
|
} else {
|
||||||
if let (Some(pt), Some(rtx_pt)) = (vp8_payload_type, vp8_rtx_payload_type) {
|
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
|
||||||
}
|
|
||||||
if let (Some(pt), Some(rtx_pt)) = (vp9_payload_type, vp9_rtx_payload_type) {
|
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
|
||||||
}
|
|
||||||
if let (Some(pt), Some(rtx_pt)) = (h264_payload_type, h264_rtx_payload_type) {
|
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
|
||||||
}
|
|
||||||
ssrc_map = ssrc_map.field(&video_ssrc.to_string(), &(video_rtx_ssrc as u32));
|
|
||||||
let bin = gstreamer::Bin::new(None);
|
|
||||||
let rtx_sender = gstreamer::ElementFactory::make("rtprtxsend", None)?;
|
|
||||||
rtx_sender.set_property("payload-type-map", pt_map.build())?;
|
|
||||||
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
|
|
||||||
bin.add(&rtx_sender)?;
|
|
||||||
bin.add_pad(&gstreamer::GhostPad::with_target(
|
|
||||||
Some(&format!("src_{}", session)),
|
|
||||||
&rtx_sender
|
|
||||||
.static_pad("src")
|
|
||||||
.context("rtprtxsend has no src pad")?,
|
|
||||||
)?)?;
|
|
||||||
bin.add_pad(&gstreamer::GhostPad::with_target(
|
|
||||||
Some(&format!("sink_{}", session)),
|
|
||||||
&rtx_sender
|
|
||||||
.static_pad("sink")
|
|
||||||
.context("rtprtxsend has no sink pad")?,
|
|
||||||
)?)?;
|
|
||||||
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
|
||||||
};
|
|
||||||
match f() {
|
|
||||||
Ok(o) => o,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("request-aux-sender: {:?}", e);
|
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
}
|
})
|
||||||
})?;
|
.collect();
|
||||||
|
{
|
||||||
|
let pts = pts.clone();
|
||||||
|
rtpbin.connect("request-aux-sender", false, move |values| {
|
||||||
|
let f = || {
|
||||||
|
let session: u32 = values[1].get()?;
|
||||||
|
debug!("creating RTX sender for session {}", session);
|
||||||
|
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
|
||||||
|
let mut ssrc_map = gstreamer::Structure::builder("application/x-rtp-ssrc-map");
|
||||||
|
for (pt, rtx_pt) in pts.iter() {
|
||||||
|
pt_map = pt_map.field(pt, rtx_pt);
|
||||||
|
}
|
||||||
|
ssrc_map = ssrc_map.field(&video_ssrc.to_string(), &(video_rtx_ssrc as u32));
|
||||||
|
let bin = gstreamer::Bin::new(None);
|
||||||
|
let rtx_sender = gstreamer::ElementFactory::make("rtprtxsend", None)?;
|
||||||
|
rtx_sender.set_property("payload-type-map", pt_map.build())?;
|
||||||
|
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
|
||||||
|
bin.add(&rtx_sender)?;
|
||||||
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
|
Some(&format!("src_{}", session)),
|
||||||
|
&rtx_sender
|
||||||
|
.static_pad("src")
|
||||||
|
.context("rtprtxsend has no src pad")?,
|
||||||
|
)?)?;
|
||||||
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
|
Some(&format!("sink_{}", session)),
|
||||||
|
&rtx_sender
|
||||||
|
.static_pad("sink")
|
||||||
|
.context("rtprtxsend has no sink pad")?,
|
||||||
|
)?)?;
|
||||||
|
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
||||||
|
};
|
||||||
|
match f() {
|
||||||
|
Ok(o) => o,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("request-aux-sender: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
rtpbin.connect("request-aux-receiver", false, move |values| {
|
rtpbin.connect("request-aux-receiver", false, move |values| {
|
||||||
let f = move || {
|
let f = || {
|
||||||
let session: u32 = values[1].get()?;
|
let session: u32 = values[1].get()?;
|
||||||
debug!("creating RTX receiver for session {}", session);
|
debug!("creating RTX receiver for session {}", session);
|
||||||
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
|
let mut pt_map = gstreamer::Structure::builder("application/x-rtp-pt-map");
|
||||||
if let (Some(pt), Some(rtx_pt)) = (vp8_payload_type, vp8_rtx_payload_type) {
|
for (pt, rtx_pt) in pts.iter() {
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
pt_map = pt_map.field(pt, rtx_pt);
|
||||||
}
|
|
||||||
if let (Some(pt), Some(rtx_pt)) = (vp9_payload_type, vp9_rtx_payload_type) {
|
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
|
||||||
}
|
|
||||||
if let (Some(pt), Some(rtx_pt)) = (h264_payload_type, h264_rtx_payload_type) {
|
|
||||||
pt_map = pt_map.field(&pt.to_string(), &(rtx_pt as u32));
|
|
||||||
}
|
}
|
||||||
let bin = gstreamer::Bin::new(None);
|
let bin = gstreamer::Bin::new(None);
|
||||||
let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?;
|
let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?;
|
||||||
|
@ -743,6 +764,7 @@ impl JingleSession {
|
||||||
let conference = conference.clone();
|
let conference = conference.clone();
|
||||||
let pipeline = pipeline.clone();
|
let pipeline = pipeline.clone();
|
||||||
let rtpbin_ = rtpbin.clone();
|
let rtpbin_ = rtpbin.clone();
|
||||||
|
let codecs = codecs.clone();
|
||||||
rtpbin.connect("pad-added", false, move |values| {
|
rtpbin.connect("pad-added", false, move |values| {
|
||||||
let rtpbin = &rtpbin_;
|
let rtpbin = &rtpbin_;
|
||||||
let f = || {
|
let f = || {
|
||||||
|
@ -772,26 +794,22 @@ impl JingleSession {
|
||||||
|
|
||||||
let source_element = match source.media_type {
|
let source_element = match source.media_type {
|
||||||
MediaType::Audio => {
|
MediaType::Audio => {
|
||||||
if Some(pt) == opus_payload_type {
|
let codec = codecs.iter()
|
||||||
gstreamer::ElementFactory::make("rtpopusdepay", None)?
|
.filter(|codec| codec.is_audio())
|
||||||
|
.find(|codec| codec.is(pt));
|
||||||
|
if let Some(codec) = codec {
|
||||||
|
gstreamer::ElementFactory::make(codec.make_depay_name(), None)?
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bail!("received audio with unsupported PT {}", pt);
|
bail!("received audio with unsupported PT {}", pt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MediaType::Video => {
|
MediaType::Video => {
|
||||||
if Some(pt) == h264_payload_type {
|
let codec = codecs.iter()
|
||||||
let element = gstreamer::ElementFactory::make("rtph264depay", None)?;
|
.filter(|codec| codec.is_video())
|
||||||
element.set_property("request-keyframe", true)?;
|
.find(|codec| codec.is(pt));
|
||||||
element
|
if let Some(codec) = codec {
|
||||||
}
|
let element = gstreamer::ElementFactory::make(codec.make_depay_name(), None)?;
|
||||||
else if Some(pt) == vp8_payload_type {
|
|
||||||
let element = gstreamer::ElementFactory::make("rtpvp8depay", None)?;
|
|
||||||
element.set_property("request-keyframe", true)?;
|
|
||||||
element
|
|
||||||
}
|
|
||||||
else if Some(pt) == vp9_payload_type {
|
|
||||||
let element = gstreamer::ElementFactory::make("rtpvp9depay", None)?;
|
|
||||||
element.set_property("request-keyframe", true)?;
|
element.set_property("request-keyframe", true)?;
|
||||||
element
|
element
|
||||||
}
|
}
|
||||||
|
@ -897,11 +915,14 @@ impl JingleSession {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let audio_sink_element = gstreamer::ElementFactory::make("rtpopuspay", None)?;
|
let opus = codecs.iter().find(|codec| codec.name == CodecName::Opus);
|
||||||
audio_sink_element.set_property(
|
let audio_sink_element = if let Some(opus) = opus {
|
||||||
"pt",
|
let audio_sink_element = gstreamer::ElementFactory::make(opus.make_pay_name(), None)?;
|
||||||
opus_payload_type.context("no opus payload type in jingle session-initiate")? as u32,
|
audio_sink_element.set_property("pt", opus.pt as u32)?;
|
||||||
)?;
|
audio_sink_element
|
||||||
|
} else {
|
||||||
|
bail!("no opus payload type in jingle session-initiate");
|
||||||
|
};
|
||||||
audio_sink_element.set_property("min-ptime", 10i64 * 1000 * 1000)?;
|
audio_sink_element.set_property("min-ptime", 10i64 * 1000 * 1000)?;
|
||||||
audio_sink_element.set_property("ssrc", audio_ssrc)?;
|
audio_sink_element.set_property("ssrc", audio_ssrc)?;
|
||||||
if audio_sink_element.has_property("auto-header-extension", None) {
|
if audio_sink_element.has_property("auto-header-extension", None) {
|
||||||
|
@ -943,35 +964,21 @@ impl JingleSession {
|
||||||
}
|
}
|
||||||
pipeline.add(&audio_sink_element)?;
|
pipeline.add(&audio_sink_element)?;
|
||||||
|
|
||||||
let video_sink_element = match conference.config.video_codec.as_str() {
|
let codec_name = conference.config.video_codec.as_str();
|
||||||
"h264" => {
|
let codec = codecs.iter().find(|codec| codec.is_codec(codec_name));
|
||||||
let element = gstreamer::ElementFactory::make("rtph264pay", None)?;
|
let video_sink_element = if let Some(codec) = codec {
|
||||||
element.set_property(
|
let element = gstreamer::ElementFactory::make(codec.make_pay_name(), None)?;
|
||||||
"pt",
|
element.set_property("pt", codec.pt as u32)?;
|
||||||
h264_payload_type.context("no h264 payload type in jingle session-initiate")? as u32,
|
if codec.name == CodecName::H264 {
|
||||||
)?;
|
|
||||||
element.set_property_from_str("aggregate-mode", "zero-latency");
|
element.set_property_from_str("aggregate-mode", "zero-latency");
|
||||||
element
|
}
|
||||||
},
|
else {
|
||||||
"vp8" => {
|
|
||||||
let element = gstreamer::ElementFactory::make("rtpvp8pay", None)?;
|
|
||||||
element.set_property(
|
|
||||||
"pt",
|
|
||||||
vp8_payload_type.context("no vp8 payload type in jingle session-initiate")? as u32,
|
|
||||||
)?;
|
|
||||||
element.set_property_from_str("picture-id-mode", "15-bit");
|
element.set_property_from_str("picture-id-mode", "15-bit");
|
||||||
element
|
}
|
||||||
},
|
element
|
||||||
"vp9" => {
|
}
|
||||||
let element = gstreamer::ElementFactory::make("rtpvp9pay", None)?;
|
else {
|
||||||
element.set_property(
|
bail!("unsupported video codec: {}", codec_name);
|
||||||
"pt",
|
|
||||||
vp9_payload_type.context("no vp9 payload type in jingle session-initiate")? as u32,
|
|
||||||
)?;
|
|
||||||
element.set_property_from_str("picture-id-mode", "15-bit");
|
|
||||||
element
|
|
||||||
},
|
|
||||||
other => bail!("unsupported video codec: {}", other),
|
|
||||||
};
|
};
|
||||||
video_sink_element.set_property("ssrc", video_ssrc)?;
|
video_sink_element.set_property("ssrc", video_ssrc)?;
|
||||||
if video_sink_element.has_property("auto-header-extension", None) {
|
if video_sink_element.has_property("auto-header-extension", None) {
|
||||||
|
@ -1114,73 +1121,40 @@ impl JingleSession {
|
||||||
let mut description = RtpDescription::new(initiate_content.name.0.clone());
|
let mut description = RtpDescription::new(initiate_content.name.0.clone());
|
||||||
|
|
||||||
description.payload_types = if initiate_content.name.0 == "audio" {
|
description.payload_types = if initiate_content.name.0 == "audio" {
|
||||||
let mut pt = PayloadType::new(
|
let codec = codecs.iter().find(|codec| codec.name == CodecName::Opus);
|
||||||
opus_payload_type.context("no opus payload type in jingle session-initiate")?,
|
if let Some(codec) = codec {
|
||||||
"opus".to_owned(),
|
let mut pt = PayloadType::new(
|
||||||
48000,
|
codec.pt,
|
||||||
2,
|
"opus".to_owned(),
|
||||||
);
|
48000,
|
||||||
pt.rtcp_fbs = opus_rtcp_fbs.clone().unwrap_or_default();
|
2,
|
||||||
vec![pt]
|
);
|
||||||
|
pt.rtcp_fbs = codec.rtcp_fbs.clone();
|
||||||
|
vec![pt]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bail!("no opus payload type in jingle session-initiate");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let mut pts = vec![];
|
let mut pts = vec![];
|
||||||
match conference.config.video_codec.as_str() {
|
let codec_name = conference.config.video_codec.as_str();
|
||||||
"h264" => {
|
let codec = codecs.iter().find(|codec| codec.is_codec(codec_name));
|
||||||
if let Some(h264_pt) = h264_payload_type {
|
if let Some(codec) = codec {
|
||||||
let mut pt = PayloadType::new(h264_pt, "H264".to_owned(), 90000, 1);
|
let mut pt = PayloadType::new(codec.pt, codec.encoding_name().to_owned(), 90000, 1);
|
||||||
pt.rtcp_fbs = h264_rtcp_fbs.clone().unwrap_or_default();
|
pt.rtcp_fbs = codec.rtcp_fbs.clone();
|
||||||
pts.push(pt);
|
pts.push(pt);
|
||||||
if let Some(rtx_pt) = h264_rtx_payload_type {
|
if let Some(rtx_pt) = codec.rtx_pt {
|
||||||
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
|
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
|
||||||
rtx_pt.parameters = vec![jingle_rtp::Parameter {
|
rtx_pt.parameters = vec![jingle_rtp::Parameter {
|
||||||
name: "apt".to_owned(),
|
name: "apt".to_owned(),
|
||||||
value: h264_pt.to_string(),
|
value: codec.pt.to_string(),
|
||||||
}];
|
}];
|
||||||
pts.push(rtx_pt);
|
pts.push(rtx_pt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bail!("no h264 payload type in jingle session-initiate");
|
bail!("unsupported video codec: {}", codec_name);
|
||||||
}
|
|
||||||
},
|
|
||||||
"vp8" => {
|
|
||||||
if let Some(vp8_pt) = vp8_payload_type {
|
|
||||||
let mut pt = PayloadType::new(vp8_pt, "VP8".to_owned(), 90000, 1);
|
|
||||||
pt.rtcp_fbs = vp8_rtcp_fbs.clone().unwrap_or_default();
|
|
||||||
pts.push(pt);
|
|
||||||
if let Some(rtx_pt) = vp8_rtx_payload_type {
|
|
||||||
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
|
|
||||||
rtx_pt.parameters = vec![jingle_rtp::Parameter {
|
|
||||||
name: "apt".to_owned(),
|
|
||||||
value: vp8_pt.to_string(),
|
|
||||||
}];
|
|
||||||
pts.push(rtx_pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bail!("no vp8 payload type in jingle session-initiate");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vp9" => {
|
|
||||||
if let Some(vp9_pt) = vp9_payload_type {
|
|
||||||
let mut pt = PayloadType::new(vp9_pt, "VP9".to_owned(), 90000, 1);
|
|
||||||
pt.rtcp_fbs = vp9_rtcp_fbs.clone().unwrap_or_default();
|
|
||||||
pts.push(pt);
|
|
||||||
if let Some(rtx_pt) = vp9_rtx_payload_type {
|
|
||||||
let mut rtx_pt = PayloadType::new(rtx_pt, "rtx".to_owned(), 90000, 1);
|
|
||||||
rtx_pt.parameters = vec![jingle_rtp::Parameter {
|
|
||||||
name: "apt".to_owned(),
|
|
||||||
value: vp9_pt.to_string(),
|
|
||||||
}];
|
|
||||||
pts.push(rtx_pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bail!("no vp9 payload type in jingle session-initiate");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
other => bail!("unsupported video codec: {}", other),
|
|
||||||
}
|
}
|
||||||
pts
|
pts
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue