Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e88ba63
feat: add data streams to livekit-uniffi
1egoman Jul 24, 2026
68b312b
feat: add example testing script for data streams v2 uniffi
1egoman Jul 27, 2026
675461d
feat: make data stream error not a flat error
1egoman Jul 30, 2026
618ed1f
fix: drop ByteStreamReader::write_to_file
1egoman Jul 30, 2026
d461e70
fix: fix compile error
1egoman Jul 30, 2026
bf849ad
feat(data-stream): abort_all_streams / abort_streams_from on incoming…
1egoman Aug 5, 2026
6401331
feat(data-stream): expose writer is_open over the FFI
1egoman Aug 5, 2026
eb91f60
fix(uniffi): make the data stream bindings compile for Kotlin
1egoman Aug 6, 2026
a73758b
fix: temporarily switch over to personal uniffi-dart fork
1egoman Aug 10, 2026
08db233
feat: add data streams dart polling manager adapter
1egoman Aug 10, 2026
9f4616f
Create data_streams_v2_uniffi.md
1egoman Aug 10, 2026
0f8b042
fix: add override for close method name for dart uniffi helper
1egoman Aug 11, 2026
3cab25e
fix: remove kotlin checksums for now
1egoman Aug 11, 2026
70846ab
fix: add livekit-datatrack to knope changeset
1egoman Aug 11, 2026
61f5165
feat: emit StreamClosed for incoming data streams and forward it over…
1egoman Aug 17, 2026
119bc2e
feat: propagate transport errors through the outgoing delegate and ba…
1egoman Aug 17, 2026
b295f17
feat: take the wire encryption type in handle_packet_received and car…
1egoman Aug 17, 2026
9285785
docs: call out that max_payload_byte_length is fixed at construction
1egoman Aug 17, 2026
00ecec7
feat: expose open_stream_count on the incoming data stream manager
1egoman Aug 17, 2026
f91c944
feat(uniffi): make on_packets_available async so hosts can honor its …
pblazej Aug 18, 2026
7f042c1
fix: hold stream trailers to the stream's encryption type
1egoman Aug 18, 2026
dfe6071
chore(uniffi): raise the Android size budget to 1.5 MiB
1egoman Aug 18, 2026
2010c63
fix: remove duplicate livekit-common entry
1egoman Aug 25, 2026
72bdb58
fix: remove data stream uniffi testing script
1egoman Aug 26, 2026
37f3f6c
Add kotlin uniffi renaming to new close in livekit-net to
MaxHeimbrock Aug 27, 2026
6e31b06
docs: add note to AGENTS.md on kotlin bindgen quirks
1egoman Aug 27, 2026
ff27f0d
chore: migrate to mainline dart-uniffi build
1egoman Sep 1, 2026
92be99e
fix: add livekit-signaling to changeset
1egoman Sep 1, 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
13 changes: 13 additions & 0 deletions .changeset/data_streams_v2_uniffi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
livekit: patch
livekit-data-stream: patch
livekit-signaling: patch
livekit-ffi: patch
livekit-uniffi: patch
livekit-datatrack: patch
livekit-net: patch
livekit-token-source: patch
livekit-api: patch
---

