use bmrng::unbounded::UnboundedRequestSender;
use livekit_common::ParticipantIdentity;
use livekit_protocol as proto;
use std::{collections::HashMap, path::Path};
use tokio::io::AsyncReadExt;
use super::constants;
use crate::{
types::Header,
utils::{SendError, StreamError, StreamProgress, StreamResult},
};
pub(crate) struct RawStreamOpenOptions {
pub(crate) header: Header,
pub(crate) destination_identities: Vec<ParticipantIdentity>,
pub(crate) sender_identity: Option<ParticipantIdentity>,
pub(crate) packet_tx: UnboundedRequestSender<proto::DataPacket, Result<(), SendError>>,
}
pub(crate) struct RawStream {
id: String,
sender_identity: Option<ParticipantIdentity>,
progress: StreamProgress,
is_closed: bool,
packet_tx: UnboundedRequestSender<proto::DataPacket, Result<(), SendError>>,
}
impl RawStream {
pub(crate) async fn open(options: RawStreamOpenOptions) -> StreamResult<Self> {
let id = options.header.stream_id.to_string();
let bytes_total = options.header.total_length;
let mut packet =
Self::create_header_packet(options.header.into(), options.destination_identities);
if let Some(sender_identity) = options.sender_identity.as_ref() {
packet.participant_identity = sender_identity.clone().into();
}
Self::send_packet(&options.packet_tx, packet).await?;
Ok(Self {
id,
sender_identity: options.sender_identity,
progress: StreamProgress { bytes_total, ..Default::default() },
is_closed: false,
packet_tx: options.packet_tx,
})
}
pub(crate) async fn write_chunk(&mut self, bytes: &[u8]) -> StreamResult<()> {
let mut packet = Self::create_chunk_packet(&self.id, self.progress.chunk_index, bytes);
if let Some(sender_identity) = self.sender_identity.as_ref() {
packet.participant_identity = sender_identity.clone().into();
}
Self::send_packet(&self.packet_tx, packet).await?;
self.progress.bytes_processed += bytes.len() as u64;
self.progress.chunk_index += 1;
Ok(())
}
pub(crate) async fn write_raw_chunks(&mut self, bytes: &[u8]) -> StreamResult<()> {
for chunk in bytes.chunks(constants::STREAM_CHUNK_SIZE_BYTES) {
self.write_chunk(chunk).await?;
}
Ok(())
}
pub(crate) async fn write_file(
&mut self,
path: impl AsRef<Path>,
compress: bool,
) -> StreamResult<()> {
let mut file = tokio::fs::File::open(path).await?;
let mut read_buf = vec![0u8; 8192];
if compress {
use futures_util::io::AsyncWriteExt;
let mut encoder = async_compression::futures::write::DeflateEncoder::new(Vec::new());
loop {
let n = file.read(&mut read_buf).await?;
if n == 0 {
break;
}
encoder
.write_all(&read_buf[..n])
.await
.expect("deflate write to Vec is infallible");
while encoder.get_ref().len() >= constants::STREAM_CHUNK_SIZE_BYTES {
let rest = encoder.get_mut().split_off(constants::STREAM_CHUNK_SIZE_BYTES);
let chunk = std::mem::replace(encoder.get_mut(), rest);
self.write_chunk(&chunk).await?;
}
}
encoder.close().await.expect("deflate finish into Vec is infallible");
let remaining = encoder.into_inner();
self.write_raw_chunks(&remaining).await?;
} else {
let mut pending: Vec<u8> = Vec::new();
loop {
let n = file.read(&mut read_buf).await?;
if n == 0 {
break;
}
pending.extend_from_slice(&read_buf[..n]);
while pending.len() >= constants::STREAM_CHUNK_SIZE_BYTES {
let rest = pending.split_off(constants::STREAM_CHUNK_SIZE_BYTES);
let chunk = std::mem::replace(&mut pending, rest);
self.write_chunk(&chunk).await?;
}
}
if !pending.is_empty() {
self.write_chunk(&pending).await?;
}
}
Ok(())
}
pub(crate) async fn close(
&mut self,
reason: Option<&str>,
attributes: Option<HashMap<String, String>>,
) -> StreamResult<()> {
if self.is_closed {
Err(StreamError::AlreadyClosed)?
}
let mut packet = Self::create_trailer_packet(&self.id, reason, attributes);
if let Some(sender_identity) = self.sender_identity.as_ref() {
packet.participant_identity = sender_identity.clone().into();
}
Self::send_packet(&self.packet_tx, packet).await?;
self.is_closed = true;
Ok(())
}
pub(crate) async fn send_packet(
tx: &UnboundedRequestSender<proto::DataPacket, Result<(), SendError>>,
packet: proto::DataPacket,
) -> StreamResult<()> {
tx.send_receive(packet)
.await
.map_err(|_| StreamError::Internal)? .map_err(|_| StreamError::SendFailed) }
pub(crate) fn create_header_packet(
header: proto::data_stream::Header,
destination_identities: Vec<ParticipantIdentity>,
) -> proto::DataPacket {
proto::DataPacket {
kind: proto::data_packet::Kind::Reliable.into(),
participant_identity: String::new(), destination_identities: destination_identities.into_iter().map(|id| id.0).collect(),
value: Some(livekit_protocol::data_packet::Value::StreamHeader(header.into())),
..Default::default()
}
}
pub(crate) fn create_chunk_packet(
id: &str,
chunk_index: u64,
content: &[u8],
) -> proto::DataPacket {
let chunk = proto::data_stream::Chunk {
stream_id: id.to_string(),
chunk_index,
content: content.to_vec(),
..Default::default()
};
proto::DataPacket {
kind: proto::data_packet::Kind::Reliable.into(),
participant_identity: String::new(), value: Some(livekit_protocol::data_packet::Value::StreamChunk(chunk)),
..Default::default()
}
}
pub(crate) fn create_trailer_packet(
id: &str,
reason: Option<&str>,
attributes: Option<HashMap<String, String>>,
) -> proto::DataPacket {
let trailer = proto::data_stream::Trailer {
stream_id: id.to_string(),
reason: reason.unwrap_or_default().to_owned(),
attributes: attributes.unwrap_or_default(),
..Default::default()
};
proto::DataPacket {
kind: proto::data_packet::Kind::Reliable.into(),
participant_identity: String::new(), value: Some(livekit_protocol::data_packet::Value::StreamTrailer(trailer)),
..Default::default()
}
}
}
impl Drop for RawStream {
fn drop(&mut self) {
if self.is_closed {
return;
}
let mut packet = Self::create_trailer_packet(&self.id, None, None);
if let Some(sender_identity) = self.sender_identity.as_ref() {
packet.participant_identity = sender_identity.clone().into();
}
let packet_tx = self.packet_tx.clone();
if let Ok(handle) = tokio::runtime::Handle::try_current() {
handle.spawn(async move { Self::send_packet(&packet_tx, packet).await });
}
}
}