258 lines
7.4 KiB
Rust
258 lines
7.4 KiB
Rust
use super::ConnectionQuality;
|
|
use super::ParticipantInternal;
|
|
use crate::options;
|
|
use crate::options::compute_video_encodings;
|
|
use crate::options::video_layers_from_encodings;
|
|
use crate::options::TrackPublishOptions;
|
|
use crate::prelude::*;
|
|
use crate::rtc_engine::RtcEngine;
|
|
use crate::DataPacketKind;
|
|
use livekit_protocol as proto;
|
|
use livekit_webrtc::rtp_parameters::RtpEncodingParameters;
|
|
use parking_lot::RwLockReadGuard;
|
|
use std::collections::HashMap;
|
|
use std::fmt::Debug;
|
|
use std::sync::Arc;
|
|
use tokio::sync::mpsc;
|
|
|
|
#[derive(Clone)]
|
|
pub struct LocalParticipant {
|
|
inner: Arc<ParticipantInternal>,
|
|
}
|
|
|
|
impl Debug for LocalParticipant {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("LocalParticipant")
|
|
.field("sid", &self.sid())
|
|
.field("identity", &self.identity())
|
|
.field("name", &self.name())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl LocalParticipant {
|
|
pub(crate) fn new(
|
|
rtc_engine: Arc<RtcEngine>,
|
|
sid: ParticipantSid,
|
|
identity: ParticipantIdentity,
|
|
name: String,
|
|
metadata: String,
|
|
) -> Self {
|
|
Self {
|
|
inner: Arc::new(ParticipantInternal::new(
|
|
rtc_engine, sid, identity, name, metadata,
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub async fn publish_track(
|
|
&self,
|
|
track: LocalTrack,
|
|
options: TrackPublishOptions,
|
|
) -> RoomResult<LocalTrackPublication> {
|
|
let mut req = proto::AddTrackRequest {
|
|
cid: track.rtc_track().id(),
|
|
name: track.name().clone(),
|
|
r#type: proto::TrackType::from(track.kind()) as i32,
|
|
muted: track.is_muted(),
|
|
source: proto::TrackSource::from(options.source) as i32,
|
|
disable_dtx: !options.dtx,
|
|
disable_red: !options.red,
|
|
..Default::default()
|
|
};
|
|
|
|
let mut encodings = Vec::default();
|
|
match &track {
|
|
LocalTrack::Video(video_track) => {
|
|
// Get the video dimension
|
|
// TODO(theomonnom): Use MediaStreamTrack::getSettings() on web
|
|
let resolution = video_track.rtc_source().video_resolution();
|
|
req.width = resolution.width;
|
|
req.height = resolution.height;
|
|
|
|
encodings = compute_video_encodings(req.width, req.height, &options);
|
|
req.layers = video_layers_from_encodings(req.width, req.height, &encodings);
|
|
}
|
|
LocalTrack::Audio(_audio_track) => {
|
|
// Setup audio encoding
|
|
let audio_encoding = options
|
|
.audio_encoding
|
|
.as_ref()
|
|
.unwrap_or(&options::audio::SPEECH.encoding);
|
|
|
|
encodings.push(RtpEncodingParameters {
|
|
max_bitrate: Some(audio_encoding.max_bitrate),
|
|
..Default::default()
|
|
});
|
|
}
|
|
}
|
|
let track_info = self.inner.rtc_engine.add_track(req).await?;
|
|
let publication = LocalTrackPublication::new(
|
|
track_info.clone(),
|
|
Arc::downgrade(&self.inner),
|
|
track.clone(),
|
|
);
|
|
track.update_info(track_info); // Update sid + source
|
|
|
|
log::debug!("publishing track with cid {:?}", track.rtc_track().id());
|
|
let transceiver = self
|
|
.inner
|
|
.rtc_engine
|
|
.create_sender(track.clone(), options, encodings)
|
|
.await?;
|
|
|
|
track.update_transceiver(Some(transceiver));
|
|
//track.start();
|
|
track.enable();
|
|
|
|
tokio::spawn({
|
|
let rtc_engine = self.inner.rtc_engine.clone();
|
|
async move {
|
|
let _ = rtc_engine.negotiate_publisher().await;
|
|
}
|
|
});
|
|
|
|
self.inner
|
|
.add_publication(TrackPublication::Local(publication.clone()));
|
|
|
|
self.inner
|
|
.dispatcher
|
|
.dispatch(&ParticipantEvent::LocalTrackPublished {
|
|
publication: publication.clone(),
|
|
});
|
|
|
|
Ok(publication)
|
|
}
|
|
|
|
pub async fn unpublish_track(
|
|
&self,
|
|
track: TrackSid,
|
|
_stop_on_unpublish: bool,
|
|
) -> RoomResult<LocalTrackPublication> {
|
|
let mut tracks = self.inner.tracks.write();
|
|
if let Some(TrackPublication::Local(publication)) = tracks.remove(&track) {
|
|
let track = publication.track();
|
|
let sender = track.transceiver().unwrap().sender();
|
|
|
|
self.inner.rtc_engine.remove_track(sender).await?;
|
|
track.update_transceiver(None);
|
|
|
|
self.inner
|
|
.dispatcher
|
|
.dispatch(&ParticipantEvent::LocalTrackUnpublished {
|
|
publication: publication.clone(),
|
|
});
|
|
// publication.update_track(None);
|
|
|
|
tokio::spawn({
|
|
let rtc_engine = self.inner.rtc_engine.clone();
|
|
async move {
|
|
let _ = rtc_engine.negotiate_publisher().await;
|
|
}
|
|
});
|
|
|
|
Ok(publication)
|
|
} else {
|
|
Err(RoomError::Internal("track not found".to_string()))
|
|
}
|
|
}
|
|
|
|
pub async fn publish_data(
|
|
&self,
|
|
data: Vec<u8>,
|
|
kind: DataPacketKind,
|
|
destination_sids: Vec<String>,
|
|
) -> RoomResult<()> {
|
|
let data = proto::DataPacket {
|
|
kind: kind as i32,
|
|
value: Some(proto::data_packet::Value::User(proto::UserPacket {
|
|
payload: data,
|
|
destination_sids: destination_sids.to_owned(),
|
|
..Default::default()
|
|
})),
|
|
};
|
|
|
|
self.inner
|
|
.rtc_engine
|
|
.publish_data(&data, kind)
|
|
.await
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_track_publication(&self, sid: &TrackSid) -> Option<LocalTrackPublication> {
|
|
self.inner.tracks.read().get(sid).map(|track| {
|
|
if let TrackPublication::Local(local) = track {
|
|
return local.clone();
|
|
}
|
|
|
|
unreachable!()
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
pub fn sid(&self) -> ParticipantSid {
|
|
self.inner.sid()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn identity(&self) -> ParticipantIdentity {
|
|
self.inner.identity()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn name(&self) -> String {
|
|
self.inner.name()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn metadata(&self) -> String {
|
|
self.inner.metadata()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_speaking(&self) -> bool {
|
|
self.inner.is_speaking()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn tracks(&self) -> RwLockReadGuard<HashMap<TrackSid, TrackPublication>> {
|
|
self.inner.tracks()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn audio_level(&self) -> f32 {
|
|
self.inner.audio_level()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn connection_quality(&self) -> ConnectionQuality {
|
|
self.inner.connection_quality()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn register_observer(&self) -> mpsc::UnboundedReceiver<ParticipantEvent> {
|
|
self.inner.register_observer()
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn update_info(self: &Self, info: proto::ParticipantInfo) {
|
|
self.inner.update_info(info);
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn set_speaking(&self, speaking: bool) {
|
|
self.inner.set_speaking(speaking);
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn set_audio_level(&self, level: f32) {
|
|
self.inner.set_audio_level(level);
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn set_connection_quality(&self, quality: ConnectionQuality) {
|
|
self.inner.set_connection_quality(quality);
|
|
}
|
|
}
|