fmt
This commit is contained in:
parent
a868e7e781
commit
4575371d6d
|
@ -1,14 +1,20 @@
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString, c_void},
|
ffi::{c_void, CStr, CString},
|
||||||
os::raw::c_char,
|
os::raw::c_char,
|
||||||
ptr,
|
ptr,
|
||||||
sync::{Arc, atomic::{AtomicPtr, Ordering}},
|
sync::{
|
||||||
|
atomic::{AtomicPtr, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use glib::{ffi::GMainContext, translate::{from_glib, from_glib_full, ToGlibPtr}};
|
use glib::{
|
||||||
pub use lib_gst_meet::{init_tracing, JitsiConference, JitsiConnection, MediaType};
|
ffi::GMainContext,
|
||||||
|
translate::{from_glib, from_glib_full, ToGlibPtr},
|
||||||
|
};
|
||||||
use lib_gst_meet::JitsiConferenceConfig;
|
use lib_gst_meet::JitsiConferenceConfig;
|
||||||
|
pub use lib_gst_meet::{init_tracing, JitsiConference, JitsiConnection, MediaType};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -42,7 +48,7 @@ impl<T> ResultExt<T> for Result<T> {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("lib-gst-meet: {:?}", e);
|
eprintln!("lib-gst-meet: {:?}", e);
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +85,10 @@ pub unsafe extern "C" fn gstmeet_connection_new(
|
||||||
let xmpp_domain = CStr::from_ptr(xmpp_domain);
|
let xmpp_domain = CStr::from_ptr(xmpp_domain);
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on(JitsiConnection::new(&websocket_url.to_string_lossy(), &xmpp_domain.to_string_lossy()))
|
.block_on(JitsiConnection::new(
|
||||||
|
&websocket_url.to_string_lossy(),
|
||||||
|
&xmpp_domain.to_string_lossy(),
|
||||||
|
))
|
||||||
.map(|(connection, background)| {
|
.map(|(connection, background)| {
|
||||||
(*context).runtime.spawn(background);
|
(*context).runtime.spawn(background);
|
||||||
connection
|
connection
|
||||||
|
@ -93,7 +102,10 @@ pub unsafe extern "C" fn gstmeet_connection_free(connection: *mut JitsiConnectio
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_connection_connect(context: *mut Context, connection: *mut JitsiConnection) -> bool {
|
pub unsafe extern "C" fn gstmeet_connection_connect(
|
||||||
|
context: *mut Context,
|
||||||
|
connection: *mut JitsiConnection,
|
||||||
|
) -> bool {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on((*connection).connect())
|
.block_on((*connection).connect())
|
||||||
|
@ -126,8 +138,12 @@ pub unsafe extern "C" fn gstmeet_connection_join_conference(
|
||||||
muc,
|
muc,
|
||||||
focus,
|
focus,
|
||||||
nick: CStr::from_ptr((*config).nick).to_string_lossy().to_string(),
|
nick: CStr::from_ptr((*config).nick).to_string_lossy().to_string(),
|
||||||
region: CStr::from_ptr((*config).region).to_string_lossy().to_string(),
|
region: CStr::from_ptr((*config).region)
|
||||||
video_codec: CStr::from_ptr((*config).video_codec).to_string_lossy().to_string(),
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
video_codec: CStr::from_ptr((*config).video_codec)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
};
|
};
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
|
@ -136,7 +152,10 @@ pub unsafe extern "C" fn gstmeet_connection_join_conference(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_conference_leave(context: *mut Context, conference: *mut JitsiConference) -> bool {
|
pub unsafe extern "C" fn gstmeet_conference_leave(
|
||||||
|
context: *mut Context,
|
||||||
|
conference: *mut JitsiConference,
|
||||||
|
) -> bool {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on(Box::from_raw(conference).leave())
|
.block_on(Box::from_raw(conference).leave())
|
||||||
|
@ -145,7 +164,12 @@ pub unsafe extern "C" fn gstmeet_conference_leave(context: *mut Context, confere
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_conference_set_muted(context: *mut Context, conference: *mut JitsiConference, media_type: MediaType, muted: bool) -> bool {
|
pub unsafe extern "C" fn gstmeet_conference_set_muted(
|
||||||
|
context: *mut Context,
|
||||||
|
conference: *mut JitsiConference,
|
||||||
|
media_type: MediaType,
|
||||||
|
muted: bool,
|
||||||
|
) -> bool {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on((*conference).set_muted(media_type, muted))
|
.block_on((*conference).set_muted(media_type, muted))
|
||||||
|
@ -154,7 +178,10 @@ pub unsafe extern "C" fn gstmeet_conference_set_muted(context: *mut Context, con
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_conference_pipeline(context: *mut Context, conference: *mut JitsiConference) -> *mut gstreamer::ffi::GstPipeline {
|
pub unsafe extern "C" fn gstmeet_conference_pipeline(
|
||||||
|
context: *mut Context,
|
||||||
|
conference: *mut JitsiConference,
|
||||||
|
) -> *mut gstreamer::ffi::GstPipeline {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on((*conference).pipeline())
|
.block_on((*conference).pipeline())
|
||||||
|
@ -164,7 +191,10 @@ pub unsafe extern "C" fn gstmeet_conference_pipeline(context: *mut Context, conf
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_conference_audio_sink_element(context: *mut Context, conference: *mut JitsiConference) -> *mut gstreamer::ffi::GstElement {
|
pub unsafe extern "C" fn gstmeet_conference_audio_sink_element(
|
||||||
|
context: *mut Context,
|
||||||
|
conference: *mut JitsiConference,
|
||||||
|
) -> *mut gstreamer::ffi::GstElement {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on((*conference).audio_sink_element())
|
.block_on((*conference).audio_sink_element())
|
||||||
|
@ -174,7 +204,10 @@ pub unsafe extern "C" fn gstmeet_conference_audio_sink_element(context: *mut Con
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn gstmeet_conference_video_sink_element(context: *mut Context, conference: *mut JitsiConference) -> *mut gstreamer::ffi::GstElement {
|
pub unsafe extern "C" fn gstmeet_conference_video_sink_element(
|
||||||
|
context: *mut Context,
|
||||||
|
conference: *mut JitsiConference,
|
||||||
|
) -> *mut gstreamer::ffi::GstElement {
|
||||||
(*context)
|
(*context)
|
||||||
.runtime
|
.runtime
|
||||||
.block_on((*conference).video_sink_element())
|
.block_on((*conference).video_sink_element())
|
||||||
|
@ -187,13 +220,16 @@ pub unsafe extern "C" fn gstmeet_conference_video_sink_element(context: *mut Con
|
||||||
pub unsafe extern "C" fn gstmeet_conference_on_participant(
|
pub unsafe extern "C" fn gstmeet_conference_on_participant(
|
||||||
context: *mut Context,
|
context: *mut Context,
|
||||||
conference: *mut JitsiConference,
|
conference: *mut JitsiConference,
|
||||||
f: unsafe extern "C" fn(*mut JitsiConference, Participant, *mut c_void) -> *mut gstreamer::ffi::GstBin,
|
f: unsafe extern "C" fn(
|
||||||
|
*mut JitsiConference,
|
||||||
|
Participant,
|
||||||
|
*mut c_void,
|
||||||
|
) -> *mut gstreamer::ffi::GstBin,
|
||||||
ctx: *mut c_void,
|
ctx: *mut c_void,
|
||||||
) {
|
) {
|
||||||
let ctx = Arc::new(AtomicPtr::new(ctx));
|
let ctx = Arc::new(AtomicPtr::new(ctx));
|
||||||
(*context)
|
(*context).runtime.block_on(
|
||||||
.runtime
|
(*conference).on_participant(move |conference, participant| {
|
||||||
.block_on((*conference).on_participant(move |conference, participant| {
|
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let participant = Participant {
|
let participant = Participant {
|
||||||
|
@ -205,10 +241,15 @@ pub unsafe extern "C" fn gstmeet_conference_on_participant(
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or_else(ptr::null),
|
.unwrap_or_else(ptr::null),
|
||||||
};
|
};
|
||||||
f(Box::into_raw(Box::new(conference)), participant, ctx.load(Ordering::Relaxed));
|
f(
|
||||||
|
Box::into_raw(Box::new(conference)),
|
||||||
|
participant,
|
||||||
|
ctx.load(Ordering::Relaxed),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -222,4 +263,4 @@ pub unsafe extern "C" fn gstmeet_conference_set_pipeline_state(
|
||||||
.block_on((*conference).set_pipeline_state(from_glib(state)))
|
.block_on((*conference).set_pipeline_state(from_glib(state)))
|
||||||
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
|
.map_err(|e| eprintln!("lib-gst-meet: {:?}", e))
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ async-trait = { version = "0.1", default-features = false }
|
||||||
bytes = { version = "1", default-features = false, features = ["std"] }
|
bytes = { version = "1", default-features = false, features = ["std"] }
|
||||||
futures = { version = "0.3", default-features = false }
|
futures = { version = "0.3", default-features = false }
|
||||||
glib = { version = "0.14", default-features = false }
|
glib = { version = "0.14", default-features = false }
|
||||||
gstreamer = { version = "0.17", default-features = false, features = ["v1_16"] }
|
gstreamer = { version = "0.17", default-features = false, features = ["v1_20"] }
|
||||||
|
gstreamer-rtp = { version = "0.17", default-features = false, features = ["v1_20"] }
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
itertools = { version = "0.10", default-features = false, features = ["use_std"] }
|
itertools = { version = "0.10", default-features = false, features = ["use_std"] }
|
||||||
libc = { version = "0.2", default-features = false }
|
libc = { version = "0.2", default-features = false }
|
||||||
|
|
|
@ -20,10 +20,7 @@ pub enum ColibriMessage {
|
||||||
previous_speakers: Vec<String>,
|
previous_speakers: Vec<String>,
|
||||||
},
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
EndpointConnectivityStatusChangeEvent {
|
EndpointConnectivityStatusChangeEvent { endpoint: String, active: bool },
|
||||||
endpoint: String,
|
|
||||||
active: bool,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
EndpointMessage {
|
EndpointMessage {
|
||||||
from: String,
|
from: String,
|
||||||
|
@ -42,17 +39,11 @@ pub enum ColibriMessage {
|
||||||
max_enabled_resolution: u16,
|
max_enabled_resolution: u16,
|
||||||
},
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
LastNChangedEvent {
|
LastNChangedEvent { last_n: u16 },
|
||||||
last_n: u16,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
LastNEndpointsChangeEvent {
|
LastNEndpointsChangeEvent { last_n_endpoints: Vec<String> },
|
||||||
last_n_endpoints: Vec<String>,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
ReceiverVideoConstraint {
|
ReceiverVideoConstraint { max_frame_height: u16 },
|
||||||
max_frame_height: u16,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
ReceiverVideoConstraints {
|
ReceiverVideoConstraints {
|
||||||
last_n: Option<u16>,
|
last_n: Option<u16>,
|
||||||
|
@ -62,21 +53,13 @@ pub enum ColibriMessage {
|
||||||
constraints: Option<HashMap<String, Constraints>>,
|
constraints: Option<HashMap<String, Constraints>>,
|
||||||
},
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
SelectedEndpointsChangedEvent {
|
SelectedEndpointsChangedEvent { selected_endpoints: Vec<String> },
|
||||||
selected_endpoints: Vec<String>,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
SenderVideoConstraints {
|
SenderVideoConstraints { video_constraints: Constraints },
|
||||||
video_constraints: Constraints,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
ServerHello {
|
ServerHello { version: Option<String> },
|
||||||
version: Option<String>,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
VideoTypeMessage {
|
VideoTypeMessage { video_type: VideoType },
|
||||||
video_type: VideoType,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
|
@ -124,11 +107,9 @@ pub(crate) struct ColibriChannel {
|
||||||
|
|
||||||
impl ColibriChannel {
|
impl ColibriChannel {
|
||||||
pub(crate) async fn new(colibri_url: &str) -> Result<Self> {
|
pub(crate) async fn new(colibri_url: &str) -> Result<Self> {
|
||||||
let request =
|
let request = Request::get(colibri_url).body(())?;
|
||||||
Request::get(colibri_url).body(())?;
|
let (colibri_websocket, _response) = tokio_tungstenite::connect_async(request).await?;
|
||||||
let (colibri_websocket, _response) =
|
|
||||||
tokio_tungstenite::connect_async(request).await?;
|
|
||||||
|
|
||||||
info!("Connected Colibri WebSocket");
|
info!("Connected Colibri WebSocket");
|
||||||
|
|
||||||
let (mut colibri_sink, mut colibri_stream) = colibri_websocket.split();
|
let (mut colibri_sink, mut colibri_stream) = colibri_websocket.split();
|
||||||
|
@ -151,11 +132,17 @@ impl ColibriChannel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => warn!("failed to parse frame on colibri websocket: {:?}\nframe: {}", e, text),
|
Err(e) => warn!(
|
||||||
|
"failed to parse frame on colibri websocket: {:?}\nframe: {}",
|
||||||
|
e, text
|
||||||
|
),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Message::Binary(data) => debug!("received unexpected {} byte binary frame on colibri websocket", data.len()),
|
Message::Binary(data) => debug!(
|
||||||
Message::Ping(_) | Message::Pong(_) => {}, // handled automatically by tungstenite
|
"received unexpected {} byte binary frame on colibri websocket",
|
||||||
|
data.len()
|
||||||
|
),
|
||||||
|
Message::Ping(_) | Message::Pong(_) => {}, // handled automatically by tungstenite
|
||||||
Message::Close(_) => {
|
Message::Close(_) => {
|
||||||
debug!("received close frame on colibri websocket");
|
debug!("received close frame on colibri websocket");
|
||||||
// TODO reconnect
|
// TODO reconnect
|
||||||
|
@ -194,10 +181,7 @@ impl ColibriChannel {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { send_tx, recv_tx })
|
||||||
send_tx,
|
|
||||||
recv_tx,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn subscribe(&self, tx: mpsc::Sender<ColibriMessage>) {
|
pub(crate) async fn subscribe(&self, tx: mpsc::Sender<ColibriMessage>) {
|
||||||
|
@ -208,4 +192,4 @@ impl ColibriChannel {
|
||||||
self.send_tx.send(msg).await?;
|
self.send_tx.send(msg).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,9 @@ static DISCO_INFO: Lazy<DiscoInfoResult> = Lazy::new(|| {
|
||||||
Feature::new(ns::JINGLE_RTP_VIDEO),
|
Feature::new(ns::JINGLE_RTP_VIDEO),
|
||||||
Feature::new(ns::JINGLE_ICE_UDP),
|
Feature::new(ns::JINGLE_ICE_UDP),
|
||||||
Feature::new(ns::JINGLE_DTLS),
|
Feature::new(ns::JINGLE_DTLS),
|
||||||
Feature::new("urn:ietf:rfc:5888"), // BUNDLE
|
Feature::new("urn:ietf:rfc:5888"), // BUNDLE
|
||||||
Feature::new("urn:ietf:rfc:5761"), // RTCP-MUX
|
Feature::new("urn:ietf:rfc:5761"), // RTCP-MUX
|
||||||
Feature::new("urn:ietf:rfc:4588"), // RTX
|
Feature::new("urn:ietf:rfc:4588"), // RTX
|
||||||
|
|
||||||
];
|
];
|
||||||
let gst_version = gstreamer::version();
|
let gst_version = gstreamer::version();
|
||||||
if gst_version.0 >= 1 && gst_version.1 >= 19 {
|
if gst_version.0 >= 1 && gst_version.1 >= 19 {
|
||||||
|
@ -105,9 +104,12 @@ type BoxedResultFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
|
||||||
pub(crate) struct JitsiConferenceInner {
|
pub(crate) struct JitsiConferenceInner {
|
||||||
pub(crate) jingle_session: Option<JingleSession>,
|
pub(crate) jingle_session: Option<JingleSession>,
|
||||||
participants: HashMap<String, Participant>,
|
participants: HashMap<String, Participant>,
|
||||||
on_participant: Option<Arc<dyn (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync>>,
|
on_participant:
|
||||||
on_participant_left: Option<Arc<dyn (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync>>,
|
Option<Arc<dyn (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync>>,
|
||||||
on_colibri_message: Option<Arc<dyn (Fn(JitsiConference, ColibriMessage) -> BoxedResultFuture) + Send + Sync>>,
|
on_participant_left:
|
||||||
|
Option<Arc<dyn (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync>>,
|
||||||
|
on_colibri_message:
|
||||||
|
Option<Arc<dyn (Fn(JitsiConference, ColibriMessage) -> BoxedResultFuture) + Send + Sync>>,
|
||||||
state: JitsiConferenceState,
|
state: JitsiConferenceState,
|
||||||
connected_tx: Option<oneshot::Sender<()>>,
|
connected_tx: Option<oneshot::Sender<()>>,
|
||||||
connected_rx: Option<oneshot::Receiver<()>>,
|
connected_rx: Option<oneshot::Receiver<()>>,
|
||||||
|
@ -292,7 +294,10 @@ impl JitsiConference {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(f))]
|
#[tracing::instrument(level = "trace", skip(f))]
|
||||||
pub async fn on_participant(&self, f: impl (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync + 'static) {
|
pub async fn on_participant(
|
||||||
|
&self,
|
||||||
|
f: impl (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync + 'static,
|
||||||
|
) {
|
||||||
let f = Arc::new(f);
|
let f = Arc::new(f);
|
||||||
let f2 = f.clone();
|
let f2 = f.clone();
|
||||||
let existing_participants: Vec<_> = {
|
let existing_participants: Vec<_> = {
|
||||||
|
@ -312,12 +317,18 @@ impl JitsiConference {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(f))]
|
#[tracing::instrument(level = "trace", skip(f))]
|
||||||
pub async fn on_participant_left(&self, f: impl (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync + 'static) {
|
pub async fn on_participant_left(
|
||||||
|
&self,
|
||||||
|
f: impl (Fn(JitsiConference, Participant) -> BoxedResultFuture) + Send + Sync + 'static,
|
||||||
|
) {
|
||||||
self.inner.lock().await.on_participant_left = Some(Arc::new(f));
|
self.inner.lock().await.on_participant_left = Some(Arc::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(f))]
|
#[tracing::instrument(level = "trace", skip(f))]
|
||||||
pub async fn on_colibri_message(&self, f: impl (Fn(JitsiConference, ColibriMessage) -> BoxedResultFuture) + Send + Sync + 'static) {
|
pub async fn on_colibri_message(
|
||||||
|
&self,
|
||||||
|
f: impl (Fn(JitsiConference, ColibriMessage) -> BoxedResultFuture) + Send + Sync + 'static,
|
||||||
|
) {
|
||||||
self.inner.lock().await.on_colibri_message = Some(Arc::new(f));
|
self.inner.lock().await.on_colibri_message = Some(Arc::new(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -472,7 +483,11 @@ impl StanzaFilter for JitsiConference {
|
||||||
let colibri_channel = ColibriChannel::new(&colibri_url).await?;
|
let colibri_channel = ColibriChannel::new(&colibri_url).await?;
|
||||||
let (tx, rx) = mpsc::channel(8);
|
let (tx, rx) = mpsc::channel(8);
|
||||||
colibri_channel.subscribe(tx).await;
|
colibri_channel.subscribe(tx).await;
|
||||||
locked_inner.jingle_session.as_mut().unwrap().colibri_channel = Some(colibri_channel);
|
locked_inner
|
||||||
|
.jingle_session
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.colibri_channel = Some(colibri_channel);
|
||||||
|
|
||||||
let self_ = self.clone();
|
let self_ = self.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
@ -523,7 +538,12 @@ impl StanzaFilter for JitsiConference {
|
||||||
muc_jid: from.clone(),
|
muc_jid: from.clone(),
|
||||||
nick: item.nick,
|
nick: item.nick,
|
||||||
};
|
};
|
||||||
if presence.type_ == presence::Type::Unavailable && locked_inner.participants.remove(&from.resource.clone()).is_some() {
|
if presence.type_ == presence::Type::Unavailable
|
||||||
|
&& locked_inner
|
||||||
|
.participants
|
||||||
|
.remove(&from.resource.clone())
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
debug!("participant left: {:?}", jid);
|
debug!("participant left: {:?}", jid);
|
||||||
if let Some(f) = &locked_inner.on_participant_left {
|
if let Some(f) = &locked_inner.on_participant_left {
|
||||||
debug!("calling on_participant_left with old participant");
|
debug!("calling on_participant_left with old participant");
|
||||||
|
|
|
@ -4,16 +4,13 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use glib::{ObjectExt, ToValue};
|
use glib::{ObjectExt, ToValue};
|
||||||
use gstreamer::prelude::{ElementExt, GObjectExtManualGst, GstBinExt, PadExt};
|
use gstreamer::prelude::{ElementExt, GObjectExtManualGst, GstBinExt, PadExt};
|
||||||
|
use gstreamer_rtp::{prelude::RTPHeaderExtensionExt, RTPHeaderExtension};
|
||||||
use nice_gst_meet as nice;
|
use nice_gst_meet as nice;
|
||||||
use pem::Pem;
|
use pem::Pem;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use rcgen::{Certificate, CertificateParams, PKCS_ECDSA_P256_SHA256};
|
use rcgen::{Certificate, CertificateParams, PKCS_ECDSA_P256_SHA256};
|
||||||
use ring::digest::{digest, SHA256};
|
use ring::digest::{digest, SHA256};
|
||||||
use tokio::{
|
use tokio::{net::lookup_host, runtime::Handle, sync::oneshot};
|
||||||
net::lookup_host,
|
|
||||||
runtime::Handle,
|
|
||||||
sync::oneshot,
|
|
||||||
};
|
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use xmpp_parsers::{
|
use xmpp_parsers::{
|
||||||
|
@ -37,7 +34,8 @@ use crate::{
|
||||||
|
|
||||||
const RTP_HDREXT_SSRC_AUDIO_LEVEL: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
|
const RTP_HDREXT_SSRC_AUDIO_LEVEL: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
|
||||||
const RTP_HDREXT_ABS_SEND_TIME: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
|
const RTP_HDREXT_ABS_SEND_TIME: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
|
||||||
const RTP_HDREXT_TRANSPORT_CC: &str = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
|
const RTP_HDREXT_TRANSPORT_CC: &str =
|
||||||
|
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
|
||||||
|
|
||||||
const DEFAULT_STUN_PORT: u16 = 3478;
|
const DEFAULT_STUN_PORT: u16 = 3478;
|
||||||
const DEFAULT_TURNS_PORT: u16 = 5349;
|
const DEFAULT_TURNS_PORT: u16 = 5349;
|
||||||
|
@ -162,14 +160,12 @@ impl JingleSession {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|pt| {
|
.find(|pt| {
|
||||||
pt.name.as_deref() == Some("rtx")
|
pt.name.as_deref() == Some("rtx")
|
||||||
&&
|
&& pt.parameters.iter().any(|param| {
|
||||||
pt
|
|
||||||
.parameters
|
|
||||||
.iter()
|
|
||||||
.any(|param| {
|
|
||||||
param.name == "apt"
|
param.name == "apt"
|
||||||
&&
|
&& param.value
|
||||||
param.value == h264_payload_type.map(|pt| pt.to_string()).unwrap_or_default()
|
== h264_payload_type
|
||||||
|
.map(|pt| pt.to_string())
|
||||||
|
.unwrap_or_default()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|pt| pt.id);
|
.map(|pt| pt.id);
|
||||||
|
@ -188,14 +184,12 @@ impl JingleSession {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|pt| {
|
.find(|pt| {
|
||||||
pt.name.as_deref() == Some("rtx")
|
pt.name.as_deref() == Some("rtx")
|
||||||
&&
|
&& pt.parameters.iter().any(|param| {
|
||||||
pt
|
|
||||||
.parameters
|
|
||||||
.iter()
|
|
||||||
.any(|param| {
|
|
||||||
param.name == "apt"
|
param.name == "apt"
|
||||||
&&
|
&& param.value
|
||||||
param.value == vp8_payload_type.map(|pt| pt.to_string()).unwrap_or_default()
|
== vp8_payload_type
|
||||||
|
.map(|pt| pt.to_string())
|
||||||
|
.unwrap_or_default()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|pt| pt.id);
|
.map(|pt| pt.id);
|
||||||
|
@ -214,14 +208,12 @@ impl JingleSession {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|pt| {
|
.find(|pt| {
|
||||||
pt.name.as_deref() == Some("rtx")
|
pt.name.as_deref() == Some("rtx")
|
||||||
&&
|
&& pt.parameters.iter().any(|param| {
|
||||||
pt
|
|
||||||
.parameters
|
|
||||||
.iter()
|
|
||||||
.any(|param| {
|
|
||||||
param.name == "apt"
|
param.name == "apt"
|
||||||
&&
|
&& param.value
|
||||||
param.value == vp9_payload_type.map(|pt| pt.to_string()).unwrap_or_default()
|
== vp9_payload_type
|
||||||
|
.map(|pt| pt.to_string())
|
||||||
|
.unwrap_or_default()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|pt| pt.id);
|
.map(|pt| pt.id);
|
||||||
|
@ -510,50 +502,59 @@ impl JingleSession {
|
||||||
}
|
}
|
||||||
Ok::<_, anyhow::Error>(Some(caps.build()))
|
Ok::<_, anyhow::Error>(Some(caps.build()))
|
||||||
}
|
}
|
||||||
else if Some(pt) == h264_payload_type || Some(pt) == vp8_payload_type || Some(pt) == vp9_payload_type {
|
else if Some(pt) == h264_payload_type
|
||||||
|
|| Some(pt) == vp8_payload_type
|
||||||
|
|| Some(pt) == vp9_payload_type
|
||||||
|
{
|
||||||
caps = caps
|
caps = caps
|
||||||
.field("media", "video")
|
.field("media", "video")
|
||||||
.field("clock-rate", 90000)
|
.field("clock-rate", 90000)
|
||||||
.field("encoding-name", if Some(pt) == h264_payload_type {
|
.field(
|
||||||
"H264"
|
"encoding-name",
|
||||||
}
|
if Some(pt) == h264_payload_type {
|
||||||
else if Some(pt) == vp8_payload_type {
|
"H264"
|
||||||
"VP8"
|
}
|
||||||
}
|
else if Some(pt) == vp8_payload_type {
|
||||||
else if Some(pt) == vp9_payload_type {
|
"VP8"
|
||||||
"VP9"
|
}
|
||||||
}
|
else if Some(pt) == vp9_payload_type {
|
||||||
else {
|
"VP9"
|
||||||
unreachable!()
|
}
|
||||||
});
|
else {
|
||||||
if let Some(hdrext) = video_hdrext_abs_send_time {
|
unreachable!()
|
||||||
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
},
|
||||||
}
|
);
|
||||||
|
// if let Some(hdrext) = video_hdrext_abs_send_time {
|
||||||
|
// caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
||||||
|
// }
|
||||||
if let Some(hdrext) = video_hdrext_transport_cc {
|
if let Some(hdrext) = video_hdrext_transport_cc {
|
||||||
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
caps = caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
||||||
}
|
}
|
||||||
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 {
|
else if Some(pt) == vp8_rtx_payload_type
|
||||||
|
|| Some(pt) == vp9_rtx_payload_type
|
||||||
|
|| Some(pt) == h264_rtx_payload_type
|
||||||
|
{
|
||||||
caps = caps
|
caps = caps
|
||||||
.field("media", "video")
|
.field("media", "video")
|
||||||
.field("clock-rate", 90000)
|
.field("clock-rate", 90000)
|
||||||
.field("encoding-name", "RTX")
|
.field("encoding-name", "RTX")
|
||||||
.field("apt", if Some(pt) == vp8_rtx_payload_type {
|
.field(
|
||||||
vp8_payload_type
|
"apt",
|
||||||
.context("missing VP8 payload type")?
|
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
|
else if Some(pt) == vp9_rtx_payload_type {
|
||||||
.context("missing VP9 payload type")?
|
vp9_payload_type.context("missing VP9 payload type")?
|
||||||
}
|
}
|
||||||
else if Some(pt) == h264_rtx_payload_type {
|
else if Some(pt) == h264_rtx_payload_type {
|
||||||
h264_payload_type
|
h264_payload_type.context("missing H264 payload type")?
|
||||||
.context("missing H264 payload type")?
|
}
|
||||||
}
|
else {
|
||||||
else {
|
unreachable!()
|
||||||
unreachable!()
|
},
|
||||||
});
|
);
|
||||||
Ok(Some(caps.build()))
|
Ok(Some(caps.build()))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -583,7 +584,10 @@ impl JingleSession {
|
||||||
let rtpjitterbuffer: gstreamer::Element = values[1].get()?;
|
let rtpjitterbuffer: gstreamer::Element = values[1].get()?;
|
||||||
let session: u32 = values[2].get()?;
|
let session: u32 = values[2].get()?;
|
||||||
let ssrc: u32 = values[3].get()?;
|
let ssrc: u32 = values[3].get()?;
|
||||||
debug!("new jitterbuffer created for session {} ssrc {}", session, ssrc);
|
debug!(
|
||||||
|
"new jitterbuffer created for session {} ssrc {}",
|
||||||
|
session, ssrc
|
||||||
|
);
|
||||||
|
|
||||||
let source = handle.block_on(async move {
|
let source = handle.block_on(async move {
|
||||||
let locked_inner = inner_.lock().await;
|
let locked_inner = inner_.lock().await;
|
||||||
|
@ -633,22 +637,18 @@ impl JingleSession {
|
||||||
rtx_sender.set_property("payload-type-map", pt_map.build())?;
|
rtx_sender.set_property("payload-type-map", pt_map.build())?;
|
||||||
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
|
rtx_sender.set_property("ssrc-map", ssrc_map.build())?;
|
||||||
bin.add(&rtx_sender)?;
|
bin.add(&rtx_sender)?;
|
||||||
bin.add_pad(
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
&gstreamer::GhostPad::with_target(
|
Some(&format!("src_{}", session)),
|
||||||
Some(&format!("src_{}", session)),
|
&rtx_sender
|
||||||
&rtx_sender
|
.static_pad("src")
|
||||||
.static_pad("src")
|
.context("rtprtxsend has no src pad")?,
|
||||||
.context("rtprtxsend has no src pad")?,
|
)?)?;
|
||||||
)?,
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
)?;
|
Some(&format!("sink_{}", session)),
|
||||||
bin.add_pad(
|
&rtx_sender
|
||||||
&gstreamer::GhostPad::with_target(
|
.static_pad("sink")
|
||||||
Some(&format!("sink_{}", session)),
|
.context("rtprtxsend has no sink pad")?,
|
||||||
&rtx_sender
|
)?)?;
|
||||||
.static_pad("sink")
|
|
||||||
.context("rtprtxsend has no sink pad")?,
|
|
||||||
)?,
|
|
||||||
)?;
|
|
||||||
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
||||||
};
|
};
|
||||||
match f() {
|
match f() {
|
||||||
|
@ -678,20 +678,18 @@ impl JingleSession {
|
||||||
let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?;
|
let rtx_receiver = gstreamer::ElementFactory::make("rtprtxreceive", None)?;
|
||||||
rtx_receiver.set_property("payload-type-map", pt_map.build())?;
|
rtx_receiver.set_property("payload-type-map", pt_map.build())?;
|
||||||
bin.add(&rtx_receiver)?;
|
bin.add(&rtx_receiver)?;
|
||||||
bin.add_pad(
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
&gstreamer::GhostPad::with_target(
|
Some(&format!("src_{}", session)),
|
||||||
Some(&format!("src_{}", session)),
|
&rtx_receiver
|
||||||
&rtx_receiver.static_pad("src")
|
.static_pad("src")
|
||||||
.context("rtprtxreceive has no src pad")?,
|
.context("rtprtxreceive has no src pad")?,
|
||||||
)?,
|
)?)?;
|
||||||
)?;
|
bin.add_pad(&gstreamer::GhostPad::with_target(
|
||||||
bin.add_pad(
|
Some(&format!("sink_{}", session)),
|
||||||
&gstreamer::GhostPad::with_target(
|
&rtx_receiver
|
||||||
Some(&format!("sink_{}", session)),
|
.static_pad("sink")
|
||||||
&rtx_receiver.static_pad("sink")
|
.context("rtprtxreceive has no sink pad")?,
|
||||||
.context("rtprtxreceive has no sink pad")?,
|
)?)?;
|
||||||
)?,
|
|
||||||
)?;
|
|
||||||
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
Ok::<_, anyhow::Error>(Some(bin.to_value()))
|
||||||
};
|
};
|
||||||
match f() {
|
match f() {
|
||||||
|
@ -703,136 +701,158 @@ impl JingleSession {
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let handle = Handle::current();
|
{
|
||||||
let inner_ = conference.inner.clone();
|
let handle = Handle::current();
|
||||||
let pipeline_ = pipeline.clone();
|
let inner = conference.inner.clone();
|
||||||
let rtpbin_ = rtpbin.clone();
|
let pipeline = pipeline.clone();
|
||||||
rtpbin.connect("pad-added", false, move |values| {
|
let rtpbin_ = rtpbin.clone();
|
||||||
let inner_ = inner_.clone();
|
rtpbin.connect("pad-added", false, move |values| {
|
||||||
let handle = handle.clone();
|
let rtpbin = &rtpbin_;
|
||||||
let pipeline_ = pipeline_.clone();
|
let f = || {
|
||||||
let rtpbin_ = rtpbin_.clone();
|
debug!("rtpbin pad-added {:?}", values);
|
||||||
let f = move || {
|
let pad: gstreamer::Pad = values[1].get()?;
|
||||||
debug!("rtpbin pad-added {:?}", values);
|
let pad_name: String = pad.property("name")?.get()?;
|
||||||
let pad: gstreamer::Pad = values[1].get()?;
|
if pad_name.starts_with("recv_rtp_src_0_") {
|
||||||
let pad_name: String = pad.property("name")?.get()?;
|
let mut parts = pad_name.split('_').skip(4);
|
||||||
if pad_name.starts_with("recv_rtp_src_0_") {
|
let ssrc: u32 = parts.next().context("malformed pad name")?.parse()?;
|
||||||
let mut parts = pad_name.split('_').skip(4);
|
let pt: u8 = parts.next().context("malformed pad name")?.parse()?;
|
||||||
let ssrc: u32 = parts.next().context("malformed pad name")?.parse()?;
|
let source = handle.block_on(async {
|
||||||
let pt: u8 = parts.next().context("malformed pad name")?.parse()?;
|
let locked_inner = inner.lock().await;
|
||||||
let source = handle.block_on(async move {
|
let jingle_session = locked_inner
|
||||||
let locked_inner = inner_.lock().await;
|
.jingle_session
|
||||||
let jingle_session = locked_inner
|
.as_ref()
|
||||||
.jingle_session
|
.context("not connected (no jingle session)")?;
|
||||||
.as_ref()
|
Ok::<_, anyhow::Error>(
|
||||||
.context("not connected (no jingle session)")?;
|
jingle_session
|
||||||
Ok::<_, anyhow::Error>(
|
.remote_ssrc_map
|
||||||
jingle_session
|
.get(&ssrc)
|
||||||
.remote_ssrc_map
|
.context(format!("unknown ssrc: {}", ssrc))?
|
||||||
.get(&ssrc)
|
.clone(),
|
||||||
.context(format!("unknown ssrc: {}", ssrc))?
|
)
|
||||||
.clone(),
|
})?;
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
debug!("pad added for remote source: {:?}", source);
|
debug!("pad added for remote source: {:?}", source);
|
||||||
|
|
||||||
let element_name = match source.media_type {
|
let source_element = match source.media_type {
|
||||||
MediaType::Audio => {
|
MediaType::Audio => {
|
||||||
if Some(pt) == opus_payload_type {
|
if Some(pt) == opus_payload_type {
|
||||||
"rtpopusdepay"
|
gstreamer::ElementFactory::make("rtpopusdepay", 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 {
|
if Some(pt) == h264_payload_type {
|
||||||
"rtph264depay"
|
let element = gstreamer::ElementFactory::make("rtph264depay", None)?;
|
||||||
}
|
element.set_property("request-keyframe", true)?;
|
||||||
else if Some(pt) == vp8_payload_type {
|
element
|
||||||
"rtpvp8depay"
|
}
|
||||||
}
|
else if Some(pt) == vp8_payload_type {
|
||||||
else if Some(pt) == vp9_payload_type {
|
let element = gstreamer::ElementFactory::make("rtpvp8depay", None)?;
|
||||||
"rtpvp9depay"
|
element.set_property("request-keyframe", true)?;
|
||||||
}
|
element
|
||||||
else {
|
}
|
||||||
bail!("received video with unsupported PT {}", pt);
|
else if Some(pt) == vp9_payload_type {
|
||||||
}
|
let element = gstreamer::ElementFactory::make("rtpvp9depay", None)?;
|
||||||
},
|
element.set_property("request-keyframe", true)?;
|
||||||
};
|
element
|
||||||
|
}
|
||||||
let source_element = gstreamer::ElementFactory::make(element_name, None)?;
|
else {
|
||||||
if source_element.has_property("auto-header-extension", None) {
|
bail!("received video with unsupported PT {}", pt);
|
||||||
source_element.set_property("auto-header-extension", true)?;
|
}
|
||||||
}
|
},
|
||||||
if source_element.has_property("request-keyframe", None) {
|
|
||||||
source_element.set_property("request-keyframe", true)?;
|
|
||||||
}
|
|
||||||
pipeline_
|
|
||||||
.add(&source_element)
|
|
||||||
.context(format!("failed to add {} to pipeline", element_name))?;
|
|
||||||
source_element.sync_state_with_parent()?;
|
|
||||||
debug!("created {} element", element_name);
|
|
||||||
rtpbin_
|
|
||||||
.link_pads(Some(&pad_name), &source_element, None)
|
|
||||||
.context(format!(
|
|
||||||
"failed to link rtpbin.{} to {}",
|
|
||||||
pad_name, element_name
|
|
||||||
))?;
|
|
||||||
debug!("linked rtpbin.{} to {}", pad_name, element_name);
|
|
||||||
|
|
||||||
let src_pad = source_element
|
|
||||||
.static_pad("src")
|
|
||||||
.context("depayloader has no src pad")?;
|
|
||||||
|
|
||||||
if let Some(participant_bin) =
|
|
||||||
pipeline_.by_name(&format!("participant_{}", source.participant_id))
|
|
||||||
{
|
|
||||||
let sink_pad_name = match source.media_type {
|
|
||||||
MediaType::Audio => "audio",
|
|
||||||
MediaType::Video => "video",
|
|
||||||
};
|
};
|
||||||
if let Some(sink_pad) = participant_bin.static_pad(sink_pad_name) {
|
|
||||||
debug!("linking depayloader to participant bin");
|
source_element.set_property("auto-header-extension", false)?;
|
||||||
src_pad.link(&sink_pad)?;
|
source_element.connect("request-extension", false, move |values| {
|
||||||
|
let f = || {
|
||||||
|
let ext_id: u32 = values[1].get()?;
|
||||||
|
let ext_uri: String = values[2].get()?;
|
||||||
|
debug!("depayloader requested extension: {} {}", ext_id, ext_uri);
|
||||||
|
let hdrext = RTPHeaderExtension::create_from_uri(&ext_uri)
|
||||||
|
.context("failed to create hdrext")?;
|
||||||
|
hdrext.set_id(ext_id);
|
||||||
|
if ext_uri == RTP_HDREXT_ABS_SEND_TIME {}
|
||||||
|
if ext_uri == RTP_HDREXT_SSRC_AUDIO_LEVEL {
|
||||||
|
}
|
||||||
|
else if ext_uri == RTP_HDREXT_TRANSPORT_CC {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bail!("unknown rtp hdrext: {}", ext_uri);
|
||||||
|
};
|
||||||
|
Ok::<_, anyhow::Error>(hdrext)
|
||||||
|
};
|
||||||
|
match f() {
|
||||||
|
Ok(hdrext) => Some(hdrext.to_value()),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("request-extension: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
pipeline
|
||||||
|
.add(&source_element)
|
||||||
|
.context("failed to add depayloader to pipeline")?;
|
||||||
|
source_element.sync_state_with_parent()?;
|
||||||
|
debug!("created depayloader");
|
||||||
|
rtpbin
|
||||||
|
.link_pads(Some(&pad_name), &source_element, None)
|
||||||
|
.context(format!("failed to link rtpbin.{} to depayloader", pad_name))?;
|
||||||
|
debug!("linked rtpbin.{} to depayloader", pad_name);
|
||||||
|
|
||||||
|
let src_pad = source_element
|
||||||
|
.static_pad("src")
|
||||||
|
.context("depayloader has no src pad")?;
|
||||||
|
|
||||||
|
if let Some(participant_bin) =
|
||||||
|
pipeline.by_name(&format!("participant_{}", source.participant_id))
|
||||||
|
{
|
||||||
|
let sink_pad_name = match source.media_type {
|
||||||
|
MediaType::Audio => "audio",
|
||||||
|
MediaType::Video => "video",
|
||||||
|
};
|
||||||
|
if let Some(sink_pad) = participant_bin.static_pad(sink_pad_name) {
|
||||||
|
debug!("linking depayloader to participant bin");
|
||||||
|
src_pad.link(&sink_pad)?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
warn!(
|
||||||
|
"no {} sink pad in {} participant bin",
|
||||||
|
sink_pad_name, source.participant_id
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
warn!(
|
debug!("no participant bin for {}", source.participant_id);
|
||||||
"no {} sink pad in {} participant bin",
|
|
||||||
sink_pad_name, source.participant_id
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !src_pad.is_linked() {
|
||||||
|
debug!("nothing linked to depayloader, adding fakesink");
|
||||||
|
let fakesink = gstreamer::ElementFactory::make("fakesink", None)?;
|
||||||
|
pipeline.add(&fakesink)?;
|
||||||
|
fakesink.sync_state_with_parent()?;
|
||||||
|
source_element.link(&fakesink)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
gstreamer::debug_bin_to_dot_file(
|
||||||
|
&pipeline,
|
||||||
|
gstreamer::DebugGraphDetails::ALL,
|
||||||
|
&format!("ssrc-added-{}", ssrc),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
debug!("no participant bin for {}", source.participant_id);
|
Ok(())
|
||||||
}
|
}
|
||||||
|
};
|
||||||
if !src_pad.is_linked() {
|
if let Err(e) = f() {
|
||||||
debug!("nothing linked to {}, adding fakesink", element_name);
|
error!("handling pad-added: {:?}", e);
|
||||||
let fakesink = gstreamer::ElementFactory::make("fakesink", None)?;
|
|
||||||
pipeline_.add(&fakesink)?;
|
|
||||||
fakesink.sync_state_with_parent()?;
|
|
||||||
source_element.link(&fakesink)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
gstreamer::debug_bin_to_dot_file(
|
|
||||||
&pipeline_,
|
|
||||||
gstreamer::DebugGraphDetails::ALL,
|
|
||||||
&format!("ssrc-added-{}", ssrc),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>(())
|
|
||||||
}
|
}
|
||||||
else {
|
None
|
||||||
Ok(())
|
})?;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
if let Err(e) = f() {
|
|
||||||
error!("handling pad-added: {:?}", e);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let audio_sink_element = gstreamer::ElementFactory::make("rtpopuspay", None)?;
|
let audio_sink_element = gstreamer::ElementFactory::make("rtpopuspay", None)?;
|
||||||
audio_sink_element.set_property(
|
audio_sink_element.set_property(
|
||||||
|
@ -841,13 +861,38 @@ impl JingleSession {
|
||||||
)?;
|
)?;
|
||||||
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)?;
|
||||||
let audio_hdrext_supported = if audio_sink_element.has_property("auto-header-extension", None) {
|
audio_sink_element.set_property("auto-header-extension", false)?;
|
||||||
audio_sink_element.set_property("auto-header-extension", true)?;
|
audio_sink_element.connect("request-extension", false, move |values| {
|
||||||
true
|
let f = || {
|
||||||
}
|
let ext_id: u32 = values[1].get()?;
|
||||||
else {
|
let ext_uri: String = values[2].get()?;
|
||||||
false
|
debug!(
|
||||||
};
|
"audio payloader requested extension: {} {}",
|
||||||
|
ext_id, ext_uri
|
||||||
|
);
|
||||||
|
let hdrext =
|
||||||
|
RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?;
|
||||||
|
hdrext.set_id(ext_id);
|
||||||
|
if ext_uri == RTP_HDREXT_ABS_SEND_TIME {
|
||||||
|
}
|
||||||
|
else if ext_uri == RTP_HDREXT_SSRC_AUDIO_LEVEL {
|
||||||
|
}
|
||||||
|
else if ext_uri == RTP_HDREXT_TRANSPORT_CC {
|
||||||
|
// hdrext.set_property("n-streams", 2u32)?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bail!("unknown rtp hdrext: {}", ext_uri);
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(hdrext)
|
||||||
|
};
|
||||||
|
match f() {
|
||||||
|
Ok(hdrext) => Some(hdrext.to_value()),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("request-extension: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})?;
|
||||||
pipeline.add(&audio_sink_element)?;
|
pipeline.add(&audio_sink_element)?;
|
||||||
|
|
||||||
let video_sink_element = match conference.config.video_codec.as_str() {
|
let video_sink_element = match conference.config.video_codec.as_str() {
|
||||||
|
@ -881,13 +926,36 @@ impl JingleSession {
|
||||||
other => bail!("unsupported video codec: {}", other),
|
other => bail!("unsupported video codec: {}", other),
|
||||||
};
|
};
|
||||||
video_sink_element.set_property("ssrc", video_ssrc)?;
|
video_sink_element.set_property("ssrc", video_ssrc)?;
|
||||||
let video_hdrext_supported = if video_sink_element.has_property("auto-header-extension", None) {
|
video_sink_element.set_property("auto-header-extension", false)?;
|
||||||
video_sink_element.set_property("auto-header-extension", true)?;
|
video_sink_element.connect("request-extension", false, move |values| {
|
||||||
true
|
let f = || {
|
||||||
}
|
let ext_id: u32 = values[1].get()?;
|
||||||
else {
|
let ext_uri: String = values[2].get()?;
|
||||||
false
|
debug!(
|
||||||
};
|
"video payloader requested extension: {} {}",
|
||||||
|
ext_id, ext_uri
|
||||||
|
);
|
||||||
|
let hdrext =
|
||||||
|
RTPHeaderExtension::create_from_uri(&ext_uri).context("failed to create hdrext")?;
|
||||||
|
hdrext.set_id(ext_id);
|
||||||
|
if ext_uri == RTP_HDREXT_ABS_SEND_TIME {
|
||||||
|
}
|
||||||
|
else if ext_uri == RTP_HDREXT_TRANSPORT_CC {
|
||||||
|
// hdrext.set_property("n-streams", 2u32)?;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bail!("unknown rtp hdrext: {}", ext_uri);
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(hdrext)
|
||||||
|
};
|
||||||
|
match f() {
|
||||||
|
Ok(hdrext) => Some(hdrext.to_value()),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("request-extension: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})?;
|
||||||
pipeline.add(&video_sink_element)?;
|
pipeline.add(&video_sink_element)?;
|
||||||
|
|
||||||
let mut audio_caps = gstreamer::Caps::builder("application/x-rtp");
|
let mut audio_caps = gstreamer::Caps::builder("application/x-rtp");
|
||||||
|
@ -903,9 +971,9 @@ impl JingleSession {
|
||||||
pipeline.add(&audio_capsfilter)?;
|
pipeline.add(&audio_capsfilter)?;
|
||||||
|
|
||||||
let mut video_caps = gstreamer::Caps::builder("application/x-rtp");
|
let mut video_caps = gstreamer::Caps::builder("application/x-rtp");
|
||||||
if let Some(hdrext) = video_hdrext_abs_send_time {
|
// if let Some(hdrext) = video_hdrext_abs_send_time {
|
||||||
video_caps = video_caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
// video_caps = video_caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_ABS_SEND_TIME);
|
||||||
}
|
// }
|
||||||
if let Some(hdrext) = video_hdrext_transport_cc {
|
if let Some(hdrext) = video_hdrext_transport_cc {
|
||||||
video_caps = video_caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
video_caps = video_caps.field(&format!("extmap-{}", hdrext), &RTP_HDREXT_TRANSPORT_CC);
|
||||||
}
|
}
|
||||||
|
@ -913,19 +981,13 @@ impl JingleSession {
|
||||||
video_capsfilter.set_property("caps", video_caps.build())?;
|
video_capsfilter.set_property("caps", video_caps.build())?;
|
||||||
pipeline.add(&video_capsfilter)?;
|
pipeline.add(&video_capsfilter)?;
|
||||||
|
|
||||||
let rtpfunnel = gstreamer::ElementFactory::make("funnel", None)?;
|
debug!("linking video payloader -> rtpbin");
|
||||||
pipeline.add(&rtpfunnel)?;
|
|
||||||
|
|
||||||
debug!("linking audio payloader -> rtp funnel");
|
|
||||||
audio_sink_element.link(&audio_capsfilter)?;
|
|
||||||
audio_capsfilter.link_pads(None, &rtpfunnel, Some("sink_0"))?;
|
|
||||||
|
|
||||||
debug!("linking video payloader -> rtp funnel");
|
|
||||||
video_sink_element.link(&video_capsfilter)?;
|
video_sink_element.link(&video_capsfilter)?;
|
||||||
video_capsfilter.link_pads(None, &rtpfunnel, Some("sink_1"))?;
|
video_capsfilter.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?;
|
||||||
|
|
||||||
debug!("linking rtp funnel -> rtpbin");
|
debug!("linking audio payloader -> rtpbin");
|
||||||
rtpfunnel.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?;
|
audio_sink_element.link(&audio_capsfilter)?;
|
||||||
|
audio_capsfilter.link_pads(None, &rtpbin, Some("send_rtp_sink_1"))?;
|
||||||
|
|
||||||
debug!("link dtlssrtpdec -> rtpbin");
|
debug!("link dtlssrtpdec -> rtpbin");
|
||||||
dtlssrtpdec.link_pads(Some("rtp_src"), &rtpbin, Some("recv_rtp_sink_0"))?;
|
dtlssrtpdec.link_pads(Some("rtp_src"), &rtpbin, Some("recv_rtp_sink_0"))?;
|
||||||
|
@ -934,6 +996,8 @@ impl JingleSession {
|
||||||
debug!("linking rtpbin -> dtlssrtpenc");
|
debug!("linking rtpbin -> dtlssrtpenc");
|
||||||
rtpbin.link_pads(Some("send_rtp_src_0"), &dtlssrtpenc, Some("rtp_sink_0"))?;
|
rtpbin.link_pads(Some("send_rtp_src_0"), &dtlssrtpenc, Some("rtp_sink_0"))?;
|
||||||
rtpbin.link_pads(Some("send_rtcp_src_0"), &dtlssrtpenc, Some("rtcp_sink_0"))?;
|
rtpbin.link_pads(Some("send_rtcp_src_0"), &dtlssrtpenc, Some("rtcp_sink_0"))?;
|
||||||
|
rtpbin.link_pads(Some("send_rtp_src_1"), &dtlssrtpenc, Some("rtp_sink_1"))?;
|
||||||
|
rtpbin.link_pads(Some("send_rtcp_src_1"), &dtlssrtpenc, Some("rtcp_sink_1"))?;
|
||||||
|
|
||||||
debug!("linking ice src -> dtlssrtpdec");
|
debug!("linking ice src -> dtlssrtpdec");
|
||||||
nicesrc.link(&dtlssrtpdec)?;
|
nicesrc.link(&dtlssrtpdec)?;
|
||||||
|
@ -1059,7 +1123,6 @@ impl JingleSession {
|
||||||
else {
|
else {
|
||||||
bail!("no vp9 payload type in jingle session-initiate");
|
bail!("no vp9 payload type in jingle session-initiate");
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
other => bail!("unsupported video codec: {}", other),
|
other => bail!("unsupported video codec: {}", other),
|
||||||
}
|
}
|
||||||
|
@ -1124,30 +1187,21 @@ impl JingleSession {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
if let Some(hdrext) = audio_hdrext_transport_cc {
|
if let Some(hdrext) = audio_hdrext_transport_cc {
|
||||||
if audio_hdrext_supported {
|
description.hdrexts.push(RtpHdrext::new(
|
||||||
description.hdrexts.push(RtpHdrext::new(hdrext.to_string(), RTP_HDREXT_TRANSPORT_CC.to_owned()));
|
hdrext.to_string(),
|
||||||
}
|
RTP_HDREXT_TRANSPORT_CC.to_owned(),
|
||||||
else {
|
));
|
||||||
debug!("transport-cc hdrext requested, but hdrext not supported by audio payloader");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if initiate_content.name.0 == "video" {
|
else if initiate_content.name.0 == "video" {
|
||||||
if let Some(hdrext) = video_hdrext_abs_send_time {
|
// if let Some(hdrext) = video_hdrext_abs_send_time {
|
||||||
if video_hdrext_supported {
|
// description.hdrexts.push(RtpHdrext::new(hdrext.to_string(), RTP_HDREXT_ABS_SEND_TIME.to_owned()));
|
||||||
description.hdrexts.push(RtpHdrext::new(hdrext.to_string(), RTP_HDREXT_ABS_SEND_TIME.to_owned()));
|
// }
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug!("abs-send-time hdrext requested, but hdrext not supported by video payloader");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(hdrext) = video_hdrext_transport_cc {
|
if let Some(hdrext) = video_hdrext_transport_cc {
|
||||||
if video_hdrext_supported {
|
description.hdrexts.push(RtpHdrext::new(
|
||||||
description.hdrexts.push(RtpHdrext::new(hdrext.to_string(), RTP_HDREXT_TRANSPORT_CC.to_owned()));
|
hdrext.to_string(),
|
||||||
}
|
RTP_HDREXT_TRANSPORT_CC.to_owned(),
|
||||||
else {
|
));
|
||||||
debug!("transport-cc hdrext requested, but hdrext not supported by video payloader");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,4 @@ pub fn init_tracing(level: tracing::Level) {
|
||||||
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
|
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
|
||||||
.with_target(false)
|
.with_target(false)
|
||||||
.init();
|
.init();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue