init
This commit is contained in:
@@ -18,5 +18,7 @@ pub async fn ws_handler(
|
|||||||
.get_by_id(channel_id)
|
.get_by_id(channel_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or(HTTPError::NotFound)?;
|
.ok_or(HTTPError::NotFound)?;
|
||||||
|
// todo : check des permissions
|
||||||
|
// aiinsi que vérifier qu'on est sur un chan vocal OU un chan de message privé
|
||||||
Ok(ws.on_upgrade(move |socket| ws_entrypoint_handler(socket, state, channel, user)))
|
Ok(ws.on_upgrade(move |socket| ws_entrypoint_handler(socket, state, channel, user)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
### Step 1 - handshake WebSocket
|
||||||
|
|
||||||
|
Le client doit effectuer une connexion HTTP (websocket) sur /rtc/{channel_id}
|
||||||
|
Le server vérifiera avant d'accepter la connexion si :
|
||||||
|
|
||||||
|
- Le channel existe
|
||||||
|
- Vérifiera les permissions sur le canal (check_perm ou si c'est un canal privé)
|
||||||
|
|
||||||
|
### Step 2 - SDP (négociation du type de media)
|
||||||
|
|
||||||
|
### Step 3 - ICE (Négociation du flux réseaux)
|
||||||
+25
-4
@@ -1,3 +1,4 @@
|
|||||||
|
use super::messages::VoiceClientMessage;
|
||||||
use crate::models::{channel, user};
|
use crate::models::{channel, user};
|
||||||
use axum::extract::ws::Message;
|
use axum::extract::ws::Message;
|
||||||
use rustrtc::peer_connection::PeerConnection;
|
use rustrtc::peer_connection::PeerConnection;
|
||||||
@@ -7,8 +8,8 @@ use tokio::sync::mpsc::UnboundedSender;
|
|||||||
/// Client connecté à un canal RTC.
|
/// Client connecté à un canal RTC.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RtcClient {
|
pub struct RtcClient {
|
||||||
pub user: user::Model,
|
pub user: Arc<user::Model>,
|
||||||
pub channel: channel::Model,
|
pub channel: Arc<channel::Model>,
|
||||||
pub peer_connection: Arc<PeerConnection>,
|
pub peer_connection: Arc<PeerConnection>,
|
||||||
pub websocket_sender: UnboundedSender<Message>,
|
pub websocket_sender: UnboundedSender<Message>,
|
||||||
}
|
}
|
||||||
@@ -21,13 +22,33 @@ impl RtcClient {
|
|||||||
websocket_sender: UnboundedSender<Message>,
|
websocket_sender: UnboundedSender<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user,
|
user: Arc::new(user),
|
||||||
channel,
|
channel: Arc::new(channel),
|
||||||
peer_connection,
|
peer_connection,
|
||||||
websocket_sender,
|
websocket_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_message(&self, raw_message: Message) {
|
||||||
|
let Message::Text(raw_message) = raw_message else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed = match serde_json::from_str::<VoiceClientMessage>(&raw_message) {
|
||||||
|
Ok(message) => message,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
match parsed {
|
||||||
|
VoiceClientMessage::SDPOffer { channel_id, sdp } => {}
|
||||||
|
VoiceClientMessage::IceCandidate {
|
||||||
|
channel_id,
|
||||||
|
candidate,
|
||||||
|
} => {}
|
||||||
|
VoiceClientMessage::Leave { channel_id } => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Ferme proprement la connexion WebRTC.
|
/// Ferme proprement la connexion WebRTC.
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
self.peer_connection.close();
|
self.peer_connection.close();
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use crate::config::NetworkConfig;
|
||||||
|
use rustrtc::{RtcConfiguration, RtcConfigurationBuilder};
|
||||||
|
|
||||||
|
// 1. Client crée une RTCPeerConnection
|
||||||
|
// 2. Client crée une SDP offer
|
||||||
|
// 3. Client envoie l'offer via WebSocket
|
||||||
|
// 4. Serveur crée sa PeerConnection
|
||||||
|
// 5. Serveur applique l'offer
|
||||||
|
// 6. Serveur crée une SDP answer
|
||||||
|
// 7. Serveur renvoie l'answer via WebSocket
|
||||||
|
// 8. Client et serveur échangent les candidats ICE
|
||||||
|
// 9. ICE sélectionne un chemin réseau
|
||||||
|
// 10. La connexion WebRTC devient active
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct RTCManager {
|
||||||
|
pub config: RtcConfiguration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RTCManager {
|
||||||
|
pub fn new(network: &NetworkConfig) -> Self {
|
||||||
|
let builder = RtcConfigurationBuilder::new()
|
||||||
|
.ice_udp_mux(true)
|
||||||
|
.ice_udp_mux_port(network.udp_port)
|
||||||
|
.bind_ip(network.host.to_string());
|
||||||
|
let config = builder.build();
|
||||||
|
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Permet de se mettre d'accord sur les paramètres media
|
||||||
|
pub async fn handle_sdp_offer(&self) {}
|
||||||
|
|
||||||
|
/// Interactive Connectivity Establishment - Permet de négocier un chemin réseau
|
||||||
|
pub async fn handle_ice_candidate(&self) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "action", rename_all = "kebab-case")]
|
||||||
|
pub enum VoiceClientMessage {
|
||||||
|
SDPOffer { channel_id: Uuid, sdp: String },
|
||||||
|
IceCandidate { channel_id: Uuid, candidate: String },
|
||||||
|
Leave { channel_id: Uuid },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(tag = "action", rename_all = "kebab-case")]
|
||||||
|
pub enum VoiceServerMessage {
|
||||||
|
Answer { channel_id: Uuid, sdp: String },
|
||||||
|
IceCandidate { channel_id: Uuid, candidate: String },
|
||||||
|
Error { message: String },
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
mod client;
|
mod client;
|
||||||
|
mod manager;
|
||||||
|
mod messages;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
pub mod ws_entrypoint;
|
pub mod ws_entrypoint;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,23 @@ use crate::core::AppState;
|
|||||||
use crate::models::{channel, user};
|
use crate::models::{channel, user};
|
||||||
use axum::extract::ws::{Message, WebSocket};
|
use axum::extract::ws::{Message, WebSocket};
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use rustrtc::PeerConnection;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
// 1. Le frontend ouvre /rtc/{channel_id}
|
||||||
|
// 2. Axum extrait channel_id
|
||||||
|
// 3. CurrentUser identifie l'utilisateur
|
||||||
|
// 4. Le serveur vérifie le canal et les permissions
|
||||||
|
// 5. Le serveur accepte le WebSocket
|
||||||
|
// 6. Le client envoie son SDP Offer
|
||||||
|
// 7. Le serveur crée la PeerConnection
|
||||||
|
// 8. Le serveur applique l'Offer
|
||||||
|
// 9. Le serveur crée une SDP Answer
|
||||||
|
// 10. Le serveur renvoie l'Answer
|
||||||
|
// 11. Les candidats ICE sont échangés
|
||||||
|
// 12. À la déconnexion, la PeerConnection est fermée
|
||||||
|
|
||||||
pub async fn ws_entrypoint_handler(
|
pub async fn ws_entrypoint_handler(
|
||||||
socket: WebSocket,
|
socket: WebSocket,
|
||||||
state: AppState,
|
state: AppState,
|
||||||
@@ -15,7 +30,8 @@ pub async fn ws_entrypoint_handler(
|
|||||||
) {
|
) {
|
||||||
let (mut sender, mut receiver) = socket.split();
|
let (mut sender, mut receiver) = socket.split();
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel::<Message>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<Message>();
|
||||||
// todo : faire le peer connexion.
|
|
||||||
|
let peer_connection = Arc::new(PeerConnection::new(state.rtc.config.clone()));
|
||||||
let mut rtc_client = RtcClient::new(user, channel, peer_connection, tx);
|
let mut rtc_client = RtcClient::new(user, channel, peer_connection, tx);
|
||||||
|
|
||||||
let send_task = tokio::spawn(async move {
|
let send_task = tokio::spawn(async move {
|
||||||
|
|||||||
Reference in New Issue
Block a user