wip
I still need to solve type checking cycle to be able to compile
This commit is contained in:
@@ -35,7 +35,7 @@ pub enum RoomError {
|
|||||||
|
|
||||||
pub type RoomResult<T> = Result<T, RoomError>;
|
pub type RoomResult<T> = Result<T, RoomError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum ConnectionState {
|
pub enum ConnectionState {
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Connecting,
|
Connecting,
|
||||||
@@ -69,10 +69,14 @@ impl Room {
|
|||||||
|
|
||||||
#[instrument(level = Level::DEBUG)]
|
#[instrument(level = Level::DEBUG)]
|
||||||
pub async fn connect(&mut self, url: &str, token: &str) -> RoomResult<()> {
|
pub async fn connect(&mut self, url: &str, token: &str) -> RoomResult<()> {
|
||||||
let (rtc_engine, engine_events) =
|
// Initialize the RTCEngine
|
||||||
RTCEngine::connect(url, token, SignalOptions::default()).await?;
|
let (rtc_engine, engine_events) = RTCEngine::new();
|
||||||
let rtc_engine = Arc::new(rtc_engine);
|
let rtc_engine = Arc::new(rtc_engine);
|
||||||
let join_response = rtc_engine.join_response();
|
rtc_engine
|
||||||
|
.connect(url, token, SignalOptions::default())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let join_response = rtc_engine.join_response().unwrap();
|
||||||
let pi = join_response.participant.unwrap().clone();
|
let pi = join_response.participant.unwrap().clone();
|
||||||
let local_participant = Arc::new(LocalParticipant::new(
|
let local_participant = Arc::new(LocalParticipant::new(
|
||||||
rtc_engine.clone(),
|
rtc_engine.clone(),
|
||||||
@@ -150,26 +154,21 @@ impl Room {
|
|||||||
Self::handle_participant_update(room_inner.clone(), room_events.clone(), update)
|
Self::handle_participant_update(room_inner.clone(), room_events.clone(), update)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
EngineEvent::AddTrack {
|
EngineEvent::MediaTrack {
|
||||||
rtp_receiver,
|
track,
|
||||||
streams,
|
stream,
|
||||||
|
receiver,
|
||||||
} => {
|
} => {
|
||||||
if streams.is_empty() {
|
let stream_id = stream.id();
|
||||||
Err(RoomError::Internal(
|
let lk_stream_id = unpack_stream_id(&stream_id);
|
||||||
"AddTrack event with empty streams".to_string(),
|
if lk_stream_id.is_none() {
|
||||||
))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let first_stream_id = streams.first().unwrap().id();
|
|
||||||
let stream_id = unpack_stream_id(&first_stream_id);
|
|
||||||
if stream_id.is_none() {
|
|
||||||
Err(RoomError::Internal(format!(
|
Err(RoomError::Internal(format!(
|
||||||
"AddTrack event with invalid track_id: {:?}",
|
"MediaTrack event with invalid track_id: {:?}",
|
||||||
first_stream_id
|
&stream_id
|
||||||
)))?;
|
)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (participant_sid, track_sid) = stream_id.unwrap();
|
let (participant_sid, track_sid) = lk_stream_id.unwrap();
|
||||||
let remote_participant =
|
let remote_participant =
|
||||||
Self::get_participant(room_inner.clone(), &participant_sid.to_string().into());
|
Self::get_participant(room_inner.clone(), &participant_sid.to_string().into());
|
||||||
|
|
||||||
@@ -181,7 +180,7 @@ impl Room {
|
|||||||
.add_subscribed_media_track(
|
.add_subscribed_media_track(
|
||||||
RoomHandle::from(room_inner),
|
RoomHandle::from(room_inner),
|
||||||
track_sid,
|
track_sid,
|
||||||
rtp_receiver.track(),
|
track,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -195,6 +194,11 @@ impl Room {
|
|||||||
)))?;
|
)))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EngineEvent::Resuming => {}
|
||||||
|
EngineEvent::Resumed => {}
|
||||||
|
EngineEvent::Restarting => {}
|
||||||
|
EngineEvent::Restarted => {}
|
||||||
|
EngineEvent::Disconnected => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
use futures::FutureExt;
|
||||||
use livekit_webrtc::data_channel::DataSendError;
|
use livekit_webrtc::data_channel::DataSendError;
|
||||||
use livekit_webrtc::jsep::SdpParseError;
|
use livekit_webrtc::jsep::SdpParseError;
|
||||||
use livekit_webrtc::media_stream::MediaStream;
|
use livekit_webrtc::media_stream::{MediaStream, MediaStreamTrackHandle};
|
||||||
use livekit_webrtc::rtc_error::RTCError;
|
use livekit_webrtc::rtc_error::RTCError;
|
||||||
use livekit_webrtc::rtp_receiver::RtpReceiver;
|
use livekit_webrtc::rtp_receiver::RtpReceiver;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::Mutex;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tokio::sync::RwLock as AsyncRwLock;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@@ -18,7 +20,7 @@ use crate::proto::{data_packet, DataPacket, JoinResponse, ParticipantUpdate};
|
|||||||
use crate::rtc_engine::lk_runtime::LKRuntime;
|
use crate::rtc_engine::lk_runtime::LKRuntime;
|
||||||
use crate::signal_client::{SignalError, SignalOptions};
|
use crate::signal_client::{SignalError, SignalOptions};
|
||||||
|
|
||||||
use self::rtc_session::{RTCSession, SessionEvent, SessionEvents};
|
use self::rtc_session::{RTCSession, SessionEvent, SessionEvents, SessionInfo};
|
||||||
|
|
||||||
mod lk_runtime;
|
mod lk_runtime;
|
||||||
mod pc_transport;
|
mod pc_transport;
|
||||||
@@ -52,25 +54,39 @@ pub enum EngineError {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum EngineEvent {
|
pub enum EngineEvent {
|
||||||
ParticipantUpdate(ParticipantUpdate),
|
ParticipantUpdate(ParticipantUpdate),
|
||||||
AddTrack {
|
MediaTrack {
|
||||||
rtp_receiver: RtpReceiver,
|
track: MediaStreamTrackHandle,
|
||||||
streams: Vec<MediaStream>,
|
stream: MediaStream,
|
||||||
|
receiver: RtpReceiver,
|
||||||
},
|
},
|
||||||
Resuming,
|
Resuming,
|
||||||
Resumed,
|
Resumed,
|
||||||
Restarting,
|
Restarting,
|
||||||
Restarted,
|
Restarted,
|
||||||
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(theomonnom): Smarter retry intervals
|
// TODO(theomonnom): Smarter retry intervals
|
||||||
pub(crate) const RECONNECT_ATTEMPTS: u32 = 10;
|
pub const RECONNECT_ATTEMPTS: u32 = 10;
|
||||||
pub(crate) const RECONNECT_INTERVAL: Duration = Duration::from_millis(300);
|
pub const RECONNECT_INTERVAL: Duration = Duration::from_millis(300);
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Share one LKRuntime across all RTCEngine instances
|
// Share one LKRuntime across all RTCEngine instances
|
||||||
static ref LK_RUNTIME: Mutex<Weak<LKRuntime>> = Mutex::new(Weak::new());
|
static ref LK_RUNTIME: Mutex<Weak<LKRuntime>> = Mutex::new(Weak::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum SimulateScenario {
|
||||||
|
SignalReconnect,
|
||||||
|
Speaker,
|
||||||
|
NodeFailure,
|
||||||
|
ServerLeave,
|
||||||
|
Migration,
|
||||||
|
ForceTcp,
|
||||||
|
ForceTls,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a running RTCSession with the ability to close the session
|
/// Represents a running RTCSession with the ability to close the session
|
||||||
/// and the engine_task
|
/// and the engine_task
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -82,7 +98,9 @@ struct EngineHandle {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct EngineInner {
|
struct EngineInner {
|
||||||
running_handle: RwLock<Option<EngineHandle>>,
|
lk_runtime: Arc<LKRuntime>,
|
||||||
|
session_info: Mutex<Option<SessionInfo>>, // Last/Current Sessioninfo
|
||||||
|
running_handle: AsyncRwLock<Option<EngineHandle>>,
|
||||||
reconnecting: AtomicBool,
|
reconnecting: AtomicBool,
|
||||||
opened: AtomicBool,
|
opened: AtomicBool,
|
||||||
engine_emitter: EngineEmitter,
|
engine_emitter: EngineEmitter,
|
||||||
@@ -90,7 +108,6 @@ struct EngineInner {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RTCEngine {
|
pub struct RTCEngine {
|
||||||
lk_runtime: Arc<LKRuntime>,
|
|
||||||
inner: Arc<EngineInner>,
|
inner: Arc<EngineInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,19 +127,15 @@ impl RTCEngine {
|
|||||||
|
|
||||||
let (engine_emitter, engine_events) = mpsc::channel(8);
|
let (engine_emitter, engine_events) = mpsc::channel(8);
|
||||||
let inner = Arc::new(EngineInner {
|
let inner = Arc::new(EngineInner {
|
||||||
|
lk_runtime: lk_runtime.unwrap(),
|
||||||
|
session_info: Default::default(),
|
||||||
running_handle: Default::default(),
|
running_handle: Default::default(),
|
||||||
reconnecting: Default::default(),
|
reconnecting: Default::default(),
|
||||||
opened: Default::default(),
|
opened: Default::default(),
|
||||||
engine_emitter,
|
engine_emitter,
|
||||||
});
|
});
|
||||||
|
|
||||||
(
|
(Self { inner }, engine_events)
|
||||||
Self {
|
|
||||||
lk_runtime: lk_runtime.unwrap(),
|
|
||||||
inner,
|
|
||||||
},
|
|
||||||
engine_events,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
@@ -132,37 +145,18 @@ impl RTCEngine {
|
|||||||
token: &str,
|
token: &str,
|
||||||
options: SignalOptions,
|
options: SignalOptions,
|
||||||
) -> EngineResult<()> {
|
) -> EngineResult<()> {
|
||||||
let (session_emitter, session_events) = mpsc::unbounded_channel();
|
self.inner.connect(url, token, options).await
|
||||||
let session = RTCSession::connect(
|
|
||||||
url,
|
|
||||||
token,
|
|
||||||
options,
|
|
||||||
self.lk_runtime.clone(),
|
|
||||||
session_emitter,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let (close_sender, close_receiver) = oneshot::channel();
|
|
||||||
let engine_task = tokio::spawn(
|
|
||||||
self.inner
|
|
||||||
.clone()
|
|
||||||
.engine_task(session_events, close_receiver),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.inner.opened.store(true, Ordering::SeqCst);
|
|
||||||
*self.inner.running_handle.write() = Some(EngineHandle {
|
|
||||||
session,
|
|
||||||
engine_task,
|
|
||||||
close_sender,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn close(&self) {
|
pub async fn close(&self) {
|
||||||
self.inner.opened.store(false, Ordering::SeqCst);
|
self.inner.opened.store(false, Ordering::SeqCst);
|
||||||
self.inner.close();
|
self.inner.close();
|
||||||
|
let _ = self
|
||||||
|
.inner
|
||||||
|
.engine_emitter
|
||||||
|
.send(EngineEvent::Disconnected)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(data))]
|
#[tracing::instrument(skip(data))]
|
||||||
@@ -175,6 +169,7 @@ impl RTCEngine {
|
|||||||
self.inner
|
self.inner
|
||||||
.running_handle
|
.running_handle
|
||||||
.read()
|
.read()
|
||||||
|
.await
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.session
|
.session
|
||||||
@@ -185,8 +180,8 @@ impl RTCEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn join_response(&self) -> Option<JoinResponse> {
|
pub fn join_response(&self) -> Option<JoinResponse> {
|
||||||
if let Some(handle) = self.inner.running_handle.read().as_ref() {
|
if let Some(info) = self.inner.session_info.lock().as_ref() {
|
||||||
Some(handle.session.info().join_response.clone())
|
Some(info.join_response.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -217,15 +212,68 @@ impl EngineInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_session_event(&self, event: SessionEvent) -> EngineResult<()> {
|
async fn on_session_event(self: &Arc<Self>, event: SessionEvent) -> EngineResult<()> {
|
||||||
|
match event {
|
||||||
|
SessionEvent::Close { reason } => {
|
||||||
|
info!("received session close: {}", reason);
|
||||||
|
self.handle_disconnected().await;
|
||||||
|
}
|
||||||
|
SessionEvent::Data { data } => {}
|
||||||
|
SessionEvent::MediaTrack {
|
||||||
|
track,
|
||||||
|
stream,
|
||||||
|
receiver,
|
||||||
|
} => {
|
||||||
|
let _ = self
|
||||||
|
.engine_emitter
|
||||||
|
.send(EngineEvent::MediaTrack {
|
||||||
|
track,
|
||||||
|
stream,
|
||||||
|
receiver,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
SessionEvent::Connected => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
url: &str,
|
||||||
|
token: &str,
|
||||||
|
options: SignalOptions,
|
||||||
|
) -> EngineResult<()> {
|
||||||
|
let (session_emitter, session_events) = mpsc::unbounded_channel();
|
||||||
|
let session = RTCSession::connect(
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
options,
|
||||||
|
self.lk_runtime.clone(),
|
||||||
|
session_emitter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let (close_sender, close_receiver) = oneshot::channel();
|
||||||
|
let engine_task = tokio::spawn(self.clone().engine_task(session_events, close_receiver));
|
||||||
|
|
||||||
|
*self.session_info.lock() = Some(session.info().clone());
|
||||||
|
*self.running_handle.write().await = Some(EngineHandle {
|
||||||
|
session,
|
||||||
|
engine_task,
|
||||||
|
close_sender,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.opened.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn close(&self) {
|
async fn close(&self) {
|
||||||
if let Some(handle) = self.running_handle.write().take() {
|
if let Some(handle) = self.running_handle.write().await.take() {
|
||||||
handle.session.close().await;
|
handle.session.close().await;
|
||||||
let _ = handle.close_sender.send(());
|
let _ = handle.close_sender.send(());
|
||||||
handle.engine_task.await;
|
let _ = handle.engine_task.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +286,7 @@ impl EngineInner {
|
|||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.running_handle.read().is_none() {
|
if self.running_handle.read().await.is_none() {
|
||||||
Err(EngineError::Connection("reconnection failed".to_owned()))?
|
Err(EngineError::Connection("reconnection failed".to_owned()))?
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +295,7 @@ impl EngineInner {
|
|||||||
|
|
||||||
/// Called every time the PeerConnection or the SignalClient is closed
|
/// Called every time the PeerConnection or the SignalClient is closed
|
||||||
/// We first try to resume the connection, if it fails, we start a full reconnect.
|
/// We first try to resume the connection, if it fails, we start a full reconnect.
|
||||||
async fn handle_disconnected(&self) {
|
async fn handle_disconnected(self: &Arc<Self>) {
|
||||||
if !self.opened.load(Ordering::SeqCst) || self.reconnecting.load(Ordering::SeqCst) {
|
if !self.opened.load(Ordering::SeqCst) || self.reconnecting.load(Ordering::SeqCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -255,6 +303,7 @@ impl EngineInner {
|
|||||||
self.reconnecting.store(true, Ordering::SeqCst);
|
self.reconnecting.store(true, Ordering::SeqCst);
|
||||||
warn!("RTCEngine disconnected unexpectedly, reconnecting...");
|
warn!("RTCEngine disconnected unexpectedly, reconnecting...");
|
||||||
|
|
||||||
|
let mut connected = false;
|
||||||
let mut full_reconnect = false;
|
let mut full_reconnect = false;
|
||||||
for i in 0..RECONNECT_ATTEMPTS {
|
for i in 0..RECONNECT_ATTEMPTS {
|
||||||
if full_reconnect {
|
if full_reconnect {
|
||||||
@@ -267,7 +316,8 @@ impl EngineInner {
|
|||||||
error!("restarting connection failed: {}", err);
|
error!("restarting connection failed: {}", err);
|
||||||
} else {
|
} else {
|
||||||
let _ = self.engine_emitter.send(EngineEvent::Restarted).await;
|
let _ = self.engine_emitter.send(EngineEvent::Restarted).await;
|
||||||
return;
|
connected = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
@@ -282,27 +332,45 @@ impl EngineInner {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = self.engine_emitter.send(EngineEvent::Resumed).await;
|
let _ = self.engine_emitter.send(EngineEvent::Resumed).await;
|
||||||
return;
|
connected = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(RECONNECT_INTERVAL).await;
|
tokio::time::sleep(RECONNECT_INTERVAL).await;
|
||||||
}
|
}
|
||||||
error!("failed to reconnect after {} attemps", RECONNECT_ATTEMPTS);
|
|
||||||
self.reconnecting.store(false, Ordering::SeqCst);
|
self.reconnecting.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
// TODO DISCONNECT
|
if !connected {
|
||||||
|
error!("failed to reconnect after {} attemps", RECONNECT_ATTEMPTS);
|
||||||
|
let _ = self.engine_emitter.send(EngineEvent::Disconnected).await;
|
||||||
|
self.close().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to recover the connection by doing a full reconnect.
|
/// Try to recover the connection by doing a full reconnect.
|
||||||
/// It recreates a new RTCSession
|
/// It recreates a new RTCSession
|
||||||
async fn try_restart_connection(&self) -> EngineResult<()> {
|
async fn try_restart_connection(self: &Arc<Self>) -> EngineResult<()> {
|
||||||
|
let info = self.session_info.lock().clone().unwrap();
|
||||||
self.close().await;
|
self.close().await;
|
||||||
Ok(())
|
self.connect(&info.url, &info.token, info.options).await?;
|
||||||
|
self.running_handle
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.session
|
||||||
|
.wait_pc_connection()
|
||||||
|
.await
|
||||||
|
|
||||||
|
// TODO(theomonnom): Resend SignalClient queue
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to restart the current session
|
/// Try to restart the current session
|
||||||
async fn try_resume_connection(&self) -> EngineResult<()> {
|
async fn try_resume_connection(&self) -> EngineResult<()> {
|
||||||
Ok(())
|
let handle = self.running_handle.read().await;
|
||||||
|
handle.as_ref().unwrap().session.restart().await?;
|
||||||
|
handle.as_ref().unwrap().session.wait_pc_connection().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub type OnOfferHandler = Box<
|
|||||||
+ Sync,
|
+ Sync,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
pub(crate) struct PCTransport {
|
pub struct PCTransport {
|
||||||
signal_target: SignalTarget,
|
signal_target: SignalTarget,
|
||||||
peer_connection: PeerConnection,
|
peer_connection: PeerConnection,
|
||||||
pending_candidates: Vec<IceCandidate>,
|
pending_candidates: Vec<IceCandidate>,
|
||||||
|
|||||||
@@ -28,14 +28,17 @@ pub enum RTCEvent {
|
|||||||
},
|
},
|
||||||
DataChannel {
|
DataChannel {
|
||||||
data_channel: DataChannel,
|
data_channel: DataChannel,
|
||||||
|
target: SignalTarget,
|
||||||
},
|
},
|
||||||
// TODO (theomonnom): Move Offer to PCTransport
|
// TODO (theomonnom): Move Offer to PCTransport
|
||||||
Offer {
|
Offer {
|
||||||
offer: SessionDescription,
|
offer: SessionDescription,
|
||||||
|
target: SignalTarget,
|
||||||
},
|
},
|
||||||
AddTrack {
|
AddTrack {
|
||||||
rtp_receiver: RtpReceiver,
|
rtp_receiver: RtpReceiver,
|
||||||
streams: Vec<MediaStream>,
|
streams: Vec<MediaStream>,
|
||||||
|
target: SignalTarget,
|
||||||
},
|
},
|
||||||
Data {
|
Data {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
@@ -91,28 +94,22 @@ fn on_add_track(target: SignalTarget, emitter: RTCEmitter) -> OnAddTrackHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn forward_pc_events(transport: &mut PCTransport, rtc_emitter: RTCEmitter) {
|
pub fn forward_pc_events(transport: &mut PCTransport, rtc_emitter: RTCEmitter) {
|
||||||
|
let signal_target = transport.signal_target();
|
||||||
transport
|
transport
|
||||||
.peer_connection()
|
.peer_connection()
|
||||||
.on_ice_candidate(on_ice_candidate(
|
.on_ice_candidate(on_ice_candidate(signal_target, rtc_emitter.clone()));
|
||||||
transport.signal_target(),
|
|
||||||
rtc_emitter.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
transport.peer_connection().on_data_channel(on_data_channel(
|
|
||||||
transport.signal_target(),
|
|
||||||
rtc_emitter.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
transport
|
transport
|
||||||
.peer_connection()
|
.peer_connection()
|
||||||
.on_add_track(on_add_track(transport.signal_target(), rtc_emitter.clone()));
|
.on_data_channel(on_data_channel(signal_target, rtc_emitter.clone()));
|
||||||
|
|
||||||
transport
|
transport
|
||||||
.peer_connection()
|
.peer_connection()
|
||||||
.on_connection_change(on_connection_change(
|
.on_add_track(on_add_track(signal_target, rtc_emitter.clone()));
|
||||||
transport.signal_target(),
|
|
||||||
rtc_emitter.clone(),
|
transport
|
||||||
));
|
.peer_connection()
|
||||||
|
.on_connection_change(on_connection_change(signal_target, rtc_emitter.clone()));
|
||||||
|
|
||||||
transport.on_offer(on_offer(transport.signal_target(), rtc_emitter.clone()));
|
transport.on_offer(on_offer(transport.signal_target(), rtc_emitter.clone()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use livekit_webrtc::media_stream::{MediaStream, MediaStreamTrackHandle};
|
use livekit_webrtc::media_stream::{MediaStream, MediaStreamTrackHandle};
|
||||||
use livekit_webrtc::rtp_receiver::RtpReceiver;
|
use livekit_webrtc::rtp_receiver::RtpReceiver;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -65,6 +66,21 @@ pub enum PCState {
|
|||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryInto<PCState> for u8 {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<PCState, Self::Error> {
|
||||||
|
match self {
|
||||||
|
0 => Ok(PCState::New),
|
||||||
|
1 => Ok(PCState::Connected),
|
||||||
|
2 => Ok(PCState::Disconnected),
|
||||||
|
3 => Ok(PCState::Reconnecting),
|
||||||
|
4 => Ok(PCState::Closed),
|
||||||
|
_ => Err("invalid PCState"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
struct IceCandidateJSON {
|
struct IceCandidateJSON {
|
||||||
@@ -236,7 +252,11 @@ impl RTCSession {
|
|||||||
self.inner.publish_data(data, kind).await
|
self.inner.publish_data(data, kind).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_pc_connectiom(&self) -> EngineResult<()> {
|
pub async fn restart(&self) -> EngineResult<()> {
|
||||||
|
self.inner.restart_session().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_pc_connection(&self) -> EngineResult<()> {
|
||||||
self.inner.wait_pc_connection().await
|
self.inner.wait_pc_connection().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,6 +266,14 @@ impl RTCSession {
|
|||||||
&self.inner.info
|
&self.inner.info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> PCState {
|
||||||
|
self.inner
|
||||||
|
.pc_state
|
||||||
|
.load(Ordering::SeqCst)
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn publisher(&self) -> &AsyncMutex<PCTransport> {
|
pub fn publisher(&self) -> &AsyncMutex<PCTransport> {
|
||||||
&self.inner.publisher_pc
|
&self.inner.publisher_pc
|
||||||
}
|
}
|
||||||
@@ -412,10 +440,13 @@ impl SessionInner {
|
|||||||
self.on_session_disconnected("pc_state failed");
|
self.on_session_disconnected("pc_state failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RTCEvent::DataChannel { data_channel } => {
|
RTCEvent::DataChannel {
|
||||||
|
data_channel,
|
||||||
|
target: _,
|
||||||
|
} => {
|
||||||
self.subscriber_dc.lock().push(data_channel);
|
self.subscriber_dc.lock().push(data_channel);
|
||||||
}
|
}
|
||||||
RTCEvent::Offer { offer } => {
|
RTCEvent::Offer { offer, target: _ } => {
|
||||||
// Send the publisher offer to the server
|
// Send the publisher offer to the server
|
||||||
self.signal_client
|
self.signal_client
|
||||||
.send(signal_request::Message::Offer(proto::SessionDescription {
|
.send(signal_request::Message::Offer(proto::SessionDescription {
|
||||||
@@ -427,6 +458,7 @@ impl SessionInner {
|
|||||||
RTCEvent::AddTrack {
|
RTCEvent::AddTrack {
|
||||||
rtp_receiver,
|
rtp_receiver,
|
||||||
mut streams,
|
mut streams,
|
||||||
|
target: _,
|
||||||
} => {
|
} => {
|
||||||
if !streams.is_empty() {
|
if !streams.is_empty() {
|
||||||
let _ = self.emitter.send(SessionEvent::MediaTrack {
|
let _ = self.emitter.send(SessionEvent::MediaTrack {
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ pub enum SignalError {
|
|||||||
|
|
||||||
/// Events used by the RTCEngine who will handle the reconnection logic
|
/// Events used by the RTCEngine who will handle the reconnection logic
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum SignalEvent {
|
pub enum SignalEvent {
|
||||||
Open,
|
Open,
|
||||||
Signal(signal_response::Message),
|
Signal(signal_response::Message),
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct SignalOptions {
|
pub struct SignalOptions {
|
||||||
pub(crate) reconnect: bool,
|
pub(crate) reconnect: bool,
|
||||||
pub(crate) sid: String,
|
pub(crate) sid: String,
|
||||||
pub auto_subscribe: bool,
|
pub auto_subscribe: bool,
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
rustflags = ["-C", "target-feature=+crt-static"]
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["*"]
|
members = ["*"]
|
||||||
exclude = ["target"]
|
exclude = ["target", ".cargo"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
use tracing_subscriber::prelude::*;
|
|
||||||
mod app;
|
mod app;
|
||||||
mod events;
|
mod events;
|
||||||
mod video_grid;
|
mod video_grid;
|
||||||
mod video_renderer;
|
mod video_renderer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let fmt_layer = tracing_subscriber::fmt::Layer::default();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(fmt_layer)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
|
|||||||
Reference in New Issue
Block a user