Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2e097f0
feat: commit initial data streams v2 implementation
1egoman Jun 26, 2026
f5beb69
fix: Create data_streams_v2.md
1egoman Jun 26, 2026
8054338
fix: remove ClientCapability from prelude
1egoman Jul 1, 2026
bddbe06
feat: allow data stream compress option to be toggled via livekit-ffi
1egoman Jul 1, 2026
e988375
fix: run cargo fmt
1egoman Jul 1, 2026
8fa2d11
generated protobuf
github-actions[bot] Jul 1, 2026
57aa3b0
fix: address toc-tou issue with decompressor state
1egoman Jul 1, 2026
0bb0e8d
fix: cleanup data stream v2 test fixture initialization
1egoman Jul 1, 2026
b2012ad
feat: make client capabilities proto deserialization use TryFrom
1egoman Jul 1, 2026
c052324
fix: run cargo fmt
1egoman Jul 1, 2026
c893534
fix: remove dead code
1egoman Jul 1, 2026
79ab33c
fix: bump protocol version to try to fix ci build error
1egoman Jul 1, 2026
479ac1e
generated protobuf
github-actions[bot] Jul 1, 2026
7c84909
fix: downgrade protocol to before data track schema metadata
1egoman Jul 1, 2026
232a3bb
refactor: convert data stream open tuple to struct and add is_compres…
1egoman Jul 1, 2026
e2f8370
feat: add is_compressed / is_inline to ByteStreamInfo / TextStreamInfo
1egoman Jul 1, 2026
07166a5
Revert "refactor: convert data stream open tuple to struct and add is…
1egoman Jul 1, 2026
f819cfa
feat: explicitly check is_compressed / is_inline in e2e tests
1egoman Jul 1, 2026
502986a
fix: add e2e test that tests compress=false path
1egoman Jul 1, 2026
ad3d729
feat: add e2e feature on compressed / inline fields
1egoman Jul 1, 2026
d873c1b
feat: get random bytes e2e test to pass by using much higher quality …
1egoman Jul 1, 2026
816a550
refactor: move RemoteParticipantRegistry to participant/registry.rs
1egoman Jul 2, 2026
c805709
refactor: restructure send_text / send_bytes
1egoman Jul 2, 2026
860b7f2
feat: throw error when remove participant disconnects halfway through…
1egoman Jul 2, 2026
7a3be4a
fix: adjust send_text / send_bytes to make the disambiguation between…
1egoman Jul 2, 2026
3c360a2
fix: clean up incoming stream manager
1egoman Jul 2, 2026
12b32a6
fix: preload buffer length
1egoman Jul 2, 2026
4459bd3
fix: add reason labels on e2e test assertions
1egoman Jul 2, 2026
ef4bb78
feat: add initial livekit-common implementation
1egoman Jul 2, 2026
e638e56
refactor: depend on livekit_common::RemoteParticipantRegistry
1egoman Jul 2, 2026
02506cb
fix: remove orphaned take_if_raw method
1egoman Jul 2, 2026
a2a93eb
refactor: update more livekit crate references to point to livekit-co…
1egoman Jul 2, 2026
e713af5
fix: remove serde from livekit-common
1egoman Jul 2, 2026
a74b901
refactor: move data stream code out into new livekit-data-stream crate
1egoman Jul 6, 2026
055879b
fix: remove arc wrapping around reserved_topics
1egoman Jul 6, 2026
f9aa1c6
refactor: rename is_topic_internal -> is_internal_topic, is_stream_in…
1egoman Jul 6, 2026
908d7b7
refactor: split up livekit-data-stream crate into multiple smaller mo…
1egoman Jul 6, 2026
fda208b
fix: run cargo fmt
1egoman Jul 6, 2026
d9e9bb3
fix: add missing OperationType reexport
1egoman Jul 6, 2026
5de595b
fix: adjust comments and docs
1egoman Jul 6, 2026
daa69d3
refactor: make pseudo_random_text easier to understand
1egoman Jul 6, 2026
d6deda8
fix: run cargo fmt
1egoman Jul 6, 2026
042cb7e
fix: drop recv_reader
1egoman Jul 6, 2026
cf353d4
fix: add MaybeCompressed docstring
1egoman Jul 6, 2026
1830493
refactor: reformat somewhat_compressible to use rand
1egoman Jul 6, 2026
15c2d7b
docs: update data streams readme
1egoman Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/data_streams_v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
livekit: patch
livekit-api: patch
livekit-datatrack: patch
livekit-ffi: patch
livekit-protocol: patch
livekit-uniffi: patch
---

Add data streams v2 - #1192 (@1egoman)
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ members = [
"livekit",
"livekit-api",
"livekit-protocol",
"livekit-common",
"livekit-data-stream",
"livekit-ffi",
"livekit-uniffi",
"livekit-datatrack",
Expand Down Expand Up @@ -51,6 +53,8 @@ livekit = { version = "0.7.50", path = "livekit" }
livekit-api = { version = "0.5.4", path = "livekit-api" }
livekit-ffi = { version = "0.12.68", path = "livekit-ffi" }
livekit-datatrack = { version = "0.1.9", path = "livekit-datatrack" }
livekit-common = { version = "0.1.0", path = "livekit-common" }
livekit-data-stream = { version = "0.1.0", path = "livekit-data-stream" }
livekit-protocol = { version = "0.7.10", path = "livekit-protocol" }
livekit-runtime = { version = "0.4.0", path = "livekit-runtime" }
soxr-sys = { version = "0.1.3", path = "soxr-sys" }
Expand Down
1 change: 1 addition & 0 deletions livekit-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ __rustls-tls = ["tokio-tungstenite?/__rustls-tls", "reqwest?/__rustls"]

[dependencies]
livekit-protocol = { workspace = true }
livekit-common = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true, features = ["derive"] }
sha2 = "0.10"
Expand Down
21 changes: 12 additions & 9 deletions livekit-api/src/signal_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,21 @@ const VALIDATE_TIMEOUT: Duration = Duration::from_secs(3);
pub const PROTOCOL_VERSION: u32 = 17;

/// Capabilities the Rust SDK advertises to the SFU at connect time.
const CLIENT_CAPABILITIES: &[proto::client_info::Capability] =
&[proto::client_info::Capability::CapPacketTrailer];

/// Default value for `ClientInfo.client_protocol` when a participant has not
/// advertised one (treat as v1-only / no data-stream RPC support).
pub const CLIENT_PROTOCOL_DEFAULT: i32 = 0;
/// `ClientInfo.client_protocol` value indicating support for RPC v2 over data streams.
pub const CLIENT_PROTOCOL_DATA_STREAM_RPC: i32 = 1;
///
/// `CapCompressionDeflateRaw` is always advertised because the SDK's deflate-raw codec
/// (flate2/miniz_oxide) is pure-Rust and compiled in unconditionally.
const CLIENT_CAPABILITIES: &[proto::client_info::Capability] = &[
proto::client_info::Capability::CapPacketTrailer,
proto::client_info::Capability::CapCompressionDeflateRaw,
];

pub use livekit_common::{
CLIENT_PROTOCOL_DATA_STREAM_RPC, CLIENT_PROTOCOL_DATA_STREAM_V2, CLIENT_PROTOCOL_DEFAULT,
};

/// The client protocol which is sent to other clients and indicates the set of apis that other
/// clients should assume this client supports.
const CLIENT_PROTOCOL_VERSION: i32 = CLIENT_PROTOCOL_DATA_STREAM_RPC;
const CLIENT_PROTOCOL_VERSION: i32 = CLIENT_PROTOCOL_DATA_STREAM_V2;

