1#![doc = include_str!("../README.md")]
16
17use chrono::{DateTime, Utc};
18use livekit_common::EncryptionType;
19use livekit_protocol::data_stream as proto;
20use std::collections::HashMap;
21use thiserror::Error;
22
23mod incoming;
24mod outgoing;
25mod utf8_chunk;
26
27pub use incoming::{
28 AnyStreamReader, ByteStreamReader, IncomingStreamManager, StreamReader, TextStreamReader,
29};
30pub use outgoing::{
31 ByteStreamWriter, OutgoingStreamManager, StreamByteOptions, StreamTextOptions, StreamWriter,
32 TextStreamWriter,
33};
34
35#[derive(Debug, Clone)]
41pub struct SendError;
42
43pub(crate) fn create_random_uuid() -> String {
45 uuid::Uuid::new_v4().to_string()
46}
47
48pub type StreamResult<T> = Result<T, StreamError>;
50
51#[derive(Debug, Error)]
53pub enum StreamError {
54 #[error("stream has already been closed")]
56 AlreadyClosed,
57
58 #[error("stream closed abnormally: {0}")]
59 AbnormalEnd(String),
60
61 #[error("UTF-8 decoding error: {0}")]
62 Utf8(#[from] std::string::FromUtf8Error),
63
64 #[error("incoming header was invalid")]
65 InvalidHeader,
66
67 #[error("expected chunk index to be exactly one more than the previous")]
68 MissedChunk,
69
70 #[error("read length exceeded total length specified in stream header")]
71 LengthExceeded,
72
73 #[error("stream data is incomplete")]
74 Incomplete,
75
76 #[error("unable to send packet")]
77 SendFailed,
78
79 #[error("I/O error: {0}")]
80 Io(#[from] std::io::Error),
81
82 #[error("internal error")]
83 Internal,
84
85 #[error("encryption type mismatch")]
86 EncryptionTypeMismatch,
87}
88
89#[derive(Clone, Copy, Default, Debug, Hash, Eq, PartialEq)]
91struct StreamProgress {
92 chunk_index: u64,
93 bytes_processed: u64,
95 bytes_total: Option<u64>,
97}
98
99impl StreamProgress {
100 #[allow(dead_code)]
102 fn percentage(&self) -> Option<f32> {
103 self.bytes_total.map(|total| self.bytes_processed as f32 / total as f32)
104 }
105}
106
107#[derive(Clone, Debug)]
109pub struct ByteStreamInfo {
110 pub id: String,
112 pub topic: String,
114 pub timestamp: DateTime<Utc>,
116 pub total_length: Option<u64>,
118 pub attributes: HashMap<String, String>,
120 pub mime_type: String,
122 pub name: String,
124 pub encryption_type: EncryptionType,
126}
127
128#[derive(Clone, Debug)]
130pub struct TextStreamInfo {
131 pub id: String,
133 pub topic: String,
135 pub timestamp: DateTime<Utc>,
137 pub total_length: Option<u64>,
139 pub attributes: HashMap<String, String>,
141 pub mime_type: String,
143 pub operation_type: OperationType,
144 pub version: i32,
145 pub reply_to_stream_id: Option<String>,
146 pub attached_stream_ids: Vec<String>,
147 pub generated: bool,
148 pub encryption_type: EncryptionType,
150}
151
152#[derive(Clone, Copy, Default, Debug, Hash, Eq, PartialEq)]
154pub enum OperationType {
155 #[default]
156 Create,
157 Update,
158 Delete,
159 Reaction,
160}
161
162impl TryFrom<proto::Header> for AnyStreamInfo {
165 type Error = StreamError;
166
167 fn try_from(mut header: proto::Header) -> Result<Self, Self::Error> {
168 Self::try_from_with_encryption(header, EncryptionType::None)
169 }
170}
171
172impl AnyStreamInfo {
173 pub fn try_from_with_encryption(
174 mut header: proto::Header,
175 encryption_type: EncryptionType,
176 ) -> Result<Self, StreamError> {
177 let Some(content_header) = header.content_header.take() else {
178 Err(StreamError::InvalidHeader)?
179 };
180 let info = match content_header {
181 proto::header::ContentHeader::ByteHeader(byte_header) => Self::Byte(
182 ByteStreamInfo::from_headers_with_encryption(header, byte_header, encryption_type),
183 ),
184 proto::header::ContentHeader::TextHeader(text_header) => Self::Text(
185 TextStreamInfo::from_headers_with_encryption(header, text_header, encryption_type),
186 ),
187 };
188 Ok(info)
189 }
190}
191
192impl ByteStreamInfo {
193 pub(crate) fn from_headers(header: proto::Header, byte_header: proto::ByteHeader) -> Self {
194 Self::from_headers_with_encryption(header, byte_header, EncryptionType::None)
195 }
196
197 pub(crate) fn from_headers_with_encryption(
198 header: proto::Header,
199 byte_header: proto::ByteHeader,
200 encryption_type: EncryptionType,
201 ) -> Self {
202 Self {
203 id: header.stream_id,
204 topic: header.topic,
205 timestamp: DateTime::<Utc>::from_timestamp_millis(header.timestamp)
206 .unwrap_or_else(|| Utc::now()),
207 total_length: header.total_length,
208 attributes: header.attributes,
209 mime_type: header.mime_type,
210 name: byte_header.name,
211 encryption_type,
212 }
213 }
214}
215
216impl TextStreamInfo {
217 pub(crate) fn from_headers(header: proto::Header, text_header: proto::TextHeader) -> Self {
218 Self::from_headers_with_encryption(header, text_header, EncryptionType::None)
219 }
220
221 pub(crate) fn from_headers_with_encryption(
222 header: proto::Header,
223 text_header: proto::TextHeader,
224 encryption_type: EncryptionType,
225 ) -> Self {
226 Self {
227 id: header.stream_id,
228 topic: header.topic,
229 timestamp: DateTime::<Utc>::from_timestamp_millis(header.timestamp)
230 .unwrap_or_else(|| Utc::now()),
231 total_length: header.total_length,
232 attributes: header.attributes,
233 mime_type: header.mime_type,
234 operation_type: text_header.operation_type().into(),
235 version: text_header.version,
236 reply_to_stream_id: (!text_header.reply_to_stream_id.is_empty())
237 .then_some(text_header.reply_to_stream_id),
238 attached_stream_ids: text_header.attached_stream_ids,
239 generated: text_header.generated,
240 encryption_type,
241 }
242 }
243}
244
245impl From<proto::OperationType> for OperationType {
246 fn from(op_type: proto::OperationType) -> Self {
247 match op_type {
248 proto::OperationType::Create => OperationType::Create,
249 proto::OperationType::Update => OperationType::Update,
250 proto::OperationType::Delete => OperationType::Delete,
251 proto::OperationType::Reaction => OperationType::Reaction,
252 }
253 }
254}
255#[derive(Clone, Debug)]
258pub(crate) enum AnyStreamInfo {
259 Byte(ByteStreamInfo),
260 Text(TextStreamInfo),
261}
262
263impl AnyStreamInfo {
264 livekit_common::enum_dispatch!(
265 [Byte, Text];
266 pub fn id(self: &Self) -> &str;
267 pub fn total_length(self: &Self) -> Option<u64>;
268 pub fn encryption_type(self: &Self) -> EncryptionType;
269 );
270}
271
272#[rustfmt::skip]
273macro_rules! stream_info {
274 () => {
275 fn id(&self) -> &str { &self.id }
276 fn total_length(&self) -> Option<u64> { self.total_length }
277 fn encryption_type(&self) -> EncryptionType { self.encryption_type }
278 };
279}
280
281impl ByteStreamInfo {
282 stream_info!();
283}
284
285impl TextStreamInfo {
286 stream_info!();
287}
288
289impl From<ByteStreamInfo> for AnyStreamInfo {
290 fn from(info: ByteStreamInfo) -> Self {
291 Self::Byte(info)
292 }
293}
294
295impl From<TextStreamInfo> for AnyStreamInfo {
296 fn from(info: TextStreamInfo) -> Self {
297 Self::Text(info)
298 }
299}