Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/livekit-capture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
livekit-capture: minor
---

Add a `livekit-capture` crate with the core abstractions for capturing video and publishing it to a LiveKit track.
68 changes: 68 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"livekit-datatrack",
"livekit-token-source",
"livekit-ffi-node-bindings",
"livekit-capture",
"livekit-net",
"livekit-runtime",
"livekit-wakeword",
Expand Down Expand Up @@ -52,6 +53,7 @@ imgproc = { version = "0.3.19", path = "imgproc" }
libwebrtc = { version = "0.3.45", path = "libwebrtc" }
livekit = { version = "0.8.3", path = "livekit" }
livekit-api = { version = "0.6.3", path = "livekit-api" }
livekit-capture = { version = "0.1.0", path = "livekit-capture" }
livekit-ffi = { version = "0.12.75", path = "livekit-ffi" }
livekit-datatrack = { version = "0.1.13", path = "livekit-datatrack" }
livekit-token-source = { version = "0.1.1", path = "livekit-token-source" }
Expand Down Expand Up @@ -84,6 +86,7 @@ prost = "0.14"
prost-build = "0.14"
prost-types = "0.14"
rand = "0.9"
schemars = "1.2"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is a really cool crate!

serde = "1"
serde_json = "1.0"
thiserror = "2"
Expand Down
8 changes: 8 additions & 0 deletions knope.toml
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,11 @@ versioned_files = [
{ path = "Cargo.toml", dependency = "livekit-token-source" },
]
changelog = "livekit-token-source/CHANGELOG.md"

[packages.livekit-capture]
versioned_files = [
"livekit-capture/Cargo.toml",
"Cargo.lock",
{ path = "Cargo.toml", dependency = "livekit-capture" },
]
changelog = "livekit-capture/CHANGELOG.md"
28 changes: 28 additions & 0 deletions livekit-capture/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "livekit-capture"
description = "Encoded video ingest helpers for LiveKit"
version = "0.1.0"
readme = "README.md"
license.workspace = true
edition.workspace = true
repository.workspace = true

[dependencies]
bytes = { workspace = true }
livekit = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync"] }

[dev-dependencies]
tokio = { workspace = true, features = ["rt", "time", "macros"] }

[features]
default = []
serde = ["dep:serde"]
schemars = ["dep:schemars", "serde"]

# Async convenience wrappers for blocking source construction when using
# a tokio runtime.
tokio = ["tokio/rt"]
60 changes: 60 additions & 0 deletions livekit-capture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# LiveKit Capture

> [!IMPORTANT]
> This crate is currently in Developer Preview mode and not ready for production use.
> There may be bugs, and APIs and configuration options are subject to change during this period.

This crate provides video capture sources and the pumps that publish them
with the LiveKit [Rust SDK](../livekit/README.md). Implement one small trait
to add a source. The application runs and supervises every source the same

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"implement one small trait to stream video from a device source" ?

way.

## Source and pump

Two concepts make up the crate. Video reaches a LiveKit track in one of two
forms, so the source and the pump each have two variants.

**A source** produces video, one blocking call at a time. Implement one of
these two traits:

- A `pixel::PixelVideoSource` produces raw `VideoFrame`s — from a camera, for
example. The WebRTC encoder encodes them.
- An `encoded::EncodedVideoSource` produces access units that are already
encoded — from an encoding pipeline, for example. The SDK sends them to the
wire without re-encoding (passthrough). When frames are encoded upstream,
this removes an extra decode and encode step and lowers latency.

**A pump** connects one source to an RTC video source:
`pixel::PixelVideoPump<S>` or `encoded::EncodedVideoPump<S>`. It builds the
matching RTC video source, derives the publish options, and runs the capture
loop. Spawn a pump onto a dedicated thread and it becomes a
`pump::RunningPump`. Both pump kinds spawn into the same type, so an
application supervises them the same way. Stop a running pump from any
thread through its stop handle.

Sources block, so the pumps run synchronous code on plain threads.

## Publishing a track

A pump supplies the RTC source and the publish options, so publication is the
same for either path.

```rust
let pump = PixelVideoPump::new(MyVideoSource::new(config).await?);

let track = LocalVideoTrack::create_video_track("my-source", pump.rtc_source());
let options = pump.publish_options();
room.local_participant().publish_track(LocalTrack::Video(track), options).await?;

let running = pump.spawn()?;

// On shutdown:
let stats = running.stop_and_join_async().await?;
```

## Adding a source

Implement `pixel::PixelVideoSource` for raw frames or
`encoded::EncodedVideoSource` for pre-encoded access units. A source is
constructed from its own config struct and is driven by the matching pump —
no other integration is needed.
Loading
Loading