#[derive(Error, Debug)]
pub enum SignalError {
Expand Down
11 changes: 11 additions & 0 deletions livekit-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "livekit-common"
description = "Common foundational types shared across LiveKit crates"
version = "0.1.0"
readme = "README.md"
license.workspace = true
edition.workspace = true
repository.workspace = true

[dependencies]
livekit-protocol = { workspace = true }
6 changes: 6 additions & 0 deletions livekit-common/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# LiveKit Common

An internal crate which holds shared data structures that many downstream modules all use, like
`ParticipantIdentity` or `ClientCapability`.

To build applications with LiveKit, please use the public APIs provided by the [livekit](../livekit) crate.
176 changes: 176 additions & 0 deletions livekit-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Copyright 2026 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Foundational types shared across LiveKit crates: participant identities, the
//! encryption/capability enums, client-protocol constants, and the remote-participant
//! registry trait consulted by the data-stream and RPC send paths.

use std::fmt::Display;

use livekit_protocol as proto;

// -------------------------------------------------------------------------------------------------
// Client protocol
// -------------------------------------------------------------------------------------------------

/// Legacy client. No v2 data-stream features.
pub const CLIENT_PROTOCOL_DEFAULT: i32 = 0;

/// RPC v2 (see RPC spec). No v2 data-stream features.
pub const CLIENT_PROTOCOL_DATA_STREAM_RPC: i32 = 1;

/// Understands inline single-packet data streams (data streams v2).
pub const CLIENT_PROTOCOL_DATA_STREAM_V2: i32 = 2;

// -------------------------------------------------------------------------------------------------
// ParticipantIdentity
// -------------------------------------------------------------------------------------------------

#[derive(Clone, Default, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ParticipantIdentity(pub String);

impl From<String> for ParticipantIdentity {
fn from(value: String) -> Self {
Self(value)
}
}

impl From<&str> for ParticipantIdentity {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}

impl From<ParticipantIdentity> for String {
fn from(value: ParticipantIdentity) -> Self {
value.0
}
}

impl Display for ParticipantIdentity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl ParticipantIdentity {
pub fn as_str(&self) -> &str {
&self.0
}
}

// -------------------------------------------------------------------------------------------------
// EncryptionType
// -------------------------------------------------------------------------------------------------

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncryptionType {
#[default]
None,
Gcm,
Custom,
}

impl From<proto::encryption::Type> for EncryptionType {
fn from(value: proto::encryption::Type) -> Self {
match value {
proto::encryption::Type::None => Self::None,
proto::encryption::Type::Gcm => Self::Gcm,
proto::encryption::Type::Custom => Self::Custom,
}
}
}

impl From<EncryptionType> for proto::encryption::Type {
fn from(value: EncryptionType) -> Self {
match value {
EncryptionType::None => Self::None,
EncryptionType::Gcm => Self::Gcm,
EncryptionType::Custom => Self::Custom,
}
}
}

impl From<EncryptionType> for i32 {
fn from(value: EncryptionType) -> Self {
match value {
EncryptionType::None => 0,
EncryptionType::Gcm => 1,
EncryptionType::Custom => 2,
}
}
}

// -------------------------------------------------------------------------------------------------
// ClientCapability
// -------------------------------------------------------------------------------------------------

/// A capability a participant's client advertises, mirroring the `ClientInfo.Capability` protobuf
/// enum.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
pub enum ClientCapability {
Unused,
PacketTrailer,
CompressionDeflateRaw,
}

impl TryFrom<i32> for ClientCapability {
type Error = &'static str;

fn try_from(value: i32) -> Result<Self, Self::Error> {
match proto::client_info::Capability::try_from(value) {
Ok(proto::client_info::Capability::CapPacketTrailer) => Ok(Self::PacketTrailer),
Ok(proto::client_info::Capability::CapCompressionDeflateRaw) => {
Ok(Self::CompressionDeflateRaw)
}
Ok(proto::client_info::Capability::CapUnused) => Ok(Self::Unused),
Err(_) => Err("unknown client capability"),
}
}
}

impl From<ClientCapability> for i32 {
fn from(value: ClientCapability) -> Self {
match value {
ClientCapability::Unused => proto::client_info::Capability::CapUnused as i32,
ClientCapability::PacketTrailer => {
proto::client_info::Capability::CapPacketTrailer as i32
}
ClientCapability::CompressionDeflateRaw => {
proto::client_info::Capability::CapCompressionDeflateRaw as i32
}
}
}
}

// -------------------------------------------------------------------------------------------------
// RemoteParticipantRegistry
// -------------------------------------------------------------------------------------------------

/// Read access to remote participants' advertised protocol and capabilities.
///
/// Used by downstream modules like the the RPC transport (v1/v2 transport selection) and
/// the data-stream send path (inline / compression eligibility) to determine what level of support
/// a participant has for protocol level features.
pub trait RemoteParticipantRegistry: Send + Sync {
/// A remote participant's `client_protocol`, or `CLIENT_PROTOCOL_DEFAULT` (0) if unknown.
fn remote_client_protocol(&self, identity: &ParticipantIdentity) -> i32;

/// A remote participant's advertised capabilities, or empty if unknown.
fn remote_capabilities(&self, identity: &ParticipantIdentity) -> Vec<ClientCapability>;

/// The identities of every remote participant, used to resolve a broadcast send.
fn remote_identities(&self) -> Vec<ParticipantIdentity>;
}
34 changes: 34 additions & 0 deletions livekit-data-stream/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "livekit-data-stream"
description = "Data stream core logic for LiveKit"
version = "0.1.0"
readme = "README.md"
license.workspace = true
edition.workspace = true
repository.workspace = true

[features]
# End-to-end testing hooks (exposes is_compressed/is_inline on stream info). Forwarded from
# the `livekit` crate's `__lk-e2e-test` feature.
__e2e-test = []
# Exposes constructors used by downstream crates' test suites (e.g. `TextStreamReader::new_for_test`).
test-utils = []
Comment on lines +10 to +15

@1egoman 1egoman Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers: should these be separate features or just one?


[dependencies]
livekit-common = { workspace = true }
livekit-protocol = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
parking_lot = { workspace = true }
bytes = { workspace = true }
tokio = { workspace = true, default-features = false, features = ["sync", "fs", "io-util", "rt"] }
futures-util = { workspace = true, default-features = false, features = ["sink"] }
prost = "0.12"
chrono = "0.4.38"
flate2 = "1"
bmrng = "0.5.2"
uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
tokio = { workspace = true, default-features = false, features = ["macros", "rt", "rt-multi-thread", "time"] }
rand = { workspace = true }
7 changes: 7 additions & 0 deletions livekit-data-stream/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LiveKit Data Stream

**Important**:
This is an internal crate that powers the data streams feature in LiveKit client SDKs (including [Rust](https://crates.io/crates/livekit) and others) and is not usable directly.

To use data streams in your application, please use the public APIs provided by the
[livekit](../livekit) crate and other client sdks.
Loading
Loading