Add data streams v2 to exposed uniffi interface - #1286 (@1egoman)
20 changes: 20 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@
- Avoid excessive nesting and prefer [`let-else`](https://doc.rust-lang.org/rust-by-example/flow_control/let_else.html)
- Avoid long parameter lists; group related inputs into a purpose-built struct when it improves readability

## UniFFI integration

Several crates export items to Swift/Kotlin/Node/Python through UniFFI — `livekit-uniffi`, plus `livekit-common`, `livekit-datatrack`, and `livekit-net`, each carrying its own `uniffi.toml`. The Kotlin bindgen has sharp edges that `cargo build`, `cargo test`, and the Swift/Node/Python bindings do **not** catch: they surface only when the generated Kotlin is compiled, and one bad name fails the entire generated file.

- Verify any change to UniFFI-exported API by actually generating and compiling the Kotlin bindings (`cargo make android-package` from `livekit-uniffi/`) — a green `cargo build` proves nothing here
- **Never export a method named `close`**
- UniFFI gives every object a non-`suspend` `close()` to satisfy `AutoCloseable`. An exported Rust method also called `close` differs from it only by `suspend`, which Kotlin rejects as conflicting overloads — see [mozilla/uniffi-rs#2955](https://github.com/mozilla/uniffi-rs/issues/2955)
- Work around it with a Kotlin-only rename, so the Rust source and the Swift/Node/Python bindings keep the original name:
```toml
[bindings.kotlin.rename]
"ByteStreamWriter.close" = "close_stream"
```
- The rename must go in the `uniffi.toml` of the crate that **declares** the item, not in `livekit-uniffi/uniffi.toml`: a rename table only reaches items from the crate that owns it. `WsConnection.close` is renamed in `livekit-net/uniffi.toml` for exactly this reason
- **Never name a field of an exported enum or record `message`**
- For an error variant carrying a `message` field, UniFFI emits a constructor property `message` next to an `override val message` inherited from `Throwable` in one class body, which does not compile — and their types differ (`String` vs `String?`), so they cannot be merged into a single override. See [mozilla/uniffi-rs#2938](https://github.com/mozilla/uniffi-rs/issues/2938), closed without a fix
- Unlike `close`, this **cannot** be renamed away: UniFFI keys the rename table by crate name but looks up enum and record members by the item's full module path, so a rename for anything declared in a submodule is silently ignored (method renames use the crate name and do work)
- Name the field `reason` in Rust instead — see `DataStreamError` in `livekit-uniffi/src/data_stream/common.rs`
- A new crate that exports UniFFI items needs its own `uniffi.toml`, including `omit_checksums = true` under `[bindings.kotlin]`
- The Kotlin checksum test is broken on ARM in every UniFFI release this workspace can use; the full explanation lives in `livekit-uniffi/uniffi.toml` and the root `Cargo.toml`

## Documenting changes

- Changes are documented using [_knope_](https://knope.tech)
Expand Down
10 changes: 6 additions & 4 deletions Cargo.lock

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

9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ serde_json = "1.0"
thiserror = "2"
tokio = { version = "1", default-features = false }
tokio-stream = "0.1"
# Test on a 64-bit ARM device before you change this version.
#
# The Kotlin bindings from uniffi 0.31.2 and 0.32.0 compare each checksum incorrectly on 64-bit
# ARM. Every affected method then fails. See https://github.com/mozilla/uniffi-rs/pull/2897, which
# introduced the defect. Version 0.31.1 has a related defect on 32-bit ARM, and it also does not
# build here, because uniffi-dart requires 0.31.2 or later. mozilla/uniffi-rs#2935 corrects both
# defects, but no release (as of mid august 2026) contains that change.
#
# For this reason, livekit-uniffi sets `omit_checksums` for Kotlin. See livekit-uniffi/uniffi.toml.
uniffi = "0.31"

# For examples
Expand Down
25 changes: 24 additions & 1 deletion livekit-data-stream/src/incoming/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use livekit_common::ParticipantIdentity;

use crate::{
incoming::AnyStreamReader,
types::{Chunk, Packet, Trailer},
types::{Chunk, Packet, StreamId, Trailer},
};

pub struct PacketReceived {
Expand All @@ -39,6 +39,14 @@ pub enum InputEvent {
PacketReceived(PacketReceived),
/// Abort every open stream sent by this participant (they disconnected mid-send).
AbortStreamsFrom(ParticipantIdentity),
/// Abort every open stream (e.g. the local connection is going away). Unlike
/// [`InputEvent::Shutdown`], the run loop keeps going so streams opened later are still handled.
AbortAllStreams,
/// Reply with the number of currently open streams (registered by a header and awaiting more
/// packets). Processed in order with the other events, so the answer reflects everything
/// enqueued before it.
#[from_variants(skip)]
QueryOpenStreamCount(tokio::sync::oneshot::Sender<usize>),
/// Stop the run loop.
Shutdown,
}
Expand All @@ -50,6 +58,20 @@ pub struct StreamOpened {
pub participant_identity: ParticipantIdentity,
}

/// A stream previously announced via [`StreamOpened`] has terminated and will produce no further
/// data: its trailer arrived, its inline payload completed, it failed with an error, or it was
/// aborted.
///
/// Emitted exactly once per opened stream. Hosts delivering streams on ordered topics use this to
/// know when a stream's handler can be considered finished on the wire (a trailer alone is not
/// enough: inline single-packet streams never receive one).
pub struct StreamClosed {
pub stream_id: StreamId,
pub participant_identity: ParticipantIdentity,
/// Topic the stream was opened on.
pub topic: String,
}

/// A "raw chunk received" notification, which is used to trigger
/// the deprecated [RoomEvent:::StreamChunkReceived] event.
pub struct ChunkReceived {
Expand Down Expand Up @@ -79,6 +101,7 @@ pub struct TrailerReceived {
#[derive(FromVariants)]
pub enum OutputEvent {
StreamOpened(StreamOpened),
StreamClosed(StreamClosed),
ChunkReceived(ChunkReceived),
TrailerReceived(TrailerReceived),
}
Loading
Loading