use std::sync::Arc;
use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use webrtc::media_stream::track_local::static_sample::TrackLocalStaticSample;
use webrtc::media_stream::track_remote::TrackRemote;
pub use webrtc::media_stream::track_remote::TrackRemoteEvent;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct TrackId(pub String);
impl TrackId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn new_random() -> Self {
Self(Uuid::new_v4().to_string())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for TrackId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum TrackDirection {
SendOnly,
RecvOnly,
SendRecv,
Inactive,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub enum DataChannelMessage {
Text(String),
Binary(Vec<u8>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataChannelConfig {
pub label: String,
pub ordered: bool,
pub max_retransmits: Option<u16>,
pub protocol: Option<String>,
}
impl Default for DataChannelConfig {
fn default() -> Self {
Self {
label: "data".to_string(),
ordered: true,
max_retransmits: None,
protocol: None,
}
}
}
pub struct DataChannel {
pub id: u16,
pub label: String,
inner: Arc<dyn webrtc::data_channel::DataChannel>,
}
impl DataChannel {
pub(crate) async fn new(inner: Arc<dyn webrtc::data_channel::DataChannel>) -> Result<Self> {
let label = inner.label().await.map_err(|e| anyhow::anyhow!("{e}"))?;
let id = inner.id();
Ok(Self { id, label, inner })
}
pub async fn receive(&self) -> Option<DataChannelMessage> {
use webrtc::data_channel::DataChannelEvent;
loop {
match self.inner.poll().await {
Some(DataChannelEvent::OnMessage(msg)) => {
return Some(if msg.is_string {
DataChannelMessage::Text(String::from_utf8_lossy(&msg.data).into_owned())
} else {
DataChannelMessage::Binary(msg.data.to_vec())
});
}
Some(DataChannelEvent::OnClose) | None => return None,
_ => continue,
}
}
}
pub async fn send_text(&self, text: &str) -> Result<()> {
self.inner
.send_text(text)
.await
.map_err(|e| anyhow::anyhow!("{e}"))
}
pub async fn send_bytes(&self, data: &[u8]) -> Result<()> {
use bytes::BytesMut;
self.inner
.send(BytesMut::from(data))
.await
.map_err(|e| anyhow::anyhow!("{e}"))
}
pub async fn send(&self, msg: &DataChannelMessage) -> Result<()> {
match msg {
DataChannelMessage::Text(s) => self.send_text(s).await,
DataChannelMessage::Binary(b) => self.send_bytes(b).await,
}
}
pub async fn close(&self) -> Result<()> {
self.inner.close().await.map_err(|e| anyhow::anyhow!("{e}"))
}
}
pub struct AudioTrack {
pub id: TrackId,
pub direction: TrackDirection,
pub(crate) ssrc: u32,
pub(crate) inner: Arc<TrackLocalStaticSample>,
}
impl AudioTrack {
pub async fn write_sample(&self, sample: &rtc::media::Sample) -> Result<()> {
self.inner
.write_sample(self.ssrc, sample, &[])
.await
.map_err(|e| anyhow::anyhow!("{e}"))
}
}
pub struct VideoTrack {
pub id: TrackId,
pub direction: TrackDirection,
pub(crate) ssrc: u32,
pub(crate) inner: Arc<TrackLocalStaticSample>,
}
impl VideoTrack {
pub async fn write_sample(&self, sample: &rtc::media::Sample) -> Result<()> {
self.inner
.write_sample(self.ssrc, sample, &[])
.await
.map_err(|e| anyhow::anyhow!("{e}"))
}
}
pub struct RemoteTrack {
pub id: TrackId,
pub kind: String,
pub codec: Option<String>,
pub(crate) inner: Arc<dyn TrackRemote>,
}
impl RemoteTrack {
pub(crate) fn new(
id: TrackId,
kind: String,
codec: Option<String>,
inner: Arc<dyn TrackRemote>,
) -> Self {
Self {
id,
kind,
codec,
inner,
}
}
pub async fn poll(&self) -> Option<TrackRemoteEvent> {
self.inner.poll().await
}
}
pub enum MediaTrack {
Audio(AudioTrack),
Video(VideoTrack),
}
impl MediaTrack {
pub fn id(&self) -> &TrackId {
match self {
MediaTrack::Audio(t) => &t.id,
MediaTrack::Video(t) => &t.id,
}
}
pub fn direction(&self) -> TrackDirection {
match self {
MediaTrack::Audio(t) => t.direction,
MediaTrack::Video(t) => t.direction,
}
}
pub fn is_audio(&self) -> bool {
matches!(self, MediaTrack::Audio(_))
}
pub fn is_video(&self) -> bool {
matches!(self, MediaTrack::Video(_))
}
}